Linux Firewall (Part 6) : VPN

Intro

In this post, we will use Wireguard to connect to Windscribe using linux network namespaces.

Enabling WireGuard system-wide poses significant risks, as all network traffic, including sensitive data, is routed through the WireGuard interface. This approach creates a single point of failure, access issues and potential vulnerability for the entire system. Moreover, system-wide WireGuard activation can lead to performance issues, particularly on resource-constrained devices or networks with high traffic volumes.

WireGuard’s integration with network namespaces offers a robust solution for enhancing network isolation and security. By deploying WireGuard within dedicated namespaces, users create isolated environments where VPN connections operate independently from the main network. This setup ensures that VPN-related activities remain segregated, reducing the risk of unauthorized access or data leakage. With WireGuard’s lightweight design and efficient performance, users can achieve robust security measures while maintaining flexibility and scalability in their network infrastructure.

We need to setup Wireguard and add some entries in the linux firewall to allow specific traffic. I will use RHEL for this post, but this can be run on most linux distributions with similar steps, for example the Raspberry Pi.

Prerequisites

If you haven’t already, install Wireguard and enable resolved

dnf install wireguard-tools
systemctl enable --now systemd-resolved

Generate private, public and pre-shared keys for WireGuard

cd /etc/wireguard
(umask 077 && wg genkey | tee private.key | wg pubkey > public.key)
(umask 077 && wg genpsk > /etc/wireguard/psk.key)

Download the Wireguard configuration file from your VPN provider, or create one if not provided. It should look something like

wg-sea.conf

[Interface]
PrivateKey = <Private Key>
Address = <IP Address>/32
DNS = <DNS Address>

[Peer]
PublicKey = <Client Public Key>
AllowedIPs = 0.0.0.0/0, ::/0
Endpoint = <IP Address or Server Name>:<port>
PresharedKey = <Pre-Shared Key>

Download and install SOCKS v5 server. I will use microsocks for this post.

Wireguard

We are going to name most things with wg-sea prefix for Seattle location. Let’s create a bash script for setup.

wg-sea-setup.sh

Delete existing network interfaces and namespaces if they exist

ip link delete wg-sea
ip link delete wg-sea.0
ip netns delete wg-sea

Create a network namespace

ip netns add wg-sea

Create a pair of virtual Ethernet devices (wg-sea.0 and wg-sea.1), with one end (wg-sea.1) being moved into the network namespace named “wg-sea”

ip link add wg-sea.0 type veth peer name wg-sea.1
ip link set wg-sea.1 netns wg-sea

Assign IP addresses to the virtual Ethernet devices: “192.168.10.1” to wg-sea.0 and “192.168.10.2” to wg-sea.1 within the “wg-sea” network namespace

ip addr add 192.168.10.1/24 dev wg-sea.0
ip -n wg-sea addr add 192.168.10.2/24 dev wg-sea.1

Bring up the network interfaces wg-sea.0 and wg-sea.1 in their respective namespaces (main and “wg-sea”)

ip link set wg-sea.0 up
ip -n wg-sea link set lo up
ip -n wg-sea link set wg-sea.1 up

Add a default route within the “wg-sea” network namespace, directing all traffic to pass through the IP address “192.168.10.1”

ip -n wg-sea route add default via 192.168.10.1

The firewall we configured in Part 2 of this series will block all incoming DNS requests not originating from LAN or DMZ. We need DNS server for name resolution of VPN peers. Our firewall is configured to allow outgoing connections to the internet, so we can use the DNS servers on the internet.

echo "nameserver 1.1.1.1" > /etc/netns/wg-sea/resolv.conf
echo "nameserver 8.8.8.8" >> /etc/netns/wg-sea/resolv.conf

Create a WireGuard interface named “wg-sea” and move it into the “wg-sea” network namespace

ip link add wg-sea type wireguard
ip link set wg-sea netns wg-sea

Read the IP address specified in the WireGuard configuration file /etc/wireguard/wg-sea.conf and assigns it to the “wg-sea” interface within the “wg-sea” network namespace

ip -n wg-sea addr add $(awk '/^Address/ {print $3}' /etc/wireguard/wg-sea.conf) dev wg-sea

Update the WireGuard configuration for the “wg-sea” interface within the “wg-sea” network namespace by synchronizing it with the stripped-down version of the configuration

ip netns exec wg-sea wg syncconf wg-sea <(wg-quick strip wg-sea)

Bring up the “wg-sea” interface within the “wg-sea” network namespace

ip -n wg-sea link set wg-sea up

Add routes within the “wg-sea” network namespace to direct traffic for IP ranges 0.0.0.0/1 and 128.0.0.0/1 through the “wg-sea” interface.

[!NOTE]

We split the 0.0.0.0/0 block into two separate blocks 0.0.0.0/1 and 128.0.0.0/1 because they are more specific and will override the default route

ip -n wg-sea route add 0.0.0.0/1 dev wg-sea
ip -n wg-sea route add 128.0.0.0/1 dev wg-sea

Extract the DNS server address specified in the WireGuard configuration file /etc/wireguard/wg-sea.conf and uses it to populate the /etc/netns/wg-sea/resolv.conf file used by the “wg-sea” network namespace. This ensures that DNS resolution within the namespace is configured to use the specified DNS server

echo "nameserver $(awk '/^DNS/ {print $3}' /etc/wireguard/wg-sea.conf)" > /etc/netns/wg-sea/resolv.conf

Firewall

Let’s configure nftables. We will start by adding some defines

defines.nft

# interfaces
define if_wg_sea = "wg-sea.0"

# networks
define net_wg_sea = 192.168.10.0/30

# machines
define wg_wsh = 10.0.0.2    # windscribe seattle hendrix

Allow connections going out from Wireguard to WAN and allow connections from LAN to Wireguard

forward.nft

chain forward {
    iifname $if_wg_sea oifname $if_wan accept   # accept wireguard to wan
    iifname $if_lan oifname $if_wg_sea accept   # accept lan to wireguard
}

Masquerade traffic going in and out of Wireguard on LAN

nat.nft

chain postrouting {
    ip saddr $net_wg_sea oifname $if_wan masquerade     # from wg to wan
    ip saddr $net_lan oifname $if_wg_sea masquerade     # from lan to wg
}

SOCKS5 Sever

Start microsocks in “wg-sea” network namespace so clients can connect to the VPN using SOCKS5

ip netns exec wg-sea microsocks

To configure Firefox to use VPN, go to Firefox Settings > Network Settings. Select Manual proxy configuration for Configure Proxy Access to the Internet. Enter the IP address of the machine where we set up Wireguard. Add port 1080 (default for microsocks) and select SOCKS v5. Select Proxy DNS when using SOCKS v5 and click OK. Firefox will now use the VPN. Turn off anytime by switching back to No Proxy.

Thank you for reading. Check out the other parts in the series below.