Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

nixos/acme: Various fixes #191861

Merged
merged 3 commits into from
Oct 6, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions nixos/modules/security/acme/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ let
);
renewOpts = escapeShellArgs (
commonOpts
++ [ "renew" ]
++ [ "renew" "--no-random-sleep" ]
++ optionals data.ocspMustStaple [ "--must-staple" ]
++ data.extraLegoRenewFlags
);
Expand Down Expand Up @@ -223,9 +223,9 @@ let
# have many certificates, the renewals are distributed over
# the course of the day to avoid rate limits.
AccuracySec = "${toString (_24hSecs / numCerts)}s";

# Skew randomly within the day, per https://letsencrypt.org/docs/integration-guide/.
RandomizedDelaySec = "24h";
FixedRandomDelay = true;
};
};

Expand Down Expand Up @@ -325,6 +325,7 @@ let
'');
} // optionalAttrs (data.listenHTTP != null && toInt (elemAt (splitString ":" data.listenHTTP) 1) < 1024) {
CapabilityBoundingSet = [ "CAP_NET_BIND_SERVICE" ];
AmbientCapabilities = [ "CAP_NET_BIND_SERVICE" ];
winterqt marked this conversation as resolved.
Show resolved Hide resolved
};

# Working directory will be /tmp
Expand Down Expand Up @@ -376,7 +377,8 @@ let

# Check if we can renew.
# We can only renew if the list of domains has not changed.
if cmp -s domainhash.txt certificates/domainhash.txt && [ -e 'certificates/${keyName}.key' -a -e 'certificates/${keyName}.crt' -a -n "$(ls -1 accounts)" ]; then
# We also need an account key. Avoids #190493
if cmp -s domainhash.txt certificates/domainhash.txt && [ -e 'certificates/${keyName}.key' -a -e 'certificates/${keyName}.crt' -a -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.
Expand Down
60 changes: 59 additions & 1 deletion nixos/tests/acme.nix
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,16 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: let
inherit documentRoot;
};

simpleConfig = {
security.acme = {
certs."http.example.test" = {
listenHTTP = ":80";
};
};

networking.firewall.allowedTCPPorts = [ 80 ];
};

# Base specialisation config for testing general ACME features
webserverBasicConfig = {
services.nginx.enable = true;
Expand Down Expand Up @@ -173,6 +183,26 @@ in {
services.nginx.logError = "stderr info";

specialisation = {
# Tests HTTP-01 verification using Lego's built-in web server
http01lego.configuration = simpleConfig;

renew.configuration = lib.mkMerge [
simpleConfig
{
# Pebble provides 5 year long certs,
# needs to be higher than that to test renewal
security.acme.certs."http.example.test".validMinDays = 9999;
}
];

# Tests that account creds can be safely changed.
accountchange.configuration = lib.mkMerge [
simpleConfig
{
security.acme.certs."http.example.test".email = "[email protected]";
}
];

# First derivation used to test general ACME features
general.configuration = { ... }: let
caDomain = nodes.acme.config.test-support.acme.caDomain;
Expand Down Expand Up @@ -446,7 +476,35 @@ in {

download_ca_certs(client)

# Perform general tests first
# Perform http-01 w/ lego test first
with subtest("Can request certificate with Lego's built in web server"):
switch_to(webserver, "http01lego")
webserver.wait_for_unit("acme-finished-http.example.test.target")
check_fullchain(webserver, "http.example.test")
check_issuer(webserver, "http.example.test", "pebble")

# Perform renewal test
with subtest("Can renew certificates when they expire"):
hash = webserver.succeed("sha256sum /var/lib/acme/http.example.test/cert.pem")
switch_to(webserver, "renew")
webserver.wait_for_unit("acme-finished-http.example.test.target")
check_fullchain(webserver, "http.example.test")
check_issuer(webserver, "http.example.test", "pebble")
hash_after = webserver.succeed("sha256sum /var/lib/acme/http.example.test/cert.pem")
assert hash != hash_after

# Perform account change test
with subtest("Handles email change correctly"):
hash = webserver.succeed("sha256sum /var/lib/acme/http.example.test/cert.pem")
switch_to(webserver, "accountchange")
webserver.wait_for_unit("acme-finished-http.example.test.target")
check_fullchain(webserver, "http.example.test")
check_issuer(webserver, "http.example.test", "pebble")
hash_after = webserver.succeed("sha256sum /var/lib/acme/http.example.test/cert.pem")
# Has to do a full run to register account, which creates new certs.
assert hash != hash_after

# Perform general tests
switch_to(webserver, "general")

with subtest("Can request certificate with HTTP-01 challenge"):
Expand Down