wg: add wg-quick
This is based on wg-config, but is even easier to use, and now makes our full tools suite. Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
This commit is contained in:
parent
bf158a73fe
commit
e975597e72
|
@ -1,11 +0,0 @@
|
||||||
PREFIX ?= /usr
|
|
||||||
DESTDIR ?=
|
|
||||||
SBINDIR ?= $(PREFIX)/sbin
|
|
||||||
|
|
||||||
all:
|
|
||||||
@echo "This is a shell script, so there is nothing to do. Try \"make install\" instead."
|
|
||||||
|
|
||||||
install:
|
|
||||||
@install -v -m0755 -D -t$(DESTDIR)$(SBINDIR) wg-config
|
|
||||||
|
|
||||||
.PHONY: all install
|
|
|
@ -1,140 +0,0 @@
|
||||||
== Installation ==
|
|
||||||
|
|
||||||
# make install
|
|
||||||
|
|
||||||
== Usage ==
|
|
||||||
|
|
||||||
wg-config is a very simple utility for adding and configuring WireGuard
|
|
||||||
interfaces using ip(8) and wg(8).
|
|
||||||
|
|
||||||
Usage: wg-config [ add | del ] INTERFACE [arguments...]
|
|
||||||
|
|
||||||
wg-config add INTERFACE --config=CONFIG_FILE [--address=ADDRESS/CIDR...]
|
|
||||||
[--route=ROUTE/CIDR...] [--no-auto-route-from-allowed-ips]
|
|
||||||
[--env-file=ENV_FILE]
|
|
||||||
|
|
||||||
The add subcommand adds a new WireGuard interface, INTERFACE, replacing
|
|
||||||
any existing interfaces of the same name. The --config argument is
|
|
||||||
required, and its argument is passed to wg(8)'s setconf subcommand. The
|
|
||||||
--address argument(s) is recommended for this utility to be useful. The
|
|
||||||
--route argument is purely optional, as by default this utility will
|
|
||||||
automatically add routes implied by --address and as implied by the
|
|
||||||
allowed-ip entries inside the --config file. To disable this automatic
|
|
||||||
route adding, you may use the option entitled --no-auto-route-from-allowed-ips.
|
|
||||||
|
|
||||||
wg-config del INTERFACE [--config=CONFIG_FILE_TO_SAVE] [--env-file=ENV_FILE]
|
|
||||||
|
|
||||||
The del subcommand removes an existing WireGuard interface. If the
|
|
||||||
optional --config is specified, then the existing configuration is
|
|
||||||
written out to the file specified, via wg(8)'s showconf subcommand.
|
|
||||||
|
|
||||||
Both `add' and del' take the --env-file=ENV_FILE option. If specified,
|
|
||||||
the contents of ENV_FILE are imported into wg-config. This can be used to
|
|
||||||
set variables in a file, instead of needing to pass them on the command
|
|
||||||
line. The following table shows the relation between the command line
|
|
||||||
options described above, and variables that may be declared in ENV_FILE:
|
|
||||||
|
|
||||||
--address=A, --address=B, --address=C ADDRESSES=( "A" "B" "C" )
|
|
||||||
--route=A, --route=B, --route=C ADDITIONAL_ROUTES=( "A" "B" "C" )
|
|
||||||
--config-file=F CONFIG_FILE="F"
|
|
||||||
echo C > /tmp/F, --config-file=/tmp/F CONFIG_FILE_CONTENTS="C"
|
|
||||||
--no-auto-route-from-allowed-ips AUTO_ROUTE=0
|
|
||||||
|
|
||||||
Additionally, ENV_FILE may define the bash functions pre_add, post_add,
|
|
||||||
pre_del, and post_del, which will be called at their respective times.
|
|
||||||
|
|
||||||
== Basic Example ==
|
|
||||||
|
|
||||||
This basic example might be used by a server.
|
|
||||||
|
|
||||||
/etc/wireguard/wg-server.conf:
|
|
||||||
|
|
||||||
[Interface]
|
|
||||||
PrivateKey = yAnz5TF+lXXJte14tji3zlMNq+hd2rYUIgJBgB3fBmk=
|
|
||||||
ListenPort = 41414
|
|
||||||
|
|
||||||
[Peer]
|
|
||||||
PublicKey = xTIBA5rboUvnH4htodjb6e697QjLERt1NAB4mZqp8Dg=
|
|
||||||
AllowedIPs = 10.192.122.3/32, 10.192.124.1/24
|
|
||||||
|
|
||||||
[Peer]
|
|
||||||
PublicKey = TrMvSoP4jYQlY6RIzBgbssQqY3vxI2Pi+y71lOWWXX0=
|
|
||||||
AllowedIPs = 10.192.122.4/32, 192.168.0.0/16
|
|
||||||
|
|
||||||
[Peer]
|
|
||||||
PublicKey = gN65BkIKy1eCE9pP1wdc8ROUtkHLF2PfAqYdyYBz6EA=
|
|
||||||
AllowedIPs = 10.10.10.230/32
|
|
||||||
|
|
||||||
/etc/wireguard/wg-server.env:
|
|
||||||
|
|
||||||
CONFIG_FILE="$(dirname "${BASH_SOURCE[0]}")/wg-server.conf"
|
|
||||||
ADDRESSES=( 10.192.122.1/34 10.10.0.1/16 )
|
|
||||||
|
|
||||||
Run at startup:
|
|
||||||
# wg-config add wgserver0 --env-file=/etc/wireguard/wg-server.env
|
|
||||||
Run at shutdown:
|
|
||||||
# wg-config del wgserver0 --env-file=/etc/wireguard/wg-server.env
|
|
||||||
|
|
||||||
== Single File Advanced Example ==
|
|
||||||
|
|
||||||
This type of configuration might be desirable for a personal access gateway
|
|
||||||
VPN, connecting to a server like in the example above.
|
|
||||||
|
|
||||||
/etc/wireguard/wg-vpn-gateway.env:
|
|
||||||
|
|
||||||
CONFIG_FILE_CONTENTS="
|
|
||||||
[Interface]
|
|
||||||
PrivateKey = 6JiA3fa+NG+x5m6aq7+lxlVaVqVf1mxK6/pDOZdNuXc=
|
|
||||||
|
|
||||||
[Peer]
|
|
||||||
PublicKey = 6NagfTu+s8+TkEKpxX7pNjJuTf4zYtoJme7iQFYIw0A=
|
|
||||||
AllowedIPs = 0.0.0.0/0
|
|
||||||
Endpoint = demo.wireguard.io:29912
|
|
||||||
"
|
|
||||||
|
|
||||||
ADDRESSES=( 10.200.100.2/32 )
|
|
||||||
|
|
||||||
post_add() {
|
|
||||||
printf 'nameserver 10.200.100.1' | cmd resolvconf -a "$INTERFACE" -m 0
|
|
||||||
}
|
|
||||||
post_del() {
|
|
||||||
cmd resolvconf -d "$INTERFACE"
|
|
||||||
}
|
|
||||||
|
|
||||||
Run to flip on the VPN:
|
|
||||||
# wg-config add wgvpn0 --env-file=/etc/wireguard/wg-vpn-gateway.env
|
|
||||||
Run to flip off the VPN:
|
|
||||||
# wg-config del wgvpn0 --env-file=/etc/wireguard/wg-vpn-gateway.env
|
|
||||||
|
|
||||||
== Advanced Example ==
|
|
||||||
|
|
||||||
This achieves the same as the above, but with an external file. It only sets the
|
|
||||||
configuration file when the subcommand is add, to prevent it from being overwritten.
|
|
||||||
The above is much simpler and probably preferred, but this example shows how powerful
|
|
||||||
the tool can be.
|
|
||||||
|
|
||||||
/etc/wireguard/wg-vpn-gateway.conf:
|
|
||||||
|
|
||||||
[Interface]
|
|
||||||
PrivateKey = 6JiA3fa+NG+x5m6aq7+lxlVaVqVf1mxK6/pDOZdNuXc=
|
|
||||||
|
|
||||||
[Peer]
|
|
||||||
PublicKey = 6NagfTu+s8+TkEKpxX7pNjJuTf4zYtoJme7iQFYIw0A=
|
|
||||||
AllowedIPs = 0.0.0.0/0
|
|
||||||
Endpoint = demo.wireguard.io:29912
|
|
||||||
|
|
||||||
/etc/wireguard/wg-vpn-gateway.env:
|
|
||||||
|
|
||||||
[[ $SUBCOMMAND == add ]] && CONFIG_FILE="$(dirname "${BASH_SOURCE[0]}")/demo-vpn.conf" || true
|
|
||||||
ADDRESSES=( 10.200.100.2/32 )
|
|
||||||
post_add() {
|
|
||||||
printf 'nameserver 10.200.100.1' | cmd resolvconf -a "$INTERFACE" -m 0
|
|
||||||
}
|
|
||||||
post_del() {
|
|
||||||
cmd resolvconf -d "$INTERFACE"
|
|
||||||
}
|
|
||||||
|
|
||||||
Run to flip on the VPN:
|
|
||||||
# wg-config add wgvpn0 --env-file=/etc/wireguard/wg-vpn-gateway.env
|
|
||||||
The config file is not overwritten on shutdown, due to the conditional in the env file:
|
|
||||||
# wg-config del wgvpn0 --env-file=/etc/wireguard/wg-vpn-gateway.env
|
|
|
@ -1,183 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
set -e -o pipefail
|
|
||||||
|
|
||||||
SELF="$(readlink -f "${BASH_SOURCE[0]}")"
|
|
||||||
export PATH="${SELF%/*}:$PATH"
|
|
||||||
|
|
||||||
cmd() {
|
|
||||||
echo "[#] $*" >&2
|
|
||||||
"$@"
|
|
||||||
}
|
|
||||||
|
|
||||||
auto_su() {
|
|
||||||
[[ $UID == 0 ]] || exec sudo -p "$PROGRAM must be run as root. Please enter the password for %u to continue: " "$SELF" "${ARGS[@]}"
|
|
||||||
}
|
|
||||||
|
|
||||||
unwind() {
|
|
||||||
set +e
|
|
||||||
[[ -n $INTERFACE && -n $(ip link show dev "$INTERFACE" type wireguard 2>/dev/null) ]] && del_if
|
|
||||||
exit
|
|
||||||
}
|
|
||||||
|
|
||||||
add_if() {
|
|
||||||
ip link delete dev "$INTERFACE" 2>/dev/null || true
|
|
||||||
cmd ip link add "$INTERFACE" type wireguard
|
|
||||||
}
|
|
||||||
|
|
||||||
del_if() {
|
|
||||||
[[ -n $(ip link show dev "$INTERFACE" type wireguard 2>/dev/null) ]] || { echo "$PROGRAM: \`$INTERFACE' is not a WireGuard interface" >&2; exit 1; }
|
|
||||||
if [[ $(ip route show table all) =~ .*\ dev\ $INTERFACE\ table\ ([0-9]+)\ .* ]]; then
|
|
||||||
cmd ip rule delete table ${BASH_REMATCH[1]}
|
|
||||||
fi
|
|
||||||
cmd ip link delete dev "$INTERFACE"
|
|
||||||
}
|
|
||||||
|
|
||||||
up_if() {
|
|
||||||
cmd ip link set "$INTERFACE" up
|
|
||||||
}
|
|
||||||
|
|
||||||
add_addr() {
|
|
||||||
cmd ip address add "$1" dev "$INTERFACE"
|
|
||||||
}
|
|
||||||
|
|
||||||
add_route() {
|
|
||||||
if [[ $1 == 0.0.0.0/0 || $1 == ::/0 ]]; then
|
|
||||||
add_default "$1"
|
|
||||||
else
|
|
||||||
cmd ip route add "$1" dev "$INTERFACE"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
add_default() {
|
|
||||||
[[ $(join <(wg show "$INTERFACE" allowed-ips) <(wg show "$INTERFACE" endpoints)) =~ .*\ ${1//./\\.}\ ([0-9.:a-f]+):[0-9]+$ ]] && local endpoint="${BASH_REMATCH[1]}"
|
|
||||||
[[ -n $endpoint ]] || return 0
|
|
||||||
local table=51820
|
|
||||||
while [[ -n $(ip route show table $table) ]]; do ((table++)); done
|
|
||||||
cmd ip route add "$1" dev "$INTERFACE" table $table
|
|
||||||
cmd ip rule add not to "$endpoint" table $table
|
|
||||||
}
|
|
||||||
|
|
||||||
set_config() {
|
|
||||||
if [[ -n $CONFIG_FILE_CONTENTS ]]; then
|
|
||||||
cmd wg setconf "$INTERFACE" <(echo "$CONFIG_FILE_CONTENTS")
|
|
||||||
else
|
|
||||||
cmd wg setconf "$INTERFACE" "$CONFIG_FILE"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
save_config() {
|
|
||||||
local old_umask="$(umask)"
|
|
||||||
umask 077
|
|
||||||
cmd wg showconf "$INTERFACE" > "$CONFIG_FILE.tmp" || { rm -f "$CONFIG_FILE.tmp"; exit 1; }
|
|
||||||
mv "$CONFIG_FILE.tmp" "$CONFIG_FILE" || { rm -f "$CONFIG_FILE.tmp"; exit 1; }
|
|
||||||
umask "$old_umask"
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd_usage() {
|
|
||||||
cat >&2 <<-_EOF
|
|
||||||
Usage: $PROGRAM [ add | del ] INTERFACE [arguments...]
|
|
||||||
|
|
||||||
$PROGRAM add INTERFACE --config=CONFIG_FILE [--address=ADDRESS/CIDR...]
|
|
||||||
[--route=ROUTE/CIDR...] [--no-auto-route-from-allowed-ips]
|
|
||||||
[--env-file=ENV_FILE]
|
|
||||||
|
|
||||||
The add subcommand adds a new WireGuard interface, INTERFACE, replacing
|
|
||||||
any existing interfaces of the same name. The --config argument is
|
|
||||||
required, and its argument is passed to wg(8)'s setconf subcommand. The
|
|
||||||
--address argument(s) is recommended for this utility to be useful. The
|
|
||||||
--route argument is purely optional, as by default this utility will
|
|
||||||
automatically add routes implied by --address and as implied by the
|
|
||||||
allowed-ip entries inside the --config file. To disable this automatic
|
|
||||||
route adding, you may use the option entitled --no-auto-route-from-allowed-ips.
|
|
||||||
|
|
||||||
$PROGRAM del INTERFACE [--config=CONFIG_FILE_TO_SAVE] [--env-file=ENV_FILE]
|
|
||||||
|
|
||||||
The del subcommand removes an existing WireGuard interface. If the
|
|
||||||
optional --config is specified, then the existing configuration is
|
|
||||||
written out to the file specified, via wg(8)'s showconf subcommand.
|
|
||||||
|
|
||||||
$PROGRAM help
|
|
||||||
|
|
||||||
Show this message.
|
|
||||||
|
|
||||||
Both \`add' and ``del' take the --env-file=ENV_FILE option. If specified,
|
|
||||||
the contents of ENV_FILE are imported into $PROGRAM. This can be used to
|
|
||||||
set variables in a file, instead of needing to pass them on the command
|
|
||||||
line. The following table shows the relation between the command line
|
|
||||||
options described above, and variables that may be declared in ENV_FILE:
|
|
||||||
|
|
||||||
--address=A, --address=B, --address=C ADDRESSES=( "A" "B" "C" )
|
|
||||||
--route=A, --route=B, --route=C ADDITIONAL_ROUTES=( "A" "B" "C" )
|
|
||||||
--config-file=F CONFIG_FILE="F"
|
|
||||||
echo C > /tmp/F, --config-file=/tmp/F CONFIG_FILE_CONTENTS="C"
|
|
||||||
--no-auto-route-from-allowed-ips AUTO_ROUTE=0
|
|
||||||
|
|
||||||
Additionally, ENV_FILE may define the bash functions pre_add, post_add,
|
|
||||||
pre_del, and post_del, which will be called at their respective times.
|
|
||||||
_EOF
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd_add() {
|
|
||||||
local i
|
|
||||||
[[ -n $CONFIG_FILE || -n $CONFIG_FILE_CONTENTS ]] || { echo "$PROGRAM: --config is required for add subcommand" >&2; exit 1; }
|
|
||||||
auto_su
|
|
||||||
trap unwind INT TERM EXIT
|
|
||||||
[[ $(type -t pre_add) != function ]] || pre_add
|
|
||||||
add_if
|
|
||||||
set_config
|
|
||||||
for i in "${ADDRESSES[@]}"; do
|
|
||||||
add_addr "$i"
|
|
||||||
done
|
|
||||||
up_if
|
|
||||||
if [[ $AUTO_ROUTE -eq 1 ]]; then
|
|
||||||
for i in $(wg show "$INTERFACE" allowed-ips | grep -Po '(?<=[\t ])[0-9.:/a-f]+' | sort -nr -k 2 -t /); do
|
|
||||||
[[ $(ip route get "$i" 2>/dev/null) == *dev\ $INTERFACE\ * ]] || add_route "$i"
|
|
||||||
done
|
|
||||||
fi
|
|
||||||
for i in "${ADDITIONAL_ROUTES[@]}"; do
|
|
||||||
add_route "$i"
|
|
||||||
done
|
|
||||||
[[ $(type -t post_add) != function ]] || post_add
|
|
||||||
trap - INT TERM EXIT
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd_del() {
|
|
||||||
auto_su
|
|
||||||
[[ $(type -t pre_del) != function ]] || pre_del
|
|
||||||
[[ -n $CONFIG_FILE ]] && save_config
|
|
||||||
del_if
|
|
||||||
[[ $(type -t post_del) != function ]] || post_del
|
|
||||||
}
|
|
||||||
|
|
||||||
declare INTERFACE="$2"
|
|
||||||
declare SUBCOMMAND="$1"
|
|
||||||
declare -a ADDRESSES
|
|
||||||
declare -a ADDITIONAL_ROUTES
|
|
||||||
declare AUTO_ROUTE=1
|
|
||||||
declare CONFIG_FILE
|
|
||||||
declare CONFIG_FILE_CONTENTS
|
|
||||||
declare PROGRAM="${0##*/}"
|
|
||||||
declare -a ARGS=( "$@" )
|
|
||||||
|
|
||||||
[[ -n $INTERFACE && -n $SUBCOMMAND ]] || { cmd_usage; exit 1; }
|
|
||||||
|
|
||||||
shift 2
|
|
||||||
|
|
||||||
for arg; do
|
|
||||||
case "$arg" in
|
|
||||||
--env-file=*) source "${arg#*=}" ;;
|
|
||||||
--config=*) CONFIG_FILE="${arg#*=}" ;;
|
|
||||||
--address=*) ADDRESSES+=( ${arg#*=} ) ;;
|
|
||||||
--route=*) ADDITIONAL_ROUTES+=( ${arg#*=} ) ;;
|
|
||||||
--no-auto-route-from-allowed-ips) AUTO_ROUTE=0 ;;
|
|
||||||
*) cmd_usage; exit 1 ;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
|
|
||||||
case "$SUBCOMMAND" in
|
|
||||||
add) cmd_add ;;
|
|
||||||
del) cmd_del ;;
|
|
||||||
*) cmd_usage; exit 1 ;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
exit 0
|
|
|
@ -7,6 +7,7 @@ BASHCOMPDIR ?= $(PREFIX)/share/bash-completion/completions
|
||||||
RUNSTATEDIR ?= /var/run
|
RUNSTATEDIR ?= /var/run
|
||||||
PKG_CONFIG ?= pkg-config
|
PKG_CONFIG ?= pkg-config
|
||||||
WITH_BASHCOMPLETION ?= yes
|
WITH_BASHCOMPLETION ?= yes
|
||||||
|
WITH_WGQUICK ?= yes
|
||||||
|
|
||||||
CFLAGS ?= -O3
|
CFLAGS ?= -O3
|
||||||
CFLAGS += -std=gnu11
|
CFLAGS += -std=gnu11
|
||||||
|
@ -30,6 +31,9 @@ install: wg
|
||||||
@install -v -d "$(DESTDIR)$(BINDIR)" && install -m 0755 -v wg "$(DESTDIR)$(BINDIR)/wg"
|
@install -v -d "$(DESTDIR)$(BINDIR)" && install -m 0755 -v wg "$(DESTDIR)$(BINDIR)/wg"
|
||||||
@install -v -d "$(DESTDIR)$(MANDIR)/man8" && install -m 0644 -v wg.8 "$(DESTDIR)$(MANDIR)/man8/wg.8"
|
@install -v -d "$(DESTDIR)$(MANDIR)/man8" && install -m 0644 -v wg.8 "$(DESTDIR)$(MANDIR)/man8/wg.8"
|
||||||
@[ "$(WITH_BASHCOMPLETION)" = "yes" ] && install -v -d "$(BASHCOMPDIR)" && install -m 0644 -v completion/wg.bash-completion "$(DESTDIR)$(BASHCOMPDIR)/wg"
|
@[ "$(WITH_BASHCOMPLETION)" = "yes" ] && install -v -d "$(BASHCOMPDIR)" && install -m 0644 -v completion/wg.bash-completion "$(DESTDIR)$(BASHCOMPDIR)/wg"
|
||||||
|
@[ "$(WITH_WGQUICK)" = "yes" ] && install -m 0755 -v wg-quick.bash "$(DESTDIR)$(BINDIR)/wg-quick"
|
||||||
|
@[ "$(WITH_WGQUICK)" = "yes" ] && install -m 0644 -v wg-quick.8 "$(DESTDIR)$(MANDIR)/man8/wg-quick.8"
|
||||||
|
@[ "$(WITH_WGQUICK)" = "yes" -a "$(WITH_BASHCOMPLETION)" = "yes" ] && install -m 0644 -v completion/wg-quick.bash-completion "$(DESTDIR)$(BASHCOMPDIR)/wg-quick"
|
||||||
|
|
||||||
check: clean
|
check: clean
|
||||||
CFLAGS=-g scan-build --view --keep-going $(MAKE) wg
|
CFLAGS=-g scan-build --view --keep-going $(MAKE) wg
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
# Copyright (C) 2017 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||||
|
|
||||||
|
_wg_quick_completion() {
|
||||||
|
local i
|
||||||
|
if [[ $COMP_CWORD -eq 1 ]]; then
|
||||||
|
COMPREPLY+=( $(compgen -W "up down" -- "${COMP_WORDS[1]}") )
|
||||||
|
return
|
||||||
|
elif [[ $COMP_CWORD -eq 2 ]]; then
|
||||||
|
local old_glob="$(shopt -p nullglob)"
|
||||||
|
shopt -s nullglob
|
||||||
|
for i in /etc/wireguard/*.conf; do
|
||||||
|
i="${i##*/}"; i="${i%.conf}"
|
||||||
|
COMPREPLY+=( $(compgen -W "$i" -- "${COMP_WORDS[2]}") )
|
||||||
|
done
|
||||||
|
eval "$old_glob"
|
||||||
|
COMPREPLY+=( $(compgen -f -X '!*.conf' -- "${COMP_WORDS[2]}") $(compgen -d -- "${COMP_WORDS[2]}") )
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
complete -o filenames -o nosort -F _wg_quick_completion wg-quick
|
|
@ -0,0 +1,194 @@
|
||||||
|
.TH WG-QUICK 8 "2016 January 1" ZX2C4 "WireGuard"
|
||||||
|
|
||||||
|
.SH NAME
|
||||||
|
wg-quick - set up a WireGuard interface simply
|
||||||
|
|
||||||
|
.SH SYNOPSIS
|
||||||
|
.B wg-quick
|
||||||
|
[
|
||||||
|
.I up
|
||||||
|
|
|
||||||
|
.I down
|
||||||
|
] [
|
||||||
|
.I CONFIG_FILE
|
||||||
|
|
|
||||||
|
.I INTERFACE
|
||||||
|
]
|
||||||
|
|
||||||
|
.SH DESCRIPTION
|
||||||
|
|
||||||
|
This is an extremely simple script for easily bringing up a WireGuard interface,
|
||||||
|
suitable for a few common use cases.
|
||||||
|
|
||||||
|
Use \fIup\fP to add and set up an interface, and use \fIdown\fP to tear down and remove
|
||||||
|
an interface. Running \fIup\fP adds a WireGuard interface, brings up the interface with the
|
||||||
|
supplied IP addresses, sets up routes, and optionally runs pre/post up scripts. Running \fIdown\fP
|
||||||
|
optionally saves the current configuration, removes the WireGuard interface, and optionally
|
||||||
|
runs pre/post down scripts.
|
||||||
|
|
||||||
|
\fICONFIG_FILE\fP is a configuration file, whose filename is the interface name
|
||||||
|
followed by `.conf'. Otherwise, \fIINTERFACE\fP is an interface name, with configuration
|
||||||
|
found at `/etc/wireguard/\fIINTERFACE\fP.conf'.
|
||||||
|
|
||||||
|
Generally speaking, this utility is just a simple script that wraps invocations to
|
||||||
|
.BR wg (8)
|
||||||
|
and
|
||||||
|
.BR ip (8)
|
||||||
|
in order to set up a WireGuard interface. It is designed for users with simple
|
||||||
|
needs, and users with more advanced needs are highly encouraged to use a more
|
||||||
|
specific tool, a more complete network manager, or otherwise just use
|
||||||
|
.BR wg (8)
|
||||||
|
and
|
||||||
|
.BR ip (8),
|
||||||
|
as usual.
|
||||||
|
|
||||||
|
.SH CONFIGURATION
|
||||||
|
|
||||||
|
The configuration file adds a few extra configuration values to the format understood by
|
||||||
|
.BR wg (8)
|
||||||
|
in order to configure additional attribute of an interface. It handles the
|
||||||
|
values that it understands, and then it passes the remaining ones directly to
|
||||||
|
.BR wg (8)
|
||||||
|
for further processing.
|
||||||
|
|
||||||
|
It infers all routes from the list of peers' allowed IPs, and automatically adds
|
||||||
|
them to the system routing table. If one of those routes is the default route
|
||||||
|
(0.0.0.0/0 or ::/0), then it uses
|
||||||
|
.BR ip-rule (8)
|
||||||
|
to handle overriding of the default gateway.
|
||||||
|
|
||||||
|
The configuration file will be passed directly to \fBwg\fP(8)'s `setconf'
|
||||||
|
sub-command, with the exception of the following additions to the \fIInterface\fP section,
|
||||||
|
which are handled by this tool:
|
||||||
|
|
||||||
|
.IP \(bu
|
||||||
|
Address \(em a comma-separated list of ip (v4 or v6) addresses (optionally with CIDR masks)
|
||||||
|
to be assigned to the interface. May be specified multiple times.
|
||||||
|
.IP \(bu
|
||||||
|
PreUp, PostUp, PreDown, PostDown \(em script snippets which will be executed by
|
||||||
|
.BR bash (1)
|
||||||
|
before/after setting up/tearing down the interface, most commonly used
|
||||||
|
to configure DNS. The special string `%i' is expanded to \fIINTERFACE\fP.
|
||||||
|
.IP \(bu
|
||||||
|
SaveConfig \(em if set to `true', the configuration is saved from the current state of the
|
||||||
|
interface upon shutdown.
|
||||||
|
|
||||||
|
.P
|
||||||
|
Recommended \fIINTERFACE\fP names include `wg0' or `wgvpn0' or even `wgmgmtlan0'.
|
||||||
|
However, the number at the end is in fact optional, and really
|
||||||
|
any free-form string [a-zA-Z0-9_=+.-]{1,16} will work. So even interface names corresponding
|
||||||
|
to geographic locations would suffice, such as `cincinnati', `nyc', or `paris', if that's
|
||||||
|
somehow desirable.
|
||||||
|
|
||||||
|
.SH EXAMPLES
|
||||||
|
|
||||||
|
These examples draw on the same syntax found for
|
||||||
|
.BR wg (8),
|
||||||
|
and a more complete description may be found there. Bold lines below are for options that extend
|
||||||
|
.BR wg (8).
|
||||||
|
|
||||||
|
The following might be used for connecting as a client to a VPN gateway for tunneling all
|
||||||
|
traffic:
|
||||||
|
|
||||||
|
[Interface]
|
||||||
|
.br
|
||||||
|
\fBAddress = 10.200.100.8/24\fP
|
||||||
|
.br
|
||||||
|
\fBPostUp = echo nameserver 10.200.100.1 | resolvconf -a %i -m 0\fP
|
||||||
|
.br
|
||||||
|
\fBPostDown = resolvconf -d %i\fP
|
||||||
|
.br
|
||||||
|
PrivateKey = oK56DE9Ue9zK76rAc8pBl6opph+1v36lm7cXXsQKrQM=
|
||||||
|
.br
|
||||||
|
PresharedKey = /UwcSPg38hW/D9Y3tcS1FOV0K1wuURMbS0sesJEP5ak=
|
||||||
|
.br
|
||||||
|
|
||||||
|
.br
|
||||||
|
[Peer]
|
||||||
|
.br
|
||||||
|
PublicKey = GtL7fZc/bLnqZldpVofMCD6hDjrK28SsdLxevJ+qtKU=
|
||||||
|
.br
|
||||||
|
AllowedIPs = 0.0.0.0/0
|
||||||
|
.br
|
||||||
|
Endpoint = demo.wireguard.io:51820
|
||||||
|
.br
|
||||||
|
|
||||||
|
Notice that the `PostUp` and `PostDown` commands are used here to configure DNS using
|
||||||
|
.BR resolvconf (8),
|
||||||
|
which is one of the many options for DNS configuration. The `Address` field is added
|
||||||
|
here in order to set up the address for the interface. The peer's allowed IPs entry
|
||||||
|
implies that this interface should be configured as the default gateway, which this
|
||||||
|
script does.
|
||||||
|
|
||||||
|
Here is a more complicated example, fit for usage on a server:
|
||||||
|
|
||||||
|
[Interface]
|
||||||
|
.br
|
||||||
|
\fBAddress = 10.192.122.1/24\fP
|
||||||
|
.br
|
||||||
|
\fBAddress = 10.10.0.1/16\fP
|
||||||
|
.br
|
||||||
|
\fBSaveConfig = true\fP
|
||||||
|
.br
|
||||||
|
PrivateKey = yAnz5TF+lXXJte14tji3zlMNq+hd2rYUIgJBgB3fBmk=
|
||||||
|
.br
|
||||||
|
ListenPort = 41414
|
||||||
|
.br
|
||||||
|
|
||||||
|
.br
|
||||||
|
[Peer]
|
||||||
|
.br
|
||||||
|
PublicKey = xTIBA5rboUvnH4htodjb6e697QjLERt1NAB4mZqp8Dg=
|
||||||
|
.br
|
||||||
|
AllowedIPs = 10.192.122.3/32, 10.192.124.1/24
|
||||||
|
.br
|
||||||
|
|
||||||
|
.br
|
||||||
|
[Peer]
|
||||||
|
.br
|
||||||
|
PublicKey = TrMvSoP4jYQlY6RIzBgbssQqY3vxI2Pi+y71lOWWXX0=
|
||||||
|
.br
|
||||||
|
AllowedIPs = 10.192.122.4/32, 192.168.0.0/16
|
||||||
|
.br
|
||||||
|
|
||||||
|
.br
|
||||||
|
[Peer]
|
||||||
|
.br
|
||||||
|
PublicKey = gN65BkIKy1eCE9pP1wdc8ROUtkHLF2PfAqYdyYBz6EA=
|
||||||
|
.br
|
||||||
|
AllowedIPs = 10.10.10.230/32
|
||||||
|
|
||||||
|
Notice the two `Address' lines at the top, and that `SaveConfig' is set to `true', indicating
|
||||||
|
that the configuration file should be saved on shutdown using the current status of the
|
||||||
|
interface.
|
||||||
|
|
||||||
|
These configuration files may be placed in any directory, putting the desired interface name
|
||||||
|
in the filename:
|
||||||
|
|
||||||
|
\fB # wg-quick up /path/to/wgnet0.conf\fP
|
||||||
|
|
||||||
|
For convienence, if only an interface name is supplied, it automatically chooses a path in
|
||||||
|
`/etc/wireguard/':
|
||||||
|
|
||||||
|
\fB # wg-quick up wgnet0\fP
|
||||||
|
|
||||||
|
This will load the configuration file `/etc/wireguard/wgnet0.conf'.
|
||||||
|
|
||||||
|
.SH SEE ALSO
|
||||||
|
.BR wg (8),
|
||||||
|
.BR ip (8),
|
||||||
|
.BR ip-link (8),
|
||||||
|
.BR ip-address (8),
|
||||||
|
.BR ip-route (8),
|
||||||
|
.BR ip-rule (8).
|
||||||
|
|
||||||
|
.SH AUTHOR
|
||||||
|
.B wg-quick
|
||||||
|
was written by
|
||||||
|
.MT Jason@zx2c4.com
|
||||||
|
Jason A. Donenfeld
|
||||||
|
.ME .
|
||||||
|
For updates and more information, a project page is available on the
|
||||||
|
.UR https://\:www.wireguard.io/
|
||||||
|
World Wide Web
|
||||||
|
.UE .
|
|
@ -0,0 +1,209 @@
|
||||||
|
#!/bin/bash
|
||||||
|
#
|
||||||
|
# Copyright (c) 2016 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||||
|
#
|
||||||
|
|
||||||
|
set -e -o pipefail
|
||||||
|
shopt -s extglob
|
||||||
|
|
||||||
|
SELF="$(readlink -f "${BASH_SOURCE[0]}")"
|
||||||
|
export PATH="${SELF%/*}:$PATH"
|
||||||
|
|
||||||
|
WG_CONFIG=""
|
||||||
|
INTERFACE=""
|
||||||
|
ADDRESSES=( )
|
||||||
|
PRE_UP=""
|
||||||
|
POST_UP=""
|
||||||
|
PRE_DOWN=""
|
||||||
|
POST_DOWN=""
|
||||||
|
SAVE_CONFIG=0
|
||||||
|
CONFIG_FILE=""
|
||||||
|
PROGRAM="${0##*/}"
|
||||||
|
ARGS=( "$@" )
|
||||||
|
|
||||||
|
parse_options() {
|
||||||
|
local interface_section=0 line key value
|
||||||
|
CONFIG_FILE="$1"
|
||||||
|
[[ $CONFIG_FILE =~ ^[a-zA-Z0-9_=+.-]{1,16}$ ]] && CONFIG_FILE="/etc/wireguard/$CONFIG_FILE.conf"
|
||||||
|
[[ -e $CONFIG_FILE ]] || die "\`$CONFIG_FILE' does not exist"
|
||||||
|
[[ $CONFIG_FILE =~ /?([a-zA-Z0-9_=+.-]{1,16})\.conf$ ]] || die "The config file must be a valid interface name, followed by .conf"
|
||||||
|
INTERFACE="${BASH_REMATCH[1]}"
|
||||||
|
shopt -s nocasematch
|
||||||
|
while read -r line; do
|
||||||
|
key="${line%%=*}"; key="${key##*( )}"; key="${key%%*( )}"
|
||||||
|
value="${line#*=}"; value="${value##*( )}"; value="${value%%*( )}"
|
||||||
|
[[ $key == "["* ]] && interface_section=0
|
||||||
|
[[ $key == "[Interface]" ]] && interface_section=1
|
||||||
|
if [[ $interface_section -eq 1 ]]; then
|
||||||
|
case "$key" in
|
||||||
|
Address) ADDRESSES+=( ${value//,/ } ); continue ;;
|
||||||
|
PreUp) PRE_UP="$value"; continue ;;
|
||||||
|
PreDown) PRE_DOWN="$value"; continue ;;
|
||||||
|
PostUp) POST_UP="$value"; continue ;;
|
||||||
|
PostDown) POST_DOWN="$value"; continue ;;
|
||||||
|
SaveConfig) read_bool SAVE_CONFIG "$value"; continue ;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
WG_CONFIG+="$line"$'\n'
|
||||||
|
done < "$CONFIG_FILE"
|
||||||
|
shopt -u nocasematch
|
||||||
|
}
|
||||||
|
|
||||||
|
read_bool() {
|
||||||
|
local -n out="$1"
|
||||||
|
case "$2" in
|
||||||
|
true) out=1 ;;
|
||||||
|
false) out=0 ;;
|
||||||
|
*) die "\`$2' is neither true nor false"
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd() {
|
||||||
|
echo "[#] $*" >&2
|
||||||
|
"$@"
|
||||||
|
}
|
||||||
|
|
||||||
|
die() {
|
||||||
|
echo "$PROGRAM: $*" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
auto_su() {
|
||||||
|
[[ $UID == 0 ]] || exec sudo -p "$PROGRAM must be run as root. Please enter the password for %u to continue: " "$SELF" "${ARGS[@]}"
|
||||||
|
}
|
||||||
|
|
||||||
|
add_if() {
|
||||||
|
cmd ip link add "$INTERFACE" type wireguard
|
||||||
|
}
|
||||||
|
|
||||||
|
del_if() {
|
||||||
|
if [[ $(ip route show table all) =~ .*\ dev\ $INTERFACE\ table\ ([0-9]+)\ .* ]]; then
|
||||||
|
cmd ip rule delete table "${BASH_REMATCH[1]}"
|
||||||
|
cmd ip rule delete table main suppress_prefixlength 0 2>/dev/null
|
||||||
|
fi
|
||||||
|
cmd ip link delete dev "$INTERFACE"
|
||||||
|
}
|
||||||
|
|
||||||
|
up_if() {
|
||||||
|
cmd ip link set "$INTERFACE" up
|
||||||
|
}
|
||||||
|
|
||||||
|
add_addr() {
|
||||||
|
cmd ip address add "$1" dev "$INTERFACE"
|
||||||
|
}
|
||||||
|
|
||||||
|
add_route() {
|
||||||
|
if [[ $1 == 0.0.0.0/0 || $1 == ::/0 ]]; then
|
||||||
|
add_default "$1"
|
||||||
|
else
|
||||||
|
cmd ip route add "$1" dev "$INTERFACE"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
add_default() {
|
||||||
|
[[ $(join <(wg show "$INTERFACE" allowed-ips) <(wg show "$INTERFACE" endpoints)) =~ .*\ ${1//./\\.}\ ([0-9.:a-f]+):[0-9]+$ ]] && local endpoint="${BASH_REMATCH[1]}"
|
||||||
|
[[ -n $endpoint ]] || return 0
|
||||||
|
local table=51820
|
||||||
|
while [[ -n $(ip route show table $table) ]]; do ((table++)); done
|
||||||
|
cmd ip route add "$1" dev "$INTERFACE" table $table
|
||||||
|
cmd ip rule add not to "$endpoint" table $table
|
||||||
|
cmd ip rule add table main suppress_prefixlength 0
|
||||||
|
}
|
||||||
|
|
||||||
|
set_config() {
|
||||||
|
cmd wg setconf "$INTERFACE" <(echo "$WG_CONFIG")
|
||||||
|
}
|
||||||
|
|
||||||
|
save_config() {
|
||||||
|
local old_umask new_config current_config address
|
||||||
|
[[ $(ip -all -brief address show dev "$INTERFACE") =~ ^$INTERFACE\ +\ [A-Z]+\ +(.+)$ ]] || true
|
||||||
|
new_config=$'[Interface]\n'
|
||||||
|
for address in ${BASH_REMATCH[1]}; do
|
||||||
|
new_config+="Address = $address"$'\n'
|
||||||
|
done
|
||||||
|
[[ $SAVE_CONFIG -eq 0 ]] || new_config+=$'SaveConfig = true\n'
|
||||||
|
[[ -z $PRE_UP ]] || new_config+="PreUp = $PRE_UP"$'\n'
|
||||||
|
[[ -z $POST_UP ]] || new_config+="PostUp = $POST_UP"$'\n'
|
||||||
|
[[ -z $PRE_DOWN ]] || new_config+="PreDown = $PRE_DOWN"$'\n'
|
||||||
|
[[ -z $POST_DOWN ]] || new_config+="PostDown = $POST_DOWN"$'\n'
|
||||||
|
old_umask="$(umask)"
|
||||||
|
umask 077
|
||||||
|
current_config="$(cmd wg showconf "$INTERFACE")"
|
||||||
|
trap "rm -f '$CONFIG_FILE.tmp; exit'" INT TERM EXIT
|
||||||
|
echo "${current_config/\[Interface\]$'\n'/$new_config}" > "$CONFIG_FILE.tmp" || die "Could not write configuration file"
|
||||||
|
mv "$CONFIG_FILE.tmp" "$CONFIG_FILE" || die "Could not move configuration file"
|
||||||
|
trap - INT TERM EXIT
|
||||||
|
umask "$old_umask"
|
||||||
|
}
|
||||||
|
|
||||||
|
execute_hook() {
|
||||||
|
[[ -n $1 ]] || return 0
|
||||||
|
local hook="${1//%i/$INTERFACE}"
|
||||||
|
echo "[#] $hook" >&2
|
||||||
|
(eval "$hook")
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd_usage() {
|
||||||
|
cat >&2 <<-_EOF
|
||||||
|
Usage: $PROGRAM [ up | down ] [ CONFIG_FILE | INTERFACE ]
|
||||||
|
|
||||||
|
CONFIG_FILE is a configuration file, whose filename is the interface name
|
||||||
|
followed by \`.conf'. Otherwise, INTERFACE is an interface name, with
|
||||||
|
configuration found at /etc/wireguard/INTERFACE.conf. It is to be readable
|
||||||
|
by wg(8)'s \`setconf' sub-command, with the exception of the following additions
|
||||||
|
to the [Interface] section, which are handled by $PROGRAM:
|
||||||
|
|
||||||
|
- Address: may be specified one or more times and contains one or more
|
||||||
|
IP addresses (with an optional CIDR mask) to be set for the interface.
|
||||||
|
- PreUp, PostUp, PreDown, PostDown: script snippets which will be executed
|
||||||
|
by bash(1) at the corresponding phases of the link, most commonly used
|
||||||
|
to configure DNS. The string \`%i' is expanded to INTERFACE.
|
||||||
|
- SaveConfig: if set to \`true', the configuration is saved from the current
|
||||||
|
state of the interface upon shutdown.
|
||||||
|
|
||||||
|
See wg-quick(8) for more info and examples.
|
||||||
|
_EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd_up() {
|
||||||
|
local i
|
||||||
|
[[ -z $(ip link show dev "$INTERFACE" 2>/dev/null) ]] || die "\`$INTERFACE' already exists"
|
||||||
|
trap 'del_if; exit' INT TERM EXIT
|
||||||
|
execute_hook "$PRE_UP"
|
||||||
|
add_if
|
||||||
|
set_config
|
||||||
|
for i in "${ADDRESSES[@]}"; do
|
||||||
|
add_addr "$i"
|
||||||
|
done
|
||||||
|
up_if
|
||||||
|
for i in $(wg show "$INTERFACE" allowed-ips | grep -Po '(?<=[\t ])[0-9.:/a-f]+' | sort -nr -k 2 -t /); do
|
||||||
|
[[ $(ip route get "$i" 2>/dev/null) == *dev\ $INTERFACE\ * ]] || add_route "$i"
|
||||||
|
done
|
||||||
|
execute_hook "$POST_UP"
|
||||||
|
trap - INT TERM EXIT
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd_down() {
|
||||||
|
[[ -n $(ip link show dev "$INTERFACE" type wireguard 2>/dev/null) ]] || die "\`$INTERFACE' is not a WireGuard interface"
|
||||||
|
execute_hook "$PRE_DOWN"
|
||||||
|
[[ $SAVE_CONFIG -eq 0 ]] || save_config
|
||||||
|
del_if
|
||||||
|
execute_hook "$POST_DOWN"
|
||||||
|
}
|
||||||
|
|
||||||
|
if [[ $# -eq 1 && ( $1 == --help || $1 == -h || $1 == help ) ]]; then
|
||||||
|
cmd_usage
|
||||||
|
elif [[ $# -eq 2 && $1 == up ]]; then
|
||||||
|
auto_su
|
||||||
|
parse_options "$2"
|
||||||
|
cmd_up
|
||||||
|
elif [[ $# -eq 2 && $1 == down ]]; then
|
||||||
|
auto_su
|
||||||
|
parse_options "$2"
|
||||||
|
cmd_down
|
||||||
|
else
|
||||||
|
cmd_usage
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
exit 0
|
6
src/wg.8
6
src/wg.8
|
@ -131,11 +131,12 @@ PublicKey \(em a base64 public key calculated by \fIwg pubkey\fP from a
|
||||||
private key, and usually transmitted out of band to the author of the
|
private key, and usually transmitted out of band to the author of the
|
||||||
configuration file. Required.
|
configuration file. Required.
|
||||||
.IP \(bu
|
.IP \(bu
|
||||||
AllowedIPs \(em a comma-separated list of IP (v4 or v6) addresses with
|
AllowedIPs \(em a comma-separated list of ip (v4 or v6) addresses with
|
||||||
CIDR masks from which this peer is allowed to send incoming traffic and
|
CIDR masks from which this peer is allowed to send incoming traffic and
|
||||||
to which outgoing traffic for this peer is directed. The catch-all
|
to which outgoing traffic for this peer is directed. The catch-all
|
||||||
\fI0.0.0.0/0\fP may be specified for matching all IPv4 addresses, and
|
\fI0.0.0.0/0\fP may be specified for matching all IPv4 addresses, and
|
||||||
\fI::/0\fP may be specified for matching all IPv6 addresses. Required.
|
\fI::/0\fP may be specified for matching all IPv6 addresses. May be specified
|
||||||
|
multiple times. Required.
|
||||||
.IP \(bu
|
.IP \(bu
|
||||||
Endpoint \(em an endpoint IP or hostname, followed by a colon, and then a
|
Endpoint \(em an endpoint IP or hostname, followed by a colon, and then a
|
||||||
port number. This endpoint will be updated automatically to the most recent
|
port number. This endpoint will be updated automatically to the most recent
|
||||||
|
@ -152,7 +153,6 @@ when unspecified, this option is off. Most users will not need this. Optional.
|
||||||
|
|
||||||
.SH CONFIGURATION FILE FORMAT EXAMPLE
|
.SH CONFIGURATION FILE FORMAT EXAMPLE
|
||||||
This example may be used as a model for writing configuration files.
|
This example may be used as a model for writing configuration files.
|
||||||
Note that not all keys are required.
|
|
||||||
|
|
||||||
[Interface]
|
[Interface]
|
||||||
.br
|
.br
|
||||||
|
|
Reference in New Issue