From 9dc6bdcc09a55dcecdec2a9a4090c99d8f04b150 Mon Sep 17 00:00:00 2001 From: Bob van der Linden Date: Sat, 15 Sep 2018 15:07:27 +0200 Subject: [PATCH 1/5] miniupnpd: wrap iptables scripts to use correct PATH (cherry picked from commit 20393278797514a9bb8d15a0ea52d57545079d4a) --- pkgs/tools/networking/miniupnpd/default.nix | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/pkgs/tools/networking/miniupnpd/default.nix b/pkgs/tools/networking/miniupnpd/default.nix index 07112d1497af22..e10a1cbe8f7065 100644 --- a/pkgs/tools/networking/miniupnpd/default.nix +++ b/pkgs/tools/networking/miniupnpd/default.nix @@ -1,5 +1,10 @@ -{ stdenv, fetchurl, iptables, libuuid, pkgconfig }: +{ stdenv, lib, fetchurl, iptables, libuuid, pkgconfig +, which, iproute, gnused, coreutils, gawk, makeWrapper +}: +let + scriptBinEnv = lib.makeBinPath [ which iproute iptables gnused coreutils gawk ]; +in stdenv.mkDerivation rec { name = "miniupnpd-2.1"; @@ -10,7 +15,7 @@ stdenv.mkDerivation rec { }; buildInputs = [ iptables libuuid ]; - nativeBuildInputs= [ pkgconfig ]; + nativeBuildInputs= [ pkgconfig makeWrapper ]; makefile = "Makefile.linux"; @@ -18,6 +23,13 @@ stdenv.mkDerivation rec { installFlags = [ "PREFIX=$(out)" "INSTALLPREFIX=$(out)" ]; + postFixup = '' + for script in $out/etc/miniupnpd/ip{,6}tables_{init,removeall}.sh + do + wrapProgram $script --set PATH '${scriptBinEnv}:$PATH' + done + ''; + meta = with stdenv.lib; { homepage = http://miniupnp.free.fr/; description = "A daemon that implements the UPnP Internet Gateway Device (IGD) specification"; From 4342bdd79d2407b5f740ee9759f29e9297f997b1 Mon Sep 17 00:00:00 2001 From: Bob van der Linden Date: Sat, 15 Sep 2018 15:08:18 +0200 Subject: [PATCH 2/5] nixos: miniupnpd: use iptables scripts (cherry picked from commit d3eff01076dad707e5cda1be2e3bd6dfab596005) --- .../modules/services/networking/miniupnpd.nix | 24 ++----------------- 1 file changed, 2 insertions(+), 22 deletions(-) diff --git a/nixos/modules/services/networking/miniupnpd.nix b/nixos/modules/services/networking/miniupnpd.nix index 19400edb68f96c..ab714a6ac75e2c 100644 --- a/nixos/modules/services/networking/miniupnpd.nix +++ b/nixos/modules/services/networking/miniupnpd.nix @@ -57,32 +57,12 @@ in }; config = mkIf cfg.enable { - # from miniupnpd/netfilter/iptables_init.sh networking.firewall.extraCommands = '' - iptables -t nat -N MINIUPNPD - iptables -t nat -A PREROUTING -i ${cfg.externalInterface} -j MINIUPNPD - iptables -t mangle -N MINIUPNPD - iptables -t mangle -A PREROUTING -i ${cfg.externalInterface} -j MINIUPNPD - iptables -t filter -N MINIUPNPD - iptables -t filter -A FORWARD -i ${cfg.externalInterface} ! -o ${cfg.externalInterface} -j MINIUPNPD - iptables -t nat -N MINIUPNPD-PCP-PEER - iptables -t nat -A POSTROUTING -o ${cfg.externalInterface} -j MINIUPNPD-PCP-PEER + ${pkgs.bash}/bin/bash -x ${pkgs.miniupnpd}/etc/miniupnpd/iptables_init.sh -i ${cfg.externalInterface} ''; - # from miniupnpd/netfilter/iptables_removeall.sh networking.firewall.extraStopCommands = '' - iptables -t nat -F MINIUPNPD - iptables -t nat -D PREROUTING -i ${cfg.externalInterface} -j MINIUPNPD - iptables -t nat -X MINIUPNPD - iptables -t mangle -F MINIUPNPD - iptables -t mangle -D PREROUTING -i ${cfg.externalInterface} -j MINIUPNPD - iptables -t mangle -X MINIUPNPD - iptables -t filter -F MINIUPNPD - iptables -t filter -D FORWARD -i ${cfg.externalInterface} ! -o ${cfg.externalInterface} -j MINIUPNPD - iptables -t filter -X MINIUPNPD - iptables -t nat -F MINIUPNPD-PCP-PEER - iptables -t nat -D POSTROUTING -o ${cfg.externalInterface} -j MINIUPNPD-PCP-PEER - iptables -t nat -X MINIUPNPD-PCP-PEER + ${pkgs.bash}/bin/bash -x ${pkgs.miniupnpd}/etc/miniupnpd/iptables_removeall.sh -i ${cfg.externalInterface} ''; systemd.services.miniupnpd = { From 3ab4c763825defbc257bf8b65b40518e351d3e1b Mon Sep 17 00:00:00 2001 From: Bob van der Linden Date: Sat, 15 Sep 2018 15:09:05 +0200 Subject: [PATCH 3/5] tests: upnp: init test for upnp using miniupnpd / miniupnpc (cherry picked from commit 32c63c69054c3128678b68d5c65edd17dac151e4) --- nixos/release.nix | 1 + nixos/tests/upnp.nix | 94 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 95 insertions(+) create mode 100644 nixos/tests/upnp.nix diff --git a/nixos/release.nix b/nixos/release.nix index 3e03eb1f66d4b1..a513056c5a035f 100644 --- a/nixos/release.nix +++ b/nixos/release.nix @@ -247,6 +247,7 @@ in rec { tests.acme = callTest tests/acme.nix {}; tests.avahi = callTest tests/avahi.nix {}; tests.beegfs = callTest tests/beegfs.nix {}; + tests.upnp = callTest tests/upnp.nix {}; tests.bittorrent = callTest tests/bittorrent.nix {}; tests.bind = callTest tests/bind.nix {}; #tests.blivet = callTest tests/blivet.nix {}; # broken since 2017-07024 diff --git a/nixos/tests/upnp.nix b/nixos/tests/upnp.nix new file mode 100644 index 00000000000000..3f2dd13fb560b6 --- /dev/null +++ b/nixos/tests/upnp.nix @@ -0,0 +1,94 @@ +# This tests whether UPnP port mappings can be created using Miniupnpd +# and Miniupnpc. +# It runs a Miniupnpd service on one machine, and verifies +# a client can indeed create a port mapping using Miniupnpc. If +# this succeeds an external client will try to connect to the port +# mapping. + +import ./make-test.nix ({ pkgs, ... }: + +let + internalRouterAddress = "192.168.3.1"; + internalClient1Address = "192.168.3.2"; + externalRouterAddress = "80.100.100.1"; + externalClient2Address = "80.100.100.2"; +in +{ + name = "upnp"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ bobvanderlinden ]; + }; + + nodes = + { + router = + { pkgs, nodes, ... }: + { virtualisation.vlans = [ 1 2 ]; + networking.nat.enable = true; + networking.nat.internalInterfaces = [ "eth2" ]; + networking.nat.externalInterface = "eth1"; + networking.firewall.enable = true; + networking.firewall.trustedInterfaces = [ "eth2" ]; + networking.interfaces.eth1.ipv4.addresses = [ + { address = externalRouterAddress; prefixLength = 24; } + ]; + networking.interfaces.eth2.ipv4.addresses = [ + { address = internalRouterAddress; prefixLength = 24; } + ]; + services.miniupnpd = { + enable = true; + externalInterface = "eth1"; + internalIPs = [ "eth2" ]; + appendConfig = '' + ext_ip=${externalRouterAddress} + ''; + }; + }; + + client1 = + { pkgs, nodes, ... }: + { environment.systemPackages = [ pkgs.miniupnpc pkgs.netcat ]; + virtualisation.vlans = [ 2 ]; + networking.defaultGateway = internalRouterAddress; + networking.interfaces.eth1.ipv4.addresses = [ + { address = internalClient1Address; prefixLength = 24; } + ]; + networking.firewall.enable = false; + + services.httpd.enable = true; + services.httpd.listen = [{ ip = "*"; port = 9000; }]; + services.httpd.adminAddr = "foo@example.org"; + services.httpd.documentRoot = "/tmp"; + }; + + client2 = + { pkgs, ... }: + { environment.systemPackages = [ pkgs.miniupnpc ]; + virtualisation.vlans = [ 1 ]; + networking.interfaces.eth1.ipv4.addresses = [ + { address = externalClient2Address; prefixLength = 24; } + ]; + networking.firewall.enable = false; + }; + }; + + testScript = + { nodes, ... }: + '' + startAll; + + # Wait for network and miniupnpd. + $router->waitForUnit("network-online.target"); + # $router->waitForUnit("nat"); + $router->waitForUnit("firewall.service"); + $router->waitForUnit("miniupnpd"); + + $client1->waitForUnit("network-online.target"); + + $client1->succeed("upnpc -a ${internalClient1Address} 9000 9000 TCP"); + + $client1->waitForUnit("httpd"); + $client2->waitUntilSucceeds("curl http://${externalRouterAddress}:9000/"); + ''; + +}) From b45321200546f6b5cda7a1c788ff31717e9f177e Mon Sep 17 00:00:00 2001 From: Bob van der Linden Date: Sat, 15 Sep 2018 15:15:32 +0200 Subject: [PATCH 4/5] tests: bittorrent: improve stability This attempts to improve stability of the test by using existing services for miniupnpd and transmission. It also uses explicit addresses for the network interfaces so that the external IP addresses are valid internet addresses (thus fixing validation problems from upnpc). Also disable eth0 from being used to transfer torrents over without that being the intention. (cherry picked from commit 276ffc5656175c1a08eb5ee34e13afb76a130912) --- nixos/tests/bittorrent.nix | 99 +++++++++++++++++++++++++------------- 1 file changed, 66 insertions(+), 33 deletions(-) diff --git a/nixos/tests/bittorrent.nix b/nixos/tests/bittorrent.nix index 609b1ff7a83a29..bb276a8ee06b91 100644 --- a/nixos/tests/bittorrent.nix +++ b/nixos/tests/bittorrent.nix @@ -13,13 +13,11 @@ let # Some random file to serve. file = pkgs.hello.src; - miniupnpdConf = nodes: pkgs.writeText "miniupnpd.conf" - '' - ext_ifname=eth1 - listening_ip=${(pkgs.lib.head nodes.router.config.networking.interfaces.eth2.ipv4.addresses).address}/24 - allow 1024-65535 192.168.2.0/24 1024-65535 - ''; - + internalRouterAddress = "192.168.3.1"; + internalClient1Address = "192.168.3.2"; + externalRouterAddress = "80.100.100.1"; + externalClient2Address = "80.100.100.2"; + externalTrackerAddress = "80.100.100.3"; in { @@ -31,39 +29,79 @@ in nodes = { tracker = { pkgs, ... }: - { environment.systemPackages = [ pkgs.transmission pkgs.opentracker ]; + { environment.systemPackages = [ pkgs.transmission ]; + + virtualisation.vlans = [ 1 ]; + networking.interfaces.eth1.ipv4.addresses = [ + { address = externalTrackerAddress; prefixLength = 24; } + ]; # We need Apache on the tracker to serve the torrents. services.httpd.enable = true; services.httpd.adminAddr = "foo@example.org"; services.httpd.documentRoot = "/tmp"; - networking.firewall.enable = false; # FIXME: figure out what ports we actually need + networking.firewall.enable = false; + + services.opentracker.enable = true; + + services.transmission.enable = true; + services.transmission.settings.dht-enabled = false; + services.transmission.settings.port-forwaring-enabled = false; }; router = - { pkgs, ... }: - { environment.systemPackages = [ pkgs.miniupnpd ]; - virtualisation.vlans = [ 1 2 ]; + { pkgs, nodes, ... }: + { virtualisation.vlans = [ 1 2 ]; networking.nat.enable = true; networking.nat.internalInterfaces = [ "eth2" ]; networking.nat.externalInterface = "eth1"; - networking.firewall.enable = false; + networking.firewall.enable = true; + networking.firewall.trustedInterfaces = [ "eth2" ]; + networking.interfaces.eth0.ipv4.addresses = []; + networking.interfaces.eth1.ipv4.addresses = [ + { address = externalRouterAddress; prefixLength = 24; } + ]; + networking.interfaces.eth2.ipv4.addresses = [ + { address = internalRouterAddress; prefixLength = 24; } + ]; + services.miniupnpd = { + enable = true; + externalInterface = "eth1"; + internalIPs = [ "eth2" ]; + appendConfig = '' + ext_ip=${externalRouterAddress} + ''; + }; }; client1 = { pkgs, nodes, ... }: - { environment.systemPackages = [ pkgs.transmission ]; + { environment.systemPackages = [ pkgs.transmission pkgs.miniupnpc ]; virtualisation.vlans = [ 2 ]; - networking.defaultGateway = - (pkgs.lib.head nodes.router.config.networking.interfaces.eth2.ipv4.addresses).address; + networking.interfaces.eth0.ipv4.addresses = []; + networking.interfaces.eth1.ipv4.addresses = [ + { address = internalClient1Address; prefixLength = 24; } + ]; + networking.defaultGateway = internalRouterAddress; networking.firewall.enable = false; + services.transmission.enable = true; + services.transmission.settings.dht-enabled = false; + services.transmission.settings.message-level = 3; }; client2 = { pkgs, ... }: { environment.systemPackages = [ pkgs.transmission ]; + virtualisation.vlans = [ 1 ]; + networking.interfaces.eth0.ipv4.addresses = []; + networking.interfaces.eth1.ipv4.addresses = [ + { address = externalClient2Address; prefixLength = 24; } + ]; networking.firewall.enable = false; + services.transmission.enable = true; + services.transmission.settings.dht-enabled = false; + services.transmission.settings.port-forwaring-enabled = false; }; }; @@ -72,43 +110,38 @@ in '' startAll; - # Enable NAT on the router and start miniupnpd. - $router->waitForUnit("nat"); - $router->succeed( - "iptables -w -t nat -N MINIUPNPD", - "iptables -w -t nat -A PREROUTING -i eth1 -j MINIUPNPD", - "echo 1 > /proc/sys/net/ipv4/ip_forward", - "miniupnpd -f ${miniupnpdConf nodes}" - ); + # Wait for network and miniupnpd. + $router->waitForUnit("network-online.target"); + $router->waitForUnit("miniupnpd"); # Create the torrent. $tracker->succeed("mkdir /tmp/data"); $tracker->succeed("cp ${file} /tmp/data/test.tar.bz2"); - $tracker->succeed("transmission-create /tmp/data/test.tar.bz2 -p -t http://${(pkgs.lib.head nodes.tracker.config.networking.interfaces.eth1.ipv4.addresses).address}:6969/announce -o /tmp/test.torrent"); + $tracker->succeed("transmission-create /tmp/data/test.tar.bz2 --private --tracker http://${externalTrackerAddress}:6969/announce --outfile /tmp/test.torrent"); $tracker->succeed("chmod 644 /tmp/test.torrent"); # Start the tracker. !!! use a less crappy tracker - $tracker->waitForUnit("network.target"); - $tracker->succeed("opentracker -p 6969 >&2 &"); + $tracker->waitForUnit("network-online.target"); + $tracker->waitForUnit("opentracker.service"); $tracker->waitForOpenPort(6969); # Start the initial seeder. - my $pid = $tracker->succeed("transmission-cli /tmp/test.torrent -M -w /tmp/data >&2 & echo \$!"); + $tracker->succeed("transmission-remote --add /tmp/test.torrent --no-portmap --no-dht --download-dir /tmp/data"); # Now we should be able to download from the client behind the NAT. $tracker->waitForUnit("httpd"); - $client1->waitForUnit("network.target"); - $client1->succeed("transmission-cli http://tracker/test.torrent -w /tmp >&2 &"); + $client1->waitForUnit("network-online.target"); + $client1->succeed("transmission-remote --add http://${externalTrackerAddress}/test.torrent --download-dir /tmp >&2 &"); $client1->waitForFile("/tmp/test.tar.bz2"); $client1->succeed("cmp /tmp/test.tar.bz2 ${file}"); # Bring down the initial seeder. - $tracker->succeed("kill -9 $pid"); + # $tracker->stopJob("transmission"); # Now download from the second client. This can only succeed if # the first client created a NAT hole in the router. - $client2->waitForUnit("network.target"); - $client2->succeed("transmission-cli http://tracker/test.torrent -M -w /tmp >&2 &"); + $client2->waitForUnit("network-online.target"); + $client2->succeed("transmission-remote --add http://${externalTrackerAddress}/test.torrent --no-portmap --no-dht --download-dir /tmp >&2 &"); $client2->waitForFile("/tmp/test.tar.bz2"); $client2->succeed("cmp /tmp/test.tar.bz2 ${file}"); ''; From fc8769dbfcd80babf0e60d22eba0f576ceb011b2 Mon Sep 17 00:00:00 2001 From: Bob van der Linden Date: Sat, 15 Sep 2018 15:15:50 +0200 Subject: [PATCH 5/5] tests: bittorrent: add bobvanderlinden as maintainer (cherry picked from commit 5fbc521bf9e5415f63ec6ae4e490cd9f0644466d) --- nixos/tests/bittorrent.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nixos/tests/bittorrent.nix b/nixos/tests/bittorrent.nix index bb276a8ee06b91..8977be9b859f39 100644 --- a/nixos/tests/bittorrent.nix +++ b/nixos/tests/bittorrent.nix @@ -23,7 +23,7 @@ in { name = "bittorrent"; meta = with pkgs.stdenv.lib.maintainers; { - maintainers = [ domenkozar eelco chaoflow rob wkennington ]; + maintainers = [ domenkozar eelco chaoflow rob wkennington bobvanderlinden ]; }; nodes =