From 0ef66cf800a95592cb108547021905cc0009b913 Mon Sep 17 00:00:00 2001 From: Andrew DeFaria Date: Fri, 21 Aug 2020 11:37:11 -0700 Subject: [PATCH] New bice using IPsets --- bin/bice.pl | 103 +++++++++++++++++++++----------------------------- lib/Logger.pm | 49 ++++++++++++++---------- 2 files changed, 71 insertions(+), 81 deletions(-) diff --git a/bin/bice.pl b/bin/bice.pl index d3b35b7..cd25b1b 100755 --- a/bin/bice.pl +++ b/bin/bice.pl @@ -71,7 +71,18 @@ my $UTC = 'UTC-7'; my $mailhost = $domain; # End customize these variables -my $verbose; +# Current IPset. This is the name of an IP match set (See +# https://kirkkosinski.com/2013/11/mass-blocking-evil-ip-addresses-iptables-ip-sets/) +# Each set can hold up to 65535 entries. We are currently on set 2. +# +# TODO: This code should handle the case where the set fills and we need to go +# to the next set. Something like "ipset list | wc - " and +# if it's > than say 60000, start a new set. +# +# Also, when a new set comes around we need to do: +# $ iptables -A FORWARD -m set --mach-set src -j DROP +my $currIPSet = 'BICE2'; + my $update = 1; my $email = 1; my $hostname = `hostname`; @@ -81,46 +92,19 @@ if ($hostname =~ /(\w*)\./) { $hostname = $1; } # if -sub AddToIPTables (@) { - my (@ips) = @_; - - # We shouldn't need to weed out duplicate but ya never know - my $ipfilename = '/etc/ipblock'; - - my $result = open my $ipfile, '<', $ipfilename; - - my (%ips, @oldips); +sub AddToIPSet($) { + my ($ip) = @_; - if ($result) { - @oldips = <$ipfile>; + my ($status, @output) = Execute "/sbin/ipset add $currIPSet $ip 2>&1"; - close $ipfile if $ipfile; + if ($status) { + return if $output[0] =~ /already added/; - chomp @oldips; + error "Unable to add $ip to ipset $currIPSet" . join ("\n", @output), 1; + } else { + return; } # if - - map { $ips{$_} = 1 } @oldips; - map { $ips{$_} = 1 } <@ips>; - - open $ipfile, '>', "$ipfilename" - or error "Unable to open $ipfilename - $!", 1; - - foreach (sort keys %ips) { - print $ipfile "$_\n"; - } # foreach - - close $ipfile; - - # Recreate the BICE chain - `/sbin/iptables -F BICE`; - `/sbin/iptables -X BICE`; - `/sbin/iptables -N BICE`; - - # Add all new @ips to iptables - `/sbin/iptables -A BICE -s $_ -p tcp -j DROP` foreach (sort keys %ips); - - return; -} # AddToIPTables +} # AddToIPSet # Use whois(1) to get the email addresses of the responsible parties for an IP # address. Note that a hash is used to eliminate duplicates. @@ -139,7 +123,7 @@ sub GetEmailAddresses ($) { my %email_addresses; - foreach (@whois_list) { + for (@whois_list) { my @lines; if ($_ eq "") { @@ -148,7 +132,7 @@ sub GetEmailAddresses ($) { @lines = grep {/.*\@.*/ } `whois -h $_ $ip`; } # if - foreach (@lines) { + for (@lines) { my @fields = split /:/, $_; $_ = $fields [@fields - 1]; @@ -156,23 +140,23 @@ sub GetEmailAddresses ($) { if (/(\S+\@\S[\.\S]+)/) { $email_addresses{$1} = ""; } # if - } # foreach + } # for # Break out of loop if we found email addresses last unless keys %email_addresses; - } # foreach + } # for return keys %email_addresses; } # GetEmailAddresses # Send email to the responsible parties. -sub SendEmail ($$$$$) { - my ($to, $subject, $message, $ip, $violations) = @_; +sub SendEmail ($$$$$$) { + my ($to, $subject, $message, $ip, $attempts, $violationNbr) = @_; if ($email) { - verbose "Reporting $ip ($violations violations) to $to"; + verbose "$violationNbr: Reporting $ip ($attempts violations) to $to"; } else { - verbose "Would have reported $ip ($violations violations) to $to"; + verbose "$violationNbr: Would have reported $ip ($attempts violations) to $to"; return; } # if @@ -243,7 +227,7 @@ sub processLogfile () { flock $writelog, LOCK_EX or error "Unable to flock $security_logfile", 1; - print $writelog $_ foreach @lines; + print $writelog $_ for @lines; flock $writelog, LOCK_UN or error "Unable to unlock $security_logfile", 1; @@ -267,14 +251,15 @@ sub ReportBreakins () { verbose "$nbrViolations sites attempting to violate our perimeter"; } # if - foreach (sort keys %violations) { - my $ip = $_; + my $violations; + for my $ip (sort keys %violations) { my $attempts; - $attempts += @{$violations{$ip}{$_}} foreach (keys %{$violations{$ip}}); + $violations++; + $attempts += @{$violations{$ip}{$_}} for (keys %{$violations{$ip}}); - my @emails = GetEmailAddresses $ip; + my @emails = GetEmailAddresses $ip; unless (@emails) { verbose 'Unable to find any responsible parties for detected breakin ' @@ -310,28 +295,27 @@ attempted and the time of the attempt:

    END # Report users - foreach my $user (sort keys %{$violations{$ip}}) { + for my $user (sort keys %{$violations{$ip}}) { if (@{$violations{$ip}{$user}} == 1) { $message .= "
  1. The user $user attempted access on $violations{$ip}{$user}[0]
  2. "; } else { $message .= "
  3. The user $user attemped access on the following date/times:
  4. "; $message .= "
      "; - $message .= "
    1. $_
    2. " foreach (@{$violations{$ip}{$user}}); + $message .= "
    3. $_
    4. " for (@{$violations{$ip}{$user}}); $message .= "
    "; } # if - } # foreach + } # for $message .= '

Your prompt attention to this matter is expected ' . 'and will be appreciated.

'; - SendEmail $to, $subject, $message, $ip, $attempts; - } # foreach + SendEmail $to, $subject, $message, $ip, $attempts, $violations; + AddToIPSet $ip; + } # for - AddToIPTables keys %violations; + return; } # ReportBreakins ## Main - -# Get options GetOptions ( 'verbose', sub { set_verbose }, 'debug', sub { set_debug }, @@ -341,8 +325,7 @@ GetOptions ( 'file=s', \$security_logfile, ) || Usage; -Usage 'Must specify filename' - unless $security_logfile; +Usage 'Must specify filename' unless $security_logfile; ReportBreakins; diff --git a/lib/Logger.pm b/lib/Logger.pm index e1fe824..a1ad323 100644 --- a/lib/Logger.pm +++ b/lib/Logger.pm @@ -93,7 +93,7 @@ BEGIN { $me =~ s/\.pl$//; } # BEGIN -sub new (;%){ +sub new(;%) { my ($class, %parms) = @_; =pod @@ -166,7 +166,7 @@ Returns: my $append = $parms{append} ? '>>' : '>'; my $logfile; - if (defined $parms{extension}) { + if ($parms{extension}) { $name .= ".$parms{extension}" unless $parms{extension} eq ''; } else { $name .= '.log'; @@ -176,7 +176,7 @@ Returns: or error "Unable to open logfile $path/$name - $!", 1; # Set unbuffered output - $logfile->autoflush (); + $logfile->autoflush(); set_verbose if $ENV{VERBOSE}; set_debug if $ENV{DEBUG}; @@ -192,7 +192,7 @@ Returns: }, $class; # bless } # new -sub append ($) { +sub append($) { my ($self, $filename) = @_; =pod @@ -241,7 +241,7 @@ Returns: return; } # append -sub name () { +sub name() { my ($self) = @_; =pod @@ -279,7 +279,7 @@ Returns: return $self->{name}; } # name -sub fullname () { +sub fullname() { my ($self) = @_; =pod @@ -317,7 +317,7 @@ Returns: return "$self->{path}/$self->{name}"; } # fullname -sub msg ($;$) { +sub msg($;$) { my ($self, $msg, $nolinefeed) = @_; =pod @@ -366,7 +366,7 @@ Returns: return; } # msg -sub disp ($;$) { +sub disp($;$) { my ($self, $msg, $nolinefeed) = @_; =pod @@ -415,7 +415,7 @@ Returns: return; } # disp -sub incrementErr (;$) { +sub incrementErr(;$) { my ($self, $increment) = @_; =pod @@ -459,7 +459,7 @@ Returns: return; } # incrementErr -sub err ($;$) { +sub err($;$) { my ($self, $msg, $errno) = @_; =pod @@ -522,7 +522,7 @@ Returns: return; } # err -sub maillog (%) { +sub maillog(%) { my ($self, %parms) = @_; =pod @@ -568,8 +568,7 @@ Returns: my $footing = $parms{footing}; my $mode = $parms{mode}; - $mode = "plain" - unless $mode; + $mode = "plain" unless $mode; my $log_filename = "$self->{path}/$self->{name}"; @@ -584,7 +583,7 @@ Returns: . $footing; } # if - mail ( + mail( from => $from, to => $to, cc => $cc, @@ -601,7 +600,7 @@ Returns: return; } # maillog -sub log { +sub log($;$) { my ($self, $msg, $nolinefeed) = @_; =pod @@ -651,7 +650,7 @@ Returns: return; } # log -sub logcmd ($) { +sub logcmd($) { my ($self, $cmd) = @_; =pod @@ -710,7 +709,7 @@ Returns: return ($?, @output); } # logcmd -sub loglines () { +sub loglines() { my ($self) = @_; =pod @@ -748,7 +747,7 @@ Returns: return ReadFile "$self->{path}/$self->{name}"; } # loglines -sub warn ($;$) { +sub warn($;$) { my ($self, $msg, $warnno) = @_; =pod @@ -805,7 +804,7 @@ Returns: return; } # warn -sub errors () { +sub errors() { my ($self) = @_; =pod @@ -843,7 +842,15 @@ Returns: return $self->{errors}; } # errors -sub warnings () { +sub dbug($) { + my ($self, $msg) = @_; + + $self->log("DEBUG: $msg") unless get_debug; + + return; +} # dbug + +sub warnings() { my ($self) = @_; =pod @@ -881,7 +888,7 @@ Returns: return $self->{warnings}; } # warnings -sub DESTROY () { +sub DESTROY() { my ($self) = @_; close ($self->{handle}); -- 2.17.1