#!/bin/bash if [ "$1" == 'debug' ];then set -x; shift; fi if [ "$1" == 'debug2' ];then set -xvT; shift; fi #------------------------------------------------- # - WatchLG - # Watch for login break-in attempts # Record in database ... and inject into firewall # after maximum 'affairs' was reached #------------------------------------------------- REALPATH=`realpath $0` WHERE=`dirname $REALPATH` ME=`basename $REALPATH` cd $WHERE . ../../system.conf . ../../watcher.conf # needed for debugging . ../../common.conf . ../../common.bashlib #----------------------- . $ME.conf . private.bashlib : echo $PATH THIS_IP=`dig +short $HOSTNAME` # BASH knows HOSTNAME trap refresh HUP # React on a 'HUP' for the filter refresh trap stopservice TERM INT # React on a 'TERM' or CTL-C when debugging trap rerun KILL 9 # React on crash trap 'errorexit $? $_' EXIT load_filter() { # $1 .. filter status # $2 .. compress flag; i.e. compress it not empty local funtag="[${FUNCNAME[0]}]" unset -f filter [[ -f filter ]] && mv filter filter.old [[ -f filter.compress ]] && rm -f filter.compress ./mkfilter $1 if [ -f filter.old ] then diff -c filter filter.old > filter-diffs else [[ -f filter-diffs ]] && rm -f filter-diffs fi # If the 'compress filter' flag is set compress filter; # i.e. remove comments and empty lines for runtime if [ ! -z "$2" ] then trace "$funtag Using filter compressed" compress_filter . filter.compress else trace "$funtag Using filter plain" . filter fi # Create/refresh superfluous_map as well SUPERFLUOUS=`mk_superfluous` } refresh() { local funtag="[${FUNCNAME[0]}]" trace "$funtag Reloading filters for $ME ..." logger "$ME[$$]: $funtag Reloading filters for $ME ..." load_filter "$funtag `date --iso=seconds`" $COMPRESS_FILTER } errorexit() { local funtag="[${FUNCNAME[0]}]" logger "$ME[$$]: $funtag trapped with exit code '$1', Cmd: '$2' ... Loop: $loop" trace "$funtag $ME trapped with exit code '$1', Cmd: '$2' ... Loop: $loop" exit 255 } stopservice() { local funtag="[${FUNCNAME[0]}]" logger "$ME[$$]: $funtag Terminating on request ..." trace "$funtag Terminating on request ..." exit } rerun() { local funtag="[${FUNCNAME[0]}]" log "$ME: $funtag Abnormal termination, exit status: $?, Loop; $loop" # log "Rule was: $RULE" # log "Bandit : $BANDIT" logger "$ME[$$]: Restarting in a minute ..." trace "$funtag $ME Restarting in a minute ..." echo "$WHERE/$ME" | at "now + 1 minute" exit } # ----------------------- Main program --------------------------- dump_runtime ln -sf $WHERE/LGinjector $TOOLS_LINK trace "--- $ME started on $HOSTNAME ... $SYSTEM $SYSVERS" trace "Watcher: $PRODUCT, $REVISION" if [ -z "$TIME_SLICE" ]; then TIME_SLICE=60; fi touch $LOG chmod 600 $LOG touch $ME.trace chmod 600 $ME.trace if [ ! -p $PIPE ]; then mkfifo -m 600 $PIPE; fi # # Load generated 'filter' function # load_filter initial $COMPRESS_FILTER # # Start scanning ... # logger "$ME[$$]: Started listening on pipe $PIPE using dynamic filters" log "Started listening on pipe $PIPE using dynamic filters" trace "Started listening on pipe $PIPE using dynamic filters" renice -10 $$ trace "Starting scanner ..." while : # Only a crash can stop us do read < $PIPE LOOP_START=`date +%s%3N` read DYN_ADDR < $LOG_DIR/DYN_IP ((loop++)) # Count loops : echo "--------[ Loop: $loop ]----------" if [ -z "$REPLY" ] then : echo "Eeek! Empty line ..." continue fi # ------ Pre-Processor (Internal Rule set) -------------- # Log lines w/o IP make no sense # » Must come FIRST of all « RULE=IPpresent BANDIT=`get_bandit` if [ -z "$BANDIT" ] then : trace "[No IP addr] ... skipped" continue fi # # Kick off any junk ... # RULE=Superfluous # SUPER=`grep -oE "$SUPERFLUOUS" <<< $REPLY` # if [ ! -z "$SUPER" ] [[ "$REPLY" =~ ($SUPERFLUOUS) ]] if [ -n "${BASH_REMATCH[0]}" ] then : echo "Superfluous stuff ... ignored ..." continue fi RULE=Freehost if [[ "$REPLY" =~ "@Freehost:" ]] then DEL_ADDR=`get_bandit` : trace "$ME/$RULE Deleting $DEL_ADDR from $TABLE" freehost $DEL_ADDR continue fi RULE=Self if [[ "$REPLY" =~ "$THIS_IP" ]] then : trace "$ME/$RULE Oops ... me $THIS_IP" continue fi RULE=DynIP if [[ "$REPLY" =~ "$DYN_ADDR" ]] then : trace "$ME/$RULE Oops ... my dyn IP $DYN_ADDR" continue fi # Careful here $TRUSTED is a list of IP addresses RULE=Trusted if [[ "$REPLY" =~ ($TRUSTLIST) ]] then : trace "$ME/$RULE Oops ... ${BASH_REMATCH[0]} in trusted hosts ... skipping" continue fi #-------------------------------------- dump_loadrate trace "[Loop: $loop] '$REPLY'" FILT_START=`date +%s%3N` filter # Returns with $BANDIT, $CLASS, $TAG, $ACTION and $RULE set FILT_RESULT=$? # Keep retcode save for the post processor FILT_END=`date +%s%3N` FILT_TIME=$(( FILT_END-FILT_START )) LOOP_TIME=$(( FILT_END-LOOP_START )) # # Post processor # case $FILT_RESULT in 0) trace "[postpro] $RULE ... fall through, $LOOP_TIME/$FILT_TIME ms" ;; 1) trace "[postpro] Finished for rule [$RULE], $LOOP_TIME/$FILT_TIME ms" continue ;; 2) trace "[postpro] Handled outside of $ME" continue ;; 3) trace "[postpro] $BANDIT whitelisted ... nothing to do ..." continue ;; 254) trace "[postpro] Ignored ... [$RULE] '$Pattern', $LOOP_TIME/$FILT_TIME ms" continue ;; 255) trace "[postpro] !!! Nasty things happend by rule '$RULE' for '$BANDIT'!!!" ;; *) trace "[postpro] Illegal return code '$FILT_RESULT' ... fall through" ;; esac #-------------[ Catch all before loop finishes ]----------- RULE=UNTREATED log "[$RULE] '$REPLY'" trace "[$RULE] '$REPLY'" done # DEAD-End ... Reaching here triggers a trap that restarts us rerun