Skip to content

Instantly share code, notes, and snippets.

@briangordon
Last active May 15, 2020 14:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save briangordon/723d11f42f1bce8b67247dd79dad9032 to your computer and use it in GitHub Desktop.
Save briangordon/723d11f42f1bce8b67247dd79dad9032 to your computer and use it in GitHub Desktop.

These are the configuration files I ended up using for setting up a Wireguard-based anonymizing VPN on Debian Buster (the current stable), formatted as a shell script. It requires the Wireguard kernel module and userland tools from backports: apt-get install -t buster-backports wireguard-tools

The idea here is to set up a network namespace containing the configuration for the wireguard interface wg0. Any program which wants to use the Wireguard interface needs to be run in that namespace. For example, ip netns exec wireguard ping google.com. All sockets opened by that program and its children will go out over the tunnel, including DNS resolution, so they won't be able to interact in any way with the local network. Conversely, other processes (which were not started with ip-netns) will continue to use the original routing tables and DNS configuration, so things like SSH, remote desktop, and NFS mounts continue to work fine over the local network.

Because the wireguard interface is in a namespace all on its own, there is no physical interface for it to actually send out packets on. I therefore depend on some peculiar behavior in which you can create the Wireguard interface in the init namespace and then move it to another namespace, and it will still open its internal UDP socket in the init namespace.

I use resolv.conf instead of a resolver daemon and ifupdown to manage my network interfaces.

The integration with ifupdown (so that the wireguard interface comes up when eno1 does) is tricky because it can't really bring up an interface inside a network namespace. My approach is to add post-up and pre-down hooks to my eno1 interface definition which bring wireguard up and down with the physical interface. Alternatively, it would be possible to put all those hooks in a separate namespace-only interface file, bind-mount over the ifstate information, and then invoke ifup inside the namespace. Unfortunately, I think that's a setup better suited to veth tunnels where you have a corresponding manual interface on the "outside" where you can put the hooks to do the bind-mounting/ifup switcheroo. In that case, you could simply call ifup/ifdown on the outside interface to (de)configure both ends of the tunnel. In our case, however, we have no outside interface that we can actually configure, so everything has to be tied to eno1 anyway, and we might as well put the wireguard setup hooks there directly.

I use ufw as my netfilter frontend, but as far as I can tell this isn't going to be an option for managing a separate set of firewall rules that apply only inside the namespace. netns doesn't do anything with ufw's rules files, and I don't really want to maintain my own script that does this manually and flushes out its chains inside the namespace. This is not really a problem for me because the namespace provides sufficient isolation and the usual firewall rules in effect on the "outside" namespace are not applied on wg0. That latter fact is due to one of the built-in ufw rules which uses conntrack to unconditionally allow packets returning on related/established connections. I also have a rule for allowing any outgoing connections. So Wireguard sends its handshake out through eno0 in a UDP packet, establishing a connection in conntrack, and as long as our PersistentKeepalive is less than $(sysctl -n net.netfilter.nf_conntrack_udp_timeout_stream) then any further communication through the tunnel is allowed.

#!/bin/bash
# Copyright 2020 Google LLC.
# SPDX-License-Identifier: Apache-2.0
VPN_CONFIG_NAME="my-vpn.conf"
VPN_NAMESERVER="1.2.3.4"
VPN_PRIVATE_KEY=""
VPN_PUBLIC_KEY=""
VPN_ENDPOINT_ADDRESS="1.2.3.4:12345"
VPN_INTERFACE_ADDRESS="1.2.3.4/32"
# Place DNS configuration
mkdir -p /etc/netns/wireguard
cat > /etc/netns/wireguard/resolv.conf <<-ENDOFMESSAGE
domain .
nameserver $VPN_NAMESERVER
ENDOFMESSAGE
# Place Wireguard configuration
mkdir -p /etc/wireguard
cat > /etc/wireguard/$VPN_CONFIG_NAME <<-ENDOFMESSAGE
[Interface]
PrivateKey = $VPN_PRIVATE_KEY
[Peer]
PublicKey = $VPN_PUBLIC_KEY
AllowedIPs = 0.0.0.0/0
Endpoint = $VPN_ENDPOINT_ADDRESS
PersistentKeepalive = 25
ENDOFMESSAGE
# Place wireguard scripts
cat > /etc/wireguard/wg-up.sh <<-ENDOFMESSAGE
#!/bin/bash
set -e
ip netns add wireguard || true
ip -n wireguard link set lo up
if ! (ip -n wireguard link show wg0 2> /dev/null)
then
ip link add wg0 type wireguard
ip link set wg0 netns wireguard
fi
ip netns exec wireguard wg setconf wg0 /etc/wireguard/$VPN_CONFIG_NAME
ip -n wireguard address add $VPN_INTERFACE_ADDRESS dev wg0 || true
ip -n wireguard link set wg0 up
ip -n wireguard route add default dev wg0
ENDOFMESSAGE
chmod a+x /etc/wireguard/wg-up.sh
cat > /etc/wireguard/wg-down.sh <<-"ENDOFMESSAGE"
#!/bin/bash
set -e
ip -n wireguard link set wg0 down
#ip netns pids wireguard | xargs kill
#ip -n wireguard link delete dev wg0
#ip netns del wireguard
ENDOFMESSAGE
chmod a+x /etc/wireguard/wg-down.sh
# Append to ethernet interface configuration
cat >> /etc/network/interfaces <<-"ENDOFMESSAGE"
# Continued from eno1 above
post-up /etc/wireguard/wg-up.sh
pre-down /etc/wireguard/wg-down.sh
ENDOFMESSAGE
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment