nixpkgs/nixos/modules/services/networking/cjdns.nix
pennae 2e751c0772 treewide: automatically md-convert option descriptions
the conversion procedure is simple:

 - find all things that look like options, ie calls to either `mkOption`
   or `lib.mkOption` that take an attrset. remember the attrset as the
   option
 - for all options, find a `description` attribute who's value is not a
   call to `mdDoc` or `lib.mdDoc`
 - textually convert the entire value of the attribute to MD with a few
   simple regexes (the set from mdize-module.sh)
 - if the change produced a change in the manual output, discard
 - if the change kept the manual unchanged, add some text to the
   description to make sure we've actually found an option. if the
   manual changes this time, keep the converted description

this procedure converts 80% of nixos options to markdown. around 2000
options remain to be inspected, but most of those fail the "does not
change the manual output check": currently the MD conversion process
does not faithfully convert docbook tags like <code> and <package>, so
any option using such tags will not be converted at all.
2022-07-30 15:16:34 +02:00

304 lines
9.2 KiB
Nix

{ config, lib, pkgs, ... }:
with lib;
let
pkg = pkgs.cjdns;
cfg = config.services.cjdns;
connectToSubmodule =
{ ... }:
{ options =
{ password = mkOption {
type = types.str;
description = lib.mdDoc "Authorized password to the opposite end of the tunnel.";
};
login = mkOption {
default = "";
type = types.str;
description = lib.mdDoc "(optional) name your peer has for you";
};
peerName = mkOption {
default = "";
type = types.str;
description = lib.mdDoc "(optional) human-readable name for peer";
};
publicKey = mkOption {
type = types.str;
description = lib.mdDoc "Public key at the opposite end of the tunnel.";
};
hostname = mkOption {
default = "";
example = "foobar.hype";
type = types.str;
description = lib.mdDoc "Optional hostname to add to /etc/hosts; prevents reverse lookup failures.";
};
};
};
# Additional /etc/hosts entries for peers with an associated hostname
cjdnsExtraHosts = pkgs.runCommand "cjdns-hosts" {} ''
exec >$out
${concatStringsSep "\n" (mapAttrsToList (k: v:
optionalString (v.hostname != "")
"echo $(${pkgs.cjdns}/bin/publictoip6 ${v.publicKey}) ${v.hostname}")
(cfg.ETHInterface.connectTo // cfg.UDPInterface.connectTo))}
'';
parseModules = x:
x // { connectTo = mapAttrs (name: value: { inherit (value) password publicKey; }) x.connectTo; };
cjdrouteConf = builtins.toJSON ( recursiveUpdate {
admin = {
bind = cfg.admin.bind;
password = "@CJDNS_ADMIN_PASSWORD@";
};
authorizedPasswords = map (p: { password = p; }) cfg.authorizedPasswords;
interfaces = {
ETHInterface = if (cfg.ETHInterface.bind != "") then [ (parseModules cfg.ETHInterface) ] else [ ];
UDPInterface = if (cfg.UDPInterface.bind != "") then [ (parseModules cfg.UDPInterface) ] else [ ];
};
privateKey = "@CJDNS_PRIVATE_KEY@";
resetAfterInactivitySeconds = 100;
router = {
interface = { type = "TUNInterface"; };
ipTunnel = {
allowedConnections = [];
outgoingConnections = [];
};
};
security = [ { exemptAngel = 1; setuser = "nobody"; } ];
} cfg.extraConfig);
in
{
options = {
services.cjdns = {
enable = mkOption {
type = types.bool;
default = false;
description = lib.mdDoc ''
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.
'';
};
extraConfig = mkOption {
type = types.attrs;
default = {};
example = { router.interface.tunDevice = "tun10"; };
description = lib.mdDoc ''
Extra configuration, given as attrs, that will be merged recursively
with the rest of the JSON generated by this module, at the root node.
'';
};
confFile = mkOption {
type = types.nullOr types.path;
default = null;
example = "/etc/cjdroute.conf";
description = lib.mdDoc ''
Ignore all other cjdns options and load configuration from this file.
'';
};
authorizedPasswords = mkOption {
type = types.listOf types.str;
default = [ ];
example = [
"snyrfgkqsc98qh1y4s5hbu0j57xw5s0"
"z9md3t4p45mfrjzdjurxn4wuj0d8swv"
"49275fut6tmzu354pq70sr5b95qq0vj"
];
description = lib.mdDoc ''
Any remote cjdns nodes that offer these passwords on
connection will be allowed to route through this node.
'';
};
admin = {
bind = mkOption {
type = types.str;
default = "127.0.0.1:11234";
description = lib.mdDoc ''
Bind the administration port to this address and port.
'';
};
};
UDPInterface = {
bind = mkOption {
type = types.str;
default = "";
example = "192.168.1.32:43211";
description = lib.mdDoc ''
Address and port to bind UDP tunnels to.
'';
};
connectTo = mkOption {
type = types.attrsOf ( types.submodule ( connectToSubmodule ) );
default = { };
example = literalExpression ''
{
"192.168.1.1:27313" = {
hostname = "homer.hype";
password = "5kG15EfpdcKNX3f2GSQ0H1HC7yIfxoCoImnO5FHM";
publicKey = "371zpkgs8ss387tmr81q04mp0hg1skb51hw34vk1cq644mjqhup0.k";
};
}
'';
description = lib.mdDoc ''
Credentials for making UDP tunnels.
'';
};
};
ETHInterface = {
bind = mkOption {
type = types.str;
default = "";
example = "eth0";
description =
lib.mdDoc ''
Bind to this device for native ethernet operation.
`all` is a pseudo-name which will try to connect to all devices.
'';
};
beacon = mkOption {
type = types.int;
default = 2;
description = lib.mdDoc ''
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 ( connectToSubmodule ) );
default = { };
example = literalExpression ''
{
"01:02:03:04:05:06" = {
hostname = "homer.hype";
password = "5kG15EfpdcKNX3f2GSQ0H1HC7yIfxoCoImnO5FHM";
publicKey = "371zpkgs8ss387tmr81q04mp0hg1skb51hw34vk1cq644mjqhup0.k";
};
}
'';
description = lib.mdDoc ''
Credentials for connecting look similar to UDP credientials
except they begin with the mac address.
'';
};
};
addExtraHosts = mkOption {
type = types.bool;
default = false;
description = lib.mdDoc ''
Whether to add cjdns peers with an associated hostname to
{file}`/etc/hosts`. Beware that enabling this
incurs heavy eval-time costs.
'';
};
};
};
config = mkIf cfg.enable {
boot.kernelModules = [ "tun" ];
# networking.firewall.allowedUDPPorts = ...
systemd.services.cjdns = {
description = "cjdns: routing engine designed for security, scalability, speed and ease of use";
wantedBy = [ "multi-user.target" "sleep.target"];
after = [ "network-online.target" ];
bindsTo = [ "network-online.target" ];
preStart = if cfg.confFile != null then "" else ''
[ -e /etc/cjdns.keys ] && source /etc/cjdns.keys
if [ -z "$CJDNS_PRIVATE_KEY" ]; then
shopt -s lastpipe
${pkg}/bin/makekeys | { read private ipv6 public; }
umask 0077
echo "CJDNS_PRIVATE_KEY=$private" >> /etc/cjdns.keys
echo -e "CJDNS_IPV6=$ipv6\nCJDNS_PUBLIC_KEY=$public" > /etc/cjdns.public
chmod 600 /etc/cjdns.keys
chmod 444 /etc/cjdns.public
fi
if [ -z "$CJDNS_ADMIN_PASSWORD" ]; then
echo "CJDNS_ADMIN_PASSWORD=$(tr -dc A-Za-z0-9 </dev/urandom | head -c 32)" \
>> /etc/cjdns.keys
fi
'';
script = (
if cfg.confFile != null then "${pkg}/bin/cjdroute < ${cfg.confFile}" else
''
source /etc/cjdns.keys
(cat <<'EOF'
${cjdrouteConf}
EOF
) | sed \
-e "s/@CJDNS_ADMIN_PASSWORD@/$CJDNS_ADMIN_PASSWORD/g" \
-e "s/@CJDNS_PRIVATE_KEY@/$CJDNS_PRIVATE_KEY/g" \
| ${pkg}/bin/cjdroute
''
);
startLimitIntervalSec = 0;
serviceConfig = {
Type = "forking";
Restart = "always";
RestartSec = 1;
CapabilityBoundingSet = "CAP_NET_ADMIN CAP_NET_RAW CAP_SETUID";
ProtectSystem = true;
# Doesn't work on i686, causing service to fail
MemoryDenyWriteExecute = !pkgs.stdenv.isi686;
ProtectHome = true;
PrivateTmp = true;
};
};
networking.hostFiles = mkIf cfg.addExtraHosts [ cjdnsExtraHosts ];
assertions = [
{ assertion = ( cfg.ETHInterface.bind != "" || cfg.UDPInterface.bind != "" || cfg.confFile != null );
message = "Neither cjdns.ETHInterface.bind nor cjdns.UDPInterface.bind defined.";
}
{ assertion = config.networking.enableIPv6;
message = "networking.enableIPv6 must be enabled for CJDNS to work";
}
];
};
}