[ SEA-GHOST MINI SHELL]
#!/bin/sh
# version 1.3
# 2020-07-31: added ignore list (SYSCP-226715877)
# 2020-08-07: added -d, -dd options (SYSCP-226715998)
# 2020-08-12: added -c (SYSCP-226715998)
# === VARIABLES ==============
set -e
# Make sure sort always sorts in same order.
LANG=C
export LANG
# for zabbix: echo code only, don't show text message
CODEONLY='off'
# will be used in functions
SCRIPT_FILENAME=$0
# Count of changed files
CHANGED_FILES=0
# Regext values for ignore before show alerts
IGNORE_VALUES_FILE=/etc/.ignore
# Count of changed files, that matched by ignore list
IGNORED_CHANGED_FILES=0
# Flag for script is running now
PIDFILE=/tmp/check_etc_attributes.pid
# === FUNCTIONS ================
function do_exit() {
local code=$1
local message=$2
if [ "$CODEONLY" == "ON" ]; then
echo $code
else
echo $message
fi
exit $code
}
function ok_exit() {
do_exit 0 "OK: $@"
}
function error_exit() {
do_exit 1 "ERROR: $@"
}
function create_PID(){
if [ -f $PIDFILE ]; then
PID=$(cat $PIDFILE)
if [ -e /proc/$PID ]; then
error_exit "check_etc_attributes - check already running"
else
## Process not found assume not running
echo $$ > $PIDFILE
if [ $? -ne 0 ]; then
error_exit "check_etc_attributes - Could not create PID file"
fi
fi
else
echo $$ > $PIDFILE
if [ $? -ne 0 ]; then
error_exit "check_etc_attributes - Could not create PID file"
fi
fi
}
# removing pid file
function remove_PID() {
rm -f $PIDFILE > /dev/null 2>&1
}
# check one value (file name) by ignore list
function ignore_check_one_value() {
for ingoreRegExp in $(grep -v ^# $IGNORE_VALUES_FILE); do
if [[ $1 =~ $ingoreRegExp ]]; then
echo 1
return
fi
done
echo 0
}
# check differences by ignorefile values
function ignore_check() {
if [ -n "$IGNORE_VALUES_FILE" ] && [ -e "$IGNORE_VALUES_FILE" ]; then
# checking each values
for oo in $CHANGED_FILE_NAMES; do
ignored=$(ignore_check_one_value $oo)
if [ "$ignored" == "1" ]; then
((IGNORED_CHANGED_FILES+=1))
fi
done
fi
}
generate_metadata() {
NOVCS='/etc -path /etc/.git -prune -o -path /etc/.svn -prune -o -path /etc/.hg -prune -o -path /etc/_darcs -prune -o'
# Keep the sort order the same at all times.
LC_COLLATE=C
export LC_COLLATE
# Store things that don't have the default user or group.
# Store all file modes, in case the user has an unusual umask.
find $NOVCS \( -type f -or -type d \) -print | sort | perl -ne '
BEGIN { $q=chr(39) }
sub uidname {
my $want=shift;
if (exists $uidcache{$want}) {
return $uidcache{$want};
}
my $name=scalar getpwuid($want);
return $uidcache{$want}=defined $name ? $name : $want;
}
sub gidname {
my $want=shift;
if (exists $gidcache{$want}) {
return $gidcache{$want};
}
my $name=scalar getgrgid($want);
return $gidcache{$want}=defined $name ? $name : $want;
}
chomp;
my @stat=stat($_);
my $mode = $stat[2];
my $uid = $stat[4];
my $gid = $stat[5];
s/$q/$q"$q"$q/g; # escape single quotes
printf "chown %s %s\n", $uid, $_;
printf "chgrp %s %s\n", $gid, $_;
printf "chmod %04o %s\n", $mode & 07777, $_;
'
# We don't handle xattrs.
# Maybe check for getfattr/setfattr and use them if they're available?
}
# Create file with metadata content
function fill_metadata_file() {
local output_file=$1
if [ -z "$output_file" ]; then
error_exit "No parameter for function [fill_metadata_file]"
fi
echo "# Generated by $SCRIPT_FILENAME. Do not edit." > $output_file
echo >> $output_file
chmod 600 $output_file
generate_metadata >> $output_file
}
# check differences between references and current state
function compare_data() {
if [ -f /etc/.attributes ] && [ -f /etc/.attributes.ref ]; then
CHANGED_FILE_NAMES=''
for i in $(diff /etc/.attributes.ref /etc/.attributes | grep -E "^<" | awk '{print $4}' | sort | uniq | sed 's/\*/\\\*/g');
do
if [ -e $i ]; then
((CHANGED_FILES+=1))
CHANGED_FILE_NAMES="$CHANGED_FILE_NAMES $i"
fi
done
fi
}
# Backup /etc/.attributes* files to /var/log/backup_attributes
function make_backups() {
local backup_path="/var/log/backup_attributes"
local DATE=`date +%Y%m%d_%H%M`
local _RANDOM=$RANDOM
local backup_file="attributes-${DATE}--${_RANDOM}"
local backup_file_ref="attributes.ref-${DATE}--${_RANDOM}"
if [ ! -d $backup_path ]; then
mkdir -p $backup_path
fi
if [ -d $backup_path ]; then
find $backup_path -type f -mtime +1 -delete
fi
if [ -f /etc/.attributes ]; then
cat /etc/.attributes | gzip > ${backup_path}/${backup_file}.gz
fi
if [ -f /etc/.attributes.ref ]; then
cat /etc/.attributes.ref | gzip > ${backup_path}/${backup_file_ref}.gz
fi
}
# from version 1.2
function get_current_state() {
#
create_PID
#
make_backups
#
fill_metadata_file /etc/.attributes
#
compare_data
#
ignore_check
#
remove_PID
}
# from version 1.2
function show_differences() {
get_current_state
diff /etc/.attributes.ref /etc/.attributes | grep -E "^<" | awk '{print $4}' | sort | uniq
exit 0
}
# from version 1.2
function show_differences_diff() {
get_current_state
diff /etc/.attributes.ref /etc/.attributes
exit 0
}
function show_help() {
echo "Check attributes of files and catalogs in /etc";
echo "Usage: $SCRIPT_FILENAME [OPTION]"
echo "OPTIONS:"
echo " -r : create reference file for checking"
echo " -h|--help : show help"
echo " -c : return checking result for zabbix: 0-no issue, 1-has error"
echo " -d : show differences file list"
echo " -dd : show detail differences file list by diff"
echo ""
echo "To ignore some files/catalogs add regexp values to: $IGNORE_VALUES_FILE"
echo ""
exit 0
}
function create_references() {
#
create_PID
#
fill_metadata_file /etc/.attributes.ref
# remove pid file
remove_PID
#
exit 0
}
function checking_current_state() {
#
get_current_state
# If less than 3 changed files/catalogs found it is safe to copy current check file as new reference
if [ $CHANGED_FILES -le 3 ]; then
# copy new values to references
cp -f /etc/.attributes /etc/.attributes.ref
ok_exit "All attributes in /etc are OK."
fi
# from version 1.1
# if all changes matched to ignore values
if [ $CHANGED_FILES -eq $IGNORED_CHANGED_FILES ]; then
# copy new values to references
cp -f /etc/.attributes /etc/.attributes.ref
ok_exit "All changes matched by ignore list. It's OK"
fi
if [ $CHANGED_FILES -gt 3 ]; then
error_exit "Attributes of the $CHANGED_FILES files/catalogs in /etc were changed!"
fi
error_exit "Something goes wrong"
}
# === MAIN PART =================
# get command line options
while [[ $# -gt 0 ]]; do
key="$1"
case "$key" in
-c)
CODEONLY="ON"
;;
# Show help flag
-h|--help)
show_help
;;
-d)
show_differences
;;
-dd)
show_differences_diff
;;
# Create reference file with permissions
-r)
create_references
;;
*)
# Do whatever you want with extra options
error_exit "Unknown option '$key'"
;;
esac
shift
done
# Not keys in command line = default mode: checking
checking_current_state
SEA-GHOST - SHELL CODING BY SEA-GHOST