at master 5.9 kB view raw
1# This is a distributed test of the Squid as a forward proxy 2# - "external" -- i.e. the internet, where the proxy and server communicate 3# - "internal" -- i.e. an office LAN, where the client and proxy communicat 4 5{ 6 pkgs, 7 lib, 8 ... 9}: 10# VLANS: 11# 1 -- simulates the internal network 12# 2 -- simulates the external network 13let 14 commonConfig = { 15 # Disable eth0 autoconfiguration 16 networking.useDHCP = false; 17 18 environment.systemPackages = [ 19 (pkgs.writeScriptBin "check-connection" '' 20 #!/usr/bin/env bash 21 22 set -e 23 24 if [[ "$2" == "" || "$1" == "--help" || "$1" == "-h" ]]; 25 then 26 echo "check-connection <target-address> <[expect-success|expect-failure]>" 27 exit 1 28 fi 29 30 ADDRESS="$1" 31 32 function test_icmp() { timeout 3 ping -c 1 "$ADDRESS"; } 33 34 if [[ "$2" == "expect-success" ]]; 35 then 36 test_icmp 37 else 38 ! test_icmp 39 fi 40 '') 41 ]; 42 }; 43in 44{ 45 name = "squid"; 46 meta.maintainers = with lib.maintainers; [ cobalt ]; 47 48 node.pkgsReadOnly = false; 49 50 nodes = { 51 client = 52 { ... }: 53 lib.mkMerge [ 54 commonConfig 55 { 56 virtualisation.vlans = [ 1 ]; 57 networking.firewall.enable = true; 58 59 # NOTE: the client doesn't need a HTTP server, this is here to allow a validation of the proxy acl 60 networking.firewall.allowedTCPPorts = [ 80 ]; 61 62 services.nginx = { 63 enable = true; 64 65 virtualHosts."server" = { 66 root = "/etc"; 67 locations."/".index = "hostname"; 68 listen = [ 69 { 70 addr = "0.0.0.0"; 71 port = 80; 72 } 73 ]; 74 }; 75 }; 76 } 77 ]; 78 79 proxy = 80 { config, nodes, ... }: 81 let 82 clientIp = (pkgs.lib.head nodes.client.networking.interfaces.eth1.ipv4.addresses).address; 83 serverIp = (pkgs.lib.head nodes.server.networking.interfaces.eth1.ipv4.addresses).address; 84 in 85 lib.mkMerge [ 86 commonConfig 87 { 88 nixpkgs.config.permittedInsecurePackages = [ "squid-7.0.1" ]; 89 90 virtualisation.vlans = [ 91 1 92 2 93 ]; 94 networking.firewall.enable = true; 95 networking.firewall.allowedTCPPorts = [ config.services.squid.proxyPort ]; 96 97 services.squid = { 98 enable = true; 99 100 extraConfig = '' 101 acl client src ${clientIp} 102 acl server dst ${serverIp} 103 http_access allow client server 104 http_access deny all 105 ''; 106 }; 107 } 108 ]; 109 110 server = 111 { ... }: 112 lib.mkMerge [ 113 commonConfig 114 { 115 virtualisation.vlans = [ 2 ]; 116 networking.firewall.enable = true; 117 networking.firewall.allowedTCPPorts = [ 80 ]; 118 119 services.nginx = { 120 enable = true; 121 122 virtualHosts."server" = { 123 root = "/etc"; 124 locations."/".index = "hostname"; 125 listen = [ 126 { 127 addr = "0.0.0.0"; 128 port = 80; 129 } 130 ]; 131 }; 132 }; 133 } 134 ]; 135 }; 136 137 testScript = 138 { nodes, ... }: 139 let 140 clientIp = (pkgs.lib.head nodes.client.networking.interfaces.eth1.ipv4.addresses).address; 141 serverIp = (pkgs.lib.head nodes.server.networking.interfaces.eth1.ipv4.addresses).address; 142 proxyExternalIp = (pkgs.lib.head nodes.proxy.networking.interfaces.eth2.ipv4.addresses).address; 143 proxyInternalIp = (pkgs.lib.head nodes.proxy.networking.interfaces.eth1.ipv4.addresses).address; 144 in 145 '' 146 client.start() 147 proxy.start() 148 server.start() 149 150 proxy.wait_for_unit("network.target") 151 proxy.wait_for_unit("squid.service") 152 client.wait_for_unit("network.target") 153 server.wait_for_unit("network.target") 154 server.wait_for_unit("nginx.service") 155 156 # Topology checks. 157 with subtest("proxy connectivity"): 158 ## The proxy should have direct access to the server and client 159 proxy.succeed("check-connection ${serverIp} expect-success") 160 proxy.succeed("check-connection ${clientIp} expect-success") 161 162 with subtest("server connectivity"): 163 ## The server should have direct access to the proxy 164 server.succeed("check-connection ${proxyExternalIp} expect-success") 165 ## ... and not have access to the client 166 server.succeed("check-connection ${clientIp} expect-failure") 167 168 with subtest("client connectivity"): 169 # The client should be also able to connect to the proxy 170 client.succeed("check-connection ${proxyInternalIp} expect-success") 171 # but not the client to the server 172 client.succeed("check-connection ${serverIp} expect-failure") 173 174 with subtest("HTTP"): 175 # the client cannot reach the server directly over HTTP 176 client.fail('[[ `timeout 3 curl --fail-with-body http://${serverIp}` ]]') 177 # ... but can with the proxy 178 client.succeed('[[ `timeout 3 curl --fail-with-body --proxy http://${proxyInternalIp}:3128 http://${serverIp}` == "server" ]]') 179 # and cannot from the server (with a 4xx error code) and ... 180 server.fail('[[ `timeout 3 curl --fail-with-body --proxy http://${proxyExternalIp}:3128 http://${clientIp}` == "client" ]]') 181 # .. not the client hostname 182 server.fail('[[ `timeout 3 curl --proxy http://${proxyExternalIp}:3128 http://${clientIp}` == "client" ]]') 183 # with an explicit deny message (no --fail because we want to parse the returned message) 184 server.succeed('[[ `timeout 3 curl --proxy http://${proxyExternalIp}:3128 http://${clientIp}` == *"ERR_ACCESS_DENIED"* ]]') 185 ''; 186}