fail2ban: add jails for SSH, nginx proxy manager, and Jellyfin

Replaces bare enable flag with a dedicated service module covering:
- SSH brute force via journald
- Nginx Proxy Manager auth failures via Docker log files
- Jellyfin auth failures via journald
Includes incremental ban times (up to 1 week) and LAN ignore rules.

https://claude.ai/code/session_01PwAXuaoJx7qD5FhVLsn7Sn
This commit is contained in:
Claude 2026-04-06 08:21:23 +00:00
parent f5bb08d7dd
commit 16363dc887
No known key found for this signature in database
3 changed files with 76 additions and 2 deletions

View file

@ -30,6 +30,7 @@
./services/bazarr.nix
./services/cloudflare-ddns.nix
./services/crowdsec.nix
./services/fail2ban.nix
];
### Make build time quicker

View file

@ -18,8 +18,6 @@
yt-dlp
];
services.fail2ban.enable = true;
# Enable Docker
virtualisation.docker.enable = true;

75
services/fail2ban.nix Normal file
View file

@ -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: "<HOST>"\).*$
^.*Error processing request from remote IP Address <HOST>.*$
ignoreregex =
'';
};
}