nixos/acme: Fix ocspMustStaple option and add test

Some of the testing setup for OCSP checking was wrong and
has been fixed too.
This commit is contained in:
Lucas Savva 2020-10-06 21:52:49 +01:00
parent 2927f3fb45
commit 1edd91ca09
No known key found for this signature in database
GPG Key ID: F9CE6D3DCDC78F2D
3 changed files with 43 additions and 5 deletions

View File

@ -121,19 +121,22 @@ let
"--email" data.email "--email" data.email
"--key-type" data.keyType "--key-type" data.keyType
] ++ protocolOpts ] ++ protocolOpts
++ optionals data.ocspMustStaple [ "--must-staple" ]
++ optionals (acmeServer != null) [ "--server" acmeServer ] ++ optionals (acmeServer != null) [ "--server" acmeServer ]
++ concatMap (name: [ "-d" name ]) extraDomains ++ concatMap (name: [ "-d" name ]) extraDomains
++ data.extraLegoFlags; ++ data.extraLegoFlags;
# Although --must-staple is common to both modes, it is not declared as a
# mode-agnostic argument in lego and thus must come after the mode.
runOpts = escapeShellArgs ( runOpts = escapeShellArgs (
commonOpts commonOpts
++ [ "run" ] ++ [ "run" ]
++ optionals data.ocspMustStaple [ "--must-staple" ]
++ data.extraLegoRunFlags ++ data.extraLegoRunFlags
); );
renewOpts = escapeShellArgs ( renewOpts = escapeShellArgs (
commonOpts commonOpts
++ [ "renew" "--reuse-key" ] ++ [ "renew" "--reuse-key" ]
++ optionals data.ocspMustStaple [ "--must-staple" ]
++ data.extraLegoRenewFlags ++ data.extraLegoRenewFlags
); );

View File

@ -97,6 +97,19 @@ in import ./make-test-python.nix ({ lib, ... }: {
}; };
}; };
# Test OCSP Stapling
specialisation.ocsp-stapling.configuration = { pkgs, ... }: {
security.acme.certs."a.example.test" = {
ocspMustStaple = true;
};
services.nginx.virtualHosts."a.example.com" = {
extraConfig = ''
ssl_stapling on;
ssl_stapling_verify on;
'';
};
};
# Test using Apache HTTPD # Test using Apache HTTPD
specialisation.httpd-aliases.configuration = { pkgs, config, lib, ... }: { specialisation.httpd-aliases.configuration = { pkgs, config, lib, ... }: {
services.nginx.enable = lib.mkForce false; services.nginx.enable = lib.mkForce false;
@ -163,6 +176,7 @@ in import ./make-test-python.nix ({ lib, ... }: {
testScript = {nodes, ...}: testScript = {nodes, ...}:
let let
caDomain = nodes.acme.config.test-support.acme.caDomain;
newServerSystem = nodes.webserver.config.system.build.toplevel; newServerSystem = nodes.webserver.config.system.build.toplevel;
switchToNewServer = "${newServerSystem}/bin/switch-to-configuration test"; switchToNewServer = "${newServerSystem}/bin/switch-to-configuration test";
in in
@ -246,6 +260,22 @@ in import ./make-test-python.nix ({ lib, ... }: {
return check_connection_key_bits(node, domain, bits, retries - 1) return check_connection_key_bits(node, domain, bits, retries - 1)
def check_stapling(node, domain, retries=3):
assert retries >= 0
# Pebble doesn't provide a full OCSP responder, so just check the URL
result = node.succeed(
"openssl s_client -CAfile /tmp/ca.crt"
f" -servername {domain} -connect {domain}:443 < /dev/null"
" | openssl x509 -noout -ocsp_uri"
)
print("OCSP Responder URL:", result)
if "${caDomain}:4002" not in result.lower():
time.sleep(1)
return check_stapling(node, domain, retries - 1)
client.start() client.start()
dnsserver.start() dnsserver.start()
@ -253,7 +283,7 @@ in import ./make-test-python.nix ({ lib, ... }: {
client.wait_for_unit("default.target") client.wait_for_unit("default.target")
client.succeed( client.succeed(
'curl --data \'{"host": "acme.test", "addresses": ["${nodes.acme.config.networking.primaryIPAddress}"]}\' http://${dnsServerIP nodes}:8055/add-a' 'curl --data \'{"host": "${caDomain}", "addresses": ["${nodes.acme.config.networking.primaryIPAddress}"]}\' http://${dnsServerIP nodes}:8055/add-a'
) )
acme.start() acme.start()
@ -262,8 +292,8 @@ in import ./make-test-python.nix ({ lib, ... }: {
acme.wait_for_unit("default.target") acme.wait_for_unit("default.target")
acme.wait_for_unit("pebble.service") acme.wait_for_unit("pebble.service")
client.succeed("curl https://acme.test:15000/roots/0 > /tmp/ca.crt") client.succeed("curl https://${caDomain}:15000/roots/0 > /tmp/ca.crt")
client.succeed("curl https://acme.test:15000/intermediate-keys/0 >> /tmp/ca.crt") client.succeed("curl https://${caDomain}:15000/intermediate-keys/0 >> /tmp/ca.crt")
with subtest("Can request certificate with HTTPS-01 challenge"): with subtest("Can request certificate with HTTPS-01 challenge"):
webserver.wait_for_unit("acme-finished-a.example.test.target") webserver.wait_for_unit("acme-finished-a.example.test.target")
@ -290,6 +320,11 @@ in import ./make-test-python.nix ({ lib, ... }: {
check_connection_key_bits(client, "a.example.test", "384") check_connection_key_bits(client, "a.example.test", "384")
webserver.succeed("grep testing /var/lib/acme/a.example.test/test") webserver.succeed("grep testing /var/lib/acme/a.example.test/test")
with subtest("Correctly implements OCSP stapling"):
switch_to(webserver, "ocsp-stapling")
webserver.wait_for_unit("acme-finished-a.example.test.target")
check_stapling(client, "a.example.test")
with subtest("Can request certificate with HTTPS-01 when nginx startup is delayed"): with subtest("Can request certificate with HTTPS-01 when nginx startup is delayed"):
switch_to(webserver, "slow-startup") switch_to(webserver, "slow-startup")
webserver.wait_for_unit("acme-finished-slow.example.com.target") webserver.wait_for_unit("acme-finished-slow.example.com.target")

View File

@ -70,7 +70,7 @@ let
privateKey = testCerts.${domain}.key; privateKey = testCerts.${domain}.key;
httpPort = 80; httpPort = 80;
tlsPort = 443; tlsPort = 443;
ocspResponderURL = "http://0.0.0.0:4002"; ocspResponderURL = "http://${domain}:4002";
strict = true; strict = true;
}; };