CREATIVE CHAOS   ▋ blog

Wireguard VPN on Debian with nftables

PUBLISHED ON 19/12/2021 — EDITED ON 11/12/2023 — SYSOPS

nftables

$ vim /etc/nftables.conf
#!/usr/sbin/nft -f

flush ruleset


define WAN_IFC      = eth0

define VPN_IFC      = wg0
define VPN_NET      = 10.10.10.0/24
define VPN_SERVICES = { 10.10.10.20, 10.10.10.21 }

table inet filter {
	chain input {
...
        # Wireguard VPN
        udp dport 51820                                 counter accept comment "Allow VPN"
        iifname $VPN_IFC udp dport 53 ip saddr $VPN_NET counter accept comment "Allow DNS for VPN"

        # Allow VPN clients to communicate with each other.
        # iifname $vpn oifname $vpn ct state new accept
...
    chain forward {
        ...
        # forward WireGuard traffic, allowing it to access internet via WAN
        iifname $VPN_IFC oifname $WAN_IFC ct state new counter accept
...
table inet nat {
    chain POSTROUTING {
		type nat hook postrouting priority srcnat; policy accept;

        # masquerade wireguard traffic
        # make wireguard traffic look like it comes from the server itself
        oifname $WAN_IFC ip saddr $VPN_NET counter masquerade comment "Masquerade VPN traffic"
...

To reload rules from the config file, use:

$ systemctl restart nftables.service

Installation & configuration

$ apt install wireguard wireguard-tools

Generate the keys:

$ wg genkey | sudo tee /etc/wireguard/server_private.key | wg pubkey | sudo tee /etc/wireguard/server_public.key

Edit the configuration file and enter the peers. You can obtain peer public key from the wireguard client running on the specific peer.

$ vim /etc/wireguard/wg0.conf
[Interface]
Address = 10.10.10.1/24
SaveConfig = true
ListenPort = 51820
PrivateKey = xxxxxxxxxxxx_SERVER_PRIVATE_KEY_xxxxxxxxxxxxxx

[Peer]
PublicKey = xxxxxxxxxxxx_PEER_PUBLIC_KEY_xxxxxxxxxxxxxx
AllowedIPs = 10.10.10.3/32

[Peer]
PublicKey = xxxxxxxxxxxx_PEER_PUBLIC_KEY_xxxxxxxxxxxxxx
AllowedIPs = 10.10.10.4/32

Secure the keys and configuration files

$ chmod 600 /etc/wireguard/ -R

Start the service:

$ systemctl start wg-quick@wg0.service

Enable service at boot:

$ systemctl enable wg-quick@wg0.service

Check status:

$ wg
interface: wg0
  public key: xxxxxxxxxxxx_SERVER_PUBLIC_KEY_xxxxxxxxxxxxxx
  private key: (hidden)
  listening port: 51820

peer: xxxxxxxxxxxx_PEER_PUBLIC_KEY_xxxxxxxxxxxxxx
  endpoint: x.x.x.x:49810
  allowed ips: 10.10.10.4/32
  latest handshake: 1 minute, 1 second ago
  transfer: 108.77 KiB received, 221.66 KiB sent

peer: xxxxxxxxxxxx_PEER_PUBLIC_KEY_xxxxxxxxxxxxxx
  endpoint: x.x.x.x:59302
  allowed ips: 10.10.10.3/32

All good, but we don’t know exactly which peer is which. So use friendly names script:

$ cd /usr/local/bin
$ wget https://raw.githubusercontent.com/FlyveHest/wg-friendly-peer-names/master/wgg.sh
$ chmod a+x wgg.sh
$ mv wgg.sh wgg
$ cd /etc/wireguard

Create a key to peer name dictionary:

$ vim peers
xxxxxxxxxxxx_PEER_PUBLIC_KEY_xxxxxxxxxxxxxx=:phone
xxxxxxxxxxxx_PEER_PUBLIC_KEY_xxxxxxxxxxxxxx=:laptop

Now we can use wgg to display prettier status:

$ wgg
interface: wg0
  public key: xxxxxxxxxxxx_SERVER_PUBLIC_KEY_xxxxxxxxxxxxxx
  private key: (hidden)
  listening port: 51820
  
peer: xxxxxxxxxxxx_PEER_PUBLIC_KEY_xxxxxxxxxxxxxx
  name: phone
  endpoint: x.x.x.x:49810
  allowed ips: 10.10.10.4/32
  latest handshake: 1 minute, 3 seconds ago
  transfer: 108.77 KiB received, 221.66 KiB sent
  
peer: xxxxxxxxxxxx_PEER_PUBLIC_KEY_xxxxxxxxxxxxxx
  name: laptop
  endpoint: x.x.x.x:59302
  allowed ips: 10.10.10.3/32