at master 4.1 kB view raw
1# This test sets up a host-to-host IPsec VPN between Alice and Bob, each on its 2# own network and with Eve as the only route between each other. We check that 3# Eve can eavesdrop the plaintext traffic between Alice and Bob, but once they 4# enable the secure tunnel Eve's spying becomes ineffective. 5 6{ lib, pkgs, ... }: 7 8let 9 10 # IPsec tunnel between Alice and Bob 11 tunnelConfig = { 12 services.libreswan.enable = true; 13 services.libreswan.connections.tunnel = '' 14 leftid=@alice 15 left=fd::a 16 rightid=@bob 17 right=fd::b 18 authby=secret 19 auto=add 20 ''; 21 environment.etc."ipsec.d/tunnel.secrets" = { 22 text = ''@alice @bob : PSK "j1JbIi9WY07rxwcNQ6nbyThKCf9DGxWOyokXIQcAQUnafsNTUJxfsxwk9WYK8fHj"''; 23 mode = "600"; 24 }; 25 }; 26 27 # Common network setup 28 baseNetwork = { 29 # shared hosts file 30 extraHosts = lib.mkVMOverride '' 31 fd::a alice 32 fd::b bob 33 fd::e eve 34 ''; 35 # remove all automatic addresses 36 useDHCP = false; 37 interfaces.eth1.ipv4.addresses = lib.mkVMOverride [ ]; 38 interfaces.eth2.ipv4.addresses = lib.mkVMOverride [ ]; 39 interfaces.eth1.ipv6.addresses = lib.mkVMOverride [ ]; 40 interfaces.eth2.ipv6.addresses = lib.mkVMOverride [ ]; 41 # open a port for testing 42 firewall.allowedUDPPorts = [ 1234 ]; 43 }; 44 45 # Adds an address and route from a to b via Eve 46 addRoute = a: b: { 47 interfaces.eth1.ipv6.addresses = [ 48 { 49 address = a; 50 prefixLength = 64; 51 } 52 ]; 53 interfaces.eth1.ipv6.routes = [ 54 { 55 address = b; 56 prefixLength = 128; 57 via = "fd::e"; 58 } 59 ]; 60 }; 61 62in 63 64{ 65 name = "libreswan"; 66 meta = with lib.maintainers; { 67 maintainers = [ rnhmjoj ]; 68 }; 69 70 # Our protagonist 71 nodes.alice = 72 { ... }: 73 { 74 virtualisation.vlans = [ 1 ]; 75 networking = baseNetwork // addRoute "fd::a" "fd::b"; 76 } 77 // tunnelConfig; 78 79 # Her best friend 80 nodes.bob = 81 { ... }: 82 { 83 virtualisation.vlans = [ 2 ]; 84 networking = baseNetwork // addRoute "fd::b" "fd::a"; 85 } 86 // tunnelConfig; 87 88 # The malicious network operator 89 nodes.eve = 90 { ... }: 91 { 92 virtualisation.vlans = [ 93 1 94 2 95 ]; 96 networking = lib.mkMerge [ 97 baseNetwork 98 { 99 interfaces.br0.ipv6.addresses = [ 100 { 101 address = "fd::e"; 102 prefixLength = 64; 103 } 104 ]; 105 bridges.br0.interfaces = [ 106 "eth1" 107 "eth2" 108 ]; 109 } 110 ]; 111 environment.systemPackages = [ pkgs.tcpdump ]; 112 boot.kernel.sysctl."net.ipv6.conf.all.forwarding" = true; 113 }; 114 115 testScript = '' 116 def alice_to_bob(msg: str): 117 """ 118 Sends a message as Alice to Bob 119 """ 120 bob.execute("nc -lu ::0 1234 >/tmp/msg &") 121 alice.sleep(1) 122 alice.succeed(f"echo '{msg}' | nc -uw 0 bob 1234") 123 bob.succeed(f"grep '{msg}' /tmp/msg") 124 125 126 def eavesdrop(): 127 """ 128 Starts eavesdropping on Alice and Bob 129 """ 130 match = "src host alice and dst host bob" 131 eve.execute(f"tcpdump -i br0 -c 1 -Avv {match} >/tmp/log &") 132 133 134 start_all() 135 136 with subtest("Network is up"): 137 alice.wait_until_succeeds("ping -c1 bob") 138 alice.succeed("systemctl restart ipsec") 139 bob.succeed("systemctl restart ipsec") 140 141 with subtest("Eve can eavesdrop cleartext traffic"): 142 eavesdrop() 143 alice_to_bob("I secretly love turnip") 144 eve.sleep(1) 145 eve.succeed("grep turnip /tmp/log") 146 147 with subtest("Libreswan is ready"): 148 alice.wait_for_unit("ipsec") 149 bob.wait_for_unit("ipsec") 150 alice.succeed("ipsec checkconfig") 151 152 with subtest("Alice and Bob can start the tunnel"): 153 alice.execute("ipsec start tunnel >&2 &") 154 bob.succeed("ipsec start tunnel") 155 # apparently this is needed to "wake" the tunnel 156 bob.execute("ping -c1 alice") 157 158 with subtest("Eve no longer can eavesdrop"): 159 eavesdrop() 160 alice_to_bob("Just kidding, I actually like rhubarb") 161 eve.sleep(1) 162 eve.fail("grep rhubarb /tmp/log") 163 ''; 164}