Skip to main content

VPN

Overview

kasad.com features a secure, private WireGuard tunnel. The kasad.com server will route requests on the tunnel out to the internet, making the tunnel usable as a VPN.

Structure

Tunnel Service

The WireGuard tunnel interface is wg0. wg-quick(8) is used to create the WireGuard tunnel and load configuration. It is automatically activated by the wg-quick@wg0.service SystemD service.

WireGuard interface

The WireGuard interface is named wgvpn0, although the interface name doesn't matter as long as it's configured properly on the server. The interface has the following configuration:

[Interface]
Address = 10.5.19.1/24
SaveConfig = false
ListenPort = 51194
PrivateKey = #############################################

## PEER LIST ##
# ...

Note: the keys in the snipped above have been hidden

IP Addressing

As can be seen in the configuration snippet, the WireGuard tunnel uses the IPv4 address subnet 10.5.19.0/24. The tunnel does not use IPv6. In theory it could, but I have not bothered to set that up.

WireGuard listener

WireGuard listens on UDP port 51194. Outgoing UDP traffic is blocked by some network firewalls (notably my school's), so the kasad.com server has a firewall rule in place to redirect incoming traffic on UDP port 123 to 51194. Since UDP port 123 is used for NTP, it usually is not blocked for outgoing traffic.

nft(8) rule to implement this:

table inet nat {
	chain prerouting {
        udp dport 123 redirect to :51194
    }
}

Peers

Each device that connects to the interface has its own keypair and IP address(es).

IP Address quirk

The IP address for each peer can be specified in CIDR notation, but the range for each peer must be unique. This means you cannot assign the same subnet for all peers and allow them to choose their own addresses. You must give a single unique address to each peer or give each one a range of IP addresses that does not overlap with any other peer's range.

Peer Configuration

Each peer must be specified in the WireGuard interface's configuration. Unknown peers will not be allowed to connect.

[Peer]
PublicKey = #############################################
AllowedIPs = 10.5.19.2/32

Peer List

Device IP Address(es) Public key
Kian's laptop 10.5.19.2 laT7XasKzIdTC5gy9jSS0PaKvdjEHEU3pQ/j2BYAujs=
Kian's old phone 10.5.19.3 tjNWCci8SQwxBcHPxIXjZkOX+K214d4WNsFV6MVuA1M=
Kian's (current) phone 10.5.19.4 Lvfk37+yv13mLGETIrBnbQD2Qw474Bpfr2KxZUMpn1Q=
Home office desktop 10.5.19.6 ALN7gfvfeswE2hwSfbRyWqsmZVPvsAa8TUaurHC9eG0=

Usage as VPN

While a VPN doesn't technically have to route traffic out to the internet, that's what they're usually used for. The kasad.com WireGuard interface is configured to forward traffic to the outside world. To utilize this, simply set 10.5.19.1 as the router address in your network configuration when connected to the WireGuard tunnel.

VPN routing configuration on server

Two things must be enabled for VPN routing to work:

  1. IP forwarding must be enabled in the Linux kernel.
  2. Firewall rules must be in place to (a) allow forwarding and (b) perform SNAT/masquerading.

Kernel options

The file /etc/sysctl.d/20-ip-forward.conf enabled IP forwarding in the kernel:

# Allow IPv4 forwarding
net.ipv4.ip_forward=1

# Allow "martian" packets on 'wgvpn0' interface.
# These are packets coming from an external interface but heading to a
# localhost destination. This is required for intercepting VPN DNS requests.
net.ipv4.conf.wgvpn0.route_localnet=1

Firewall rules

The portions of the nft(8) rule file that apply to the VPN routing are displayed below. Omitted lines are marked with an ellipsis comment (# ...).

table inet filter {
    chain input {
        # ...
        iifname "wgvpn*" ip saddr 10.5.19.0/24 goto vpninput
    }
    chain vpninput {
        accept
    }
    chain forward {
        # ...
        iifname wgvpn0 oifname ens3 accept
        oifname wgvpn0 ct state established,related accept
    }
}

table inet nat {
    chain postrouting {
        # ...
        ip saddr 10.5.19.0/24 oifname ens3 masquerade
    }
}