changeset 63:60f59936fabb

good authentication prevents ip blocking for awhile
author Carl Byington <carl@five-ten-sg.com>
date Sat, 19 Dec 2015 10:12:24 -0800
parents c30df5975c49
children 4b147494fc64
files ChangeLog NEWS configure.in src/syslogconfig.cpp src/syslogconfig.h src/tokenizer.cpp syslog2iptables.conf.top syslog2iptables.spec.in xml/syslog2iptables.in
diffstat 9 files changed, 191 insertions(+), 125 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Sat Oct 04 10:21:40 2014 -0700
+++ b/ChangeLog	Sat Dec 19 10:12:24 2015 -0800
@@ -1,3 +1,6 @@
+1.16 2015-12-19
+     good authentication prevents ip blocking for awhile
+
 1.15 2014-10-02
      add script to build syslog2iptables.conf
 
--- a/NEWS	Sat Oct 04 10:21:40 2014 -0700
+++ b/NEWS	Sat Dec 19 10:12:24 2015 -0800
@@ -1,3 +1,4 @@
+1.16 2015-12-19 good authentication prevents ip blocking for awhile
 1.15 2014-10-02 add script to build syslog2iptables.conf
 1.14 2014-06-10 Add exponential increase in penalty for repeat offenders.
 1.13 2009-01-25 Document multiple contexts.
--- a/configure.in	Sat Oct 04 10:21:40 2014 -0700
+++ b/configure.in	Sat Dec 19 10:12:24 2015 -0800
@@ -1,6 +1,6 @@
 
 AC_PREREQ(2.59)
-AC_INIT(syslog2iptables,1.15,carl@five-ten-sg.com)
+AC_INIT(syslog2iptables,1.16,carl@five-ten-sg.com)
 AC_CONFIG_SRCDIR([config.h.in])
 AC_CONFIG_HEADER([config.h])
 
--- a/src/syslogconfig.cpp	Sat Oct 04 10:21:40 2014 -0700
+++ b/src/syslogconfig.cpp	Sat Dec 19 10:12:24 2015 -0800
@@ -64,6 +64,7 @@
 
 void IPR::add(int ip, int amount, CONTEXT &con, const char *file_name, int pattern_index, const char *message) {
     if (con.looking(ip)) {
+        if (amount > 0) {
         ip_buckets::iterator j = repeat_offenders.find(ip);
         int scale = (j == repeat_offenders.end()) ? 1 : (*j).second.count;
         amount *= scale;
@@ -81,7 +82,9 @@
         }
         else {
             bucket &b = (*i).second;
-            if (b.count < (INT_MAX-amount)) {
+                if ((b.count >= 0) && (b.count < 2600000)) {
+                    // good authentication (count<0) prevents blocking
+                    // not much point in blocking for more than a month
                 b.count += amount;
                 if ((!b.blocked) && (con.get_threshold() <= b.count)) {
                     b.blocked = true;
@@ -91,6 +94,37 @@
             }
         }
     }
+
+        else {  // amount < 0
+            char buf[maxlen];
+            in_addr ad;
+            ad.s_addr = htonl(ip);
+            snprintf(buf, maxlen, "%s for %s", message, inet_ntoa(ad));
+            my_syslog(buf);
+
+            ip_buckets::iterator j = repeat_offenders.find(ip);
+            if (j != repeat_offenders.end()) {
+                repeat_offenders.erase(j++);
+                snprintf(buf, maxlen, "removing %s from repeat offenders", inet_ntoa(ad));
+                my_syslog(buf);
+            }
+            ip_buckets::iterator i = violations.find(ip);
+            if (i == violations.end()) {
+                bucket b;
+                b.count = amount;
+                b.blocked = false;
+                violations[ip] = b;
+            }
+            else {
+                bucket &b = (*i).second;
+                b.count = amount;
+                if (b.blocked) {
+                    update(ip, false, 0, NULL, 0, NULL);
+                    changed(con, ip, false);
+                }
+            }
+        }
+    }
 }
 
 
@@ -98,6 +132,14 @@
     for (ip_buckets::iterator i=violations.begin(); i!=violations.end(); ) {
         int    ip = (*i).first;
         bucket &b = (*i).second;
+        if (b.count < 0) {
+            if (b.count >= -amount) violations.erase(i++);
+            else {
+                b.count += amount;
+                i++;
+            }
+        }
+        else {
         if (b.count <= amount) {
             if (b.blocked) {
                 update(ip, false, 0, NULL, 0, NULL);
@@ -110,6 +152,7 @@
             i++;
         }
     }
+    }
     daily_timer -= amount;
     if (daily_timer < 0) {
         daily_timer = 86400;
--- a/src/syslogconfig.h	Sat Oct 04 10:21:40 2014 -0700
+++ b/src/syslogconfig.h	Sat Dec 19 10:12:24 2015 -0800
@@ -21,7 +21,8 @@
     const char *    pattern;    // owned by the string table
     regex_t         re;
     int             index;      // zero based substring of the regex match that contains the ip address or hostname
-    int             amount;     // count to add to the ip address leaky bucket
+    int             amount;     // if positive, count to add to the ip address leaky bucket;
+                                //   if negative count to set into the ip address leaky bucket
     const char *    message;    // for logging, owned by the string table
 public:
     ~PATTERN();
--- a/src/tokenizer.cpp	Sat Oct 04 10:21:40 2014 -0700
+++ b/src/tokenizer.cpp	Sat Dec 19 10:12:24 2015 -0800
@@ -78,7 +78,7 @@
     { s_single,   s_term,     s_string,   s_single,  s_eol,      },  // 0x2A *
     { s_single,   s_token,    s_string,   s_single,  s_eol,      },  // 0x2B +
     { s_single,   s_term,     s_string,   s_single,  s_eol,      },  // 0x2C ,
-    { s_single,   s_token,    s_string,   s_single,  s_eol,      },  // 0x2D -
+    { s_token,    s_token,    s_string,   s_token,   s_eol,      },  // 0x2D -  // like digits
     { s_single,   s_token,    s_string,   s_single,  s_eol,      },  // 0x2E .
     { s_slash,    s_term,     s_string,   s_slash,   s_eol,      },  // 0x2F /
     { s_token,    s_token,    s_string,   s_token,   s_eol,      },  // 0x30 0
--- a/syslog2iptables.conf.top	Sat Oct 04 10:21:40 2014 -0700
+++ b/syslog2iptables.conf.top	Sat Dec 19 10:12:24 2015 -0800
@@ -9,6 +9,11 @@
     };
 
     file "/var/log/secure" {
+        pattern "manual unblock (.*)" {
+            index 1;    // zero based
+            bucket -5000;
+            message "manual unblock";
+        };
         pattern "sshd.*Failed password .* from ::ffff:(.*) port" {
             index 1;    // zero based
             bucket 400;
@@ -46,12 +51,12 @@
         };
         pattern "dovecot.* authentication failure; .* rhost=::ffff:(.*) " {
             index 1;    // zero based
-            bucket 400;
+            bucket 100;
             message "dovecot failed password";
         };
         pattern "dovecot.* authentication failure; .* rhost=(.*) " {
             index 1;    // zero based
-            bucket 400;
+            bucket 100;
             message "dovecot failed password";
         };
     };
@@ -59,7 +64,7 @@
     file "/var/log/messages" {
         pattern "dovecot.* authentication failure; .* rhost=(.*) " {
             index 1;    // zero based
-            bucket 400;
+            bucket 100;
             message "dovecot failed password";
         };
         pattern "kernel.*local-net-to.*SRC=(.*) DST=.*DPT=" {
@@ -75,19 +80,14 @@
     };
 
     file "/var/log/maillog" {
-        pattern "\]: .* \[(.*)\] did not issue MAIL" {
-            index 1;    // zero based
-            bucket 200;
-            message "sendmail banner probe";
-        };
         pattern "lost input channel from.* \[(.*)\] .* after (mail|rcpt|auth)" {
             index 1;    // zero based
-            bucket 200;
+            bucket 100;
             message "sendmail spammer dropping connection";
         };
-        pattern " \[(.*)\]: possible SMTP attack" {
+        pattern " \[(.*)\].* possible SMTP attack" {
             index 1;    // zero based
-            bucket 600;
+            bucket 100;
             message "sendmail authentication attack";
         };
         pattern "rejecting commands from.* \[(.*)\] due to pre-greeting traffic" {
@@ -97,17 +97,22 @@
         };
         pattern "authentication failure: checkpass failed,.*\[(.*)\]" {
             index 1;    // zero based
-            bucket 600;
-            message "sendmail authentication attack";
+            bucket 100;
+            message "sendmail authentication failed";
         };
         pattern "dovecot.*Aborted login.*rip=(.*)," {
             index 1;    // zero based
             bucket 100;
             message "dovecot failed password";
         };
-        pattern "dovecot: pop3-login: Disconnected: Shutting down.*rip=(.*)," {
+        pattern "dovecot.*Login: .* rip=(.*)," {
             index 1;    // zero based
-            bucket 100;
-            message "dovecot failed password";
+            bucket -5000;
+            message "dovecot good authentication";
+        };
+        pattern "sendmail.*AUTH=server, .*\[(.*)\]," {
+            index 1;    // zero based
+            bucket -5000;
+            message "sendmail good authentication";
         };
     };
--- a/syslog2iptables.spec.in	Sat Oct 04 10:21:40 2014 -0700
+++ b/syslog2iptables.spec.in	Sat Dec 19 10:12:24 2015 -0800
@@ -72,6 +72,9 @@
 
 
 %changelog
+* Sat Dec 19 2015 Carl Byington <carl@five-ten-sg.com> - 1.16-1
+- good authentication prevents ip blocking for awhile
+
 * Thu Oct 02 2014 Carl Byington <carl@five-ten-sg.com> - 1.15-1
 - add script to build syslog2iptables.conf
 
--- a/xml/syslog2iptables.in	Sat Oct 04 10:21:40 2014 -0700
+++ b/xml/syslog2iptables.in	Sat Dec 19 10:12:24 2015 -0800
@@ -19,7 +19,7 @@
 
     <refentry id="@PACKAGE@.1">
         <refentryinfo>
-            <date>2009-01-25</date>
+            <date>2015-12-18</date>
             <author>
                 <firstname>Carl</firstname>
                 <surname>Byington</surname>
@@ -61,20 +61,30 @@
             or ip address can be used as input to this package.</para>
 
             <para>The <citerefentry> <refentrytitle>@PACKAGE@.conf</refentrytitle>
-            <manvolnum>5</manvolnum> </citerefentry> file specifies the syslog files
-            to be monitored, and the regular expressions (<citerefentry>
+            <manvolnum>5</manvolnum> </citerefentry> file specifies the syslog
+            files to be monitored, and the regular expressions (<citerefentry>
             <refentrytitle>regex</refentrytitle> <manvolnum>7</manvolnum>
             </citerefentry>) to be applied to new lines in those files.  Each
-            regular expression needs an index to specify the matching substring that
-            contains either an ip address or host name, and a bucket count which is
-            added to the leaky bucket for that ip address when a matching line is
-            read from that syslog file.</para>
+            regular expression needs an INDEX to specify the matching substring
+            that contains either an ip address or host name, and a DELTA which is
+            used to modify the leaky bucket count for that ip address when a
+            matching line is read from that syslog file. </para>
+
+            <para>If the DELTA is negative, the leaky bucket count is set to that
+            DELTA value, any existing blocking for that ip address is removed, and
+            new blocking is prevented until that bucket leaks upward to zero.
+            </para>
+
+            <para>If the DELTA is positive and the current leaky bucket count is
+            not negative, that DELTA value is added to the leaky bucket count for
+            that ip address. Once the bucket contains more than a configurable
+            THRESHOLD number of tokens, that ip address is added to the INPUT
+            chain with a DROP target.</para>
 
             <para>Each ip address has an associated leaky bucket, which leaks one
-            token per second.  Once the bucket contains more than a configurable
-            threshold number of tokens, that ip address is added to the INPUT chain
-            with a DROP target.  When the bucket is drained to zero, that ip address
-            is removed from the INPUT chain.</para>
+            token per second so the count moves toward zero.  When the bucket is
+            drained to zero, that ip address is removed from the INPUT
+            chain.</para>
 
             <para>The discussion has focused on syslog files, but any ascii text
             file can be used, so long as some other process appends lines to that
@@ -85,6 +95,11 @@
             via logrotate.  <command>@PACKAGE@</command> properly detects and
             handles this case by closing the old file, and reopening the newly
             created file.</para>
+
+            <para>With the default config file, you can manually unblock an ip
+            address with <command>logger -p authpriv.info "manual unblock
+            1.2.3.4"</command> </para>
+
         </refsect1>
 
         <refsect1 id='options.1'>
@@ -166,7 +181,7 @@
 
     <refentry id="@PACKAGE@.conf.5">
         <refentryinfo>
-            <date>2009-01-25</date>
+            <date>2015-12-18</date>
         </refentryinfo>
 
         <refmeta>
@@ -205,8 +220,8 @@
 IG-SINGLE := IP-ADDRESS "/" CIDR-BITS
 FILE      := "file" FILENAME "{" PATTERN+ "}"
 PATTERN   := "pattern" REGULAR-EXPRESSION "{" {INDEX | BUCKET | MESSAGE}+ "};"
-INDEX     := "index" REGEX-INTEGER-VALUE ";"
-BUCKET    := "bucket" BUCKET-ADD-INTEGER-VALUE ";"
+INDEX     := "index" REGEX-INTEGER ";"
+DELTA     := "bucket" BUCKET-DELTA-INTEGER ";"
 MESSAGE   := "message" REASON ";"
 REASON    := string to appear in syslog messages
 IPT-CMD   := string containing exactly one %s replacement token for
@@ -216,26 +231,6 @@
         <refsect1 id='sample.5'>
             <title>Sample</title>
             <literallayout class="monospaced"><![CDATA[
-context dns {
-    threshold 1100;
-
-    add_command    "/sbin/iptables -I INPUT --protocol udp --destination-port 53 --src %s --jump DROP";
-    remove_command "/sbin/iptables -D INPUT --protocol udp --destination-port 53 --src %s --jump DROP";
-
-    ignore {
-        127.0.0.0/8;        // localhost
-    };
-
-    file "/var/log/messages" {
-        pattern "named.*client (.*)#.*query.*cache.*denied" {
-            index 1;    // zero based
-            bucket 400;
-            message "DNS attack";
-        };
-    };
-};
-
-
 context general {
     threshold 550;
 
@@ -247,6 +242,11 @@
     };
 
     file "/var/log/secure" {
+        pattern "manual unblock (.*)" {
+            index 1;    // zero based
+            bucket -5000;
+            message "manual unblock";
+        };
         pattern "sshd.*Failed password .* from ::ffff:(.*) port" {
             index 1;    // zero based
             bucket 400;
@@ -257,86 +257,96 @@
             bucket 400;
             message "ssh failed password";
         };
+        pattern "sshd.*authentication failure; .* rhost=(.*) " {
+            index 1;    // zero based
+            bucket 400;
+            message "ssh failed password";
+        };
+        pattern "sshd.*Did not receive identification string from (.*)" {
+            index 1;    // zero based
+            bucket 400;
+            message "ssh failed password";
+        };
         pattern "proftpd.*no such user found from (.*) \[" {
             index 1;    // zero based
             bucket 400;
             message "ftp failed password";
         };
+        pattern "proftpd.* authentication failure; .* rhost=(.*) " {
+            index 1;    // zero based
+            bucket 400;
+            message "ftp failed password";
     };
-
-    file "/var/log/messages" {
-        pattern "ipop3d.* Login failed .* \[(.*)\]" {
+        pattern "vsftpd.* authentication failure; .* rhost=(.*) " {
             index 1;    // zero based
             bucket 400;
-            message "pop3 failed password";
+            message "ftp failed password";
+        };
+        pattern "dovecot.* authentication failure; .* rhost=::ffff:(.*) " {
+            index 1;    // zero based
+            bucket 100;
+            message "dovecot failed password";
+        };
+        pattern "dovecot.* authentication failure; .* rhost=(.*) " {
+            index 1;    // zero based
+            bucket 100;
+            message "dovecot failed password";
         };
     };
 
-    file "/var/log/httpd/access_log" {
-        // of course you cannot use this if you actually use cgi-bin directories
-        pattern "(.*) - - .* /cgi-bin" {
+    file "/var/log/messages" {
+        pattern "dovecot.* authentication failure; .* rhost=(.*) " {
             index 1;    // zero based
-            bucket 400;
-            message "apache cgi-bin reference";
+            bucket 100;
+            message "dovecot failed password";
         };
-        // or if you actually have an index2.php script
-        pattern "(.*) - - .*/index2.php" {
+        pattern "kernel.*local-net-to.*SRC=(.*) DST=.*DPT=" {
             index 1;    // zero based
             bucket 400;
-            message "apache index2.php reference";
+            message "kernel firewall blocked packet";
         };
-        // or if you have a main.php script
-        pattern "(.*) - - .*/main.php" {
+        pattern "kernel.*outside-net-from.*SRC=(.*) DST=.*DPT=" {
             index 1;    // zero based
             bucket 400;
-            message "apache main.php reference";
-        };
-        pattern "(.*) - - .*/awstats.pl" {
-            index 1;    // zero based
-            bucket 400;
-            message "apache awstats.pl reference";
-        };
-        pattern "(.*) - - .*/adxmlrpc" {
-            index 1;    // zero based
-            bucket 400;
-            message "apache adxmlrpc reference";
+            message "kernel firewall blocked packet";
         };
     };
 
     file "/var/log/maillog" {
         pattern "lost input channel from .* \[(.*)\] .* after (mail|rcpt|auth)" {
             index 1;    // zero based
-            bucket 200;
+            bucket 100;
             message "sendmail spammer dropping connection";
         };
-        pattern " \[(.*)\]: possible SMTP attack" {
+        pattern " \[(.*)\].* possible SMTP attack" {
             index 1;    // zero based
-            bucket 600;
+            bucket 100;
             message "sendmail authentication attack";
         };
         pattern "rejecting commands from .* \[(.*)\] due to pre-greeting traffic" {
             index 1;    // zero based
-            bucket 200;
+            bucket 1800;
             message "sendmail pre-greeting";
         };
+        pattern "authentication failure: checkpass failed, .*\[(.*)\]" {
+            index 1;    // zero based
+            bucket 100;
+            message "sendmail authentication failed";
+        };
         pattern "dovecot.*Aborted login.*rip=(.*)," {
             index 1;    // zero based
             bucket 100;
             message "dovecot failed password";
         };
-        pattern "dovecot: pop3-login: Disconnected: Shutting down.*rip=(.*)," {
+        pattern "dovecot.*Login: .* rip=(.*)," {
             index 1;    // zero based
-            bucket 100;
-            message "dovecot failed password";
+            bucket -5000;
+            message "dovecot good authentication";
         };
-
-        // make sure your upstream MX servers are listed in the
-        // ignore block above, otherwise you will kill them off
-        // when they try to forward such mail to you.
-        pattern "sendmail.*from=<>,.*nrcpts=0,.*\[(.*)\]" {
+        pattern "sendmail.*AUTH=server, .*\[(.*)\]," {
             index 1;    // zero based
-            bucket 200;
-            message "sendmail rejected bounce";
+            bucket -5000;
+            message "sendmail good authentication";
         };
     };
 };]]></literallayout>