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