How To Set Up WireGuard Firewall Rules in Linux

How do I set up WireGuard Firewall rules (iptables) in Linux?

For road warrior WireGuard and other purposes, you need to set up and configure firewall rules. You need to configure NAT (Network Address Translation) to allow WireGuard clients to access the Internet. In Linux, we use a term called IP Masquerade. It means one to many NAT (1:Many). We also need a FORWARD chain rule. This page explains how to set up NAT and FORWARD firewall rules for WireGuard in Linux.
Tutorial requirements
Operating system/appLinux with iptables/ip6tables
Root privileges required Yes
Difficulty Advanced (rss)
Estimated completion time 15m
Table of contents

Procedure to set up WireGuard firewall rules


Linux comes with raw iptables and easy to use frontend scripts. For example, UFW is one such popular tool. Before we use any tools, we need to understand the exact iptables rules. Naturally, you must have WireGuard configured. If not see our tutorials:

Step 1: Setting up NAT firewall rules

The syntax is as follows:
# iptables -t nat -I POSTROUTING 1 -s {sub/net} -o {interface} -j MASQUERADE
Make sure all outgoing packets are translated via VPN:
# iptables -t nat -I POSTROUTING 1 -s 10.8.1.0/24 -o eth0 -j MASQUERADE
Where,

  1. -t nat : Set up nat table for WireGuard.
  2. -I POSTROUTING 1 : Insert rule at position 1 for altering packets as they are about to go out for the POSTROUTING chain.
  3. -s 10.8.1.0/24 : Only do NAT if source address created by WireGuard wg0 interface.
  4. -o eth0 : Name of an interface via which a packet is going to be sent. In this case, eth0 connected to the Internet.
  5. -j MASQUERADE : Tell (jump) what to do if the packet matches according to given conditions. The MASQUERADE target is only valid in the nat table, in the POSTROUTING chain. This rule is responsible for routing traffic to the Internet for all WireGuard clients.

Step 2: Accept all traffic created by wg0 interface

Allow all traffic on wg0 interface:
# iptables -I INPUT 1 -i {interface} -j ACCEPT
# iptables -I INPUT 1 -i wg0 -j ACCEPT

The above rules allows for packets destined to wg0.

Step 3: Configuring FORWARD rules

We must allow for packets being routed through the WireGuard server by setting up the FORWARD rule. The syntax is:
# iptables -I FORWARD 1 -i eth0 -o wg0 -j ACCEPT
# iptables -I FORWARD 1 -i wg0 -o eth0 -j ACCEPT

Step 4: Open WireGuard UDP port # 51194

Finally, open UDP port # 51194 as follows:
# iptables -I INPUT 1 -i eth0 -p udp --dport 51194 -j ACCEPT

Step 5: Command to remove WireGuard iptables rules

We can reverse all command by deleting all added iptabes rules as follows:
# iptables -t nat -D POSTROUTING -s 10.8.1.0/24 -o eth0 -j MASQUERADE
# iptables -D INPUT -i wg0 -j ACCEPT
# iptables -D FORWARD -i eth0 -o wg0 -j ACCEPT
# iptables -D FORWARD -i wg0 -o eth0 -j ACCEPT
# iptables -D INPUT -i eth0 -p udp --dport 51194 -j ACCEPT

Step 6: Turn on IP forwarding on Linux

For IPv4 we set the following Linux kernel variables to accept incoming network packets on wg0, passed on to another network interface such as eth0, and then forwards it accordingly:
# sysctl -w net.ipv4.ip_forward=1
For IPv6, try the following sysctl command:
# sysctl -w net.ipv6.conf.all.forwarding=1

Step 7: Update wireguard config files for firewall and routing support

We need to tell WireGuard commands and script snippets which will be executed by using the following two directives:
# Turn on NAT when wg0 comes up #
PostUp = /path/to/add-nat-routing.sh
# Turn of NAT when wg0 goes down #
PostDown = /path/to/remove-nat-routing.sh

Putting it all together: Firewall rule for WireGuard

Update your /etc/wireguard/wg0.conf file as follows:
# vim /etc/wireguard/wg0.conf
Append in the [Interface] section:
PostUp = /etc/wireguard/helper/add-nat-routing.sh
PostDown = /etc/wireguard/helper/remove-nat-routing.sh

Here is how it should look:

[Interface]
Address = 10.8.0.1/24
ListenPort = 51194
PrivateKey = My_Server_KEY
SaveConfig = false
PostUp = /etc/wireguard/helper/add-nat-routing.sh
PostDown = /etc/wireguard/helper/remove-nat-routing.sh
 
[Peer]
# My Linux Desktop 
PublicKey = My_Linux_Desktop_KEY
PresharedKey = My_PRE_SHARED_KEY
AllowedIPs = 10.8.0.2/32

Create a new directory using the mkdir command:
# mkdir -v /etc/wireguard/helper/
Contains of add-nat-routing.sh displayed using the cat command:
# cat /etc/wireguard/helper/add-nat-routing.sh

#!/bin/bash
IPT="/sbin/iptables"
IPT6="/sbin/ip6tables"          
 
IN_FACE="eth0"                   # NIC connected to the internet
WG_FACE="wg0"                    # WG NIC 
SUB_NET="10.8.1.0/24"            # WG IPv4 sub/net aka CIDR
WG_PORT="51194"                  # WG udp port
SUB_NET_6="fd42:42:42:42::/112"  # WG IPv6 sub/net
 
## IPv4 ##
$IPT -t nat -I POSTROUTING 1 -s $SUB_NET -o $IN_FACE -j MASQUERADE
$IPT -I INPUT 1 -i $WG_FACE -j ACCEPT
$IPT -I FORWARD 1 -i $IN_FACE -o $WG_FACE -j ACCEPT
$IPT -I FORWARD 1 -i $WG_FACE -o $IN_FACE -j ACCEPT
$IPT -I INPUT 1 -i $IN_FACE -p udp --dport $WG_PORT -j ACCEPT
 
## IPv6 (Uncomment) ##
## $IPT6 -t nat -I POSTROUTING 1 -s $SUB_NET_6 -o $IN_FACE -j MASQUERADE
## $IPT6 -I INPUT 1 -i $WG_FACE -j ACCEPT
## $IPT6 -I FORWARD 1 -i $IN_FACE -o $WG_FACE -j ACCEPT
## $IPT6 -I FORWARD 1 -i $WG_FACE -o $IN_FACE -j ACCEPT

AND:
# cat /etc/wireguard/helper/remove-nat-routing.sh

#!/bin/bash
IPT="/sbin/iptables"
IPT6="/sbin/ip6tables"          
 
IN_FACE="eth0"                   # NIC connected to the internet
WG_FACE="wg0"                    # WG NIC 
SUB_NET="10.8.1.0/24"            # WG IPv4 sub/net aka CIDR
WG_PORT="51194"                  # WG udp port
SUB_NET_6="fd42:42:42:42::/112"  # WG IPv6 sub/net
 
# IPv4 rules #
$IPT -t nat -D POSTROUTING -s $SUB_NET -o $IN_FACE -j MASQUERADE
$IPT -D INPUT -i $WG_FACE -j ACCEPT
$IPT -D FORWARD -i $IN_FACE -o $WG_FACE -j ACCEPT
$IPT -D FORWARD -i $WG_FACE -o $IN_FACE -j ACCEPT
$IPT -D INPUT -i $IN_FACE -p udp --dport $WG_PORT -j ACCEPT
 
# IPv6 rules (uncomment) #
## $IPT6 -t nat -D POSTROUTING -s $SUB_NET_6 -o $IN_FACE -j MASQUERADE
## $IPT6 -D INPUT -i $WG_FACE -j ACCEPT
## $IPT6 -D FORWARD -i $IN_FACE -o $WG_FACE -j ACCEPT
## $IPT6 -D FORWARD -i $WG_FACE -o $IN_FACE -j ACCEPT

Make sure you create the following file using a text editor too:
# vim /etc/sysctl.d/10-wireguard.conf
Add the following text:

net.ipv4.ip_forward=1
net.ipv6.conf.all.forwarding=1

Reload all changes and turn on NAT routing:
# sysctl -p /etc/sysctl.d/10-wireguard.conf
# chmod -v +x /etc/wireguard/helper/*.sh
# systemctl restart wg-quick@wg0.service

Update client’s config file

You must tell Wireguard client that the remote server is the client’s gateway. In other words we are going to override the default route on the client. Edit the /etc/wireguard/wg0.conf on client side as follows in [Peer] section.
AllowedIPs = 0.0.0.0/0
Here is how it looks on client side:

# Config client for Linode VPN #
[Interface]
## This Desktop/client's private key ##
PrivateKey = {KEY_GOES_HERE}
 
## Client ip address ##
Address = 10.8.0.2/24
 
[Peer]
## Remote Ubuntu 20.04 wg0 server public key ##
PublicKey = {KEY_GOES_HERE}
 
## set ACL ##
#################################################
## Allow remote server as gateway 
## Edit/Update old AllowedIPs entry as follows 
## Otherwise client won't show server's IP 
#################################################
AllowedIPs = 0.0.0.0/0
 
## Your Ubuntu 20.04 LTS server's public IPv4/IPv6 address and port ##
Endpoint = {SERVER_IP_HERE}:51194
 
##  Key connection alive ##
PersistentKeepalive = 15

Verification

Test your configuration from the client side. See if you can access the Internet using the ping command, dig command/host command and a web-browser:
vivek@client:~$ ping -c 4 1.1.1.1
vivek@client:~$ host www.cyberciti.biz
## See if you can access WG based DNS server too (must be configured) ##
vivek@client:~$ dig -p 53 www.google.com 10.8.0.1
## View routing using the ip command ##
vivek@client:~$ ip r
## We must get our WireGuard public IP address ##
## Find public IP address from command line on Linux ##
vivek@client:~$ dig TXT +short o-o.myaddr.l.google.com @ns1.google.com

Conclusion

In this guide, we have shown you how to enable IP forwarding and NAT rules using iptables in Linux for WireGuard VPN clients to provide internal clients with Internet access. See WireGuard home page for more information.

This entry is 4 of 6 in the WireGuard moden Linux/Unix/*BSD VPN Tutorial series. Keep reading the rest of the series:
  1. Ubuntu 20.04 set up WireGuard VPN server
  2. CentOS 8 set up WireGuard VPN server
  3. Debian 10 set up WireGuard VPN server
  4. WireGuard Firewall Rules in Linux
  5. Wireguard VPN client in a FreeBSD jail
  6. Alpine Linux set up WireGuard VPN server
🐧 If you liked this page, please support my work on Patreon or with a donation.
🐧 Get the latest tutorials on SysAdmin, Linux/Unix, Open Source/DevOps topics:
CategoryList of Unix and Linux commands
File Managementcat
FirewallAlpine Awall CentOS 8 OpenSUSE RHEL 8 Ubuntu 16.04 Ubuntu 18.04 Ubuntu 20.04
Network Utilitiesdig host ip nmap
OpenVPNCentOS 7 CentOS 8 Debian 10 Debian 8/9 Ubuntu 18.04 Ubuntu 20.04
Package Managerapk apt
Processes Managementbg chroot cron disown fg jobs killall kill pidof pstree pwdx time
Searchinggrep whereis which
User Informationgroups id lastcomm last lid/libuser-lid logname members users whoami who w
WireGuard VPNAlpine CentOS 8 Debian 10 Firewall Ubuntu 20.04
5 comments… add one
  • Aye Jul 18, 2020 @ 8:21

    Is firewall rule is for static or dynamic IP address?

    • supportLocalHostCloud Aug 8, 2020 @ 4:29

      It works both static and dynamic IP address.

  • Mark Lievf Sep 10, 2020 @ 12:09

    Super helpful, thanks!

  • vbdev Oct 28, 2020 @ 9:03

    Hello and Thank You for your excellent written tutorials.
    First I have to admit that I am new in setting up such network items outside LAN oder Intranet. I have developed a windows forms app taking data from a MS SQL Server 2019 for Linux hosted on an Ubuntu 20.04 LTS server listening on port 1433 as the only port open so far with limited IPs allowed to access this port. I was excited to use the key-based security of the (wireguard) VPN as an authorization mean and decided to test it out.
    I was able to create a VPN using https://www.cyberciti.biz/faq/ubuntu-20-04-set-up-wireguard-vpn-server/ and can ping and with the addition of PostUp and PostDown use it as a VPN for browsing but thats not the use case. My was to close 1433 in firewall completely and connect the app requesting the data from SQL Server via this wireguard VPN and using in the connection string the VPN server private address 192.168.6.1 instead ofg the public IP address of the SQL Server. But I cannot connect via VPN – via public IP address no problem at all. Is it possible at all to connect to the VPN server and have a request to port 1433 TCP in the connection string and expect the SQL Server listen on 1433 and reply to the request?
    Thanks in advance for any help.

    • 🐧 Vivek Gite Oct 29, 2020 @ 7:37

      Are you using ufw on the host Ubuntu server? If so just open that port to VPN sub/net such as 192.168.6.0/24 to tcp/1433. For example:

       sudo ufw allow in on wg0 from 192.168.6.0/24  to any port 1433 proto tcp comment 'Open TCP SQL SERVER PORT for all vpn connected server'

      Where,

      • wg0 – Your WireGuard interface on Ubuntu server.
      • 192.168.6.0/24 – Your WireGuard sub/net. You can also use your client/web-server IP such as 192.168.6.2 instead of sub/net.
      • 1433 – SQL server port
      • proto tcp – TCP protocol

      By default ufw closes all port. See our guide about ufw on ubuntu 20.04.

Leave a Reply

Your email address will not be published. Required fields are marked *

Use HTML <pre>...</pre>, <code>...</code> and <kbd>...</kbd> for code samples.