1{
2 lib,
3 pkgs,
4 ...
5}:
6let
7 # cant use .test, since that gets caught by traefik
8 domain = "nixos.eu";
9 secret = "1234567890";
10
11 dnsServerIP = nodes: nodes.dnsserver.networking.primaryIPAddress;
12
13in
14{
15 name = "pangolin";
16 meta.maintainers = with lib.maintainers; [
17 jackr
18 sigmasquadron
19 ];
20
21 # The full test is not yet implemented, but once upstream supports a way to
22 # configure Pangolin non-interactively, the full test will look like the following:
23 # - 'acme': ACME server to replace the real servers at Let's Encrypt.
24 # - 'dnsserver': The pebble challenge test server so we can use a private DNS
25 # for everything here.
26 # - 'VPS': The Pangolin instance, running Gerbil, Traefik, and Badger as well.
27 # - 'privateHost': The private server running an HTTP server on its local
28 # network that will be tunnelled via Newt to the VPS.
29 # - 'client': An outside node that will test if the service hosted in
30 # 'privateHost' is publicly accessible.
31 # TODO: In the future, we should also have a machine to test the
32 # functionality of Olm, as well as a split Pangolin/Gerbil
33 # configuration once that is implemented into the module.
34 nodes = {
35 acme =
36 { nodes, ... }:
37 {
38 imports = [ ./common/acme/server ];
39 networking.nameservers = lib.mkForce [ (dnsServerIP nodes) ];
40 };
41
42 dnsserver =
43 { nodes, ... }:
44 {
45 networking = {
46 firewall.allowedTCPPorts = [
47 8055
48 53
49 ];
50 firewall.allowedUDPPorts = [ 53 ];
51
52 # nixos/lib/testing/network.nix will provide name resolution via /etc/hosts
53 # for all nodes based on their host names and domain
54 hostName = "dnsserver";
55 domain = "eu";
56 };
57 systemd.services.pebble-challtestsrv = {
58 description = "Pebble ACME challenge test server";
59 wantedBy = [ "network.target" ];
60 serviceConfig = {
61 ExecStart = "${lib.getExe' pkgs.pebble "pebble-challtestsrv"} -dns01 ':53' -defaultIPv6 '' -defaultIPv4 '${nodes.VPS.networking.primaryIPAddress}'";
62 # Required to bind on privileged ports.
63 AmbientCapabilities = [ "CAP_NET_BIND_SERVICE" ];
64 };
65 };
66 };
67
68 VPS =
69 { nodes, ... }:
70 {
71 imports = [ ./common/acme/client ];
72 networking = {
73 inherit domain;
74 hosts.${nodes.VPS.networking.primaryIPAddress} = [
75 domain
76 "pangolin.${domain}"
77 ];
78 nameservers = lib.mkForce [ (dnsServerIP nodes) ];
79 };
80
81 environment = {
82 etc = {
83 "nixos/secrets/pangolin.env".text = ''
84 SERVER_SECRET=${secret}
85 '';
86 };
87 };
88
89 services = {
90 pangolin = {
91 enable = true;
92 baseDomain = domain;
93 letsEncryptEmail = "pangolin@${domain}";
94 openFirewall = true;
95 environmentFile = "/etc/nixos/secrets/pangolin.env";
96 settings = {
97 flags.enable_integration_api = true;
98 };
99 };
100 # set up local ca server, so we can get our certs signed without going on the internet
101 traefik.staticConfigOptions.certificatesResolvers.letsencrypt.acme.caServer =
102 lib.mkForce "https://${nodes.acme.test-support.acme.caDomain}/dir";
103 };
104 };
105
106 };
107 testScript = ''
108 ${(import ./acme/utils.nix).pythonUtils}
109
110 with subtest("start ACME and DNS server"):
111 acme.start()
112 wait_for_running(acme)
113 acme.wait_for_open_port(443)
114 dnsserver.start()
115 dnsserver.wait_for_open_port(53)
116
117 VPS.start()
118
119 with subtest("start Pangolin"):
120 VPS.wait_for_unit("pangolin.service")
121 VPS.wait_for_open_port(3000)
122 VPS.wait_for_open_port(3001)
123 VPS.wait_for_open_port(3002)
124 VPS.wait_for_open_port(3003)
125
126 with subtest("start Gerbil"):
127 VPS.wait_for_unit("gerbil.service")
128
129 with subtest("start Traefik"):
130 VPS.wait_for_unit("traefik.service")
131 VPS.wait_for_open_port(80)
132 VPS.wait_for_open_port(443)
133
134 with subtest("check traefik certs}"):
135 download_ca_certs(VPS, "acme.test")
136
137 '';
138}