]> git.ipfire.org Git - thirdparty/wireguard-tools.git/commitdiff
contrib: add wg-config
authorJason A. Donenfeld <Jason@zx2c4.com>
Thu, 8 Dec 2016 15:13:25 +0000 (16:13 +0100)
committerJason A. Donenfeld <Jason@zx2c4.com>
Fri, 9 Dec 2016 20:31:11 +0000 (21:31 +0100)
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
contrib/wg-config/Makefile [new file with mode: 0644]
contrib/wg-config/README [new file with mode: 0644]
contrib/wg-config/tungate [new file with mode: 0755]
contrib/wg-config/wg-config [new file with mode: 0755]

diff --git a/contrib/wg-config/Makefile b/contrib/wg-config/Makefile
new file mode 100644 (file)
index 0000000..aa81848
--- /dev/null
@@ -0,0 +1,13 @@
+PREFIX ?= /usr
+DESTDIR ?=
+SBINDIR ?= $(PREFIX)/sbin
+
+SCRIPTS := wg-config tungate
+
+all:
+       @echo "These are shell scripts, so there is nothing to do. Try \"make install\" instead."
+
+install:
+       @install -v -m0755 -D -t$(DESTDIR)$(SBINDIR) $(SCRIPTS)
+
+.PHONY: all install
diff --git a/contrib/wg-config/README b/contrib/wg-config/README
new file mode 100644 (file)
index 0000000..2e594c6
--- /dev/null
@@ -0,0 +1,140 @@
+== 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.
+
+
+== Helper Tool ==
+
+tungate is a separate utility, developed originally not explicitly for
+WireGuard, which acts as a poor man's way of ensuring 0/1 and 128/1 default
+route overrides still work with an endpoint going over the original default
+route. It's quite handy, and wg-config makes use of it for dealing with
+0.0.0.0/0 routes. At the moment it only supports IPv4, but adding IPv6
+should be pretty easy.
+
+== Example ==
+
+/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
+
+== Advanced Example ==
+
+/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
+
+== Single File Advanced Example ==
+
+/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
diff --git a/contrib/wg-config/tungate b/contrib/wg-config/tungate
new file mode 100755 (executable)
index 0000000..b537b81
--- /dev/null
@@ -0,0 +1,43 @@
+#!/bin/bash
+
+unwind() {
+       ip route del "$1/32" 2>/dev/null
+       exit
+}
+
+short_route() {
+       ip route | sed -n "s/$1 \\(.* dev [^ ]\\+\\).*/\\1/p" | head -n 1
+}
+
+resolve_ip() {
+       local filter='/^[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}$/{p;q;}'
+       [[ $(sed -n "$filter" <<<"$1") == "$1" ]] && echo "$1" ||
+               host -t a "$1" | cut -d ' ' -f 4 | sed -n "$filter"
+}
+
+set_route() {
+       local default_route="$(short_route default)"
+       [[ -n $default_route ]] || { echo "[-] Could not determine default route"; return; }
+       echo "[+] Adding route for $1 to be $default_route"
+       ip route del "$1/32" 2>/dev/null
+       ip route add "$1/32" $default_route
+}
+
+check_loop() {
+       local ip="$(resolve_ip "$1")"
+       [[ -n $ip ]] || { echo "[-] Could not determine IP of '$1'" && return 1; }
+       echo "[+] Making sure $ip goes over the default (non-0/1,128/1) route"
+
+       set_route "$ip"
+       trap unwind INT TERM EXIT
+
+       while read; do
+               [[ $(short_route "$ip") != "$(short_route default)" ]] && set_route "$ip"
+       done < <(exec ip monitor route)
+}
+
+[[ $# -ne 1 ]] && { echo "Usage: $0 IP|HOST" >&2; exit 1; }
+[[ $UID -ne 0 ]] && exec sudo "$(readlink -f "$0")" "$@"
+
+check_loop "$1"
+exit $?
diff --git a/contrib/wg-config/wg-config b/contrib/wg-config/wg-config
new file mode 100755 (executable)
index 0000000..eaa45f2
--- /dev/null
@@ -0,0 +1,187 @@
+#!/bin/bash
+set -e -o pipefail
+
+export PATH="$(dirname "$(readlink -f "$0")"):$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: " "$(readlink -f "$0")" "${ARGS[@]}" || true
+}
+
+unwind() {
+       set +e
+       [[ -n $INTERFACE && -n $(ip link show dev "$INTERFACE" type wireguard 2>/dev/null) ]] && cmd ip link delete dev "$INTERFACE"
+       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; }
+       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() {
+       cmd ip route add "$1" dev "$INTERFACE"
+}
+
+add_default() {
+       if [[ $1 == ::/0 ]]; then
+               echo "tungate: does not yet support IPv6, skipping ::/0" >&2
+               return 0
+       elif [[ $1 == 0.0.0.0/0 ]]; then
+               local endpoint="$(wg show "$INTERFACE" endpoints | grep "^$(wg show "$INTERFACE" allowed-ips | grep 0.0.0.0/0 | head -n 1 | cut -f 1)" | cut -f 2 | cut -d : -f 1)"
+               add_route 0/1
+               add_route 128/1
+               killall tungate 2>/dev/null || true
+               echo "[&] Forking \`tungate' for $endpoint to background" >&2
+               tungate "$endpoint" >/dev/null 2>&1 & disown
+               return 0
+       fi
+       return 1
+}
+
+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 || true
+       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 | cut -f 2 | tr -d ,); do
+                       if ! add_default "$i" && [[ $(ip route get "$i") != *dev\ $INTERFACE\ * ]]; then
+                               add_route "$i"
+                       fi
+               done
+       fi
+       for i in "${ADDITIONAL_ROUTES[@]}"; do
+               if ! add_default "$i"; then
+                       add_route "$i"
+               fi
+       done
+       [[ $(type -t post_add) == function ]] && post_add || true
+       trap - INT TERM EXIT
+}
+
+cmd_del() {
+       auto_su
+       [[ $(type -t pre_del) == function ]] && pre_del || true
+       killall tungate 2>/dev/null || true
+       [[ -n $CONFIG_FILE ]] && save_config
+       del_if
+        [[ $(type -t post_del) == function ]] && post_del || true
+}
+
+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