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.
305 lines
9.2 KiB
Nix
305 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";
|
|
}
|
|
];
|
|
|
|
};
|
|
|
|
}
|