Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save Shourai/1088f78b5e3696190a8ce6a6045c477b to your computer and use it in GitHub Desktop.
Save Shourai/1088f78b5e3696190a8ce6a6045c477b to your computer and use it in GitHub Desktop.
Allowing SSH on a server with an active OpenVPN client

If you want to SSH to a VPS which is running openVPN do the following:


The problem is that the default gateway gets changed by OpenVPN, and that breaks your current SSH connection unless you set up appropriate routes before you start OpenVPN.

What follows works for me. It uses iptables and ip (iproute2). Below, it is assumed that the default gateway interface before OpenVPN is started is "eth0". The idea is to ensure that when a connection to eth0 is made, even if eth0 is not the default gateway interface anymore, response packets for the connection go back on eth0 again.

You could use the same number for the connection mark, firewall mark and routing table. I used distinct numbers to make the diffences between them more apparent.

# set "connection" mark of connection from eth0 when first packet of connection arrives
sudo iptables -t mangle -A PREROUTING -i eth0 -m conntrack --ctstate NEW -j CONNMARK --set-mark 1234

# set "firewall" mark for response packets in connection with our connection mark
sudo iptables -t mangle -A OUTPUT -m connmark --mark 1234 -j MARK --set-mark 4321

# our routing table with eth0 as gateway interface
sudo ip route add default dev eth0 table 3412

# route packets with our firewall mark using our routing table
sudo ip rule add fwmark 4321 table 3412



The above works fine for me on Debian Jessie. But on an older Wheezy system I have just found that I need to add "via" to the routing table entry:

# our routing table with eth0 as gateway interface
sudo ip route add default dev eth0 via 12.345.67.89 table 3412

There "12.345.67.89" must be the original non-VPN gateway.


To delete the iptables rules:

sudo iptables -L -t mangle --line-numbers

will give you a number for each rule in your mangle table, then use

sudo iptables -t mangle -D POSTROUTING x

where x is the rule number where you defined the mark.

To show the ip route and firewall rules:

sudo ip route show table all
sudo ip rule show

To delete the ip route and firewall rules:

sudo ip route delete default dev eth0 table 3412
sudo ip rule delete fwmark 4321 table 3412

Second solution

To resolve this issue you will need to set up both iptables and routing rules. The specific problem you're encountering is that outgoing SSH packets are being routed via your anonymous VPN tunnel interface instead of your Ethernet interface. This is happening because your VPN software set up a routing rule to send any and all unhandled traffic via the tunnel interface. Good for anonymizing your network traffic; bad for establishing SSH connections to your computer.

There are a few ways to fix this problem, but I will share with you the one which worked for me in an identical situation. Here's what we need to do:

  1. Create a new IP rule table to handle non-VPN traffic
  2. Add an IP rule to lookup our no-VPN table for any packets marked with a specific netfilter mask
  3. Add an IP route which directs all traffic in our no-VPN table to use your Ethernet interface instead of the tunnel
  4. Add an iptables rule to mark all SSH traffic with our designated netfilter mask

Note: I was working with Raspbian while doing the following, so you might need to adjust the commands a little to fit your distro. Creating a new IP rule table

Begin by inspecting iproute2's table definition file. We want to make sure we don't use the name or number of any existing rule tables.

cat /etc/iproute2/rt_tables

You'll likely see something along these lines:

# reserved values
255      local 
254      main
253      default   
0        unspec
# local
#1      inr.ruhep

Pick an arbitrary number and name for your new rule table -- anything not used above. I will use number 201 and name novpn for the remainder of this answer.

Append a definition directly to the definition file or edit it in the text editor of your choice:

echo "201 novpn" >> /etc/iproute2/rt_tables

Add a new IP rule to lookup the no-VPN table

Check for any existing ip rules that deal with netfilter masks:

ip rule show | grep fwmark

If grep turns up nothing, you're in the clear. If it does print some lines, take note of the hexadecimal number to the right of the word fwmark in each line. You will need to pick a number that is not currently in use. Since I had no existing fwmark rules, I chose the number 65.

ip rule add fwmark 65 table novpn

What this does is cause any packets with netfilter mask 65 to lookup our new novpn table for instructions on how to route the packets. Direct all traffic in our new table to use the Ethernet interface

ip route add default via YOUR.GATEWAY.IP.HERE dev eth0 table novpn

The important thing to note here is dev eth0. This forces all traffic that passes through the novpn table to only use the hardware Ethernet interface, instead of the virtual tunnel interface that your VPN creates.

Now would be a good time to flush your iproute cache, to make sure your new rules and routes take immediate effect:

ip route flush cache

Instruct firewall rule to mark all SSH traffic with the designated netfilter mask

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

There are too many options here for me to explain in any great depth. I strongly encourage you to read the manual page for iptables to get a sense of what's going on here:

man iptables

In a nutshell: we are appending an output rule to the firewall's mangle table (for specialized packet handling) that instructs it to mark any TCP packets originating from source port 22 with our designated netfilter mask 65. What next?

At this point, you should be ready to test SSH. If all goes well, you should be met with the happy "login as" prompt.

For security's sake, I recommend you instruct your firewall to drop any incoming SSH requests from the tunnel interface:

iptables -A INPUT -i tun0 -p tcp -m tcp --dport 22 -j DROP

Note that the all of the instructions above are transient (except for the creation of the rule table ID) -- they will clear the next time you restart your computer. Making them permanent is an exercise I leave to you.

Copy link

How to persist with first solution when reboot?

Copy link

IanBlakeley commented Jul 13, 2021

I can think of 2 ways

  1. Use the iptables-save > ~/iptables.txt. then can restore with iptables-restore < ~/iptables.txt
  2. Place the commands into a batch script and execute that from cron

Copy link

yuriw commented Feb 15, 2023

What about NotrdVPN Whitelisting?
Did they break it?

Copy link

aoaim commented Oct 5, 2023

Thanks, the second solution works well for me.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment