[ SEA-GHOST MINI SHELL]

Path : /proc/2/root/var/lib/zabbix/
FILE UPLOADER :
Current File : //proc/2/root/var/lib/zabbix/check_etc_attributes.sh

#!/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