#
#
# monitor ssh log messages to react to and block attacks
#

# remember IP addresses where?
$BLOCKLIST = "/etc/firewall/blocklist";

# other stuff
$DEBUGV = $ENV{"DEBUG"};
$CLEAN_INTERVAL = 5000;

if ( $ENV{"READ_STDIN"} ) {
	warn "Reading from stdin.\n";
	die unless open(FWLOG, "<&STDIN");
} else {
	unless (open(FWLOG, "tail --follow=name --retry --max-unchanged-stats=10 /var/log/messages |")) {
		warn "Cannot tail --follow=name --retry --max-unchanged-stats=10 /var/log/messages\n";
		sleep 60;
		die;
	}
}

if (open(LOGGING, "| logger -t 'ssh-react[$$]'")) {
	select(LOGGING); $| = 1;     # make unbuffered
} else {
	warn "Cannot log to syslog\n";
	open(LOGGING, ">&STDOUT");
}

print LOGGING "ssh-react starts\n";

while (<FWLOG>) {
	chomp;
	warn "READ: $_\n" if $DEBUGV;
	#Nov 10 12:18:33 rhad-new sshd[12930]: Illegal user test from 222.106.2.117
	if (/^\S+\s+\S+\s+\S+\s\S+\ssshd\[[0-9]+\]:\s?failed password for (?:illegal user )?\S+ from [0-9\.]+/io) {
		$reason = "";
		warn "MATCHED: $_\n" if $DEBUGV;
		if (($user, $sip) = /.* for (?:illegal user )?(\S+) from ([0-9\.]+)/io) {
			warn " ssh as $user from $sip\n" if $DEBUGV;

			next if $poisoned{"$sip"};

			if (++$SrcSeen{"$sip"} > 2) {
				chomp($now_string = localtime);
				$reason = $SrcSeen{"$sip"} . " ssh login attempts with illegal user(s)";
				print LOGGING "blocking $sip for $reason\n";
				system("/sbin/ipchains -I i-i-ssh -s $sip/32 -j DENY");
				system("echo '$sip/32' '# $now_string - $reason' >> $BLOCKLIST");
				$poisoned{"$sip"} = 1;
			}
		}
	} else {
		# non-matching log line
		if ($SEEN_LINES++ > $CLEAN_INTERVAL) {
			$num_hosts = keys %SrcSeen;
			$num_poisoned = keys %poisoned;
			print "$num_hosts seen, $num_poisoned blocked\n";
			$SEEN_LINES = 0;
		}
	}
}



