Reverse VPN Tunnel Implementation for NAT-Traversal on Mobile Hotspots


2 views

Modern 4G/LTE networks implement carrier-grade NAT (CGNAT), where multiple customers share a single public IP address. This breaks traditional VPN server setups because:

  • No inbound connections can reach devices behind CGNAT
  • Dynamic DNS becomes ineffective
  • Port forwarding at the hotspot level doesn't help

The solution requires reversing the connection model. Instead of clients connecting to the server, we make the server initiate outbound connections to:

  1. A persistent cloud relay server
  2. Client devices with stable IPs
  3. A rendezvous service coordinating connections

Here's how to configure OpenVPN in "reverse client" mode:


# Server config (on your Pi)
client
remote your-relay-server.com 1194
resolv-retry infinite
nobind
persist-key
persist-tun
remote-cert-tls server
tls-client
auth SHA256
cipher AES-256-CBC

# Client config (on relay server)
mode server
tls-server
ifconfig 10.8.0.1 10.8.0.2
push "route 10.8.0.0 255.255.255.0"

WireGuard's simpler model works well with periodic keepalives:


# Pi configuration (mobile endpoint)
[Interface]
PrivateKey = [PI_PRIVATE_KEY]
Address = 10.0.0.2/24

[Peer]
PublicKey = [RELAY_PUBLIC_KEY]
Endpoint = relay.example.com:51820
AllowedIPs = 10.0.0.0/24
PersistentKeepalive = 25

For maximum reliability, implement a connection broker:


# Python example using AWS IoT Core as broker
import paho.mqtt.client as mqtt

def on_connect(client, userdata, flags, rc):
    client.subscribe("vpn/connection/request")

def on_message(client, userdata, msg):
    if msg.topic == "vpn/connection/request":
        initiate_vpn_connection(msg.payload.decode())

client = mqtt.Client()
client.on_connect = on_connect
client.on_message = on_message
client.connect("AWS_IOT_ENDPOINT", 8883, 60)
client.loop_forever()

For solar-powered operation:

  • Aggressive connection timeout settings
  • Exponential backoff on retries
  • Packet size optimization to reduce 4G data usage
  • Connection coalescing (bundle multiple services over single VPN)

When building portable Raspberry Pi servers on 4G networks, we face a fundamental networking limitation: Carrier-Grade NAT (CGNAT). Unlike traditional broadband connections where you get a public IP, 4G/LTE networks place devices behind multiple layers of NAT, making inbound connections impossible. This creates significant challenges for:

  • Remote SSH access to field-deployed devices
  • VPN server functionality on mobile networks
  • IoT deployments requiring persistent connections

The solution involves establishing persistent outbound connections from your NAT-trapped device to a publicly accessible relay server. Here's a proven architecture:


[Public VPS] ←→ [4G Raspberry Pi]
    ↑
[Client Devices]

WireGuard's low overhead makes it ideal for battery-powered setups. Configure the VPS as follows:


# /etc/wireguard/wg0.conf on VPS
[Interface]
Address = 10.0.0.1/24
PrivateKey = VPS_PRIVATE_KEY
ListenPort = 51820

# Raspberry Pi peer
[Peer]
PublicKey = PI_PUBLIC_KEY
AllowedIPs = 10.0.0.2/32
PersistentKeepalive = 25

On the Raspberry Pi:


# /etc/wireguard/wg0.conf
[Interface]
Address = 10.0.0.2/24
PrivateKey = PI_PRIVATE_KEY

# VPS peer
[Peer]
PublicKey = VPS_PUBLIC_KEY
Endpoint = vps.example.com:51820
AllowedIPs = 0.0.0.0/0
PersistentKeepalive = 25

Create a systemd service to maintain the tunnel:


# /etc/systemd/system/wg-tunnel.service
[Unit]
Description=WireGuard Reverse Tunnel
After=network.target

[Service]
Type=simple
ExecStart=/usr/bin/wg-quick up wg0
ExecStop=/usr/bin/wg-quick down wg0
Restart=always
RestartSec=30

[Install]
WantedBy=multi-user.target

Set up SSH port forwarding on the VPS:


# On VPS, add to SSH config
Host pi-tunnel
    HostName 10.0.0.2
    Port 22
    User pi
    ServerAliveInterval 60

For environments where WireGuard isn't suitable:

  1. SSH Reverse Tunneling:
    
    # On Raspberry Pi
    autossh -M 0 -N -R 2222:localhost:22 user@vps.example.com
    
  2. ngrok Alternative:
    
    # Using bore (Rust alternative)
    bore local 22 --to bore.pub --port 2222
    

For solar-powered deployments:

  • Set MTU to 1280 to reduce packet size
  • Adjust PersistentKeepalive based on network conditions
  • Implement connection quality monitoring with:

#!/bin/bash
while true; do
    if ! ping -c 1 10.0.0.1 &> /dev/null; then
        wg-quick down wg0
        wg-quick up wg0
    fi
    sleep 60
done