grafana-alloy: don't build the frontend twice
[NixPkgs.git] / nixos / modules / services / security / sshguard.nix
blob3be0a8c700b9e0023591f3b30522e8eeac2006ae
1 { config, lib, pkgs, ... }:
3 with lib;
5 let
6   cfg = config.services.sshguard;
8   configFile = let
9     args = lib.concatStringsSep " " ([
10       "-afb"
11       "-p info"
12       "-o cat"
13       "-n1"
14     ] ++ (map (name: "-t ${escapeShellArg name}") cfg.services));
15     backend = if config.networking.nftables.enable
16       then "sshg-fw-nft-sets"
17       else "sshg-fw-ipset";
18   in pkgs.writeText "sshguard.conf" ''
19     BACKEND="${pkgs.sshguard}/libexec/${backend}"
20     LOGREADER="LANG=C ${config.systemd.package}/bin/journalctl ${args}"
21   '';
23 in {
25   ###### interface
27   options = {
29     services.sshguard = {
30       enable = mkOption {
31         default = false;
32         type = types.bool;
33         description = "Whether to enable the sshguard service.";
34       };
36       attack_threshold = mkOption {
37         default = 30;
38         type = types.int;
39         description = ''
40             Block attackers when their cumulative attack score exceeds threshold. Most attacks have a score of 10.
41           '';
42       };
44       blacklist_threshold = mkOption {
45         default = null;
46         example = 120;
47         type = types.nullOr types.int;
48         description = ''
49             Blacklist an attacker when its score exceeds threshold. Blacklisted addresses are loaded from and added to blacklist-file.
50           '';
51       };
53       blacklist_file = mkOption {
54         default = "/var/lib/sshguard/blacklist.db";
55         type = types.path;
56         description = ''
57             Blacklist an attacker when its score exceeds threshold. Blacklisted addresses are loaded from and added to blacklist-file.
58           '';
59       };
61       blocktime = mkOption {
62         default = 120;
63         type = types.int;
64         description = ''
65             Block attackers for initially blocktime seconds after exceeding threshold. Subsequent blocks increase by a factor of 1.5.
67             sshguard unblocks attacks at random intervals, so actual block times will be longer.
68           '';
69       };
71       detection_time = mkOption {
72         default = 1800;
73         type = types.int;
74         description = ''
75             Remember potential attackers for up to detection_time seconds before resetting their score.
76           '';
77       };
79       whitelist = mkOption {
80         default = [ ];
81         example = [ "198.51.100.56" "198.51.100.2" ];
82         type = types.listOf types.str;
83         description = ''
84             Whitelist a list of addresses, hostnames, or address blocks.
85           '';
86       };
88       services = mkOption {
89         default = [ "sshd" ];
90         example = [ "sshd" "exim" ];
91         type = types.listOf types.str;
92         description = ''
93             Systemd services sshguard should receive logs of.
94           '';
95       };
96     };
97   };
99   ###### implementation
101   config = mkIf cfg.enable {
103     environment.etc."sshguard.conf".source = configFile;
105     systemd.services.sshguard = {
106       description = "SSHGuard brute-force attacks protection system";
108       wantedBy = [ "multi-user.target" ];
109       after = [ "network.target" ];
110       partOf = optional config.networking.firewall.enable "firewall.service";
112       restartTriggers = [ configFile ];
114       path = with pkgs; if config.networking.nftables.enable
115         then [ nftables iproute2 systemd ]
116         else [ iptables ipset iproute2 systemd ];
118       # The sshguard ipsets must exist before we invoke
119       # iptables. sshguard creates the ipsets after startup if
120       # necessary, but if we let sshguard do it, we can't reliably add
121       # the iptables rules because postStart races with the creation
122       # of the ipsets. So instead, we create both the ipsets and
123       # firewall rules before sshguard starts.
124       preStart = optionalString config.networking.firewall.enable ''
125         ${pkgs.ipset}/bin/ipset -quiet create -exist sshguard4 hash:net family inet
126         ${pkgs.iptables}/bin/iptables  -I INPUT -m set --match-set sshguard4 src -j DROP
127       '' + optionalString (config.networking.firewall.enable && config.networking.enableIPv6) ''
128         ${pkgs.ipset}/bin/ipset -quiet create -exist sshguard6 hash:net family inet6
129         ${pkgs.iptables}/bin/ip6tables -I INPUT -m set --match-set sshguard6 src -j DROP
130       '';
132       postStop = optionalString config.networking.firewall.enable ''
133         ${pkgs.iptables}/bin/iptables  -D INPUT -m set --match-set sshguard4 src -j DROP
134         ${pkgs.ipset}/bin/ipset -quiet destroy sshguard4
135       '' + optionalString (config.networking.firewall.enable && config.networking.enableIPv6) ''
136         ${pkgs.iptables}/bin/ip6tables -D INPUT -m set --match-set sshguard6 src -j DROP
137         ${pkgs.ipset}/bin/ipset -quiet destroy sshguard6
138       '';
140       unitConfig.Documentation = "man:sshguard(8)";
142       serviceConfig = {
143         Type = "simple";
144         ExecStart = let
145           args = lib.concatStringsSep " " ([
146             "-a ${toString cfg.attack_threshold}"
147             "-p ${toString cfg.blocktime}"
148             "-s ${toString cfg.detection_time}"
149             (optionalString (cfg.blacklist_threshold != null) "-b ${toString cfg.blacklist_threshold}:${cfg.blacklist_file}")
150           ] ++ (map (name: "-w ${escapeShellArg name}") cfg.whitelist));
151         in "${pkgs.sshguard}/bin/sshguard ${args}";
152         Restart = "always";
153         ProtectSystem = "strict";
154         ProtectHome = "tmpfs";
155         RuntimeDirectory = "sshguard";
156         StateDirectory = "sshguard";
157         CapabilityBoundingSet = "CAP_NET_ADMIN CAP_NET_RAW";
158       };
159     };
160   };