at 18.03-beta 7.9 kB view raw
1{ config, lib, pkgs, ... }: 2 3with lib; 4 5let 6 7 cfg = config.services.tinc; 8 9in 10 11{ 12 13 ###### interface 14 15 options = { 16 17 services.tinc = { 18 19 networks = mkOption { 20 default = { }; 21 type = with types; attrsOf (submodule { 22 options = { 23 24 extraConfig = mkOption { 25 default = ""; 26 type = types.lines; 27 description = '' 28 Extra lines to add to the tinc service configuration file. 29 ''; 30 }; 31 32 name = mkOption { 33 default = null; 34 type = types.nullOr types.str; 35 description = '' 36 The name of the node which is used as an identifier when communicating 37 with the remote nodes in the mesh. If null then the hostname of the system 38 is used to derive a name (note that tinc may replace non-alphanumeric characters in 39 hostnames by underscores). 40 ''; 41 }; 42 43 ed25519PrivateKeyFile = mkOption { 44 default = null; 45 type = types.nullOr types.path; 46 description = '' 47 Path of the private ed25519 keyfile. 48 ''; 49 }; 50 51 debugLevel = mkOption { 52 default = 0; 53 type = types.addCheck types.int (l: l >= 0 && l <= 5); 54 description = '' 55 The amount of debugging information to add to the log. 0 means little 56 logging while 5 is the most logging. <command>man tincd</command> for 57 more details. 58 ''; 59 }; 60 61 hosts = mkOption { 62 default = { }; 63 type = types.attrsOf types.lines; 64 description = '' 65 The name of the host in the network as well as the configuration for that host. 66 This name should only contain alphanumerics and underscores. 67 ''; 68 }; 69 70 interfaceType = mkOption { 71 default = "tun"; 72 type = types.enum [ "tun" "tap" ]; 73 description = '' 74 The type of virtual interface used for the network connection 75 ''; 76 }; 77 78 listenAddress = mkOption { 79 default = null; 80 type = types.nullOr types.str; 81 description = '' 82 The ip address to listen on for incoming connections. 83 ''; 84 }; 85 86 bindToAddress = mkOption { 87 default = null; 88 type = types.nullOr types.str; 89 description = '' 90 The ip address to bind to (both listen on and send packets from). 91 ''; 92 }; 93 94 package = mkOption { 95 type = types.package; 96 default = pkgs.tinc_pre; 97 defaultText = "pkgs.tinc_pre"; 98 description = '' 99 The package to use for the tinc daemon's binary. 100 ''; 101 }; 102 103 chroot = mkOption { 104 default = true; 105 type = types.bool; 106 description = '' 107 Change process root directory to the directory where the config file is located (/etc/tinc/netname/), for added security. 108 The chroot is performed after all the initialization is done, after writing pid files and opening network sockets. 109 110 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. 111 ''; 112 }; 113 }; 114 }); 115 116 description = '' 117 Defines the tinc networks which will be started. 118 Each network invokes a different daemon. 119 ''; 120 }; 121 }; 122 123 }; 124 125 126 ###### implementation 127 128 config = mkIf (cfg.networks != { }) { 129 130 environment.etc = fold (a: b: a // b) { } 131 (flip mapAttrsToList cfg.networks (network: data: 132 flip mapAttrs' data.hosts (host: text: nameValuePair 133 ("tinc/${network}/hosts/${host}") 134 ({ mode = "0644"; user = "tinc.${network}"; inherit text; }) 135 ) // { 136 "tinc/${network}/tinc.conf" = { 137 mode = "0444"; 138 text = '' 139 Name = ${if data.name == null then "$HOST" else data.name} 140 DeviceType = ${data.interfaceType} 141 ${optionalString (data.ed25519PrivateKeyFile != null) "Ed25519PrivateKeyFile = ${data.ed25519PrivateKeyFile}"} 142 ${optionalString (data.listenAddress != null) "ListenAddress = ${data.listenAddress}"} 143 ${optionalString (data.bindToAddress != null) "BindToAddress = ${data.bindToAddress}"} 144 Interface = tinc.${network} 145 ${data.extraConfig} 146 ''; 147 }; 148 } 149 )); 150 151 networking.interfaces = flip mapAttrs' cfg.networks (network: data: nameValuePair 152 ("tinc.${network}") 153 ({ 154 virtual = true; 155 virtualType = "${data.interfaceType}"; 156 }) 157 ); 158 159 systemd.services = flip mapAttrs' cfg.networks (network: data: nameValuePair 160 ("tinc.${network}") 161 ({ 162 description = "Tinc Daemon - ${network}"; 163 wantedBy = [ "multi-user.target" ]; 164 after = [ "network.target" ]; 165 path = [ data.package ]; 166 restartTriggers = 167 let 168 drvlist = [ config.environment.etc."tinc/${network}/tinc.conf".source ] 169 ++ mapAttrsToList (host: _: config.environment.etc."tinc/${network}/hosts/${host}".source) data.hosts; 170 in # drvlist might be too long to be used directly 171 [ (builtins.hashString "sha256" (concatMapStrings (d: d.outPath) drvlist)) ]; 172 serviceConfig = { 173 Type = "simple"; 174 Restart = "always"; 175 RestartSec = "3"; 176 ExecStart = "${data.package}/bin/tincd -D -U tinc.${network} -n ${network} ${optionalString (data.chroot) "-R"} --pidfile /run/tinc.${network}.pid -d ${toString data.debugLevel}"; 177 }; 178 preStart = '' 179 mkdir -p /etc/tinc/${network}/hosts 180 chown tinc.${network} /etc/tinc/${network}/hosts 181 mkdir -p /etc/tinc/${network}/invitations 182 chown tinc.${network} /etc/tinc/${network}/invitations 183 184 # Determine how we should generate our keys 185 if type tinc >/dev/null 2>&1; then 186 # Tinc 1.1+ uses the tinc helper application for key generation 187 ${if data.ed25519PrivateKeyFile != null then " # Keyfile managed by nix" else '' 188 # Prefer ED25519 keys (only in 1.1+) 189 [ -f "/etc/tinc/${network}/ed25519_key.priv" ] || tinc -n ${network} generate-ed25519-keys 190 ''} 191 # Otherwise use RSA keys 192 [ -f "/etc/tinc/${network}/rsa_key.priv" ] || tinc -n ${network} generate-rsa-keys 4096 193 else 194 # Tinc 1.0 uses the tincd application 195 [ -f "/etc/tinc/${network}/rsa_key.priv" ] || tincd -n ${network} -K 4096 196 fi 197 ''; 198 }) 199 ); 200 201 environment.systemPackages = let 202 cli-wrappers = pkgs.stdenv.mkDerivation { 203 name = "tinc-cli-wrappers"; 204 buildInputs = [ pkgs.makeWrapper ]; 205 buildCommand = '' 206 mkdir -p $out/bin 207 ${concatStringsSep "\n" (mapAttrsToList (network: data: 208 optionalString (versionAtLeast data.package.version "1.1pre") '' 209 makeWrapper ${data.package}/bin/tinc "$out/bin/tinc.${network}" \ 210 --add-flags "--pidfile=/run/tinc.${network}.pid" 211 '') cfg.networks)} 212 ''; 213 }; 214 in [ cli-wrappers ]; 215 216 users.extraUsers = flip mapAttrs' cfg.networks (network: _: 217 nameValuePair ("tinc.${network}") ({ 218 description = "Tinc daemon user for ${network}"; 219 isSystemUser = true; 220 }) 221 ); 222 223 }; 224 225}