1 { config, lib, pkgs, ... }:
6 cfg = config.services.cjdns;
11 { password = lib.mkOption {
13 description = "Authorized password to the opposite end of the tunnel.";
15 login = lib.mkOption {
18 description = "(optional) name your peer has for you";
20 peerName = lib.mkOption {
23 description = "(optional) human-readable name for peer";
25 publicKey = lib.mkOption {
27 description = "Public key at the opposite end of the tunnel.";
29 hostname = lib.mkOption {
31 example = "foobar.hype";
33 description = "Optional hostname to add to /etc/hosts; prevents reverse lookup failures.";
38 # Additional /etc/hosts entries for peers with an associated hostname
39 cjdnsExtraHosts = pkgs.runCommand "cjdns-hosts" {} ''
41 ${lib.concatStringsSep "\n" (lib.mapAttrsToList (k: v:
42 lib.optionalString (v.hostname != "")
43 "echo $(${pkgs.cjdns}/bin/publictoip6 ${v.publicKey}) ${v.hostname}")
44 (cfg.ETHInterface.connectTo // cfg.UDPInterface.connectTo))}
48 x // { connectTo = lib.mapAttrs (name: value: { inherit (value) password publicKey; }) x.connectTo; };
50 cjdrouteConf = builtins.toJSON ( lib.recursiveUpdate {
52 bind = cfg.admin.bind;
53 password = "@CJDNS_ADMIN_PASSWORD@";
55 authorizedPasswords = map (p: { password = p; }) cfg.authorizedPasswords;
57 ETHInterface = if (cfg.ETHInterface.bind != "") then [ (parseModules cfg.ETHInterface) ] else [ ];
58 UDPInterface = if (cfg.UDPInterface.bind != "") then [ (parseModules cfg.UDPInterface) ] else [ ];
61 privateKey = "@CJDNS_PRIVATE_KEY@";
63 resetAfterInactivitySeconds = 100;
66 interface = { type = "TUNInterface"; };
68 allowedConnections = [];
69 outgoingConnections = [];
73 security = [ { exemptAngel = 1; setuser = "nobody"; } ];
84 enable = lib.mkOption {
85 type = lib.types.bool;
88 Whether to enable the cjdns network encryption
89 and routing engine. A file at /etc/cjdns.keys will
90 be created if it does not exist to contain a random
91 secret key that your IPv6 address will be derived from.
95 extraConfig = lib.mkOption {
96 type = lib.types.attrs;
98 example = { router.interface.tunDevice = "tun10"; };
100 Extra configuration, given as attrs, that will be merged recursively
101 with the rest of the JSON generated by this module, at the root node.
105 confFile = lib.mkOption {
106 type = lib.types.nullOr lib.types.path;
108 example = "/etc/cjdroute.conf";
110 Ignore all other cjdns options and load configuration from this file.
114 authorizedPasswords = lib.mkOption {
115 type = lib.types.listOf lib.types.str;
118 "snyrfgkqsc98qh1y4s5hbu0j57xw5s0"
119 "z9md3t4p45mfrjzdjurxn4wuj0d8swv"
120 "49275fut6tmzu354pq70sr5b95qq0vj"
123 Any remote cjdns nodes that offer these passwords on
124 connection will be allowed to route through this node.
129 bind = lib.mkOption {
130 type = lib.types.str;
131 default = "127.0.0.1:11234";
133 Bind the administration port to this address and port.
139 bind = lib.mkOption {
140 type = lib.types.str;
142 example = "192.168.1.32:43211";
144 Address and port to bind UDP tunnels to.
147 connectTo = lib.mkOption {
148 type = lib.types.attrsOf ( lib.types.submodule ( connectToSubmodule ) );
150 example = lib.literalExpression ''
152 "192.168.1.1:27313" = {
153 hostname = "homer.hype";
154 password = "5kG15EfpdcKNX3f2GSQ0H1HC7yIfxoCoImnO5FHM";
155 publicKey = "371zpkgs8ss387tmr81q04mp0hg1skb51hw34vk1cq644mjqhup0.k";
160 Credentials for making UDP tunnels.
166 bind = lib.mkOption {
167 type = lib.types.str;
171 Bind to this device for native ethernet operation.
172 `all` is a pseudo-name which will try to connect to all devices.
176 beacon = lib.mkOption {
177 type = lib.types.int;
180 Auto-connect to other cjdns nodes on the same network.
183 1: Accept beacons, this will cause cjdns to accept incoming
184 beacon messages and try connecting to the sender.
185 2: Accept and send beacons, this will cause cjdns to broadcast
186 messages on the local network which contain a randomly
187 generated per-session password, other nodes which have this
188 set to 1 or 2 will hear the beacon messages and connect
193 connectTo = lib.mkOption {
194 type = lib.types.attrsOf ( lib.types.submodule ( connectToSubmodule ) );
196 example = lib.literalExpression ''
198 "01:02:03:04:05:06" = {
199 hostname = "homer.hype";
200 password = "5kG15EfpdcKNX3f2GSQ0H1HC7yIfxoCoImnO5FHM";
201 publicKey = "371zpkgs8ss387tmr81q04mp0hg1skb51hw34vk1cq644mjqhup0.k";
206 Credentials for connecting look similar to UDP credientials
207 except they begin with the mac address.
212 addExtraHosts = lib.mkOption {
213 type = lib.types.bool;
216 Whether to add cjdns peers with an associated hostname to
217 {file}`/etc/hosts`. Beware that enabling this
218 incurs heavy eval-time costs.
226 config = lib.mkIf cfg.enable {
228 boot.kernelModules = [ "tun" ];
230 # networking.firewall.allowedUDPPorts = ...
232 systemd.services.cjdns = {
233 description = "cjdns: routing engine designed for security, scalability, speed and ease of use";
234 wantedBy = [ "multi-user.target" "sleep.target"];
235 after = [ "network-online.target" ];
236 bindsTo = [ "network-online.target" ];
238 preStart = lib.optionalString (cfg.confFile == null) ''
239 [ -e /etc/cjdns.keys ] && source /etc/cjdns.keys
241 if [ -z "$CJDNS_PRIVATE_KEY" ]; then
243 ${pkg}/bin/makekeys | { read private ipv6 public; }
245 install -m 600 <(echo "CJDNS_PRIVATE_KEY=$private") /etc/cjdns.keys
246 install -m 444 <(echo -e "CJDNS_IPV6=$ipv6\nCJDNS_PUBLIC_KEY=$public") /etc/cjdns.public
249 if [ -z "$CJDNS_ADMIN_PASSWORD" ]; then
250 echo "CJDNS_ADMIN_PASSWORD=$(tr -dc A-Za-z0-9 </dev/urandom | head -c 32)" \
256 if cfg.confFile != null then "${pkg}/bin/cjdroute < ${cfg.confFile}" else
258 source /etc/cjdns.keys
263 -e "s/@CJDNS_ADMIN_PASSWORD@/$CJDNS_ADMIN_PASSWORD/g" \
264 -e "s/@CJDNS_PRIVATE_KEY@/$CJDNS_PRIVATE_KEY/g" \
265 | ${pkg}/bin/cjdroute
269 startLimitIntervalSec = 0;
274 CapabilityBoundingSet = "CAP_NET_ADMIN CAP_NET_RAW CAP_SETUID";
275 ProtectSystem = true;
276 # Doesn't work on i686, causing service to fail
277 MemoryDenyWriteExecute = !pkgs.stdenv.hostPlatform.isi686;
283 networking.hostFiles = lib.mkIf cfg.addExtraHosts [ cjdnsExtraHosts ];
286 { assertion = ( cfg.ETHInterface.bind != "" || cfg.UDPInterface.bind != "" || cfg.confFile != null );
287 message = "Neither cjdns.ETHInterface.bind nor cjdns.UDPInterface.bind defined.";
289 { assertion = config.networking.enableIPv6;
290 message = "networking.enableIPv6 must be enabled for CJDNS to work";