vuls: init at 0.27.0
[NixPkgs.git] / nixos / modules / services / networking / cjdns.nix
blob6aaaf629506894db4078057958a7a8fc2eb199dc
1 { config, lib, pkgs, ... }:
2 let
4   pkg = pkgs.cjdns;
6   cfg = config.services.cjdns;
8   connectToSubmodule =
9   { ... }:
10   { options =
11     { password = lib.mkOption {
12         type = lib.types.str;
13         description = "Authorized password to the opposite end of the tunnel.";
14       };
15       login = lib.mkOption {
16         default = "";
17         type = lib.types.str;
18         description = "(optional) name your peer has for you";
19       };
20       peerName = lib.mkOption {
21         default = "";
22         type = lib.types.str;
23         description = "(optional) human-readable name for peer";
24       };
25       publicKey = lib.mkOption {
26         type = lib.types.str;
27         description = "Public key at the opposite end of the tunnel.";
28       };
29       hostname = lib.mkOption {
30         default = "";
31         example = "foobar.hype";
32         type = lib.types.str;
33         description = "Optional hostname to add to /etc/hosts; prevents reverse lookup failures.";
34       };
35     };
36   };
38   # Additional /etc/hosts entries for peers with an associated hostname
39   cjdnsExtraHosts = pkgs.runCommand "cjdns-hosts" {} ''
40     exec >$out
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))}
45   '';
47   parseModules = x:
48     x // { connectTo = lib.mapAttrs (name: value: { inherit (value) password publicKey; }) x.connectTo; };
50   cjdrouteConf = builtins.toJSON ( lib.recursiveUpdate {
51     admin = {
52       bind = cfg.admin.bind;
53       password = "@CJDNS_ADMIN_PASSWORD@";
54     };
55     authorizedPasswords = map (p: { password = p; }) cfg.authorizedPasswords;
56     interfaces = {
57       ETHInterface = if (cfg.ETHInterface.bind != "") then [ (parseModules cfg.ETHInterface) ] else [ ];
58       UDPInterface = if (cfg.UDPInterface.bind != "") then [ (parseModules cfg.UDPInterface) ] else [ ];
59     };
61     privateKey = "@CJDNS_PRIVATE_KEY@";
63     resetAfterInactivitySeconds = 100;
65     router = {
66       interface = { type = "TUNInterface"; };
67       ipTunnel = {
68         allowedConnections = [];
69         outgoingConnections = [];
70       };
71     };
73     security = [ { exemptAngel = 1; setuser = "nobody"; } ];
75   } cfg.extraConfig);
80   options = {
82     services.cjdns = {
84       enable = lib.mkOption {
85         type = lib.types.bool;
86         default = false;
87         description = ''
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.
92         '';
93       };
95       extraConfig = lib.mkOption {
96         type = lib.types.attrs;
97         default = {};
98         example = { router.interface.tunDevice = "tun10"; };
99         description = ''
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.
102         '';
103       };
105       confFile = lib.mkOption {
106         type = lib.types.nullOr lib.types.path;
107         default = null;
108         example = "/etc/cjdroute.conf";
109         description = ''
110           Ignore all other cjdns options and load configuration from this file.
111         '';
112       };
114       authorizedPasswords = lib.mkOption {
115         type = lib.types.listOf lib.types.str;
116         default = [ ];
117         example = [
118           "snyrfgkqsc98qh1y4s5hbu0j57xw5s0"
119           "z9md3t4p45mfrjzdjurxn4wuj0d8swv"
120           "49275fut6tmzu354pq70sr5b95qq0vj"
121         ];
122         description = ''
123           Any remote cjdns nodes that offer these passwords on
124           connection will be allowed to route through this node.
125         '';
126       };
128       admin = {
129         bind = lib.mkOption {
130           type = lib.types.str;
131           default = "127.0.0.1:11234";
132           description = ''
133             Bind the administration port to this address and port.
134           '';
135         };
136       };
138       UDPInterface = {
139         bind = lib.mkOption {
140           type = lib.types.str;
141           default = "";
142           example = "192.168.1.32:43211";
143           description = ''
144             Address and port to bind UDP tunnels to.
145           '';
146          };
147         connectTo = lib.mkOption {
148           type = lib.types.attrsOf ( lib.types.submodule ( connectToSubmodule ) );
149           default = { };
150           example = lib.literalExpression ''
151             {
152               "192.168.1.1:27313" = {
153                 hostname = "homer.hype";
154                 password = "5kG15EfpdcKNX3f2GSQ0H1HC7yIfxoCoImnO5FHM";
155                 publicKey = "371zpkgs8ss387tmr81q04mp0hg1skb51hw34vk1cq644mjqhup0.k";
156               };
157             }
158           '';
159           description = ''
160             Credentials for making UDP tunnels.
161           '';
162         };
163       };
165       ETHInterface = {
166         bind = lib.mkOption {
167           type = lib.types.str;
168           default = "";
169           example = "eth0";
170           description = ''
171               Bind to this device for native ethernet operation.
172               `all` is a pseudo-name which will try to connect to all devices.
173             '';
174         };
176         beacon = lib.mkOption {
177           type = lib.types.int;
178           default = 2;
179           description = ''
180             Auto-connect to other cjdns nodes on the same network.
181             Options:
182               0: Disabled.
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
189                  automatically.
190           '';
191         };
193         connectTo = lib.mkOption {
194           type = lib.types.attrsOf ( lib.types.submodule ( connectToSubmodule ) );
195           default = { };
196           example = lib.literalExpression ''
197             {
198               "01:02:03:04:05:06" = {
199                 hostname = "homer.hype";
200                 password = "5kG15EfpdcKNX3f2GSQ0H1HC7yIfxoCoImnO5FHM";
201                 publicKey = "371zpkgs8ss387tmr81q04mp0hg1skb51hw34vk1cq644mjqhup0.k";
202               };
203             }
204           '';
205           description = ''
206             Credentials for connecting look similar to UDP credientials
207             except they begin with the mac address.
208           '';
209         };
210       };
212       addExtraHosts = lib.mkOption {
213         type = lib.types.bool;
214         default = false;
215         description = ''
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.
219         '';
220       };
222     };
224   };
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
242             shopt -s lastpipe
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
247         fi
249         if [ -z "$CJDNS_ADMIN_PASSWORD" ]; then
250             echo "CJDNS_ADMIN_PASSWORD=$(tr -dc A-Za-z0-9 </dev/urandom | head -c 32)" \
251                 >> /etc/cjdns.keys
252         fi
253       '';
255       script = (
256         if cfg.confFile != null then "${pkg}/bin/cjdroute < ${cfg.confFile}" else
257           ''
258             source /etc/cjdns.keys
259             (cat <<'EOF'
260             ${cjdrouteConf}
261             EOF
262             ) | sed \
263                 -e "s/@CJDNS_ADMIN_PASSWORD@/$CJDNS_ADMIN_PASSWORD/g" \
264                 -e "s/@CJDNS_PRIVATE_KEY@/$CJDNS_PRIVATE_KEY/g" \
265                 | ${pkg}/bin/cjdroute
266          ''
267       );
269       startLimitIntervalSec = 0;
270       serviceConfig = {
271         Type = "forking";
272         Restart = "always";
273         RestartSec = 1;
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;
278         ProtectHome = true;
279         PrivateTmp = true;
280       };
281     };
283     networking.hostFiles = lib.mkIf cfg.addExtraHosts [ cjdnsExtraHosts ];
285     assertions = [
286       { assertion = ( cfg.ETHInterface.bind != "" || cfg.UDPInterface.bind != "" || cfg.confFile != null );
287         message = "Neither cjdns.ETHInterface.bind nor cjdns.UDPInterface.bind defined.";
288       }
289       { assertion = config.networking.enableIPv6;
290         message = "networking.enableIPv6 must be enabled for CJDNS to work";
291       }
292     ];
294   };