Skip to content

Instantly share code, notes, and snippets.

@qdm12
Last active March 25, 2024 21:24
Star You must be signed in to star a gist
Save qdm12/4e0e4f9d1a34db9cf63ebb0997827d0d to your computer and use it in GitHub Desktop.
Wireguard and iptables restrictions for multiple users

Wireguard and iptables restrictions for multiple users

If you don't know what Wireguard is, well, you should. It's fast, easy to setup and highly configurable. We will configure Wireguard for multiple users with various restrictions using iptables.

Assumptions

This should fit most setups (not mine though 😉)

  • LAN network: 192.168.1.0/24 (192.168.1.1 => 192.168.1.254)
  • LAN DNS server address: 192.168.1.1
  • Wireguard is installed (kernel and tools) on a Linux host (it should also work on other platforms though).
  • The Linux host address: 192.168.1.10
  • The Linux host main interface: enp4s0 (find it with ip a)

Initial server setup

  1. Ensure your iptables firewall has its FORWARD table policy set to DROP:

    iptables -P FORWARD DROP
  2. Generate a Wireguard private key

    echo "Private key: $(wg genkey)"
    Private key: OM5BUrGVAswOm/r8asLtdUgJB8rrXflD6TVFL5aGAHk=
    
  3. Create a file /etc/wireguard/wg0.conf (or any other name than wg0) with content:

    [Interface]
    Address = 10.0.0.1/24
    ListenPort = 51820
    PrivateKey = OM5BUrGVAswOm/r8asLtdUgJB8rrXflD6TVFL5aGAHk=
    PostUp = /etc/wireguard/postup.sh
    PostDown = /etc/wireguard/postdown.sh
    

    Don't forget to replace the PrivateKey value with the one you generated

  4. Create a file /etc/wireguard/postup.sh with content:

    WIREGUARD_INTERFACE=wg0
    WIREGUARD_LAN=10.0.0.0/24
    MASQUERADE_INTERFACE=enp4s0
    
    iptables -t nat -I POSTROUTING -o $MASQUERADE_INTERFACE -j MASQUERADE -s $WIREGUARD_LAN
    
    # Add a WIREGUARD_wg0 chain to the FORWARD chain
    CHAIN_NAME="WIREGUARD_$WIREGUARD_INTERFACE"
    iptables -N $CHAIN_NAME
    iptables -A FORWARD -j $CHAIN_NAME
    
    # Accept related or established traffic
    iptables -A $CHAIN_NAME -o $WIREGUARD_INTERFACE -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
    
    # Accept traffic from any Wireguard IP address connected to the Wireguard server
    iptables -A $CHAIN_NAME -s $WIREGUARD_LAN -i $WIREGUARD_INTERFACE -j ACCEPT
    
    # Drop everything else coming through the Wireguard interface
    iptables -A $CHAIN_NAME -i $WIREGUARD_INTERFACE -j DROP
    
    # Return to FORWARD chain
    iptables -A $CHAIN_NAME -j RETURN
  5. Create a file /etc/wireguard/postdown.sh with content:

    WIREGUARD_INTERFACE=wg0
    WIREGUARD_LAN=10.0.0.0/24
    MASQUERADE_INTERFACE=enp4s0
    CHAIN_NAME="WIREGUARD_$WIREGUARD_INTERFACE"
    
    iptables -t nat -D POSTROUTING -o $MASQUERADE_INTERFACE -j MASQUERADE -s $WIREGUARD_LAN
    
    # Remove and delete the WIREGUARD_wg0 chain
    iptables -D FORWARD -j $CHAIN_NAME
    iptables -F $CHAIN_NAME
    iptables -X $CHAIN_NAME
  6. Start the Wireguard server (without any client configured yet):

    wg-quick up wg0

First admin client

Let's setup a client with full access to Internet and your LAN through Wireguard.

  1. Install Wireguard on your client device

  2. On your client device, create a configuration file client.conf with content:

    [Interface]
    Address = 10.0.0.2/32
    DNS = 192.168.1.1
    PrivateKey = YOUR_CLIENT_PRIVATE_KEY
    
    [Peer]
    AllowedIPs = 0.0.0.0/0, ::/0
    Endpoint = 192.168.1.10:51820
    PersistentKeepalive = 25
    PublicKey = YOUR_SERVER_PUBLIC_KEY
    
  3. Replace in the configuration above YOUR_SERVER_PUBLIC_KEY with the key shown using wg show wg0 public-key on your server. For example: p6aGalk69yCM8vNhbvC5mEH/HhJr1c8f55UaeJSChX0=.

  4. Generate keys for your client:

    priv=`wg genkey` && printf "Private key: $priv\nPublic key: `echo "$priv" | wg pubkey`\n" && unset -v priv
    Private key: 2L4L8YwusK4Ot4jVoo/1wwQfLAeRM6kJ/WWxzfnWKm4=
    Public key: GmVyaj+K36xEk7ko/8jijMB9XX9dFgi4mJxsAEFMHmA=
    
  5. Replace in client.conf YOUR_CLIENT_PRIVATE_KEY with the private key just generated above.

  6. Use the public key shown above to add the following block to /etc/wireguard/wg0.conf on your server:

    [Peer]
    # Your first admin client
    PublicKey = GmVyaj+K36xEk7ko/8jijMB9XX9dFgi4mJxsAEFMHmA=
    AllowedIPs = 10.0.0.2/32
    
  7. On your server, restart Wireguard: wg-quick down wg0 && wg-quick up wg0.

  8. You should now be able to connect to the Wireguard server from your client. You can check on the server with wg and it should show a latest handshake line.

LAN only user

Let's add a user who should only have access to the LAN.

  1. Repeat steps 1 to 5 from the First admin client section above.

  2. Use the public key shown in step 4 to add the following block to /etc/wireguard/wg0.conf on your server:

    [Peer]
    # LAN only user
    PublicKey = 7GneIV/Od7WEKfTpIXr+rTzPf3okaQTBwsfBs5Eqiyw=
    AllowedIPs = 10.0.0.3/32
    
  3. Shutdown Wireguard: wg-quick down wg0

  4. Modify /etc/wireguard/postup.sh:

    • Limit full access to our first admin client 10.0.0.2 only by changing:

      iptables -A $CHAIN_NAME -s 10.0.0.0/24 -i $WIREGUARD_INTERFACE -j ACCEPT

      to

      iptables -A $CHAIN_NAME -s 10.0.0.2 -i $WIREGUARD_INTERFACE -j ACCEPT
    • Add a new line to allow our new user to LAN access only

      iptables -A $CHAIN_NAME -s 10.0.0.3 -i $WIREGUARD_INTERFACE -d 192.168.1.0/24 -j ACCEPT
  5. Start Wireguard: wg-quick up wg0

  6. Note that the client should set its AllowedIPs = 0.0.0.0/0, ::/0 to AllowedIPs = 192.168.1.0/24 so it tunnels only to when trying to reach an address on the LAN.

Restrict the user to a port

Let's re-use the user we previously created. Let's only allow him to access port 445 (Samba) on a server 192.168.1.20 for example.

  1. Shutdown Wireguard: wg-quick down wg0

  2. Modify

    iptables -A $CHAIN_NAME -s 10.0.0.3 -i $WIREGUARD_INTERFACE -d 192.168.1.0/24 -j ACCEPT

    to

    iptables -A $CHAIN_NAME -s 10.0.0.3 -i $WIREGUARD_INTERFACE -d 192.168.10.20 -p tcp --dport 445 -j ACCEPT
  3. Start Wireguard: wg-quick up wg0

Restrict the user to limited Web browsing only

That's useful for our friends who want to stream restricted content on Netflix without allowing them on your LAN 😉

  1. Shutdown Wireguard: wg-quick down wg0

  2. Modify

    iptables -A $CHAIN_NAME -s 10.0.0.3 -i $WIREGUARD_INTERFACE -d 192.168.10.20 -p tcp --dport 445 -j ACCEPT

    to

    # DNS
    iptables -A $CHAIN_NAME -s 10.0.0.3 -i $WIREGUARD_INTERFACE -d 192.168.1.1 -p udp --dport 53 -j ACCEPT
    # Drop traffic to your any private IP address
    iptables -A $CHAIN_NAME -s 10.0.0.3 -i $WIREGUARD_INTERFACE -d 10.0.0.0/8,172.16.0.0/12,192.168.0.0/16 -j DROP
    # Accept outgoing connections to HTTP(S) ports to any IP address (public because of rule above)
    iptables -A $CHAIN_NAME -s 10.0.0.3 -i $WIREGUARD_INTERFACE -d 0.0.0.0/0 -p tcp -m multiport --dports 80,443 -j ACCEPT
  3. Start Wireguard: wg-quick up wg0

  4. Don't forget to set back the client to AllowedIPs = 0.0.0.0/0, ::/0

More rules

Feel free to comment, I'll try my best to make you a rule 😉

Enjoy!

@samboman
Copy link

samboman commented Jan 31, 2022

I did add the following to deny ping from WireGuard Internet-only clients, as it is enabled by default:

# Drop ping (ICMP) from WIREGUARD_GUEST to private adress (RUN ONLY ONCE)
#iptables -I FORWARD -s $WIREGUARD_GUEST -i $WIREGUARD_INTERFACE -d 10.0.0.0/8,172.16.0.0/12,192.168.0.0/16 -p icmp --icmp-type echo-request -j REJECT
#iptables -I FORWARD -s $WIREGUARD_GUEST -i $WIREGUARD_INTERFACE -d 10.0.0.0/8,172.16.0.0/12,192.168.0.0/16 -p icmp --icmp-type echo-reply -j REJECT

As you can see I just run it once, otherwise I'll end up in multiple inserts in iptables. Is there a way to add this to the -A $CHAIN_NAME, so it gets deleted from postdown.sh?

@MrPeanut12
Copy link

How should these rules be modified if we want to set the INPUT table policy to DROP (while allowing SSH from LAN and wg0)?

@diogopms
Copy link

diogopms commented Feb 5, 2022

Can we use ufw instead of iptables to support this flow?

@techjp
Copy link

techjp commented Feb 13, 2022

iptables -P FORWARD -j DROP gives me the error:
-P requires a chain and a policy

@samboman, I get the same error. I wonder if @qdm12 might have made a mistake with the commands? Should it not be iptables -P FORWARD DROP?

@Normanras
Copy link

@samboman Do you mind sharing what you set $WIREGUARD_GUEST to in your scripts?

@samboman
Copy link

samboman commented Mar 5, 2022

This is how I ended up and solved it for a trusted and a internet-only-network (guest).

postup.sh

WIREGUARD_INTERFACE=wg0
WIREGUARD_LAN=10.55.0.022
WIREGUARD_TRUSTED=10.55.0.024
WIREGUARD_GUEST=10.55.1.024
MASQUERADE_INTERFACE=eth0

iptables -t nat -I POSTROUTING -o $MASQUERADE_INTERFACE -j MASQUERADE -s $WIREGUARD_LAN

# Add a WIREGUARD_wg0 chain to the FORWARD chain
CHAIN_NAME=WIREGUARD_$WIREGUARD_INTERFACE
iptables -N $CHAIN_NAME
iptables -A FORWARD -j $CHAIN_NAME

# Accept related or established traffic
iptables -A $CHAIN_NAME -o $WIREGUARD_INTERFACE -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT

# TRUSTED
# Accept traffic from any trusted IP address
iptables -A $CHAIN_NAME -s $WIREGUARD_TRUSTED -i $WIREGUARD_INTERFACE -j ACCEPT

# GUEST
# Drop traffic to your any private IP address
iptables -A $CHAIN_NAME -s $WIREGUARD_GUEST -i $WIREGUARD_INTERFACE -d 10.0.0.08,172.16.0.012,192.168.0.016 -j DROP
# Accept outgoing connections to any IP address (public because of rule above)
iptables -A $CHAIN_NAME -s $WIREGUARD_GUEST -i $WIREGUARD_INTERFACE -d 0.0.0.00 -j ACCEPT
# Drop ping (ICMP) from WIREGUARD_GUEST to private adress (RUN ONLY ONCE)
# iptables -I FORWARD -s $WIREGUARD_GUEST -i $WIREGUARD_INTERFACE -d 10.0.0.08,172.16.0.012,192.168.0.016 -p icmp --icmp-type echo-request -j REJECT
# iptables -I FORWARD -s $WIREGUARD_GUEST -i $WIREGUARD_INTERFACE -d 10.0.0.08,172.16.0.012,192.168.0.016 -p icmp --icmp-type echo-reply -j REJECT
# Drop everything else coming through the Wireguard interface
iptables -A $CHAIN_NAME -i $WIREGUARD_INTERFACE -j DROP

# Return to FORWARD chain
iptables -A $CHAIN_NAME -j RETURN

postdown.sh

WIREGUARD_INTERFACE=wg0
WIREGUARD_LAN=10.55.0.0/22
MASQUERADE_INTERFACE=eth0
CHAIN_NAME="WIREGUARD_$WIREGUARD_INTERFACE"

iptables -t nat -D POSTROUTING -o $MASQUERADE_INTERFACE -j MASQUERADE -s $WIREGUARD_LAN

# Remove and delete the WIREGUARD_wg0 chain
iptables -D FORWARD -j $CHAIN_NAME
iptables -F $CHAIN_NAME
iptables -X $CHAIN_NAME

@Darkyere
Copy link

Darkyere commented May 19, 2022

Is it possible in your exambles above.

To add a config that will allow acces only too specific and multiple IP's and Ports.
While at the same time allowing the person to reach the internet?

I would like to allow friends acces to certain computers on certain ports.
But i would allso like them to be able to acces the public internet while retraining acces to my LAN.

Best Regards,
Darkyere

@int2018
Copy link

int2018 commented May 30, 2022

Hi,

I am not sure if this is the right place to ask, so please be kind if not.

I have setup Wireguard on a Raspberry and it handles the remote access to the Qnap NAS in the network. So far everything is good.
The "problem" is, that multiple users can open the same file (e.g. Excel sheet) without a warning. This means there is the potential of data loss since the last one who saves the file "wins".
Is this something that can be handled in wireguard?

Thanks in advance

@curiousercreative
Copy link

curiousercreative commented May 30, 2022

The "problem" is, that multiple users can open the same file (e.g. Excel sheet) without a warning. This means there is the potential of data loss since the last one who saves the file "wins". Is this something that can be handled in wireguard?

@int2018 No, nothing Wireguard can do about this. You can look into file locking. Some applications (Adobe InDesign, darktable, KeePassXC) will handle this, but I believe SMB and NFS can perhaps be configured with similar functionality.

@ImDaBigBoss
Copy link

Hello, is there a way I can stop the wireguard server starting a connection to one of the clients?
Say I have these clients and these permissions:

  • 10.0.0.1 Server: Can start connections to 10.0.0.3
  • 10.0.0.2 Admin client: Can start connections to everything
  • 10.0.0.3 Client: Can start connecions to 10.0.0.1

Thanks in advance

@nikkadim
Copy link

nikkadim commented Aug 4, 2022

@qdm12 my wireguard stopped working some time ago and it's taken me until now to debug it. Turns out, there's an always reject forwarded packets policy last and since we're appending our chain, my forwarded packets are rejected before our chain is entered and packets are accepted. To resolve this for myself, I've changed iptables -A FORWARD -j $CHAIN_NAME to iptables -I FORWARD -j $CHAIN_NAME which will insert rather than append (first rather than last).
+1

@int2018
Copy link

int2018 commented Aug 4, 2022

Hi all,

my wireguard is all set up and running since months (wireguard on raspi, qnap nas/ smb server and windows clients.
It is working well (despite the issue mentioned before) but I still experience a strange behavior.
When I work with Windows Explorer and doubleclick on a folder it opens, I open another folder, and so on. So far so good, everything works as expected.
But then (no exact time) I doubleclick another folder or I doubleclick to open a file (e.g. pdf) the connection "hangs". I doubleclick again and the Explorer window tile changes to ... (not responding). Again after some time (seems 5-10 seconds) the connection is working again and the click I made earlier gets handled, the folder opens or the pdf file or whatever I clicked.
This is just a description of what I see on the (remote) client side. I talked to colleagues and they have the same phenomena. And it also happens when there is only one client connected. It does not seem to be a problem of too much load, the raspberry seems mostly in idle.

So the question is how/where can I check/tweak/debug what the problem is?
Can it be related to the Qnap NAS with SMB (standard setup) ?

Thanks for a hint...

@Normanras
Copy link

@qdm12 This particular gist has been helpful for me in understanding how iptables related to wireguard and peers. However, I could never quite get the example for peers with internet access but no LAN access. I thought I was understanding it, but i couldn't get it to work consistently.

Instead, I've added this rule as a permanent iptables rule and it seems to be accomplishing exactly what I need - internet access, no LAN access.

iptables -I FORWARD -s $WG_LAN -d $LOCAL_LAN -j DROP

Am I missing some security flaw or concern in using this single rule as opposed to your post* scripts? Truly curious, not grilling or questioning your script, I want to understand.

@rafaelgimenes
Copy link

i have a wireguard server on VPS on CANADA, and internally i have a raspberry pi with the pihole for my internal DNS custom setup cloudfare getting close DNS server in my area Brazil.
When my raspberry pi is connected on wireguard/VPS, my dns resolution changes to CANADA, in this peer(rasp berry) I only would like to allow to receive some packet from another peers, but do not out going to internet and assumes that it from CANADA.

Is it possible?

@Venderwel
Copy link

Venderwel commented Oct 10, 2022

Hello, trying to use your post to allow someone internet only to my VPN. Have Wireguard working, also with a few peers. But when I add

PostUp = /etc/wireguard/postup.sh
PostDown = /etc/wireguard/postdown.sh

to my wg0.conf and then use: wg-quick up wg0
I get /etc/wireguard/postup.sh: Permission denied

this is the access to both files
-rw-r--r-- 1 root root 337 Oct 10 14:42 /etc/wireguard/postup.sh
-rw-r--r-- 1 root root 337 Oct 10 14:42 /etc/wireguard/postdown.sh
What am I doing wrong?

Edit:
Ah, duh....changed it with cmod and now it works.

@PJ111288
Copy link

PJ111288 commented Feb 9, 2023

Hello Guys,

Problem: Client>>Server (Only 1 server) or Client: 10.0.0.51 should be accessing only this server 66.172.62.22.

I want allow a user to access only a server that is on public ip through wiregaurd only. How can i achieve this through the above iptables method because i tried but did not succeed. here is the my postup and postdown conf.

Postup.sh

WIREGUARD_INTERFACE=wg0
WIREGUARD_LAN=10.0.0.0/24
MASQUERADE_INTERFACE=ens33

iptables -t nat -I POSTROUTING -o $MASQUERADE_INTERFACE -j MASQUERADE -s $WIREGUARD_LAN

Add a WIREGUARD_wg0 chain to the FORWARD chain

CHAIN_NAME="WIREGUARD_$WIREGUARD_INTERFACE"
iptables -N $CHAIN_NAME
iptables -A FORWARD -j $CHAIN_NAME

Accept related or established traffic

iptables -A $CHAIN_NAME -o $WIREGUARD_INTERFACE -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT

Accept traffic from any Wireguard IP address connected to the Wireguard server

iptables -A $CHAIN_NAME -s $WIREGUARD_LAN -i $WIREGUARD_INTERFACE -j ACCEPT

Client: 10.0.0.51 should be accessing only this server 66.172.62.22

iptables -A $CHAIN_NAME -s 10.0.0.51 -i $WIREGUARD_INTERFACE -j ACCEPT
iptables -A $CHAIN_NAME -s 10.0.0.51 -i $WIREGUARD_INTERFACE -d 66.172.62.22 -j ACCEPT

Drop everything else coming through the Wireguard interface

iptables -A $CHAIN_NAME -i $WIREGUARD_INTERFACE -j DROP

Return to FORWARD chain

iptables -A $CHAIN_NAME -j RETURN

Postdown.sh

WIREGUARD_INTERFACE=wg0-server
WIREGUARD_LAN=10.0.0.0/24
MASQUERADE_INTERFACE=ens33

iptables -t nat -D POSTROUTING -o $MASQUERADE_INTERFACE - j MASQUERADE -s $WIREGUARD_LAN

Remove and delete the WIREGUARD_wg0-server chain

iptables -D FORWARD -j $CHAIN_NAME
iptables -F $CHAIN_NAME
iptables -X $CHAIN_NAME

Regards,
AS

@peppelongo96
Copy link

peppelongo96 commented Feb 14, 2023

Hi guys! Can someone say what isn't going here? Seems that no one of peers checks iptables conditions i.e. I can get access to any local ip using any ONLY_WEB_PEER.

First, I'm running Armbian on OrangePiZero2. I got my Wireguard VPN using PiVPN script. Also I was forced to use legacy iptables.

I got most of code from @samboman post because appears tidier to me :)

postdown.sh
(same from instructions except for WIREGUARD_LAN for sure)

postup.sh

WIREGUARD_INTERFACE=wg0
WIREGUARD_LAN=10.181.139.0/24
MASQUERADE_INTERFACE=eth0
#
FULL_ACCESS_PEER_1=10.181.139.2/32
FULL_ACCESS_PEER_2=10.181.139.3/32
#
ONLY_WEB_PEER_1=10.181.139.4/32
ONLY_WEB_PEER_2=10.181.139.5/32
ONLY_WEB_PEER_3=10.181.139.6/32
ONLY_WEB_PEER_4=10.181.139.7/32

iptables -t nat -I POSTROUTING -o $MASQUERADE_INTERFACE -j MASQUERADE -s $WIREGUARD_LAN

# Add a WIREGUARD_wg0 chain to the FORWARD chain
CHAIN_NAME=WIREGUARD_$WIREGUARD_INTERFACE
iptables -N $CHAIN_NAME
iptables -A FORWARD -j $CHAIN_NAME

# Accept related or established traffic
iptables -A $CHAIN_NAME -o $WIREGUARD_INTERFACE -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT

# TRUSTED
# Accept traffic from any trusted IP address
iptables -A $CHAIN_NAME -s $FULL_ACCESS_PEER_1 -i $WIREGUARD_INTERFACE -j ACCEPT
iptables -A $CHAIN_NAME -s $FULL_ACCESS_PEER_2 -i $WIREGUARD_INTERFACE -j ACCEPT

# GUEST
# Drop traffic to your any private IP address
iptables -A $CHAIN_NAME -s $ONLY_WEB_PEER_1 -i $WIREGUARD_INTERFACE -d 10.0.0.0/8,172.16.0.0/12,192.168.0.0/16 -j DROP
iptables -A $CHAIN_NAME -s $ONLY_WEB_PEER_2 -i $WIREGUARD_INTERFACE -d 10.0.0.0/8,172.16.0.0/12,192.168.0.0/16 -j DROP
iptables -A $CHAIN_NAME -s $ONLY_WEB_PEER_3 -i $WIREGUARD_INTERFACE -d 10.0.0.0/8,172.16.0.0/12,192.168.0.0/16 -j DROP
iptables -A $CHAIN_NAME -s $ONLY_WEB_PEER_4 -i $WIREGUARD_INTERFACE -d 10.0.0.0/8,172.16.0.0/12,192.168.0.0/16 -j DROP
# Accept outgoing connections to any IP address (public because of rule above)
#iptables -A $CHAIN_NAME -s $ONLY_WEB_PEER_1 -i $WIREGUARD_INTERFACE -d 0.0.0.0/0 -j ACCEPT
#iptables -A $CHAIN_NAME -s $ONLY_WEB_PEER_2 -i $WIREGUARD_INTERFACE -d 0.0.0.0/0 -j ACCEPT
#iptables -A $CHAIN_NAME -s $ONLY_WEB_PEER_3 -i $WIREGUARD_INTERFACE -d 0.0.0.0/0 -j ACCEPT
#iptables -A $CHAIN_NAME -s $ONLY_WEB_PEER_4 -i $WIREGUARD_INTERFACE -d 0.0.0.0/0 -j ACCEPT
# Drop ping (ICMP) from WIREGUARD_GUEST to private adress (RUN ONLY ONCE)
# iptables -I FORWARD -s $WIREGUARD_GUEST -i $WIREGUARD_INTERFACE -d 10.0.0.08,172.16.0.012,192.168.0.016 -p icmp --icmp-type echo-request -j REJECT
# iptables -I FORWARD -s $WIREGUARD_GUEST -i $WIREGUARD_INTERFACE -d 10.0.0.08,172.16.0.012,192.168.0.016 -p icmp --icmp-type echo-reply -j REJECT

# Drop everything else coming through the Wireguard interface
iptables -A $CHAIN_NAME -i $WIREGUARD_INTERFACE -j DROP

# Return to FORWARD chain
iptables -A $CHAIN_NAME -j RETURN

I also checked FORWARD - DROP default rule and masquerade interface as instructions.

Thanks

@carlos-gomez-espinosa
Copy link

carlos-gomez-espinosa commented Feb 15, 2023

My local network is like 192.168.1.1
I want to give access only for internet to WIREGUARD_GUEST client, but when tested, it has access also to my local network.
Can you help me?

WIREGUARD_INTERFACE=wg0
WIREGUARD_LAN=10.14.65.0/24
WIREGUARD_TRUSTED=10.14.65.2
WIREGUARD_GUEST=10.14.65.4
MASQUERADE_INTERFACE=eth0

iptables -t nat -I POSTROUTING -o $MASQUERADE_INTERFACE -j MASQUERADE -s $WIREGUARD_LAN

# Add a WIREGUARD_wg0 chain to the FORWARD chain
CHAIN_NAME=WIREGUARD_$WIREGUARD_INTERFACE
iptables -N $CHAIN_NAME
iptables -A FORWARD -j $CHAIN_NAME

# Accept related or established traffic
iptables -A $CHAIN_NAME -o $WIREGUARD_INTERFACE -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT

# TRUSTED
# Accept traffic from any trusted IP address
iptables -A $CHAIN_NAME -s $WIREGUARD_TRUSTED -i $WIREGUARD_INTERFACE -j ACCEPT

# GUEST
# Drop traffic to your any private IP address
iptables -A $CHAIN_NAME -s $WIREGUARD_GUEST -i $WIREGUARD_INTERFACE -d 192.168.1.0/16 -j DROP
# Accept outgoing connections to any IP address (public because of rule above)
iptables -A $CHAIN_NAME -s $WIREGUARD_GUEST -i $WIREGUARD_INTERFACE -d 0.0.0.0/0 -j ACCEPT
# Drop everything else coming through the Wireguard interface
iptables -A $CHAIN_NAME -i $WIREGUARD_INTERFACE -j DROP

# Return to FORWARD chain
iptables -A $CHAIN_NAME -j RETURN

@aimtrainer
Copy link

aimtrainer commented Mar 29, 2023

With Netflix having announced a crackdown on account sharing, I've set up a Wireguard user for my friends to log in with every now and then to let Netflix know, that their devices are in my home (have the same external ip) similar to the "Restrict the user to limited Web browsing only"-section.
I had to to it directly via iptables, as Wireguard wouldn't start with the postup.sh and postdown.sh in the wg0.conf. But that's okay I guess, since Wireguard runs 24/7 anyways.

Yet the next step was to restrict the the Wireguard users' permissions to only be able to use Netflix. I whitelisted the ip-ranges and ports from here, but I couldn't get Netflix to work.
I'm not sure whether these ip-ranges are somewhat regional (US?) and I would need different ips, since I'm in Europe. Also I know that Netflix has servers directly at ISPs acting as proxies in order to be closer to the end user. But I don't know if the end user "sees" their ips.

I don't know about the rest of the world. Here in Germany Netflix has not yet rolled out their anti-account-sharing-measures. But once they do, this use case, I'm describing, might not be all that extraordinary.
So if somebody has an idea what to forward in order to make Netflix work, I'd appreciate a hint :)

(I've tried the following workaround:

  • Assigned the user my pihole as dns
  • created a group in pihole in order to only whitelist Netflix-stuff on a domain level
  • tried to add the Wireguard user's ip (10.x.x.x) to that group --> failed, because pihole only sees the Wireguard-server's ip.

This wouldn't have been a good solution anyway, since all you'd need to bypass it, is to configure your own dns in your Wireguard client.)

@samboman
Copy link

samboman commented Mar 29, 2023

@aimtrainer, are you trying to protect your local network from your VPN guests. Then my solution might work for you:
https://gist.github.com/qdm12/4e0e4f9d1a34db9cf63ebb0997827d0d?permalink_comment_id=4086277#gistcomment-4086277

See my postup.sh row:
iptables -A $CHAIN_NAME -s $WIREGUARD_GUEST -i $WIREGUARD_INTERFACE -d 10.0.0.08,172.16.0.012,192.168.0.016 -j DROP
It will drop everything coming from the WIREGUARD_GUEST VPN network. Then the WireGuard Client configuration can specify the whitelisted IPs (Netflix servers). The client can modify the client configuration and send any traffic to the VPN, but in my case it's okay. I just want to protect my local network, andlet the client send any traffic to the VPN. If that's not okay, then you need to specify the rules at the server-side also with iptables.

@aimtrainer
Copy link

aimtrainer commented Mar 29, 2023

@aimtrainer, are you trying to protect your local network from your VPN guests. Then my solution might work for you: https://gist.github.com/qdm12/4e0e4f9d1a34db9cf63ebb0997827d0d?permalink_comment_id=4086277#gistcomment-4086277

See my postup.sh row: iptables -A $CHAIN_NAME -s $WIREGUARD_GUEST -i $WIREGUARD_INTERFACE -d 10.0.0.08,172.16.0.012,192.168.0.016 -j DROP It will drop everything coming from the WIREGUARD_GUEST VPN network. Then the WireGuard Client configuration can specify the whitelisted IPs (Netflix servers). The client can modify the client configuration and send any traffic to the VPN, but in my case it's okay. I just want to protect my local network, andlet the client send any traffic to the VPN. If that's not okay, then you need to specify the rules at the server-side also with iptables.

Thanks for your quick reply @samboman. But the "protecting my local network"-part already works. The problem is, that I don't know what I need to whitelist in order to get Netflix to work. Of course I can allow all http and https traffic. But I'd rather have people not have the ability to do anything besides watching Netflix (or ideally "check in" with Netflix from my ip from time to time and do the watching outside of the vpn).

@aimtrainer
Copy link

aimtrainer commented Mar 29, 2023

This seems to work for me:
-A FORWARD -s <Wireguard-client's IP> -d 45.57.0.0/17 -i wg0 -p tcp -m multiport --dports 80,443 -j ACCEPT
Here are the ip ranges Netflix uses. I just whitelisted the one that contains the the ips my dns resolved.

@zilexa
Copy link

zilexa commented Apr 22, 2023

I need 4 clients that can only access my Jellyfin server via Wireguard:

  • can only access the host server on port 8096 through tunnel, nothing else.
  • Also means they cannot access other ports on the host + cannot connect to/from other peers.
  • client public internet traffic cannot go through tunnel.
  • my host is 192.168.88.2 (dns server is my router on 192.168.88.1)

Would this be enough, 1 rule for each peer?

iptables -A $CHAIN_NAME -s 10.0.0.3 -i $WIREGUARD_INTERFACE -d 192.168.88.2 -p tcp --dport 8096 -j ACCEPT

Does this automatically mean this is the only access they have? I do not want them to be able to change their client.conf file to let all their internet traffic go through my host. Or do I need more rules to prevent that from happening?

Also need 3 clients that can only access 2 other clients (for RDP remote support of parents).

  • for example 10.0.0.11, 10.0.0.12, 10.0.0.13 should only be allowed to access 10.0.0.20 and 10.0.0.30 (not the host running wireguard server).
  • they should not be allowed to access any services on the host.

Not sure how to do this, none of the examples fit this usecase unfortunately..

@asyncdigital
Copy link

It would be a good question here to ask how to disable ping and speedtest.net (or other speedtesters available) on a wireguard interface to save on the bandwidth. Please advise.

@zilexa
Copy link

zilexa commented Oct 3, 2023

I guess my main question left:
What if I want multiple, let's say 8 clients only allow access to a specific IP and port?
Should I just add 8 rules, creating the 'port-only' rule 8 times or is there a way to work with an IP address range?

@mailliwal
Copy link

mailliwal commented Dec 9, 2023

WIREGUARD_INTERFACE=wg0
WIREGUARD_LAN=10.123.0.0/24
MASQUERADE_INTERFACE=eth0

iptables -t nat -I POSTROUTING -o $MASQUERADE_INTERFACE -j MASQUERADE -s $WIREGUARD_LAN

Add a WIREGUARD_wg0 chain to the FORWARD chain

CHAIN_NAME="WIREGUARD_$WIREGUARD_INTERFACE"
iptables -N $CHAIN_NAME
iptables -A FORWARD -j $CHAIN_NAME

Accept related or established traffic

iptables -A $CHAIN_NAME -o $WIREGUARD_INTERFACE -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT

NGINX

iptables -A WIREGUARD_wg0 -s $WIREGUARD_LAN -i $WIREGUARD_INTERFACE -d 192.168.1.83 -p tcp -m multiport --destination-port 80,443 -j ACCEPT

DNS

iptables -A WIREGUARD_wg0 -s $WIREGUARD_LAN -i $WIREGUARD_INTERFACE -d 192.168.1.82,192.168.1.250 -p udp --destination-port 53 -j ACCEPT

RDP

iptables -A WIREGUARD_wg0 -s $WIREGUARD_LAN -i $WIREGUARD_INTERFACE -d 192.168.1.86 -p tcp --destination-port 3389 -j ACCEPT

SMB

iptables -A WIREGUARD_wg0 -s $WIREGUARD_LAN -i $WIREGUARD_INTERFACE -d 192.168.1.250 -p tcp --destination-port 445 -j ACCEPT

Internet (http / https / ntp)

iptables -A WIREGUARD_wg0 -s $WIREGUARD_LAN -i $WIREGUARD_INTERFACE -d 0.0.0.0/0 -p tcp -m multiport --destination-port 80,443,123 -j ACCEPT

Drop everything else coming through the Wireguard interface

iptables -A $CHAIN_NAME -i $WIREGUARD_INTERFACE -j DROP

Return to FORWARD chain

iptables -A $CHAIN_NAME -j RETURN

=====================================================

Hi,

Added upper content to postup.sh

But I still available to access 192.168.1.80:8080

It should be dropped and I would like to access via NGINX 443/tcp instead of direct access

May I know anything wrong ?

THANKS

@owenperkins111
Copy link

Is it best to have UFW disabled with this? When i have UFW enabled both my clients can access everything. With it disabled this works as expected. Though i would like to keep UFW enabled as a best practice

@centralhardware
Copy link

centralhardware commented Dec 27, 2023

as mentiond above config for internet only don't work,

WIREGUARD_INTERFACE=wg1
WIREGUARD_LAN=10.8.8.0/24
WIREGUARD_GUEST=10.8.8.0/24
MASQUERADE_INTERFACE=eth0

iptables -t nat -I POSTROUTING -o $MASQUERADE_INTERFACE -j MASQUERADE -s $WIREGUARD_LAN

# Add a WIREGUARD_wg0 chain to the FORWARD chain
CHAIN_NAME=WIREGUARD_$WIREGUARD_INTERFACE
iptables -N $CHAIN_NAME
iptables -A FORWARD -j $CHAIN_NAME

# Accept related or established traffic
iptables -A $CHAIN_NAME -o $WIREGUARD_INTERFACE -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT

# GUEST
# Drop traffic to your any private IP address
iptables -A $CHAIN_NAME -s $WIREGUARD_GUEST -i $WIREGUARD_INTERFACE -d 10.0.0.0/8,172.16.0.0/12,192.168.0.0/16 -j DROP
# Accept outgoing connections to any IP address (public because of rule above)
iptables -A $CHAIN_NAME -s $WIREGUARD_GUEST -i $WIREGUARD_INTERFACE -d 0.0.0.0/0 -j ACCEPT
# Drop ping (ICMP) from WIREGUARD_GUEST to private adress (RUN ONLY ONCE)
# iptables -I FORWARD -s $WIREGUARD_GUEST -i $WIREGUARD_INTERFACE -d 10.0.0.08,172.16.0.012,192.168.0.016 -p icmp --icmp-type echo-request -j REJECT
# iptables -I FORWARD -s $WIREGUARD_GUEST -i $WIREGUARD_INTERFACE -d 10.0.0.08,172.16.0.012,192.168.0.016 -p icmp --icmp-type echo-reply -j REJECT
# Drop everything else coming through the Wireguard interface
iptables -A $CHAIN_NAME -i $WIREGUARD_INTERFACE -j DROP

# Return to FORWARD chain
iptables -A $CHAIN_NAME -j RETURN

what's wrong?

@ainformatico
Copy link

ainformatico commented Feb 21, 2024

Found this gist and thanks to all the comments above, found the inspiration to make my config work. Thanks for that.
Hope my config helps those seeking for something similar.

What I want

  • Laptop(10.11.0.100): trusted device with full access to the LAN network and the Internet.

  • Phone(10.11.0.3): guest device with full Internet access, but no LAN access, except to use the DNS running on the LAN (pihole) and a HTTP server also running on the LAN.

  • 192.168.0.10: Just runs Wireguard server

  • 192.168.0.53: Just runs Pihole server

  • 192.168.0.30: Just runs HTTP server

How I achieved it

🚨 IMPORTANT: The order of the rules in the file matter! If you move things around it will break.
If you know what you are doing, then you can take advantage of -I vs -A and move it as you wish.

WIREGUARD_INTERFACE=wg0
WIREGUARD_LAN=10.11.0.0/24
MASQUERADE_INTERFACE=eth0

iptables -t nat -I POSTROUTING -o $MASQUERADE_INTERFACE -j MASQUERADE -s $WIREGUARD_LAN

# Add a WIREGUARD_wg0 chain to the FORWARD chain
CHAIN_NAME="WIREGUARD_$WIREGUARD_INTERFACE"
iptables -N $CHAIN_NAME
iptables -A FORWARD -j $CHAIN_NAME

# Accept related or established traffic
iptables -A $CHAIN_NAME -o $WIREGUARD_INTERFACE -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT

##################################
# TRUSTED CLIENT (10.11.0.100)
## Accept and forward _all traffic_ from the Wireguard IP address of the trusted client. No restrictions.
iptables -A $CHAIN_NAME -s 10.11.0.100 -i $WIREGUARD_INTERFACE -j ACCEPT
##################################

##################################
# GUEST (10.11.0.3)
## Allow guest to use a DNS that runs in your LAN(192.168.0.53) e.g. Pihole.
### This requires the guest to set the DNS server in the Wireguard config: `DNS = 192.168.0.53`.
### Remove if not necessary.
iptables -A $CHAIN_NAME -s 10.11.0.3 -i $WIREGUARD_INTERFACE -p udp --dport 53 -d 192.168.0.53 -j ACCEPT

## Allow guest to access a HTTP server running in a LAN host(192.168.0.30).
### Remove if not necessary.
iptables -A $CHAIN_NAME -s 10.11.0.3 -i $WIREGUARD_INTERFACE -p tcp --match multiport --dport 80,443 -d 192.168.0.30 -j ACCEPT

## Prevent guest from accessing **this** host. The one running the Wireguard server(192.168.0.10).
### Depending on your needs and setup, you might need to also create a rule for the Wireguard IP of the server too. Check first.
### IMPORTANT: this is an INPUT rule as we don't want "INPUT" to this host. Other rules stay as FORWARD because they are for other hosts.
### IMPORTANT: If used, add to the postdown.sh: iptables -D INPUT -s 10.11.0.3 -i $WIREGUARD_INTERFACE -p tcp -d 192.168.0.10 -j DROP
### Remove if not necessary.
iptables -A INPUT -s 10.11.0.3 -i $WIREGUARD_INTERFACE -p tcp -d 192.168.0.10 -j DROP

## Drop traffic to any LAN or Wireguard IP address.
iptables -A $CHAIN_NAME -s 10.11.0.3 -i $WIREGUARD_INTERFACE -d 10.11.0.0/24,192.168.0.0/24 -j DROP

## Accept outgoing connections to the Internet.
iptables -A $CHAIN_NAME -s 10.11.0.3 -i $WIREGUARD_INTERFACE -d 0.0.0.0/0 -j ACCEPT
##################################

# Drop everything else coming through the Wireguard interface
iptables -A $CHAIN_NAME -i $WIREGUARD_INTERFACE -j DROP

# Return to FORWARD chain
iptables -A $CHAIN_NAME -j RETURN

@zilexa
Copy link

zilexa commented Feb 21, 2024

@ainformatico thanks a lot. This is almost what I was looking for, for years.

In my case, I only have one LAN homeserver that runs multiple services including DNS.
So only 192.168.0.53, and I only want to allow clients to access the DNS service and a few other services, based on port.
No other access to the server should be allowed.

Do you perhaps now how to do that?

Its Basically your setup, but servers 192.168.0.53 and 192.168.0.30 are in my case just 1 server.

Also I noticed suddenly in your comments you say your Wireguard server is 192.168.0.10, not 192.168.0.53. That's quite confusing..

@ainformatico
Copy link

@zilexa, I have different servers (different virtual machines)

  • 192.168.0.10: Just runs Wireguard server
  • 192.168.0.53: Just runs Pihole server
  • 192.168.0.30: Just runs HTTP server

With different clients:

  • 10.11.0.100: Laptop
  • 10.11.0.3: Phone

I have updated my comment above to reflect more info.

Now, regarding your comment. I will assume that your Wireguard server is also your DNS and other services servers.
In that case, you just need to update the IPs and firewall chain accordingly. As it is the same host, you can't use FORWARD anymore. You have to go to the INPUT chain in iptables. The reason is that you are not really forwarding anything, it is the same host.

Assuming your server running all services is 192.168.0.60, and the client is 10.11.0.3 yo can:

## Allow DNS
iptables -A INPUT -s 10.11.0.3 -i $WIREGUARD_INTERFACE -p udp --dport 53 -d 192.168.0.60 -j ACCEPT

## Allow guest to access different services at different ports (80, 443, 8080, 9000.)
iptables -A INPUT -s 10.11.0.3 -i $WIREGUARD_INTERFACE -p tcp --match multiport --dport 80,443,8080,9000 -d 192.168.0.60 -j ACCEPT

In your postdown.sh you will have to undo those rules:

## Allow DNS
iptables -D INPUT -s 10.11.0.3 -i $WIREGUARD_INTERFACE -p udp --dport 53 -d 192.168.0.60 -j ACCEPT

## Allow guest to access different services at different ports (80, 443, 8080, 9000.)
iptables -D INPUT -s 10.11.0.3 -i $WIREGUARD_INTERFACE -p tcp --match multiport --dport 80,443,8080,9000 -d 192.168.0.60 -j ACCEPT

@DrilealoTatarara
Copy link

Hi, im having problems configuring a tunnel with wireguard between the client, alocated in a AWS clowd instance with public IP 52.71.59.33, and the server, alocated in a pod of Kubernetes. This pod's cluster is virtualized with vSphere in a machine of the WAN network of the company. I'd like you to help me to staclish the tunnel between boths. I dont have much knowledge about iptables and forwarding, etc. Please help me! :(

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment