summaryrefslogtreecommitdiff
path: root/network/sshblock/sshblock.pl.tpl
diff options
context:
space:
mode:
Diffstat (limited to 'network/sshblock/sshblock.pl.tpl')
-rw-r--r--network/sshblock/sshblock.pl.tpl230
1 files changed, 230 insertions, 0 deletions
diff --git a/network/sshblock/sshblock.pl.tpl b/network/sshblock/sshblock.pl.tpl
new file mode 100644
index 0000000000..bc2f166d8e
--- /dev/null
+++ b/network/sshblock/sshblock.pl.tpl
@@ -0,0 +1,230 @@
+#!/usr/bin/perl -wT
+
+# Copyright 2009 Kagan D. MacTane
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require 5.004;
+use strict;
+use Sys::Syslog;
+
+# -------------------------------------------------------------------
+
+# This is the whitelist. Any IP address that's listed in this array will
+# never be blocked.
+my @never_block_these = qw(127.0.0.1 192.168.1.1);
+
+# Where should SSHblock track which addresses it's blocked? This will be
+# a text file with tab-delimited fields:
+# IP address - # of times blocked # timestamp of last block
+my $history_file = '/etc/ssh/block-history';
+
+# If email notifications
+# Set to an empty string to suppress email notifications.
+my $send_email = '';
+
+# Only send email if IP has been blocked at least this many times.
+# E.g., at $email_level = 3, email will only be sent if an IP is
+# blocked for the 3rd (or greater) time.
+my $email_level = 2;
+
+# This is just to keep SSHblock from having to run `hostname` every time
+# it wants to notify you that it's blocked something. This text is only
+# used in the notification email, and can be safely altered.
+my $myhostname = 'localhost';
+
+my $Syslog_Level = 'info';
+my $Syslog_Facility = 'user';
+my $Syslog_Options = '';
+
+my $VERSION = 0.5;
+
+# -------------------------------------------------------------------
+
+$ENV{PATH} = '/sbin:/usr/sbin:/usr/bin:/bin';
+
+unless (scalar @ARGV) {
+ LogMessage("Called with no arguments; aborting.");
+ print "Usage: $0 ip_addr\n\"perldoc $0\" for full man page\n";
+ exit 1;
+}
+my $ip_addr = shift;
+
+# Ensure we were passed a valid IP address as first argument
+if ($ip_addr =~ /^((\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3}))$/) {
+ $ip_addr = $1;
+} else {
+ LogMessage("first arg not IP address: $ip_addr");
+ exit 1;
+}
+if ($2 > 255 || $3 > 255 || $4 > 255 || $5 > 255) {
+ exit 1;
+}
+
+# Abort if you're not running as root
+if ($>) {
+ print "Only root can run this program.\n";
+ exit 1;
+}
+
+# And don't waste processor cycles if the IP's already blocked
+exit 0 if (is_blocked($ip_addr));
+
+if (grep { $_ eq $ip_addr } @never_block_these) {
+ LogMessage("Not bothering to block whitelisted IP $ip_addr");
+ exit 3;
+}
+
+# block command:
+`iptables -A INPUT -p tcp -s $ip_addr --dport 22 --syn -j DROP`;
+
+if ($?) {
+ LogMessage("Failed to block IP $ip_addr; error $?: $!");
+ exit $?;
+}
+
+# Assuming that succeeded, add a record to the history file.
+
+my @history;
+open FH, "$history_file" || exit 7;
+@history = <FH>;
+close FH;
+
+my ($prev) = grep /^$ip_addr\s/, @history;
+my $how_many;
+
+if ($prev) {
+ my @prev = split /\s+/, $prev;
+ $prev[1]++;
+ splice(@prev, 2, 1, time());
+ map { if ($_ =~ /^$ip_addr\s/) { $_ = join("\t", @prev)."\n"; } } @history;
+ $how_many = ordinal($prev[1]);
+} else {
+ push(@history, join("\t", $ip_addr, 1, time()), "\n");
+ $how_many = ordinal(1);
+}
+
+open FH, ">$history_file" || exit 8;
+print FH @history;
+close FH;
+
+LogMessage("Blocked IP $ip_addr for $how_many time");
+
+my $num = $how_many;
+$num =~ s/\D//g;
+
+
+if ($send_email && $num >= $email_level) {
+ open MAIL, "|[[SENDMAIL_PATH]] -t";
+ print MAIL "From: sshblock <noreply\@$myhostname>\nTo: $send_email\nSubject: SSH Block: $ip_addr\n\nBlocked IP address $ip_addr for $how_many time.\n\n";
+ close MAIL;
+}
+
+exit 0;
+
+sub is_blocked {
+ my $ip_addr = shift;
+ return grep /^$ip_addr/, split /\n/, `iptables -nL | grep DROP | grep 'dpt:22' | grep '0x17/0x02' | awk '{print \$4}'`;
+}
+
+sub ordinal {
+ my $num = shift;
+ if (length($num) > 1 && substr($num, -2, 1) == 1) {
+ return $num . 'th';
+ }
+ if (substr($num, -1) == 1) {
+ return $num . 'st';
+ } elsif (substr($num, -1) == 2) {
+ return $num . 'nd';
+ } elsif (substr($num, -1) == 3) {
+ return $num . 'rd';
+ } else {
+ return $num . 'th';
+ }
+}
+
+
+
+sub LogMessage {
+ my $format = shift;
+ openlog('sshblock', $Syslog_Options, $Syslog_Facility);
+ syslog($Syslog_Level, $format, @_);
+ closelog();
+}
+
+
+__END__
+
+=head1 NAME
+
+sshblock.pl - SSH dictionary attack blocker
+
+=head1 SYNOPSIS
+
+B<sshblock.pl> I<ip_address>
+
+=head1 DESCRIPTION
+
+This is part of the SSHblock system; the B<sshblock.pl> executable is responsible for blocking IP addresses from access to port 22. B<sshblock.pl> does this by adding a firewall rule to B<iptables>, which must be present on the system. Because of this, SSHblock must be run as root.
+
+B<sshblock.pl> only blocks addresses; unblocking them is the responsibility of B<sshunblock.pl>, which should be run as an hourly cron(8) job.
+
+=head1 INVOCATION
+
+B<sshblock.pl> is intended to be called by swatch(1) or a similar automated process. You I<can> call it from the command line, passing it a single IP address to block, and this won't actualyl cause any problems, but it will only take one argument per invocation.
+
+By default, SSHblock logs its activity to syslog(8), using the "user" facility at level "info".
+
+=head1 CONFIGURATION
+
+SSHblock can be configured by changing the following options in the program's source code:
+
+=over
+
+=item B<@never_block_these>
+
+This array holds SSHblock's whitelist. Any IP address found in this array will never be blocked.
+
+=item B<$history_file>
+
+Where SSHblock should store its history file. This file keeps a record of all IP addresses SSHblock has ever blocked, one per line. Each line consists of three tab-delimited fields: the IP address; the total number of times it's been blocked; and the timestamp it was last blocked at.
+
+=item B<$email_level>
+
+If this number is nonzero, then SSHblock will send an email to the address specified in B<$send_email> whenever an address is blocked for the Nth or greater time. For example, if $email_level is 3, SSHblock will remain silent when it blocks an address for the 1st or 2nd time, but send email on the 3rd time.
+
+=back
+
+=head1 FILES
+
+=over
+
+=item F</etc/ssh/block-history>
+
+=back
+
+
+=head1 BUGS
+
+Please let me know if you find any.
+
+=head1 AUTHOR
+
+Kagan D. MacTane (kai@mactane.org)
+
+=head1 SEE ALSO
+
+sshunblock(8), iptables(8), L<http://sourceforge.net/projects/swatch/>
+
+=cut
+