Clone of https://github.com/NixOS/nixpkgs.git (to stress-test knotserver)

acme: Add csr option (#376334)

authored by Martin Weinelt and committed by GitHub f462e256 b475c480

Changed files
+93 -13
nixos
doc
manual
release-notes
modules
security
tests
+2
nixos/doc/manual/release-notes/rl-2505.section.md
··· 638 638 They are still expected to be working until future version 5.0.0, but will generate warnings in logs. 639 639 Read the [release notes](https://www.authelia.com/blog/4.39-release-notes/) for human readable summaries of the changes. 640 640 641 + - `security.acme` now supports renewal using CSRs (Certificate Signing Request) through the options `security.acme.*.csr` and `security.acme.*.csrKey`. 642 + 641 643 - `programs.fzf.keybindings` now supports the fish shell. 642 644 643 645 - `gerbera` now has wavpack support.
+47 -13
nixos/modules/security/acme/default.nix
··· 236 236 237 237 # Create hashes for cert data directories based on configuration 238 238 # Flags are separated to avoid collisions 239 - hashData = with builtins; '' 240 - ${lib.concatStringsSep " " data.extraLegoFlags} - 241 - ${lib.concatStringsSep " " data.extraLegoRunFlags} - 242 - ${lib.concatStringsSep " " data.extraLegoRenewFlags} - 243 - ${toString acmeServer} ${toString data.dnsProvider} 244 - ${toString data.ocspMustStaple} ${data.keyType} 245 - ''; 239 + hashData = 240 + with builtins; 241 + '' 242 + ${lib.concatStringsSep " " data.extraLegoFlags} - 243 + ${lib.concatStringsSep " " data.extraLegoRunFlags} - 244 + ${lib.concatStringsSep " " data.extraLegoRenewFlags} - 245 + ${toString acmeServer} ${toString data.dnsProvider} 246 + ${toString data.ocspMustStaple} ${data.keyType} 247 + '' 248 + + (lib.optionalString (data.csr != null) (" - " + data.csr)); 246 249 certDir = mkHash hashData; 247 250 # TODO remove domainHash usage entirely. Waiting on go-acme/lego#1532 248 251 domainHash = mkHash "${lib.concatStringsSep " " extraDomains} ${data.domain}"; ··· 286 289 "--accept-tos" # Checking the option is covered by the assertions 287 290 "--path" 288 291 "." 289 - "-d" 290 - data.domain 291 292 "--email" 292 293 data.email 293 - "--key-type" 294 - data.keyType 295 294 ] 296 295 ++ protocolOpts 297 296 ++ lib.optionals (acmeServer != null) [ 298 297 "--server" 299 298 acmeServer 300 299 ] 300 + ++ lib.optionals (data.csr != null) [ 301 + "--csr" 302 + data.csr 303 + ] 304 + ++ lib.optionals (data.csr == null) [ 305 + "--key-type" 306 + data.keyType 307 + "-d" 308 + data.domain 309 + ] 301 310 ++ lib.concatMap (name: [ 302 311 "-d" 303 312 name ··· 327 336 webroots = lib.remove null ( 328 337 lib.unique (builtins.map (certAttrs: certAttrs.webroot) (lib.attrValues config.security.acme.certs)) 329 338 ); 339 + 340 + certificateKey = if data.csrKey != null then "${data.csrKey}" else "certificates/${keyName}.key"; 330 341 in 331 342 { 332 343 inherit accountHash cert selfsignedDeps; ··· 529 540 # Check if we can renew. 530 541 # We can only renew if the list of domains has not changed. 531 542 # We also need an account key. Avoids #190493 532 - if cmp -s domainhash.txt certificates/domainhash.txt && [ -e 'certificates/${keyName}.key' ] && [ -e 'certificates/${keyName}.crt' ] && [ -n "$(find accounts -name '${data.email}.key')" ]; then 543 + if cmp -s domainhash.txt certificates/domainhash.txt && [ -e '${certificateKey}' ] && [ -e 'certificates/${keyName}.crt' ] && [ -n "$(find accounts -name '${data.email}.key')" ]; then 533 544 534 545 # Even if a cert is not expired, it may be revoked by the CA. 535 546 # Try to renew, and silently fail if the cert is not expired. ··· 564 575 touch out/renewed 565 576 echo Installing new certificate 566 577 cp -vp 'certificates/${keyName}.crt' out/fullchain.pem 567 - cp -vp 'certificates/${keyName}.key' out/key.pem 578 + cp -vp '${certificateKey}' out/key.pem 568 579 cp -vp 'certificates/${keyName}.issuer.crt' out/chain.pem 569 580 ln -sf fullchain.pem out/cert.pem 570 581 cat out/key.pem out/fullchain.pem > out/full.pem ··· 845 856 description = "Domain to fetch certificate for (defaults to the entry name)."; 846 857 }; 847 858 859 + csr = lib.mkOption { 860 + type = lib.types.nullOr lib.types.str; 861 + default = null; 862 + description = "Path to a certificate signing request to apply when fetching the certificate."; 863 + }; 864 + 865 + csrKey = lib.mkOption { 866 + type = lib.types.nullOr lib.types.str; 867 + default = null; 868 + description = "Path to the private key to the matching certificate signing request."; 869 + }; 870 + 848 871 extraDomainNames = lib.mkOption { 849 872 type = lib.types.listOf lib.types.str; 850 873 default = [ ]; ··· 1111 1134 message = '' 1112 1135 Option `security.acme.certs.${cert}.credentialFiles` can only be 1113 1136 used for variables suffixed by "_FILE". 1137 + ''; 1138 + } 1139 + 1140 + { 1141 + assertion = lib.all ( 1142 + certOpts: 1143 + (certOpts.csr == null && certOpts.csrKey == null) 1144 + || (certOpts.csr != null && certOpts.csrKey != null) 1145 + ) certs; 1146 + message = '' 1147 + When passing a certificate signing request both `security.acme.certs.${cert}.csr` and `security.acme.certs.${cert}.csrKey` need to be set. 1114 1148 ''; 1115 1149 } 1116 1150 ]) cfg.certs
+44
nixos/tests/acme/http01-builtin.nix
··· 99 99 "builtin-3.${domain}".listenHTTP = ":80"; 100 100 }; 101 101 }; 102 + 103 + csr.configuration = 104 + let 105 + conf = pkgs.writeText "openssl.csr.conf" '' 106 + [req] 107 + default_bits = 2048 108 + prompt = no 109 + default_md = sha256 110 + req_extensions = req_ext 111 + distinguished_name = dn 112 + 113 + [ dn ] 114 + CN = ${config.networking.fqdn} 115 + 116 + [ req_ext ] 117 + subjectAltName = @alt_names 118 + 119 + [ alt_names ] 120 + DNS.1 = ${config.networking.fqdn} 121 + ''; 122 + csrData = 123 + pkgs.runCommandNoCC "csr-and-key" 124 + { 125 + buildInputs = [ pkgs.openssl ]; 126 + } 127 + '' 128 + mkdir -p $out 129 + openssl req -new -newkey rsa:2048 -nodes \ 130 + -keyout $out/key.pem \ 131 + -out $out/request.csr \ 132 + -config ${conf} 133 + ''; 134 + in 135 + { 136 + security.acme.certs."${config.networking.fqdn}" = { 137 + csr = "${csrData}/request.csr"; 138 + csrKey = "${csrData}/key.pem"; 139 + }; 140 + }; 102 141 }; 103 142 }; 104 143 }; ··· 211 250 212 251 with subtest("Validate permissions (self-signed)"): 213 252 check_permissions(builtin, cert, "acme") 253 + 254 + with subtest("Can renew using a CSR"): 255 + builtin.succeed(f"systemctl clean acme-{cert}.service --what=state") 256 + switch_to(builtin, "csr") 257 + check_issuer(builtin, cert, "pebble") 214 258 ''; 215 259 }