Loading nixos/doc/manual/release-notes/rl-2505.section.md +2 −0 Original line number Diff line number Diff line Loading @@ -638,6 +638,8 @@ They are still expected to be working until future version 5.0.0, but will generate warnings in logs. Read the [release notes](https://www.authelia.com/blog/4.39-release-notes/) for human readable summaries of the changes. - `security.acme` now supports renewal using CSRs (Certificate Signing Request) through the options `security.acme.*.csr` and `security.acme.*.csrKey`. - `programs.fzf.keybindings` now supports the fish shell. - `gerbera` now has wavpack support. Loading nixos/modules/security/acme/default.nix +47 −13 Original line number Diff line number Diff line Loading @@ -236,13 +236,16 @@ let # Create hashes for cert data directories based on configuration # Flags are separated to avoid collisions hashData = with builtins; '' hashData = with builtins; '' ${lib.concatStringsSep " " data.extraLegoFlags} - ${lib.concatStringsSep " " data.extraLegoRunFlags} - ${lib.concatStringsSep " " data.extraLegoRenewFlags} - ${toString acmeServer} ${toString data.dnsProvider} ${toString data.ocspMustStaple} ${data.keyType} ''; '' + (lib.optionalString (data.csr != null) (" - " + data.csr)); certDir = mkHash hashData; # TODO remove domainHash usage entirely. Waiting on go-acme/lego#1532 domainHash = mkHash "${lib.concatStringsSep " " extraDomains} ${data.domain}"; Loading Loading @@ -286,18 +289,24 @@ let "--accept-tos" # Checking the option is covered by the assertions "--path" "." "-d" data.domain "--email" data.email "--key-type" data.keyType ] ++ protocolOpts ++ lib.optionals (acmeServer != null) [ "--server" acmeServer ] ++ lib.optionals (data.csr != null) [ "--csr" data.csr ] ++ lib.optionals (data.csr == null) [ "--key-type" data.keyType "-d" data.domain ] ++ lib.concatMap (name: [ "-d" name Loading Loading @@ -327,6 +336,8 @@ let webroots = lib.remove null ( lib.unique (builtins.map (certAttrs: certAttrs.webroot) (lib.attrValues config.security.acme.certs)) ); certificateKey = if data.csrKey != null then "${data.csrKey}" else "certificates/${keyName}.key"; in { inherit accountHash cert selfsignedDeps; Loading Loading @@ -529,7 +540,7 @@ let # Check if we can renew. # We can only renew if the list of domains has not changed. # We also need an account key. Avoids #190493 if cmp -s domainhash.txt certificates/domainhash.txt && [ -e 'certificates/${keyName}.key' ] && [ -e 'certificates/${keyName}.crt' ] && [ -n "$(find accounts -name '${data.email}.key')" ]; then if cmp -s domainhash.txt certificates/domainhash.txt && [ -e '${certificateKey}' ] && [ -e 'certificates/${keyName}.crt' ] && [ -n "$(find accounts -name '${data.email}.key')" ]; then # Even if a cert is not expired, it may be revoked by the CA. # Try to renew, and silently fail if the cert is not expired. Loading Loading @@ -564,7 +575,7 @@ let touch out/renewed echo Installing new certificate cp -vp 'certificates/${keyName}.crt' out/fullchain.pem cp -vp 'certificates/${keyName}.key' out/key.pem cp -vp '${certificateKey}' out/key.pem cp -vp 'certificates/${keyName}.issuer.crt' out/chain.pem ln -sf fullchain.pem out/cert.pem cat out/key.pem out/fullchain.pem > out/full.pem Loading Loading @@ -845,6 +856,18 @@ let description = "Domain to fetch certificate for (defaults to the entry name)."; }; csr = lib.mkOption { type = lib.types.nullOr lib.types.str; default = null; description = "Path to a certificate signing request to apply when fetching the certificate."; }; csrKey = lib.mkOption { type = lib.types.nullOr lib.types.str; default = null; description = "Path to the private key to the matching certificate signing request."; }; extraDomainNames = lib.mkOption { type = lib.types.listOf lib.types.str; default = [ ]; Loading Loading @@ -1113,6 +1136,17 @@ in used for variables suffixed by "_FILE". ''; } { assertion = lib.all ( certOpts: (certOpts.csr == null && certOpts.csrKey == null) || (certOpts.csr != null && certOpts.csrKey != null) ) certs; message = '' When passing a certificate signing request both `security.acme.certs.${cert}.csr` and `security.acme.certs.${cert}.csrKey` need to be set. ''; } ]) cfg.certs )); Loading nixos/tests/acme/http01-builtin.nix +44 −0 Original line number Diff line number Diff line Loading @@ -99,6 +99,45 @@ in "builtin-3.${domain}".listenHTTP = ":80"; }; }; csr.configuration = let conf = pkgs.writeText "openssl.csr.conf" '' [req] default_bits = 2048 prompt = no default_md = sha256 req_extensions = req_ext distinguished_name = dn [ dn ] CN = ${config.networking.fqdn} [ req_ext ] subjectAltName = @alt_names [ alt_names ] DNS.1 = ${config.networking.fqdn} ''; csrData = pkgs.runCommandNoCC "csr-and-key" { buildInputs = [ pkgs.openssl ]; } '' mkdir -p $out openssl req -new -newkey rsa:2048 -nodes \ -keyout $out/key.pem \ -out $out/request.csr \ -config ${conf} ''; in { security.acme.certs."${config.networking.fqdn}" = { csr = "${csrData}/request.csr"; csrKey = "${csrData}/key.pem"; }; }; }; }; }; Loading Loading @@ -211,5 +250,10 @@ in with subtest("Validate permissions (self-signed)"): check_permissions(builtin, cert, "acme") with subtest("Can renew using a CSR"): builtin.succeed(f"systemctl clean acme-{cert}.service --what=state") switch_to(builtin, "csr") check_issuer(builtin, cert, "pebble") ''; } Loading
nixos/doc/manual/release-notes/rl-2505.section.md +2 −0 Original line number Diff line number Diff line Loading @@ -638,6 +638,8 @@ They are still expected to be working until future version 5.0.0, but will generate warnings in logs. Read the [release notes](https://www.authelia.com/blog/4.39-release-notes/) for human readable summaries of the changes. - `security.acme` now supports renewal using CSRs (Certificate Signing Request) through the options `security.acme.*.csr` and `security.acme.*.csrKey`. - `programs.fzf.keybindings` now supports the fish shell. - `gerbera` now has wavpack support. Loading
nixos/modules/security/acme/default.nix +47 −13 Original line number Diff line number Diff line Loading @@ -236,13 +236,16 @@ let # Create hashes for cert data directories based on configuration # Flags are separated to avoid collisions hashData = with builtins; '' hashData = with builtins; '' ${lib.concatStringsSep " " data.extraLegoFlags} - ${lib.concatStringsSep " " data.extraLegoRunFlags} - ${lib.concatStringsSep " " data.extraLegoRenewFlags} - ${toString acmeServer} ${toString data.dnsProvider} ${toString data.ocspMustStaple} ${data.keyType} ''; '' + (lib.optionalString (data.csr != null) (" - " + data.csr)); certDir = mkHash hashData; # TODO remove domainHash usage entirely. Waiting on go-acme/lego#1532 domainHash = mkHash "${lib.concatStringsSep " " extraDomains} ${data.domain}"; Loading Loading @@ -286,18 +289,24 @@ let "--accept-tos" # Checking the option is covered by the assertions "--path" "." "-d" data.domain "--email" data.email "--key-type" data.keyType ] ++ protocolOpts ++ lib.optionals (acmeServer != null) [ "--server" acmeServer ] ++ lib.optionals (data.csr != null) [ "--csr" data.csr ] ++ lib.optionals (data.csr == null) [ "--key-type" data.keyType "-d" data.domain ] ++ lib.concatMap (name: [ "-d" name Loading Loading @@ -327,6 +336,8 @@ let webroots = lib.remove null ( lib.unique (builtins.map (certAttrs: certAttrs.webroot) (lib.attrValues config.security.acme.certs)) ); certificateKey = if data.csrKey != null then "${data.csrKey}" else "certificates/${keyName}.key"; in { inherit accountHash cert selfsignedDeps; Loading Loading @@ -529,7 +540,7 @@ let # Check if we can renew. # We can only renew if the list of domains has not changed. # We also need an account key. Avoids #190493 if cmp -s domainhash.txt certificates/domainhash.txt && [ -e 'certificates/${keyName}.key' ] && [ -e 'certificates/${keyName}.crt' ] && [ -n "$(find accounts -name '${data.email}.key')" ]; then if cmp -s domainhash.txt certificates/domainhash.txt && [ -e '${certificateKey}' ] && [ -e 'certificates/${keyName}.crt' ] && [ -n "$(find accounts -name '${data.email}.key')" ]; then # Even if a cert is not expired, it may be revoked by the CA. # Try to renew, and silently fail if the cert is not expired. Loading Loading @@ -564,7 +575,7 @@ let touch out/renewed echo Installing new certificate cp -vp 'certificates/${keyName}.crt' out/fullchain.pem cp -vp 'certificates/${keyName}.key' out/key.pem cp -vp '${certificateKey}' out/key.pem cp -vp 'certificates/${keyName}.issuer.crt' out/chain.pem ln -sf fullchain.pem out/cert.pem cat out/key.pem out/fullchain.pem > out/full.pem Loading Loading @@ -845,6 +856,18 @@ let description = "Domain to fetch certificate for (defaults to the entry name)."; }; csr = lib.mkOption { type = lib.types.nullOr lib.types.str; default = null; description = "Path to a certificate signing request to apply when fetching the certificate."; }; csrKey = lib.mkOption { type = lib.types.nullOr lib.types.str; default = null; description = "Path to the private key to the matching certificate signing request."; }; extraDomainNames = lib.mkOption { type = lib.types.listOf lib.types.str; default = [ ]; Loading Loading @@ -1113,6 +1136,17 @@ in used for variables suffixed by "_FILE". ''; } { assertion = lib.all ( certOpts: (certOpts.csr == null && certOpts.csrKey == null) || (certOpts.csr != null && certOpts.csrKey != null) ) certs; message = '' When passing a certificate signing request both `security.acme.certs.${cert}.csr` and `security.acme.certs.${cert}.csrKey` need to be set. ''; } ]) cfg.certs )); Loading
nixos/tests/acme/http01-builtin.nix +44 −0 Original line number Diff line number Diff line Loading @@ -99,6 +99,45 @@ in "builtin-3.${domain}".listenHTTP = ":80"; }; }; csr.configuration = let conf = pkgs.writeText "openssl.csr.conf" '' [req] default_bits = 2048 prompt = no default_md = sha256 req_extensions = req_ext distinguished_name = dn [ dn ] CN = ${config.networking.fqdn} [ req_ext ] subjectAltName = @alt_names [ alt_names ] DNS.1 = ${config.networking.fqdn} ''; csrData = pkgs.runCommandNoCC "csr-and-key" { buildInputs = [ pkgs.openssl ]; } '' mkdir -p $out openssl req -new -newkey rsa:2048 -nodes \ -keyout $out/key.pem \ -out $out/request.csr \ -config ${conf} ''; in { security.acme.certs."${config.networking.fqdn}" = { csr = "${csrData}/request.csr"; csrKey = "${csrData}/key.pem"; }; }; }; }; }; Loading Loading @@ -211,5 +250,10 @@ in with subtest("Validate permissions (self-signed)"): check_permissions(builtin, cert, "acme") with subtest("Can renew using a CSR"): builtin.succeed(f"systemctl clean acme-{cert}.service --what=state") switch_to(builtin, "csr") check_issuer(builtin, cert, "pebble") ''; }