1{
2 options,
3 config,
4 pkgs,
5 lib,
6 ...
7}:
8
9let
10 inherit (lib) mkOption types mkIf;
11
12 opt = options.services.quicktun;
13 cfg = config.services.quicktun;
14in
15{
16 options = {
17 services.quicktun = mkOption {
18 default = { };
19 description = ''
20 QuickTun tunnels.
21
22 See <http://wiki.ucis.nl/QuickTun> for more information about available options.
23 '';
24 type = types.attrsOf (
25 types.submodule (
26 { name, ... }:
27 let
28 qtcfg = cfg.${name};
29 in
30 {
31 options = {
32 tunMode = mkOption {
33 type = with types; coercedTo bool (b: if b then 1 else 0) (ints.between 0 1);
34 default = false;
35 example = true;
36 description = "Whether to operate in tun (IP) or tap (Ethernet) mode.";
37 };
38
39 remoteAddress = mkOption {
40 type = types.str;
41 default = "0.0.0.0";
42 example = "tunnel.example.com";
43 description = ''
44 IP address or hostname of the remote end (use `0.0.0.0` for a floating/dynamic remote endpoint).
45 '';
46 };
47
48 localAddress = mkOption {
49 type = with types; nullOr str;
50 default = null;
51 example = "0.0.0.0";
52 description = "IP address or hostname of the local end.";
53 };
54
55 localPort = mkOption {
56 type = types.port;
57 default = 2998;
58 description = "Local UDP port.";
59 };
60
61 remotePort = mkOption {
62 type = types.port;
63 default = qtcfg.localPort;
64 defaultText = lib.literalExpression "config.services.quicktun.<name>.localPort";
65 description = " remote UDP port";
66 };
67
68 remoteFloat = mkOption {
69 type = with types; coercedTo bool (b: if b then 1 else 0) (ints.between 0 1);
70 default = false;
71 example = true;
72 description = ''
73 Whether to allow the remote address and port to change when properly encrypted packets are received.
74 '';
75 };
76
77 protocol = mkOption {
78 type = types.enum [
79 "raw"
80 "nacl0"
81 "nacltai"
82 "salty"
83 ];
84 default = "nacltai";
85 description = "Which protocol to use.";
86 };
87
88 privateKey = mkOption {
89 type = with types; nullOr str;
90 default = null;
91 description = ''
92 Local secret key in hexadecimal form.
93
94 ::: {.warning}
95 This option is deprecated. Please use {var}`services.quicktun.<name>.privateKeyFile` instead.
96 :::
97
98 ::: {.note}
99 Not needed when {var}`services.quicktun.<name>.protocol` is set to `raw`.
100 :::
101 '';
102 };
103
104 privateKeyFile = mkOption {
105 type = with types; nullOr path;
106 # This is a hack to deprecate `privateKey` without using `mkChangedModuleOption`
107 default =
108 if qtcfg.privateKey == null then null else pkgs.writeText "quickttun-key-${name}" qtcfg.privateKey;
109 defaultText = "null";
110 description = ''
111 Path to file containing local secret key in binary or hexadecimal form.
112
113 ::: {.note}
114 Not needed when {var}`services.quicktun.<name>.protocol` is set to `raw`.
115 :::
116 '';
117 };
118
119 publicKey = mkOption {
120 type = with types; nullOr str;
121 default = null;
122 description = ''
123 Remote public key in hexadecimal form.
124
125 ::: {.note}
126 Not needed when {var}`services.quicktun.<name>.protocol` is set to `raw`.
127 :::
128 '';
129 };
130
131 timeWindow = mkOption {
132 type = types.ints.unsigned;
133 default = 5;
134 description = ''
135 Allowed time window for first received packet in seconds (positive number allows packets from history)
136 '';
137 };
138
139 upScript = mkOption {
140 type = with types; nullOr lines;
141 default = null;
142 description = ''
143 Run specified command or script after the tunnel device has been opened.
144 '';
145 };
146 };
147 }
148 )
149 );
150 };
151 };
152
153 config = {
154 warnings = lib.pipe cfg [
155 (lib.mapAttrsToList (name: value: if value.privateKey != null then name else null))
156 (builtins.filter (n: n != null))
157 (map (n: " - services.quicktun.${n}.privateKey"))
158 (
159 services:
160 lib.optional (services != [ ]) ''
161 `services.quicktun.<name>.privateKey` is deprecated.
162 Please use `services.quicktun.<name>.privateKeyFile` instead.
163
164 Offending options:
165 ${lib.concatStringsSep "\n" services}
166 ''
167 )
168 ];
169
170 systemd.services = lib.mkMerge (
171 lib.mapAttrsToList (name: qtcfg: {
172 "quicktun-${name}" = {
173 wantedBy = [ "multi-user.target" ];
174 after = [ "network.target" ];
175 environment = {
176 INTERFACE = name;
177 TUN_MODE = toString qtcfg.tunMode;
178 REMOTE_ADDRESS = qtcfg.remoteAddress;
179 LOCAL_ADDRESS = mkIf (qtcfg.localAddress != null) (qtcfg.localAddress);
180 LOCAL_PORT = toString qtcfg.localPort;
181 REMOTE_PORT = toString qtcfg.remotePort;
182 REMOTE_FLOAT = toString qtcfg.remoteFloat;
183 PRIVATE_KEY_FILE = mkIf (qtcfg.privateKeyFile != null) qtcfg.privateKeyFile;
184 PUBLIC_KEY = mkIf (qtcfg.publicKey != null) qtcfg.publicKey;
185 TIME_WINDOW = toString qtcfg.timeWindow;
186 TUN_UP_SCRIPT = mkIf (qtcfg.upScript != null) (
187 pkgs.writeScript "quicktun-${name}-up.sh" qtcfg.upScript
188 );
189 SUID = "nobody";
190 };
191 serviceConfig = {
192 Type = "simple";
193 ExecStart = "${pkgs.quicktun}/bin/quicktun.${qtcfg.protocol}";
194 };
195 };
196 }) cfg
197 );
198 };
199}