Add wg-setup.sh to help with setting up wireguard in p2p mode
authorJacob Lifshay <programmerjake@gmail.com>
Wed, 8 Jun 2022 06:56:11 +0000 (23:56 -0700)
committerJacob Lifshay <programmerjake@gmail.com>
Wed, 8 Jun 2022 06:56:11 +0000 (23:56 -0700)
wg-setup.sh [new file with mode: 0755]

diff --git a/wg-setup.sh b/wg-setup.sh
new file mode 100755 (executable)
index 0000000..299f5c0
--- /dev/null
@@ -0,0 +1,210 @@
+#!/bin/bash
+set -e
+umask 077
+
+function fail()
+{
+    printf "error: $@" >&2
+    echo >&2
+    exit 1
+}
+
+any_warnings=0
+
+function warn()
+{
+    any_warnings=1
+    printf "warning: $@" >&2
+    echo >&2
+}
+
+function install_deps()
+{
+    if ! which wg >/dev/null; then
+        echo "wireguard not found, installing:"
+        sudo apt update
+        sudo apt install wireguard
+    fi
+}
+
+function check_cidr_addr()
+{
+    local addr="$1"
+    [[ "$addr" =~ ^(0|[1-9][0-9]*)'.'(0|[1-9][0-9]*)'.'(0|[1-9][0-9]*)'.'(0|[1-9][0-9]*)'/'(0|[1-9][0-9]*)$ ]] \
+        || fail "can't parse IPv4 CIDR address: %q" "$addr"
+    ((${BASH_REMATCH[1]} <= 255 &&
+        ${BASH_REMATCH[2]} <= 255 &&
+        ${BASH_REMATCH[3]} <= 255 &&
+        ${BASH_REMATCH[4]} <= 255 &&
+        ${BASH_REMATCH[5]} <= 32)) \
+        || fail "invalid IPv4 CIDR address: %s" "$addr"
+}
+
+function init_server()
+{
+    local server_iface_name="$1" listen_port="$2" server_iface_addr="$3"
+    check_cidr_addr "$server_iface_addr"
+    set -o noclobber
+    local private_key="$(wg genkey)"
+    local config_contents="[Interface]
+PrivateKey = $private_key
+ListenPort = $listen_port
+Address = $server_iface_addr
+SaveConfig = true
+"
+    echo "$config_contents" > "/etc/wireguard/$server_iface_name.conf"
+    echo "Created config for $server_iface_name"
+    echo "You need to allow UDP port $listen_port through your firewall"
+}
+
+function add_client()
+{
+    local server_iface_name="$1" server_public_addr="$2" client_config="$3" client_iface_addr="$4"
+    check_cidr_addr "$client_iface_addr"
+    if [[ " $(wg show interfaces) " =~ " $server_iface_name " ]]; then
+        fail "You need to shutdown interface %s first:\nwg-quick down %s\nor:\nsystemctl stop wg-quick@%s" \
+            "$server_iface_name" "$server_iface_name" "$server_iface_name"
+    fi
+    local server_iface_conf="/etc/wireguard/$server_iface_name.conf"
+    local lines line key eq_value value section="" server_private_key=""
+    local server_iface_addr="" server_listen_port=""
+    mapfile -t lines < "$server_iface_conf"
+    for line in "${lines[@]}"; do
+        line="${line%%#*}" # remove comments
+        [[ "$line" =~ ^([^=]*)('='(.*))?$ ]] || fail "regex failed -- not supposed to happen"
+        key="${BASH_REMATCH[1]}"
+        eq_value="${BASH_REMATCH[2]}"
+        value="${BASH_REMATCH[3]}"
+        key="${key#"${key%%[![:space:]]*}"}" # remove leading whitespace
+        key="${key%"${key##*[![:space:]]}"}" # remove trailing whitespace
+        value="${value#"${value%%[![:space:]]*}"}" # remove leading whitespace
+        value="${value%"${value##*[![:space:]]}"}" # remove trailing whitespace
+        [[ "$key" == "" && "$eq_value" == "" ]] && continue
+        if [[ "$key" =~ ^'['(.+)']'$ && "$eq_value" == "" ]]; then
+            section="${BASH_REMATCH[1]}"
+            case "$section" in
+                'Interface'|'Peer')
+                    ;;
+                *)
+                    warn "unknown config section %s" "$key"
+                    ;;
+            esac
+        elif [[ "$section" == "Interface" ]]; then
+            case "$key" in
+                'PrivateKey')
+                    [[ "$value" == "" ]] && fail "empty [Interface] PrivateKey value"
+                    server_private_key="$value"
+                    ;;
+                'Address')
+                    [[ "$value" == "" ]] && fail "empty [Interface] Address value"
+                    [[ "$server_iface_addr" != ""
+                        || "$value" =~ [[:space:],] ]] && \
+                        fail "multiple [Interface] Address values not supported"
+                    server_iface_addr="$value"
+                    check_cidr_addr "$server_iface_addr"
+                    ;;
+                'ListenPort')
+                    [[ "$value" =~ ^[1-9][0-9]* ]] || \
+                        fail "invalid [Interface] ListenPort value: %s" "$value"
+                    server_listen_port="$value"
+                    ;;
+                'SaveConfig'|'FwMark'|'DNS'|'MTU')
+                    ;;
+                'Table'|'PreUp'|'PreDown'|'PostUp'|'PostDown')
+                    ;;
+                *)
+                    warn "unknown config key [Interface] %s" "$key"
+                    ;;
+            esac
+        elif [[ "$section" == "Peer" ]]; then
+            case "$key" in
+                'AllowedIPs'|'PublicKey'|'PresharedKey'|'Endpoint'|'PersistentKeepalive')
+                    ;;
+                *)
+                    warn "unknown config key [Peer] %s" "$key"
+                    ;;
+            esac
+        fi
+    done
+    [[ "$server_iface_addr" != "" ]] || fail "missing [Interface] Address config key"
+    [[ "$server_private_key" != "" ]] || fail "missing [Interface] PrivateKey config key"
+    [[ "$server_listen_port" != "" ]] || fail "missing [Interface] ListenPort config key"
+    if ((any_warnings)); then
+        echo -n "Warnings generated, do you want to continue? [y/N]: " >&2
+        local cont
+        read -r cont
+        if [[ "$cont" != "y" ]]; then
+            exit 1
+        fi
+    fi
+    local client_private_key="$(wg genkey)"
+    local server_public_key="$(wg pubkey <<<"$server_private_key")"
+    local client_public_key="$(wg pubkey <<<"$client_private_key")"
+    local preshared_key="$(wg genpsk)"
+    local client_config_contents="[Interface]
+PrivateKey = $client_private_key
+Address = $client_iface_addr
+SaveConfig = true
+
+[Peer]
+PublicKey = $server_public_key
+PresharedKey = $preshared_key
+AllowedIPs = $server_iface_addr
+Endpoint = $server_public_addr:$server_listen_port
+PersistentKeepalive = 25
+"
+    set -o noclobber
+    echo "$client_config_contents" > "$client_config"
+    set +o noclobber
+    local server_config_new_peer="
+[Peer]
+PublicKey = $client_public_key
+PresharedKey = $preshared_key
+AllowedIPs = $client_iface_addr
+PersistentKeepalive = 25
+"
+    echo "$server_config_new_peer" >> "$server_iface_conf"
+    cat <<EOF
+Client added.
+Move $client_config to /etc/wireguard/<client-interface>.conf on the client,
+making sure that it is owned by root and has mode 600. Make sure it is NOT
+left lying around since it contains the private key for the client, as well
+as the preshared key.
+
+Once you did that, run on the server:
+wg-quick up $server_iface_name
+or:
+systemctl start wg-quick@$server_iface_name
+and run on the client:
+wg-quick up <client-interface>
+or:
+systemctl start wg-quick@<client-interface>
+EOF
+}
+
+case "$1" in
+    init-server)
+        install_deps
+        init_server "${@:2}"
+        ;;
+    add-client)
+        install_deps
+        add_client "${@:2}"
+        ;;
+    *)
+        cat >&2 <<EOF
+Usage: $0 init-server <server-iface-name> <listen-port> <server-iface-addr>
+   or: $0 add-client <server-iface-name> <server-public-addr> <client-config.conf> <client-iface-addr>
+
+init-server: create a new wireguard config for the server, writes to
+    '/etc/wireguard/<server-iface-name>.conf'
+
+add-client: add a client to the wireguard config for the server at
+    '/etc/wireguard/<server-iface-name>.conf'
+    Writes the generated client config to <client-config.conf>.
+    The client will connect to the server through
+    public IP or DNS address <server-public-addr>.
+EOF
+        exit 1
+    ;;
+esac