❄️ Dotfiles for our NixOS system configuration.
at master 3.8 kB view raw
1{ pkgs, ... }: 2 3{ 4 age.secrets.abuseipdb = { 5 file = ../../secrets/abuseipdb.age; 6 mode = "600"; 7 }; 8 9 services.fail2ban = { 10 enable = true; 11 12 # Include curl for making HTTP requests to AbuseIPDB 13 extraPackages = [ pkgs.curl ]; 14 15 # Globally applicable settings for all jails 16 daemonSettings = { 17 Definition = { 18 # How long to keep an IP in the ban list (in seconds) 19 # 1 day = 86400 seconds 20 bantime = "86400"; 21 22 # How far back to look for failures (in seconds) 23 # 1 hour = 3600 seconds 24 findtime = "3600"; 25 26 # Number of failures before banning 27 maxretry = 3; 28 29 # Allow fail2ban to write to syslog 30 logtarget = "SYSLOG"; 31 }; 32 }; 33 34 # Ignore local networks and trusted services 35 ignoreIP = [ 36 "127.0.0.1/8" 37 "::1" 38 "100.64.0.0/10" # Tailscale IP range 39 ]; 40 41 # Jails for protecting various services 42 jails = { 43 # SSH protection - monitor failed login attempts using systemd journal 44 sshd.settings = { 45 enabled = true; 46 port = "ssh"; 47 filter = "sshd"; 48 backend = "systemd"; 49 maxretry = 5; 50 findtime = "3600"; 51 bantime = "86400"; 52 action = "iptables-multiport[name=SSH, port='ssh'] abuseipdb-agenix[abuseipdb_category='18,22']"; 53 }; 54 55 # Caddy HTTP/HTTPS protection - monitor for repeated 4xx/5xx errors 56 caddy-http.settings = { 57 enabled = true; 58 port = "http,https"; 59 filter = "caddy-http"; 60 logpath = "/var/log/caddy/access-*.log"; 61 backend = "auto"; 62 maxretry = 10; 63 findtime = "600"; 64 bantime = "3600"; 65 action = "iptables-multiport[name=Caddy, port='http,https'] abuseipdb-agenix[abuseipdb_category='21']"; 66 }; 67 68 # Rate-based protection - ban on excessive requests 69 caddy-ratelimit.settings = { 70 enabled = true; 71 port = "http,https"; 72 filter = "caddy-ratelimit"; 73 logpath = "/var/log/caddy/access-*.log"; 74 backend = "auto"; 75 maxretry = 50; 76 findtime = "60"; 77 bantime = "1800"; 78 action = "iptables-multiport[name=Caddy-RateLimit, port='http,https'] abuseipdb-agenix[abuseipdb_category='21']"; 79 }; 80 }; 81 }; 82 83 # Custom filters for Fail2Ban 84 environment.etc = { 85 # Caddy HTTP error monitoring filter 86 "fail2ban/filter.d/caddy-http.conf".text = '' 87 [Definition] 88 failregex = ^<HOST> -.*" (?:400|401|403|404|405|429|500|502|503|504) .*$ 89 ignoreregex = 90 ''; 91 92 # Caddy rate limiting filter - detects repeated requests within short timeframe 93 "fail2ban/filter.d/caddy-ratelimit.conf".text = '' 94 [Definition] 95 failregex = ^<HOST> -.*" \d{3} .*$ 96 ignoreregex = 97 ''; 98 99 # Custom abuseipdb action that reads API key from file 100 "fail2ban/action.d/abuseipdb-agenix.conf".text = '' 101 [Definition] 102 # Report IP to AbuseIPDB, reading API key from Agenix secret file 103 # Uses double quotes to allow shell expansion of $(cat /run/agenix/abuseipdb) 104 # Sleep 12 seconds to respect AbuseIPDB rate limit (~5 requests per minute) 105 # Note: Don't use <matches> - fail2ban's wrapper causes issues with multiline content 106 # Don't use --fail so 429 rate limit errors don't mark action as failed 107 actionban = sleep 12; curl 'https://api.abuseipdb.com/api/v2/report' -H 'Accept: application/json' -H "Key: $(cat /run/agenix/abuseipdb)" --data-urlencode 'ip=<ip>' --data 'categories=<abuseipdb_category>' > /dev/null 2>&1 108 109 actionstart = 110 actionstop = 111 actioncheck = 112 actionunban = 113 114 [Init] 115 abuseipdb_category = 18 116 ''; 117 }; 118 119 # Ensure the log directory exists 120 systemd.tmpfiles.rules = [ 121 "d /var/log/fail2ban 0755 root root -" 122 ]; 123}