如何让 openvpn 与 docker 一起工作

How make openvpn work with docker

我最近安装了 privacy vpn,结果发现启用 openvpn 中断 docker。

当我尝试 运行 docker-compose up 时出现以下错误

ERROR: could not find an available, non-overlapping IPv4 address pool among the defaults to assign to the network

禁用 vpn 可以解决问题(但我宁愿不禁用它)。有什么办法可以让这两者和平共处呢?我使用 debian jessie,我的 openvpn 有以下版本 string

 OpenVPN 2.3.4 x86_64-pc-linux-gnu [SSL (OpenSSL)] [LZO] [EPOLL] [PKCS11] [MH] [IPv6] built on Jun 26 2017

很多人"solved"这个问题是通过禁用 openvpn 来解决的,所以我特地询问如何让这两个同时工作。

参考文献:

如果这有什么不同,我的 vpn 提供商是:https://www.ovpn.com/ 这里是(经过一些编辑的)配置文件:

client
dev tun

proto udp

remote host port
remote-random

mute-replay-warnings
replay-window 256

push "dhcp-option DNS 46.227.67.134"    
push "dhcp-option DNS 192.165.9.158"

remote-cert-tls server
cipher aes-256-cbc
pull

nobind
reneg-sec 432000
resolv-retry infinite

comp-lzo
verb 1

persist-key
persist-tun
auth-user-pass /etc/openvpn/credentials
ca ovpn-ca.crt
tls-auth ovpn-tls.key 1

解决方案 (TL;DR;)

使用以下内容创建 /etc/openvpn/fix-routes.sh 脚本:

#!/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

向文件添加可执行位:chmod o+x /etc/openvpn/fix-routes.sh。将此文件的所有者更改为 root:chown root:root /etc/openvpn/fix-routes.sh

将以下两行添加到您的配置中:

 script-security 2
 route-up  /etc/openvpn/fix-routes.sh

说明

Openvpn 为以下网络添加路由:0.0.0.0/1128.0.0.0/1(这些路由覆盖整个 IP 范围),并且 docker 无法找到创建它的 IP 地址范围自己的专用网络。

您需要添加默认路由(通过 openvpn 路由所有内容)并禁用这两个特定路由。 fix-routes 脚本可以做到这一点。

openvpn 添加自己的路由后调用此脚本。要执行脚本,您需要将 script-security 设置为 2,这允许从 openvpn 上下文执行 bash 脚本。

谢谢

我要感谢author of this comment on github, also thanks to ovpn support

如果您在 docker 撰写文件中定义子网 CIDR,您还可以获得 docker-撰写工作:

networks:
  your-network:
   ipam:
      config:
      - subnet: 172.16.238.0/24
        gateway: 172.16.238.1

另一种选择:首先创建具有子网 CIDR 的网络,然后在 docker 撰写文件中指定您要使用此网络:

docker network create your-network --subnet 172.24.24.0/24

在您的 docker 撰写文件中:

networks:
  your-network:
    external: true

这里有一些额外的上下文:0.0.0.0 和 128.0.0.0 路由仅在 OpenVPN 服务器(也称为访问服务器)配置为推送路由以通过 VPN 发送所有端点的 Internet 流量时创建。通过添加这些广泛的路由,可以在不干扰本地 LAN 路由的情况下路由用户的 Internet 流量,并确保端点仍然能够将 OpenVPN 流量本身路由到本地路由器。

如果不需要通过 OpenVPN 服务器发送所有互联网流量,您最好让您的 VPN 管理员创建一个配置文件,该配置文件仅通过以下方式将流量路由到所需的目的地(例如私有 IP 地址范围) VPN 而不是一切。这应该避免弄乱端点上的路由。

也许一种方法是添加除 172.16.0.0/12 之外的所有路由以通过 VPN 进行路由,这样我们可以确保所有出去的事情都得到妥善处理:

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

基于 ,这里有一个使用 PostgreSQL 的完整分步示例。

在未连接 VPN 的情况下,创建永久 docker network:

docker network create my-network --subnet 172.24.24.0/24

在 docker-compose 文件中,将网络指定为外部网络:

version: "2"<br>
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
    <b>networks:
      - default</b>
    ports:
      - "127.0.0.1:5432:5432"<br>
<b>networks:
  default:
    external:
      name: my-network</b>
</pre>

就是这样。现在您可以像往常一样启用您的 VPN 和 start/stop 容器:

docker-compose up -d
docker-compose down

无需每次都打开 VPN on/off,或以 root 身份添加奇怪的脚本。

免责声明:

此解决方案最初设计用于下一个配置:

  • Ubuntu 18.04
  • OpenVPN 2.4.4
  • Docker-CE 19.03.5
  • Docker-Compose 1.24.0
  • 桥接 IPV4 网络
  • Docker-Swarm 未使用

其他配置可能有所不同。


问题

开始您的 VPN 连接。


案例一

当您尝试重新启动 docker 守护程序时,您将在日志中看到:

failed to start daemon: Error initializing network controller: list bridge addresses failed: PredefinedLocalScopeDefaultNetworks


案例二

当您尝试创建桥接网络时(隐式地 dockerdocker-compose 尝试创建这种网络)在下一个案例中:

  • docker create network 没有定义子网参数
  • docker-compose up 没有定义子网参数

你会得到:

ERROR: could not find an available, non-overlapping IPv4 address pool among the defaults to assign to the network

解决方案 (TL;BPR)

    来自 private address space 的 docker 网络的
  1. Select 地址范围未计划用于您的 VPN 内的资源。假设是172.26.0.0/16.

  2. 将更改添加到 Docker 的守护程序配置文件 daemon.json 文件。 :

    {
        "bip": "172.26.0.1/17",
        "fixed-cidr": "172.26.0.0/17", 
        "default-address-pools" : [
            {
                "base" : "172.26.128.0/17",
                "size" : 24
            }
        ]
    }
    

    其中:

    • bip - 又名«bridge ip»:docker0桥接网络的特定桥接IP地址,如果未指定其他则默认使用.
    • fixed-cidr - docker0 接口和本地容器的 CIDR 范围。仅当您想限制 bip.
    • 定义的 IP 范围时才需要
    • default-address-pools - docker_gwbridgedocker-swarm 需要)接口和桥接网络的 CIDR 范围。 size 参数设置此范围内新建网络的默认子掩码。


    在本例中,我们将初始 172.26.0.0/16 范围除以相等的 172.26.0.0 - 172.26.127.255172.26.128.0 - 172.26.255.255 池。

    小心 daemon.json 格式化,否则重启 docker 的守护程序时会出现这样的错误

    unable to configure the Docker daemon with file /etc/docker/daemon.json
    
  3. 运行 你的 VPN 连接
  4. 通过运行 命令查找您的设备名称。通常它类似于 tun0
    ip addr show type tun
    
  5. 显示创建的路线
    ip route show dev tun0
    
  6. 找到与我们选择的地址重叠的矿池,让它成为:
    172.16.0.0/12 via 10.8.0.1
    
  7. 使用我们选择的 Docker 池 172.26.0.0/16 在块上拆分子网上的池。您可以使用 this amazing calculator by David C。我们有:

    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
    
  8. 创建 /etc/openvpn/mynetwork-route-up.sh 脚本,用于 OpenVPN 从路由中排除我们的子网,内容如下(注意我们排除了我们的网络):

    #!/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
    
  9. 使用以下内容创建 /etc/openvpn/mynetwork-route-pre-down.sh 脚本(注意我们排除了我们的网络):

    #!/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
    
  10. 使该脚本可执行

    sudo chmod u+x /etc/openvpn/mynetwork-route-up.sh
    sudo chmod u+x /etc/openvpn/mynetwork-route-pre-down.sh
    
  11. 将此行添加到 .ovpn 配置的末尾

    script-security 2
    route-up /etc/openvpn/mynetwork-route-up.sh
    route-pre-down /etc/openvpn/mynetwork-route-pre-down.sh
    
  12. 重启你的 OpenVPN

  13. 运行(用于删除守护程序重新启动时可能发生冲突的网络)

docker network prune
  1. 重新启动 Docker 守护进程
    sudo service docker restart
    

原因

OpenVPN 经常用于通过隧道或至少代理专用池路由所有流量。那么为什么 docker 在开始时会失败?

案例一

当您启动 Docker 守护进程时,它会检查守护进程的配置桥接网络是否与路由重叠(向上->向下堆栈跟踪):

您可以 see here,您也可以在守护程序配置中禁用创建默认桥接网络以修复此错误。


案例二

当 Docker 的组件 libnetwork 尝试创建它的网络时,它会检查所有可用地址是否与路由重叠。如果没有找到,它 returns 一个错误(向上->向下堆栈跟踪):


当然,也存在出现此错误的其他情况。必须抓住他们!


解决方法(不推荐)

使用子网参数创建网络

Docker 允许您显式传递子网地址范围,并且在这种情况下似乎不执行重叠检查。

https://github.com/docker/libnetwork/blob/922cd533eac14b6e0754756c5cacf9f44af5d699/network.go#L1657


OpenVPN停止时创建网络,然后启动

我没有深入研究,但我认为,OpenVPN 不会检查重叠。


P.S.

谢谢(https://whosebug.com/users/7918/jb)[jb] for ,写这个答案给了我很多启发。

深入了解网络Docker,可以阅读这篇文章:

此外,don't forget this!

导致问题的默认路由由 OpenVPN 服务器推送到 OpenVPN 客户端。

而不是创建一个脚本来删除路由,您可以简单地首先停止创建有问题的路由。

有两种方法可以做到这一点:

  1. 如果您可以更改 OpenVPN 服务器上的设置,请编辑配置并删除 redirect-gateway 选项。在我的 EdgeRouter 上,相关行如下所示:

    openvpn-option "--push redirect-gateway def1"

    一个 Linux 服务器我相信它看起来像这样:

    push "redirect-gateway def1"

  2. 如果您无法更改 OpenVPN 服务器上的设置,您可以告诉您的 OpenVPN 客户端忽略来自服务器的推送路由。在我的 Linux 客户端上,相关行如下所示:

    pull-filter ignore redirect-gateway

完成这些更改并重新启动 OpenVPN 服务后,您应该能够启动 Docker 容器而不会出现可怕的 could not find an available, non-overlapping IPv4 address 错误。

对我有用的是: 看完后

Creating network "airflow_default" with the default driver
ERROR: could not find an available, non-overlapping IPv4 address pool 
among the defaults to assign to the network

只需手动创建该网络:

docker network create airflow_default --subnet 172.24.24.0/24