diff --git a/modules/services/step-ca/mtls.nix b/modules/services/step-ca/mtls.nix index 7eda11f..5ea0302 100644 --- a/modules/services/step-ca/mtls.nix +++ b/modules/services/step-ca/mtls.nix @@ -50,6 +50,16 @@ let type = lib.types.str; default = "5m"; }; + user = lib.mkOption { + description = "User account to run the mTLS renewal service as."; + type = lib.types.str; + default = "root"; + }; + group = lib.mkOption { + description = "Group to run the mTLS renewal service as. Defaults to the configured renewal user when null."; + type = lib.types.nullOr lib.types.str; + default = null; + }; reloadUnits = lib.mkOption { description = "systemd units to try-reload-or-restart after a successful certificate renewal."; type = lib.types.listOf lib.types.str; @@ -62,6 +72,85 @@ let }; }; }; + + mkMtlsRenewService = { + pkgs, + tlsCert, + tlsKey, + mtlsBundle, + reloadUnits ? [ ], + postCommands ? [ ], + user ? "root", + group ? null, + }: + let + serviceGroup = if group == null then user else group; + renewReloadScript = lib.concatMapStringsSep "\n" (unit: '' + if ${lib.getExe' pkgs.systemd "systemctl"} --quiet is-active "${unit}"; then + ${lib.getExe' pkgs.systemd "systemctl"} try-reload-or-restart "${unit}" + fi + '') reloadUnits; + renewPostCommands = lib.concatStringsSep "\n" postCommands; + in + { + description = "Renew the mTLS certificate when Smallstep marks it ready"; + wantedBy = [ ]; + after = [ "network-online.target" ]; + wants = [ "network-online.target" ]; + path = [ pkgs.coreutils pkgs.step-cli pkgs.systemd ]; + serviceConfig = { + Type = "oneshot"; + User = user; + Group = serviceGroup; + }; + script = '' + set -euo pipefail + + if ${lib.getExe pkgs.step-cli} certificate needs-renewal "${tlsCert}"; then + echo "Renewing mTLS certificate" + else + rc=$? + if [ "$rc" -eq 1 ]; then + echo "mTLS certificate does not need renewal" + exit 0 + fi + + if [ "$rc" -eq 2 ]; then + echo "mTLS certificate missing: ${tlsCert}" >&2 + exit 1 + fi + + echo "step certificate needs-renewal failed with rc=$rc" >&2 + exit "$rc" + fi + + ${lib.getExe pkgs.step-cli} ca renew --force "${tlsCert}" "${tlsKey}" + + umask 077 + cat "${tlsCert}" "${tlsKey}" > "${mtlsBundle}" + + ${renewReloadScript} + ${renewPostCommands} + ''; + }; + + mkMtlsRenewTimer = { + onCalendar, + randomizedDelaySec, + unit ? "mtls-renew.service", + }: { + description = "Periodic Smallstep renewal for the mTLS certificate"; + wantedBy = [ "timers.target" ]; + timerConfig = { + Persistent = true; + OnCalendar = onCalendar; + AccuracySec = "1us"; + RandomizedDelaySec = randomizedDelaySec; + Unit = unit; + }; + }; + + in { flake.modules.nixos.mtls = { config, lib, pkgs, ... }: @@ -73,12 +162,6 @@ in mtlsBundle = "${certDir}/${cfg.bundleFilename}"; rootCA = "${certDir}/root_ca.crt"; sanArgs = lib.concatMapStringsSep " " (san: "--san \"${san}\"") cfg.san; - renewReloadScript = lib.concatMapStringsSep "\n" (unit: '' - if ${lib.getExe' pkgs.systemd "systemctl"} --quiet is-active "${unit}"; then - ${lib.getExe' pkgs.systemd "systemctl"} try-reload-or-restart "${unit}" - fi - '') cfg.renew.reloadUnits; - renewPostCommands = lib.concatStringsSep "\n" cfg.renew.postCommands; in { options.mtls = opts; @@ -102,59 +185,14 @@ in '') ]; - systemd.services.mtls-renew = lib.mkIf cfg.renew.enable { - description = "Renew the mTLS certificate when Smallstep marks it ready"; - wantedBy = [ ]; - after = [ "network-online.target" ]; - wants = [ "network-online.target" ]; - path = [ pkgs.coreutils pkgs.step-cli pkgs.systemd ]; - serviceConfig = { - Type = "oneshot"; - User = "root"; - Group = "root"; - }; - script = '' - set -euo pipefail + systemd.services.mtls-renew = lib.mkIf cfg.renew.enable (mkMtlsRenewService { + inherit pkgs tlsCert tlsKey mtlsBundle; + inherit (cfg.renew) reloadUnits postCommands user group; + }); - if ${lib.getExe pkgs.step-cli} certificate needs-renewal "${tlsCert}"; then - echo "Renewing mTLS certificate" - else - rc=$? - if [ "$rc" -eq 1 ]; then - echo "mTLS certificate does not need renewal" - exit 0 - fi - - if [ "$rc" -eq 2 ]; then - echo "mTLS certificate missing: ${tlsCert}" >&2 - exit 1 - fi - - echo "step certificate needs-renewal failed with rc=$rc" >&2 - exit "$rc" - fi - - ${lib.getExe pkgs.step-cli} ca renew --force "${tlsCert}" "${tlsKey}" - - umask 077 - cat "${tlsCert}" "${tlsKey}" > "${mtlsBundle}" - - ${renewReloadScript} - ${renewPostCommands} - ''; - }; - - systemd.timers.mtls-renew = lib.mkIf cfg.renew.enable { - description = "Periodic Smallstep renewal for the mTLS certificate"; - wantedBy = [ "timers.target" ]; - timerConfig = { - Persistent = true; - OnCalendar = cfg.renew.onCalendar; - AccuracySec = "1us"; - RandomizedDelaySec = cfg.renew.randomizedDelaySec; - Unit = "mtls-renew.service"; - }; - }; + systemd.timers.mtls-renew = lib.mkIf cfg.renew.enable (mkMtlsRenewTimer { + inherit (cfg.renew) onCalendar randomizedDelaySec; + }); }; }; @@ -178,7 +216,7 @@ in }; config = { - home.packages = with pkgs; [ + home.packages = with pkgs; lib.optionals cfg.enable [ step-cli (writeShellScriptBin "mtls-generate" '' set -euo pipefail @@ -195,6 +233,15 @@ in -enddate -in ${mtlsBundle} '') ]; + + # systemd.services.mtls-renew = lib.mkIf cfg.renew.enable (mkMtlsRenewService { + # inherit pkgs tlsCert tlsKey mtlsBundle; + # inherit (cfg.renew) reloadUnits postCommands group; + # }); + + # systemd.timers.mtls-renew = lib.mkIf cfg.renew.enable (mkMtlsRenewTimer { + # inherit (cfg.renew) onCalendar randomizedDelaySec; + # }); }; }; } \ No newline at end of file