From 3a4631445572beacbe6965015c6a8e5c32c34c9a Mon Sep 17 00:00:00 2001 From: rnhmjoj Date: Sat, 15 May 2021 23:22:03 +0200 Subject: [PATCH] nixos/tests/libreswan: add test --- nixos/tests/all-tests.nix | 1 + nixos/tests/libreswan.nix | 134 ++++++++++++++++++++ pkgs/tools/networking/libreswan/default.nix | 3 + 3 files changed, 138 insertions(+) create mode 100644 nixos/tests/libreswan.nix diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix index c34bf3623b61..8b7a36d843e7 100644 --- a/nixos/tests/all-tests.nix +++ b/nixos/tests/all-tests.nix @@ -214,6 +214,7 @@ in latestKernel.login = handleTest ./login.nix { latestKernel = true; }; leaps = handleTest ./leaps.nix {}; lidarr = handleTest ./lidarr.nix {}; + libreswan = handleTest ./libreswan.nix {}; lightdm = handleTest ./lightdm.nix {}; limesurvey = handleTest ./limesurvey.nix {}; locate = handleTest ./locate.nix {}; diff --git a/nixos/tests/libreswan.nix b/nixos/tests/libreswan.nix new file mode 100644 index 000000000000..17ae60af8eed --- /dev/null +++ b/nixos/tests/libreswan.nix @@ -0,0 +1,134 @@ +# This test sets up a host-to-host IPsec VPN between Alice and Bob, each on its +# own network and with Eve as the only route between each other. We check that +# Eve can eavesdrop the plaintext traffic between Alice and Bob, but once they +# enable the secure tunnel Eve's spying becomes ineffective. + +import ./make-test-python.nix ({ lib, pkgs, ... }: + +let + + # IPsec tunnel between Alice and Bob + tunnelConfig = { + services.libreswan.enable = true; + services.libreswan.connections.tunnel = + '' + leftid=@alice + left=fd::a + rightid=@bob + right=fd::b + authby=secret + auto=add + ''; + environment.etc."ipsec.d/tunnel.secrets" = + { text = ''@alice @bob : PSK "j1JbIi9WY07rxwcNQ6nbyThKCf9DGxWOyokXIQcAQUnafsNTUJxfsxwk9WYK8fHj"''; + mode = "600"; + }; + }; + + # Common network setup + baseNetwork = { + # shared hosts file + extraHosts = lib.mkVMOverride '' + fd::a alice + fd::b bob + fd::e eve + ''; + # remove all automatic addresses + useDHCP = false; + interfaces.eth1.ipv4.addresses = lib.mkVMOverride []; + interfaces.eth2.ipv4.addresses = lib.mkVMOverride []; + # open a port for testing + firewall.allowedUDPPorts = [ 1234 ]; + }; + + # Adds an address and route from a to b via Eve + addRoute = a: b: { + interfaces.eth1.ipv6.addresses = + [ { address = a; prefixLength = 64; } ]; + interfaces.eth1.ipv6.routes = + [ { address = b; prefixLength = 128; via = "fd::e"; } ]; + }; + +in + +{ + name = "libreswan"; + meta = with lib.maintainers; { + maintainers = [ rnhmjoj ]; + }; + + # Our protagonist + nodes.alice = { ... }: { + virtualisation.vlans = [ 1 ]; + networking = baseNetwork // addRoute "fd::a" "fd::b"; + } // tunnelConfig; + + # Her best friend + nodes.bob = { ... }: { + virtualisation.vlans = [ 2 ]; + networking = baseNetwork // addRoute "fd::b" "fd::a"; + } // tunnelConfig; + + # The malicious network operator + nodes.eve = { ... }: { + virtualisation.vlans = [ 1 2 ]; + networking = lib.mkMerge + [ baseNetwork + { interfaces.br0.ipv6.addresses = + [ { address = "fd::e"; prefixLength = 64; } ]; + bridges.br0.interfaces = [ "eth1" "eth2" ]; + } + ]; + environment.systemPackages = [ pkgs.tcpdump ]; + boot.kernel.sysctl."net.ipv6.conf.all.forwarding" = true; + }; + + testScript = + '' + def alice_to_bob(msg: str): + """ + Sends a message as Alice to Bob + """ + bob.execute("nc -lu ::0 1234 >/tmp/msg &") + alice.sleep(1) + alice.succeed(f"echo '{msg}' | nc -uw 0 bob 1234") + bob.succeed(f"grep '{msg}' /tmp/msg") + + + def eavesdrop(): + """ + Starts eavesdropping on Alice and Bob + """ + match = "src host alice and dst host bob" + eve.execute(f"tcpdump -i br0 -c 1 -Avv {match} >/tmp/log &") + + + start_all() + + with subtest("Network is up"): + alice.wait_until_succeeds("ping -c1 bob") + + with subtest("Eve can eavesdrop cleartext traffic"): + eavesdrop() + alice_to_bob("I secretly love turnip") + eve.sleep(1) + eve.succeed("grep turnip /tmp/log") + + with subtest("Libreswan is ready"): + alice.wait_for_unit("ipsec") + bob.wait_for_unit("ipsec") + alice.succeed("ipsec verify 1>&2") + + with subtest("Alice and Bob can start the tunnel"): + alice.execute("ipsec auto --start tunnel &") + bob.succeed("ipsec auto --start tunnel") + # apparently this is needed to "wake" the tunnel + bob.execute("ping -c1 alice") + + with subtest("Eve no longer can eavesdrop"): + eavesdrop() + alice_to_bob("Just kidding, I actually like rhubarb") + eve.sleep(1) + eve.fail("grep rhubarb /tmp/log") + ''; +}) diff --git a/pkgs/tools/networking/libreswan/default.nix b/pkgs/tools/networking/libreswan/default.nix index 4132da1966d2..cb27d6270fa7 100644 --- a/pkgs/tools/networking/libreswan/default.nix +++ b/pkgs/tools/networking/libreswan/default.nix @@ -1,6 +1,7 @@ { lib , stdenv , fetchurl +, nixosTests , pkg-config , systemd , gmp @@ -127,6 +128,8 @@ stdenv.mkDerivation rec { -i $out/bin/ipsec ''; + passthru.tests.libreswan = nixosTests.libreswan; + meta = with lib; { homepage = "https://libreswan.org"; description = "A free software implementation of the VPN protocol based on IPSec and the Internet Key Exchange";