nixos/acme: add compat for IP SANs to minica fallback (#479212)
This commit is contained in:
@@ -20,6 +20,16 @@ let
|
||||
mkAccountHash = acmeServer: data: mkHash "${toString acmeServer} ${data.keyType} ${data.email}";
|
||||
accountDirRoot = "/var/lib/acme/.lego/accounts/";
|
||||
|
||||
isIP =
|
||||
address:
|
||||
let
|
||||
isIPv6 = lib.length (lib.splitString ":" address) > 2;
|
||||
isIPv4 = (lib.match "^([0-9]+\\.){3}[0-9]+$" address) != null;
|
||||
in
|
||||
isIPv6 || isIPv4;
|
||||
|
||||
isDomainName = name: !isIP name;
|
||||
|
||||
# Lockdir is acme-setup.service's RuntimeDirectory.
|
||||
# Since that service is a oneshot with RemainAfterExit,
|
||||
# the folder will exist during all renewal services.
|
||||
@@ -389,10 +399,14 @@ let
|
||||
exit 0
|
||||
fi
|
||||
|
||||
minica \
|
||||
--ca-key ca/key.pem \
|
||||
--ca-cert ca/cert.pem \
|
||||
--domains ${lib.escapeShellArg (builtins.concatStringsSep "," ([ data.domain ] ++ extraDomains))}
|
||||
minica ${
|
||||
lib.cli.toCommandLineShellGNU { } {
|
||||
ca-key = "ca/key.pem";
|
||||
ca-cert = "ca/cert.pem";
|
||||
domains = lib.concatStringsSep "," (lib.filter isDomainName ([ data.domain ] ++ extraDomains));
|
||||
ip-addresses = lib.concatStringsSep "," (lib.filter isIP ([ data.domain ] ++ extraDomains));
|
||||
}
|
||||
}
|
||||
|
||||
# Create files to match directory layout for real certificates
|
||||
(
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
let
|
||||
domain = "example.test";
|
||||
ip = "192.168.1.2";
|
||||
in
|
||||
{
|
||||
name = "http01-builtin";
|
||||
@@ -34,6 +34,7 @@ in
|
||||
environment.systemPackages = [ pkgs.openssl ];
|
||||
|
||||
security.acme.certs."${config.networking.fqdn}" = {
|
||||
extraDomainNames = [ ip ];
|
||||
listenHTTP = ":80";
|
||||
};
|
||||
|
||||
@@ -124,6 +125,7 @@ in
|
||||
|
||||
[ alt_names ]
|
||||
DNS.1 = ${config.networking.fqdn}
|
||||
IP.1 = ${ip}
|
||||
'';
|
||||
csrData =
|
||||
pkgs.runCommand "csr-and-key"
|
||||
@@ -142,6 +144,7 @@ in
|
||||
security.acme.certs."${config.networking.fqdn}" = {
|
||||
csr = "${csrData}/request.csr";
|
||||
csrKey = "${csrData}/key.pem";
|
||||
extraDomainNames = lib.mkForce [ ];
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -158,6 +161,7 @@ in
|
||||
${(import ./utils.nix).pythonUtils}
|
||||
|
||||
domain = "${domain}"
|
||||
ip = "${ip}"
|
||||
cert = "${certName}"
|
||||
cert2 = "builtin-2." + domain
|
||||
cert3 = "builtin-3." + domain
|
||||
@@ -173,6 +177,7 @@ in
|
||||
|
||||
check_issuer(builtin, cert, "pebble")
|
||||
check_domain(builtin, cert, cert)
|
||||
check_ip(builtin, cert, ip)
|
||||
|
||||
with subtest("Validate permissions"):
|
||||
check_permissions(builtin, cert, "acme")
|
||||
@@ -300,6 +305,7 @@ in
|
||||
builtin.wait_for_unit("renew-triggered.target")
|
||||
|
||||
check_issuer(builtin, cert, "pebble")
|
||||
check_ip(builtin, cert, ip)
|
||||
|
||||
with subtest("Generate self-signed certs"):
|
||||
acme.shutdown()
|
||||
|
||||
@@ -84,6 +84,11 @@ def check_domain(node, cert_name, domain, fail=False) -> None:
|
||||
cmd = f"openssl x509 -noout -checkhost '{domain}' -in /var/lib/acme/{cert_name}/cert.pem"
|
||||
run(node, cmd, fail=fail)
|
||||
|
||||
# Ensures the provided ip address matches with the given cert
|
||||
def check_ip(node, cert_name, ip_address, fail=False) -> None:
|
||||
cmd = f"openssl x509 -noout -checkip '{ip_address}' -in /var/lib/acme/{cert_name}/cert.pem"
|
||||
run(node, cmd, fail=fail)
|
||||
|
||||
# Ensures the required values for OCSP stapling are present
|
||||
# Pebble doesn't provide a full OCSP responder, so just checks the URL
|
||||
def check_stapling(node, cert_name, ca_domain, fail=False):
|
||||
|
||||
Reference in New Issue
Block a user