OpenVPN Full Tunnel Cuts Your SSH Session: Fix the Reply Path
Taking the default route sends SSH replies into the tunnel. Pin the peer, filter redirect-gateway, or route by source before you lose the box.
Turning on a full-tunnel OpenVPN client on a machine you reach over SSH can cut the session within a second, and the machine is doing nothing wrong. The tunnel does not change how packets reach the server; it changes how the replies leave. Once the default route points at tun0, a reply to your SSH client goes into the provider's network, which has no route back to your address. The connection hangs, the next attempt times out, and the daemon is still listening on port 22 the whole time.
Read the routing table before you read the VPN log. The log will say the tunnel came up, and it is right. The question is which interface each reply is handed to, and that is a per-packet lookup against a table the tunnel just rewrote.
Why the reply leaves by the wrong door
Inbound packets still arrive on the physical interface, because a VPN client cannot change how the internet reaches your server's public address. Replies are a separate decision: for every packet, the kernel looks up the destination, and after --redirect-gateway the default entry says tunnel. The provider's exit sees a destination it does not serve and drops the packet.
OpenVPN's own protection covers exactly one destination. The 2.6 manual describes --redirect-gateway as a sequence: it adds a host route for the VPN peer through the gateway in use before the tunnel, so the encrypted traffic cannot loop back into itself, and then replaces the default route. That host route is for the peer only. Your SSH client is just another internet address and gets no exception.
One prefix mistake is worth ruling out early. ip route add -net 0.0.0.0 gw 10.8.0.1 dev tun0 without a length matches the single address 0.0.0.0, not the internet; it must be 0.0.0.0/0. And if taking the default breaks the tunnel's own transport, add the endpoint as a host route through the physical interface, using the provider's real address.
Pin your SSH client address for an immediate fix
The fastest repair is a host route for your client address through the gateway the server used before the tunnel existed. From your side, find the address the server will see, then read the server's table so you know the old gateway:
curl -s https://ifconfig.co
ip route
On the server, add the exception before the client starts. The addresses here are placeholders; the shape is what matters:
sudo ip route add 80.128.240.55 via 192.168.178.1
This is the fix used in the reported case, and it works immediately. It is also fragile in two predictable ways: the route disappears at reboot, and it becomes wrong the moment your ISP hands you a different address. Treat it as an emergency measure and make it durable on purpose, through a network unit or dispatcher hook, or accept that it is temporary and write down the recovery path.
Stop the client from taking the default
The cleaner option is to keep the tunnel from replacing the default route at all. route-nopull, used with --client or --pull, accepts pushed options except routes, block-outside-dns, and DHCP-style options such as DNS servers, so you add only the prefixes you need. A narrower tool is a pull filter, which drops one offending directive and keeps the rest:
route-nopull
pull-filter ignore "redirect-gateway"
Use one or the other, not both, and be clear about what changes. With no default route through the tunnel, the server no longer browses the internet through the provider; it reaches only what you routed explicitly. If the full tunnel was the entire point of the setup, this is the wrong knob and the host route above is the right one.
Split the return path by source or by connection
When the inbound address is stable, a source rule keeps replies local without touching the tunnel's own routes. Give the physical interface address its own table containing the LAN prefix and the physical default, then select that table for traffic sourced from it:
ip rule add from 192.168.1.11 table 128
ip route add table 128 to 192.168.1.0/24 dev eth0
ip route add table 128 default via 192.168.1.1
This works because replies to an inbound SSH connection are sourced from the address the client connected to. A reported case where every inbound service had to keep working used exactly this shape. When the inbound address floats or the path is dynamic, mark the connection instead: set a conntrack mark on the first packet arriving from the physical interface, copy it onto reply packets in OUTPUT, and route marked packets through a table that points at the physical gateway.
iptables -t mangle -A PREROUTING -i eth0 -m conntrack --ctstate NEW -j CONNMARK --set-mark 1234
iptables -t mangle -A OUTPUT -m connmark --mark 1234 -j MARK --set-mark 4321
ip route add default via 192.168.1.1 dev eth0 table 3412
ip rule add fwmark 4321 table 3412
Keep the connection mark, firewall mark and table number distinct while testing. Reusing one number for two roles produces a rule that looks correct and matches the wrong packets. The table's default entry also needs via with the LAN gateway on an ordinary Ethernet link; a bare dev eth0 default works only when the interface is point-to-point or the next hop is resolved another way. The reported Jessie host happened to sit on a link where the bare form worked, which is not a distribution rule to copy.
Verify without locking yourself out
Do not test by disconnecting. Keep the first session open, bring the tunnel up, and ask the kernel which path your own address would take:
ip route get 80.128.240.55
The answer should name the physical interface and the old gateway, not the tunnel. Then open a second SSH connection; that is the real test, because the old session may survive on state that a new connection cannot reproduce. If the second hangs while the first still answers, the rule never matched the traffic that matters, and the marks or the source address are where to look.
Keep an out-of-band console available for the whole exercise. A server whose only access path is the tunnel you just broke cannot be repaired over that tunnel, and the rollback is easier when you have written down the table numbers and prefixes you added.