about summary refs log tree commit diff
path: root/nixos/modules/services/security/fail2ban.nix
diff options
context:
space:
mode:
Diffstat (limited to 'nixos/modules/services/security/fail2ban.nix')
-rw-r--r--nixos/modules/services/security/fail2ban.nix306
1 files changed, 228 insertions, 78 deletions
diff --git a/nixos/modules/services/security/fail2ban.nix b/nixos/modules/services/security/fail2ban.nix
index 716ae7a2d2f4..cb748c93d24e 100644
--- a/nixos/modules/services/security/fail2ban.nix
+++ b/nixos/modules/services/security/fail2ban.nix
@@ -6,15 +6,32 @@ let
 
   cfg = config.services.fail2ban;
 
-  fail2banConf = pkgs.writeText "fail2ban.conf" cfg.daemonConfig;
+  fail2banConf = pkgs.writeText "fail2ban.local" cfg.daemonConfig;
 
-  jailConf = pkgs.writeText "jail.conf"
-    (concatStringsSep "\n" (attrValues (flip mapAttrs cfg.jails (name: def:
+  jailConf = pkgs.writeText "jail.local" ''
+    [INCLUDES]
+
+    before = paths-nixos.conf
+
+    ${concatStringsSep "\n" (attrValues (flip mapAttrs cfg.jails (name: def:
       optionalString (def != "")
         ''
           [${name}]
           ${def}
-        ''))));
+        '')))}
+  '';
+
+  pathsConf = pkgs.writeText "paths-nixos.conf" ''
+    # NixOS
+
+    [INCLUDES]
+
+    before = paths-common.conf
+
+    after  = paths-overrides.local
+
+    [DEFAULT]
+  '';
 
 in
 
@@ -31,21 +48,135 @@ in
         description = "Whether to enable the fail2ban service.";
       };
 
+      package = mkOption {
+        default = pkgs.fail2ban;
+        type = types.package;
+        example = "pkgs.fail2ban_0_11";
+        description = "The fail2ban package to use for running the fail2ban service.";
+      };
+
+      packageFirewall = mkOption {
+        default = pkgs.iptables;
+        type = types.package;
+        example = "pkgs.nftables";
+        description = "The firewall package used by fail2ban service.";
+      };
+
+      banaction = mkOption {
+        default = "iptables-multiport";
+        type = types.str;
+        example = "nftables-multiport";
+        description = ''
+          Default banning action (e.g. iptables, iptables-new, iptables-multiport,
+          shorewall, etc) It is used to define action_* variables. Can be overridden
+          globally or per section within jail.local file
+        '';
+      };
+
+      banaction-allports = mkOption {
+        default = "iptables-allport";
+        type = types.str;
+        example = "nftables-allport";
+        description = ''
+          Default banning action (e.g. iptables, iptables-new, iptables-multiport,
+          shorewall, etc) It is used to define action_* variables. Can be overridden
+          globally or per section within jail.local file
+        '';
+      };
+
+      bantime-increment.enable = mkOption {
+        default = false;
+        type = types.bool;
+        description = ''
+          Allows to use database for searching of previously banned ip's to increase
+          a default ban time using special formula, default it is banTime * 1, 2, 4, 8, 16, 32...
+        '';
+      };
+
+      bantime-increment.rndtime = mkOption {
+        default = "4m";
+        type = types.str;
+        example = "8m";
+        description = ''
+          "bantime-increment.rndtime" is the max number of seconds using for mixing with random time
+          to prevent "clever" botnets calculate exact time IP can be unbanned again
+        '';
+      };
+
+      bantime-increment.maxtime = mkOption {
+        default = "10h";
+        type = types.str;
+        example = "48h";
+        description = ''
+          "bantime-increment.maxtime" is the max number of seconds using the ban time can reach (don't grows further)
+        '';
+      };
+
+      bantime-increment.factor = mkOption {
+        default = "1";
+        type = types.str;
+        example = "4";
+        description = ''
+          "bantime-increment.factor" is a coefficient to calculate exponent growing of the formula or common multiplier,
+          default value of factor is 1 and with default value of formula, the ban time grows by 1, 2, 4, 8, 16 ...
+        '';
+      };
+
+      bantime-increment.formula = mkOption {
+        default = "ban.Time * (1<<(ban.Count if ban.Count<20 else 20)) * banFactor";
+        type = types.str;
+        example = "ban.Time * math.exp(float(ban.Count+1)*banFactor)/math.exp(1*banFactor)";
+        description = ''
+          "bantime-increment.formula" used by default to calculate next value of ban time, default value bellow,
+          the same ban time growing will be reached by multipliers 1, 2, 4, 8, 16, 32...
+        '';
+      };
+
+      bantime-increment.multipliers = mkOption {
+        default = "1 2 4 8 16 32 64";
+        type = types.str;
+        example = "2 4 16 128";
+        description = ''
+          "bantime-increment.multipliers" used to calculate next value of ban time instead of formula, coresponding
+          previously ban count and given "bantime.factor" (for multipliers default is 1);
+          following example grows ban time by 1, 2, 4, 8, 16 ... and if last ban count greater as multipliers count,
+          always used last multiplier (64 in example), for factor '1' and original ban time 600 - 10.6 hours
+        '';
+      };
+
+      bantime-increment.overalljails = mkOption {
+        default = false;
+        type = types.bool;
+        example = true;
+        description = ''
+          "bantime-increment.overalljails"  (if true) specifies the search of IP in the database will be executed
+          cross over all jails, if false (dafault), only current jail of the ban IP will be searched
+        '';
+      };
+
+      ignoreIP = mkOption {
+        default = [ ];
+        type = types.listOf types.str;
+        example = [ "192.168.0.0/16" "2001:DB8::42" ];
+        description = ''
+          "ignoreIP" can be a list of IP addresses, CIDR masks or DNS hosts. Fail2ban will not ban a host which
+          matches an address in this list. Several addresses can be defined using space (and/or comma) separator.
+        '';
+      };
+
       daemonConfig = mkOption {
-        default =
-          ''
-            [Definition]
-            loglevel  = INFO
-            logtarget = SYSLOG
-            socket    = /run/fail2ban/fail2ban.sock
-            pidfile   = /run/fail2ban/fail2ban.pid
-          '';
+        default = ''
+          [Definition]
+          logtarget = SYSLOG
+          socket    = /run/fail2ban/fail2ban.sock
+          pidfile   = /run/fail2ban/fail2ban.pid
+          dbfile    = /var/lib/fail2ban/fail2ban.sqlite3
+        '';
         type = types.lines;
-        description =
-          ''
-            The contents of Fail2ban's main configuration file.  It's
-            generally not necessary to change it.
-          '';
+        description = ''
+          The contents of Fail2ban's main configuration file.  It's
+          generally not necessary to change it.
+       '';
       };
 
       jails = mkOption {
@@ -65,88 +196,107 @@ in
           }
         '';
         type = types.attrsOf types.lines;
-        description =
-          ''
-            The configuration of each Fail2ban “jail”.  A jail
-            consists of an action (such as blocking a port using
-            <command>iptables</command>) that is triggered when a
-            filter applied to a log file triggers more than a certain
-            number of times in a certain time period.  Actions are
-            defined in <filename>/etc/fail2ban/action.d</filename>,
-            while filters are defined in
-            <filename>/etc/fail2ban/filter.d</filename>.
-          '';
+        description = ''
+          The configuration of each Fail2ban “jail”.  A jail
+          consists of an action (such as blocking a port using
+          <command>iptables</command>) that is triggered when a
+          filter applied to a log file triggers more than a certain
+          number of times in a certain time period.  Actions are
+          defined in <filename>/etc/fail2ban/action.d</filename>,
+          while filters are defined in
+          <filename>/etc/fail2ban/filter.d</filename>.
+        '';
       };
 
     };
 
   };
 
-
   ###### implementation
 
   config = mkIf cfg.enable {
 
-    environment.systemPackages = [ pkgs.fail2ban ];
+    environment.systemPackages = [ cfg.package ];
 
-    environment.etc."fail2ban/fail2ban.conf".source = fail2banConf;
-    environment.etc."fail2ban/jail.conf".source = jailConf;
-    environment.etc."fail2ban/action.d".source = "${pkgs.fail2ban}/etc/fail2ban/action.d/*.conf";
-    environment.etc."fail2ban/filter.d".source = "${pkgs.fail2ban}/etc/fail2ban/filter.d/*.conf";
-
-    systemd.services.fail2ban =
-      { description = "Fail2ban Intrusion Prevention System";
+    environment.etc = {
+      "fail2ban/fail2ban.local".source = fail2banConf;
+      "fail2ban/jail.local".source = jailConf;
+      "fail2ban/fail2ban.conf".source = "${cfg.package}/etc/fail2ban/fail2ban.conf";
+      "fail2ban/jail.conf".source = "${cfg.package}/etc/fail2ban/jail.conf";
+      "fail2ban/paths-common.conf".source = "${cfg.package}/etc/fail2ban/paths-common.conf";
+      "fail2ban/paths-nixos.conf".source = pathsConf;
+      "fail2ban/action.d".source = "${cfg.package}/etc/fail2ban/action.d/*.conf";
+      "fail2ban/filter.d".source = "${cfg.package}/etc/fail2ban/filter.d/*.conf";
+    };
 
-        wantedBy = [ "multi-user.target" ];
-        after = [ "network.target" ];
-        partOf = optional config.networking.firewall.enable "firewall.service";
+    systemd.services.fail2ban = {
+      description = "Fail2ban Intrusion Prevention System";
 
-        restartTriggers = [ fail2banConf jailConf ];
-        path = [ pkgs.fail2ban pkgs.iptables pkgs.iproute ];
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network.target" ];
+      partOf = optional config.networking.firewall.enable "firewall.service";
 
-        preStart =
-          ''
-            mkdir -p /var/lib/fail2ban
-          '';
+      restartTriggers = [ fail2banConf jailConf pathsConf ];
+      reloadIfChanged = true;
 
-        unitConfig.Documentation = "man:fail2ban(1)";
+      path = [ cfg.package cfg.packageFirewall pkgs.iproute ];
 
-        serviceConfig =
-          { Type = "forking";
-            ExecStart = "${pkgs.fail2ban}/bin/fail2ban-client -x start";
-            ExecStop = "${pkgs.fail2ban}/bin/fail2ban-client stop";
-            ExecReload = "${pkgs.fail2ban}/bin/fail2ban-client reload";
-            PIDFile = "/run/fail2ban/fail2ban.pid";
-            Restart = "always";
+      unitConfig.Documentation = "man:fail2ban(1)";
 
-            ReadOnlyDirectories = "/";
-            ReadWriteDirectories = "/run/fail2ban /var/tmp /var/lib";
-            PrivateTmp = "true";
-            RuntimeDirectory = "fail2ban";
-            CapabilityBoundingSet = "CAP_DAC_READ_SEARCH CAP_NET_ADMIN CAP_NET_RAW";
-          };
+      serviceConfig = {
+        ExecStart = "${cfg.package}/bin/fail2ban-server -xf start";
+        ExecStop = "${cfg.package}/bin/fail2ban-server stop";
+        ExecReload = "${cfg.package}/bin/fail2ban-server reload";
+        Type = "simple";
+        Restart = "on-failure";
+        PIDFile = "/run/fail2ban/fail2ban.pid";
+        # Capabilities
+        CapabilityBoundingSet = [ "CAP_AUDIT_READ" "CAP_DAC_READ_SEARCH" "CAP_NET_ADMIN" "CAP_NET_RAW" ];
+        # Security
+        NoNewPrivileges = true;
+        # Directory
+        RuntimeDirectory = "fail2ban";
+        RuntimeDirectoryMode = "0750";
+        StateDirectory = "fail2ban";
+        StateDirectoryMode = "0750";
+        LogsDirectory = "fail2ban";
+        LogsDirectoryMode = "0750";
+        # Sandboxing
+        ProtectSystem = "strict";
+        ProtectHome = true;
+        PrivateTmp = true;
+        PrivateDevices = true;
+        ProtectHostname = true;
+        ProtectKernelTunables = true;
+        ProtectKernelModules = true;
+        ProtectControlGroups = true;
       };
+    };
 
     # Add some reasonable default jails.  The special "DEFAULT" jail
     # sets default values for all other jails.
-    services.fail2ban.jails.DEFAULT =
-      ''
-        ignoreip = 127.0.0.1/8
-        bantime  = 600
-        findtime = 600
-        maxretry = 3
-        backend  = systemd
-        enabled  = true
-       '';
-
+    services.fail2ban.jails.DEFAULT = ''
+      ${optionalString cfg.bantime-increment.enable ''
+        # Bantime incremental
+        bantime.increment    = ${if cfg.bantime-increment.enable then "true" else "false"}
+        bantime.maxtime      = ${cfg.bantime-increment.maxtime}
+        bantime.factor       = ${cfg.bantime-increment.factor}
+        bantime.formula      = ${cfg.bantime-increment.formula}
+        bantime.multipliers  = ${cfg.bantime-increment.multipliers}
+        bantime.overalljails = ${if cfg.bantime-increment.overalljails then "true" else "false"}
+      ''}
+      # Miscellaneous options
+      ignoreip    = 127.0.0.1/8 ${optionalString config.networking.enableIPv6 "::1"} ${concatStringsSep " " cfg.ignoreIP}
+      maxretry    = 3
+      backend     = systemd
+      # Actions
+      banaction   = ${cfg.banaction}
+      banaction_allports = ${cfg.banaction-allports}
+    '';
     # Block SSH if there are too many failing connection attempts.
-    services.fail2ban.jails.ssh-iptables =
-      ''
-        filter   = sshd
-        action   = iptables-multiport[name=SSH, port="${concatMapStringsSep "," (p: toString p) config.services.openssh.ports}", protocol=tcp]
-        maxretry = 5
-      '';
-
+    services.fail2ban.jails.sshd = mkDefault ''
+      enabled = true
+      port    = ${concatMapStringsSep "," (p: toString p) config.services.openssh.ports}
+    '';
   };
-
 }