From 67a2a58314274ec5865d49198700eefd2c1a22a9 Mon Sep 17 00:00:00 2001 From: Emery Hemingway Date: Sun, 26 Oct 2014 15:15:48 -0400 Subject: [PATCH] cjdns: service tweaks, new NixOS test --- nixos/modules/services/networking/cjdns.nix | 48 +++---- nixos/release.nix | 1 + nixos/tests/cjdns.nix | 123 ++++++++++++++++++ pkgs/tools/networking/cjdns/default.nix | 3 + .../networking/cjdns/makekeys-sigpipe.patch | 29 +++++ 5 files changed, 180 insertions(+), 24 deletions(-) create mode 100644 nixos/tests/cjdns.nix create mode 100644 pkgs/tools/networking/cjdns/makekeys-sigpipe.patch diff --git a/nixos/modules/services/networking/cjdns.nix b/nixos/modules/services/networking/cjdns.nix index 9888419309c1..be0acb27324a 100644 --- a/nixos/modules/services/networking/cjdns.nix +++ b/nixos/modules/services/networking/cjdns.nix @@ -204,8 +204,29 @@ in systemd.services.cjdns = { description = "encrypted networking for everybody"; - wantedBy = [ "multi-user.target" ]; - after = [ "network-interfaces.target" ]; + wantedBy = [ "network.target" ]; + after = [ "networkSetup.service" "network-interfaces.target" ]; + + preStart = if cfg.confFile != "" then "" else '' + [ -e /etc/cjdns.keys ] && source /etc/cjdns.keys + + if [ -z "$CJDNS_PRIVATE_KEY" ]; then + shopt -s lastpipe + ${pkg}/bin/makekeys | { read private ipv6 public; } + + umask 0077 + echo "CJDNS_PRIVATE_KEY=$private" >> /etc/cjdns.keys + echo -e "CJDNS_IPV6=$ipv6\nCJDNS_PUBLIC_KEY=$public" > /etc/cjdns.public + + chmod 600 /etc/cjdns.keys + chmod 444 /etc/cjdns.public + fi + + if [ -z "$CJDNS_ADMIN_PASSWORD" ]; then + echo "CJDNS_ADMIN_PASSWORD=$(${pkgs.coreutils}/bin/head -c 96 /dev/urandom | ${pkgs.coreutils}/bin/tr -dc A-Za-z0-9)" \ + >> /etc/cjdns.keys + fi + ''; script = ( if cfg.confFile != "" then "${pkg}/bin/cjdroute < ${cfg.confFile}" else @@ -224,27 +245,6 @@ in }; }; - system.activationScripts.cjdns = if (cfg.confFile == "") then "" else '' - cjdnsWriteKeys() { - private=$1 - ipv6=$2 - public=$3 - - echo "CJDNS_PRIVATE_KEY=$1" >> /etc/cjdns.keys - echo -e "CJDNS_IPV6=$2\nCJDNS_PUBLIC_KEY=$3" > /etc/cjdns.public - - chmod 600 /etc/cjdns.keys - chmod 444 /etc/cjdns.public - } - - grep -q "CJDNS_PRIVATE_KEY=" /etc/cjdns.keys || \ - cjdnsWriteKeys $(${pkg}/bin/makekeys) - - grep -q "CJDNS_ADMIN_PASSWORD=" /etc/cjdns.keys || \ - echo "CJDNS_ADMIN_PASSWORD=$(${pkgs.coreutils}/bin/head -c 96 /dev/urandom | ${pkgs.coreutils}/bin/tr -dc A-Za-z0-9)" \ - >> /etc/cjdns.keys - ''; - networking.extraHosts = "${cjdnsHosts}"; assertions = [ @@ -258,4 +258,4 @@ in }; -} \ No newline at end of file +} diff --git a/nixos/release.nix b/nixos/release.nix index cb79dd3a226b..5a89b38acc77 100644 --- a/nixos/release.nix +++ b/nixos/release.nix @@ -235,6 +235,7 @@ in rec { tests.avahi = callTest tests/avahi.nix {}; tests.bittorrent = callTest tests/bittorrent.nix {}; tests.blivet = callTest tests/blivet.nix {}; + tests.cjdns = callTest tests/cjdns.nix {}; tests.containers = callTest tests/containers.nix {}; tests.firefox = callTest tests/firefox.nix {}; tests.firewall = callTest tests/firewall.nix {}; diff --git a/nixos/tests/cjdns.nix b/nixos/tests/cjdns.nix new file mode 100644 index 000000000000..7bb3863c683f --- /dev/null +++ b/nixos/tests/cjdns.nix @@ -0,0 +1,123 @@ +let + carolKey = "2d2a338b46f8e4a8c462f0c385b481292a05f678e19a2b82755258cf0f0af7e2"; + carolPubKey = "n932l3pjvmhtxxcdrqq2qpw5zc58f01vvjx01h4dtd1bb0nnu2h0.k"; + carolPassword = "678287829ce4c67bc8b227e56d94422ee1b85fa11618157b2f591de6c6322b52"; + carolIp4 = "192.168.0.9"; + + basicConfig = + { config, pkgs, ... }: + { services.cjdns.enable = true; + + # Turning off DHCP isn't very realistic but makes + # the sequence of address assignment less stochastic. + networking.useDHCP = false; + + networking.interfaces.eth1.prefixLength = 24; + # CJDNS output is incompatible with the XML log. + systemd.services.cjdns.serviceConfig.StandardOutput = "null"; + #networking.firewall.enable = true; + networking.firewall.allowPing = true; + #networking.firewall.rejectPackets = true; + }; + +in + +import ./make-test.nix { + name = "cjdns"; + + nodes = rec + { # Alice finds peers over over ETHInterface. + alice = + { config, ... }: + { imports = [ basicConfig ]; + + services.cjdns.ETHInterface.bind = "eth1"; + + services.httpd.enable = true; + services.httpd.adminAddr = "foo@example.org"; + networking.firewall.allowedTCPPorts = [ 80 ]; + }; + + # Bob explicitly connects to Carol over UDPInterface. + bob = + { config, lib, nodes, ... }: + + let carolIp4 = lib.mkForce nodes.carol.config.networking.interfaces.eth1; in + + { imports = [ basicConfig ]; + + networking.interfaces.eth1.ipAddress = "192.168.0.2"; + + services.cjdns = + { UDPInterface = + { bind = "0.0.0.0:1024"; + connectTo."192.168.0.1:1024}" = + { hostname = "carol.hype"; + password = carolPassword; + publicKey = carolPubKey; + }; + }; + }; + }; + + # Carol listens on ETHInterface and UDPInterface, + # but knows neither Alice or Bob. + carol = + { config, lib, nodes, ... }: + let + carolIp4 = (lib.mkForce nodes.carol.config.networking.interfaces.eth1); + in + { imports = [ basicConfig ]; + + environment.etc."cjdns.keys".text = '' + CJDNS_PRIVATE_KEY=${carolKey} + CJDNS_ADMIN_PASSWORD=FOOBAR + ''; + + networking.interfaces.eth1.ipAddress = "192.168.0.1"; + + services.cjdns = + { authorizedPasswords = [ carolPassword ]; + ETHInterface.bind = "eth1"; + UDPInterface.bind = "192.168.0.1:1024"; + }; + networking.firewall.allowedUDPPorts = [ 1024 ]; + }; + + }; + + testScript = + '' + startAll; + + $alice->waitForUnit("cjdns.service"); + $bob->waitForUnit("cjdns.service"); + $carol->waitForUnit("cjdns.service"); + + sub cjdnsIp { + my ($machine) = @_; + my $ip = (split /[ \/]+/, $machine->succeed("ip -o -6 addr show dev tun0"))[3]; + $machine->log("has ip $ip"); + return $ip; + } + + my $aliceIp6 = cjdnsIp $alice; + my $bobIp6 = cjdnsIp $bob; + my $carolIp6 = cjdnsIp $carol; + + # ping a few times each to let the routing table establish itself + + $alice->succeed("ping6 -c 4 $carolIp6"); + $bob->succeed("ping6 -c 4 carol.hype"); + + $carol->succeed("ping6 -c 4 $aliceIp6"); + $carol->succeed("ping6 -c 4 $bobIp6"); + + $alice->succeed("ping6 -c 4 $bobIp6"); + $bob->succeed("ping6 -c 4 $aliceIp6"); + + $alice->waitForUnit("httpd.service"); + + $bob->succeed("curl --fail -g http://[$aliceIp6]"); + ''; +} diff --git a/pkgs/tools/networking/cjdns/default.nix b/pkgs/tools/networking/cjdns/default.nix index c32bc224bdde..cbaca948b2b2 100644 --- a/pkgs/tools/networking/cjdns/default.nix +++ b/pkgs/tools/networking/cjdns/default.nix @@ -14,6 +14,9 @@ stdenv.mkDerivation { sha256 = "11z8dk7byxh9pfv7mhfvnk465qln1g7z8c8f822623d59lwjpbs1"; }; + # Make the NixOS service work a little better. + patches = [ ./makekeys-sigpipe.patch ]; + buildInputs = [ which python27 nodejs ] ++ # for flock stdenv.lib.optional stdenv.isLinux [ utillinux ]; diff --git a/pkgs/tools/networking/cjdns/makekeys-sigpipe.patch b/pkgs/tools/networking/cjdns/makekeys-sigpipe.patch new file mode 100644 index 000000000000..2b21f56709d0 --- /dev/null +++ b/pkgs/tools/networking/cjdns/makekeys-sigpipe.patch @@ -0,0 +1,29 @@ +diff --git a/contrib/c/makekeys.c b/contrib/c/makekeys.c +index 29582f1..555cf85 100644 +--- a/contrib/c/makekeys.c ++++ b/contrib/c/makekeys.c +@@ -21,6 +21,7 @@ + + #include "crypto_scalarmult_curve25519.h" + ++#include + #include + + int main(int argc, char** argv) +@@ -35,6 +36,8 @@ int main(int argc, char** argv) + uint8_t hexPrivateKey[65]; + uint8_t printedIp[40]; + ++ signal(SIGPIPE,SIG_DFL); ++ + for (;;) { + Random_bytes(rand, privateKey, 32); + crypto_scalarmult_curve25519_base(publicKey, privateKey); +@@ -43,6 +46,7 @@ int main(int argc, char** argv) + Base32_encode(publicKeyBase32, 53, publicKey, 32); + AddrTools_printShortIp(printedIp, ip); + printf("%s %s %s.k\n", hexPrivateKey, printedIp, publicKeyBase32); ++ fflush(stdout); + } + } + return 0;