forked from DGNum/infrastructure
feat(firewall): Sunset fail2ban and switch to reaction
This commit is contained in:
parent
86c1018dc8
commit
fd0aeacff4
12 changed files with 137 additions and 214 deletions
|
@ -4,7 +4,6 @@ lib.extra.mkConfig {
|
||||||
enabledModules = [
|
enabledModules = [
|
||||||
# List of modules to enable
|
# List of modules to enable
|
||||||
"dgn-backups"
|
"dgn-backups"
|
||||||
"dgn-fail2ban"
|
|
||||||
"dgn-web"
|
"dgn-web"
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -32,11 +31,6 @@ lib.extra.mkConfig {
|
||||||
];
|
];
|
||||||
|
|
||||||
extraConfig = {
|
extraConfig = {
|
||||||
dgn-fail2ban.jails = lib.extra.enableAttrs' "enabled" [
|
|
||||||
"sshd-bruteforce"
|
|
||||||
"sshd-timeout"
|
|
||||||
];
|
|
||||||
|
|
||||||
dgn-hardware.useZfs = true;
|
dgn-hardware.useZfs = true;
|
||||||
|
|
||||||
services.netbird.enable = true;
|
services.netbird.enable = true;
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
lib.extra.mkConfig {
|
lib.extra.mkConfig {
|
||||||
enabledModules = [
|
enabledModules = [
|
||||||
# List of modules to enable
|
# List of modules to enable
|
||||||
"dgn-fail2ban"
|
|
||||||
];
|
];
|
||||||
|
|
||||||
enabledServices = [
|
enabledServices = [
|
||||||
|
@ -12,11 +11,6 @@ lib.extra.mkConfig {
|
||||||
];
|
];
|
||||||
|
|
||||||
extraConfig = {
|
extraConfig = {
|
||||||
dgn-fail2ban.jails = lib.extra.enableAttrs' "enabled" [
|
|
||||||
"sshd-bruteforce"
|
|
||||||
"sshd-timeout"
|
|
||||||
];
|
|
||||||
|
|
||||||
services.netbird.enable = true;
|
services.netbird.enable = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,6 @@ lib.extra.mkConfig {
|
||||||
enabledModules = [
|
enabledModules = [
|
||||||
# List of modules to enable
|
# List of modules to enable
|
||||||
"dgn-backups"
|
"dgn-backups"
|
||||||
"dgn-fail2ban"
|
|
||||||
"dgn-web"
|
"dgn-web"
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -22,8 +21,6 @@ lib.extra.mkConfig {
|
||||||
];
|
];
|
||||||
|
|
||||||
extraConfig = {
|
extraConfig = {
|
||||||
dgn-fail2ban.jails.sshd-preauth.enabled = true;
|
|
||||||
|
|
||||||
dgn-hardware.useZfs = true;
|
dgn-hardware.useZfs = true;
|
||||||
|
|
||||||
services.netbird.enable = true;
|
services.netbird.enable = true;
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
lib.extra.mkConfig {
|
lib.extra.mkConfig {
|
||||||
enabledModules = [
|
enabledModules = [
|
||||||
# List of modules to enable
|
# List of modules to enable
|
||||||
"dgn-fail2ban"
|
|
||||||
];
|
];
|
||||||
|
|
||||||
enabledServices = [
|
enabledServices = [
|
||||||
|
@ -14,11 +13,6 @@ lib.extra.mkConfig {
|
||||||
];
|
];
|
||||||
|
|
||||||
extraConfig = {
|
extraConfig = {
|
||||||
dgn-fail2ban.jails = lib.extra.enableAttrs' "enabled" [
|
|
||||||
"sshd-bruteforce"
|
|
||||||
"sshd-timeout"
|
|
||||||
];
|
|
||||||
|
|
||||||
services.netbird.enable = true;
|
services.netbird.enable = true;
|
||||||
services.nginx.enable = true;
|
services.nginx.enable = true;
|
||||||
networking.firewall.allowedTCPPorts = [ 80 ];
|
networking.firewall.allowedTCPPorts = [ 80 ];
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
lib.extra.mkConfig {
|
lib.extra.mkConfig {
|
||||||
enabledModules = [
|
enabledModules = [
|
||||||
# List of modules to enable
|
# List of modules to enable
|
||||||
"dgn-fail2ban"
|
|
||||||
"dgn-web"
|
"dgn-web"
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
lib.extra.mkConfig {
|
lib.extra.mkConfig {
|
||||||
enabledModules = [
|
enabledModules = [
|
||||||
# List of modules to enable
|
# List of modules to enable
|
||||||
"dgn-fail2ban"
|
|
||||||
"dgn-web"
|
"dgn-web"
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -14,11 +13,6 @@ lib.extra.mkConfig {
|
||||||
];
|
];
|
||||||
|
|
||||||
extraConfig = {
|
extraConfig = {
|
||||||
dgn-fail2ban.jails = lib.extra.enableAttrs' "enabled" [
|
|
||||||
"sshd-bruteforce"
|
|
||||||
"sshd-timeout"
|
|
||||||
];
|
|
||||||
|
|
||||||
# Restrict access to this node
|
# Restrict access to this node
|
||||||
dgn-access-control.users.root = [ "thubrecht" ];
|
dgn-access-control.users.root = [ "thubrecht" ];
|
||||||
|
|
||||||
|
|
|
@ -46,7 +46,7 @@
|
||||||
"dgn-acme"
|
"dgn-acme"
|
||||||
"dgn-backups"
|
"dgn-backups"
|
||||||
"dgn-console"
|
"dgn-console"
|
||||||
"dgn-fail2ban"
|
"dgn-firewall"
|
||||||
"dgn-hardware"
|
"dgn-hardware"
|
||||||
"dgn-netbox-agent"
|
"dgn-netbox-agent"
|
||||||
"dgn-network"
|
"dgn-network"
|
||||||
|
@ -67,6 +67,7 @@
|
||||||
[
|
[
|
||||||
"age-secrets"
|
"age-secrets"
|
||||||
"services/bupstash"
|
"services/bupstash"
|
||||||
|
"services/reaction"
|
||||||
"services/systemd-notify"
|
"services/systemd-notify"
|
||||||
]
|
]
|
||||||
++ nodeMeta.nix-modules
|
++ nodeMeta.nix-modules
|
||||||
|
|
|
@ -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";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -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;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
87
modules/dgn-firewall/default.nix
Normal file
87
modules/dgn-firewall/default.nix
Normal 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 { });
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
46
modules/dgn-firewall/streams.nix
Normal file
46
modules/dgn-firewall/streams.nix
Normal 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" ];
|
||||||
|
}
|
|
@ -191,9 +191,9 @@
|
||||||
"url": "https://git.hubrecht.ovh/hubrecht/nix-modules.git"
|
"url": "https://git.hubrecht.ovh/hubrecht/nix-modules.git"
|
||||||
},
|
},
|
||||||
"branch": "main",
|
"branch": "main",
|
||||||
"revision": "a5645b5b3bed87ce9c9d633ad20537b0edd34e74",
|
"revision": "32e76ee64352587663766e1a3945a6fe0917e35d",
|
||||||
"url": null,
|
"url": null,
|
||||||
"hash": "1vmi9ldmc1vibw21bv1lg41mbii8v0w9k3809awyf1ikir99jbyf"
|
"hash": "16vnpnby6s174y4nzb26z2pc49ba7lw7vpf6r7p4dqci92b0yg5j"
|
||||||
},
|
},
|
||||||
"nix-patches": {
|
"nix-patches": {
|
||||||
"type": "GitRelease",
|
"type": "GitRelease",
|
||||||
|
|
Loading…
Reference in a new issue