Linux NAT GRE Tunnel
Oct 21, 2017
You will need to allow GRE traffic on your router to be passed to the local machine. This is pretty easy with an EdgeMAX router.
$PUBLIC_IP
- your local public IP address (https://ipv4.icanhazip.com)
$LAN_IP
- your machines local LAN IP (ip a
)
$REMOTE_IP
- IP address of the remote Linux server you are tunneling to
If your local network uses the 192.168.0.x
subnet, be sure to change it to something else like 192.168.1.x
Local
auto gre1
iface gre1 inet tunnel
address 192.168.0.2
netmask 255.255.255.252
mtu 1420
mode gre
endpoint $REMOTE_IP
ttl 255
post-up ip rule add from 192.168.0.0/30 table GRE && ip route add default via 192.168.0.1 table GRE
Run this once:
echo '500 GRE' >> /etc/iproute2/rt_tables
iptables -t mangle -A POSTROUTING -p tcp --tcp-flags SYN,RST SYN -o gre+ -j TCPMSS --clamp-mss-to-pmtu
Remote
auto gre1
iface gre1 inet tunnel
address 192.168.0.1
netmask 255.255.255.252
mtu 1420
mode gre
endpoint $PUBLIC_IP
ttl 255
iptables -A INPUT -p gre -s $PUBLIC_IP -j ACCEPT
iptables -t nat -A POSTROUTING -s 192.168.0.0/30 ! -o gre+ -j SNAT --to-source $REMOTE_IP
iptables -t mangle -A POSTROUTING -p tcp --tcp-flags SYN,RST SYN -o gre+ -j TCPMSS --clamp-mss-to-pmtu
# Port forwarding
iptables -A INPUT -p tcp --dport 443 -m state --state NEW -j ACCEPT
iptables -A PREROUTING -t nat -i eth0 -p tcp --dport 443 -j DNAT --to 192.168.0.2:443
iptables -A FORWARD -d 192.168.0.2 -p tcp --dport 443 -j ACCEPT
Make sure net.ipv4.ip_forward=1
and net.ipv4.conf.all.proxy_arp=1
are enabled and net.ipv4.conf.all.rp_filter=0
is off.
Using a GRE tunnel instead of SSH/OpenVPN allows for the remote IP addresses to show up correctly instead of localhost or the tunnel IP.
I’ve had issues where the GRE tunnel times out or disconnects causing the tunnel to stop functioning. I’m not sure what the cause is and it could be an issue with the remote server.
EdgeMAX
Inbound Interface: Internet facing interface Translations Address: Local LAN IP Protocol: gre Src Address: Remote server
Keep Alive
The tunnel appears to become unreachable after a certain amount of time when no data is being passed between the ends. Any iptables
forward rules will not reach the destination in this case. I could not find any information about setting a keepalive variable in the interfaces file for Debian. So, the easiest thing is to setup a systemd service with a ping interval of 5 seconds.
/etc/systemd/system/tunnel-keepalive.service
[Unit]
Description=Tunnel Keepalive
After=network.target
[Service]
Type=simple
Restart=always
RestartSec=5
ExecStart=/bin/ping -q -i 5 192.168.0.1
[Install]
WantedBy=multi-user.target