How to Maintain SSH Access While Routing Traffic Through OpenVPN on CentOS 7


1 views

When examining your configuration, the core issue stems from OpenVPN's default behavior of redirecting all traffic through the VPN tunnel (0.0.0.0/1 and 128.0.0.0/1 routes). This creates a routing loop when attempting to SSH into your original public IP (104.167.102.77). Let's break down what happens:

1. SSH connection attempts reach ens33 (104.167.102.77)
2. Return traffic gets routed through tun0 (10.172.1.5)
3. Connection breaks because response can't route back properly

We'll modify the OpenVPN configuration to exclude your SSH traffic from the VPN tunnel while maintaining other traffic routing. Add these directives to your config.ovpn:

route-nopull
route 104.167.102.77 255.255.255.255 net_gateway
route 0.0.0.0 128.0.0.0 vpn_gateway
route 128.0.0.0 128.0.0.0 vpn_gateway

To ensure SSH traffic remains on the physical interface, we'll add iptables rules:

# Preserve existing connections
iptables -A INPUT -i ens33 -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT

# Allow new SSH connections on physical interface
iptables -A INPUT -i ens33 -p tcp --dport 22 -j ACCEPT

# Block SSH attempts coming through VPN
iptables -A INPUT -i tun0 -p tcp --dport 22 -j DROP

For CentOS 7, make these changes permanent:

# Save iptables rules
service iptables save
systemctl enable iptables

# Modify OpenVPN service to wait for network
systemctl edit openvpn@client.service

[Service]
ExecStartPre=/bin/sleep 5

After implementation, verify with these commands:

# Check routing table
ip route show table all

# Test SSH connectivity while VPN is active
ssh -v user@104.167.102.77

# Verify outbound traffic routing
curl --interface tun0 ifconfig.co
curl --interface ens33 ifconfig.co

If you encounter problems:

# Check for MTU mismatches
ping -M do -s 1500 104.167.102.1

# Examine OpenVPN logs
journalctl -u openvpn@client -f

# Test raw connectivity
nc -zv 104.167.102.77 22

For more advanced setups, consider policy routing:

# Create custom routing table
echo "200 vpnbypass" >> /etc/iproute2/rt_tables

# Add rule for SSH traffic
ip rule add from 104.167.102.77 table vpnbypass

# Set default route in custom table
ip route add default via 104.167.102.1 dev ens33 table vpnbypass

When you activate OpenVPN with default configurations on a Linux VPS, it typically modifies your routing table to send ALL traffic through the VPN tunnel. This creates our core problem:

# Before OpenVPN
$ ip route
default via 104.167.102.1 dev ens33

# After OpenVPN
$ ip route
0.0.0.0/1 via 10.172.1.5 dev tun0
default via 104.167.102.1 dev ens33
128.0.0.0/1 via 10.172.1.5 dev tun0

We'll use Linux's advanced routing capabilities to create separate routing tables:

# Create custom routing table (ID 100)
echo "100 vpnbypass" >> /etc/iproute2/rt_tables

# Add rules to use this table for SSH traffic
ip rule add from 104.167.102.77 table vpnbypass
ip rule add fwmark 1 table vpnbypass

# Populate the custom table
ip route add default via 104.167.102.1 dev ens33 table vpnbypass
ip route add 104.167.102.0/24 dev ens33 src 104.167.102.77 table vpnbypass

Modify your PIA OpenVPN configuration (config.ovpn):

client
dev tun
proto udp
remote nl.privateinternetaccess.com 1194
resolv-retry infinite
nobind
persist-key
persist-tun
ca ca.crt
tls-client
remote-cert-tls server
auth-user-pass
comp-lzo
verb 1
reneg-sec 0
crl-verify crl.pem

# Add these critical directives:
route-nopull
route 0.0.0.0 0.0.0.0 vpn_gateway
script-security 2
up /etc/openvpn/update-resolv-conf
down /etc/openvpn/update-resolv-conf

Create these iptables rules to mark SSH traffic:

iptables -t mangle -A OUTPUT -p tcp --sport 22 -j MARK --set-mark 1
iptables -t mangle -A OUTPUT -p tcp --dport 22 -j MARK --set-mark 1

For CentOS 7, create /etc/rc.local to make rules persistent:

#!/bin/bash
ip rule add from 104.167.102.77 table vpnbypass
ip rule add fwmark 1 table vpnbypass
ip route add default via 104.167.102.1 dev ens33 table vpnbypass
iptables -t mangle -A OUTPUT -p tcp --sport 22 -j MARK --set-mark 1
exit 0

Then make it executable:

chmod +x /etc/rc.local
systemctl enable rc-local

After implementation:

# Confirm SSH remains accessible
$ telnet 104.167.102.77 22
Trying 104.167.102.77...
Connected to 104.167.102.77.

# Verify internet traffic uses VPN
$ curl ifconfig.me
109.201.154.177  # Should show PIA exit IP

# Check routing tables
$ ip route show table vpnbypass
default via 104.167.102.1 dev ens33