|
23 | 23 |
|
24 | 24 | privateKey = mkOption {
|
25 | 25 | example = "yAnz5TF+lXXJte14tji3zlMNq+hd2rYUIgJBgB3fBmk=";
|
26 |
| - type = types.str; |
27 |
| - description = "Base64 private key generated by wg genkey."; |
| 26 | + type = with types; nullOr str; |
| 27 | + default = null; |
| 28 | + description = '' |
| 29 | + Base64 private key generated by wg genkey. |
| 30 | +
|
| 31 | + Warning: Consider using privateKeyFile instead if you do not |
| 32 | + want to store the key in the world-readable Nix store. |
| 33 | + ''; |
| 34 | + }; |
| 35 | + |
| 36 | + privateKeyFile = mkOption { |
| 37 | + example = "/private/wireguard_key"; |
| 38 | + type = with types; nullOr str; |
| 39 | + default = null; |
| 40 | + description = '' |
| 41 | + Private key file as generated by wg genkey. |
| 42 | + ''; |
28 | 43 | };
|
29 | 44 |
|
30 | 45 | listenPort = mkOption {
|
|
91 | 106 | example = "rVXs/Ni9tu3oDBLS4hOyAUAa1qTWVA3loR8eL20os3I=";
|
92 | 107 | type = with types; nullOr str;
|
93 | 108 | description = ''
|
94 |
| - base64 preshared key generated by wg genpsk. Optional, |
| 109 | + Base64 preshared key generated by wg genpsk. Optional, |
| 110 | + and may be omitted. This option adds an additional layer of |
| 111 | + symmetric-key cryptography to be mixed into the already existing |
| 112 | + public-key cryptography, for post-quantum resistance. |
| 113 | +
|
| 114 | + Warning: Consider using presharedKeyFile instead if you do not |
| 115 | + want to store the key in the world-readable Nix store. |
| 116 | + ''; |
| 117 | + }; |
| 118 | + |
| 119 | + presharedKeyFile = mkOption { |
| 120 | + default = null; |
| 121 | + example = "/private/wireguard_psk"; |
| 122 | + type = with types; nullOr str; |
| 123 | + description = '' |
| 124 | + File pointing to preshared key as generated by wg pensk. Optional, |
95 | 125 | and may be omitted. This option adds an additional layer of
|
96 | 126 | symmetric-key cryptography to be mixed into the already existing
|
97 | 127 | public-key cryptography, for post-quantum resistance.
|
@@ -134,54 +164,59 @@ let
|
134 | 164 |
|
135 | 165 | };
|
136 | 166 |
|
137 |
| - generateConf = name: values: pkgs.writeText "wireguard-${name}.conf" '' |
138 |
| - [Interface] |
139 |
| - PrivateKey = ${values.privateKey} |
140 |
| - ${optionalString (values.listenPort != null) "ListenPort = ${toString values.listenPort}"} |
141 |
| -
|
142 |
| - ${concatStringsSep "\n\n" (map (peer: '' |
143 |
| - [Peer] |
144 |
| - PublicKey = ${peer.publicKey} |
145 |
| - ${optionalString (peer.presharedKey != null) "PresharedKey = ${peer.presharedKey}"} |
146 |
| - ${optionalString (peer.allowedIPs != []) "AllowedIPs = ${concatStringsSep ", " peer.allowedIPs}"} |
147 |
| - ${optionalString (peer.endpoint != null) "Endpoint = ${peer.endpoint}"} |
148 |
| - ${optionalString (peer.persistentKeepalive != null) "PersistentKeepalive = ${toString peer.persistentKeepalive}"} |
149 |
| - '') values.peers)} |
150 |
| - ''; |
151 |
| - |
152 | 167 | ipCommand = "${pkgs.iproute}/bin/ip";
|
153 | 168 | wgCommand = "${pkgs.wireguard}/bin/wg";
|
154 | 169 |
|
155 | 170 | generateUnit = name: values:
|
| 171 | + # exactly one way to specify the private key must be set |
| 172 | + assert (values.privateKey != null) != (values.privateKeyFile != null); |
| 173 | + let privKey = if values.privateKeyFile != null then values.privateKeyFile else pkgs.writeText "wg-key" values.privateKey; |
| 174 | + in |
156 | 175 | nameValuePair "wireguard-${name}"
|
157 | 176 | {
|
158 | 177 | description = "WireGuard Tunnel - ${name}";
|
159 | 178 | after = [ "network.target" ];
|
160 | 179 | wantedBy = [ "multi-user.target" ];
|
| 180 | + |
161 | 181 | serviceConfig = {
|
162 | 182 | Type = "oneshot";
|
163 | 183 | RemainAfterExit = true;
|
164 |
| - ExecStart = lib.flatten([ |
| 184 | + ExecStart = flatten([ |
165 | 185 | values.preSetup
|
166 | 186 |
|
167 | 187 | "-${ipCommand} link del dev ${name}"
|
168 | 188 | "${ipCommand} link add dev ${name} type wireguard"
|
169 |
| - "${wgCommand} setconf ${name} ${generateConf name values}" |
170 | 189 |
|
171 | 190 | (map (ip:
|
172 |
| - ''${ipCommand} address add ${ip} dev ${name}'' |
| 191 | + "${ipCommand} address add ${ip} dev ${name}" |
173 | 192 | ) values.ips)
|
174 | 193 |
|
| 194 | + ("${wgCommand} set ${name} private-key ${privKey}" + |
| 195 | + optionalString (values.listenPort != null) " listen-port ${toString values.listenPort}") |
| 196 | + |
| 197 | + (map (peer: |
| 198 | + assert (peer.presharedKeyFile == null) || (peer.presharedKey == null); # at most one of the two must be set |
| 199 | + let psk = if peer.presharedKey != null then pkgs.writeText "wg-psk" peer.presharedKey else peer.presharedKeyFile; |
| 200 | + in |
| 201 | + "${wgCommand} set ${name} peer ${peer.publicKey}" + |
| 202 | + optionalString (psk != null) " preshared-key ${psk}" + |
| 203 | + optionalString (peer.endpoint != null) " endpoint ${peer.endpoint}" + |
| 204 | + optionalString (peer.persistentKeepalive != null) " persistent-keepalive ${toString peer.persistentKeepalive}" + |
| 205 | + optionalString (peer.allowedIPs != []) " allowed-ips ${concatStringsSep "," peer.allowedIPs}" |
| 206 | + ) values.peers) |
| 207 | + |
175 | 208 | "${ipCommand} link set up dev ${name}"
|
176 | 209 |
|
177 |
| - (flatten (map (peer: (map (ip: |
| 210 | + (map (peer: (map (ip: |
178 | 211 | "${ipCommand} route add ${ip} dev ${name}"
|
179 |
| - ) peer.allowedIPs)) values.peers)) |
| 212 | + ) peer.allowedIPs)) values.peers) |
180 | 213 |
|
181 | 214 | values.postSetup
|
182 | 215 | ]);
|
183 |
| - |
184 |
| - ExecStop = [ ''${ipCommand} link del dev "${name}"'' ] ++ values.postShutdown; |
| 216 | + ExecStop = flatten([ |
| 217 | + "${ipCommand} link del dev ${name}" |
| 218 | + values.postShutdown |
| 219 | + ]); |
185 | 220 | };
|
186 | 221 | };
|
187 | 222 |
|
|
0 commit comments