From dd8363b8ef229051afa6c56bf18aa5c327fe062d Mon Sep 17 00:00:00 2001 From: Abel `00z' Camarillo <00z@the00z.org> Date: Sun, 29 Jun 2008 23:42:59 -0500 Subject: [PATCH] Initial commit. Original version. --- CHANGELOG | 66 +++++++++ CREDITS | 7 + INSTALL | 118 +++++++++++++++ Makefile | 49 +++++++ README | 62 ++++++++ VERSION | 1 + blocksshd | 446 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ blocksshd.conf | 29 ++++ blocksshd.man | 167 +++++++++++++++++++++ blocksshd.spec | 87 +++++++++++ 10 files changed, 1032 insertions(+) create mode 100644 CHANGELOG create mode 100644 CREDITS create mode 100644 INSTALL create mode 100644 Makefile create mode 100644 README create mode 100644 VERSION create mode 100755 blocksshd create mode 100644 blocksshd.conf create mode 100644 blocksshd.man create mode 100644 blocksshd.spec diff --git a/CHANGELOG b/CHANGELOG new file mode 100644 index 0000000..22decf2 --- /dev/null +++ b/CHANGELOG @@ -0,0 +1,66 @@ +BlockSSHD v1.3 - June 27 2008 + - Removed IPv6 support + +BlockSSHD v1.2 - August 9 2007 + - Added new init script + - Changed default blocking list location to /etc + - Documentation fixes + +BlockSSHD v1.1 - April 4 2007 + - Fixed weird ^Ms in files + - Added Anton's WHOIS function to blocking emails + - If restore block function is off then remove log file to ensure old + IPS are not accidently applied + - If restore block function is on then automatically create log file + - Fixed uninstall script in blocksshd.spec + +BlockSSHD v1.0.1 - March 4 2007 + - Minor fix to correct an error with the unblocking function (thanks + to David Kozinn for reporting it) + +BlockSSHD v1.0 - November 8 2006 + - Added support for subnets in whitelist (thanks to Lester Hightower for this) + - Minor changes to spec files + - File locations changed to prefix of /usr + - Configuration file moved to /etc/blocksshd.conf + +BlockSSHD v0.9 - September 26 2006 + - Fixed init script binary location + - Fixed minor documentation errors + - Fixed spec file errors - including adding conf file installation + (Thanks to Samuel Granjeaud for reporting these bugs) + +BlockSSHD v0.8 - August 30 2006 + - Removed requirement for Prod::PID::File CPAN module + - Removed requirement for Prod::Daemon CPAN module + - Fixed Makefile error with --wildcards tar option + +BlockSSHD v0.7 - July 30 2006 + - Changed unblock function. When unblocking is enabled then the unblock process will both remove the blocked IP address from the firewall but also remove it from the log file + - Added RPM/SRPM packages + - Updated distribution mechanism to support RPMs + - Fixed restore blocked function to use alternate file handle + +BlockSSHD v0.6 - July 17 2006 + - Added support for pf and BSD + - Fixed some minor bugs + +BlockSSHD v0.5 - July 12 2006 + - Fixed regular expression bug introduced in v0.4 + +BlockSSHD v0.4 - June 17 2006 + - Adjusted regulasr expressions to support Debian sid and Dropbear (an embedded device SSH server). With Dropbear support it may be possible to run blocksshd on routers or other Linux-based devices that support Perl and the required modules. + +BlockSSHD v0.3 - Apr 6 2006 + - Added additional regular expressions for matching failures on Debian, SuSE and Red Hat + +BlockSSHD v0.2 - Apr 5 2006 + - Added unblock function + - Fixed some issues with signals and exit handling + +BlockSSHD v0.1 - Mar 24 2006 + - First public release + - Script uses File::Tail module to collect data + - Script runs daemonized in background and can be started from rc script + + diff --git a/CREDITS b/CREDITS new file mode 100644 index 0000000..07ffbb0 --- /dev/null +++ b/CREDITS @@ -0,0 +1,7 @@ +Thanks to: + +- Daniel Gerzo for his original BruteForceBlocker + +- Anton Valqk for adding the pf support + +- Lester Hightower for subnet support in whitelists diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000..d596ed0 --- /dev/null +++ b/INSTALL @@ -0,0 +1,118 @@ +PRE-REQUISITES + + BlockSSHD requires the following CPAN modules: + + *) Sys::Syslog - often comes with Perl and may already be installed; + *) Sys::Hostname - often comes with Perl and may already be installed; + *) File::Tail; + *) Tie::File; + *) Net::DNS; + *) Net::Subnets; + *) Getopt::Long; + + Please install them prior to running BlockSSHD. + +INSTALLATION + + 1) BlockSSHD can work with iptables on Linux or pf on BSD. Depending on which + you use you will need to add a new chain and/or rule to your iptables or pf + firewall that directs incoming SSH connections to be blocked. + + 1.1) iptables + + For iptables firewalls you can create a new chain like so: + + $ iptables -N + + Where is the same of the iptables chain specified in the configuration + file. + + Then you can create the re-direction rule like so: + + $ iptables -I INPUT -p tcp -m tcp --dport 22 -j + + If you wish to block FTP logins using ProFTPd then you can add port 21 to the + above rule or add an additional rule. + + This rule selects all incoming SSH connections on port 22 and redirects them + to . Your blocking rules will be placed in that chain and if the source + IP address of an incoming connection matches a blocking rule in then + the connection will be dropped. + + The rule will need to be placed above any other incoming SSH rules to ensure + all incoming SSH connections are jumped to the blocking chain. If you use a + distribution like Red Hat and Mandrake that has automated/GUI tools to contruct + your iptables rule base you will need to add this rule using this mechanism. + + In both example iptables commands replace with the name of the + BlockSSHD chain. The name of this chain also needs to be defined in the + blocksshd.conf configuration file. BlockSSHD also checks the presence of this + chain each time it is started and will re-create it if it has been deleted. + + All blocking rules in the chain will also be flushed each time BlockSSHD is + started. + + 1.2) pf + + For the pf firewall you will need to add a rule to block IP addresses listed + in the target chain like so: + + block in on $ext_if proto tcp from to me port { 21,22 } + + The above rule blocks brute force login attacks on both port 21 and 22 and can + prevent brute force attacks using both SSH and ProFTPd. + + 2) Install the configuration file into the /etc directory and the + script to /usr/sbin (or similar directories in your environment - + however you will need to adjust the script to reflect the new locations of + these files) + + This installation can be done using the following command: + + # make install + + To install a Red Hat-style init script into /etc/rc.d/init.d use: + + # make init + + 3) Adjust the configuration file to suit your environment (see the + CONFIGURATION section). + + 4) BlockSSHD also logs to syslog. It uses a program name of blocksshd and a + facility of auth. You can use this combination to direct the syslog messages + from BlockSSHD to a specific file or location. + + 5) You're done. + +CONFIGURATION + + All configuration is held in an external file, blocksshd.conf, this file is + usually located in the /etc/ directory but this can be overridden in + the script. + + There are a number of configuration directives located in this file and these + are listed below. + + *) os - Specify the operating system BlockSSHd will run under. Use linux for + Linux and bsd for BSD + *) pid_file - Location of the BlockSSHd PID file + *) send_email - Enable the sending of email notifications + *) email - Email address to send email notifications to + *) chain - Name of the iptables table to hold the rules + *) logfile - Log file to monitor for SSH login failure messages + *) logcheck - Interval to check log file in seconds + *) max_attempts - Maximum number of failures before blocking IP + *) timeout - Time without activity after which IP counts are reset in seconds + *) unblock - Enable unblocking functionality + *) unblock_timeout - Period in seconds since blocking that an IP address is + unblocked + *) restore_blocked - Unable this option to log IP addresses and then re-block + them when BlockSSHd is restarted + *) log_ips - Location of the blocked IP address log file + *) mail - Location of the mail binary used to send emails + *) email_whois_lookup - Enable WHOIS lookup of the blocked IP address to be included in blocking notification + *) whois - Location of the whois binary + *) sed - Location of the sed binary + *) iptables - Location of the iptables binary + *) pfctl - Location of the pfctl binary + *) whitelist - A list of IP addresses that you never want blocked diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..5fd22b4 --- /dev/null +++ b/Makefile @@ -0,0 +1,49 @@ +# +# Makefile for blocksshd +# +# Copyright 2006-2007, James Turnbull & Anton Valqk. +# +# + +APPNAME = blocksshd +CONF = blocksshd.conf +INSTALL = install +PREFIX = /usr +EXEC_PREFIX = $(PREFIX) +BINDIR = $(EXEC_PREFIX)/bin +SYSCONFDIR = /etc +MANDIR = $(PREFIX)/man +MAN1DIR = $(MANDIR)/man1 +INIT = init/blocksshd +INITDIR = /etc/rc.d/init.d +DESTDIR = + +all: $(APPNAME).1.gz + +install: all + $(INSTALL) -p -m 755 -d $(DESTDIR)$(BINDIR) + $(INSTALL) -p -m 755 -d $(DESTDIR)$(SYSCONFDIR) + $(INSTALL) -p -m 755 -d $(DESTDIR)$(MAN1DIR) + $(INSTALL) -p -m 755 $(APPNAME) $(DESTDIR)$(BINDIR) + $(INSTALL) -p -m 644 $(CONF) $(DESTDIR)$(SYSCONFDIR) + $(INSTALL) -p -m 644 $(APPNAME).1.gz $(DESTDIR)$(MAN1DIR) + +init: + $(INSTALL) -m 755 -d $(DESTDIR)$(INITDIR) + $(INSTALL) -m 755 $(INIT) $(DESTDIR)$(INITDIR)/$(APPNAME) + +uninstall: + rm $(DESTDIR)$(BINDIR)/$(APPNAME) + rm $(DESTDIR)$(SYSCONFDIR)/$(CONF) + rm $(DESTDIR)$(MANDIR)/$(APPNAME).1.gz + rm $(DESTDIR)$(INITDIR)/$(APPNAME) + +clean: + rm -fr $(APPNAME).1.gz + +$(APPNAME).1.gz: $(APPNAME).man CREDITS + touch -r $(APPNAME).man timestamp + cat $(APPNAME).man CREDITS | gzip -c > $(APPNAME).1.gz + touch -r timestamp $(APPNAME).1.gz + +.PHONY: all install init uninstall clean diff --git a/README b/README new file mode 100644 index 0000000..8913a8d --- /dev/null +++ b/README @@ -0,0 +1,62 @@ + +BlockSSHD v1.1 + +BlockSSHD is a Perl script based on BruteForceBlocker v1.2.3 that dynamically +adds IPTables rules for Linux and pf firewall rules for BSD that block SSH +brute force attacks. It can also detect ProFTPd login failures. + +BlockSSHD checks a log file you specify, for example /var/log/secure on a Red +Hat, for SSH login failure messages. If it detects a failure message it +records the source IP address and starts a counter. If messages continue to be +detected from the same source IP address the counter is incremented for each +message. When the counter reaches a user-specified threshold then the script +will add a firewall rule blocking SSH connections from that source IP address. +A user-specified time-out is also defined to trigger a reset of the counter. If +the counter is incremented but has not yet reached the blocking threshold and a +new login failure message arrives then BlockSSHD checks the time-out. If the +last increment of the counter occurred earlier than the current time minus the +time-out period then the counter is reset rather than incremented. The time-out +defaults to 600 seconds (10 minutes). + +The BlockSSHD script can also unblock IP address after a period. This is +enabled in the blocksshd.conf configuration file using the unblock option and +with the period set using the unblock_timeout option. + +The BlockSSHD script can also log the IP addresses blocked to a file. This logging +allows you to re-apply these blocked IP addresses if the host or scrip is re-started. +This allows you to restore previously blocked IP addresses. The log file is not a +complete record of all IP addresses blocked but merely aids in re-applying already +blocked IP addresses - it only logs IP addresses if the restore_blocked option in the +configuration file is set to 1. + +If you have both the unblock function and the re-block function enabled then when the +IP address is unblocked it will also be removed from the log file. + +The BlockSSHD script has some command line options: + +*) -d | --daemon | --start - Runs the script as a daemon +*) --stop - Stops the script +*) -h | --help - Prints help text +*) -v | --version - Print the version + +Running the BlockSSHD script without any command line options will start it +interactively. If you are having issues with blocksshd you can use the interactive +mode to debug it. Another useful debugging option is to run blocksshd like so: + +# tail -f /file/blocksshd/logs/to | grep -i blocksshd + +You will also find a Red Hat style init script in the init directory. + +For installation instructions see the INSTALL file. + +Please feel free to email me with any issues - james@hardening-linux.com + +Copyright 2006, James Turnbull +Support for pf and whois added by Anton - valqk@webreality.org - http://www.webreality.org +Support for subnets in the whitelist added by Lester Hightower - hightowe@10east.com - http://www.10east.com/ + +This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA diff --git a/VERSION b/VERSION new file mode 100644 index 0000000..7e32cd5 --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +1.3 diff --git a/blocksshd b/blocksshd new file mode 100755 index 0000000..07382b8 --- /dev/null +++ b/blocksshd @@ -0,0 +1,446 @@ +#!/usr/bin/perl -w + +# This is BlockSSHD which protects computers from SSH brute force attacks by +# dynamically blocking IP addresses using iptables based on log entries. +# BlockSSHD is modified from BruteForceBlocker v1.2.3 by Daniel Gerzo + +# Copyright (C) 2006, James Turnbull +# Support for pf and whois added by Anton - valqk@webreality.org - http://www.webreality.org +# Support for subnets in the whitelist added by Lester Hightower - hightowe@10east.com - http://www.10east.com/ + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +# Public License for more details. + +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +use strict; +use warnings; + +use Sys::Syslog; +use Sys::Hostname; +use Tie::File; +use File::Tail; +use Net::DNS::Resolver; +use Net::Subnets; +use Getopt::Long; + +use POSIX qw(setsid); +use vars qw($opt_d $opt_h $opt_v $opt_stop); + +$ENV{'PATH'} = '/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin'; + +my $version = "1.3"; + +our $cfg; + +# This is where the configuration file is located +require '/etc/blocksshd.conf'; + +my $work = { + ipv4 => '(?:\d{1,3}\.){3}\d{1,3}', # regexp to match ipv4 address + fqdn => '[\da-z\-.]+\.[a-z]{2,4}', # regexp to match fqdn + hostname => hostname, # get hostname +}; + +$cfg->{'whitelist_prepared'}=&loadwhitelist($cfg->{whitelist}); + +Getopt::Long::Configure('bundling'); +GetOptions + ("start" => \$opt_d, "daemon" => \$opt_d, "d" => \$opt_d, + "h" => \$opt_h, "help" => \$opt_h, + "v" => \$opt_v, "version" => \$opt_v, + "stop" => \$opt_stop); + +if ($opt_d) { + if (-e $cfg->{pid_file}) + { die "BlockSSHD is already running!\n" } + + # Fork daemon + chdir '/' || die "Can't change directory to /: $!"; + umask 0; + open(STDIN, "+>/dev/null"); + open(STDOUT, "+>&STDIN"); + open(STDERR, "+>&STDIN"); + defined(my $pid = fork) || die "Can't fork: $!"; + exit if $pid; + setsid || die "Can't start a new session: $!"; + + # Record PID + open (PID,">$cfg->{pid_file}") || die ("Cannot open BlockSSHD PID file $cfg->{pid_file}!\n"); + print PID "$$\n"; + close PID; +} + +if ($opt_stop) { + exithandler(); +} + +if ($opt_h) { + print_help(); + exit; +} + +if ($opt_v) { + print "BlockSSHD version $version\n"; + exit; +} + +openlog('blocksshd', 'pid', 'auth'); + +my $alarmed = 0; # ALRM state +my %count = (); # hash used to store total number of failed tries +my %timea = (); # hash used to store last time when IP was active +my %timeb = (); # hash used to store time when IP was blocked +my $res = Net::DNS::Resolver->new; + +# Watch signals + +$SIG{'ALRM'} = \&unblock; +$SIG{'INT'} = \&exithandler; +$SIG{'QUIT'} = \&exithandler; +$SIG{'KILL'} = \&exithandler; +$SIG{'TERM'} = \&exithandler; + +# Notify of startup + +syslog('notice', "Starting BlockSSHD"); + +# Create iptables chain + +setup(); + +# Clear existing rules + +flush(); + +# Restore previously blocked rules + +if($cfg->{restore_blocked} == 1) { + restore_blocked(); +} + +# The core process + +my $ref=tie *FH, "File::Tail", (name=>$cfg->{logfile}, + maxinterval=>$cfg->{logcheck}, + interval=> 10, + errmode=> "return"); + +if ( $cfg->{unblock} == 1) { +alarm( ($cfg->{unblock_timeout} /2) ); +} + +while () { + if( $alarmed ) { + $alarmed = 0; + next; + } + + if ( + /.*Failed (password) .* from ($work->{ipv4}|$work->{fqdn}) port [0-9]+/i || + /.*(Invalid|Illegal) user .* from ($work->{ipv4}|$work->{fqdn})$/i || + /.*Failed .* for (invalid|illegal) user * from ($work->{ipv4}|$work->{fqdn}) port [0-9]+ .*/i || + /.*Failed .* for (invalid|illegal) user .* from ($work->{ipv4}|$work->{fqdn})/i || + /.*(Postponed) .* for .* from ($work->{ipv4}|$work->{fqdn}) port [0-9]+ .*/i || + /.*Did not receive (identification) string from ($work->{ipv4}|$work->{fqdn})$/i || + /.*Bad protocol version (identification) .* from ($work->{ipv4}|$work->{fqdn})$/i || + /.* login attempt for (nonexistent) user from ($work->{ipv4}|$work->{fqdn})$/i || + /.* bad (password) attempt for '.*' from ($work->{ipv4}|$work->{fqdn}):[0-9]+/i || + /.*unknown (user) .* from ($work->{ipv4}|$work->{fqdn}).*/i || + /.*User .* (from) ($work->{ipv4}|$work->{fqdn}) not allowed because.*/i || + /.*USER.*no such (user) found from ($work->{ipv4}|$work->{fqdn}).*/i + ) { + if($1 || $2) { + my $IP=$1 unless $2; + $IP=$2 if $2; + if ( $IP =~ /$work->{fqdn}/i) { + foreach my $type (qw(AAAA A)) { + my $query = $res->search($IP, $type); + if ($query) { + foreach my $rr ($query->answer) { + block($rr->address); + } + } + } + } else { + block($IP); + } + } + } +} + +closelog(); + +sub block { + # Confirm iptables table is created + setup(); + + my ($IP) = shift or die "Missing IP address!\n"; + + # check to see if IP address already blocked + + if($cfg->{os} eq 'linux') { + my ($exists) = system("$cfg->{iptables} -n -L $cfg->{chain} | grep -q '$IP'"); + if ($exists == 0) { + return; + } + } + elsif($cfg->{os} eq 'bsd') { + my ($exists) = system("$cfg->{pfctl} -t $cfg->{chain} -T show| grep -q '$IP'"); + if ($exists == 0) { + return; + } + } + + # Reset IP count if timeout exceeded + if ($timea{$IP} && ($timea{$IP} < time - $cfg->{timeout})) { + syslog('notice', "Resetting $IP count, since it wasn't active for more than $cfg->{timeout} seconds"); + delete $count{$IP}; + } + $timea{$IP} = time; + + # increase the total number of failed attempts + $count{$IP}++; + + if ($count{$IP} < $cfg->{max_attempts}+1) { + syslog('notice', "$IP was logged with a total count of $count{$IP} failed attempts"); + } + if ($count{$IP} >= $cfg->{max_attempts}+1) { + syslog('notice', "IP $IP reached the maximum number of failed attempts!"); + system_block($IP); + } +} + +sub system_block { + my $IP=shift or die("Can't find IP to block.\n"); + if (ref($cfg->{'whitelist_prepared'}->check(\$IP)) ne 'SCALAR') { + if($cfg->{os} eq 'linux') { + syslog('notice', "Blocking $IP in iptables table $cfg->{chain}."); + system("$cfg->{iptables} -I $cfg->{chain} -p tcp --dport 22 -s $IP -j DROP") == 0 || syslog('notice', "Couldn't add $IP to firewall"); + } + if($cfg->{os} eq 'bsd') { + syslog('notice', "Blocking $IP in pf table $cfg->{chain}."); + system("$cfg->{pfctl} -t $cfg->{chain} -T add $IP") == 0 || syslog('notice', "Couldn't add $IP to firewall"); + } + $timeb{$IP} = time; + # send mail if it is configured + if ($cfg->{send_email} eq '1') { + notify($IP); + } + if ($cfg->{restore_blocked} eq '1') { + log_ip($IP); + } + } +} + +sub setup { + # Check and setup iptables table if missing + if($cfg->{os} eq 'linux') { + system("$cfg->{iptables} -L $cfg->{chain} | grep -qs '$cfg->{chain}'") == 0 || + system("$cfg->{iptables} -N $cfg->{chain}"); + } + # Create IP log file if restore block function is on + if($cfg->{restore_blocked} == 1) { + if( !-e $cfg->{log_ips} ) { + open CLOG,">$cfg->{log_ips}" || syslog('notice',"Can't create $cfg->{log_ips}\n"); + close(CLOG); + } + } +} + +sub flush { + # Flush any existing firewall rules + syslog('notice', "Flushing existing rules in $cfg->{chain}."); + if($cfg->{os} eq 'linux') { + system("$cfg->{iptables} -F $cfg->{chain}") == 0 || syslog('notice', "Unable to flush existing firewalls rules from $cfg->{chain}"); + } elsif($cfg->{os} eq 'bsd') { + system("$cfg->{pfctl} -t $cfg->{chain} -T flush") == 0 || syslog('notice', "Unable to flush existing firewalls rules from $cfg->{chain}"); + } else { + syslog('notice',"No operating system specified in blocksshd.conf configuration file."); + } + # If blocking restore is turned off then clear contents of block + # file + if($cfg->{restore_blocked} == 0) { + if( -e $cfg->{log_ips} && !-z $cfg->{log_ips} ) { + unlink($cfg->{log_ips}); + } + } +} + +sub unblock { + # unblock old IPs based on timeout + $alarmed = 1; + + if($cfg->{os} eq 'linux') { + open IPT, "$cfg->{iptables} -n -L $cfg->{chain} |"; + + while() { + chomp; + next if ($_ !~ /^DROP/); + my ($target, $prot, $opt, $source, $dest, $prot2, $dport) = split(' ', $_); + while ( my ($block_ip, $block_time) = each(%timeb) ) { + if (($block_ip == $source) && ($block_time < time - $cfg->{unblock_timeout})) { + syslog('notice', "Unblocking IP address $block_ip."); + system("$cfg->{iptables} -D $cfg->{chain} -p tcp --dport 22 -s $block_ip -j DROP ") == 0 || syslog('notice', "Couldn't unblock $block_ip from firewall."); + if( -e $cfg->{log_ips} && ((-s $cfg->{log_ips}) > 0)) { + unlog_ip($block_ip); + } + delete $timeb{$block_ip}; + delete $timea{$block_ip}; + delete $count{$block_ip}; + } + } + } + + close IPT; + + } elsif($cfg->{os} eq 'bsd') { + open IPT, "$cfg->{pfctl} -t $cfg->{chain} -T show|" || syslog('error',"Can't open $cfg->{pfctl} for reading."); + + while() { + s/^\s+//; + my $source=$_; + while ( my ($block_ip, $block_time) = each(%timeb) ) { + if (($block_ip == $source) && ($block_time < time - $cfg->{unblock_timeout})) { + syslog('notice', "Unblocking IP address $block_ip."); + system("$cfg->{pfctl} -t $cfg->{chain} -T delete $block_ip") == 0 || syslog('notice', "Couldn't unblock $block_ip from firewall."); + if( $cfg->{restore_blocked} == 1) { + unlog_ip($block_ip); + } + delete $timeb{$block_ip}; + delete $timea{$block_ip}; + delete $count{$block_ip}; + } + } + } + + close IPT; + + } else { + die("No operating system specified in blocksshd.conf configuration file."); + } + + alarm( ($cfg->{unblock_timeout}/2) ); +} + +sub loadwhitelist { + my $rwhiteList = shift @_; # $cfg->{whitelist} + my $sn = Net::Subnets->new; + + if (ref($rwhiteList) eq 'ARRAY') { + my @subnets = map { chomp $_; &trim($_); } @{$rwhiteList}; + @subnets = grep(!/^#|^$/, @subnets); + my $p_sn='^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\/[0-9]{1,2}$'; + my @bad_subnets = grep(!/$p_sn/, @subnets); + + if (scalar(@bad_subnets) > 0) { + die "The whilelist holds invalid subnet entries: " . + join(', ', @bad_subnets) . "\n"; + } + + @subnets = grep(/$p_sn/, @subnets); + $sn->subnets( \@subnets ); + } + + return $sn; +} + +sub trim { + my $str=shift @_; + $str =~ s/^[\s\r\n]+//; + $str =~ s/[\s\r\n]+$//; + return $str; +} + +sub log_ip { + my $IP = shift or syslog('notice',"Can't get ip to log!\n"); + my $inlist=0; + if( -e $cfg->{log_ips} && ((-s $cfg->{log_ips}) > 0)) { + open LOG,"<$cfg->{log_ips}" || syslog('notice',"Can't open $cfg->{log_ips}\n"); + while() { + chomp; + if($_ == $IP) { + $inlist=1; + last; + } + } + close LOG; + } + if($inlist == 0) { + open LOG,">>$cfg->{log_ips}" || syslog('notice',"Can't open $cfg->{log_ips}\n"); + print LOG "$IP\n"; + close LOG; + } +} + +sub unlog_ip { + my $block_ip = shift or die("Can't get IP to unlog!\n"); + my @file; + + if( -e $cfg->{log_ips} && ((-s $cfg->{log_ips}) > 0)) { + + tie @file, 'Tie::File', $cfg->{log_ips}; + @file=grep { $_ ne $block_ip } @file; + untie @file; + + syslog('notice',"Removed unblocked IP address ($block_ip) from log file $cfg->{log_ips}"); + } +} + +sub restore_blocked { + if( -e $cfg->{log_ips} && ((-s $cfg->{log_ips}) > 0)) { + open RLOG,"<$cfg->{log_ips}" || syslog('notice',"Can't open $cfg->{log_ips}\n"); + while() { + chomp; + if(/$work->{ipv4}|$work->{fqdn}/i) { + syslog('notice',"Blocking IP $_ - previously blocked and saved in $cfg->{log_ips}"); + system_block($_); + } + else { + syslog('notice',"Invalid IP address ($_) found in $cfg->{log_ips}"); + } + } + close (RLOG); + } +} + +sub notify { + # send notification emails + my ($IP) = shift or die "Missing IP address!\n"; + + syslog('notice', "Sending notification email to $cfg->{email}"); + my $whois = ''; + if($cfg->{email_whois_lookup} == 1) { + $whois = `$cfg->{whois} $IP|$cfg->{sed} -e 's/\"/\\"/g'`; + } + system("echo \"$work->{hostname}: BlockSSHD blocking $IP\n\n $whois\" | $cfg->{mail} -s 'BlockSSHD blocking notification' $cfg->{email}"); +} + +sub exithandler { + if (-e $cfg->{pid_file}) + { + my $pid=`/bin/cat $cfg->{pid_file}`; + system("/bin/kill -9 $pid"); + unlink($cfg->{pid_file}); + die "BlockSSHD exiting\n"; + } else { + die "BlockSSHD is not running!\n"; + } +} + +sub print_help { + print "BlockSSHD command line options\n"; + print "-d | --daemon | --start Start BlockSSHD in daemon mode\n"; + print "--stop Stop BlockSSHD\n"; + print "-h | --help Print this help text\n"; + print "-v | --version Display version\n"; +} diff --git a/blocksshd.conf b/blocksshd.conf new file mode 100644 index 0000000..a781095 --- /dev/null +++ b/blocksshd.conf @@ -0,0 +1,29 @@ +# vim: syntax=perl + +$cfg = { + os => 'linux', # Target OS - either linux or bsd + chain => 'blocksshd', # Name of iptables or pf chain + logfile => '/var/log/secure', # Log file to monitor + logcheck => '10', # How often to check the log file + max_attempts => '4', # Max number of failures + timeout => '360', # Reset IP count if no activity after time out in seconds + unblock => '1', # Enable unblocking + unblock_timeout => '43200', # Time in seconds after which to unblock a blocked IP address + restore_blocked => '0', # Turn on checking for previously blocked IPs + log_ips => '/etc/blocksshd.list', # Log file for blocked IPs + pid_file => '/var/run/blocksshd.pid', # Location of PID file + send_email => '1', # Enable the sending of email notifications + email => 'root', # Email address to send notifications + mail => '/bin/mail', # Location of mail binary + email_whois_lookup => '1', # enable whois lookup of the blocked ip addres in the sent email + whois => '/usr/bin/whois', # location of the whois binary + sed => '/bin/sed', # location of the sed binary + iptables => '/sbin/iptables', # Location of iptables binary - only for Linux + pfctl => '/sbin/pfctl', # Location of pfctl binary - only for BSD + whitelist => [qw{ + 127.0.0.1/32 + }], # whitelist - list of IPs that will never be blocked - IPs must be specified in the form address/subnet mask +}; + +#leave 1; here! +1; diff --git a/blocksshd.man b/blocksshd.man new file mode 100644 index 0000000..5f5e50c --- /dev/null +++ b/blocksshd.man @@ -0,0 +1,167 @@ +.TH BLOCKSSHD 1 +.\" NAME should be all caps, SECTION should be 1-8, maybe w/ subsection +.\" other parms are allowed: see man(7), man(1) +.rn SH Sh +.de SH +.br +.ne 11 +.Sh "\\$1" +.. +.rn SS Ss +.de SS +.br +.ne 10 +.Ss "\\$1" +.. +.rn TP Tp +.de TP +.br +.ne 9 +.Tp \\$1 +.. +.rn RS Rs +.de RS +.na +.nf +.Rs +.. +.rn RE Re +.de RE +.Re +.fi +.ad +.. +.de Sx +.PP +.ne \\$1 +.RS +.. +.de Ex +.RE +.PP +.. +.SH NAME +blocksshd \- Blocks brute force SSH attacks using iptables. +.SH SYNOPSIS +.B blocksshd +.I [\-d \-\-daemon \-\-start] [\-\-stop] [\-h \-\-help] [\-v \-\-version] +.SH "DESCRIPTION" +This manual page documents the +.BR Blocksshd +script. +.PP +.B Blocksshd +is a Perl script based on BruteForceBlocker v1.2.3 that dynamically adds IPTables rules for Linux and pf firewall rules for BSD that block SSH brute force attacks. It can also detect ProFTPd login failures. +BlockSSHD checks a log file you specify, for example /var/log/secure on a Red +Hat, for SSH login failure messages. If it detects a failure message it +records the source IP address and starts a counter. If messages continue to be +detected from the same source IP address the counter is incremented for each +message. When the counter reaches a user-specified threshold then the script +will add a firewall rule blocking SSH connections from that source IP address. +A user-specified time-out is also defined to trigger a reset of the counter. If +the counter is incremented but has not yet reached the blocking threshold and a +new login failure message arrives then BlockSSHD checks the time-out. If the +last increment of the counter occurred earlier than the current time minus the +time-out period then the counter is reset rather than incremented. The time-out +defaults to 600 seconds (10 minutes). + +The BlockSSHD script can also unblock IP address after a period. This is +enabled in the blocksshd.conf configuration file using the unblock option and +with the period set using the unblock_timeout option. + +The BlockSSHD script can also log the IP addresses blocked to a file and +re-apply these blocked IP addresses when the script is re-started. This allows +you to restore previously blocked IP addresses after a restart or when your +firewall rules are flushed. If you have the unblock function and the re-block +function enabled then when the IP address is unblocked it will also be removed +from the log file. + +.SH PRE-REQUISITES + +BlockSSHD requires the following CPAN modules: + +.br +*) Sys::Syslog - often comes with Perl and may already be installed; +.br +*) Sys::Hostname - often comes with Perl and may already be installed; +.br +*) File::Tail; +.br +*) Net::DNS; +.br +*) Proc::Daemon; +.br +*) Proc::PID::File; +.br +*) Getopt::Long; + +Please install them prior to running BlockSSHD. + +.SH OPTIONS +.TP +.B \-d, \-\-daemon, \-\-start +.IP "Start BlockSSHD in daemon mode" +.TP +.B \-\-stop +.IP "Stop BlockSSHD" +.TP +.B \-h, \-\-help +.IP "Print help text" +.TP +.B \-v, \-\-version +.IP "Print the version number" +.SH EXAMPLES +Start BlockSSHD +.Sx 3 +blocksshd --start +.Ex +Stop BlockSSHD +.Sx 3 +blocksshd --stop +.Ex +.SH Configuration File +There a number of configuration directives located in this file and these are listed below. + +.br +*) os - Specify the operating system BlockSSHd will run under. Use linux for Linux and bsd for BSD +.br +*) pid_file - Location of the BlockSSHd PID file +.br +*) send_email - Enable the sending of email notifications +.br +*) email - Email address to send email notifications to +.br +*) chain - Name of the iptables chain to hold the rules +.br +*) logfile - Log file to monitor for SSH login failure messages +.br +*) logcheck - Interval to check log file in seconds +.br +*) max_attempts - Maximum number of failures before blocking IP +.br +*) timeout - Time without activity after which IP counts are reset in seconds +.br +*) unblock - Enable unblocking functionality +.br +*) unblock_timeout - Period in seconds since blocking that an IP address is unblocked +.br +*) restore_blocked - Unable this option to log IP addresses and then re-block them when BlockSSHd is restarted +.br +*) log_ips - Location of the blocked IP address log file +.br +*) mail - Location of the mail binary used to send emails +.br +*) email_whois_lookup - Enable WHOIS lookup of the blocked IP address to be included in blocking notification +.br +*) whois - Location of the whois binary +.br +*) sed - Location of the sed binary +.br +*) iptables - Location of the iptables binary +.br +*) pfctl - Location of the pfctl binary +.br +*) whitelist - A list of IP addresses that you never want blocked +.SH AUTHORS +This manual page was written by James Turnbull + diff --git a/blocksshd.spec b/blocksshd.spec new file mode 100644 index 0000000..b5ce4d0 --- /dev/null +++ b/blocksshd.spec @@ -0,0 +1,87 @@ +Summary: Blocks brute force SSH attacks using iptables +Name: blocksshd +Version: 1.3 +Release: 1 +License: GPLv2 +Group: Applications/Internet +URL: http://sourceforge.net/projects/blocksshd/ +BuildArch: noarch +Source: http://downloads.sourgeforge.net/blocksshd/blocksshd-%{version}.tar.bz2 +Requires: perl >= 5, chkconfig +AutoReq: no +BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root + +%description +BlockSSHD is defense against SSHd brute force attacks. +The Perl script is based on BruteForceBlocker v1.2.3. +It dynamically adds IPTables rules in response to SSHd +brute force attacks.. + +%prep + +%setup -q + +%build +%{__make} + +%install +%{__rm} -rf "%{buildroot}" + +%{__make} install DESTDIR="%{buildroot}" \ + BINDIR="%{_bindir}" \ + SYSCONFDIR="%{_sysconfdir}" \ + MANDIR="%{_mandir}" \ + INITRDDIR="%{_initrddir}" \ + MANCOMPRESS= \ + INSTALL="%{__install} -p" + +%{__make} init DESTDIR="%{buildroot}" \ + INITRDDIR="%{_initrddir}" + +%clean +%{__rm} -rf "%{buildroot}" + +%files +%defattr(-,root,root,-) +%doc CHANGELOG README CREDITS INSTALL +%{_bindir}/blocksshd +%{_sysconfdir}/blocksshd.conf +%{_mandir}/man1/blocksshd.1* +%{_initrddir}/blocksshd +%config(noreplace) %{_sysconfdir}/blocksshd.conf + +%post +/sbin/chkconfig --add blocksshd + +%preun +/sbin/chkconfig --del blocksshd + +%changelog +* Fri Jun 27 2008 James Turnbull 1.3-1 +- Removed IPv6 support + +* Wed Aug 09 2007 James Turnbull 1.2-1 +- Added new init script +- Changed default blocking list location to /etc +- Documentation fixes + +* Tue May 22 2007 James Turnbull 1.1-2 +- Fixed preun field +- Incremented spec file to 1.1-2 + +* Thu Apr 12 2007 James Turnbull 1.1-1 +- Added -p to spec file +- Changed download source from dl to download +- Changed description +- Added preun option +- Incremented blocksshd.spec to 1.1-1 + +* Tue Sep 26 2006 James Turnbull 0.9 +- Added sysconfdir installation + +* Sun Jul 30 2006 James Turnbull 0.7 +- Added init file support + +* Tue Jul 25 2006 James Turnbull 0.7 +- Spec file for Fedora Extras + -- 2.11.4.GIT