Docker Desktop with VPN (pulse)

I am facing an issue with Docker and the VPN client my company is using, which is Pulse Secure VPN.
I have a couple of containers that connect with each other (iis, sql, solr) and are started via Docker Compose.

All goes well until I connect with VPN.
Each container is assigned an IP address (eg 172.25.146.208), the moment I connect with VPN or restart my containers. The following route is added automatically:

Destination Netmask Gateway Interface Metric
172.25.146.0 255.255.240.0 On-link 10.xx.xx.xx 1

  • xx hidden because its a company ip address

After the route is added:

  • containers can not find each other anymore, ping mssql from another container will timeout
  • I can not connect connect to exposed ports from my host anymore.

My guess is that the VPN client is rerouting all traffic to VPN.
I already tried different subnets, including some local ranges (192.168.5.x or 10.0.2.x) but whatever I try, the route is always added.

Any idea if there are solutions to this problems or maybe a workaround?

Had the same problem for my work setup on Linux, where we’re also bound to an Ivanti gateway with PulseSecure as VPN client without split-tunneling. The issue with this is that the PulseSecure client puts itself as the default gateway for every route or local interface it encounters and overwrites any of your IP route changes immediately.

Since you’re speaking of Docker Desktop, I assume you’re on Windows. I have no experience with DD since I’m using Rancher Desktop, but as far as it concerns Rancher, it’s not an issue there, since it integrates itself already in the Hypervisor (network-wise), so no noticable issues there for me.

On Linux (in my case AlmaLinux), however, I had to craft my own solution. Since you have a bit more power over policy based routing on Linux, you can place your own routes in a routing table which sits higher in the priority list than the main routing table. Say, you want to put your Docker related routes in a custom routing table named ‘docker’ which is consulted before the ‘main’ routing table (which the PulseSecure client operates on). For this, you’d first create that named table:

$ cat /etc/iproute2/rt_tables
200 docker

The number doesn’t really matter, it’s just an ID you pick for yourself. IDs 253–255 are already taken by the default tables ‘local’, ‘main’ and ‘default’. The rest is free to chose.

After that, you create a new IP rule for that table which funnels all routing requests first through that table, but before the ‘main’ table:

ip rule add table docker

That’s already it. It automaticaly creates a new rule with a priority one higher than the priority of the ‘main’ table. But you can also specify a custom priority, say 1000, by adding priority 1000 to the above command. Your IP rules should look similar to this:

$ ip rule list
0:      from all lookup local
32765:  from all lookup docker
32766:  from all lookup main
32767:  from all lookup default

Notice that the priority of the ‘docker’ table is one higher than the ‘main’ table (lower numbers mean higher priority).

The only thing left to do now is to add your IP routes for your Docker networks to that table. Assuming the default subnet of your Docker is 172.17.0.0/16, you would do it like so:

$ ip route add 172.17.0.0/16 dev docker0 metric 1

At least for me it was important that I also added the metric, otherwise the PulseSecure client would constantly throw up and get confused. Of course it’s up to you now to figure out all the subnets and interface names that you might have, but these are the basic commands that you need to talk to your local containers while not messing around with the IP routes set up by the PulseSecure client.

I for one wrote a little helper script for myself which hooks into Systemd and watches for any changes on Docker networks and alters routes accordingly in real time. You can get creative with the tools you have at hand.