summary refs log tree commit diff
path: root/nixos
diff options
context:
space:
mode:
authorEmery Hemingway <emery@vfemail.net>2014-08-31 15:42:38 -0400
committerEmery Hemingway <emery@vfemail.net>2014-08-31 18:14:16 -0400
commitf60ac82cac0c4c94da76d294114955c8185752ff (patch)
treee26b8e701f165d585f55070f1fea05c7e411be5e /nixos
parentd9b13c1eb1b6834461c43f378c0b9ba9318c0225 (diff)
downloadnixpkgs-f60ac82cac0c4c94da76d294114955c8185752ff.tar
nixpkgs-f60ac82cac0c4c94da76d294114955c8185752ff.tar.gz
nixpkgs-f60ac82cac0c4c94da76d294114955c8185752ff.tar.bz2
nixpkgs-f60ac82cac0c4c94da76d294114955c8185752ff.tar.lz
nixpkgs-f60ac82cac0c4c94da76d294114955c8185752ff.tar.xz
nixpkgs-f60ac82cac0c4c94da76d294114955c8185752ff.tar.zst
nixpkgs-f60ac82cac0c4c94da76d294114955c8185752ff.zip
cjdns: new declarative service expression
systemd service wants network-interfaces.target rather than network.target
assertion on config.networking.enableIPv6
Diffstat (limited to 'nixos')
-rw-r--r--nixos/modules/services/networking/cjdns.nix316
1 files changed, 167 insertions, 149 deletions
diff --git a/nixos/modules/services/networking/cjdns.nix b/nixos/modules/services/networking/cjdns.nix
index 9306ffd5a18..0519172db91 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.
-
-  cjdrouteConf = '' {
-    "admin": {"bind": "${cfg.admin.bind}", "password": "\${CJDNS_ADMIN}" },
-    "privateKey": "\${CJDNS_KEY}",
-
-    "interfaces": {
-    ''
-
-    + 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)
-      + '' } ]''
-
-    + (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" } ]
-    }
-    '';   
-
-    cjdrouteConfFile = pkgs.writeText "cjdroute.conf" cjdrouteConf
-    */
+  # 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 [ ];
+    };
+
+    privateKey = "@CJDNS_PRIVATE_KEY@";
+
+    resetAfterInactivitySeconds = 100;
+
+    router = {
+      interface = { type = "TUNInterface"; };
+      ipTunnel = {
+        allowedConnections = [];
+        outgoingConnections = [];
+      };
+    };
+
+    security = [ { exemptAngel = 1; setuser = "nobody"; } ];
+
+  });
+
 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 <literal>confFile</literal>.
+          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";
-	  description = ''
-	    File containing a password to the administration port.
+      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.
 	  '';
 	};
       };
 
-      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"}
+      ETHInterface = {
+        bind = mkOption {
+	  default = "";
+	  example = "eth0";
+	  description = ''
+	    Bind to this device for native ethernet operation.
 	  '';
-	};
-
-      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