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