summaryrefslogtreecommitdiff
path: root/wg-reallyquick
blob: e3f50937160c5c6b04045d16c5f2f54f46902373 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
#!/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