1{
2 config,
3 lib,
4 pkgs,
5 ...
6}:
7let
8 domain = "example.test";
9
10 dnsServerIP = nodes: nodes.dnsserver.networking.primaryIPAddress;
11
12 dnsScript = pkgs.writeShellScript "dns-hook.sh" ''
13 set -euo pipefail
14 echo '[INFO]' "[$2]" 'dns-hook.sh' $*
15 if [ "$1" = "present" ]; then
16 ${pkgs.curl}/bin/curl --data @- http://dnsserver.test:8055/set-txt << EOF
17 {"host": "$2", "value": "$3"}
18 EOF
19 else
20 ${pkgs.curl}/bin/curl --data @- http://dnsserver.test:8055/clear-txt << EOF
21 {"host": "$2"}
22 EOF
23 fi
24 '';
25in
26{
27 name = "dns01";
28 meta = {
29 maintainers = lib.teams.acme.members;
30 # Hard timeout in seconds. Average run time is about 60 seconds.
31 timeout = 180;
32 };
33
34 nodes = {
35 # The fake ACME server which will respond to client requests
36 acme =
37 { nodes, ... }:
38 {
39 imports = [ ../common/acme/server ];
40 networking.nameservers = lib.mkForce [ (dnsServerIP nodes) ];
41 };
42
43 # A fake DNS server which can be configured with records as desired
44 # Used to test DNS-01 challenge
45 dnsserver =
46 { nodes, ... }:
47 {
48 networking = {
49 firewall.allowedTCPPorts = [
50 8055
51 53
52 ];
53 firewall.allowedUDPPorts = [ 53 ];
54
55 # nixos/lib/testing/network.nix will provide name resolution via /etc/hosts
56 # for all nodes based on their host names and domain
57 hostName = "dnsserver";
58 domain = "test";
59 };
60 systemd.services.pebble-challtestsrv = {
61 enable = true;
62 description = "Pebble ACME challenge test server";
63 wantedBy = [ "network.target" ];
64 serviceConfig = {
65 ExecStart = "${pkgs.pebble}/bin/pebble-challtestsrv -dns01 ':53' -defaultIPv6 '' -defaultIPv4 '${nodes.client.networking.primaryIPAddress}'";
66 # Required to bind on privileged ports.
67 AmbientCapabilities = [ "CAP_NET_BIND_SERVICE" ];
68 };
69 };
70 };
71
72 client =
73 { nodes, ... }:
74 {
75 imports = [ ../common/acme/client ];
76 networking.domain = domain;
77 networking.nameservers = lib.mkForce [ (dnsServerIP nodes) ];
78
79 # OpenSSL will be used for more thorough certificate validation
80 environment.systemPackages = [ pkgs.openssl ];
81
82 security.acme.certs."${domain}" = {
83 domain = "*.${domain}";
84 dnsProvider = "exec";
85 dnsPropagationCheck = false;
86 environmentFile = pkgs.writeText "wildcard.env" ''
87 EXEC_PATH=${dnsScript}
88 EXEC_POLLING_INTERVAL=1
89 EXEC_PROPAGATION_TIMEOUT=1
90 EXEC_SEQUENCE_INTERVAL=1
91 '';
92 };
93 };
94 };
95
96 testScript = ''
97 ${(import ./utils.nix).pythonUtils}
98
99 cert = "${domain}"
100
101 dnsserver.start()
102 acme.start()
103
104 wait_for_running(dnsserver)
105 dnsserver.wait_for_open_port(53)
106 wait_for_running(acme)
107 acme.wait_for_open_port(443)
108
109 with subtest("Boot and acquire a new cert"):
110 client.start()
111 wait_for_running(client)
112
113 check_issuer(client, cert, "pebble")
114 check_domain(client, cert, cert, fail=True)
115 check_domain(client, cert, f"toodeep.nesting.{cert}", fail=True)
116 check_domain(client, cert, f"whatever.{cert}")
117 '';
118}