1import ./make-test-python.nix ({ lib, pkgs, ...}: {
2 name = "haproxy";
3 nodes = {
4 server = { ... }: {
5 services.haproxy = {
6 enable = true;
7 config = ''
8 global
9 limited-quic
10
11 defaults
12 mode http
13 timeout connect 10s
14 timeout client 10s
15 timeout server 10s
16
17 log /dev/log local0 debug err
18 option logasap
19 option httplog
20 option httpslog
21
22 backend http_server
23 server httpd [::1]:8000 alpn http/1.1
24
25 frontend http
26 bind :80
27 bind :443 ssl strict-sni crt /etc/ssl/fullchain.pem alpn h2,http/1.1
28 bind quic4@:443 ssl strict-sni crt /etc/ssl/fullchain.pem alpn h3 allow-0rtt
29
30 http-after-response add-header alt-svc 'h3=":443"; ma=60' if { ssl_fc }
31
32 http-request use-service prometheus-exporter if { path /metrics }
33 use_backend http_server
34
35 frontend http-cert-auth
36 bind :8443 ssl strict-sni crt /etc/ssl/fullchain.pem verify required ca-file /etc/ssl/cacert.crt
37 bind quic4@:8443 ssl strict-sni crt /etc/ssl/fullchain.pem verify required ca-file /etc/ssl/cacert.crt alpn h3
38
39 use_backend http_server
40 '';
41 };
42 services.httpd = {
43 enable = true;
44 virtualHosts.localhost = {
45 documentRoot = pkgs.writeTextDir "index.txt" "We are all good!";
46 adminAddr = "notme@yourhost.local";
47 listen = [{
48 ip = "::1";
49 port = 8000;
50 }];
51 };
52 };
53 networking.firewall.allowedTCPPorts = [ 80 443 8443 ];
54 networking.firewall.allowedUDPPorts = [ 443 8443 ];
55 };
56 client = { ... }: {
57 environment.systemPackages = [ pkgs.curlHTTP3 ];
58 };
59 };
60 testScript = ''
61 # Helpers
62 def cmd(command):
63 print(f"+{command}")
64 r = os.system(command)
65 if r != 0:
66 raise Exception(f"Command {command} failed with exit code {r}")
67
68 def openssl(command):
69 cmd(f"${pkgs.openssl}/bin/openssl {command}")
70
71 # Generate CA.
72 openssl("req -new -newkey rsa:4096 -nodes -x509 -days 7 -subj '/C=ZZ/ST=Cloud/L=Unspecified/O=NixOS/OU=Tests/CN=CA Certificate' -keyout cacert.key -out cacert.crt")
73
74 # Generate and sign Server.
75 openssl("req -newkey rsa:4096 -nodes -subj '/CN=server/OU=Tests/O=NixOS' -keyout server.key -out server.csr")
76 openssl("x509 -req -in server.csr -out server.crt -CA cacert.crt -CAkey cacert.key -days 7")
77 cmd("cat server.crt server.key > fullchain.pem")
78
79 # Generate and sign Client.
80 openssl("req -newkey rsa:4096 -nodes -subj '/CN=client/OU=Tests/O=NixOS' -keyout client.key -out client.csr")
81 openssl("x509 -req -in client.csr -out client.crt -CA cacert.crt -CAkey cacert.key -days 7")
82 cmd("cat client.crt client.key > client.pem")
83
84 # Start the actual test.
85 start_all()
86 server.copy_from_host("fullchain.pem", "/etc/ssl/fullchain.pem")
87 server.copy_from_host("cacert.crt", "/etc/ssl/cacert.crt")
88 server.succeed("chmod 0644 /etc/ssl/fullchain.pem /etc/ssl/cacert.crt")
89
90 client.copy_from_host("cacert.crt", "/etc/ssl/cacert.crt")
91 client.copy_from_host("client.pem", "/root/client.pem")
92
93 server.wait_for_unit("multi-user.target")
94 server.wait_for_unit("haproxy.service")
95 server.wait_for_unit("httpd.service")
96
97 assert "We are all good!" in client.succeed("curl -f http://server/index.txt")
98 assert "haproxy_process_pool_allocated_bytes" in client.succeed("curl -f http://server/metrics")
99
100 with subtest("https"):
101 assert "We are all good!" in client.succeed("curl -f --cacert /etc/ssl/cacert.crt https://server/index.txt")
102
103 with subtest("https-cert-auth"):
104 # Client must succeed in authenticating with the right certificate.
105 assert "We are all good!" in client.succeed("curl -f --cacert /etc/ssl/cacert.crt --cert-type pem --cert /root/client.pem https://server:8443/index.txt")
106 # Client must fail without certificate.
107 client.fail("curl --cacert /etc/ssl/cacert.crt https://server:8443/index.txt")
108
109 with subtest("h3"):
110 assert "We are all good!" in client.succeed("curl -f --http3-only --cacert /etc/ssl/cacert.crt https://server/index.txt")
111
112 with subtest("h3-cert-auth"):
113 # Client must succeed in authenticating with the right certificate.
114 assert "We are all good!" in client.succeed("curl -f --http3-only --cacert /etc/ssl/cacert.crt --cert-type pem --cert /root/client.pem https://server:8443/index.txt")
115 # Client must fail without certificate.
116 client.fail("curl -f --http3-only --cacert /etc/ssl/cacert.crt https://server:8443/index.txt")
117
118 with subtest("reload"):
119 server.succeed("systemctl reload haproxy")
120 # wait some time to ensure the following request hits the reloaded haproxy
121 server.sleep(5)
122 assert "We are all good!" in client.succeed("curl -f http://server/index.txt")
123 '';
124})