diff --git a/machines/compute01/_configuration.nix b/machines/compute01/_configuration.nix index 59060d8..91fb3bb 100644 --- a/machines/compute01/_configuration.nix +++ b/machines/compute01/_configuration.nix @@ -4,7 +4,6 @@ lib.extra.mkConfig { enabledModules = [ # List of modules to enable "dgn-backups" - "dgn-fail2ban" "dgn-web" ]; @@ -32,11 +31,6 @@ lib.extra.mkConfig { ]; extraConfig = { - dgn-fail2ban.jails = lib.extra.enableAttrs' "enabled" [ - "sshd-bruteforce" - "sshd-timeout" - ]; - dgn-hardware.useZfs = true; services.netbird.enable = true; diff --git a/machines/rescue01/_configuration.nix b/machines/rescue01/_configuration.nix index 6c50ba3..ca68e2e 100644 --- a/machines/rescue01/_configuration.nix +++ b/machines/rescue01/_configuration.nix @@ -3,7 +3,6 @@ lib.extra.mkConfig { enabledModules = [ # List of modules to enable - "dgn-fail2ban" ]; enabledServices = [ @@ -12,11 +11,6 @@ lib.extra.mkConfig { ]; extraConfig = { - dgn-fail2ban.jails = lib.extra.enableAttrs' "enabled" [ - "sshd-bruteforce" - "sshd-timeout" - ]; - services.netbird.enable = true; }; diff --git a/machines/storage01/_configuration.nix b/machines/storage01/_configuration.nix index 69f7880..a15da38 100644 --- a/machines/storage01/_configuration.nix +++ b/machines/storage01/_configuration.nix @@ -4,7 +4,6 @@ lib.extra.mkConfig { enabledModules = [ # List of modules to enable "dgn-backups" - "dgn-fail2ban" "dgn-web" ]; @@ -22,8 +21,6 @@ lib.extra.mkConfig { ]; extraConfig = { - dgn-fail2ban.jails.sshd-preauth.enabled = true; - dgn-hardware.useZfs = true; services.netbird.enable = true; diff --git a/machines/vault01/_configuration.nix b/machines/vault01/_configuration.nix index bea9622..5e94808 100644 --- a/machines/vault01/_configuration.nix +++ b/machines/vault01/_configuration.nix @@ -3,7 +3,6 @@ lib.extra.mkConfig { enabledModules = [ # List of modules to enable - "dgn-fail2ban" ]; enabledServices = [ @@ -14,11 +13,6 @@ lib.extra.mkConfig { ]; extraConfig = { - dgn-fail2ban.jails = lib.extra.enableAttrs' "enabled" [ - "sshd-bruteforce" - "sshd-timeout" - ]; - services.netbird.enable = true; services.nginx.enable = true; networking.firewall.allowedTCPPorts = [ 80 ]; diff --git a/machines/web01/_configuration.nix b/machines/web01/_configuration.nix index 8cb9425..1e30feb 100644 --- a/machines/web01/_configuration.nix +++ b/machines/web01/_configuration.nix @@ -3,7 +3,6 @@ lib.extra.mkConfig { enabledModules = [ # List of modules to enable - "dgn-fail2ban" "dgn-web" ]; diff --git a/machines/web02/_configuration.nix b/machines/web02/_configuration.nix index bb44155..898f7f6 100644 --- a/machines/web02/_configuration.nix +++ b/machines/web02/_configuration.nix @@ -3,7 +3,6 @@ lib.extra.mkConfig { enabledModules = [ # List of modules to enable - "dgn-fail2ban" "dgn-web" ]; @@ -14,11 +13,6 @@ lib.extra.mkConfig { ]; extraConfig = { - dgn-fail2ban.jails = lib.extra.enableAttrs' "enabled" [ - "sshd-bruteforce" - "sshd-timeout" - ]; - # Restrict access to this node dgn-access-control.users.root = [ "thubrecht" ]; diff --git a/modules/default.nix b/modules/default.nix index 3047718..8e5d995 100644 --- a/modules/default.nix +++ b/modules/default.nix @@ -46,7 +46,7 @@ "dgn-acme" "dgn-backups" "dgn-console" - "dgn-fail2ban" + "dgn-firewall" "dgn-hardware" "dgn-netbox-agent" "dgn-network" @@ -67,6 +67,7 @@ [ "age-secrets" "services/bupstash" + "services/reaction" "services/systemd-notify" ] ++ nodeMeta.nix-modules diff --git a/modules/dgn-fail2ban/default.nix b/modules/dgn-fail2ban/default.nix deleted file mode 100644 index b2f3fe6..0000000 --- a/modules/dgn-fail2ban/default.nix +++ /dev/null @@ -1,90 +0,0 @@ -{ - config, - lib, - pkgs, - ... -}: - -let - inherit (lib) - mkDefault - mkEnableOption - mkIf - mkOption - - types - ; - - cfg = config.dgn-fail2ban; - - settingsFormat = pkgs.formats.keyValue { }; - - configFormat = pkgs.formats.ini { }; - - jailOptions = { - options = { - enabled = mkOption { - type = types.bool; - - default = true; - description = "Wether to enable this jail."; - }; - - filter = mkOption { - type = types.nullOr (types.submodule { freeformType = configFormat.type; }); - - description = "Content of the filter used for this jail."; - }; - - settings = mkOption { - type = types.submodule { freeformType = settingsFormat.type; }; - - default = { }; - description = "Additional configuration for the jail."; - }; - }; - }; -in -{ - options.dgn-fail2ban = { - enable = mkEnableOption "fail2ban service."; - - jails = mkOption { - type = types.attrsOf (types.submodule jailOptions); - - default = { }; - description = "Set of jails defined for fail2ban."; - }; - }; - - config = mkIf cfg.enable { - dgn-fail2ban.jails = builtins.mapAttrs (_: j: j // { enabled = mkDefault false; }) ( - import ./jails.nix { } - ); - - services.fail2ban = { - enable = true; - - inherit (cfg) jails; - - ignoreIP = [ - "10.0.0.0/8" - "129.199.0.0/16" - "172.16.0.0/12" - "192.168.0.0/16" - "100.64.0.0/10" - "fd00::/8" - ]; - - bantime-increment = { - enable = true; - - maxtime = "48h"; - factor = "600"; - }; - - extraPackages = [ pkgs.ipset ]; - banaction = "iptables-ipset-proto6-allports"; - }; - }; -} diff --git a/modules/dgn-fail2ban/jails.nix b/modules/dgn-fail2ban/jails.nix deleted file mode 100644 index 49a5d4b..0000000 --- a/modules/dgn-fail2ban/jails.nix +++ /dev/null @@ -1,93 +0,0 @@ -# Copyright Tom Hubrecht, (2023) -# -# Tom Hubrecht -# -# This software is a computer program whose purpose is to configure -# machines and servers with NixOS. -# -# This software is governed by the CeCILL license under French law and -# abiding by the rules of distribution of free software. You can use, -# modify and/ or redistribute the software under the terms of the CeCILL -# license as circulated by CEA, CNRS and INRIA at the following URL -# "http://www.cecill.info". -# -# As a counterpart to the access to the source code and rights to copy, -# modify and redistribute granted by the license, users are provided only -# with a limited warranty and the software's author, the holder of the -# economic rights, and the successive licensors have only limited -# liability. -# -# In this respect, the user's attention is drawn to the risks associated -# with loading, using, modifying and/or developing or reproducing the -# software by the user in light of its specific status of free software, -# that may mean that it is complicated to manipulate, and that also -# therefore means that it is reserved for developers and experienced -# professionals having in-depth computer knowledge. Users are therefore -# encouraged to load and test the software's suitability as regards their -# requirements in conditions enabling the security of their systems and/or -# data to be ensured and, more generally, to use and operate it in the -# same conditions as regards security. -# -# The fact that you are presently reading this means that you have had -# knowledge of the CeCILL license and that you accept its terms. - -_: { - nginx-spam = { - filter.Definition.failregex = ''^.*GET.*(matrix/server|\.php|admin|wp\-).* HTTP/\d.\d\" 404.*$''; - - settings = { - logpath = "/var/log/nginx/access.log"; - backend = "auto"; - maxretry = 500; - findtime = 60; - }; - }; - - postfix-bruteforce = { - filter.Definition = { - failregex = "warning: [\\w\\.\\-]+\\[\\]: SASL LOGIN authentication failed.*$"; - journalmatch = "_SYSTEMD_UNIT=postfix.service"; - }; - - settings = { - findtime = 600; - maxretry = 1; - }; - }; - - sshd-bruteforce = { - filter.Definition = { - failregex = "pam_unix\\(sshd:auth\\): authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=.*$"; - journalmatch = "_SYSTEMD_UNIT=sshd.service"; - }; - - settings = { - findtime = 600; - maxretry = 1; - }; - }; - - sshd-preauth = { - filter.Definition = { - failregex = "Received disconnect from port .* Bye Bye \\[preauth\\]$"; - journalmatch = "_SYSTEMD_UNIT=sshd.service"; - }; - - settings = { - findtime = 600; - maxretry = 1; - }; - }; - - sshd-timeout = { - filter.Definition = { - failregex = "fatal: Timeout before authentication for .*$"; - journalmatch = "_SYSTEMD_UNIT=sshd.service"; - }; - - settings = { - findtime = 600; - maxretry = 1; - }; - }; -} diff --git a/modules/dgn-firewall/default.nix b/modules/dgn-firewall/default.nix new file mode 100644 index 0000000..4d16617 --- /dev/null +++ b/modules/dgn-firewall/default.nix @@ -0,0 +1,87 @@ +{ + pkgs, + lib, + name, + ... +}: + +let + inherit (lib) + concatStringsSep + length + replicate + splitString + ; + + inherit (lib.lists) map; + + c4 = "(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)"; + + mkV4' = p: concatStringsSep "\\." (p ++ (replicate (4 - (length p)) c4)); + mkV4 = s: mkV4' (splitString "." s); + + nft = s: [ "nft" ] ++ [ s ]; + + streams' = import ./streams.nix; +in + +{ + # Switch to nftables + networking.nftables.enable = true; + + services.reaction = { + enable = true; + + extraPackages = [ pkgs.nftables ]; + + runAsRoot = true; + + logLevel = "WARN"; + + settings = { + patterns = { + ip = { + regex = "(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(?:.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}|(?:(?:[0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|(?:[0-9a-fA-F]{1,4}:){1,7}:|(?:[0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|(?:[0-9a-fA-F]{1,4}:){1,5}(?::[0-9a-fA-F]{1,4}){1,2}|(?:[0-9a-fA-F]{1,4}:){1,4}(?::[0-9a-fA-F]{1,4}){1,3}|(?:[0-9a-fA-F]{1,4}:){1,3}(?::[0-9a-fA-F]{1,4}){1,4}|(?:[0-9a-fA-F]{1,4}:){1,2}(?::[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:(?:(?::[0-9a-fA-F]{1,4}){1,6})|:(?:(?::[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(?::[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(?:ffff(?::0{1,4}){0,1}:){0,1}(?:(?:25[0-5]|(?:2[0-4]|1{0,1}[0-9]){0,1}[0-9]).){3,3}(?:25[0-5]|(?:2[0-4]|1{0,1}[0-9]){0,1}[0-9])|(?:[0-9a-fA-F]{1,4}:){1,4}:(?:(?:25[0-5]|(?:2[0-4]|1{0,1}[0-9]){0,1}[0-9]).){3,3}(?:25[0-5]|(?:2[0-4]|1{0,1}[0-9]){0,1}[0-9]))"; + + ignoreregex = map mkV4 [ + "10" # Legacy wireguard + "129.199" # ENS + "100.76" # Netbird internal + ]; + + ignore = [ + "127.0.0.1" + "::1" + ]; + }; + }; + + start = [ + (nft '' + table inet reaction { + set ipv4bans { + type ipv4_addr + flags interval + auto-merge + } + set ipv6bans { + type ipv6_addr + flags interval + auto-merge + } + chain input { + type filter hook input priority 0 + policy accept + ip saddr @ipv4bans drop + ip6 saddr @ipv6bans drop + } + } + '') + ]; + + stop = [ (nft "delete table inet reaction") ]; + + streams = streams'.default // (streams'.${name} or { }); + }; + }; +} diff --git a/modules/dgn-firewall/streams.nix b/modules/dgn-firewall/streams.nix new file mode 100644 index 0000000..18b5caf --- /dev/null +++ b/modules/dgn-firewall/streams.nix @@ -0,0 +1,46 @@ +let + act = a: [ + "nft46" + "${a} element inet reaction ipvXbans { }" + ]; + + journalctl = u: [ + "journalctl" + "-fn0" + "-u" + "${u}.service" + ]; + + ban = after: { + ban.cmd = act "add"; + unban = { + inherit after; + + cmd = act "delete"; + }; + }; + + available = { + ssh = { + cmd = journalctl "sshd"; + + filters = { + failedlogin = { + regex = [ + "authentication failure;.*rhost=" + "Connection reset by authenticating user .* " + "Connection closed by invalid user .* port .*" + "Failed password for .* from " + "Invalid user .* from port .*" + "Unable to negotiate with port .*" + ]; + actions = ban "48h"; + }; + }; + }; + }; +in + +builtins.mapAttrs (_: builtins.foldl' (a: s: a // { ${s} = available.${s}; }) { }) { + default = [ "ssh" ]; +} diff --git a/npins/sources.json b/npins/sources.json index 7560e8f..fa3443f 100644 --- a/npins/sources.json +++ b/npins/sources.json @@ -191,9 +191,9 @@ "url": "https://git.hubrecht.ovh/hubrecht/nix-modules.git" }, "branch": "main", - "revision": "a5645b5b3bed87ce9c9d633ad20537b0edd34e74", + "revision": "32e76ee64352587663766e1a3945a6fe0917e35d", "url": null, - "hash": "1vmi9ldmc1vibw21bv1lg41mbii8v0w9k3809awyf1ikir99jbyf" + "hash": "16vnpnby6s174y4nzb26z2pc49ba7lw7vpf6r7p4dqci92b0yg5j" }, "nix-patches": { "type": "GitRelease",