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 systemd.services.create-test-cert = {
21 wantedBy = [ "sysinit.target" ];
22 before = [ "sysinit.target" "shutdown.target" ];
23 conflicts = [ "shutdown.target" ];
24 unitConfig.DefaultDependencies = false;
25 serviceConfig.Type = "oneshot";
27 ${pkgs.openssl}/bin/openssl req -batch -x509 -newkey rsa -nodes -out /test-cert.pem -keyout /test-key.pem -subj /CN=${config.networking.hostName}
28 ( umask 077; cat /test-key.pem /test-cert.pem > /test-key-and-cert.pem )
29 chown stunnel /test-key.pem /test-key-and-cert.pem
33 serverCommon = { pkgs, ... }: {
34 networking.firewall.allowedTCPPorts = [ 443 ];
35 services.stunnel.servers.https = {
38 cert = "/test-key-and-cert.pem";
40 systemd.services.simple-webserver = {
41 wantedBy = [ "multi-user.target" ];
44 ${pkgs.python3}/bin/python -m http.server 80
48 copyCert = src: dest: filename: ''
49 from shlex import quote
50 ${src}.wait_for_file("/test-key-and-cert.pem")
51 server_cert = ${src}.succeed("cat /test-cert.pem")
52 ${dest}.succeed("echo %s > ${filename}" % quote(server_cert))
56 basicServer = makeTest {
62 imports = [ makeCert serverCommon stunnelCommon ];
63 environment.etc."webroot/index.html".text = "well met";
70 ${copyCert "server" "client" "/authorized-server-cert.crt"}
72 server.wait_for_unit("simple-webserver")
73 server.wait_for_unit("stunnel")
75 client.succeed("curl --fail --cacert /authorized-server-cert.crt https://server/ > out")
76 client.succeed('[[ "$(< out)" == "well met" ]]')
80 serverAndClient = makeTest {
81 name = "serverAndClient";
85 imports = [ stunnelCommon ];
86 services.stunnel.clients = {
89 connect = "server:443";
90 CAFile = "/authorized-server-cert.crt";
92 httpsClientWithHostVerify = {
94 connect = "server:443";
95 CAFile = "/authorized-server-cert.crt";
96 verifyHostname = "server";
98 httpsClientWithHostVerifyFail = {
100 connect = "server:443";
101 CAFile = "/authorized-server-cert.crt";
102 verifyHostname = "wronghostname";
107 imports = [ makeCert serverCommon stunnelCommon ];
108 environment.etc."webroot/index.html".text = "hello there";
115 ${copyCert "server" "client" "/authorized-server-cert.crt"}
117 server.wait_for_unit("simple-webserver")
118 server.wait_for_unit("stunnel")
120 # In case stunnel came up before we got the server's cert copied over
121 client.succeed("systemctl reload-or-restart stunnel")
123 client.succeed("curl --fail http://localhost/ > out")
124 client.succeed('[[ "$(< out)" == "hello there" ]]')
126 client.succeed("curl --fail http://localhost:81/ > out")
127 client.succeed('[[ "$(< out)" == "hello there" ]]')
129 client.fail("curl --fail http://localhost:82/ > out")
130 client.succeed('[[ "$(< out)" == "" ]]')
134 mutualAuth = makeTest {
139 imports = [ makeCert stunnelCommon ];
140 services.stunnel.clients.authenticated-https = {
142 connect = "server:443";
144 CAFile = "/authorized-server-cert.crt";
145 cert = "/test-cert.pem";
146 key = "/test-key.pem";
149 wrongclient = client;
151 imports = [ makeCert serverCommon stunnelCommon ];
152 services.stunnel.servers.https = {
153 CAFile = "/authorized-client-certs.crt";
156 environment.etc."webroot/index.html".text = "secret handshake";
163 ${copyCert "server" "client" "/authorized-server-cert.crt"}
164 ${copyCert "client" "server" "/authorized-client-certs.crt"}
165 ${copyCert "server" "wrongclient" "/authorized-server-cert.crt"}
167 # In case stunnel came up before we got the cross-certs in place
168 client.succeed("systemctl reload-or-restart stunnel")
169 server.succeed("systemctl reload-or-restart stunnel")
170 wrongclient.succeed("systemctl reload-or-restart stunnel")
172 server.wait_for_unit("simple-webserver")
173 client.fail("curl --fail --insecure https://server/ > out")
174 client.succeed('[[ "$(< out)" == "" ]]')
175 client.succeed("curl --fail http://localhost/ > out")
176 client.succeed('[[ "$(< out)" == "secret handshake" ]]')
177 wrongclient.fail("curl --fail http://localhost/ > out")
178 wrongclient.succeed('[[ "$(< out)" == "" ]]')