From cfc871cce6aefaa0ff64619780a807cba761c6b2 Mon Sep 17 00:00:00 2001 From: Gregor Kleen Date: Mon, 30 Jan 2023 12:20:23 +0100 Subject: ... --- hosts/surtr/tls/default.nix | 155 ++++++++++++++++++++------------------------ 1 file changed, 69 insertions(+), 86 deletions(-) (limited to 'hosts/surtr/tls/default.nix') diff --git a/hosts/surtr/tls/default.nix b/hosts/surtr/tls/default.nix index f1a515db..b1c05888 100644 --- a/hosts/surtr/tls/default.nix +++ b/hosts/surtr/tls/default.nix @@ -3,111 +3,94 @@ with lib; let - inherit (customUtils) mapFilterAttrs; - 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; - - domainOptions = { - options = { - wildcard = mkOption { - type = types.bool; - default = false; - }; - zone = mkOption { - type = types.nullOr types.str; - default = null; - }; - certCfg = mkOption { - type = types.attrs; - default = {}; - }; - }; - }; in { options = { security.acme = { - domains = mkOption { - type = types.attrsOf (types.submodule domainOptions); + # 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.domains = genAttrs ["dirty-haskell.org" "141.li" "xmpp.li" "synapse.li" "yggdrasil.li" "praseodym.org" "rheperire.org" "kleen.li" "nights.email" "bouncy.email" "kleen.consulting"] (domain: { wildcard = true; }); - - fileSystems."/var/lib/acme" = - { device = "surtr/safe/var-lib-acme"; - fsType = "zfs"; - }; - security.acme = { + # Some default/global ACME settings + acceptTerms = true; - preliminarySelfsigned = true; # DNS challenge is slow + # DNS challenge is slow + preliminarySelfsigned = true; defaults = { email = "phikeebaogobaegh@141.li"; - keyType = "rsa4096"; # we don't like NIST curves - extraLegoRenewFlags = [ - # "--preferred-chain" "ISRG Root X1" - # "--always-deactivate-authorizations" "true" - ]; - extraLegoRunFlags = config.security.acme.defaults.extraLegoRenewFlags; + # We don't like NIST curves and Let's Encrypt doesn't support + # anything better + keyType = "rsa4096"; }; - certs = - let - domainAttrset = domain: let - tsigPath = ./tsig_keys + "/${domain}"; - isTsig = pathExists tsigPath; - shared = { - inherit domain; - extraDomainNames = optional cfg.domains.${domain}.wildcard "*.${domain}"; - dnsResolver = "127.0.0.1:5353"; - }; - mkRFC2136 = shared // rec { - 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/tsig_secret - RFC2136_TTL=0 - RFC2136_PROPAGATION_TIMEOUT=60 - RFC2136_POLLING_INTERVAL=2 - RFC2136_SEQUENCE_INTERVAL=1 - ''; - dnsPropagationCheck = false; - }; - in assert isTsig; mkRFC2136 // cfg.domains.${domain}.certCfg; - in genAttrs (attrNames cfg.domains) domainAttrset; - }; - sops.secrets = let - toTSIGSecret = n: v: - if v == "regular" || v == "symlink" - then nameValuePair (tsigSecretName n) { - format = "binary"; - sopsFile = ./tsig_keys + "/${n}"; - } else null; - in mapFilterAttrs (_: v: v != null) toTSIGSecret (builtins.readDir ./tsig_keys); + # 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; + }; - systemd.services = - let - serviceAttrset = domain: { - after = [ "knot.service" ]; - bindsTo = [ "knot.service" ]; - serviceConfig = { - LoadCredential = ["tsig_secret:${config.sops.secrets.${tsigSecretName domain}.path}"]; - SystemCallFilter = mkForce [ "@system-service" "~@privileged" "@chown" ]; - }; - }; - in mapAttrs' (domain: nameValuePair "acme-${domain}") (genAttrs (attrNames config.security.acme.certs) serviceAttrset); + # 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; - services.certspotter = { - extraOptions = [ "-verbose" "-num_workers" "4" "-batch_size" "2000" ]; - watchList = map (domain: ".${domain}") (attrNames cfg.domains); - logs = "https://www.gstatic.com/ct/log_list/v2/all_logs_list.json"; - }; + # 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; }; } -- cgit v1.2.3