You want the SOCKS proxy's traffic to leave through the VPN while the rest of the host keeps its normal path. Two designs do that, and they differ in what breaks when something goes wrong. One pushes a default route into the tunnel and then works around it; the other leaves the main table alone and sends a single source address to a second table. The second is cleaner, and it has traps of its own.

Two designs, two different blast radii

The full-tunnel design is what most VPN clients do by default: replace the default route so everything follows the tunnel, then exclude what should not. It is easy to reason about and hard to keep narrow, because every local service you add inherits the tunnel until you exclude it. The failure mode is broad, since one wrong route takes the host's own connectivity with it.

The source-selected design goes the other way. The main table stays as it was, and one policy rule sends packets from a specific local address to a table with a tunnel default. Nothing else changes, which means the blast radius is one address.

That narrowness is also the constraint: the design only works if the proxy's outgoing packets really carry that address.

What Dante's external setting does

The external setting picks the addresses the proxy uses for outgoing connections; it does not touch the kernel's routing policy. That separation is what makes the second design possible, and it is also where people misread the documentation. Pointing external at a tunnel address makes the proxy source its connections from that address, and the routing table then decides where those packets go.

The listener is a separate concern. Keep internal restricted to the clients that should use the proxy, require authentication, and firewall the port, because an open listener is not a feature of VPN egress.

Confirm that the configured address actually belongs to the tunnel inside the proxy's network namespace.

Building the source-selected table

For a TUN address of 10.90.0.2/24 with a reachable peer at 10.90.0.1, and a table number nothing else uses, the arrangement looks like this:

ip -4 route add table 100 10.90.0.0/24 dev tun0 src 10.90.0.2
ip -4 route add table 100 default via 10.90.0.1 dev tun0 metric 10
ip -4 route add table 100 unreachable default metric 32760
ip -4 rule add priority 1000 from 10.90.0.2/32 lookup 100

The unreachable route is the part people skip. Without it, a table whose tunnel route disappears falls through to the ordinary default and the proxy silently leaves by the WAN. With it, the lookup fails, which is a visible failure instead of a quiet leak. The rule's priority also has to sit ahead of anything else matching that source.

These values describe one topology. Point-to-point peers, net30 addressing and clients that hand out a different prefix all need their own routes, so copy the shape rather than the addresses.

Where the source rule leaks

A rule matching a source address affects every process that uses it, not just the proxy. If another daemon binds the same address, its traffic takes the tunnel too, and the failure will look like an unrelated service problem. Give the proxy an address nothing else uses, or accept that the scope is the address rather than the process.

DNS is the other leak. A resolver socket does not necessarily use the proxy's external data-connection source, so name lookups can still leave by the WAN. Test resolution separately from connection establishment, and decide whether that outcome is acceptable.

IPv6 needs its own rules and routes, because an IPv4 arrangement proves nothing about it.

Pick by the failure you can tolerate

Choose the full tunnel when the host should be protected as a whole and you can maintain the exclusion list. Choose source-based routing when the host must keep its own path and only one service should be redirected. Both are legitimate; the mistake is picking one and then testing it as if it were the other.

Whichever you choose, verify from both sides: a request through the proxy and a direct request from the host, with the observed exit address recorded for each.

If the tunnel drops, decide in advance whether the proxy should fail or fall back, and let the routing table express that decision.

Inyandiko zihuye