{ options, config, pkgs, lib, ... }: let inherit (lib) mkOption mdDoc types mkIf; opt = options.services.quicktun; cfg = config.services.quicktun; in { options = { services.quicktun = mkOption { default = { }; description = mdDoc '' QuickTun tunnels. See for more information about available options. ''; type = types.attrsOf (types.submodule ({ name, ... }: let qtcfg = cfg.${name}; in { options = { tunMode = mkOption { type = with types; coercedTo bool (b: if b then 1 else 0) (ints.between 0 1); default = false; example = true; description = mdDoc "Whether to operate in tun (IP) or tap (Ethernet) mode."; }; remoteAddress = mkOption { type = types.str; default = "0.0.0.0"; example = "tunnel.example.com"; description = mdDoc '' IP address or hostname of the remote end (use `0.0.0.0` for a floating/dynamic remote endpoint). ''; }; localAddress = mkOption { type = with types; nullOr str; default = null; example = "0.0.0.0"; description = mdDoc "IP address or hostname of the local end."; }; localPort = mkOption { type = types.port; default = 2998; description = mdDoc "Local UDP port."; }; remotePort = mkOption { type = types.port; default = qtcfg.localPort; defaultText = lib.literalExpression "config.services.quicktun..localPort"; description = mdDoc " remote UDP port"; }; remoteFloat = mkOption { type = with types; coercedTo bool (b: if b then 1 else 0) (ints.between 0 1); default = false; example = true; description = mdDoc '' Whether to allow the remote address and port to change when properly encrypted packets are received. ''; }; protocol = mkOption { type = types.enum [ "raw" "nacl0" "nacltai" "salty" ]; default = "nacltai"; description = mdDoc "Which protocol to use."; }; privateKey = mkOption { type = with types; nullOr str; default = null; description = mdDoc '' Local secret key in hexadecimal form. ::: {.warning} This option is deprecated. Please use {var}`services.quicktun..privateKeyFile` instead. ::: ::: {.note} Not needed when {var}`services.quicktun..protocol` is set to `raw`. ::: ''; }; privateKeyFile = mkOption { type = with types; nullOr path; # This is a hack to deprecate `privateKey` without using `mkChangedModuleOption` default = if qtcfg.privateKey == null then null else pkgs.writeText "quickttun-key-${name}" qtcfg.privateKey; defaultText = "null"; description = mdDoc '' Path to file containing local secret key in binary or hexadecimal form. ::: {.note} Not needed when {var}`services.quicktun..protocol` is set to `raw`. ::: ''; }; publicKey = mkOption { type = with types; nullOr str; default = null; description = mdDoc '' Remote public key in hexadecimal form. ::: {.note} Not needed when {var}`services.quicktun..protocol` is set to `raw`. ::: ''; }; timeWindow = mkOption { type = types.ints.unsigned; default = 5; description = mdDoc '' Allowed time window for first received packet in seconds (positive number allows packets from history) ''; }; upScript = mkOption { type = with types; nullOr lines; default = null; description = mdDoc '' Run specified command or script after the tunnel device has been opened. ''; }; }; })); }; }; config = { warnings = lib.pipe cfg [ (lib.mapAttrsToList (name: value: if value.privateKey != null then name else null)) (builtins.filter (n: n != null)) (map (n: " - services.quicktun.${n}.privateKey")) (services: lib.optional (services != [ ]) '' `services.quicktun..privateKey` is deprecated. Please use `services.quicktun..privateKeyFile` instead. Offending options: ${lib.concatStringsSep "\n" services} '') ]; systemd.services = lib.mkMerge ( lib.mapAttrsToList (name: qtcfg: { "quicktun-${name}" = { wantedBy = [ "multi-user.target" ]; after = [ "network.target" ]; environment = { INTERFACE = name; TUN_MODE = toString qtcfg.tunMode; REMOTE_ADDRESS = qtcfg.remoteAddress; LOCAL_ADDRESS = mkIf (qtcfg.localAddress != null) (qtcfg.localAddress); LOCAL_PORT = toString qtcfg.localPort; REMOTE_PORT = toString qtcfg.remotePort; REMOTE_FLOAT = toString qtcfg.remoteFloat; PRIVATE_KEY_FILE = mkIf (qtcfg.privateKeyFile != null) qtcfg.privateKeyFile; PUBLIC_KEY = mkIf (qtcfg.publicKey != null) qtcfg.publicKey; TIME_WINDOW = toString qtcfg.timeWindow; TUN_UP_SCRIPT = mkIf (qtcfg.upScript != null) (pkgs.writeScript "quicktun-${name}-up.sh" qtcfg.upScript); SUID = "nobody"; }; serviceConfig = { Type = "simple"; ExecStart = "${pkgs.quicktun}/bin/quicktun.${qtcfg.protocol}"; }; }; }) cfg ); }; }