add dump-option for milestones in JSON format
[utils.git] / wg-setup.sh
1 #!/bin/bash
2 set -e
3 umask 077
4
5 function fail()
6 {
7 printf "error: $@" >&2
8 echo >&2
9 exit 1
10 }
11
12 any_warnings=0
13
14 function warn()
15 {
16 any_warnings=1
17 printf "warning: $@" >&2
18 echo >&2
19 }
20
21 function install_deps()
22 {
23 if ! which wg >/dev/null; then
24 echo "wireguard not found, installing:"
25 sudo apt update
26 sudo apt install wireguard
27 fi
28 }
29
30 function check_cidr_addr()
31 {
32 local addr="$1"
33 [[ "$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]*)$ ]] \
34 || fail "can't parse IPv4 CIDR address: %q" "$addr"
35 ((${BASH_REMATCH[1]} <= 255 &&
36 ${BASH_REMATCH[2]} <= 255 &&
37 ${BASH_REMATCH[3]} <= 255 &&
38 ${BASH_REMATCH[4]} <= 255 &&
39 ${BASH_REMATCH[5]} <= 32)) \
40 || fail "invalid IPv4 CIDR address: %s" "$addr"
41 }
42
43 function init_server()
44 {
45 local server_iface_name="$1" listen_port="$2" server_iface_addr="$3"
46 check_cidr_addr "$server_iface_addr"
47 set -o noclobber
48 local private_key="$(wg genkey)"
49 local config_contents="[Interface]
50 PrivateKey = $private_key
51 ListenPort = $listen_port
52 Address = $server_iface_addr
53 SaveConfig = true
54 "
55 echo "$config_contents" > "/etc/wireguard/$server_iface_name.conf"
56 echo "Created config for $server_iface_name"
57 echo "You need to allow UDP port $listen_port through your firewall"
58 }
59
60 function add_client()
61 {
62 local server_iface_name="$1" server_public_addr="$2" client_config="$3" client_iface_addr="$4"
63 check_cidr_addr "$client_iface_addr"
64 if [[ " $(wg show interfaces) " =~ " $server_iface_name " ]]; then
65 fail "You need to shutdown interface %s first:\nwg-quick down %s\nor:\nsystemctl stop wg-quick@%s" \
66 "$server_iface_name" "$server_iface_name" "$server_iface_name"
67 fi
68 local server_iface_conf="/etc/wireguard/$server_iface_name.conf"
69 local lines line key eq_value value section="" server_private_key=""
70 local server_iface_addr="" server_listen_port=""
71 mapfile -t lines < "$server_iface_conf"
72 for line in "${lines[@]}"; do
73 line="${line%%#*}" # remove comments
74 [[ "$line" =~ ^([^=]*)('='(.*))?$ ]] || fail "regex failed -- not supposed to happen"
75 key="${BASH_REMATCH[1]}"
76 eq_value="${BASH_REMATCH[2]}"
77 value="${BASH_REMATCH[3]}"
78 key="${key#"${key%%[![:space:]]*}"}" # remove leading whitespace
79 key="${key%"${key##*[![:space:]]}"}" # remove trailing whitespace
80 value="${value#"${value%%[![:space:]]*}"}" # remove leading whitespace
81 value="${value%"${value##*[![:space:]]}"}" # remove trailing whitespace
82 [[ "$key" == "" && "$eq_value" == "" ]] && continue
83 if [[ "$key" =~ ^'['(.+)']'$ && "$eq_value" == "" ]]; then
84 section="${BASH_REMATCH[1]}"
85 case "$section" in
86 'Interface'|'Peer')
87 ;;
88 *)
89 warn "unknown config section %s" "$key"
90 ;;
91 esac
92 elif [[ "$section" == "Interface" ]]; then
93 case "$key" in
94 'PrivateKey')
95 [[ "$value" == "" ]] && fail "empty [Interface] PrivateKey value"
96 server_private_key="$value"
97 ;;
98 'Address')
99 [[ "$value" == "" ]] && fail "empty [Interface] Address value"
100 [[ "$server_iface_addr" != ""
101 || "$value" =~ [[:space:],] ]] && \
102 fail "multiple [Interface] Address values not supported"
103 server_iface_addr="$value"
104 check_cidr_addr "$server_iface_addr"
105 ;;
106 'ListenPort')
107 [[ "$value" =~ ^[1-9][0-9]* ]] || \
108 fail "invalid [Interface] ListenPort value: %s" "$value"
109 server_listen_port="$value"
110 ;;
111 'SaveConfig'|'FwMark'|'DNS'|'MTU')
112 ;;
113 'Table'|'PreUp'|'PreDown'|'PostUp'|'PostDown')
114 ;;
115 *)
116 warn "unknown config key [Interface] %s" "$key"
117 ;;
118 esac
119 elif [[ "$section" == "Peer" ]]; then
120 case "$key" in
121 'AllowedIPs'|'PublicKey'|'PresharedKey'|'Endpoint'|'PersistentKeepalive')
122 ;;
123 *)
124 warn "unknown config key [Peer] %s" "$key"
125 ;;
126 esac
127 fi
128 done
129 [[ "$server_iface_addr" != "" ]] || fail "missing [Interface] Address config key"
130 [[ "$server_private_key" != "" ]] || fail "missing [Interface] PrivateKey config key"
131 [[ "$server_listen_port" != "" ]] || fail "missing [Interface] ListenPort config key"
132 if ((any_warnings)); then
133 echo -n "Warnings generated, do you want to continue? [y/N]: " >&2
134 local cont
135 read -r cont
136 if [[ "$cont" != "y" ]]; then
137 exit 1
138 fi
139 fi
140 local client_private_key="$(wg genkey)"
141 local server_public_key="$(wg pubkey <<<"$server_private_key")"
142 local client_public_key="$(wg pubkey <<<"$client_private_key")"
143 local preshared_key="$(wg genpsk)"
144 local client_config_contents="[Interface]
145 PrivateKey = $client_private_key
146 Address = $client_iface_addr
147 SaveConfig = true
148
149 [Peer]
150 PublicKey = $server_public_key
151 PresharedKey = $preshared_key
152 AllowedIPs = $server_iface_addr
153 Endpoint = $server_public_addr:$server_listen_port
154 PersistentKeepalive = 25
155 "
156 set -o noclobber
157 echo "$client_config_contents" > "$client_config"
158 set +o noclobber
159 local server_config_new_peer="
160 [Peer]
161 PublicKey = $client_public_key
162 PresharedKey = $preshared_key
163 AllowedIPs = $client_iface_addr
164 PersistentKeepalive = 25
165 "
166 echo "$server_config_new_peer" >> "$server_iface_conf"
167 cat <<EOF
168 Client added.
169 Move $client_config to /etc/wireguard/<client-interface>.conf on the client,
170 making sure that it is owned by root and has mode 600. Make sure it is NOT
171 left lying around since it contains the private key for the client, as well
172 as the preshared key.
173
174 Once you did that, run on the server:
175 wg-quick up $server_iface_name
176 or:
177 systemctl start wg-quick@$server_iface_name
178 and run on the client:
179 wg-quick up <client-interface>
180 or:
181 systemctl start wg-quick@<client-interface>
182 EOF
183 }
184
185 case "$1" in
186 init-server)
187 install_deps
188 init_server "${@:2}"
189 ;;
190 add-client)
191 install_deps
192 add_client "${@:2}"
193 ;;
194 *)
195 cat >&2 <<EOF
196 Usage: $0 init-server <server-iface-name> <listen-port> <server-iface-addr>
197 or: $0 add-client <server-iface-name> <server-public-addr> <client-config.conf> <client-iface-addr>
198
199 init-server: create a new wireguard config for the server, writes to
200 '/etc/wireguard/<server-iface-name>.conf'
201
202 add-client: add a client to the wireguard config for the server at
203 '/etc/wireguard/<server-iface-name>.conf'
204 Writes the generated client config to <client-config.conf>.
205 The client will connect to the server through
206 public IP or DNS address <server-public-addr>.
207 EOF
208 exit 1
209 ;;
210 esac