at master 11 kB view raw
1{ pkgs, ... }: 2let 3 certs = import ../common/acme/server/snakeoil-certs.nix; 4 domain = certs.domain; 5in 6{ 7 name = "firezone"; 8 meta.maintainers = with pkgs.lib.maintainers; [ oddlama ]; 9 10 nodes = { 11 server = 12 { 13 config, 14 lib, 15 pkgs, 16 ... 17 }: 18 { 19 security.pki.certificateFiles = [ certs.ca.cert ]; 20 21 networking.extraHosts = '' 22 ${config.networking.primaryIPAddress} ${domain} 23 ${config.networking.primaryIPv6Address} ${domain} 24 ''; 25 26 networking.firewall.allowedTCPPorts = [ 27 80 28 443 29 ]; 30 31 services.nginx = { 32 enable = true; 33 virtualHosts.${domain} = { 34 sslCertificate = certs.${domain}.cert; 35 sslCertificateKey = certs.${domain}.key; 36 }; 37 }; 38 39 services.firezone.server = { 40 enable = true; 41 enableLocalDB = true; 42 nginx.enable = true; 43 44 # Doesn't need to work for this test, but needs to be configured 45 # otherwise the server will not start. 46 smtp = { 47 from = "firezone@example.com"; 48 host = "mail.localhost"; 49 port = 465; 50 implicitTls = true; 51 username = "firezone@example.com"; 52 passwordFile = pkgs.writeText "tmpmailpasswd" "supermailpassword"; 53 }; 54 55 provision = { 56 enable = true; 57 accounts.main = { 58 name = "My Account"; 59 relayGroups.my-relays.name = "Relays"; 60 gatewayGroups.site.name = "Site"; 61 actors = { 62 admin = { 63 type = "account_admin_user"; 64 name = "Admin"; 65 email = "admin@example.com"; 66 }; 67 client = { 68 type = "service_account"; 69 name = "A client"; 70 email = "client@example.com"; 71 }; 72 }; 73 resources.res1 = { 74 type = "dns"; 75 name = "Dns Resource"; 76 address = "resource.example.com"; 77 gatewayGroups = [ "site" ]; 78 filters = [ 79 { protocol = "icmp"; } 80 { 81 protocol = "tcp"; 82 ports = [ 80 ]; 83 } 84 ]; 85 }; 86 resources.res2 = { 87 type = "ip"; 88 name = "Ip Resource"; 89 address = "172.20.2.1"; 90 gatewayGroups = [ "site" ]; 91 }; 92 resources.res3 = { 93 type = "cidr"; 94 name = "Cidr Resource"; 95 address = "172.20.1.0/24"; 96 gatewayGroups = [ "site" ]; 97 }; 98 policies.pol1 = { 99 description = "Allow anyone res1 access"; 100 group = "everyone"; 101 resource = "res1"; 102 }; 103 policies.pol2 = { 104 description = "Allow anyone res2 access"; 105 group = "everyone"; 106 resource = "res2"; 107 }; 108 policies.pol3 = { 109 description = "Allow anyone res3 access"; 110 group = "everyone"; 111 resource = "res3"; 112 }; 113 }; 114 }; 115 116 api.externalUrl = "https://${domain}/api/"; 117 web.externalUrl = "https://${domain}/"; 118 }; 119 120 systemd.services.firezone-server-domain.postStart = lib.mkAfter '' 121 ${lib.getExe config.services.firezone.server.domain.package} rpc 'Code.eval_file("${./create-tokens.exs}")' 122 ''; 123 }; 124 125 relay = 126 { 127 nodes, 128 config, 129 lib, 130 ... 131 }: 132 { 133 security.pki.certificateFiles = [ certs.ca.cert ]; 134 networking.extraHosts = '' 135 ${nodes.server.networking.primaryIPAddress} ${domain} 136 ${nodes.server.networking.primaryIPv6Address} ${domain} 137 ''; 138 139 services.firezone.relay = { 140 enable = true; 141 logLevel = "debug"; 142 name = "test-relay"; 143 apiUrl = "wss://${domain}/api/"; 144 tokenFile = "/tmp/shared/relay_token.txt"; 145 publicIpv4 = config.networking.primaryIPAddress; 146 publicIpv6 = config.networking.primaryIPv6Address; 147 openFirewall = true; 148 }; 149 150 # Don't auto-start so we can wait until the token was provisioned 151 systemd.services.firezone-relay.wantedBy = lib.mkForce [ ]; 152 }; 153 154 # A resource that is only connected to the gateway, 155 # allowing us to confirm the VPN works 156 resource = { 157 virtualisation.vlans = [ 158 1 159 2 160 ]; 161 162 networking.interfaces.eth1.ipv4.addresses = [ 163 { 164 address = "172.20.1.1"; 165 prefixLength = 24; 166 } 167 ]; 168 169 networking.interfaces.eth2.ipv4.addresses = [ 170 { 171 address = "172.20.2.1"; 172 prefixLength = 24; 173 } 174 ]; 175 176 networking.firewall.allowedTCPPorts = [ 177 80 178 ]; 179 180 services.nginx = { 181 enable = true; 182 virtualHosts = { 183 "localhost" = { 184 default = true; 185 locations."/".extraConfig = '' 186 return 200 'greetings from the resource'; 187 add_header Content-Type text/plain; 188 ''; 189 }; 190 }; 191 }; 192 }; 193 194 gateway = 195 { 196 nodes, 197 lib, 198 ... 199 }: 200 { 201 virtualisation.vlans = [ 202 1 203 2 204 ]; 205 206 networking = { 207 interfaces.eth1.ipv4.addresses = [ 208 { 209 address = "172.20.1.2"; 210 prefixLength = 24; 211 } 212 ]; 213 214 interfaces.eth2.ipv4.addresses = [ 215 { 216 address = "172.20.2.2"; 217 prefixLength = 24; 218 } 219 ]; 220 221 firewall.enable = false; 222 nftables.enable = true; 223 nftables.tables."filter".family = "inet"; 224 nftables.tables."filter".content = '' 225 chain incoming { 226 type filter hook input priority 0; policy accept; 227 } 228 229 chain postrouting { 230 type nat hook postrouting priority srcnat; policy accept; 231 meta protocol ip iifname "tun-firezone" oifname { "eth1", "eth2" } masquerade random 232 } 233 234 chain forward { 235 type filter hook forward priority 0; policy drop; 236 iifname "tun-firezone" accept 237 oifname "tun-firezone" accept 238 } 239 240 chain output { 241 type filter hook output priority 0; policy accept; 242 } 243 ''; 244 }; 245 246 boot.kernel.sysctl."net.ipv4.ip_forward" = "1"; 247 # boot.kernel.sysctl."net.ipv4.conf.all.src_valid_mark" = "1"; 248 boot.kernel.sysctl."net.ipv6.conf.default.forwarding" = "1"; 249 boot.kernel.sysctl."net.ipv6.conf.all.forwarding" = "1"; 250 251 security.pki.certificateFiles = [ certs.ca.cert ]; 252 networking.extraHosts = '' 253 ${nodes.server.networking.primaryIPAddress} ${domain} 254 ${nodes.server.networking.primaryIPv6Address} ${domain} 255 172.20.1.1 resource.example.com 256 ''; 257 258 services.firezone.gateway = { 259 enable = true; 260 logLevel = "debug"; 261 name = "test-gateway"; 262 apiUrl = "wss://${domain}/api/"; 263 tokenFile = "/tmp/shared/gateway_token.txt"; 264 }; 265 266 # Don't auto-start so we can wait until the token was provisioned 267 systemd.services.firezone-gateway.wantedBy = lib.mkForce [ ]; 268 }; 269 270 client = 271 { 272 nodes, 273 lib, 274 ... 275 }: 276 { 277 security.pki.certificateFiles = [ certs.ca.cert ]; 278 networking.useNetworkd = true; 279 networking.extraHosts = '' 280 ${nodes.server.networking.primaryIPAddress} ${domain} 281 ${nodes.server.networking.primaryIPv6Address} ${domain} 282 ''; 283 284 services.firezone.headless-client = { 285 enable = true; 286 logLevel = "debug"; 287 name = "test-client-somebody"; 288 apiUrl = "wss://${domain}/api/"; 289 tokenFile = "/tmp/shared/client_token.txt"; 290 }; 291 292 # Don't auto-start so we can wait until the token was provisioned 293 systemd.services.firezone-headless-client.wantedBy = lib.mkForce [ ]; 294 }; 295 }; 296 297 testScript = 298 { ... }: 299 '' 300 start_all() 301 302 with subtest("Start server"): 303 server.wait_for_unit("firezone.target") 304 server.wait_until_succeeds("curl -Lsf https://${domain} | grep 'Welcome to Firezone'") 305 server.wait_until_succeeds("curl -Ls https://${domain}/api | grep 'Not Found'") 306 307 # Wait for tokens and copy them to shared folder 308 server.wait_for_file("/var/lib/private/firezone/relay_token.txt") 309 server.wait_for_file("/var/lib/private/firezone/gateway_token.txt") 310 server.wait_for_file("/var/lib/private/firezone/client_token.txt") 311 server.succeed("cp /var/lib/private/firezone/*_token.txt /tmp/shared") 312 313 with subtest("Connect relay"): 314 relay.succeed("systemctl start firezone-relay") 315 relay.wait_for_unit("firezone-relay.service") 316 relay.wait_until_succeeds("journalctl --since -2m --unit firezone-relay.service --grep 'Connected to portal.*${domain}'", timeout=30) 317 318 with subtest("Connect gateway"): 319 gateway.succeed("systemctl start firezone-gateway") 320 gateway.wait_for_unit("firezone-gateway.service") 321 gateway.wait_until_succeeds("journalctl --since -2m --unit firezone-gateway.service --grep 'Connected to portal.*${domain}'", timeout=30) 322 relay.wait_until_succeeds("journalctl --since -2m --unit firezone-relay.service --grep 'Created allocation.*IPv4'", timeout=30) 323 relay.wait_until_succeeds("journalctl --since -2m --unit firezone-relay.service --grep 'Created allocation.*IPv6'", timeout=30) 324 325 # Assert both relay ips are known 326 gateway.wait_until_succeeds("journalctl --since -2m --unit firezone-gateway.service --grep 'Updated allocation.*relay_ip4.*Some.*relay_ip6.*Some'", timeout=30) 327 328 with subtest("Connect headless-client"): 329 client.succeed("systemctl start firezone-headless-client") 330 client.wait_for_unit("firezone-headless-client.service") 331 client.wait_until_succeeds("journalctl --since -2m --unit firezone-headless-client.service --grep 'Connected to portal.*${domain}'", timeout=30) 332 client.wait_until_succeeds("journalctl --since -2m --unit firezone-headless-client.service --grep 'Tunnel ready'", timeout=30) 333 334 with subtest("Check DNS based access"): 335 # Check that we can access the resource through the VPN via DNS 336 client.wait_until_succeeds("curl -4 -Lsf http://resource.example.com | grep 'greetings from the resource'") 337 client.wait_until_succeeds("curl -6 -Lsf http://resource.example.com | grep 'greetings from the resource'") 338 339 with subtest("Check CIDR based access"): 340 # Check that we can access the resource through the VPN via CIDR 341 client.wait_until_succeeds("ping -c1 -W1 172.20.1.1") 342 343 with subtest("Check IP based access"): 344 # Check that we can access the resource through the VPN via IP 345 client.wait_until_succeeds("ping -c1 -W1 172.20.2.1") 346 ''; 347}