diff --git a/nixos/modules/services/networking/cjdns.nix b/nixos/modules/services/networking/cjdns.nix
index 9306ffd5a18a..0519172db914 100644
--- a/nixos/modules/services/networking/cjdns.nix
+++ b/nixos/modules/services/networking/cjdns.nix
@@ -1,13 +1,3 @@
-# You may notice the commented out sections in this file,
-# it would be great to configure cjdns from nix, but cjdns
-# reads its configuration from stdin, including the private
-# key and admin password, all nested in a JSON structure.
-#
-# Until a good method of storing the keys outside the nix
-# store and mixing them back into a string is devised
-# (without too much shell hackery), a skeleton of the
-# configuration building lies commented out.
-
{ config, lib, pkgs, ... }:
with lib;
@@ -16,41 +6,35 @@ let
cfg = config.services.cjdns;
- /*
- # can't keep keys and passwords in the nix store,
- # but don't want to deal with this stdin quagmire.
+ # would be nice to merge 'cfg' with a //,
+ # but the json nesting is wacky.
+ cjdrouteConf = builtins.toJSON ( {
+ admin = {
+ bind = cfg.admin.bind;
+ password = "@CJDNS_ADMIN_PASSWORD@";
+ };
+ authorizedPasswords = map (p: { password = p; }) cfg.authorizedPasswords;
+ interfaces = {
+ ETHInterface = if (cfg.ETHInterface.bind != "") then [ cfg.ETHInterface ] else [ ];
+ UDPInterface = if (cfg.UDPInterface.bind != "") then [ cfg.UDPInterface ] else [ ];
+ };
- cjdrouteConf = '' {
- "admin": {"bind": "${cfg.admin.bind}", "password": "\${CJDNS_ADMIN}" },
- "privateKey": "\${CJDNS_KEY}",
+ privateKey = "@CJDNS_PRIVATE_KEY@";
- "interfaces": {
- ''
+ resetAfterInactivitySeconds = 100;
- + optionalString (cfg.interfaces.udp.bind.address != null) ''
- "UDPInterface": [ {
- "bind": "${cfg.interfaces.udp.bind.address}:"''
- ${if cfg.interfaces.upd.bind.port != null
- then ${toString cfg.interfaces.udp.bind.port}
- else ${RANDOM}
- fi)
- + '' } ]''
+ router = {
+ interface = { type = "TUNInterface"; };
+ ipTunnel = {
+ allowedConnections = [];
+ outgoingConnections = [];
+ };
+ };
- + (if cfg.interfaces.eth.bind != null then ''
- "ETHInterface": [ {
- "bind": "${cfg.interfaces.eth.bind}",
- "beacon": ${toString cfg.interfaces.eth.beacon}
- } ]
- '' fi )
- + ''
- },
- "router": { "interface": { "type": "TUNInterface" }, },
- "security": [ { "setuser": "nobody" } ]
- }
- '';
+ security = [ { exemptAngel = 1; setuser = "nobody"; } ];
+
+ });
- cjdrouteConfFile = pkgs.writeText "cjdroute.conf" cjdrouteConf
- */
in
{
@@ -62,146 +46,180 @@ in
type = types.bool;
default = false;
description = ''
- Enable this option to start a instance of the
- cjdns network encryption and and routing engine.
- Configuration will be read from confFile.
+ Whether to enable the cjdns network encryption
+ and routing engine. A file at /etc/cjdns.keys will
+ be created if it does not exist to contain a random
+ secret key that your IPv6 address will be derived from.
'';
};
- confFile = mkOption {
- default = "/etc/cjdroute.conf";
- description = ''
- Configuration file to pipe to cjdroute.
+ authorizedPasswords = mkOption {
+ type = types.listOf types.str;
+ default = [ ];
+ example = [
+ "snyrfgkqsc98qh1y4s5hbu0j57xw5s0"
+ "z9md3t4p45mfrjzdjurxn4wuj0d8swv"
+ "49275fut6tmzu354pq70sr5b95qq0vj"
+ ];
+ description = ''
+ Any remote cjdns nodes that offer these passwords on
+ connection will be allowed to route through this node.
'';
};
-
- /*
+
admin = {
bind = mkOption {
+ type = types.string;
default = "127.0.0.1:11234";
description = ''
Bind the administration port to this address and port.
'';
};
+ };
- passwordFile = mkOption {
- example = "/root/cjdns.adminPassword";
+ UDPInterface = {
+ bind = mkOption {
+ type = types.string;
+ default = "";
+ example = "192.168.1.32:43211";
+ description = ''
+ Address and port to bind UDP tunnels to.
+ '';
+ };
+ connectTo = mkOption {
+ type = types.attrsOf ( types.submodule (
+ { options, ... }:
+ { options = {
+ # TODO make host an option, and add it to networking.extraHosts
+ password = mkOption {
+ type = types.str;
+ description = "Authorized password to the opposite end of the tunnel.";
+ };
+ publicKey = mkOption {
+ type = types.str;
+ description = "Public key at the opposite end of the tunnel.";
+ };
+ };
+ }
+ ));
+ default = { };
+ example = {
+ "192.168.1.1:27313" = {
+ password = "5kG15EfpdcKNX3f2GSQ0H1HC7yIfxoCoImnO5FHM";
+ publicKey = "371zpkgs8ss387tmr81q04mp0hg1skb51hw34vk1cq644mjqhup0.k";
+ };
+ };
+ description = ''
+ Credentials for making UDP tunnels.
+ '';
+ };
+ };
+
+ ETHInterface = {
+ bind = mkOption {
+ default = "";
+ example = "eth0";
description = ''
- File containing a password to the administration port.
+ Bind to this device for native ethernet operation.
'';
- };
- };
-
- keyFile = mkOption {
- type = types.str;
- example = "/root/cjdns.key";
- description = ''
- Path to a file containing a cjdns private key on a single line.
- '';
- };
-
- passwordsFile = mkOption {
- type = types.str;
- default = null;
- example = "/root/cjdns.authorizedPasswords";
- description = ''
- A file containing a list of json dictionaries with passwords.
- For example:
- {"password": "s8xf5z7znl4jt05g922n3wpk75wkypk"},
- { "name": "nice guy",
- "password": "xhthk1mglz8tpjrbbvdlhyc092rhpx5"},
- {"password": "3qfxyhmrht7uwzq29pmhbdm9w4bnc8w"}
- '';
- };
-
- interfaces = {
- udp = {
- bind = {
- address = mkOption {
- default = "0.0.0.0";
- description = ''
- Address to bind UDP tunnels to; disable by setting to null;
- '';
- };
- port = mkOption {
- type = types.int;
- default = null;
- description = ''
- Port to bind UDP tunnels to.
- A port will be choosen at random if this is not set.
- This option is required to act as the server end of
- a tunnel.
- '';
- };
- };
- };
-
- eth = {
- bind = mkOption {
- default = null;
- example = "eth0";
- description = ''
- Bind to this device and operate with native wire format.
- '';
- };
-
- beacon = mkOption {
- default = 2;
- description = ''
- Auto-connect to other cjdns nodes on the same network.
- Options:
- 0 -- Disabled.
-
- 1 -- Accept beacons, this will cause cjdns to accept incoming
- beacon messages and try connecting to the sender.
-
- 2 -- Accept and send beacons, this will cause cjdns to broadcast
- messages on the local network which contain a randomly
- generated per-session password, other nodes which have this
- set to 1 or 2 will hear the beacon messages and connect
- automatically.
- '';
- };
-
- connectTo = mkOption {
- type = types.listOf types.str;
- default = [];
- description = ''
- Credentials for connecting look similar to UDP credientials
- except they begin with the mac address, for example:
- "01:02:03:04:05:06":{"password":"a","publicKey":"b"}
- '';
- };
};
+
+ beacon = mkOption {
+ type = types.int;
+ default = 2;
+ description = ''
+ Auto-connect to other cjdns nodes on the same network.
+ Options:
+ 0: Disabled.
+ 1: Accept beacons, this will cause cjdns to accept incoming
+ beacon messages and try connecting to the sender.
+ 2: Accept and send beacons, this will cause cjdns to broadcast
+ messages on the local network which contain a randomly
+ generated per-session password, other nodes which have this
+ set to 1 or 2 will hear the beacon messages and connect
+ automatically.
+ '';
+ };
+
+ connectTo = mkOption {
+ type = types.attrsOf ( types.submodule (
+ { options, ... }:
+ { options = {
+ password = mkOption {
+ type = types.str;
+ description = "Authorized password to the opposite end of the tunnel.";
+ };
+ publicKey = mkOption {
+ type = types.str;
+ description = "Public key at the opposite end of the tunnel.";
+ };
+ };
+ }
+ ));
+ default = { };
+ example = {
+ "01:02:03:04:05:06" = {
+ password = "5kG15EfpdcKNX3f2GSQ0H1HC7yIfxoCoImnO5FHM";
+ publicKey = "371zpkgs8ss387tmr81q04mp0hg1skb51hw34vk1cq644mjqhup0.k";
+ };
+ };
+ description = ''
+ Credentials for connecting look similar to UDP credientials
+ except they begin with the mac address.
+ '';
+ };
};
- */
+
};
+
};
config = mkIf config.services.cjdns.enable {
boot.kernelModules = [ "tun" ];
- /*
- networking.firewall.allowedUDPPorts = mkIf (cfg.udp.bind.port != null) [
- cfg.udp.bind.port
- ];
- */
+ # networking.firewall.allowedUDPPorts = ...
systemd.services.cjdns = {
description = "encrypted networking for everybody";
wantedBy = [ "multi-user.target" ];
- wants = [ "network.target" ];
- before = [ "network.target" ];
- path = [ pkgs.cjdns ];
+ after = [ "network-interfaces.target" ];
+
+ script = ''
+ source /etc/cjdns.keys
+ echo '${cjdrouteConf}' | sed \
+ -e "s/@CJDNS_ADMIN_PASSWORD@/$CJDNS_ADMIN_PASSWORD/g" \
+ -e "s/@CJDNS_PRIVATE_KEY@/$CJDNS_PRIVATE_KEY/g" \
+ | ${pkgs.cjdns}/sbin/cjdroute
+ '';
serviceConfig = {
Type = "forking";
- ExecStart = ''
- ${pkgs.stdenv.shell} -c "${pkgs.cjdns}/sbin/cjdroute < ${cfg.confFile}"
- '';
Restart = "on-failure";
};
};
+
+ system.activationScripts.cjdns = ''
+ grep -q "CJDNS_PRIVATE_KEY=" /etc/cjdns.keys || \
+ echo "CJDNS_PRIVATE_KEY=$(${pkgs.cjdns}/sbin/makekey)" \
+ >> /etc/cjdns.keys
+
+ 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
+
+ chmod 600 /etc/cjdns.keys
+ '';
+
+ assertions = [
+ { assertion = ( cfg.ETHInterface.bind != "" || cfg.UDPInterface.bind != "" );
+ message = "Neither cjdns.ETHInterface.bind nor cjdns.UDPInterface.bind defined.";
+ }
+ { assertion = config.networking.enableIPv6;
+ message = "networking.enableIPv6 must be enabled for CJDNS to work";
+ }
+ ];
+
};
-}
+
+}
\ No newline at end of file
diff --git a/pkgs/tools/networking/cjdns/default.nix b/pkgs/tools/networking/cjdns/default.nix
index 48e21f4507e5..be107dfa81e7 100644
--- a/pkgs/tools/networking/cjdns/default.nix
+++ b/pkgs/tools/networking/cjdns/default.nix
@@ -1,21 +1,27 @@
{ stdenv, fetchgit, nodejs, which, python27 }:
let
- date = "20140303";
- rev = "f11ce1fd4795b0173ac0ef18c8a6f752aa824adb";
+ date = "20140829";
+ rev = "9595d67f9edd759054c5bd3aaee0968ff55e361a";
in
stdenv.mkDerivation {
name = "cjdns-${date}-${stdenv.lib.strings.substring 0 7 rev}";
src = fetchgit {
- url = "git://github.com/cjdelisle/cjdns.git";
+ url = "https://github.com/cjdelisle/cjdns.git";
inherit rev;
- sha256 = "1bxhf9f1v0slf9mz3ll6jf45mkwvwxlf3yqxx9k23kjyr1nsc8s8";
+ sha256 = "519c549c42ae26c5359ae13a4548c44b51e36db403964b4d9f78c19b749dfb83";
};
buildInputs = [ which python27 nodejs];
- builder = ./builder.sh;
+ patches = [ ./makekey.patch ];
+
+ buildPhase = "bash do";
+ installPhase = ''
+ mkdir -p $out/sbin
+ cp cjdroute makekey $out/sbin
+ '';
meta = {
homepage = https://github.com/cjdelisle/cjdns;
diff --git a/pkgs/tools/networking/cjdns/makekey.patch b/pkgs/tools/networking/cjdns/makekey.patch
new file mode 100644
index 000000000000..fcce5e3e728e
--- /dev/null
+++ b/pkgs/tools/networking/cjdns/makekey.patch
@@ -0,0 +1,64 @@
+diff --git a/contrib/c/makekey.c b/contrib/c/makekey.c
+new file mode 100644
+index 0000000..c7184e5
+--- /dev/null
++++ b/contrib/c/makekey.c
+@@ -0,0 +1,46 @@
++/* vim: set expandtab ts=4 sw=4: */
++/*
++ * You may redistribute this program and/or modify it under the terms of
++ * the GNU General Public License as published by the Free Software Foundation,
++ * either version 3 of the License, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program. If not, see .
++ */
++#include "crypto/random/Random.h"
++#include "memory/MallocAllocator.h"
++#include "crypto/AddressCalc.h"
++#include "util/AddrTools.h"
++#include "util/Hex.h"
++
++#include "crypto_scalarmult_curve25519.h"
++
++#include
++
++int main(int argc, char** argv)
++{
++ struct Allocator* alloc = MallocAllocator_new(1<<22);
++ struct Random* rand = Random_new(alloc, NULL, NULL);
++
++ uint8_t privateKey[32];
++ uint8_t publicKey[32];
++ uint8_t ip[16];
++ uint8_t hexPrivateKey[65];
++
++ for (;;) {
++ Random_bytes(rand, privateKey, 32);
++ crypto_scalarmult_curve25519_base(publicKey, privateKey);
++ if (AddressCalc_addressForPublicKey(ip, publicKey)) {
++ Hex_encode(hexPrivateKey, 65, privateKey, 32);
++ printf(hexPrivateKey);
++ return 0;
++ }
++ }
++ return 0;
++}
++
+diff --git a/node_build/make.js b/node_build/make.js
+index 5e51645..11465e3 100644
+--- a/node_build/make.js
++++ b/node_build/make.js
+@@ -339,6 +339,7 @@ Builder.configure({
+ builder.buildExecutable('contrib/c/privatetopublic.c');
+ builder.buildExecutable('contrib/c/sybilsim.c');
+ builder.buildExecutable('contrib/c/makekeys.c');
++ builder.buildExecutable('contrib/c/makekey.c');
+
+ builder.buildExecutable('crypto/random/randombytes.c');
+