at 25.11-pre 6.6 kB view raw
1{ 2 serverName, 3 group, 4 baseModule, 5 domain ? "example.test", 6}: 7{ 8 config, 9 lib, 10 pkgs, 11 ... 12}: 13{ 14 name = serverName; 15 meta = { 16 maintainers = lib.teams.acme.members; 17 # Hard timeout in seconds. Average run time is about 100 seconds. 18 timeout = 300; 19 }; 20 21 nodes = { 22 # The fake ACME server which will respond to client requests 23 acme = 24 { nodes, ... }: 25 { 26 imports = [ ../common/acme/server ]; 27 }; 28 29 webserver = 30 { nodes, ... }: 31 { 32 imports = [ 33 ../common/acme/client 34 baseModule 35 ]; 36 networking.domain = domain; 37 networking.firewall.allowedTCPPorts = [ 38 80 39 443 40 ]; 41 42 # Resolve the vhosts the easy way 43 networking.hosts."127.0.0.1" = [ 44 "proxied.${domain}" 45 "certchange.${domain}" 46 "zeroconf.${domain}" 47 "zeroconf2.${domain}" 48 "nullroot.${domain}" 49 ]; 50 51 # OpenSSL will be used for more thorough certificate validation 52 environment.systemPackages = [ pkgs.openssl ]; 53 54 # Used to determine if service reload was triggered. 55 # This does not provide a guarantee that the webserver is finished reloading, 56 # to handle that there is retry logic wrapping any connectivity checks. 57 systemd.targets."renew-triggered" = { 58 wantedBy = [ "${serverName}-config-reload.service" ]; 59 after = [ "${serverName}-config-reload.service" ]; 60 }; 61 62 security.acme.certs."proxied.${domain}" = { 63 listenHTTP = ":8080"; 64 group = group; 65 }; 66 67 specialisation = { 68 # Test that the web server is correctly reloaded when the cert changes 69 certchange.configuration = { 70 security.acme.certs."proxied.${domain}".extraDomainNames = [ 71 "certchange.${domain}" 72 ]; 73 }; 74 75 # A useful transitional step before other tests, and tests behaviour 76 # of removing an extra domain from a cert. 77 certundo.configuration = { }; 78 79 # Tests these features: 80 # - enableACME behaves as expected 81 # - serverAliases are appended to extraDomainNames 82 # - Correct routing to the specific virtualHost for a cert 83 # Inherits previous test config 84 zeroconf.configuration = { 85 services.${serverName}.virtualHosts."zeroconf.${domain}" = { 86 addSSL = true; 87 enableACME = true; 88 serverAliases = [ "zeroconf2.${domain}" ]; 89 }; 90 }; 91 92 # Test that serverAliases are correctly removed which triggers 93 # cert regeneration and service reload. 94 rmalias.configuration = { 95 services.${serverName}.virtualHosts."zeroconf.${domain}" = { 96 addSSL = true; 97 enableACME = true; 98 }; 99 }; 100 101 # Test that "acmeRoot = null" still results in 102 # valid cert generation by inheriting defaults. 103 nullroot.configuration = { 104 security.acme.defaults.listenHTTP = ":8080"; 105 services.${serverName}.virtualHosts."nullroot.${domain}" = { 106 onlySSL = true; 107 enableACME = true; 108 acmeRoot = null; 109 }; 110 }; 111 }; 112 }; 113 }; 114 115 testScript = 116 { nodes, ... }: 117 '' 118 ${(import ./utils.nix).pythonUtils} 119 120 domain = "${domain}" 121 ca_domain = "${nodes.acme.test-support.acme.caDomain}" 122 fqdn = f"proxied.{domain}" 123 124 acme.start() 125 wait_for_running(acme) 126 acme.wait_for_open_port(443) 127 128 with subtest("Acquire a cert through a proxied lego"): 129 webserver.start() 130 webserver.succeed("systemctl is-system-running --wait") 131 wait_for_running(webserver) 132 download_ca_certs(webserver, ca_domain) 133 check_connection(webserver, fqdn) 134 135 with subtest("Can run on selfsigned certificates"): 136 # Switch to selfsigned first 137 webserver.succeed(f"systemctl clean acme-{fqdn}.service --what=state") 138 webserver.succeed(f"systemctl start acme-selfsigned-{fqdn}.service") 139 check_issuer(webserver, fqdn, "minica") 140 webserver.succeed("systemctl restart ${serverName}-config-reload.service") 141 # Check that the web server has picked up the selfsigned cert 142 check_connection(webserver, fqdn, minica=True) 143 webserver.succeed("systemctl stop renew-triggered.target") 144 webserver.succeed(f"systemctl start acme-{fqdn}.service") 145 webserver.wait_for_unit("renew-triggered.target") 146 check_issuer(webserver, fqdn, "pebble") 147 check_connection(webserver, fqdn) 148 149 with subtest("security.acme changes reflect on web server part 1"): 150 check_connection(webserver, f"certchange.{domain}", fail=True) 151 switch_to(webserver, "certchange") 152 webserver.wait_for_unit("renew-triggered.target") 153 check_connection(webserver, f"certchange.{domain}") 154 check_connection(webserver, fqdn) 155 156 with subtest("security.acme changes reflect on web server part 2"): 157 check_connection(webserver, f"certchange.{domain}") 158 switch_to(webserver, "certundo") 159 webserver.wait_for_unit("renew-triggered.target") 160 check_connection(webserver, f"certchange.{domain}", fail=True) 161 check_connection(webserver, fqdn) 162 163 with subtest("Zero configuration SSL certificates for a vhost"): 164 check_connection(webserver, f"zeroconf.{domain}", fail=True) 165 switch_to(webserver, "zeroconf") 166 webserver.wait_for_unit("renew-triggered.target") 167 check_connection(webserver, f"zeroconf.{domain}") 168 check_connection(webserver, f"zeroconf2.{domain}") 169 check_connection(webserver, fqdn) 170 171 with subtest("Removing an alias from a vhost"): 172 check_connection(webserver, f"zeroconf2.{domain}") 173 switch_to(webserver, "rmalias") 174 webserver.wait_for_unit("renew-triggered.target") 175 check_connection(webserver, f"zeroconf2.{domain}", fail=True) 176 check_connection(webserver, f"zeroconf.{domain}") 177 check_connection(webserver, fqdn) 178 179 with subtest("Create cert using inherited default validation mechanism"): 180 check_connection(webserver, f"nullroot.{domain}", fail=True) 181 switch_to(webserver, "nullroot") 182 webserver.wait_for_unit("renew-triggered.target") 183 check_connection(webserver, f"nullroot.{domain}") 184 ''; 185}