Skip to content

Commit 6714da3

Browse files
committed
wg-quick linux: Add strip-and-eval cmd to extract keys from PostUp
The manpage mentions the trick to use PostUp to read the PrivateKey (or PresharedKey) from a command (or file). However, when you actually use that you notice that this is currently not fully supported. The issue is that ```Shell wg syncconf wgnet0 <(wg-quick strip wgnet0) ``` from the manpage now breaks the VPN because it *removes* the private key from the WireGuard interface. The reason is that `strip` removes PostUp of course. This patch tries to add full support to read WireGuard keys from files or command outputs by evaluating PostUp using a best effort approach (using regex). It will not work for everything but when you follow the manpage closely, it will work. I also propose to update the systemd template to make seamless use of this. This is not a must because the sysadmin can easily change the ExecReload using systemd drop-in files. Example use of this patch: https://github.com/ypid/ansible-wireguard/tree/prepare-for-debops
1 parent 265e81a commit 6714da3

File tree

3 files changed

+43
-4
lines changed

3 files changed

+43
-4
lines changed

src/man/wg-quick.8

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ wg-quick - set up a WireGuard interface simply
1313
.I save
1414
|
1515
.I strip
16+
|
17+
.I strip-and-eval
1618
] [
1719
.I CONFIG_FILE
1820
|
@@ -34,6 +36,7 @@ with all
3436
.BR wg-quick (8)-specific
3537
options removed, suitable for use with
3638
.BR wg (8).
39+
Use \fIstrip-and-eval\fP in case you use `PostUp' to read PrivateKey or PresharedKey from a file or command and need them in the output.
3740

3841
\fICONFIG_FILE\fP is a configuration file, whose filename is the interface name
3942
followed by `.conf'. Otherwise, \fIINTERFACE\fP is an interface name, with configuration
@@ -256,6 +259,12 @@ sessions:
256259

257260
\fB # wg syncconf wgnet0 <(wg-quick strip wgnet0)\fP
258261

262+
\fIstrip-and-eval\fP additionally extracts PrivateKey and PresharedKey from `PostUp` statements and translates them into configuration that
263+
.BR wg (8)
264+
does understand:
265+
266+
\fB # wg syncconf wgnet0 <(wg-quick strip-and-eval wgnet0)\fP
267+
259268
.SH SEE ALSO
260269
.BR wg (8),
261270
.BR ip (8),

src/systemd/[email protected]

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ Type=oneshot
1515
RemainAfterExit=yes
1616
ExecStart=/usr/bin/wg-quick up %i
1717
ExecStop=/usr/bin/wg-quick down %i
18-
ExecReload=/bin/bash -c 'exec /usr/bin/wg syncconf %i <(exec /usr/bin/wg-quick strip %i)'
18+
ExecReload=/bin/bash -c 'exec /usr/bin/wg syncconf %i <(exec /usr/bin/wg-quick strip-and-eval %i)'
1919
Environment=WG_ENDPOINT_RESOLUTION_RETRIES=infinity
2020

2121
[Install]

src/wg-quick/linux.bash

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,11 @@ die() {
3838
}
3939

4040
parse_options() {
41+
local parsing_mode part_of_command peer_pubkey
4142
local interface_section=0 line key value stripped v
43+
declare -A peer_pubkey_to_psk
4244
CONFIG_FILE="$1"
45+
parsing_mode="${2:-safe}"
4346
[[ $CONFIG_FILE =~ ^[a-zA-Z0-9_=+.-]{1,15}$ ]] && CONFIG_FILE="/etc/wireguard/$CONFIG_FILE.conf"
4447
[[ -e $CONFIG_FILE ]] || die "\`$CONFIG_FILE' does not exist"
4548
[[ $CONFIG_FILE =~ (^|/)([a-zA-Z0-9_=+.-]{1,15})\.conf$ ]] || die "The config file must be a valid interface name, followed by .conf"
@@ -63,12 +66,35 @@ parse_options() {
6366
Table) TABLE="$value"; continue ;;
6467
PreUp) PRE_UP+=( "$value" ); continue ;;
6568
PreDown) PRE_DOWN+=( "$value" ); continue ;;
66-
PostUp) POST_UP+=( "$value" ); continue ;;
69+
PostUp) POST_UP+=( "$value" );
70+
if [[ $parsing_mode == "unsafe" ]]; then
71+
part_of_command=""
72+
if [[ $value =~ ^wg\ +set\ +%i\ +private-key\ +(.+)$ ]]; then
73+
key='PrivateKey'
74+
part_of_command="${BASH_REMATCH[1]}"
75+
elif [[ $value =~ ^wg\ +set\ +%i\ +peer\ +(.+)\ +preshared-key\ +(.+)$ ]]; then
76+
key='PresharedKey'
77+
peer_pubkey="${BASH_REMATCH[1]}"
78+
part_of_command="${BASH_REMATCH[2]}"
79+
fi
80+
if [[ -n "$part_of_command" ]]; then
81+
part_of_command="${part_of_command//%i/$INTERFACE}"
82+
value="$(eval "cat $part_of_command")"
83+
case "$key" in
84+
PresharedKey) peer_pubkey_to_psk["$peer_pubkey"]="$value" ;;
85+
*) WG_CONFIG+="$key = $value"$'\n' ;;
86+
esac
87+
fi
88+
fi
89+
continue ;;
6790
PostDown) POST_DOWN+=( "$value" ); continue ;;
6891
SaveConfig) read_bool SAVE_CONFIG "$value"; continue ;;
6992
esac
7093
fi
7194
WG_CONFIG+="$line"$'\n'
95+
if [[ $interface_section -eq 0 && $key == 'PublicKey' && -n "${peer_pubkey_to_psk[$value]}" ]]; then
96+
WG_CONFIG+="PresharedKey = ${peer_pubkey_to_psk[$value]}"$'\n'
97+
fi
7298
done < "$CONFIG_FILE"
7399
shopt -u nocasematch
74100
}
@@ -224,7 +250,7 @@ add_default() {
224250
cmd ip $proto rule add not fwmark $table table $table
225251
cmd ip $proto rule add table main suppress_prefixlength 0
226252

227-
local marker="-m comment --comment \"wg-quick(8) rule for $INTERFACE\"" restore=$'*raw\n' nftable="wg-quick-$INTERFACE" nftcmd
253+
local marker="-m comment --comment \"wg-quick(8) rule for $INTERFACE\"" restore=$'*raw\n' nftable="wg-quick-$INTERFACE" nftcmd
228254
printf -v nftcmd '%sadd table %s %s\n' "$nftcmd" "$pf" "$nftable"
229255
printf -v nftcmd '%sadd chain %s %s preraw { type filter hook prerouting priority -300; }\n' "$nftcmd" "$pf" "$nftable"
230256
printf -v nftcmd '%sadd chain %s %s premangle { type filter hook prerouting priority -150; }\n' "$nftcmd" "$pf" "$nftable"
@@ -298,7 +324,7 @@ execute_hooks() {
298324

299325
cmd_usage() {
300326
cat >&2 <<-_EOF
301-
Usage: $PROGRAM [ up | down | save | strip ] [ CONFIG_FILE | INTERFACE ]
327+
Usage: $PROGRAM [ up | down | save | strip | strip-and-eval ] [ CONFIG_FILE | INTERFACE ]
302328
303329
CONFIG_FILE is a configuration file, whose filename is the interface name
304330
followed by \`.conf'. Otherwise, INTERFACE is an interface name, with
@@ -381,6 +407,10 @@ elif [[ $# -eq 2 && $1 == strip ]]; then
381407
auto_su
382408
parse_options "$2"
383409
cmd_strip
410+
elif [[ $# -eq 2 && $1 == strip-and-eval ]]; then
411+
auto_su
412+
parse_options "$2" "unsafe"
413+
cmd_strip
384414
else
385415
cmd_usage
386416
exit 1

0 commit comments

Comments
 (0)