194 lines
		
	
	
		
			6.1 KiB
		
	
	
	
		
			Nix
		
	
	
	
	
	
			
		
		
	
	
			194 lines
		
	
	
		
			6.1 KiB
		
	
	
	
		
			Nix
		
	
	
	
	
	
| { config, lib, pkgs, ... }:
 | |
| 
 | |
| with lib;
 | |
| 
 | |
| let
 | |
| 
 | |
|   cfg = config.services.tinc;
 | |
| 
 | |
| in
 | |
| 
 | |
| {
 | |
| 
 | |
|   ###### interface
 | |
| 
 | |
|   options = {
 | |
| 
 | |
|     services.tinc = {
 | |
| 
 | |
|       networks = mkOption {
 | |
|         default = { };
 | |
|         type = types.loaOf types.optionSet;
 | |
|         description = ''
 | |
|           Defines the tinc networks which will be started.
 | |
|           Each network invokes a different daemon.
 | |
|         '';
 | |
|         options = {
 | |
| 
 | |
|           extraConfig = mkOption {
 | |
|             default = "";
 | |
|             type = types.lines;
 | |
|             description = ''
 | |
|               Extra lines to add to the tinc service configuration file.
 | |
|             '';
 | |
|           };
 | |
| 
 | |
|           name = mkOption {
 | |
|             default = null;
 | |
|             type = types.nullOr types.str;
 | |
|             description = ''
 | |
|               The name of the node which is used as an identifier when communicating
 | |
|               with the remote nodes in the mesh. If null then the hostname of the system
 | |
|               is used.
 | |
|             '';
 | |
|           };
 | |
| 
 | |
|           ed25519PrivateKeyFile = mkOption {
 | |
|             default = null;
 | |
|             type = types.nullOr types.path;
 | |
|             description = ''
 | |
|               Path of the private ed25519 keyfile.
 | |
|             '';
 | |
|           };
 | |
| 
 | |
|           debugLevel = mkOption {
 | |
|             default = 0;
 | |
|             type = types.addCheck types.int (l: l >= 0 && l <= 5);
 | |
|             description = ''
 | |
|               The amount of debugging information to add to the log. 0 means little
 | |
|               logging while 5 is the most logging. <command>man tincd</command> for
 | |
|               more details.
 | |
|             '';
 | |
|           };
 | |
| 
 | |
|           hosts = mkOption {
 | |
|             default = { };
 | |
|             type = types.loaOf 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.
 | |
|             '';
 | |
|           };
 | |
| 
 | |
|           interfaceType = mkOption {
 | |
|             default = "tun";
 | |
|             type = types.addCheck types.str (n: n == "tun" || n == "tap");
 | |
|             description = ''
 | |
|               The type of virtual interface used for the network connection
 | |
|             '';
 | |
|           };
 | |
| 
 | |
|           listenAddress = mkOption {
 | |
|             default = null;
 | |
|             type = types.nullOr types.str;
 | |
|             description = ''
 | |
|               The ip adress to bind to.
 | |
|             '';
 | |
|           };
 | |
| 
 | |
|           package = mkOption {
 | |
|             type = types.package;
 | |
|             default = pkgs.tinc_pre;
 | |
|             defaultText = "pkgs.tinc_pre";
 | |
|             description = ''
 | |
|               The package to use for the tinc daemon's binary.
 | |
|             '';
 | |
|           };
 | |
| 
 | |
|           chroot = mkOption {
 | |
|             default = true;
 | |
|             type = types.bool;
 | |
|             description = ''
 | |
|               Change process root directory to the directory where the config file is located (/etc/tinc/netname/), for added security.
 | |
|               The chroot is performed after all the initialization is done, after writing pid files and opening network sockets.
 | |
| 
 | |
|               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.
 | |
|             '';
 | |
|           };
 | |
|         };
 | |
|       };
 | |
|     };
 | |
| 
 | |
|   };
 | |
| 
 | |
| 
 | |
|   ###### implementation
 | |
| 
 | |
|   config = mkIf (cfg.networks != { }) {
 | |
| 
 | |
|     environment.etc = fold (a: b: a // b) { }
 | |
|       (flip mapAttrsToList cfg.networks (network: data:
 | |
|         flip mapAttrs' data.hosts (host: text: nameValuePair
 | |
|           ("tinc/${network}/hosts/${host}")
 | |
|           ({ mode = "0444"; inherit text; })
 | |
|         ) // {
 | |
|           "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.listenAddress != null) "BindToAddress = ${data.listenAddress}"}
 | |
|               Device = /dev/net/tun
 | |
|               Interface = tinc.${network}
 | |
|               ${data.extraConfig}
 | |
|             '';
 | |
|           };
 | |
|         }
 | |
|       ));
 | |
| 
 | |
|     networking.interfaces = flip mapAttrs' cfg.networks (network: data: nameValuePair
 | |
|       ("tinc.${network}")
 | |
|       ({
 | |
|         virtual = true;
 | |
|         virtualType = "${data.interfaceType}";
 | |
|       })
 | |
|     );
 | |
| 
 | |
|     systemd.services = flip mapAttrs' cfg.networks (network: data: nameValuePair
 | |
|       ("tinc.${network}")
 | |
|       ({
 | |
|         description = "Tinc Daemon - ${network}";
 | |
|         wantedBy = [ "network.target" ];
 | |
|         after = [ "network-interfaces.target" ];
 | |
|         path = [ data.package ];
 | |
|         restartTriggers = [ config.environment.etc."tinc/${network}/tinc.conf".source ]
 | |
|           ++ mapAttrsToList (host: _ : config.environment.etc."tinc/${network}/hosts/${host}".source) data.hosts;
 | |
|         serviceConfig = {
 | |
|           Type = "simple";
 | |
|           PIDFile = "/run/tinc.${network}.pid";
 | |
|         };
 | |
|         preStart = ''
 | |
|           mkdir -p /etc/tinc/${network}/hosts
 | |
| 
 | |
|           # Determine how we should generate our keys
 | |
|           if type tinc >/dev/null 2>&1; then
 | |
|             # Tinc 1.1+ uses the tinc helper application for key generation
 | |
|           ${if data.ed25519PrivateKeyFile != null then "  # Keyfile managed by nix" else ''
 | |
|             # Prefer ED25519 keys (only in 1.1+)
 | |
|             [ -f "/etc/tinc/${network}/ed25519_key.priv" ] || tinc -n ${network} generate-ed25519-keys
 | |
|           ''}
 | |
|             # Otherwise use RSA keys
 | |
|             [ -f "/etc/tinc/${network}/rsa_key.priv" ] || tinc -n ${network} generate-rsa-keys 4096
 | |
|           else
 | |
|             # Tinc 1.0 uses the tincd application
 | |
|             [ -f "/etc/tinc/${network}/rsa_key.priv" ] || tincd -n ${network} -K 4096
 | |
|           fi
 | |
|         '';
 | |
|         script = ''
 | |
|           tincd -D -U tinc.${network} -n ${network} ${optionalString (data.chroot) "-R"} --pidfile /run/tinc.${network}.pid -d ${toString data.debugLevel}
 | |
|         '';
 | |
|       })
 | |
|     );
 | |
| 
 | |
|     users.extraUsers = flip mapAttrs' cfg.networks (network: _:
 | |
|       nameValuePair ("tinc.${network}") ({
 | |
|         description = "Tinc daemon user for ${network}";
 | |
|         isSystemUser = true;
 | |
|       })
 | |
|     );
 | |
| 
 | |
|   };
 | |
| 
 | |
| }
 | 
