I have recently installed privacy vpn, and it turns out that enabled openvpn breaks docker.
When I try to run docker-compose up
i get following error <
Based on answer from Anas El Barkani, here's a complete step-by-step example using PostgreSQL.
While VPN is not connected, create a permanent docker network:
docker network create my-network --subnet 172.24.24.0/24
In docker-compose file, specify network as external:
version: "2"
services:
postgres:
container_name: postgres
image: postgres
volumes:
- ./volumes/postgres/data:/var/lib/postgresql/data
environment:
- POSTGRES_DB=dummy
- POSTGRES_USER=user
- POSTGRES_PASSWORD=123456
- POSTGRES_HOST=localhost
networks:
- default
ports:
- "127.0.0.1:5432:5432"
networks:
default:
external:
name: my-network
That's all. Now you can enable your VPN, and start/stop container as usual:
docker-compose up -d
docker-compose down
No need to turn VPN on/off every time, or to add weird scripts as root.
Create /etc/openvpn/fix-routes.sh
script with following contents:
#!/bin/sh
echo "Adding default route to $route_vpn_gateway with /0 mask..."
ip route add default via $route_vpn_gateway
echo "Removing /1 routes..."
ip route del 0.0.0.0/1 via $route_vpn_gateway
ip route del 128.0.0.0/1 via $route_vpn_gateway
Add executable bit to the file: chmod o+x /etc/openvpn/fix-routes.sh
. Change owner of this file to root: chown root:root /etc/openvpn/fix-routes.sh
.
Add to your config following two lines:
script-security 2
route-up /etc/openvpn/fix-routes.sh
Openvpn adds routes that for following networks: 0.0.0.0/1
and 128.0.0.0/1
(these routes cover entire IP range), and docker can't find range of IP addresses to create it's own private network.
You need to add a default route (to route everything through openvpn) and disable these two specific routes. fix-routes
script does that.
This script is called after openvpn adds its own routes. To execute scripts you'll need to set script-security
to 2
which allows execution of bash scripts from openvpn context.
I'd like to thank author of this comment on github, also thanks to ovpn support.
This solution originally designed for next configuration:
and may differ for other configurations.
Start your VPN connection.
Case 1
When you try to restart docker daemon you'll get in the logs:
failed to start daemon: Error initializing network controller: list bridge addresses failed: PredefinedLocalScopeDefaultNetworks
Case 2
When you try to create bridge network (implicitly docker
and docker-compose
try to create this kind of network) within next cases:
docker create network
without defining subnet parameterdocker-compose up
without defining subnet parameteryou'll get:
ERROR: could not find an available, non-overlapping IPv4 address pool among the defaults to assign to the network
Select address range for docker network from the private address space that not planned be to use for resources inside your VPN. Imagine that it is 172.26.0.0/16
.
Add changes to the Docker's daemon config file daemon.json file. :
{
"bip": "172.26.0.1/17",
"fixed-cidr": "172.26.0.0/17",
"default-address-pools" : [
{
"base" : "172.26.128.0/17",
"size" : 24
}
]
}
Where:
bip
- aka «bridge ip»: specific bridge IP address for the docker0
bridge network, which used by default if other was not specified.fixed-cidr
- CIDR range for docker0
interface and local containers. Only needed if you want to restrict the IP range defined by bip
.default-address-pools
- CIDR range for docker_gwbridge
(needed for docker-swarm) interface and bridge networks. size
parameter set the default submask for newly created networks inside this range.
We divide our initial 172.26.0.0/16
range by equal 172.26.0.0 - 172.26.127.255
and 172.26.128.0 - 172.26.255.255
pools in this example.
Be careful with daemon.json
formating, otherwise you'd get the error like this when restart docker's daemon
unable to configure the Docker daemon with file /etc/docker/daemon.json
tun0
ip addr show type tun
ip route show dev tun0
172.16.0.0/12 via 10.8.0.1
Split that pool on subnets on chunks with our selected Docker pool 172.26.0.0/16
. You can use this amazing calculator by David C. We've got:
172.16.0.1/13
172.24.0.1/15
172.26.0.0/16
172.27.0.1/16
172.28.0.1/14
Create /etc/openvpn/mynetwork-route-up.sh
script, for OpenVPN for excluding our subnet from routes, with following contents (note that we excluded our network):
#!/usr/bin/env bash
echo "Remove the route that conflicts with the Docker's subnet"
ip route del 172.16.0.0/12 via $route_vpn_gateway
echo "Bring back routes that don't intersect"
ip route add 172.16.0.0/13 via $route_vpn_gateway dev $dev
ip route add 172.24.0.0/15 via $route_vpn_gateway dev $dev
ip route add 172.27.0.0/16 via $route_vpn_gateway dev $dev
ip route add 172.28.0.0/14 via $route_vpn_gateway dev $dev
Create /etc/openvpn/mynetwork-route-pre-down.sh
script with following contents (note that we excluded our network):
#!/usr/bin/env bash
echo "Remove manually created routes"
ip route del 172.16.0.0/13 dev $dev
ip route del 172.24.0.0/15 dev $dev
ip route del 172.27.0.0/16 dev $dev
ip route del 172.28.0.0/14 dev $dev
echo "Creating original route because OpenVPN will try to del that"
ip route add 172.16.0.0/12 via $route_vpn_gateway dev $dev
Make that scripts executable
sudo chmod u+x /etc/openvpn/mynetwork-route-up.sh
sudo chmod u+x /etc/openvpn/mynetwork-route-pre-down.sh
Add this lines to the end of your .ovpn
config
script-security 2
route-up /etc/openvpn/mynetwork-route-up.sh
route-pre-down /etc/openvpn/mynetwork-route-pre-down.sh
Restart your OpenVPN
Run (for removing networks that may conflict when daemon will restart)
docker network prune
sudo service docker restart
OpenVPN used frequently to route all traffic through tunnel, or at least, proxy private pools. So why docker fails, when it started?
Case 1
When you starting the Docker daemon, it checks daemon's config bridge network for overlaping with routes (up->down stacktrace):
Some additional context here: the 0.0.0.0 and 128.0.0.0 routes are only created if the OpenVPN server (aka Access Server) is configured to push routes to send all the endpoint's Internet traffic via the VPN. By adding these broad routes, the user's Internet traffic can be routed while not interfering with routing on the local LAN, and ensuring that the endpoint remains able to route the OpenVPN traffic itself to the local router.
If sending all Internet traffic via the OpenVPN server isn't a requirement, you may be better off asking your VPN admin to create a profile that only routes traffic to required destinations (such as private IP address ranges) via the VPN instead of everything. That should avoid having to mess with the routes on the endpoint.
Maybe one way to do it is to add all routes excluding 172.16.0.0/12 to route through VPN so we are sure everything going out is properly handled:
sudo ip route add 192.0.0.0/2 via $route_vpn_gateway
sudo ip route add 128.0.0.0/3 via $route_vpn_gateway
sudo ip route add 176.0.0.0/4 via $route_vpn_gateway
sudo ip route add 160.0.0.0/5 via $route_vpn_gateway
sudo ip route add 168.0.0.0/6 via $route_vpn_gateway
sudo ip route add 174.0.0.0/7 via $route_vpn_gateway
sudo ip route add 173.0.0.0/8 via $route_vpn_gateway
sudo ip route add 172.128.0.0/9 via $route_vpn_gateway
sudo ip route add 172.64.0.0/10 via $route_vpn_gateway
sudo ip route add 172.32.0.0/11 via $route_vpn_gateway
sudo ip route add 172.0.0.0/12 via $route_vpn_gateway
# And finally delete the default route which handle 172.16.0.0/12
sudo ip route del 128.0.0.0/1 via $route_vpn_gateway
You can also get docker-compose working if you define the subnet CIDR in your docker compose file:
networks:
your-network:
ipam:
config:
- subnet: 172.16.238.0/24
gateway: 172.16.238.1
Another option: create first the network with the subnet CIDR and then specify in the docker compose file that you want to use this network:
docker network create your-network --subnet 172.24.24.0/24
In your docker compose file:
networks:
your-network:
external: true