Many sysadmins and developers need to securely expose services behind firewalls using encrypted tunnels. A common approach involves redirecting local connections through SSH port forwarding to a remote system. However, older iptables NAT techniques like:
iptables -A OUTPUT -t nat --dport ${LOCAL_UNPRIV_PORT} \
-j DNAT ${REMOTE_SYSTEM}:${REMOTE_PORT}
no longer work in modern Linux kernels due to the removal of implicit source local NAT support (the IP_NF_NAT_LOCAL
kernel module). The kernel logs typically show:
kernel: NAT: no longer support implicit source local NAT
Here's a more maintainable approach using SSH tunnels and proper iptables rules:
# On the RedHat server (jump host):
ssh -N -L 0.0.0.0:${UNPRIV_PORT}:localhost:${UNPRIV_PORT} user@localhost
# On the client machine:
ssh -N -R ${UNPRIV_PORT}:${TARGET_IP}:3389 user@jump_host
For systems where you must use iptables, configure these rules instead:
# Enable IP forwarding
sysctl -w net.ipv4.ip_forward=1
# NAT rule for incoming connections
iptables -t nat -A PREROUTING -p tcp --dport ${UNPRIV_PORT} \
-j DNAT --to-destination ${TARGET_IP}:3389
# Masquerade outgoing traffic
iptables -t nat -A POSTROUTING -j MASQUERADE
# Allow forwarded traffic
iptables -A FORWARD -p tcp --dport 3389 -j ACCEPT
When iptables proves problematic, socat provides a reliable alternative:
# On the jump host:
socat TCP-LISTEN:${UNPRIV_PORT},fork TCP:${TARGET_IP}:3389
# With SSH tunneling:
ssh -L ${UNPRIV_PORT}:localhost:${UNPRIV_PORT} user@jump_host
Always remember to:
- Restrict source IPs in your iptables rules
- Use SSH key authentication
- Consider fail2ban for brute force protection
- Monitor tunnel connections
# Example restricted iptables rule:
iptables -A INPUT -p tcp --dport ${UNPRIV_PORT} \
-s ${TRUSTED_IP} -j ACCEPT
When troubleshooting, check these essential tools:
# Check NAT table:
iptables -t nat -L -v -n
# Monitor connections:
ss -tulpn | grep ${UNPRIV_PORT}
# Check kernel messages:
dmesg | grep NAT
# Verify SSH tunnels:
lsof -i -n | egrep '\<ssh\>'
When attempting to create specialized SSH port forwarding for RDP access behind firewalls, many admins hit a snag with modern Linux kernels. The traditional approach using:
iptables -A OUTPUT -t nat --dport ${LOCAL_PORT} \
-j DNAT ${REMOTE_IP}:${REMOTE_PORT}
now triggers the kernel message "NAT: no longer support implicit source local NAT". This change dates back to kernel 2.6.10's removal of IP_NF_NAT_LOCAL support.
Modern kernels prevent DNAT operations on locally-generated packets in the OUTPUT chain due to security concerns about loopback interface manipulation. The packets never hit the PREROUTING chain where NAT typically occurs.
Here are three validated methods to achieve local-to-remote forwarding:
Method 1: Loopback Interface Redirection
# Enable routing of local packets
sysctl -w net.ipv4.conf.all.route_localnet=1
# Redirect local port to remote
iptables -t nat -A PREROUTING -p tcp --dport 3389 -j DNAT --to-destination 10.0.0.5:5000
iptables -t nat -A POSTROUTING -p tcp -d 10.0.0.5 --dport 5000 -j SNAT --to-source ${LOCAL_IP}
Method 2: SSH Double Tunnel
# On client machine
ssh -L 5000:localhost:5000 user@jump_host
# On jump host
ssh -L 5000:target_host:3389 user@target_host
Method 3: socat Relay
# On intermediate host
socat TCP4-LISTEN:5000,fork TCP4:target_host:3389
When implementing these solutions:
- Always restrict source IPs using iptables -s filters
- Consider rate limiting to prevent brute force attacks
- Use SSH key authentication exclusively
- Monitor connection attempts in your firewall logs
The socat method typically shows 15-20% lower latency than iptables redirects for RDP traffic, but consumes more CPU. For high-availability scenarios, consider using HAProxy with health checks:
listen rdp_gateway
bind :5000
mode tcp
server target1 10.0.0.5:3389 check inter 2000
Remember to test any solution thoroughly in a staging environment before deploying to production. The optimal approach depends on your specific network topology and security requirements.