{ self, inputs, lib, ... }: let mkSANArgs = sans: builtins.concatLists (map (name: [ "--san" name ]) sans); mkOpts = config: let cfg = config.mtls; in { certDir = lib.mkOption { description = "String path to the directory where the certs will be stored"; type = lib.types.str; default = "/etc/mtls"; }; keyFile = lib.mkOption { description = "String path for the private key"; type = lib.types.str; default = "${config.certDir}/key.pem"; }; certFile = lib.mkOption { description = "String path for the public cert"; type = lib.types.str; default = "${config.certDir}/cert.pem"; }; bundleFile = lib.mkOption { description = "String path for the mTLS key bundle"; type = lib.types.str; default = "${config.certDir}/mtls.pem"; }; subject = lib.mkOption { description = "Subject for the cert"; type = lib.types.str; }; provisioner = lib.mkOption { type = lib.types.nullOr lib.types.str; }; provisionerPasswordFile = lib.mkOption { type = lib.types.nullOr lib.types.str; }; overwrite = lib.mkEnableOption "Overwrite existing cert file?"; SANs = lib.mkOption { description = "A list of Subject Alternative Names"; type = lib.types.listOf lib.types.str; default = [ ]; }; }; in { flake.wrappers.mtls = { generate = inputs.wrappers.lib.wrapModule ({ config, lib, wlib, ... }: { options = (mkOpts config); config = { binName = "mtls-generate"; package = config.pkgs.step-cli; extraPackages = with config.pkgs; [ coreutils step-cli systemd ]; preHook = "mkdir -p ${config.certDir}"; args = [ "ca" "certificate" "${config.subject}" "${config.certFile}" "${config.keyFile}" "--not-before" "-5m" "--not-after" "24h" ] ++ lib.optionals (config.provisioner != null) [ "--provisioner" "${config.provisioner}" ] ++ lib.optionals (config.provisionerPasswordFile != null) [ "--provisioner-password-file" "${config.provisionerPasswordFile}" ] ++ lib.optionals config.overwrite [ "-f" ] ++ mkSANArgs config.SANs; postHook = '' (umask 077; cat "${config.certFile}" "${config.keyFile}" > "${config.bundleFile}") ''; }; }); renew = inputs.wrappers.lib.wrapModule ({ config, lib, wlib, ... }: { # https://github.com/Lassulus/wrappers#generating-systemd-services imports = [ wlib.modules.systemd ]; options = (mkOpts config); config = { binName = "mtls-renew"; package = config.pkgs.step-cli; extraPackages = with config.pkgs; [ coreutils step-cli systemd ]; args = [ "ca" "renew" "${config.certFile}" "${config.keyFile}" ]; postHook = '' (umask 077; cat "${config.certFile}" "${config.keyFile}" > "${config.bundleFile}") ''; systemd = { description = "Renew the mTLS certificate when Smallstep marks it ready"; documentation = [ "https://smallstep.com/docs/step-ca/certificate-authority-server-production" ]; startLimitIntervalSec = 0; after = [ "network-online.target" ]; wants = [ "network-online.target" ]; serviceConfig = { Type = lib.mkDefault "oneshot"; ExecCondition = "${lib.getExe config.pkgs.step-cli} certificate needs-renewal ${config.certFile}"; }; startAt = "hourly"; }; }; }); check = inputs.wrappers.lib.wrapModule ({ config, lib, wlib, ... }: { options = (mkOpts config); config = { binName = "mtls-check"; # This pattern is necessary to wrap packages like openssl that provide more than one binary package = config.pkgs.symlinkJoin { name = "openssl"; paths = [ config.pkgs.openssl.bin config.pkgs.openssl.man ]; meta.mainProgram = "openssl"; }; args = [ "x509" "-noout" "-in" config.bundleFile "-subject" "-issuer" "-ext" "subjectAltName,extendedKeyUsage" "-enddate" ]; }; }); }; }