I have a fairly simple network, outlined below. For the question in particular, two subnets in their own VLAN.
I have a service running in VLAN1000, lets say it's a webserver. I have forwarded 443/tcp from my WAN interface to this machine, and it works fine from the outside.
From VLAN5, however, it doesn't work properly. It requires either split horizon DNS or hairpin NAT. I'm currently using split horizon DNS, but that only works for machines were I can control DNS, which I can't for some computers. It also doesn't work with some services just using IPs.
I'd like to implement a hairpin NAT rule which works with my dynamic WAN IP. I can't find any relevant documentation on this, but I'm probably missing something obvious.
How do I implement this?
I solved this with some scripting, accepting the potential for a small time where it might not work in case the WAN IP changes, before the script has had a chance to run again.
The script is simple:
#!/usr/bin/env bash
fname=/etc/nftables.d/ip.conf
test -f $fname || touch $fname
current_ip=$(curl -s https://ifconfig.me)
last_ip=$(awk '{print $4}' "${fname}")
if [[ $current_ip != $last_ip ]]
then
cat <<EOF > "${fname}"
define wan_ip = ${current_ip}
EOF
nft -n -f /etc/nftables.conf && nft -f /etc/nftables.conf && exit 0
exit 1
fi
It writes the current IP to a file (/etc/nftables.d/ip.conf
), and checks if the IP ifconfig.me returns is different from the current one. If it is, it writes a new ip.conf
and reloads the ruleset.
There's also a possibility of the curl command failing. No error handling.
Then, for nftables, I now match on IP rather than interface.
chain prerouting {
type nat hook prerouting priority 0;
ip daddr $wan_ip dnat ip to tcp dport map {
80 : $host_http,
443 : $host_http,
8883 : $host_mqtt
}
}
I then run this as a systemd timer/service.
/etc/systemd/system/update-wan-ip.timer
[Unit]
Description=Update wan ip
[Timer]
OnBootSec=3min
OnUnitActiveSec=7min
RandomizedDelaySec=31sec
[Install]
WantedBy=timers.target
/etc/systemd/system/update-wan-ip.service
[Unit]
Description=Update and store WAN IP
Requires=network-online.target
After=network-online.target
[Service]
Type=oneshot
WorkingDirectory=%h
ExecStart=%h/update-current-ip.sh
[Install]
WantedBy=default.target