WireGuard – Restrict access to specific IP addresses

To restrict client access to specific IP addresses, you can use the WG_POST_UP environment variable in the wg-easy Docker image to override the default iptable rules and define custom ones.

Prerequisite

This article describes the procedure for the wg-easy Docker container. It can also be adapted to any WireGuard setup.
The following example is made for a configuration with the default address 10.8.0.x (WG_DEFAULT_ADDRESS) and two clients with the IP addresses 10.8.0.2 and 10.8.0.3.
I want to achieve, that client1 (10.8.0.2) has no access restrictions and client2 (10.8.0.3) is only allowed to connect to a specific IP address (192.168.1.50).

Configuration

The default WG_POST_UP variable is:
iptables -t nat -A POSTROUTING -s 10.8.0.0/24 -o eth0 -j MASQUERADE;
iptables -A INPUT -p udp -m udp --dport 51820 -j ACCEPT;
iptables -A FORWARD -i wg0 -j ACCEPT;
iptables -A FORWARD -o wg0 -j ACCEPT;

If the WG_DEFAULT_ADDRESS is different than 10.8.0.x, you have to substitute “10.8.0.0/24” with your chosen address space.

The new WG_POST_UP variable is:
iptables -t nat -A POSTROUTING -s 10.8.0.0/24 -o eth0 -j MASQUERADE;
iptables -A INPUT -p udp -m udp --dport 51820 -j ACCEPT;
iptables -A FORWARD -i wg0 -s 10.8.0.2/32 -j ACCEPT;
iptables -A FORWARD -o wg0 -d 10.8.0.2/32 -j ACCEPT;
iptables -A FORWARD -i wg0 -s 10.8.0.3/32 -d 192.168.1.50/32 -j ACCEPT;
iptables -A FORWARD -o wg0 -s 192.168.1.50/32 -d 10.8.0.3/32 -j ACCEPT;
iptables -A FORWARD -i wg0 -j DROP;
iptables -A FORWARD -o wg0 -j DROP;

Explanation

To better understand the iptable rules in this context, it’s important to know that:
Inbound traffic means incoming traffic from a WireGuard client towards the local network based on the source address
Outbound traffic means outgoing traffic from the local network towards a WireGuard client based on the destination address

Explanation client1 (10.8.0.2):
This line targets incoming traffic on interface wg0 (-i) for the IP address 10.8.0.2 (-s) and accepts it:
iptables -A FORWARD -i wg0 -s 10.8.0.2/32 -j ACCEPT;

This line targets outgoing traffic on interface wg0 (-o) for the IP address 10.8.0.2 (-d) and accepts it:
iptables -A FORWARD -o wg0 -d 10.8.0.2/32 -j ACCEPT;

Explanation client2 (10.8.0.3):
This line targets incoming traffic on interface wg0 (-i) for the IP address 10.8.0.3 (-s) from the IP address 192.168.1.50 (-d) and accepts it:
iptables -A FORWARD -i wg0 -s 10.8.0.3/32 -d 192.168.1.50/32 -j ACCEPT;

This line targets outgoing traffic on interface wg0 (-o) for the IP address 192.168.1.50 (-s) from the IP address 10.8.0.3 (-d) and accepts it:
iptables -A FORWARD -o wg0 -s 192.168.1.50/32 -d 10.8.0.3/32 -j ACCEPT;

Explanation DROP part:
With the last two lines every other traffic which isn’t affected by an iptables rule will be dropped:
iptables -A FORWARD -i wg0 -j DROP;
iptables -A FORWARD -o wg0 -j DROP;

Docker Compose file

A Docker Compose file for wg-easy with the implemented config from above could look like this:
services:
  wg-easy:
    environment:
      - WG_DEFAULT_ADDRESS=10.8.0.x
      - WG_POST_UP=
        iptables -t nat -A POSTROUTING -s 10.8.0.0/24 -o eth0 -j MASQUERADE;
        iptables -A INPUT -p udp -m udp --dport 51820 -j ACCEPT;
        iptables -A FORWARD -i wg0 -s 10.8.0.2/32 -j ACCEPT;
        iptables -A FORWARD -o wg0 -d 10.8.0.2/32 -j ACCEPT;
        iptables -A FORWARD -i wg0 -s 10.8.0.3/32 -d 192.168.1.50/32 -j ACCEPT;
        iptables -A FORWARD -o wg0 -s 192.168.1.50/32 -d 10.8.0.3/32 -j ACCEPT;
        iptables -A FORWARD -i wg0 -j DROP;
        iptables -A FORWARD -o wg0 -j DROP;
...

Share your thoughts