python3Packages.orjson: Disable failing tests on 32 bit
[NixPkgs.git] / nixos / modules / services / networking / keepalived / default.nix
blobe9df08f00c37d91912cd5d1b14cbc8202dcd01a0
1 { config, lib, pkgs, ... }:
3 with lib;
5 let
7   cfg = config.services.keepalived;
9   keepalivedConf = pkgs.writeText "keepalived.conf" ''
10     global_defs {
11       ${optionalString cfg.enableScriptSecurity "enable_script_security"}
12       ${snmpGlobalDefs}
13       ${cfg.extraGlobalDefs}
14     }
16     ${vrrpScriptStr}
17     ${vrrpInstancesStr}
18     ${cfg.extraConfig}
19   '';
21   snmpGlobalDefs = with cfg.snmp; optionalString enable (
22     optionalString (socket != null) "snmp_socket ${socket}\n"
23     + optionalString enableKeepalived "enable_snmp_keepalived\n"
24     + optionalString enableChecker "enable_snmp_checker\n"
25     + optionalString enableRfc "enable_snmp_rfc\n"
26     + optionalString enableRfcV2 "enable_snmp_rfcv2\n"
27     + optionalString enableRfcV3 "enable_snmp_rfcv3\n"
28     + optionalString enableTraps "enable_traps"
29   );
31   vrrpScriptStr = concatStringsSep "\n" (map (s:
32     ''
33       vrrp_script ${s.name} {
34         script "${s.script}"
35         interval ${toString s.interval}
36         fall ${toString s.fall}
37         rise ${toString s.rise}
38         timeout ${toString s.timeout}
39         weight ${toString s.weight}
40         user ${s.user} ${optionalString (s.group != null) s.group}
42         ${s.extraConfig}
43       }
44     ''
45   ) vrrpScripts);
47   vrrpInstancesStr = concatStringsSep "\n" (map (i:
48     ''
49       vrrp_instance ${i.name} {
50         interface ${i.interface}
51         state ${i.state}
52         virtual_router_id ${toString i.virtualRouterId}
53         priority ${toString i.priority}
54         ${optionalString i.noPreempt "nopreempt"}
56         ${optionalString i.useVmac (
57           "use_vmac" + optionalString (i.vmacInterface != null) " ${i.vmacInterface}"
58         )}
59         ${optionalString i.vmacXmitBase "vmac_xmit_base"}
61         ${optionalString (i.unicastSrcIp != null) "unicast_src_ip ${i.unicastSrcIp}"}
62         unicast_peer {
63           ${concatStringsSep "\n" i.unicastPeers}
64         }
66         virtual_ipaddress {
67           ${concatMapStringsSep "\n" virtualIpLine i.virtualIps}
68         }
70         ${optionalString (builtins.length i.trackScripts > 0) ''
71           track_script {
72             ${concatStringsSep "\n" i.trackScripts}
73           }
74         ''}
76         ${optionalString (builtins.length i.trackInterfaces > 0) ''
77           track_interface {
78             ${concatStringsSep "\n" i.trackInterfaces}
79           }
80         ''}
82         ${i.extraConfig}
83       }
84     ''
85   ) vrrpInstances);
87   virtualIpLine = (ip:
88     ip.addr
89     + optionalString (notNullOrEmpty ip.brd) " brd ${ip.brd}"
90     + optionalString (notNullOrEmpty ip.dev) " dev ${ip.dev}"
91     + optionalString (notNullOrEmpty ip.scope) " scope ${ip.scope}"
92     + optionalString (notNullOrEmpty ip.label) " label ${ip.label}"
93   );
95   notNullOrEmpty = s: !(s == null || s == "");
97   vrrpScripts = mapAttrsToList (name: config:
98     {
99       inherit name;
100     } // config
101   ) cfg.vrrpScripts;
103   vrrpInstances = mapAttrsToList (iName: iConfig:
104     {
105       name = iName;
106     } // iConfig
107   ) cfg.vrrpInstances;
109   vrrpInstanceAssertions = i: [
110     { assertion = i.interface != "";
111       message = "services.keepalived.vrrpInstances.${i.name}.interface option cannot be empty.";
112     }
113     { assertion = i.virtualRouterId >= 0 && i.virtualRouterId <= 255;
114       message = "services.keepalived.vrrpInstances.${i.name}.virtualRouterId must be an integer between 0..255.";
115     }
116     { assertion = i.priority >= 0 && i.priority <= 255;
117       message = "services.keepalived.vrrpInstances.${i.name}.priority must be an integer between 0..255.";
118     }
119     { assertion = i.vmacInterface == null || i.useVmac;
120       message = "services.keepalived.vrrpInstances.${i.name}.vmacInterface has no effect when services.keepalived.vrrpInstances.${i.name}.useVmac is not set.";
121     }
122     { assertion = !i.vmacXmitBase || i.useVmac;
123       message = "services.keepalived.vrrpInstances.${i.name}.vmacXmitBase has no effect when services.keepalived.vrrpInstances.${i.name}.useVmac is not set.";
124     }
125   ] ++ flatten (map (virtualIpAssertions i.name) i.virtualIps)
126     ++ flatten (map (vrrpScriptAssertion i.name) i.trackScripts);
128   virtualIpAssertions = vrrpName: ip: [
129     { assertion = ip.addr != "";
130       message = "The 'addr' option for an services.keepalived.vrrpInstances.${vrrpName}.virtualIps entry cannot be empty.";
131     }
132   ];
134   vrrpScriptAssertion = vrrpName: scriptName: {
135     assertion = builtins.hasAttr scriptName cfg.vrrpScripts;
136     message = "services.keepalived.vrrpInstances.${vrrpName} trackscript ${scriptName} is not defined in services.keepalived.vrrpScripts.";
137   };
139   pidFile = "/run/keepalived.pid";
144   options = {
145     services.keepalived = {
147       enable = mkOption {
148         type = types.bool;
149         default = false;
150         description = lib.mdDoc ''
151           Whether to enable Keepalived.
152         '';
153       };
155       enableScriptSecurity = mkOption {
156         type = types.bool;
157         default = false;
158         description = lib.mdDoc ''
159           Don't run scripts configured to be run as root if any part of the path is writable by a non-root user.
160         '';
161       };
163       snmp = {
165         enable = mkOption {
166           type = types.bool;
167           default = false;
168           description = lib.mdDoc ''
169             Whether to enable the builtin AgentX subagent.
170           '';
171         };
173         socket = mkOption {
174           type = types.nullOr types.str;
175           default = null;
176           description = lib.mdDoc ''
177             Socket to use for connecting to SNMP master agent. If this value is
178             set to null, keepalived's default will be used, which is
179             unix:/var/agentx/master, unless using a network namespace, when the
180             default is udp:localhost:705.
181           '';
182         };
184         enableKeepalived = mkOption {
185           type = types.bool;
186           default = false;
187           description = lib.mdDoc ''
188             Enable SNMP handling of vrrp element of KEEPALIVED MIB.
189           '';
190         };
192         enableChecker = mkOption {
193           type = types.bool;
194           default = false;
195           description = lib.mdDoc ''
196             Enable SNMP handling of checker element of KEEPALIVED MIB.
197           '';
198         };
200         enableRfc = mkOption {
201           type = types.bool;
202           default = false;
203           description = lib.mdDoc ''
204             Enable SNMP handling of RFC2787 and RFC6527 VRRP MIBs.
205           '';
206         };
208         enableRfcV2 = mkOption {
209           type = types.bool;
210           default = false;
211           description = lib.mdDoc ''
212             Enable SNMP handling of RFC2787 VRRP MIB.
213           '';
214         };
216         enableRfcV3 = mkOption {
217           type = types.bool;
218           default = false;
219           description = lib.mdDoc ''
220             Enable SNMP handling of RFC6527 VRRP MIB.
221           '';
222         };
224         enableTraps = mkOption {
225           type = types.bool;
226           default = false;
227           description = lib.mdDoc ''
228             Enable SNMP traps.
229           '';
230         };
232       };
234       vrrpScripts = mkOption {
235         type = types.attrsOf (types.submodule (import ./vrrp-script-options.nix {
236           inherit lib;
237         }));
238         default = {};
239         description = lib.mdDoc "Declarative vrrp script config";
240       };
242       vrrpInstances = mkOption {
243         type = types.attrsOf (types.submodule (import ./vrrp-instance-options.nix {
244           inherit lib;
245         }));
246         default = {};
247         description = lib.mdDoc "Declarative vhost config";
248       };
250       extraGlobalDefs = mkOption {
251         type = types.lines;
252         default = "";
253         description = lib.mdDoc ''
254           Extra lines to be added verbatim to the 'global_defs' block of the
255           configuration file
256         '';
257       };
259       extraConfig = mkOption {
260         type = types.lines;
261         default = "";
262         description = lib.mdDoc ''
263           Extra lines to be added verbatim to the configuration file.
264         '';
265       };
267       secretFile = mkOption {
268         type = types.nullOr types.path;
269         default = null;
270         example = "/run/keys/keepalived.env";
271         description = lib.mdDoc ''
272           Environment variables from this file will be interpolated into the
273           final config file using envsubst with this syntax: `$ENVIRONMENT`
274           or `''${VARIABLE}`.
275           The file should contain lines formatted as `SECRET_VAR=SECRET_VALUE`.
276           This is useful to avoid putting secrets into the nix store.
277         '';
278       };
280     };
281   };
283   config = mkIf cfg.enable {
285     assertions = flatten (map vrrpInstanceAssertions vrrpInstances);
287     systemd.timers.keepalived-boot-delay = {
288       description = "Keepalive Daemon delay to avoid instant transition to MASTER state";
289       after = [ "network.target" "network-online.target" "syslog.target" ];
290       requires = [ "network-online.target" ];
291       wantedBy = [ "multi-user.target" ];
292       timerConfig = {
293         OnActiveSec = "5s";
294         Unit = "keepalived.service";
295       };
296     };
298     systemd.services.keepalived = let
299       finalConfigFile = if cfg.secretFile == null then keepalivedConf else "/run/keepalived/keepalived.conf";
300     in {
301       description = "Keepalive Daemon (LVS and VRRP)";
302       after = [ "network.target" "network-online.target" "syslog.target" ];
303       wants = [ "network-online.target" ];
304       serviceConfig = {
305         Type = "forking";
306         PIDFile = pidFile;
307         KillMode = "process";
308         RuntimeDirectory = "keepalived";
309         EnvironmentFile = lib.optional (cfg.secretFile != null) cfg.secretFile;
310         ExecStartPre = lib.optional (cfg.secretFile != null)
311         (pkgs.writeShellScript "keepalived-pre-start" ''
312           umask 077
313           ${pkgs.envsubst}/bin/envsubst -i "${keepalivedConf}" > ${finalConfigFile}
314         '');
315         ExecStart = "${pkgs.keepalived}/sbin/keepalived"
316           + " -f ${finalConfigFile}"
317           + " -p ${pidFile}"
318           + optionalString cfg.snmp.enable " --snmp";
319         ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
320         Restart = "always";
321         RestartSec = "1s";
322       };
323     };
324   };