at 21.11-pre 11 kB view raw
1{ config, lib, pkgs, ... }: 2 3with lib; 4 5let 6 7 cfg = config.services.fail2ban; 8 9 fail2banConf = pkgs.writeText "fail2ban.local" cfg.daemonConfig; 10 11 jailConf = pkgs.writeText "jail.local" '' 12 [INCLUDES] 13 14 before = paths-nixos.conf 15 16 ${concatStringsSep "\n" (attrValues (flip mapAttrs cfg.jails (name: def: 17 optionalString (def != "") 18 '' 19 [${name}] 20 ${def} 21 '')))} 22 ''; 23 24 pathsConf = pkgs.writeText "paths-nixos.conf" '' 25 # NixOS 26 27 [INCLUDES] 28 29 before = paths-common.conf 30 31 after = paths-overrides.local 32 33 [DEFAULT] 34 ''; 35 36in 37 38{ 39 40 ###### interface 41 42 options = { 43 44 services.fail2ban = { 45 enable = mkOption { 46 default = false; 47 type = types.bool; 48 description = "Whether to enable the fail2ban service."; 49 }; 50 51 package = mkOption { 52 default = pkgs.fail2ban; 53 type = types.package; 54 example = "pkgs.fail2ban_0_11"; 55 description = "The fail2ban package to use for running the fail2ban service."; 56 }; 57 58 packageFirewall = mkOption { 59 default = pkgs.iptables; 60 type = types.package; 61 example = "pkgs.nftables"; 62 description = "The firewall package used by fail2ban service."; 63 }; 64 65 extraPackages = mkOption { 66 default = []; 67 type = types.listOf types.package; 68 example = lib.literalExample "[ pkgs.ipset ]"; 69 description = '' 70 Extra packages to be made available to the fail2ban service. The example contains 71 the packages needed by the `iptables-ipset-proto6` action. 72 ''; 73 }; 74 75 maxretry = mkOption { 76 default = 3; 77 type = types.ints.unsigned; 78 description = "Number of failures before a host gets banned."; 79 }; 80 81 banaction = mkOption { 82 default = "iptables-multiport"; 83 type = types.str; 84 example = "nftables-multiport"; 85 description = '' 86 Default banning action (e.g. iptables, iptables-new, iptables-multiport, 87 shorewall, etc) It is used to define action_* variables. Can be overridden 88 globally or per section within jail.local file 89 ''; 90 }; 91 92 banaction-allports = mkOption { 93 default = "iptables-allport"; 94 type = types.str; 95 example = "nftables-allport"; 96 description = '' 97 Default banning action (e.g. iptables, iptables-new, iptables-multiport, 98 shorewall, etc) It is used to define action_* variables. Can be overridden 99 globally or per section within jail.local file 100 ''; 101 }; 102 103 bantime-increment.enable = mkOption { 104 default = false; 105 type = types.bool; 106 description = '' 107 Allows to use database for searching of previously banned ip's to increase 108 a default ban time using special formula, default it is banTime * 1, 2, 4, 8, 16, 32... 109 ''; 110 }; 111 112 bantime-increment.rndtime = mkOption { 113 default = "4m"; 114 type = types.str; 115 example = "8m"; 116 description = '' 117 "bantime-increment.rndtime" is the max number of seconds using for mixing with random time 118 to prevent "clever" botnets calculate exact time IP can be unbanned again 119 ''; 120 }; 121 122 bantime-increment.maxtime = mkOption { 123 default = "10h"; 124 type = types.str; 125 example = "48h"; 126 description = '' 127 "bantime-increment.maxtime" is the max number of seconds using the ban time can reach (don't grows further) 128 ''; 129 }; 130 131 bantime-increment.factor = mkOption { 132 default = "1"; 133 type = types.str; 134 example = "4"; 135 description = '' 136 "bantime-increment.factor" is a coefficient to calculate exponent growing of the formula or common multiplier, 137 default value of factor is 1 and with default value of formula, the ban time grows by 1, 2, 4, 8, 16 ... 138 ''; 139 }; 140 141 bantime-increment.formula = mkOption { 142 default = "ban.Time * (1<<(ban.Count if ban.Count<20 else 20)) * banFactor"; 143 type = types.str; 144 example = "ban.Time * math.exp(float(ban.Count+1)*banFactor)/math.exp(1*banFactor)"; 145 description = '' 146 "bantime-increment.formula" used by default to calculate next value of ban time, default value bellow, 147 the same ban time growing will be reached by multipliers 1, 2, 4, 8, 16, 32... 148 ''; 149 }; 150 151 bantime-increment.multipliers = mkOption { 152 default = "1 2 4 8 16 32 64"; 153 type = types.str; 154 example = "2 4 16 128"; 155 description = '' 156 "bantime-increment.multipliers" used to calculate next value of ban time instead of formula, coresponding 157 previously ban count and given "bantime.factor" (for multipliers default is 1); 158 following example grows ban time by 1, 2, 4, 8, 16 ... and if last ban count greater as multipliers count, 159 always used last multiplier (64 in example), for factor '1' and original ban time 600 - 10.6 hours 160 ''; 161 }; 162 163 bantime-increment.overalljails = mkOption { 164 default = false; 165 type = types.bool; 166 example = true; 167 description = '' 168 "bantime-increment.overalljails" (if true) specifies the search of IP in the database will be executed 169 cross over all jails, if false (dafault), only current jail of the ban IP will be searched 170 ''; 171 }; 172 173 ignoreIP = mkOption { 174 default = [ ]; 175 type = types.listOf types.str; 176 example = [ "192.168.0.0/16" "2001:DB8::42" ]; 177 description = '' 178 "ignoreIP" can be a list of IP addresses, CIDR masks or DNS hosts. Fail2ban will not ban a host which 179 matches an address in this list. Several addresses can be defined using space (and/or comma) separator. 180 ''; 181 }; 182 183 daemonConfig = mkOption { 184 default = '' 185 [Definition] 186 logtarget = SYSLOG 187 socket = /run/fail2ban/fail2ban.sock 188 pidfile = /run/fail2ban/fail2ban.pid 189 dbfile = /var/lib/fail2ban/fail2ban.sqlite3 190 ''; 191 type = types.lines; 192 description = '' 193 The contents of Fail2ban's main configuration file. It's 194 generally not necessary to change it. 195 ''; 196 }; 197 198 jails = mkOption { 199 default = { }; 200 example = literalExample '' 201 { apache-nohome-iptables = ''' 202 # Block an IP address if it accesses a non-existent 203 # home directory more than 5 times in 10 minutes, 204 # since that indicates that it's scanning. 205 filter = apache-nohome 206 action = iptables-multiport[name=HTTP, port="http,https"] 207 logpath = /var/log/httpd/error_log* 208 findtime = 600 209 bantime = 600 210 maxretry = 5 211 '''; 212 } 213 ''; 214 type = types.attrsOf types.lines; 215 description = '' 216 The configuration of each Fail2ban jail. A jail 217 consists of an action (such as blocking a port using 218 <command>iptables</command>) that is triggered when a 219 filter applied to a log file triggers more than a certain 220 number of times in a certain time period. Actions are 221 defined in <filename>/etc/fail2ban/action.d</filename>, 222 while filters are defined in 223 <filename>/etc/fail2ban/filter.d</filename>. 224 ''; 225 }; 226 227 }; 228 229 }; 230 231 ###### implementation 232 233 config = mkIf cfg.enable { 234 235 warnings = mkIf (config.networking.firewall.enable == false && config.networking.nftables.enable == false) [ 236 "fail2ban can not be used without a firewall" 237 ]; 238 239 environment.systemPackages = [ cfg.package ]; 240 241 environment.etc = { 242 "fail2ban/fail2ban.local".source = fail2banConf; 243 "fail2ban/jail.local".source = jailConf; 244 "fail2ban/fail2ban.conf".source = "${cfg.package}/etc/fail2ban/fail2ban.conf"; 245 "fail2ban/jail.conf".source = "${cfg.package}/etc/fail2ban/jail.conf"; 246 "fail2ban/paths-common.conf".source = "${cfg.package}/etc/fail2ban/paths-common.conf"; 247 "fail2ban/paths-nixos.conf".source = pathsConf; 248 "fail2ban/action.d".source = "${cfg.package}/etc/fail2ban/action.d/*.conf"; 249 "fail2ban/filter.d".source = "${cfg.package}/etc/fail2ban/filter.d/*.conf"; 250 }; 251 252 systemd.services.fail2ban = { 253 description = "Fail2ban Intrusion Prevention System"; 254 255 wantedBy = [ "multi-user.target" ]; 256 after = [ "network.target" ]; 257 partOf = optional config.networking.firewall.enable "firewall.service"; 258 259 restartTriggers = [ fail2banConf jailConf pathsConf ]; 260 reloadIfChanged = true; 261 262 path = [ cfg.package cfg.packageFirewall pkgs.iproute2 ] ++ cfg.extraPackages; 263 264 unitConfig.Documentation = "man:fail2ban(1)"; 265 266 serviceConfig = { 267 ExecStart = "${cfg.package}/bin/fail2ban-server -xf start"; 268 ExecStop = "${cfg.package}/bin/fail2ban-server stop"; 269 ExecReload = "${cfg.package}/bin/fail2ban-server reload"; 270 Type = "simple"; 271 Restart = "on-failure"; 272 PIDFile = "/run/fail2ban/fail2ban.pid"; 273 # Capabilities 274 CapabilityBoundingSet = [ "CAP_AUDIT_READ" "CAP_DAC_READ_SEARCH" "CAP_NET_ADMIN" "CAP_NET_RAW" ]; 275 # Security 276 NoNewPrivileges = true; 277 # Directory 278 RuntimeDirectory = "fail2ban"; 279 RuntimeDirectoryMode = "0750"; 280 StateDirectory = "fail2ban"; 281 StateDirectoryMode = "0750"; 282 LogsDirectory = "fail2ban"; 283 LogsDirectoryMode = "0750"; 284 # Sandboxing 285 ProtectSystem = "strict"; 286 ProtectHome = true; 287 PrivateTmp = true; 288 PrivateDevices = true; 289 ProtectHostname = true; 290 ProtectKernelTunables = true; 291 ProtectKernelModules = true; 292 ProtectControlGroups = true; 293 }; 294 }; 295 296 # Add some reasonable default jails. The special "DEFAULT" jail 297 # sets default values for all other jails. 298 services.fail2ban.jails.DEFAULT = '' 299 ${optionalString cfg.bantime-increment.enable '' 300 # Bantime incremental 301 bantime.increment = ${boolToString cfg.bantime-increment.enable} 302 bantime.maxtime = ${cfg.bantime-increment.maxtime} 303 bantime.factor = ${cfg.bantime-increment.factor} 304 bantime.formula = ${cfg.bantime-increment.formula} 305 bantime.multipliers = ${cfg.bantime-increment.multipliers} 306 bantime.overalljails = ${boolToString cfg.bantime-increment.overalljails} 307 ''} 308 # Miscellaneous options 309 ignoreip = 127.0.0.1/8 ${optionalString config.networking.enableIPv6 "::1"} ${concatStringsSep " " cfg.ignoreIP} 310 maxretry = ${toString cfg.maxretry} 311 backend = systemd 312 # Actions 313 banaction = ${cfg.banaction} 314 banaction_allports = ${cfg.banaction-allports} 315 ''; 316 # Block SSH if there are too many failing connection attempts. 317 services.fail2ban.jails.sshd = mkDefault '' 318 enabled = true 319 port = ${concatMapStringsSep "," (p: toString p) config.services.openssh.ports} 320 ''; 321 }; 322}