network/sshblock: Added (an SSH dictionary-attack blocker).
Signed-off-by: Matteo Bernardini <ponce@slackbuilds.org>
This commit is contained in:
parent
de6041627c
commit
8d0150f686
|
@ -0,0 +1,7 @@
|
|||
SSHblock is intended to dynamically and automatically stop SSH-based
|
||||
dictionary attacks by blocking any IP address that fails an SSH
|
||||
login too many times too quickly, and automatically unblocks it
|
||||
after a while.
|
||||
|
||||
You may change the pre-defined configuration of SSHblock in
|
||||
sshblock.pl.tpl (whitelist IP, email, and hostname).
|
|
@ -0,0 +1,14 @@
|
|||
config() {
|
||||
NEW="$1"
|
||||
OLD="$(dirname $NEW)/$(basename $NEW .new)"
|
||||
# If there's no config file by that name, mv it over:
|
||||
if [ ! -r $OLD ]; then
|
||||
mv $NEW $OLD
|
||||
elif [ "$(cat $OLD | md5sum)" = "$(cat $NEW | md5sum)" ]; then
|
||||
# toss the redundant copy
|
||||
rm $NEW
|
||||
fi
|
||||
# Otherwise, we leave the .new copy for the admin to consider...
|
||||
}
|
||||
|
||||
config etc/rc.d/rc.sshblock.new
|
|
@ -0,0 +1,56 @@
|
|||
#!/bin/bash
|
||||
|
||||
if [ ! $UID ]; then
|
||||
echo "You must be root to use SSHblock."
|
||||
exit 1;
|
||||
fi
|
||||
|
||||
case "$1" in
|
||||
'start')
|
||||
swatch -c /etc/swatch/sshblock -t /var/log/messages &> /dev/null &
|
||||
if [ ! `ls /etc/cron.hourly | grep sshunblock` ]; then
|
||||
ln -s /usr/sbin/sshunblock.pl /etc/cron.hourly
|
||||
fi
|
||||
;;
|
||||
'stop')
|
||||
pid=`ps auxwww | grep swatch | grep -v grep | grep sshblock | awk '{print $2}'`
|
||||
kill $pid
|
||||
;;
|
||||
'clear')
|
||||
for ip in `iptables -nL INPUT | tail +3 | grep DROP | grep dpt:22 | awk '{print $4}'`; do
|
||||
iptables -D INPUT -p tcp -s $ip --dport 22 --syn -j DROP
|
||||
done
|
||||
;;
|
||||
'list')
|
||||
echo "Blocked IP addresses:"
|
||||
iptables -nL INPUT | tail +3 | grep DROP | grep dpt:22 | awk '{print $4}'
|
||||
;;
|
||||
'status')
|
||||
blocking=`ps auxwww | grep swatch | grep -v grep | grep sshblock | wc -l`
|
||||
blocked=`iptables -nL INPUT | tail +3 | grep DROP | grep dpt:22 | wc -l`
|
||||
unblocking=`ls -l /etc/cron.hourly | grep sshunblock | wc -l`
|
||||
if [ $blocked -eq 1 ]; then
|
||||
pl=''
|
||||
verb='is'
|
||||
else
|
||||
pl='es'
|
||||
verb='are'
|
||||
fi
|
||||
if [ $blocking -gt 0 ]; then
|
||||
echo "SSHblock is active"
|
||||
else
|
||||
echo "SSHblock is not running"
|
||||
fi
|
||||
echo "There $verb currently $blocked address$pl blocked."
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $0 [start|stop|clear|status|list]"
|
||||
echo " "
|
||||
echo "start: Start SSHblock system"
|
||||
echo "stop: Stop blocking new IPs; old ones will still expire at the usual rate"
|
||||
echo "clear: Clear all blocked addresses"
|
||||
echo "status: Report whether SSHblock is running, how many IPs are blocked"
|
||||
echo "list: List all blocked IP addresses"
|
||||
exit
|
||||
;;
|
||||
esac
|
|
@ -0,0 +1,19 @@
|
|||
# HOW TO EDIT THIS FILE:
|
||||
# The "handy ruler" below makes it easier to edit a package description.
|
||||
# Line up the first '|' above the ':' following the base package name, and
|
||||
# the '|' on the right side marks the last column you can put a character in.
|
||||
# You must make exactly 11 lines for the formatting to be correct. It's also
|
||||
# customary to leave one space after the ':' except on otherwise blank lines.
|
||||
|
||||
|-----handy-ruler------------------------------------------------------|
|
||||
sshblock: sshblock (an SSH Dictionary-Attack Blocker)
|
||||
sshblock:
|
||||
sshblock: SSHblock is intended to dynamically and automatically stop SSH-based
|
||||
sshblock: dictionary attacks by blocking any IP address that fails an SSH
|
||||
sshblock: login too many times too quickly, and automatically unblocks it
|
||||
sshblock: after a while.
|
||||
sshblock:
|
||||
sshblock: Project Website: http://kagan.mactane.org/software/sshblock/
|
||||
sshblock:
|
||||
sshblock:
|
||||
sshblock:
|
|
@ -0,0 +1,95 @@
|
|||
#!/bin/sh
|
||||
|
||||
# Slackware build script for sshblock
|
||||
|
||||
# Copyright 2012 Willy Sudiarto Raharjo <willysr@slackware-id.org>
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use of this script, with or without modification, is
|
||||
# permitted provided that the following conditions are met:
|
||||
#
|
||||
# 1. Redistributions of this script must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR IMPLIED
|
||||
# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
# EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
# OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
PRGNAM=sshblock
|
||||
VERSION=${VERSION:-0.5}
|
||||
BUILD=${BUILD:-1}
|
||||
TAG=${TAG:-_SBo}
|
||||
|
||||
if [ -z "$ARCH" ]; then
|
||||
case "$( uname -m )" in
|
||||
i?86) ARCH=i486 ;;
|
||||
arm*) ARCH=arm ;;
|
||||
*) ARCH=$( uname -m ) ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
CWD=$(pwd)
|
||||
TMP=${TMP:-/tmp/SBo}
|
||||
PKG=$TMP/package-$PRGNAM
|
||||
OUTPUT=${OUTPUT:-/tmp}
|
||||
|
||||
if [ "$ARCH" = "i486" ]; then
|
||||
SLKCFLAGS="-O2 -march=i486 -mtune=i686"
|
||||
LIBDIRSUFFIX=""
|
||||
elif [ "$ARCH" = "i686" ]; then
|
||||
SLKCFLAGS="-O2 -march=i686 -mtune=i686"
|
||||
LIBDIRSUFFIX=""
|
||||
elif [ "$ARCH" = "x86_64" ]; then
|
||||
SLKCFLAGS="-O2 -fPIC"
|
||||
LIBDIRSUFFIX="64"
|
||||
else
|
||||
SLKCFLAGS="-O2"
|
||||
LIBDIRSUFFIX=""
|
||||
fi
|
||||
|
||||
set -e
|
||||
|
||||
rm -rf $PKG
|
||||
mkdir -p $TMP $PKG $OUTPUT
|
||||
cd $TMP
|
||||
rm -rf $PRGNAM-$VERSION
|
||||
tar xvf $CWD/$PRGNAM-$VERSION.tar.bz2
|
||||
cd $PRGNAM-$VERSION
|
||||
chown -R root:root .
|
||||
find . \
|
||||
\( -perm 777 -o -perm 775 -o -perm 711 -o -perm 555 -o -perm 511 \) \
|
||||
-exec chmod 755 {} \; -o \
|
||||
\( -perm 666 -o -perm 664 -o -perm 600 -o -perm 444 -o -perm 440 -o -perm 400 \) \
|
||||
-exec chmod 644 {} \;
|
||||
|
||||
mkdir -p $PKG/usr/sbin $PKG/etc/ssh $PKG/etc/swatch $PKG/etc/rc.d $PKG/etc/cron.hourly
|
||||
touch $PKG/etc/ssh/block-history
|
||||
|
||||
install -m 0644 $CWD/rc.sshblock $PKG/etc/rc.d/rc.sshblock.new
|
||||
install -m 0755 $CWD/sshblock.pl.tpl $PKG/usr/sbin/sshblock.pl
|
||||
install -m 0755 $CWD/sshunblock.pl.tpl $PKG/usr/sbin/sshunblock.pl
|
||||
install -m 0755 $CWD/sshblock.tpl $PKG/etc/swatch/sshblock
|
||||
install -m 0755 $CWD/sshunblock.pl.tpl $PKG/etc/cron.hourly/sshunblock
|
||||
|
||||
find $PKG -print0 | xargs -0 file | grep -e "executable" -e "shared object" | grep ELF \
|
||||
| cut -f 1 -d : | xargs strip --strip-unneeded 2> /dev/null || true
|
||||
|
||||
mkdir -p $PKG/usr/doc/$PRGNAM-$VERSION
|
||||
cp -a \
|
||||
README \
|
||||
$PKG/usr/doc/$PRGNAM-$VERSION
|
||||
cat $CWD/$PRGNAM.SlackBuild > $PKG/usr/doc/$PRGNAM-$VERSION/$PRGNAM.SlackBuild
|
||||
|
||||
mkdir -p $PKG/install
|
||||
cat $CWD/slack-desc > $PKG/install/slack-desc
|
||||
cat $CWD/doinst.sh > $PKG/install/doinst.sh
|
||||
|
||||
cd $PKG
|
||||
/sbin/makepkg -l y -c n $OUTPUT/$PRGNAM-$VERSION-$ARCH-$BUILD$TAG.${PKGTYPE:-tgz}
|
|
@ -0,0 +1,10 @@
|
|||
PRGNAM="sshblock"
|
||||
VERSION="0.5"
|
||||
HOMEPAGE="http://kagan.mactane.org/software/sshblock/"
|
||||
DOWNLOAD="http://kagan.mactane.org/software/libraries/download/sshblock-0.5.tar.bz2"
|
||||
MD5SUM="dbfaee5f45296de2f9a22d5fe79e7332"
|
||||
DOWNLOAD_x86_64=""
|
||||
MD5SUM_x86_64=""
|
||||
REQUIRES="swatch"
|
||||
MAINTAINER="Willy Sudiarto Raharjo"
|
||||
EMAIL="willysr@slackware-id.org"
|
|
@ -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
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
watchfor /Failed password for invalid user \w+ from ([\d\.]+) port/
|
||||
exec "/usr/sbin/sshblock.pl $1"
|
||||
threshold track_by=$1, type=threshold, count=3, seconds=90
|
||||
|
||||
watchfor /Failed password for root from ([\d\.]+) port/
|
||||
exec "/usr/sbin/sshblock.pl $1"
|
||||
threshold track_by=$1, type=threshold, count=3, seconds=30
|
||||
|
|
@ -0,0 +1,179 @@
|
|||
#!/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 option *MUST* match the corresponding value in sshblock.pl!
|
||||
my $history_file = '/etc/ssh/block-history';
|
||||
|
||||
|
||||
# Blocking duration formula: If blocking for the Nth time, and
|
||||
# b = $base and m = $mult, then block for:
|
||||
#
|
||||
# T = m * ( b ^ (N-1) )
|
||||
#
|
||||
# Time T is expressed in hours.
|
||||
my $base = 4;
|
||||
my $mult = 3;
|
||||
|
||||
my $Syslog_Level = 'info';
|
||||
my $Syslog_Facility = 'user';
|
||||
my $Syslog_Options = '';
|
||||
|
||||
my $VERSION = 0.5;
|
||||
|
||||
$ENV{'PATH'} = '/bin:/usr/bin:/usr/sbin:/sbin';
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
|
||||
# Abort if you're not running as root
|
||||
if ($>) {
|
||||
print "Only root can run this program.\n";
|
||||
exit 1;
|
||||
}
|
||||
|
||||
my $now = time();
|
||||
|
||||
my @history;
|
||||
open FH, "$history_file" || exit 7;
|
||||
@history = <FH>;
|
||||
close FH;
|
||||
|
||||
my @input_chain = `iptables -nL INPUT | tail +3`;
|
||||
|
||||
# Your iptables output needs to look like:
|
||||
# DROP tcp -- 1.2.3.4 0.0.0.0/0 tcp dpt:22 flags:0x17/0x02
|
||||
|
||||
if (scalar @input_chain > 0) {
|
||||
LogMessage("Checking ".scalar @input_chain." blocked IPs against ".scalar @history." block-history entries.");
|
||||
}
|
||||
|
||||
foreach my $item (@input_chain) {
|
||||
my @stats = split(/\s+/, $item);
|
||||
next unless ($stats[0] eq 'DROP');
|
||||
next unless ($stats[1] eq 'tcp');
|
||||
next unless ($stats[6] eq 'dpt:22');
|
||||
next unless ($stats[7] eq 'flags:0x17/0x02');
|
||||
my $curr_ip = $stats[3];
|
||||
|
||||
if ($curr_ip =~ /^(\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b)$/) {
|
||||
$curr_ip = $1;
|
||||
} else {
|
||||
LogMessage("Invalid IP address in output from iptables?!?");
|
||||
next;
|
||||
}
|
||||
for ( my $i = 0; $i < scalar @history; $i++ ) {
|
||||
my $hist_ip = (split(/\t/, $history[$i]))[0];
|
||||
if ((split(/\t/, $history[$i]))[0] eq $curr_ip) {
|
||||
my ($times_blocked, $blocked_since) = (split(/\t/, $history[$i]))[1,2];
|
||||
my $duration = $now - $blocked_since;
|
||||
$duration /= 3600;
|
||||
$duration = sprintf("%.2f", $duration);
|
||||
my $block_for = $mult * ($base ** ($times_blocked - 1));
|
||||
if ($duration > $block_for) {
|
||||
`iptables -D INPUT -p tcp -s $curr_ip --dport 22 --syn -j DROP`;
|
||||
if ($?) {
|
||||
LogMessage("Couldn't unblock IP $curr_ip (now blocked for $duration hours)! Error $?: $!");
|
||||
} else {
|
||||
LogMessage("Unblocked IP $curr_ip after $duration hours.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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 LogMessage {
|
||||
my $format = shift;
|
||||
openlog('sshblock', $Syslog_Options, $Syslog_Facility);
|
||||
syslog($Syslog_Level, $format, @_);
|
||||
closelog();
|
||||
}
|
||||
|
||||
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
sshunblock.pl - SSH dictionary attack (un)blocker
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
B<sshblock.pl>
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
This is part of the SSHblock system; the B<sshunblock.pl> executable is responsible for unblocking blocked IP addresses after a suitable length of time has passed. It does this by removing the B<iptables> firewall rules created by B<sshblock.pl>. In order to use iptables, sshunblock.pl must be run as root.
|
||||
|
||||
=head1 INVOCATION
|
||||
|
||||
B<sshunblock.pl> is intended to be called as an hourly cron(8) job. Calling it more or less frequently will not interfere with its operation.
|
||||
|
||||
By default, SSHblock logs its activity to syslog(8), using the "user" facility at level "info".
|
||||
|
||||
=head1 CONFIGURATION
|
||||
|
||||
F<sshunblock.pl> can be configured by changing the following options in the program's source code.
|
||||
|
||||
=over
|
||||
|
||||
=item B<$history_file>
|
||||
|
||||
Note that B<this option MUST match the value in sshblock.pl!> This is 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<$base, $mult>
|
||||
|
||||
These control the behavior of SSHblock's exponential increase algorithm. By tweaking these, you can make SSHblock block attacking IP addresses for longer or shorter periods of 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
|
||||
|
||||
sshblock(8), iptables(8), L<http://sourceforge.net/projects/swatch/>
|
||||
|
||||
=cut
|
||||
|
Loading…
Reference in New Issue