1
0
Fork 1
mirror of https://github.com/NixOS/nixpkgs.git synced 2025-06-09 17:46:29 +09:00
nixpkgs/nixos/tests/acme/webserver.nix
Lucas Savva 229640ed3a nixos/tests/acme: Refactor test suite
Split tests up based on certain use cases:

- http01-builtin: Tests most functionality of the core module, such
 as the systemd and hashing components, whilst utilising lego's built
 in http01 resolution mechanis.
- dns01: Tests only that DNS01 renewal works as expected.
- nginx: Tests nginx compatability
- httpd: Tests httpd compatability
- caddy: Tests caddy compatability
2025-02-22 23:58:29 +00:00

185 lines
6.4 KiB
Nix

{
serverName,
group,
baseModule,
domain ? "example.test",
}:
{
config,
lib,
pkgs,
...
}:
{
name = serverName;
meta = {
maintainers = lib.teams.acme.members;
# Hard timeout in seconds. Average run time is about 100 seconds.
timeout = 300;
};
nodes = {
# The fake ACME server which will respond to client requests
acme =
{ nodes, ... }:
{
imports = [ ../common/acme/server ];
};
webserver =
{ nodes, ... }:
{
imports = [
../common/acme/client
baseModule
];
networking.domain = domain;
networking.firewall.allowedTCPPorts = [
80
443
];
# Resolve the vhosts the easy way
networking.hosts."127.0.0.1" = [
"proxied.${domain}"
"certchange.${domain}"
"zeroconf.${domain}"
"zeroconf2.${domain}"
"nullroot.${domain}"
];
# OpenSSL will be used for more thorough certificate validation
environment.systemPackages = [ pkgs.openssl ];
# Used to determine if service reload was triggered.
# This does not provide a guarantee that the webserver is finished reloading,
# to handle that there is retry logic wrapping any connectivity checks.
systemd.targets."renew-triggered" = {
wantedBy = [ "${serverName}-config-reload.service" ];
after = [ "${serverName}-config-reload.service" ];
};
security.acme.certs."proxied.${domain}" = {
listenHTTP = ":8080";
group = group;
};
specialisation = {
# Test that the web server is correctly reloaded when the cert changes
certchange.configuration = {
security.acme.certs."proxied.${domain}".extraDomainNames = [
"certchange.${domain}"
];
};
# A useful transitional step before other tests, and tests behaviour
# of removing an extra domain from a cert.
certundo.configuration = { };
# Tests these features:
# - enableACME behaves as expected
# - serverAliases are appended to extraDomainNames
# - Correct routing to the specific virtualHost for a cert
# Inherits previous test config
zeroconf.configuration = {
services.${serverName}.virtualHosts."zeroconf.${domain}" = {
addSSL = true;
enableACME = true;
serverAliases = [ "zeroconf2.${domain}" ];
};
};
# Test that serverAliases are correctly removed which triggers
# cert regeneration and service reload.
rmalias.configuration = {
services.${serverName}.virtualHosts."zeroconf.${domain}" = {
addSSL = true;
enableACME = true;
};
};
# Test that "acmeRoot = null" still results in
# valid cert generation by inheriting defaults.
nullroot.configuration = {
security.acme.defaults.listenHTTP = ":8080";
services.${serverName}.virtualHosts."nullroot.${domain}" = {
onlySSL = true;
enableACME = true;
acmeRoot = null;
};
};
};
};
};
testScript =
{ nodes, ... }:
''
${(import ./utils.nix).pythonUtils}
domain = "${domain}"
ca_domain = "${nodes.acme.test-support.acme.caDomain}"
fqdn = f"proxied.{domain}"
acme.start()
wait_for_running(acme)
acme.wait_for_open_port(443)
with subtest("Acquire a cert through a proxied lego"):
webserver.start()
webserver.succeed("systemctl is-system-running --wait")
wait_for_running(webserver)
download_ca_certs(webserver, ca_domain)
check_connection(webserver, fqdn)
with subtest("Can run on selfsigned certificates"):
# Switch to selfsigned first
webserver.succeed(f"systemctl clean acme-{fqdn}.service --what=state")
webserver.succeed(f"systemctl start acme-selfsigned-{fqdn}.service")
check_issuer(webserver, fqdn, "minica")
webserver.succeed("systemctl restart ${serverName}-config-reload.service")
# Check that the web server has picked up the selfsigned cert
check_connection(webserver, fqdn, minica=True)
webserver.succeed("systemctl stop renew-triggered.target")
webserver.succeed(f"systemctl start acme-{fqdn}.service")
webserver.wait_for_unit("renew-triggered.target")
check_issuer(webserver, fqdn, "pebble")
check_connection(webserver, fqdn)
with subtest("security.acme changes reflect on web server part 1"):
check_connection(webserver, f"certchange.{domain}", fail=True)
switch_to(webserver, "certchange")
webserver.wait_for_unit("renew-triggered.target")
check_connection(webserver, f"certchange.{domain}")
check_connection(webserver, fqdn)
with subtest("security.acme changes reflect on web server part 2"):
check_connection(webserver, f"certchange.{domain}")
switch_to(webserver, "certundo")
webserver.wait_for_unit("renew-triggered.target")
check_connection(webserver, f"certchange.{domain}", fail=True)
check_connection(webserver, fqdn)
with subtest("Zero configuration SSL certificates for a vhost"):
check_connection(webserver, f"zeroconf.{domain}", fail=True)
switch_to(webserver, "zeroconf")
webserver.wait_for_unit("renew-triggered.target")
check_connection(webserver, f"zeroconf.{domain}")
check_connection(webserver, f"zeroconf2.{domain}")
check_connection(webserver, fqdn)
with subtest("Removing an alias from a vhost"):
check_connection(webserver, f"zeroconf2.{domain}")
switch_to(webserver, "rmalias")
webserver.wait_for_unit("renew-triggered.target")
check_connection(webserver, f"zeroconf2.{domain}", fail=True)
check_connection(webserver, f"zeroconf.{domain}")
check_connection(webserver, fqdn)
with subtest("Create cert using inherited default validation mechanism"):
check_connection(webserver, f"nullroot.{domain}", fail=True)
switch_to(webserver, "nullroot")
webserver.wait_for_unit("renew-triggered.target")
check_connection(webserver, f"nullroot.{domain}")
'';
}