1{ system ? builtins.currentSystem, config ? { }
2, pkgs ? import ../.. { inherit system config; } }:
3
4with import ../lib/testing-python.nix { inherit system pkgs; };
5with pkgs.lib;
6
7let
8 stunnelCommon = {
9 services.stunnel = {
10 enable = true;
11 user = "stunnel";
12 };
13 users.groups.stunnel = { };
14 users.users.stunnel = {
15 isSystemUser = true;
16 group = "stunnel";
17 };
18 };
19 makeCert = { config, pkgs, ... }: {
20 system.activationScripts.create-test-cert = stringAfter [ "users" ] ''
21 ${pkgs.openssl}/bin/openssl req -batch -x509 -newkey rsa -nodes -out /test-cert.pem -keyout /test-key.pem -subj /CN=${config.networking.hostName}
22 ( umask 077; cat /test-key.pem /test-cert.pem > /test-key-and-cert.pem )
23 chown stunnel /test-key.pem /test-key-and-cert.pem
24 '';
25 };
26 serverCommon = { pkgs, ... }: {
27 networking.firewall.allowedTCPPorts = [ 443 ];
28 services.stunnel.servers.https = {
29 accept = "443";
30 connect = 80;
31 cert = "/test-key-and-cert.pem";
32 };
33 systemd.services.simple-webserver = {
34 wantedBy = [ "multi-user.target" ];
35 script = ''
36 cd /etc/webroot
37 ${pkgs.python3}/bin/python -m http.server 80
38 '';
39 };
40 };
41 copyCert = src: dest: filename: ''
42 from shlex import quote
43 ${src}.wait_for_file("/test-key-and-cert.pem")
44 server_cert = ${src}.succeed("cat /test-cert.pem")
45 ${dest}.succeed("echo %s > ${filename}" % quote(server_cert))
46 '';
47
48in {
49 basicServer = makeTest {
50 name = "basicServer";
51
52 nodes = {
53 client = { };
54 server = {
55 imports = [ makeCert serverCommon stunnelCommon ];
56 environment.etc."webroot/index.html".text = "well met";
57 };
58 };
59
60 testScript = ''
61 start_all()
62
63 ${copyCert "server" "client" "/authorized-server-cert.crt"}
64
65 server.wait_for_unit("simple-webserver")
66 server.wait_for_unit("stunnel")
67
68 client.succeed("curl --fail --cacert /authorized-server-cert.crt https://server/ > out")
69 client.succeed('[[ "$(< out)" == "well met" ]]')
70 '';
71 };
72
73 serverAndClient = makeTest {
74 name = "serverAndClient";
75
76 nodes = {
77 client = {
78 imports = [ stunnelCommon ];
79 services.stunnel.clients = {
80 httpsClient = {
81 accept = "80";
82 connect = "server:443";
83 CAFile = "/authorized-server-cert.crt";
84 };
85 httpsClientWithHostVerify = {
86 accept = "81";
87 connect = "server:443";
88 CAFile = "/authorized-server-cert.crt";
89 verifyHostname = "server";
90 };
91 httpsClientWithHostVerifyFail = {
92 accept = "82";
93 connect = "server:443";
94 CAFile = "/authorized-server-cert.crt";
95 verifyHostname = "wronghostname";
96 };
97 };
98 };
99 server = {
100 imports = [ makeCert serverCommon stunnelCommon ];
101 environment.etc."webroot/index.html".text = "hello there";
102 };
103 };
104
105 testScript = ''
106 start_all()
107
108 ${copyCert "server" "client" "/authorized-server-cert.crt"}
109
110 server.wait_for_unit("simple-webserver")
111 server.wait_for_unit("stunnel")
112
113 # In case stunnel came up before we got the server's cert copied over
114 client.succeed("systemctl reload-or-restart stunnel")
115
116 client.succeed("curl --fail http://localhost/ > out")
117 client.succeed('[[ "$(< out)" == "hello there" ]]')
118
119 client.succeed("curl --fail http://localhost:81/ > out")
120 client.succeed('[[ "$(< out)" == "hello there" ]]')
121
122 client.fail("curl --fail http://localhost:82/ > out")
123 client.succeed('[[ "$(< out)" == "" ]]')
124 '';
125 };
126
127 mutualAuth = makeTest {
128 name = "mutualAuth";
129
130 nodes = rec {
131 client = {
132 imports = [ makeCert stunnelCommon ];
133 services.stunnel.clients.authenticated-https = {
134 accept = "80";
135 connect = "server:443";
136 verifyPeer = true;
137 CAFile = "/authorized-server-cert.crt";
138 cert = "/test-cert.pem";
139 key = "/test-key.pem";
140 };
141 };
142 wrongclient = client;
143 server = {
144 imports = [ makeCert serverCommon stunnelCommon ];
145 services.stunnel.servers.https = {
146 CAFile = "/authorized-client-certs.crt";
147 verifyPeer = true;
148 };
149 environment.etc."webroot/index.html".text = "secret handshake";
150 };
151 };
152
153 testScript = ''
154 start_all()
155
156 ${copyCert "server" "client" "/authorized-server-cert.crt"}
157 ${copyCert "client" "server" "/authorized-client-certs.crt"}
158 ${copyCert "server" "wrongclient" "/authorized-server-cert.crt"}
159
160 # In case stunnel came up before we got the cross-certs in place
161 client.succeed("systemctl reload-or-restart stunnel")
162 server.succeed("systemctl reload-or-restart stunnel")
163 wrongclient.succeed("systemctl reload-or-restart stunnel")
164
165 server.wait_for_unit("simple-webserver")
166 client.fail("curl --fail --insecure https://server/ > out")
167 client.succeed('[[ "$(< out)" == "" ]]')
168 client.succeed("curl --fail http://localhost/ > out")
169 client.succeed('[[ "$(< out)" == "secret handshake" ]]')
170 wrongclient.fail("curl --fail http://localhost/ > out")
171 wrongclient.succeed('[[ "$(< out)" == "" ]]')
172 '';
173 };
174}