{ self, inputs, lib, ... }: let # Options that will be in common between the nixos module and the home-manager module. mkOpts = config: let cfg = config.mtls; in { enable = lib.mkEnableOption "Enable mTLS"; subject = lib.mkOption { description = "The Common Name, DNS Name, or IP address that will be set as the Subject Common Name for the certificate. If no Subject Alternative Names (SANs) are configured (via the --san flag) then the subject will be set as the only SAN."; type = lib.types.str; }; certDir = lib.mkOption { description = "String path to the directory where the certs will be stored"; type = lib.types.str; }; caFile = lib.mkOption { description = "String path for the root CA file"; type = lib.types.str; default = "${cfg.certDir}/root_ca.crt"; }; keyFile = lib.mkOption { description = "String path for the private key"; type = lib.types.str; default = "${cfg.certDir}/key.pem"; }; certFile = lib.mkOption { description = "String path for the public cert"; type = lib.types.str; default = "${cfg.certDir}/cert.pem"; }; bundleFile = lib.mkOption { description = "String path for the mTLS key bundle"; type = lib.types.str; default = "${cfg.certDir}/mtls.pem"; }; san = lib.mkOption { description = "List of SAN to give the mTLS cert"; type = lib.types.listOf lib.types.str; default = [ ]; }; provisioner = lib.mkOption { type = lib.types.str; default = "admin"; }; lifetime = lib.mkOption { type = lib.types.str; default = "24h"; }; renew = { enable = lib.mkOption { description = "Enable automatic mTLS certificate renewal using a systemd timer."; type = lib.types.bool; default = cfg.enable; }; onCalendar = lib.mkOption { description = "systemd OnCalendar schedule for mTLS certificate renewal checks."; type = lib.types.str; default = "*:1/15"; }; randomizedDelaySec = lib.mkOption { description = "Randomized delay added to renewal timer runs to avoid synchronized renewals."; 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."; type = lib.types.nullOr lib.types.str; default = "mtls"; }; reloadUnits = lib.mkOption { description = "systemd units to try-reload-or-restart after a successful certificate renewal."; type = lib.types.listOf lib.types.str; default = [ ]; }; postCommands = lib.mkOption { description = "Shell commands to run after a successful certificate renewal."; type = lib.types.listOf lib.types.lines; default = [ ]; }; }; }; in { flake.modules.nixos.mtls = { config, lib, pkgs, ... }: let cfg = config.mtls; mtlsWrappers = inputs.self.wrappers.mtls; mtlsGenerate = mtlsWrappers.generate.apply { inherit pkgs; inherit (cfg) subject; SANs = cfg.san; provisioner = "admin"; provisionerPasswordFile = config.sops.secrets."janus/admin_jwk".path; }; mtlsRenew = mtlsWrappers.renew.apply { inherit pkgs; }; mtlsCheck = mtlsWrappers.check.apply { inherit pkgs; }; in { options.mtls = (mkOpts config) // { certDir = lib.mkOption { description = "String path to where the mtls certs will be stored."; type = lib.types.str; default = "/etc/step-ca/certs"; }; bootstrap = { enable = lib.mkOption { description = "Enable initial mTLS issuance when cert material is missing or invalid."; type = lib.types.bool; default = false; }; wantedBy = lib.mkOption { description = "systemd targets that should pull in mtls-bootstrap.service."; type = with lib.types; listOf str; default = [ "multi-user.target" ]; }; after = lib.mkOption { description = "systemd units/targets that mtls-bootstrap.service should run after."; type = with lib.types; listOf str; default = [ "network-online.target" ]; }; wants = lib.mkOption { description = "systemd units/targets that mtls-bootstrap.service should pull in."; type = with lib.types; listOf str; default = [ "network-online.target" ]; }; provisionerPasswordFile = lib.mkOption { description = "Optional path passed to mtls-generate as --provisioner-password-file for noninteractive issuance."; type = lib.types.nullOr lib.types.str; default = null; }; }; certReaders = lib.mkOption { description = ""; type = lib.types.listOf lib.types.str; default = [ ]; }; }; config = lib.mkIf cfg.enable { users.groups.certReaders = { name = cfg.renew.group; members = cfg.certReaders; }; environment.systemPackages = [ mtlsGenerate.wrapper mtlsCheck.wrapper mtlsRenew.wrapper ]; systemd = { packages = [ mtlsRenew.outputs.systemd-system ]; # Timer-driven oneshot: only the timer is enabled. NixOS does not # honor the unit's [Install] section for systemd.packages, so the # wantedBy must be set explicitly here. timers.mtls-renew.wantedBy = [ "timers.target" ]; }; }; }; }