Guide for setting up a server with a public IP to port forward to a host behind a pfsense.
The use case for this is you want to host a service and either you do not want to expose your public IP or your pfsense firewall is behind a private IP such as CG-NAT. For this demo I am using a Debian 12 system with a public IP but it should work with other Linux distributions mostly the same way but you will have to adjust the command accordingly.
Debian 12 Host With Public IP Setup
First we need to enable IP Forwarding. IP forwarding is the ability for an operating system to accept incoming network packets on one interface, recognize that it is not meant for the system itself, but that it should be passed on to another network. Edit the file /etc/sysctl.conf
and change and uncomment to the line that says net.ipv4.ip_forward=1
Now reboot or run sysctl -p
to activate the changes.
Install wireguard and iptables
apt install iptables wireguard-tools -y
Go to to the Wireguard config cd /etc/wireguard
and then run the following command to generate the public and private keys for the server.
umask 077; wg genkey | tee privatekey | wg pubkey > publickey
The run cat privatekey
and copy it so we can put it in to the server config file.
Create the /etc/wireguard/wg0.conf
vim /etc/wireguard/wg0.conf
Server wg0.conf file as used in the video
[Interface]
PrivateKey = <Server Private Key>
Address = 10.69.69.1/24
ListenPort = 51820
PostUp = iptables -t nat -A PREROUTING -p tcp --dport 19999 -j DNAT --to-destination 10.69.69.2:19999
PostUp = iptables -t nat -A POSTROUTING -p tcp -d 10.69.69.2 --dport 19999 -j SNAT --to-source 10.69.69.1
PostUp = iptables -A FORWARD -p tcp -d 10.69.69.2 --dport 19999 -j ACCEPT
PostDown = iptables -t nat -D PREROUTING -p tcp --dport 19999 -j DNAT --to-destination 10.69.69.2:19999
PostDown = iptables -t nat -D POSTROUTING -p tcp -d 10.69.69.2 --dport 19999 -j SNAT --to-source 10.69.69.1
PostDown = iptables -D FORWARD -p tcp -d 10.69.69.2 --dport 19999 -j ACCEPT
[Peer]
# pfsense
PublicKey= < pfsense public key>
AllowedIPs=10.69.69.2/32
PersistentKeepalive=25
Explanation of WireGuard iptables Rules
1. PREROUTING Rule
PostUp = iptables -t nat -A PREROUTING -p tcp --dport 19999 -j DNAT --to-destination 10.69.69.2:19999
- Purpose: This rule ensures that incoming traffic on TCP port 19999 (destined for the public IP of the Debian Host) is forwarded to the WireGuard peer (pfSense) with the tunnel IP
10.69.69.2
. - Mechanism:
- The
PREROUTING
chain in thenat
table is used to modify the destination address of packets as soon as they arrive. - In this case, any traffic arriving on the Debian Host on port 19999 will have its destination rewritten to
10.69.69.2:19999
.
- The
2. POSTROUTING Rule
PostUp = iptables -t nat -A POSTROUTING -p tcp -d 10.69.69.2 --dport 19999 -j SNAT --to-source 10.69.69.1
- Purpose: Ensures that the source address of packets forwarded to
10.69.69.2:19999
is rewritten to the Debian Host WireGuard tunnel IP (10.69.69.1
). - Why This is Needed:
- Without this rule, the forwarded traffic would retain the original source IP of the client (e.g., a public IP of a remote client).
- The pfSense behind the WireGuard tunnel would send return traffic directly to the client, bypassing the Debian Host, breaking the connection.
- By rewriting the source IP to
10.69.69.1
, the response traffic is sent back to the Debian Host, which can then forward it to the original client.
3. FORWARD Rule
PostUp = iptables -A FORWARD -p tcp -d 10.69.69.2 --dport 19999 -j ACCEPT
- Purpose: Explicitly allows the Debian Host to forward traffic destined for
10.69.69.2:19999
through the kernel. - Why This is Needed:
- By default, Linux systems might block traffic being forwarded between interfaces unless explicitly allowed.
- This rule ensures that traffic arriving at the Debian Host for port
19999
is permitted to be forwarded to the WireGuard tunnel.
Summary of the Flow
- PREROUTING: Rewrites the destination of traffic destined for the Debian Host public IP on port
19999
to the WireGuard peer10.69.69.2
. - POSTROUTING: Rewrites the source IP of forwarded packets to
10.69.69.1
to ensure return traffic is routed back through the Debian Host. - FORWARD: Permits the Debian Host to forward the traffic between the public interface and the WireGuard tunnel interface.
The PostDown removes the rules when the wireguard tunnel is stopped.
To test that the server works run wg-quick up wg0
to bring up the interface. Running wg-quick down
will bring the interface down.
If you want the wg0 interface to be active on boot you need to run
systemctl enable wg-quick@wg0
Then you can use to systemctl start wg-quick@wg0
start the server, systemctl stop wg-quick@wg0
stop the server and systemctl status wg-quick@wg0
to check the status.
The command wg show
will give you the wireguard status
pfsense setup
As shown in the video:
- Create a new Wireguard Tunnel in pfsense
- Generate new keys and copy the pfsense public key into
/etc/wireguard/wg0.conf
on the Debian 12 Host With Public IP - Create a peer for that tunnel, set the Endpoint to the Debian Server Public IP, add the public key from the Debian 12 Host from the
/etc/wireguard/publickey
, and be sure to use a Keep Alive time if the pfsense is behind CG-NAT as the host can not initiate the connection. Set allowed IP to10.69.69.1/32
- Create interface in pfsense set IPv4 Configuration Type to static IP, set MTU and MSS to 1420, setup static ip to
10.69.69.2
- Under routing make sure Default gateway is not set to automatic and set to WAN (or a group if you have that configured)
- Create gateway in pfsense (this allows for the data to be routing back) choose the WG interface use
10.69.69.2
as the Gateway IP and either disable monitoring or set the monitor ip to10.69.69.1
- Create port forward in pfsense to your server behind the pfsense (this will also create the proper firewall rules under the interface )
For this setup extra static routes are not needed as they are in my pfsense wiregaurd site to site tutorial here: