diff --git a/nixos/hosts/framework-server/default.nix b/nixos/hosts/framework-server/default.nix index b7ed9c5e..fd58a129 100644 --- a/nixos/hosts/framework-server/default.nix +++ b/nixos/hosts/framework-server/default.nix @@ -14,6 +14,7 @@ ./wireguard.nix ./cron.nix ./firewall.nix + ./fail2ban/traefik.nix ]; environment.systemPackages = [ diff --git a/nixos/hosts/framework-server/fail2ban/traefik.nix b/nixos/hosts/framework-server/fail2ban/traefik.nix new file mode 100644 index 00000000..c39b2ad3 --- /dev/null +++ b/nixos/hosts/framework-server/fail2ban/traefik.nix @@ -0,0 +1,39 @@ +{ ... }: { + + # References: + # https://blog.lrvt.de/configuring-fail2ban-with-traefik/ + # https://nixos.wiki/wiki/Fail2ban#Extending_Fail2ban + + services.fail2ban.jails = { + traefik-general-forceful-browsing = { + settings = { + enabled = true; + filter = "traefik-general-forceful-browsingo"; + action = ''action-ban-docker-forceful-browsing''; + logpath = "/var/log/traefik/access.log"; + backend = "auto"; + findtime = 600; + bantime = 600; + maxretry = 5; + }; + }; + }; + + environment.etc= { + "fail2ban/filter.d/raefik-general-forceful-browsing.conf".text = pkgs.lib.mkDefault (pkgs.lib.mkAfter '' + [INCLUDES] + + [Definition] + + # fail regex based on traefik JSON access logs with enabled user agent logging + failregex = ^{"ClientAddr":".*","ClientHost":"","ClientPort":".*","ClientUsername":".*","DownstreamContentSize":.*,"DownstreamStatus":.*,"Duration":.*,"OriginContentSize":.*,"OriginDuration":.*,"OriginStatus":(405|404|403|402|401),"Overhead":.*,"RequestAddr":".*","RequestContentSize":.*,"RequestCount":.*,"RequestHost":".*","RequestMethod":".*","RequestPath":".*","RequestPort":".*","RequestProtocol":".*","RequestScheme":".*","RetryAttempts":.*,.*"StartLocal":".*","StartUTC":".*","TLSCipher":".*","TLSVersion":".*","entryPointName":".*","level":".*","msg":".*",("request_User-Agent":".*",){0,1}?"time":".*"}$ + + # custom date pattern for traefik JSON access logs + # based on https://github.com/fail2ban/fail2ban/issues/2558#issuecomment-546738270 + datepattern = "StartLocal"\s*:\s*"%%Y-%%m-%%d[T]%%H:%%M:%%S\.%%f\d*(%%z)?", + + # ignore common errors like missing media files or JS/CSS/TXT/ICO stuff + ignoreregex = ^{"ClientAddr":".*","ClientHost":"","ClientPort":".*","ClientUsername":".*","DownstreamContentSize":.*,"DownstreamStatus":.*,"Duration":.*,"OriginContentSize":.*,"OriginDuration":.*,"OriginStatus":(405|404|403|402|401),"Overhead":.*,"RequestAddr":".*","RequestContentSize":.*,"RequestCount":.*,"RequestHost":".*","RequestMethod":".*","RequestPath":".*(\.png|\.txt|\.jpg|\.ico|\.js|\.css|\.ttf|\.woff|\.woff2)(/)*?","RequestPort":".*","RequestProtocol":".*","RequestScheme":".*","RetryAttempts":.*,.*"StartLocal":".*","StartUTC":".*","TLSCipher":".*","TLSVersion":".*","entryPointName":".*","level":".*","msg":".*",("request_User-Agent":".*",){0,1}?"time":".*"}$ + ''); + }; +}