#!/bin/bash set -e -o pipefail shopt -s extglob CONFIG_FILE="" WG_CONFIG="" INTERFACE="" NETNS="" ADDRESSES=( ) MTU="" DNS=( ) DNS_SEARCH=( ) die() { echo "$0: $*" >&2 exit 1 } parse_options() { local interface_section=0 line key value stripped v CONFIG_FILE="$1" [[ $CONFIG_FILE =~ ^[a-zA-Z0-9_=+.-]{1,15}$ ]] && CONFIG_FILE="/etc/wireguard/$CONFIG_FILE.conf" [[ -e $CONFIG_FILE ]] || die "\`$CONFIG_FILE' does not exist" [[ $CONFIG_FILE =~ (^|/)([a-zA-Z0-9_=+.-]{1,15})\.conf$ ]] || die "The config file must be a valid interface name, followed by .conf" CONFIG_FILE="$(readlink -f "$CONFIG_FILE")" ((($(stat -c '0%#a' "$CONFIG_FILE") & $(stat -c '0%#a' "${CONFIG_FILE%/*}") & 0007) == 0)) || echo "Warning: \`$CONFIG_FILE' is world accessible" >&2 INTERFACE="${BASH_REMATCH[2]}" NETNS="$INTERFACE" shopt -s nocasematch while read -r line || [[ -n $line ]]; do stripped="${line%%\#*}" key="${stripped%%=*}"; key="${key##*([[:space:]])}"; key="${key%%*([[:space:]])}" value="${stripped#*=}"; value="${value##*([[:space:]])}"; value="${value%%*([[:space:]])}" [[ $key == "["* ]] && interface_section=0 [[ $key == "[Interface]" ]] && interface_section=1 if [[ $interface_section -eq 1 ]]; then case "$key" in # Allow user to additionally specify interface name # (if unspecified, use filename, see use of BASH_REMATCH above) Name) INTERFACE="$value" continue ;; # Allow use to specify netns # (if unspecified, use $INTERFACE) NetNS) NETNS="$value" continue ;; Address) ADDRESSES+=( ${value//,/ } ) continue ;; MTU) MTU="$value" continue ;; DNS) for v in ${value//,/ }; do [[ $v =~ (^[0-9.]+$)|(^.*:.*$) ]] && DNS+=( $v ) || DNS_SEARCH+=( $v ) done continue ;; esac fi WG_CONFIG+="$line"$'\n' done < "$CONFIG_FILE" shopt -u nocasematch } add_addr() { local proto=-4 [[ $1 == *:* ]] && proto=-6 ip -n "$NETNS" $proto address add "$1" dev "$INTERFACE" } up() { ip netns add "$NETNS" ip link add "$INTERFACE" type wireguard ip link set "$INTERFACE" netns "$NETNS" ip netns exec "$NETNS" wg setconf "$INTERFACE" <(echo "$WG_CONFIG") for i in "${ADDRESSES[@]}"; do add_addr "$i" done if [[ -n $MTU ]]; then ip -n "$NETNS" link set mtu "$MTU" up dev "$INTERFACE" fi ip -n "$NETNS" link set lo up ip -n "$NETNS" link set "$INTERFACE" up ip -n "$NETNS" route add default dev "$INTERFACE" mkdir -p "/etc/netns/$NETNS" { printf 'nameserver %s\n' "${DNS[@]}" [[ ${#DNS_SEARCH[@]} -eq 0 ]] || printf 'search %s\n' "${DNS_SEARCH[*]}" } | tee "/etc/netns/$NETNS/resolv.conf" > /dev/null } down() { ip -n "$NETNS" link del "$INTERFACE" ip netns del "$NETNS" rm -rf "/etc/netns/$NETNS" } COMMAND="$1" parse_options "$2" echo "interface: $INTERFACE" echo "netns: $NETNS" case "$COMMAND" in up) up "$@" ;; down) down "$@" ;; *) echo "Usage: $0 up|down" >&2; exit 1 ;; esac