1{
2 config,
3 lib,
4 pkgs,
5 ...
6}:
7let
8 domain = "example.test";
9in
10{
11 # Caddy only supports useACMEHost, hence we use a distinct test suite
12 name = "caddy";
13 meta = {
14 maintainers = lib.teams.acme.members;
15 # Hard timeout in seconds. Average run time is about 60 seconds.
16 timeout = 180;
17 };
18
19 nodes = {
20 # The fake ACME server which will respond to client requests
21 acme =
22 { nodes, ... }:
23 {
24 imports = [ ../common/acme/server ];
25 };
26
27 caddy =
28 { nodes, config, ... }:
29 let
30 fqdn = config.networking.fqdn;
31 in
32 {
33 imports = [ ../common/acme/client ];
34 networking.domain = domain;
35 networking.firewall.allowedTCPPorts = [
36 80
37 443
38 ];
39
40 # Resolve the vhosts the easy way
41 networking.hosts."127.0.0.1" = [
42 "caddy-alt.${domain}"
43 ];
44
45 # OpenSSL will be used for more thorough certificate validation
46 environment.systemPackages = [ pkgs.openssl ];
47
48 security.acme.certs."${fqdn}" = {
49 listenHTTP = ":8080";
50 reloadServices = [ "caddy.service" ];
51 };
52
53 users.users."${config.services.caddy.user}".extraGroups = [ "acme" ];
54
55 services.caddy = {
56 enable = true;
57 # FIXME reloading caddy is not sufficient to load new certs.
58 # Restart it manually until this is fixed.
59 enableReload = false;
60 globalConfig = ''
61 auto_https off
62 '';
63 virtualHosts."${fqdn}:443" = {
64 useACMEHost = fqdn;
65 };
66 virtualHosts.":80".extraConfig = ''
67 reverse_proxy localhost:8080
68 '';
69 };
70
71 specialisation.add_domain.configuration = {
72 security.acme.certs.${fqdn}.extraDomainNames = [
73 "caddy-alt.${domain}"
74 ];
75 };
76 };
77 };
78
79 testScript =
80 { nodes, ... }:
81 ''
82 ${(import ./utils.nix).pythonUtils}
83
84 domain = "${domain}"
85 ca_domain = "${nodes.acme.test-support.acme.caDomain}"
86 fqdn = "${nodes.caddy.networking.fqdn}"
87
88 with subtest("Boot and start with selfsigned certificates"):
89 caddy.start()
90 caddy.wait_for_unit("caddy.service")
91 check_issuer(caddy, fqdn, "minica")
92 # Check that the web server has picked up the selfsigned cert
93 check_connection(caddy, fqdn, minica=True)
94
95 acme.start()
96 wait_for_running(acme)
97 acme.wait_for_open_port(443)
98
99 with subtest("Acquire a new cert"):
100 caddy.succeed(f"systemctl restart acme-{fqdn}.service")
101 check_issuer(caddy, fqdn, "pebble")
102 check_domain(caddy, fqdn, fqdn)
103 download_ca_certs(caddy, ca_domain)
104 check_connection(caddy, fqdn)
105
106 with subtest("security.acme changes reflect on caddy"):
107 check_connection(caddy, f"caddy-alt.{domain}", fail=True)
108 switch_to(caddy, "add_domain")
109 check_connection(caddy, f"caddy-alt.{domain}")
110 '';
111}