]> mj.ucw.cz Git - bouncer.git/commitdiff
More configurability
authorMartin Mares <mj@ucw.cz>
Sat, 5 Nov 2016 10:19:15 +0000 (11:19 +0100)
committerMartin Mares <mj@ucw.cz>
Sat, 5 Nov 2016 10:19:15 +0000 (11:19 +0100)
bouncer.c

index d747a3aa53401b3d6c8573e5494a703306129899..25e3e30d0a2c3f38062fe134d93ff3ae07db68f4 100644 (file)
--- a/bouncer.c
+++ b/bouncer.c
@@ -6,6 +6,8 @@
  *     FIXME: ipset create bouncer4 hash:ip family inet
  *     FIXME: ipset create bouncer6 hash:ip family inet6
  *     FIXME: sshd_config: UseDNS no
+ *     FIXME: PAM module names should be made configurable
+ *     FIXME: Parse "N more failures" messages
  */
 
 #define LOCAL_DEBUG
@@ -13,6 +15,7 @@
 #include <ucw/lib.h>
 #include <ucw/clists.h>
 #include <ucw/conf.h>
+#include <ucw/log.h>
 #include <ucw/mainloop.h>
 #include <ucw/opt.h>
 #include <ucw/string.h>
@@ -21,7 +24,9 @@
 #include <arpa/inet.h>
 #include <errno.h>
 #include <string.h>
+#include <sys/types.h>
 #include <sys/socket.h>
+#include <sys/stat.h>
 #include <sys/un.h>
 #include <time.h>
 #include <unistd.h>
@@ -87,27 +92,27 @@ static bool addr_parse(struct addr *addr, const char *src)
 
 /*** Configuration ***/
 
+static char *listen_on = "/var/run/bouncer.sock";
 static uns max_failures = ~0U;
 static uns max_suspect_time = 86400;
 static uns max_banned_time = 86400;
 static uns max_suspects = ~0U;
 static uns max_banned = ~0U;
-
-/*
- * FIXME:
- * - names of ipsets
- * - name of socket
- * - logging
- * - PAM module name(s) to match
- */
+static char *ipv4_set;
+static char *ipv6_set;
+static char *config_log_stream;
 
 static struct cf_section bouncer_cf = {
   CF_ITEMS {
+    CF_STRING("ListenOn", &listen_on),
     CF_UNS("MaxSuspects", &max_suspects),
     CF_UNS("MaxBanned", &max_banned),
     CF_UNS("MaxSuspectTime", &max_suspect_time),
     CF_UNS("MaxBannedTime", &max_banned_time),
     CF_UNS("MaxFailures", &max_failures),
+    CF_STRING("IPv4Set", &ipv4_set),
+    CF_STRING("IPv6Set", &ipv6_set),
+    CF_STRING("LogStream", &config_log_stream),
     CF_END
   }
 };
@@ -116,16 +121,6 @@ static struct cf_section bouncer_cf = {
 
 static struct ipset_session *is_sess;
 
-enum is_index {
-  IS_IPV4,
-  IS_IPV6,
-};
-
-static const char * const is_names[] = {
-  "bouncer4",
-  "bouncer6",
-};
-
 static const char *trim_eol(const char *msg)
 {
   int len = strlen(msg);
@@ -170,24 +165,31 @@ static void is_init(void)
     die("Unable to initialize ipset session");
 }
 
-static void is_setup(int set)
+static bool is_setup(char *set)
 {
-  if (ipset_parse_setname(is_sess, IPSET_SETNAME, is_names[set]) < 0)
+  if (!set)
+    return 0;
+
+  if (ipset_parse_setname(is_sess, IPSET_SETNAME, set) < 0)
     is_die("ipset_parse_setname");
+  return 1;
 }
 
-static void is_flush(int set)
+static void is_flush(char *set)
 {
-  is_setup(set);
+  if (!is_setup(set))
+    return;
 
   if (ipset_cmd(is_sess, IPSET_CMD_FLUSH, 0) < 0)
     return is_err("IPSET_CMD_FLUSH");
 }
 
-static void is_modify(bool add, struct addr addr)
+static bool is_modify(bool add, struct addr addr)
 {
-  is_setup(addr_is_v4(addr) ? IS_IPV4 : IS_IPV6);
   int cmd = add ? IPSET_CMD_ADD : IPSET_CMD_DEL;
+  char *set = addr_is_v4(addr) ? ipv4_set : ipv6_set;
+  if (!is_setup(set))
+    return 0;
 
   if (ipset_envopt_parse(is_sess, IPSET_ENV_EXIST, NULL) < 0)
     is_die("IPSET_ENV_EXIST");
@@ -202,10 +204,18 @@ static void is_modify(bool add, struct addr addr)
   char buf[ADDR_BUFSIZE];
   addr_format(buf, addr);
   if (ipset_parse_elem(is_sess, 0, buf) < 0)
-    return is_err("ipset_parse_elem");
+    {
+      is_err("ipset_parse_elem");
+      return 0;
+    }
 
   if (ipset_cmd(is_sess, cmd, 0) < 0)
-    return is_err(add ? "IPSET_CMD_ADD" : "IPSET_CMD_DEL");
+    {
+      is_err(add ? "IPSET_CMD_ADD" : "IPSET_CMD_DEL");
+      return 0;
+    }
+
+  return 1;
 }
 
 /*** Handling of login failures ***/
@@ -249,7 +259,15 @@ static void cleanup_list(clist *list, uns *counter, timestamp_t max_time, uns ma
       timestamp_t expire_in = c->last_fail + max_time - now;
       if (*counter > max_count)
        {
-         // FIXME: Warn with rate limit
+         static timestamp_t last_overflow_warning;
+         if (last_overflow_warning + 60000 < now)
+           {
+             last_overflow_warning = now;
+             if (c->banned)
+               msg(L_WARN, "Too many bans, dropping some. Try increasing MaxBanned.");
+             else
+               msg(L_WARN, "Too many suspects, dropping some. Try increasing MaxSuspects.");
+           }
          expire_in = 0;
        }
 
@@ -261,13 +279,11 @@ static void cleanup_list(clist *list, uns *counter, timestamp_t max_time, uns ma
 
       if (c->banned)
        {
-         DBG("%s: unbanned", AFMT(c->addr));
+         msg(L_INFO, "Unbanning %s", AFMT(c->addr));
          is_modify(0, c->addr);
        }
       else
-       {
-         DBG("%s: removed from LRU", AFMT(c->addr));
-       }
+       msg(L_DEBUG, "Suspect %s: acquitted", AFMT(c->addr));
 
       clist_remove(&c->n);
       culprit_remove(c);
@@ -296,7 +312,7 @@ static void handle_failed_login(struct addr addr)
       c->banned = 0;
       clist_add_tail(&suspect_list, &c->n);
       num_suspects++;
-      DBG("%s: first fail", AFMT(addr));
+      msg(L_DEBUG, "Suspect %s: new", AFMT(addr));
     }
   else if (!c->banned)
     {
@@ -304,12 +320,12 @@ static void handle_failed_login(struct addr addr)
       c->fail_count++;
       clist_remove(&c->n);
       clist_add_tail(&suspect_list, &c->n);
-      DBG("%s: next fail, cnt=%u", AFMT(addr), c->fail_count);
+      msg(L_DEBUG, "Suspect %s: failures=%u", AFMT(addr), c->fail_count);
     }
 
   if (!c->banned && c->fail_count >= max_failures)
     {
-      DBG("%s: banned", AFMT(addr));
+      msg(L_INFO, "Banning %s: failures=%u", AFMT(addr), c->fail_count);
       c->banned = 1;
       clist_remove(&c->n);
       num_suspects--;
@@ -374,7 +390,7 @@ static void process_msg(char *line)
     return;
   DBG("Parse 2: <%s>", p);
 
-  // pam_unix(something), colon, space (FIXME: make configurable)
+  // pam_unix(something), colon, space
   if (!check_next(&p, "pam_unix("))
     return;
   do
@@ -433,7 +449,6 @@ static void process_msg(char *line)
 
 /*** Socket for receiving messages from rsyslog ***/
 
-static const char sk_name[] = "/var/run/bouncer.sock";
 struct main_file sk_file;
 
 static int sk_read(struct main_file *mf)
@@ -465,22 +480,23 @@ static int sk_read(struct main_file *mf)
 
 static void sk_init(void)
 {
-  unlink(sk_name);
+  unlink(listen_on);
+  mode_t old_umask = umask(0077);
 
   int fd;
   if ((fd = socket(PF_UNIX, SOCK_DGRAM, 0)) < 0)
     die("Cannot create PF_UNIX socket: %m");
 
   struct sockaddr_un sa = { .sun_family = AF_UNIX };
-  strcpy(sa.sun_path, sk_name);
+  strcpy(sa.sun_path, listen_on);
   if (bind(fd, (struct sockaddr *) &sa, sizeof(sa)) < 0)
-    die("Cannot bind socket %s: %m", sk_name);
-
-  // FIXME: Permissions
+    die("Cannot bind socket %s: %m", listen_on);
 
   sk_file.fd = fd;
   sk_file.read_handler = sk_read;
   file_add(&sk_file);
+
+  umask(old_umask);
 }
 
 /*** Main ***/
@@ -502,16 +518,18 @@ int main(int argc UNUSED, char **argv)
   cf_declare_section("Bouncer", &bouncer_cf, 0);
   opt_parse(&options, argv+1);
 
+  if (config_log_stream)
+    log_configured(config_log_stream);
+
   main_init();
   is_init();
   fail_init();
-
-  // FIXME msg(L_INFO, "Clearing previous state");
-  is_flush(IS_IPV4);
-  is_flush(IS_IPV6);
-
   sk_init();
 
+  is_flush(ipv4_set);
+  is_flush(ipv6_set);
+
+  msg(L_INFO, "Starting");
   main_loop();
   return 0;
 }