feat(firewall): Sunset fail2ban and switch to reaction

This commit is contained in:
Tom Hubrecht 2024-09-01 22:47:56 +02:00
parent 86c1018dc8
commit fd0aeacff4
Signed by: thubrecht
SSH key fingerprint: SHA256:r+nK/SIcWlJ0zFZJGHtlAoRwq1Rm+WcKAm5ADYMoQPc
12 changed files with 137 additions and 214 deletions

View file

@ -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;

View file

@ -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;
};

View file

@ -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;

View file

@ -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 ];

View file

@ -3,7 +3,6 @@
lib.extra.mkConfig {
enabledModules = [
# List of modules to enable
"dgn-fail2ban"
"dgn-web"
];

View file

@ -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" ];

View file

@ -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

View file

@ -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";
};
};
}

View file

@ -1,93 +0,0 @@
# Copyright Tom Hubrecht, (2023)
#
# Tom Hubrecht <tom@hubrecht.ovh>
#
# 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 = ''^<HOST>.*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\\.\\-]+\\[<HOST>\\]: 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=<ADDR>.*$";
journalmatch = "_SYSTEMD_UNIT=sshd.service";
};
settings = {
findtime = 600;
maxretry = 1;
};
};
sshd-preauth = {
filter.Definition = {
failregex = "Received disconnect from <ADDR> port .* Bye Bye \\[preauth\\]$";
journalmatch = "_SYSTEMD_UNIT=sshd.service";
};
settings = {
findtime = 600;
maxretry = 1;
};
};
sshd-timeout = {
filter.Definition = {
failregex = "fatal: Timeout before authentication for <ADDR>.*$";
journalmatch = "_SYSTEMD_UNIT=sshd.service";
};
settings = {
findtime = 600;
maxretry = 1;
};
};
}

View file

@ -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 { });
};
};
}

View file

@ -0,0 +1,46 @@
let
act = a: [
"nft46"
"${a} element inet reaction ipvXbans { <ip> }"
];
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=<ip>"
"Connection reset by authenticating user .* <ip>"
"Connection closed by invalid user .* <ip> port .*"
"Failed password for .* from <ip>"
"Invalid user .* from <ip> port .*"
"Unable to negotiate with <ip> port .*"
];
actions = ban "48h";
};
};
};
};
in
builtins.mapAttrs (_: builtins.foldl' (a: s: a // { ${s} = available.${s}; }) { }) {
default = [ "ssh" ];
}

View file

@ -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",