diff --git a/nixos/modules/services/networking/keepalived/default.nix b/nixos/modules/services/networking/keepalived/default.nix index 378cd9365848..c9ac2ee25990 100644 --- a/nixos/modules/services/networking/keepalived/default.nix +++ b/nixos/modules/services/networking/keepalived/default.nix @@ -8,10 +8,12 @@ let keepalivedConf = pkgs.writeText "keepalived.conf" '' global_defs { + ${optionalString cfg.enableScriptSecurity "enable_script_security"} ${snmpGlobalDefs} ${cfg.extraGlobalDefs} } + ${vrrpScriptStr} ${vrrpInstancesStr} ${cfg.extraConfig} ''; @@ -26,6 +28,22 @@ let + optionalString enableTraps "enable_traps" ); + vrrpScriptStr = concatStringsSep "\n" (map (s: + '' + vrrp_script ${s.name} { + script "${s.script}" + interval ${toString s.interval} + fall ${toString s.fall} + rise ${toString s.rise} + timeout ${toString s.timeout} + weight ${toString s.weight} + user ${s.user} ${optionalString (s.group != null) s.group} + + ${s.extraConfig} + } + '' + ) vrrpScripts); + vrrpInstancesStr = concatStringsSep "\n" (map (i: '' vrrp_instance ${i.name} { @@ -49,6 +67,18 @@ let ${concatMapStringsSep "\n" virtualIpLine i.virtualIps} } + ${optionalString (builtins.length i.trackScripts > 0) '' + track_script { + ${concatStringsSep "\n" i.trackScripts} + } + ''} + + ${optionalString (builtins.length i.trackInterfaces > 0) '' + track_interface { + ${concatStringsSep "\n" i.trackInterfaces} + } + ''} + ${i.extraConfig} } '' @@ -64,6 +94,12 @@ let notNullOrEmpty = s: !(s == null || s == ""); + vrrpScripts = mapAttrsToList (name: config: + { + inherit name; + } // config + ) cfg.vrrpScripts; + vrrpInstances = mapAttrsToList (iName: iConfig: { name = iName; @@ -86,7 +122,8 @@ let { assertion = !i.vmacXmitBase || i.useVmac; message = "services.keepalived.vrrpInstances.${i.name}.vmacXmitBase has no effect when services.keepalived.vrrpInstances.${i.name}.useVmac is not set."; } - ] ++ flatten (map (virtualIpAssertions i.name) i.virtualIps); + ] ++ flatten (map (virtualIpAssertions i.name) i.virtualIps) + ++ flatten (map (vrrpScriptAssertion i.name) i.trackScripts); virtualIpAssertions = vrrpName: ip: [ { assertion = ip.addr != ""; @@ -94,6 +131,11 @@ let } ]; + vrrpScriptAssertion = vrrpName: scriptName: { + assertion = builtins.hasAttr scriptName cfg.vrrpScripts; + message = "services.keepalived.vrrpInstances.${vrrpName} trackscript ${scriptName} is not defined in services.keepalived.vrrpScripts."; + }; + pidFile = "/run/keepalived.pid"; in @@ -110,6 +152,14 @@ in ''; }; + enableScriptSecurity = mkOption { + type = types.bool; + default = false; + description = '' + Don't run scripts configured to be run as root if any part of the path is writable by a non-root user. + ''; + }; + snmp = { enable = mkOption { @@ -181,8 +231,16 @@ in }; + vrrpScripts = mkOption { + type = types.attrsOf (types.submodule (import ./vrrp-script-options.nix { + inherit lib; + })); + default = {}; + description = "Declarative vrrp script config"; + }; + vrrpInstances = mkOption { - type = types.attrsOf (types.submodule (import ./vrrp-options.nix { + type = types.attrsOf (types.submodule (import ./vrrp-instance-options.nix { inherit lib; })); default = {}; diff --git a/nixos/modules/services/networking/keepalived/vrrp-options.nix b/nixos/modules/services/networking/keepalived/vrrp-instance-options.nix similarity index 88% rename from nixos/modules/services/networking/keepalived/vrrp-options.nix rename to nixos/modules/services/networking/keepalived/vrrp-instance-options.nix index 79eff3ae5419..85b9bc337726 100644 --- a/nixos/modules/services/networking/keepalived/vrrp-options.nix +++ b/nixos/modules/services/networking/keepalived/vrrp-instance-options.nix @@ -108,6 +108,20 @@ with lib; description = "Declarative vhost config"; }; + trackScripts = mkOption { + type = types.listOf types.str; + default = []; + example = [ "chk_cmd1" "chk_cmd2" ]; + description = "List of script names to invoke for health tracking."; + }; + + trackInterfaces = mkOption { + type = types.listOf types.str; + default = []; + example = [ "eth0" "eth1" ]; + description = "List of network interfaces to monitor for health tracking."; + }; + extraConfig = mkOption { type = types.lines; default = ""; diff --git a/nixos/modules/services/networking/keepalived/vrrp-script-options.nix b/nixos/modules/services/networking/keepalived/vrrp-script-options.nix new file mode 100644 index 000000000000..a3f794c40a89 --- /dev/null +++ b/nixos/modules/services/networking/keepalived/vrrp-script-options.nix @@ -0,0 +1,64 @@ +{ lib } : + +with lib; +with lib.types; +{ + options = { + + script = mkOption { + type = str; + example = "\${pkgs.curl} -f http://localhost:80"; + description = "(Path of) Script command to execute followed by args, i.e. cmd [args]..."; + }; + + interval = mkOption { + type = int; + default = 1; + description = "Seconds between script invocations."; + }; + + timeout = mkOption { + type = int; + default = 5; + description = "Seconds after which script is considered to have failed."; + }; + + weight = mkOption { + type = int; + default = 0; + description = "Following a failure, adjust the priority by this weight."; + }; + + rise = mkOption { + type = int; + default = 5; + description = "Required number of successes for OK transition."; + }; + + fall = mkOption { + type = int; + default = 3; + description = "Required number of failures for KO transition."; + }; + + user = mkOption { + type = str; + default = "keepalived_script"; + description = "Name of user to run the script under."; + }; + + group = mkOption { + type = nullOr str; + default = null; + description = "Name of group to run the script under. Defaults to user group."; + }; + + extraConfig = mkOption { + type = lines; + default = ""; + description = "Extra lines to be added verbatim to the vrrp_script section."; + }; + + }; + +}