diff --git a/common.nix b/common.nix index 416895e..b484cd0 100644 --- a/common.nix +++ b/common.nix @@ -30,6 +30,7 @@ ./services/bazarr.nix ./services/cloudflare-ddns.nix ./services/crowdsec.nix + ./services/fail2ban.nix ]; ### Make build time quicker diff --git a/hosts/FredOS-Mediaserver.nix b/hosts/FredOS-Mediaserver.nix index a27c18a..72ed92a 100644 --- a/hosts/FredOS-Mediaserver.nix +++ b/hosts/FredOS-Mediaserver.nix @@ -18,8 +18,6 @@ yt-dlp ]; - services.fail2ban.enable = true; - # Enable Docker virtualisation.docker.enable = true; diff --git a/services/fail2ban.nix b/services/fail2ban.nix new file mode 100644 index 0000000..c2d3057 --- /dev/null +++ b/services/fail2ban.nix @@ -0,0 +1,75 @@ +{ config, lib, pkgs, ... }: +{ + config = lib.mkIf (config.networking.hostName == "FredOS-Mediaserver") { + + services.fail2ban = { + enable = true; + + # Default ban settings (overridable per jail) + maxretry = 5; + bantime = "1h"; + + # Progressively longer bans for repeat offenders, up to 1 week + bantime-increment = { + enable = true; + multiplier = "1 2 4 8 16 32 64"; + maxtime = "168h"; + overalljails = true; + }; + + # Never ban local network traffic + ignoreIP = [ + "127.0.0.1/8" + "::1" + "192.168.0.0/16" + "10.0.0.0/8" + ]; + + jails = { + + # SSH brute force — uses built-in sshd filter via journald + sshd = { + settings = { + enabled = true; + filter = "sshd"; + maxretry = 5; + bantime = "1h"; + }; + }; + + # Nginx Proxy Manager — watches Docker-mounted log files + # Catches repeated 401/403 responses (auth failures, bad requests) + nginx-proxy-manager = { + settings = { + enabled = true; + filter = "nginx-http-auth"; + logpath = "/home/fred/docker/nginx-proxy-manager/data/logs/*.log"; + maxretry = 10; + bantime = "1h"; + }; + }; + + # Jellyfin auth failures — uses journald backend + jellyfin = { + settings = { + enabled = true; + backend = "systemd"; + journalmatch = "_SYSTEMD_UNIT=jellyfin.service"; + maxretry = 5; + bantime = "2h"; + }; + }; + + }; + }; + + # Custom Jellyfin filter — matches failed auth log lines from the journal + environment.etc."fail2ban/filter.d/jellyfin.conf".text = '' + [Definition] + failregex = ^.*Authentication request for .* has been denied \(IP: ""\).*$ + ^.*Error processing request from remote IP Address .*$ + ignoreregex = + ''; + + }; +}