{ config, lib, customUtils, pkgs, ... }: with lib; let tsigSecretName = domain: "${domain}_tsig-secret"; tsigKey = domain: let tsigKeyPath = ./tsig_keys + "/${domain}"; in assert assertMsg (pathExists tsigKeyPath) "‘${domain}’ does not exist in `tls/tsig_keys` -- is this a new ACME domain and you forgot to generate the TSIG key? If so, run `gup tls/tsig_keys/${domain}`"; tsigKeyPath; cfg = config.security.acme; in { options = { security.acme = { # This file introduces an additional nixos module option # `security.acme.rfc2136Domains`. # The new option is an attrset of domain names mapping to # additional settings. rfc2136Domains = mkOption { type = types.attrsOf (types.submodule { options = { wildcard = mkOption { type = types.bool; default = false; }; restartUnits = mkOption { type = types.listOf types.str; default = []; }; }; }); default = {}; }; }; }; config = { security.acme = { # Some default/global ACME settings acceptTerms = true; # DNS challenge is slow preliminarySelfsigned = true; defaults = { email = "phikeebaogobaegh@141.li"; # We don't like NIST curves and Let's Encrypt doesn't support # anything better keyType = "rsa4096"; }; # For each domain specified in # `config.security.acme.rfc2136Domains`, configure an additional # entry in `config.security.acme.certs` containing appropriate # settings to provision the certificate via DNS-01 certs = mapAttrs (domain: domainCfg: { inherit domain; extraDomainNames = optional domainCfg.wildcard "*.${domain}"; dnsResolver = "127.0.0.1:53"; dnsProvider = "rfc2136"; credentialsFile = pkgs.writeText "${domain}_credentials.env" '' RFC2136_NAMESERVER=127.0.0.1:53 RFC2136_TSIG_ALGORITHM=hmac-sha256. RFC2136_TSIG_KEY=${domain}_acme_key RFC2136_TSIG_SECRET_FILE=/run/credentials/acme-${domain}.service/${tsigSecretName domain} RFC2136_TTL=0 RFC2136_PROPAGATION_TIMEOUT=60 RFC2136_POLLING_INTERVAL=2 RFC2136_SEQUENCE_INTERVAL=1 ''; dnsPropagationCheck = false; postRun = mkIf (domainCfg.restartUnits != []) '' systemctl --no-block try-restart ${escapeShellArgs domainCfg.restartUnits} ''; }) cfg.rfc2136Domains; }; # Decrypt all `tsig_keys/*` at runtime sops.secrets = mapAttrs' (domain: domainCfg: nameValuePair (tsigSecretName domain) { format = "binary"; sopsFile = tsigKey domain; restartUnits = [ "acme-${domain}.service" ]; }) cfg.rfc2136Domains; # Provide appropriate `tsig_key/*` to systemd service performing # certificate provisioning systemd.services = mapAttrs' (domain: domainCfg: nameValuePair "acme-${domain}" { after = [ "knot.service" ]; bindsTo = [ "knot.service" ]; serviceConfig = { LoadCredential = [ "${tsigSecretName domain}:${config.sops.secrets.${tsigSecretName domain}.path}" ]; SystemCallFilter = mkForce [ "@system-service" "~@privileged" "@chown" ]; }; }) cfg.rfc2136Domains; }; }