1 { system ? builtins.currentSystem, config ? { }
2 , pkgs ? import ../.. { inherit system config; } }:
4 with import ../lib/testing-python.nix { inherit system pkgs; };
13 users.groups.stunnel = { };
14 users.users.stunnel = {
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
26 serverCommon = { pkgs, ... }: {
27 networking.firewall.allowedTCPPorts = [ 443 ];
28 services.stunnel.servers.https = {
31 cert = "/test-key-and-cert.pem";
33 systemd.services.simple-webserver = {
34 wantedBy = [ "multi-user.target" ];
37 ${pkgs.python3}/bin/python -m http.server 80
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))
49 basicServer = makeTest {
55 imports = [ makeCert serverCommon stunnelCommon ];
56 environment.etc."webroot/index.html".text = "well met";
63 ${copyCert "server" "client" "/authorized-server-cert.crt"}
65 server.wait_for_unit("simple-webserver")
66 server.wait_for_unit("stunnel")
68 client.succeed("curl --fail --cacert /authorized-server-cert.crt https://server/ > out")
69 client.succeed('[[ "$(< out)" == "well met" ]]')
73 serverAndClient = makeTest {
74 name = "serverAndClient";
78 imports = [ stunnelCommon ];
79 services.stunnel.clients = {
82 connect = "server:443";
83 CAFile = "/authorized-server-cert.crt";
85 httpsClientWithHostVerify = {
87 connect = "server:443";
88 CAFile = "/authorized-server-cert.crt";
89 verifyHostname = "server";
91 httpsClientWithHostVerifyFail = {
93 connect = "server:443";
94 CAFile = "/authorized-server-cert.crt";
95 verifyHostname = "wronghostname";
100 imports = [ makeCert serverCommon stunnelCommon ];
101 environment.etc."webroot/index.html".text = "hello there";
108 ${copyCert "server" "client" "/authorized-server-cert.crt"}
110 server.wait_for_unit("simple-webserver")
111 server.wait_for_unit("stunnel")
113 # In case stunnel came up before we got the server's cert copied over
114 client.succeed("systemctl reload-or-restart stunnel")
116 client.succeed("curl --fail http://localhost/ > out")
117 client.succeed('[[ "$(< out)" == "hello there" ]]')
119 client.succeed("curl --fail http://localhost:81/ > out")
120 client.succeed('[[ "$(< out)" == "hello there" ]]')
122 client.fail("curl --fail http://localhost:82/ > out")
123 client.succeed('[[ "$(< out)" == "" ]]')
127 mutualAuth = makeTest {
132 imports = [ makeCert stunnelCommon ];
133 services.stunnel.clients.authenticated-https = {
135 connect = "server:443";
137 CAFile = "/authorized-server-cert.crt";
138 cert = "/test-cert.pem";
139 key = "/test-key.pem";
142 wrongclient = client;
144 imports = [ makeCert serverCommon stunnelCommon ];
145 services.stunnel.servers.https = {
146 CAFile = "/authorized-client-certs.crt";
149 environment.etc."webroot/index.html".text = "secret handshake";
156 ${copyCert "server" "client" "/authorized-server-cert.crt"}
157 ${copyCert "client" "server" "/authorized-client-certs.crt"}
158 ${copyCert "server" "wrongclient" "/authorized-server-cert.crt"}
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")
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)" == "" ]]')