at 23.11-pre 13 kB view raw
1import ./make-test-python.nix ({ pkgs, lib, ... }: let 2 3 # We'll need to be able to trade cert files between nodes via scp. 4 inherit (import ./ssh-keys.nix pkgs) 5 snakeOilPrivateKey snakeOilPublicKey; 6 7 makeNebulaNode = { config, ... }: name: extraConfig: lib.mkMerge [ 8 { 9 # Expose nebula for doing cert signing. 10 environment.systemPackages = [ pkgs.nebula ]; 11 users.users.root.openssh.authorizedKeys.keys = [ snakeOilPublicKey ]; 12 services.openssh.enable = true; 13 networking.interfaces.eth1.useDHCP = false; 14 15 services.nebula.networks.smoke = { 16 # Note that these paths won't exist when the machine is first booted. 17 ca = "/etc/nebula/ca.crt"; 18 cert = "/etc/nebula/${name}.crt"; 19 key = "/etc/nebula/${name}.key"; 20 listen = { host = "0.0.0.0"; port = 4242; }; 21 }; 22 } 23 extraConfig 24 ]; 25 26in 27{ 28 name = "nebula"; 29 30 nodes = { 31 32 lighthouse = { ... } @ args: 33 makeNebulaNode args "lighthouse" { 34 networking.interfaces.eth1.ipv4.addresses = lib.mkForce [{ 35 address = "192.168.1.1"; 36 prefixLength = 24; 37 }]; 38 39 services.nebula.networks.smoke = { 40 isLighthouse = true; 41 isRelay = true; 42 firewall = { 43 outbound = [ { port = "any"; proto = "any"; host = "any"; } ]; 44 inbound = [ { port = "any"; proto = "any"; host = "any"; } ]; 45 }; 46 }; 47 }; 48 49 allowAny = { ... } @ args: 50 makeNebulaNode args "allowAny" { 51 networking.interfaces.eth1.ipv4.addresses = lib.mkForce [{ 52 address = "192.168.1.2"; 53 prefixLength = 24; 54 }]; 55 56 services.nebula.networks.smoke = { 57 staticHostMap = { "10.0.100.1" = [ "192.168.1.1:4242" ]; }; 58 isLighthouse = false; 59 lighthouses = [ "10.0.100.1" ]; 60 relays = [ "10.0.100.1" ]; 61 firewall = { 62 outbound = [ { port = "any"; proto = "any"; host = "any"; } ]; 63 inbound = [ { port = "any"; proto = "any"; host = "any"; } ]; 64 }; 65 }; 66 }; 67 68 allowFromLighthouse = { ... } @ args: 69 makeNebulaNode args "allowFromLighthouse" { 70 networking.interfaces.eth1.ipv4.addresses = lib.mkForce [{ 71 address = "192.168.1.3"; 72 prefixLength = 24; 73 }]; 74 75 services.nebula.networks.smoke = { 76 staticHostMap = { "10.0.100.1" = [ "192.168.1.1:4242" ]; }; 77 isLighthouse = false; 78 lighthouses = [ "10.0.100.1" ]; 79 relays = [ "10.0.100.1" ]; 80 firewall = { 81 outbound = [ { port = "any"; proto = "any"; host = "any"; } ]; 82 inbound = [ { port = "any"; proto = "any"; host = "lighthouse"; } ]; 83 }; 84 }; 85 }; 86 87 allowToLighthouse = { ... } @ args: 88 makeNebulaNode args "allowToLighthouse" { 89 networking.interfaces.eth1.ipv4.addresses = lib.mkForce [{ 90 address = "192.168.1.4"; 91 prefixLength = 24; 92 }]; 93 94 services.nebula.networks.smoke = { 95 enable = true; 96 staticHostMap = { "10.0.100.1" = [ "192.168.1.1:4242" ]; }; 97 isLighthouse = false; 98 lighthouses = [ "10.0.100.1" ]; 99 relays = [ "10.0.100.1" ]; 100 firewall = { 101 outbound = [ { port = "any"; proto = "any"; host = "lighthouse"; } ]; 102 inbound = [ { port = "any"; proto = "any"; host = "any"; } ]; 103 }; 104 }; 105 }; 106 107 disabled = { ... } @ args: 108 makeNebulaNode args "disabled" { 109 networking.interfaces.eth1.ipv4.addresses = lib.mkForce [{ 110 address = "192.168.1.5"; 111 prefixLength = 24; 112 }]; 113 114 services.nebula.networks.smoke = { 115 enable = false; 116 staticHostMap = { "10.0.100.1" = [ "192.168.1.1:4242" ]; }; 117 isLighthouse = false; 118 lighthouses = [ "10.0.100.1" ]; 119 relays = [ "10.0.100.1" ]; 120 firewall = { 121 outbound = [ { port = "any"; proto = "any"; host = "lighthouse"; } ]; 122 inbound = [ { port = "any"; proto = "any"; host = "any"; } ]; 123 }; 124 }; 125 }; 126 127 }; 128 129 testScript = let 130 131 setUpPrivateKey = name: '' 132 ${name}.start() 133 ${name}.succeed( 134 "mkdir -p /root/.ssh", 135 "chown 700 /root/.ssh", 136 "cat '${snakeOilPrivateKey}' > /root/.ssh/id_snakeoil", 137 "chown 600 /root/.ssh/id_snakeoil", 138 "mkdir -p /root" 139 ) 140 ''; 141 142 # From what I can tell, StrictHostKeyChecking=no is necessary for ssh to work between machines. 143 sshOpts = "-oStrictHostKeyChecking=no -oUserKnownHostsFile=/dev/null -oIdentityFile=/root/.ssh/id_snakeoil"; 144 145 restartAndCheckNebula = name: ip: '' 146 ${name}.systemctl("restart nebula@smoke.service") 147 ${name}.succeed("ping -c5 ${ip}") 148 ''; 149 150 # Create a keypair on the client node, then use the public key to sign a cert on the lighthouse. 151 signKeysFor = name: ip: '' 152 lighthouse.wait_for_unit("sshd.service") 153 ${name}.wait_for_unit("sshd.service") 154 ${name}.succeed( 155 "mkdir -p /etc/nebula", 156 "nebula-cert keygen -out-key /etc/nebula/${name}.key -out-pub /etc/nebula/${name}.pub", 157 "scp ${sshOpts} /etc/nebula/${name}.pub root@192.168.1.1:/root/${name}.pub", 158 ) 159 lighthouse.succeed( 160 'nebula-cert sign -ca-crt /etc/nebula/ca.crt -ca-key /etc/nebula/ca.key -name "${name}" -groups "${name}" -ip "${ip}" -in-pub /root/${name}.pub -out-crt /root/${name}.crt' 161 ) 162 ${name}.succeed( 163 "scp ${sshOpts} root@192.168.1.1:/root/${name}.crt /etc/nebula/${name}.crt", 164 "scp ${sshOpts} root@192.168.1.1:/etc/nebula/ca.crt /etc/nebula/ca.crt", 165 '(id nebula-smoke >/dev/null && chown -R nebula-smoke:nebula-smoke /etc/nebula) || true' 166 ) 167 ''; 168 169 getPublicIp = node: '' 170 ${node}.succeed("ip --brief addr show eth1 | awk '{print $3}' | tail -n1 | cut -d/ -f1").strip() 171 ''; 172 173 # Never do this for anything security critical! (Thankfully it's just a test.) 174 # Restart Nebula right after the mutual block and/or restore so the state is fresh. 175 blockTrafficBetween = nodeA: nodeB: '' 176 node_a = ${getPublicIp nodeA} 177 node_b = ${getPublicIp nodeB} 178 ${nodeA}.succeed("iptables -I INPUT -s " + node_b + " -j DROP") 179 ${nodeB}.succeed("iptables -I INPUT -s " + node_a + " -j DROP") 180 ${nodeA}.systemctl("restart nebula@smoke.service") 181 ${nodeB}.systemctl("restart nebula@smoke.service") 182 ''; 183 allowTrafficBetween = nodeA: nodeB: '' 184 node_a = ${getPublicIp nodeA} 185 node_b = ${getPublicIp nodeB} 186 ${nodeA}.succeed("iptables -D INPUT -s " + node_b + " -j DROP") 187 ${nodeB}.succeed("iptables -D INPUT -s " + node_a + " -j DROP") 188 ${nodeA}.systemctl("restart nebula@smoke.service") 189 ${nodeB}.systemctl("restart nebula@smoke.service") 190 ''; 191 in '' 192 # Create the certificate and sign the lighthouse's keys. 193 ${setUpPrivateKey "lighthouse"} 194 lighthouse.succeed( 195 "mkdir -p /etc/nebula", 196 'nebula-cert ca -name "Smoke Test" -out-crt /etc/nebula/ca.crt -out-key /etc/nebula/ca.key', 197 'nebula-cert sign -ca-crt /etc/nebula/ca.crt -ca-key /etc/nebula/ca.key -name "lighthouse" -groups "lighthouse" -ip "10.0.100.1/24" -out-crt /etc/nebula/lighthouse.crt -out-key /etc/nebula/lighthouse.key', 198 'chown -R nebula-smoke:nebula-smoke /etc/nebula' 199 ) 200 201 # Reboot the lighthouse and verify that the nebula service comes up on boot. 202 # Since rebooting takes a while, we'll just restart the service on the other nodes. 203 lighthouse.shutdown() 204 lighthouse.start() 205 lighthouse.wait_for_unit("nebula@smoke.service") 206 lighthouse.succeed("ping -c5 10.0.100.1") 207 208 # Create keys for allowAny's nebula service and test that it comes up. 209 ${setUpPrivateKey "allowAny"} 210 ${signKeysFor "allowAny" "10.0.100.2/24"} 211 ${restartAndCheckNebula "allowAny" "10.0.100.2"} 212 213 # Create keys for allowFromLighthouse's nebula service and test that it comes up. 214 ${setUpPrivateKey "allowFromLighthouse"} 215 ${signKeysFor "allowFromLighthouse" "10.0.100.3/24"} 216 ${restartAndCheckNebula "allowFromLighthouse" "10.0.100.3"} 217 218 # Create keys for allowToLighthouse's nebula service and test that it comes up. 219 ${setUpPrivateKey "allowToLighthouse"} 220 ${signKeysFor "allowToLighthouse" "10.0.100.4/24"} 221 ${restartAndCheckNebula "allowToLighthouse" "10.0.100.4"} 222 223 # Create keys for disabled's nebula service and test that it does not come up. 224 ${setUpPrivateKey "disabled"} 225 ${signKeysFor "disabled" "10.0.100.5/24"} 226 disabled.fail("systemctl status nebula@smoke.service") 227 disabled.fail("ping -c5 10.0.100.5") 228 229 # The lighthouse can ping allowAny and allowFromLighthouse but not disabled 230 lighthouse.succeed("ping -c3 10.0.100.2") 231 lighthouse.succeed("ping -c3 10.0.100.3") 232 lighthouse.fail("ping -c3 10.0.100.5") 233 234 # allowAny can ping the lighthouse, but not allowFromLighthouse because of its inbound firewall 235 allowAny.succeed("ping -c3 10.0.100.1") 236 allowAny.fail("ping -c3 10.0.100.3") 237 238 # allowFromLighthouse can ping the lighthouse and allowAny 239 allowFromLighthouse.succeed("ping -c3 10.0.100.1") 240 allowFromLighthouse.succeed("ping -c3 10.0.100.2") 241 242 # block allowFromLighthouse <-> allowAny, and allowFromLighthouse -> allowAny should still work. 243 ${blockTrafficBetween "allowFromLighthouse" "allowAny"} 244 allowFromLighthouse.succeed("ping -c10 10.0.100.2") 245 ${allowTrafficBetween "allowFromLighthouse" "allowAny"} 246 allowFromLighthouse.succeed("ping -c10 10.0.100.2") 247 248 # allowToLighthouse can ping the lighthouse but not allowAny or allowFromLighthouse 249 allowToLighthouse.succeed("ping -c3 10.0.100.1") 250 allowToLighthouse.fail("ping -c3 10.0.100.2") 251 allowToLighthouse.fail("ping -c3 10.0.100.3") 252 253 # allowAny can ping allowFromLighthouse now that allowFromLighthouse pinged it first 254 allowAny.succeed("ping -c3 10.0.100.3") 255 256 # block allowAny <-> allowFromLighthouse, and allowAny -> allowFromLighthouse should still work. 257 ${blockTrafficBetween "allowAny" "allowFromLighthouse"} 258 allowFromLighthouse.succeed("ping -c10 10.0.100.2") 259 allowAny.succeed("ping -c10 10.0.100.3") 260 ${allowTrafficBetween "allowAny" "allowFromLighthouse"} 261 allowFromLighthouse.succeed("ping -c10 10.0.100.2") 262 allowAny.succeed("ping -c10 10.0.100.3") 263 264 # allowToLighthouse can ping allowAny if allowAny pings it first 265 allowAny.succeed("ping -c3 10.0.100.4") 266 allowToLighthouse.succeed("ping -c3 10.0.100.2") 267 268 # block allowToLighthouse <-> allowAny, and allowAny <-> allowToLighthouse should still work. 269 ${blockTrafficBetween "allowAny" "allowToLighthouse"} 270 allowAny.succeed("ping -c10 10.0.100.4") 271 allowToLighthouse.succeed("ping -c10 10.0.100.2") 272 ${allowTrafficBetween "allowAny" "allowToLighthouse"} 273 allowAny.succeed("ping -c10 10.0.100.4") 274 allowToLighthouse.succeed("ping -c10 10.0.100.2") 275 276 # block lighthouse <-> allowFromLighthouse and allowAny <-> allowFromLighthouse; allowFromLighthouse won't get to allowAny 277 ${blockTrafficBetween "allowFromLighthouse" "lighthouse"} 278 ${blockTrafficBetween "allowFromLighthouse" "allowAny"} 279 allowFromLighthouse.fail("ping -c3 10.0.100.2") 280 ${allowTrafficBetween "allowFromLighthouse" "lighthouse"} 281 ${allowTrafficBetween "allowFromLighthouse" "allowAny"} 282 allowFromLighthouse.succeed("ping -c3 10.0.100.2") 283 284 # block lighthouse <-> allowAny, allowAny <-> allowFromLighthouse, and allowAny <-> allowToLighthouse; it won't get to allowFromLighthouse or allowToLighthouse 285 ${blockTrafficBetween "allowAny" "lighthouse"} 286 ${blockTrafficBetween "allowAny" "allowFromLighthouse"} 287 ${blockTrafficBetween "allowAny" "allowToLighthouse"} 288 allowFromLighthouse.fail("ping -c3 10.0.100.2") 289 allowAny.fail("ping -c3 10.0.100.3") 290 allowAny.fail("ping -c3 10.0.100.4") 291 ${allowTrafficBetween "allowAny" "lighthouse"} 292 ${allowTrafficBetween "allowAny" "allowFromLighthouse"} 293 ${allowTrafficBetween "allowAny" "allowToLighthouse"} 294 allowFromLighthouse.succeed("ping -c3 10.0.100.2") 295 allowAny.succeed("ping -c3 10.0.100.3") 296 allowAny.succeed("ping -c3 10.0.100.4") 297 298 # block lighthouse <-> allowToLighthouse and allowToLighthouse <-> allowAny; it won't get to allowAny 299 ${blockTrafficBetween "allowToLighthouse" "lighthouse"} 300 ${blockTrafficBetween "allowToLighthouse" "allowAny"} 301 allowAny.fail("ping -c3 10.0.100.4") 302 allowToLighthouse.fail("ping -c3 10.0.100.2") 303 ${allowTrafficBetween "allowToLighthouse" "lighthouse"} 304 ${allowTrafficBetween "allowToLighthouse" "allowAny"} 305 allowAny.succeed("ping -c3 10.0.100.4") 306 allowToLighthouse.succeed("ping -c3 10.0.100.2") 307 ''; 308})