1
0
Fork 0
forked from NixOS/nixpkgs

acme: Add csr option (#376334)

This commit is contained in:
Martin Weinelt 2025-05-17 11:24:08 +02:00 committed by GitHub
commit f462e2564d
Signed by: github
GPG key ID: B5690EEEBB952194
3 changed files with 93 additions and 13 deletions

View file

@ -638,6 +638,8 @@
They are still expected to be working until future version 5.0.0, but will generate warnings in logs. 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. 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. - `programs.fzf.keybindings` now supports the fish shell.
- `gerbera` now has wavpack support. - `gerbera` now has wavpack support.

View file

@ -236,13 +236,16 @@ let
# Create hashes for cert data directories based on configuration # Create hashes for cert data directories based on configuration
# Flags are separated to avoid collisions # Flags are separated to avoid collisions
hashData = with builtins; '' hashData =
with builtins;
''
${lib.concatStringsSep " " data.extraLegoFlags} - ${lib.concatStringsSep " " data.extraLegoFlags} -
${lib.concatStringsSep " " data.extraLegoRunFlags} - ${lib.concatStringsSep " " data.extraLegoRunFlags} -
${lib.concatStringsSep " " data.extraLegoRenewFlags} - ${lib.concatStringsSep " " data.extraLegoRenewFlags} -
${toString acmeServer} ${toString data.dnsProvider} ${toString acmeServer} ${toString data.dnsProvider}
${toString data.ocspMustStaple} ${data.keyType} ${toString data.ocspMustStaple} ${data.keyType}
''; ''
+ (lib.optionalString (data.csr != null) (" - " + data.csr));
certDir = mkHash hashData; certDir = mkHash hashData;
# TODO remove domainHash usage entirely. Waiting on go-acme/lego#1532 # TODO remove domainHash usage entirely. Waiting on go-acme/lego#1532
domainHash = mkHash "${lib.concatStringsSep " " extraDomains} ${data.domain}"; domainHash = mkHash "${lib.concatStringsSep " " extraDomains} ${data.domain}";
@ -286,18 +289,24 @@ let
"--accept-tos" # Checking the option is covered by the assertions "--accept-tos" # Checking the option is covered by the assertions
"--path" "--path"
"." "."
"-d"
data.domain
"--email" "--email"
data.email data.email
"--key-type"
data.keyType
] ]
++ protocolOpts ++ protocolOpts
++ lib.optionals (acmeServer != null) [ ++ lib.optionals (acmeServer != null) [
"--server" "--server"
acmeServer acmeServer
] ]
++ lib.optionals (data.csr != null) [
"--csr"
data.csr
]
++ lib.optionals (data.csr == null) [
"--key-type"
data.keyType
"-d"
data.domain
]
++ lib.concatMap (name: [ ++ lib.concatMap (name: [
"-d" "-d"
name name
@ -327,6 +336,8 @@ let
webroots = lib.remove null ( webroots = lib.remove null (
lib.unique (builtins.map (certAttrs: certAttrs.webroot) (lib.attrValues config.security.acme.certs)) 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 in
{ {
inherit accountHash cert selfsignedDeps; inherit accountHash cert selfsignedDeps;
@ -529,7 +540,7 @@ let
# Check if we can renew. # Check if we can renew.
# We can only renew if the list of domains has not changed. # We can only renew if the list of domains has not changed.
# We also need an account key. Avoids #190493 # 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. # 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. # Try to renew, and silently fail if the cert is not expired.
@ -564,7 +575,7 @@ let
touch out/renewed touch out/renewed
echo Installing new certificate echo Installing new certificate
cp -vp 'certificates/${keyName}.crt' out/fullchain.pem 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 cp -vp 'certificates/${keyName}.issuer.crt' out/chain.pem
ln -sf fullchain.pem out/cert.pem ln -sf fullchain.pem out/cert.pem
cat out/key.pem out/fullchain.pem > out/full.pem cat out/key.pem out/fullchain.pem > out/full.pem
@ -845,6 +856,18 @@ let
description = "Domain to fetch certificate for (defaults to the entry name)."; 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 { extraDomainNames = lib.mkOption {
type = lib.types.listOf lib.types.str; type = lib.types.listOf lib.types.str;
default = [ ]; default = [ ];
@ -1113,6 +1136,17 @@ in
used for variables suffixed by "_FILE". 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 ]) cfg.certs
)); ));

View file

@ -99,6 +99,45 @@ in
"builtin-3.${domain}".listenHTTP = ":80"; "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";
};
};
}; };
}; };
}; };
@ -211,5 +250,10 @@ in
with subtest("Validate permissions (self-signed)"): with subtest("Validate permissions (self-signed)"):
check_permissions(builtin, cert, "acme") 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")
''; '';
} }