forked from NixOS/nixpkgs
acme: Add csr option (#376334)
This commit is contained in:
commit
f462e2564d
3 changed files with 93 additions and 13 deletions
|
@ -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.
|
||||||
|
|
|
@ -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
|
||||||
));
|
));
|
||||||
|
|
||||||
|
|
|
@ -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")
|
||||||
'';
|
'';
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue