vimPlugins.LuaSnip-snippets-nvim: init at 2022-03-17 (#377544)
[NixPkgs.git] / nixos / modules / services / networking / cjdns.nix
blob1b9f53c9b28a5990e3c07d550b85a7d61bf3050e
2   config,
3   lib,
4   pkgs,
5   ...
6 }:
7 let
9   pkg = pkgs.cjdns;
11   cfg = config.services.cjdns;
13   connectToSubmodule =
14     { ... }:
15     {
16       options = {
17         password = lib.mkOption {
18           type = lib.types.str;
19           description = "Authorized password to the opposite end of the tunnel.";
20         };
21         login = lib.mkOption {
22           default = "";
23           type = lib.types.str;
24           description = "(optional) name your peer has for you";
25         };
26         peerName = lib.mkOption {
27           default = "";
28           type = lib.types.str;
29           description = "(optional) human-readable name for peer";
30         };
31         publicKey = lib.mkOption {
32           type = lib.types.str;
33           description = "Public key at the opposite end of the tunnel.";
34         };
35         hostname = lib.mkOption {
36           default = "";
37           example = "foobar.hype";
38           type = lib.types.str;
39           description = "Optional hostname to add to /etc/hosts; prevents reverse lookup failures.";
40         };
41       };
42     };
44   # Additional /etc/hosts entries for peers with an associated hostname
45   cjdnsExtraHosts = pkgs.runCommand "cjdns-hosts" { } ''
46     exec >$out
47     ${lib.concatStringsSep "\n" (
48       lib.mapAttrsToList (
49         k: v:
50         lib.optionalString (
51           v.hostname != ""
52         ) "echo $(${pkgs.cjdns}/bin/publictoip6 ${v.publicKey}) ${v.hostname}"
53       ) (cfg.ETHInterface.connectTo // cfg.UDPInterface.connectTo)
54     )}
55   '';
57   parseModules =
58     x:
59     x
60     // {
61       connectTo = lib.mapAttrs (name: value: { inherit (value) password publicKey; }) x.connectTo;
62     };
64   cjdrouteConf = builtins.toJSON (
65     lib.recursiveUpdate {
66       admin = {
67         bind = cfg.admin.bind;
68         password = "@CJDNS_ADMIN_PASSWORD@";
69       };
70       authorizedPasswords = map (p: { password = p; }) cfg.authorizedPasswords;
71       interfaces = {
72         ETHInterface = if (cfg.ETHInterface.bind != "") then [ (parseModules cfg.ETHInterface) ] else [ ];
73         UDPInterface = if (cfg.UDPInterface.bind != "") then [ (parseModules cfg.UDPInterface) ] else [ ];
74       };
76       privateKey = "@CJDNS_PRIVATE_KEY@";
78       resetAfterInactivitySeconds = 100;
80       router = {
81         interface = {
82           type = "TUNInterface";
83         };
84         ipTunnel = {
85           allowedConnections = [ ];
86           outgoingConnections = [ ];
87         };
88       };
90       security = [
91         {
92           exemptAngel = 1;
93           setuser = "nobody";
94         }
95       ];
97     } cfg.extraConfig
98   );
103   options = {
105     services.cjdns = {
107       enable = lib.mkOption {
108         type = lib.types.bool;
109         default = false;
110         description = ''
111           Whether to enable the cjdns network encryption
112           and routing engine. A file at /etc/cjdns.keys will
113           be created if it does not exist to contain a random
114           secret key that your IPv6 address will be derived from.
115         '';
116       };
118       extraConfig = lib.mkOption {
119         type = lib.types.attrs;
120         default = { };
121         example = {
122           router.interface.tunDevice = "tun10";
123         };
124         description = ''
125           Extra configuration, given as attrs, that will be merged recursively
126           with the rest of the JSON generated by this module, at the root node.
127         '';
128       };
130       confFile = lib.mkOption {
131         type = lib.types.nullOr lib.types.path;
132         default = null;
133         example = "/etc/cjdroute.conf";
134         description = ''
135           Ignore all other cjdns options and load configuration from this file.
136         '';
137       };
139       authorizedPasswords = lib.mkOption {
140         type = lib.types.listOf lib.types.str;
141         default = [ ];
142         example = [
143           "snyrfgkqsc98qh1y4s5hbu0j57xw5s0"
144           "z9md3t4p45mfrjzdjurxn4wuj0d8swv"
145           "49275fut6tmzu354pq70sr5b95qq0vj"
146         ];
147         description = ''
148           Any remote cjdns nodes that offer these passwords on
149           connection will be allowed to route through this node.
150         '';
151       };
153       admin = {
154         bind = lib.mkOption {
155           type = lib.types.str;
156           default = "127.0.0.1:11234";
157           description = ''
158             Bind the administration port to this address and port.
159           '';
160         };
161       };
163       UDPInterface = {
164         bind = lib.mkOption {
165           type = lib.types.str;
166           default = "";
167           example = "192.168.1.32:43211";
168           description = ''
169             Address and port to bind UDP tunnels to.
170           '';
171         };
172         connectTo = lib.mkOption {
173           type = lib.types.attrsOf (lib.types.submodule (connectToSubmodule));
174           default = { };
175           example = lib.literalExpression ''
176             {
177               "192.168.1.1:27313" = {
178                 hostname = "homer.hype";
179                 password = "5kG15EfpdcKNX3f2GSQ0H1HC7yIfxoCoImnO5FHM";
180                 publicKey = "371zpkgs8ss387tmr81q04mp0hg1skb51hw34vk1cq644mjqhup0.k";
181               };
182             }
183           '';
184           description = ''
185             Credentials for making UDP tunnels.
186           '';
187         };
188       };
190       ETHInterface = {
191         bind = lib.mkOption {
192           type = lib.types.str;
193           default = "";
194           example = "eth0";
195           description = ''
196             Bind to this device for native ethernet operation.
197             `all` is a pseudo-name which will try to connect to all devices.
198           '';
199         };
201         beacon = lib.mkOption {
202           type = lib.types.int;
203           default = 2;
204           description = ''
205             Auto-connect to other cjdns nodes on the same network.
206             Options:
207               0: Disabled.
208               1: Accept beacons, this will cause cjdns to accept incoming
209                  beacon messages and try connecting to the sender.
210               2: Accept and send beacons, this will cause cjdns to broadcast
211                  messages on the local network which contain a randomly
212                  generated per-session password, other nodes which have this
213                  set to 1 or 2 will hear the beacon messages and connect
214                  automatically.
215           '';
216         };
218         connectTo = lib.mkOption {
219           type = lib.types.attrsOf (lib.types.submodule (connectToSubmodule));
220           default = { };
221           example = lib.literalExpression ''
222             {
223               "01:02:03:04:05:06" = {
224                 hostname = "homer.hype";
225                 password = "5kG15EfpdcKNX3f2GSQ0H1HC7yIfxoCoImnO5FHM";
226                 publicKey = "371zpkgs8ss387tmr81q04mp0hg1skb51hw34vk1cq644mjqhup0.k";
227               };
228             }
229           '';
230           description = ''
231             Credentials for connecting look similar to UDP credientials
232             except they begin with the mac address.
233           '';
234         };
235       };
237       addExtraHosts = lib.mkOption {
238         type = lib.types.bool;
239         default = false;
240         description = ''
241           Whether to add cjdns peers with an associated hostname to
242           {file}`/etc/hosts`.  Beware that enabling this
243           incurs heavy eval-time costs.
244         '';
245       };
247     };
249   };
251   config = lib.mkIf cfg.enable {
253     boot.kernelModules = [ "tun" ];
255     # networking.firewall.allowedUDPPorts = ...
257     systemd.services.cjdns = {
258       description = "cjdns: routing engine designed for security, scalability, speed and ease of use";
259       wantedBy = [
260         "multi-user.target"
261         "sleep.target"
262       ];
263       after = [ "network-online.target" ];
264       bindsTo = [ "network-online.target" ];
266       preStart = lib.optionalString (cfg.confFile == null) ''
267         [ -e /etc/cjdns.keys ] && source /etc/cjdns.keys
269         if [ -z "$CJDNS_PRIVATE_KEY" ]; then
270             shopt -s lastpipe
271             ${pkg}/bin/makekeys | { read private ipv6 public; }
273             install -m 600 <(echo "CJDNS_PRIVATE_KEY=$private") /etc/cjdns.keys
274             install -m 444 <(echo -e "CJDNS_IPV6=$ipv6\nCJDNS_PUBLIC_KEY=$public") /etc/cjdns.public
275         fi
277         if [ -z "$CJDNS_ADMIN_PASSWORD" ]; then
278             echo "CJDNS_ADMIN_PASSWORD=$(tr -dc A-Za-z0-9 </dev/urandom | head -c 32)" \
279                 >> /etc/cjdns.keys
280         fi
281       '';
283       script = (
284         if cfg.confFile != null then
285           "${pkg}/bin/cjdroute < ${cfg.confFile}"
286         else
287           ''
288             source /etc/cjdns.keys
289             (cat <<'EOF'
290             ${cjdrouteConf}
291             EOF
292             ) | sed \
293                 -e "s/@CJDNS_ADMIN_PASSWORD@/$CJDNS_ADMIN_PASSWORD/g" \
294                 -e "s/@CJDNS_PRIVATE_KEY@/$CJDNS_PRIVATE_KEY/g" \
295                 | ${pkg}/bin/cjdroute
296           ''
297       );
299       startLimitIntervalSec = 0;
300       serviceConfig = {
301         Type = "forking";
302         Restart = "always";
303         RestartSec = 1;
304         CapabilityBoundingSet = "CAP_NET_ADMIN CAP_NET_RAW CAP_SETUID";
305         ProtectSystem = true;
306         # Doesn't work on i686, causing service to fail
307         MemoryDenyWriteExecute = !pkgs.stdenv.hostPlatform.isi686;
308         ProtectHome = true;
309         PrivateTmp = true;
310       };
311     };
313     networking.hostFiles = lib.mkIf cfg.addExtraHosts [ cjdnsExtraHosts ];
315     assertions = [
316       {
317         assertion = (cfg.ETHInterface.bind != "" || cfg.UDPInterface.bind != "" || cfg.confFile != null);
318         message = "Neither cjdns.ETHInterface.bind nor cjdns.UDPInterface.bind defined.";
319       }
320       {
321         assertion = config.networking.enableIPv6;
322         message = "networking.enableIPv6 must be enabled for CJDNS to work";
323       }
324     ];
326   };