From 499e366d7b71997b860450cdd541724c64c35eea Mon Sep 17 00:00:00 2001 From: Minijackson Date: Sun, 6 Dec 2020 16:04:16 +0100 Subject: nixos/tinc: add settings and hostSettings for RFC42-style options --- nixos/modules/services/networking/tinc.nix | 237 +++++++++++++++++++++++++++-- 1 file changed, 224 insertions(+), 13 deletions(-) (limited to 'nixos/modules/services/networking/tinc.nix') diff --git a/nixos/modules/services/networking/tinc.nix b/nixos/modules/services/networking/tinc.nix index 725bd9bf940..5fc09fc56a0 100644 --- a/nixos/modules/services/networking/tinc.nix +++ b/nixos/modules/services/networking/tinc.nix @@ -1,13 +1,156 @@ { config, lib, pkgs, ... }: with lib; - let - cfg = config.services.tinc; -in + mkValueString = value: + if value == true then "yes" + else if value == false then "no" + else generators.mkValueStringDefault { } value; + + toTincConf = generators.toKeyValue { + listsAsDuplicateKeys = true; + mkKeyValue = generators.mkKeyValueDefault { inherit mkValueString; } "="; + }; + + tincConfType = with types; + let + valueType = oneOf [ bool str int ]; + in + attrsOf (either valueType (listOf valueType)); + + addressSubmodule = { + options = { + address = mkOption { + type = types.str; + description = "The external IP address or hostname where the host can be reached."; + }; + + port = mkOption { + type = types.nullOr types.port; + default = null; + description = '' + The port where the host can be reached. + + If no port is specified, the default Port is used. + ''; + }; + }; + }; + + subnetSubmodule = { + options = { + address = mkOption { + type = types.str; + description = '' + The subnet of this host. + + Subnets can either be single MAC, IPv4 or IPv6 addresses, in which case + a subnet consisting of only that single address is assumed, or they can + be a IPv4 or IPv6 network address with a prefix length. + + IPv4 subnets are notated like 192.168.1.0/24, IPv6 subnets are notated + like fec0:0:0:1::/64. MAC addresses are notated like 0:1a:2b:3c:4d:5e. + + Note that subnets like 192.168.1.1/24 are invalid. + ''; + }; + + prefixLength = mkOption { + type = with types; nullOr (addCheck int (n: n >= 0 && n <= 128)); + default = null; + description = '' + The prefix length of the subnet. + + If null, a subnet consisting of only that single address is assumed. + + This conforms to standard CIDR notation as described in RFC1519. + ''; + }; + + weight = mkOption { + type = types.ints.unsigned; + default = 10; + description = '' + Indicates the priority over identical Subnets owned by different nodes. + + Lower values indicate higher priority. Packets will be sent to the + node with the highest priority, unless that node is not reachable, in + which case the node with the next highest priority will be tried, and + so on. + ''; + }; + }; + }; + + hostSubmodule = { config, ... }: { + options = { + addresses = mkOption { + type = types.listOf (types.submodule addressSubmodule); + default = [ ]; + description = '' + The external address where the host can be reached. This will set this + host's option. + + This variable is only required if you want to connect to this host. + ''; + }; + + subnets = mkOption { + type = types.listOf (types.submodule subnetSubmodule); + default = [ ]; + description = '' + The subnets which this tinc daemon will serve. This will set this + host's option. + Tinc tries to look up which other daemon it should send a packet to by + searching the appropriate subnet. If the packet matches a subnet, it + will be sent to the daemon who has this subnet in his host + configuration file. + ''; + }; + + rsaPublicKey = mkOption { + type = types.str; + default = ""; + description = '' + Legacy RSA public key of the host in PEM format, including start and + end markers. + + This will be appended as-is in the host's configuration file. + + The ed25519 public key can be specified using the + option instead. + ''; + }; + + settings = mkOption { + default = { }; + type = types.submodule { freeformType = tincConfType; }; + description = '' + Configuration for this host. + + See + for supported values. + ''; + }; + }; + + config.settings = { + Address = mkDefault (map + (address: "${address.address} ${toString address.port}") + config.addresses); + + Subnet = mkDefault (map + (subnet: + if subnet.prefixLength == null then "${subnet.address}#${toString subnet.weight}" + else "${subnet.address}/${toString subnet.prefixLength}#${toString subnet.weight}") + config.subnets); + }; + }; + +in { ###### interface @@ -18,7 +161,7 @@ in networks = mkOption { default = { }; - type = with types; attrsOf (submodule { + type = with types; attrsOf (submodule ({ config, ... }: { options = { extraConfig = mkOption { @@ -26,6 +169,9 @@ in type = types.lines; description = '' Extra lines to add to the tinc service configuration file. + + Note that using the declarative + option is preferred. ''; }; @@ -69,6 +215,40 @@ in hosts = mkOption { default = { }; type = types.attrsOf types.lines; + description = '' + The name of the host in the network as well as the configuration for that host. + This name should only contain alphanumerics and underscores. + + Note that using the declarative + option is preferred. + ''; + }; + + hostSettings = mkOption { + default = { }; + example = literalExample '' + { + host1 = { + addresses = [ + { address = "192.168.1.42"; } + { address = "192.168.1.42"; port = 1655; } + ]; + subnets = [ { address = "10.0.0.42"; } ]; + rsaPublicKey = "..."; + settings = { + Ed25519PublicKey = "..."; + }; + }; + host2 = { + subnets = [ { address = "10.0.1.0"; prefixLength = 24; weight = 2; } ]; + rsaPublicKey = "..."; + settings = { + Compression = 10; + }; + }; + } + ''; + type = types.attrsOf (types.submodule hostSubmodule); description = '' The name of the host in the network as well as the configuration for that host. This name should only contain alphanumerics and underscores. @@ -79,7 +259,7 @@ in default = "tun"; type = types.enum [ "tun" "tap" ]; description = '' - The type of virtual interface used for the network connection + The type of virtual interface used for the network connection. ''; }; @@ -118,8 +298,44 @@ in Note that tinc can't run scripts anymore (such as tinc-down or host-up), unless it is setup to be runnable inside chroot environment. ''; }; + + settings = mkOption { + default = { }; + type = types.submodule { freeformType = tincConfType; }; + example = literalExample '' + { + Interface = "custom.interface"; + DirectOnly = true; + Mode = "switch"; + } + ''; + description = '' + Configuration of the Tinc daemon for this network. + + See + for supported values. + ''; + }; + }; + + config = { + hosts = mapAttrs + (hostname: host: '' + ${toTincConf host.settings} + ${host.rsaPublicKey} + '') + config.hostSettings; + + settings = { + DeviceType = mkDefault config.interfaceType; + Name = mkDefault (if config.name == null then "$HOST" else config.name); + Ed25519PrivateKeyFile = mkIf (config.ed25519PrivateKeyFile != null) (mkDefault config.ed25519PrivateKeyFile); + PrivateKeyFile = mkIf (config.rsaPrivateKeyFile != null) (mkDefault config.rsaPrivateKeyFile); + ListenAddress = mkIf (config.listenAddress != null) (mkDefault config.listenAddress); + BindToAddress = mkIf (config.bindToAddress != null) (mkDefault config.bindToAddress); + }; }; - }); + })); description = '' Defines the tinc networks which will be started. @@ -144,13 +360,7 @@ in "tinc/${network}/tinc.conf" = { mode = "0444"; text = '' - Name = ${if data.name == null then "$HOST" else data.name} - DeviceType = ${data.interfaceType} - ${optionalString (data.ed25519PrivateKeyFile != null) "Ed25519PrivateKeyFile = ${data.ed25519PrivateKeyFile}"} - ${optionalString (data.rsaPrivateKeyFile != null) "PrivateKeyFile = ${data.rsaPrivateKeyFile}"} - ${optionalString (data.listenAddress != null) "ListenAddress = ${data.listenAddress}"} - ${optionalString (data.bindToAddress != null) "BindToAddress = ${data.bindToAddress}"} - Interface = tinc.${network} + ${toTincConf ({ Interface = "tinc.${network}"; } // data.settings)} ${data.extraConfig} ''; }; @@ -221,4 +431,5 @@ in }; + meta.maintainers = with maintainers; [ minijackson ]; } -- cgit 1.4.1