允许 Docker 容器在主机上使用 IPSEC VPN

允许 Docker 容器在主机上使用 IPSEC VPN

我的工作站上有 Docker 和 IPSEC VPN 隧道,但容器无法访问 VPN 后面的主机。我能做些什么吗,例如使用 IPTables 或路由来允许访问?[注意:我目前正在 libvirt 托管的虚拟机内运行这些测试,然后 libvirt 桥的地址为 192.168.122.1]

我的网络设置如下:

1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN 
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: ens3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
    link/ether 52:54:00:0f:f2:bb brd ff:ff:ff:ff:ff:ff
    inet 192.168.122.87/24 brd 192.168.122.255 scope global dynamic ens3
       valid_lft 3490sec preferred_lft 3490sec
    inet6 fe80::5054:ff:fe0f:f2bb/64 scope link 
       valid_lft forever preferred_lft forever
3: tap0: <BROADCAST,UP,LOWER_UP> mtu 1380 qdisc pfifo_fast state UNKNOWN qlen 500
    link/ether d6:2b:f6:24:c5:1c brd ff:ff:ff:ff:ff:ff
    inet 172.20.1.29/24 brd 172.20.1.255 scope global tap0
       valid_lft forever preferred_lft forever
    inet6 fe80::d42b:f6ff:fe24:c51c/64 scope link 
       valid_lft forever preferred_lft forever
4: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP 
    link/ether 02:42:24:7f:6a:1a brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.1/16 scope global docker0
       valid_lft forever preferred_lft forever
    inet6 fe80::42:24ff:fe7f:6a1a/64 scope link 
       valid_lft forever preferred_lft forever

我的路线如下(VPN 端点 IP 已混淆):

default via 192.168.122.1 dev ens3  proto static  metric 100 
10.0.0.0/8 via 172.20.1.29 dev tap0  proto static 
10.11.12.13 via 192.168.122.1 dev ens3  proto static 
172.17.0.0/16 dev docker0  proto kernel  scope link  src 172.17.0.1 
172.20.1.0/24 dev tap0  proto kernel  scope link  src 172.20.1.29 
192.168.122.0/24 dev ens3  proto kernel  scope link  src 192.168.122.87  metric 100

我可以看到一些现有的 masq 规则,当我从容器中发送测试 ping 时,第一条规则上的数据包计数器会增加:

[robin@rhel72 ~]$ sudo iptables -t nat -L -n -v --line-numbers | sed -n '/^Chain POSTROUTING /,/^$/ p'
Chain POSTROUTING (policy ACCEPT 4 packets, 534 bytes)
num   pkts bytes target     prot opt in     out     source               destination         
1       63  4290 MASQUERADE  all  --  *      !docker0  172.17.0.0/16        0.0.0.0/0           
2      473 30282 POSTROUTING_direct  all  --  *      *       0.0.0.0/0            0.0.0.0/0           
3      473 30282 POSTROUTING_ZONES_SOURCE  all  --  *      *       0.0.0.0/0            0.0.0.0/0           
4      473 30282 POSTROUTING_ZONES  all  --  *      *       0.0.0.0/0            0.0.0.0/0

ping -c1最后,当我在容器内部运行时对连接进行 tcpdump 时,我可以看到类似部分响应的内容:

[robin@rhel72 ~]$ sudo tcpdump -i any -n 'icmp'
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on any, link-type LINUX_SLL (Linux cooked), capture size 65535 bytes
09:55:01.919214 IP 172.17.0.2 > 10.60.1.201: ICMP echo request, id 27, seq 1, length 64
09:55:01.919214 IP 172.17.0.2 > 10.60.1.201: ICMP echo request, id 27, seq 1, length 64
09:55:01.940613 IP 10.60.1.201 > 172.20.1.29: ICMP echo reply, id 27, seq 1, length 64

编辑1

Docker容器内的路由表是:

[root@451c1c9c708c /]# ip route
default via 172.17.0.1 dev eth0 
172.17.0.0/16 dev eth0  proto kernel  scope link  src 172.17.0.2

值得注意的是,我正在运行 Docker 守护程序并使用该--icc=false标志来隔离容器。

答案1

我编写了一个 python 程序来包装 ipsec,安装 iptables 条目以允许 docker 容器与 VPN 隧道通信:

https://github.com/cbrichford/docker-ipsec

而不是这样做: ipsec up

你做: docker-ipsec up

该脚本可能需要一些工作才能与最新的 docker 网络功能一起使用,但可以与旧的默认docker0网桥一起使用。

如果你不想使用我的脚本来编辑 iptables,可以按照以下方式构建 iptables 命令:

  1. 找出 VPN 中主机的虚拟 IP 地址。将其称为 virtualIP。
  2. 找出 VPN 中 IP 地址的 CIDR 块。如果 VPN 中的 IP 地址始终为 10.10.XX 格式,则您的 CIDR 块将为 10.10.0.0/16。将此称为 vpnSubnet
  3. 找出默认路由接口。这将类似于“eth0”。这是我用来查找它的命令:sudo ip route show | grep -e "^default" | awk -- "{ print \$5 }"。将其称为 defaultRouteInterface。
  4. 找出您想要授予 VPN 访问权限的 docker 网络的 CIDR 块。我使用以下命令找到它:sudo ip route show | grep -e "[[:space:]]dev[[:space:]]docker" | awk -- "{ print \$1 }"。将其称为 dockerSubnet。

您需要运行的 iptables 命令是: sudo \ iptables \ -j SNAT \ -t nat \ -I POSTROUTING 1 \ -o ${defaultRouteInterface} \ -d "${vpnSubnet}" \ -s "${dockerSubnet}" \ --to-source "${virtualIP}"

答案2

这个答案已经有一段时间没有更新了。根据@ChristopherBrichford的回答,您可以使用以下命令将docker0网络重定向到VPN。它与@CodeMedic的命令略有不同,因为某些操作系统可能不再可用ifconfig

将 docker0 上所有容器的流量引导至 VPN

sudo iptables -j SNAT -t nat -I POSTROUTING 1 \
    -o $(ip route show | grep -e "^default" | awk -- "{ print \$5 }") \
    -d $(ip route list table 220 | grep -o '^[0-9.]*/[0-9]*') \
    -s $(ip route show | grep -e "[[:space:]]dev[[:space:]]docker" | awk -- "{ print \$1 }") \
    --to-source $(ip -4 -o addr show | awk '/ (lo|docker|br-|tun)/ {next;} /valid_lft forever/ {print $4}' | cut -d/ -f1)

10.0.10.0/24如果 VPN 子网是docker0 子网172.17.0.0/16,则该命令的最终结果将如下所示:

sudo iptables -j SNAT -t nat -I POSTROUTING 1 \
    -o eth0 \
    -d "10.0.10.0/24" \
    -s "172.17.0.0/16" \
    --to-source "10.0.10.111"

将流量从 br-# 上的特定容器引导至 VPN

您还可以从特定的正在运行的容器重定向 VPN 流量。要实现此功能,只能有一个 docker 网络处于活动状态,因为它会根据活动的 docker 网络对其进行过滤。

sudo iptables -j SNAT -t nat -I POSTROUTING 1 \
    -o $(ip route show | grep -e "^default" | awk -- "{ print \$5 }") \
    -d $(ip route list table 220 | grep -o '^[0-9.]*/[0-9]*') \
    -s $(ip route show | grep -e "[[:space:]]dev[[:space:]]docker" | awk -- "{ print \$1 }") \
    --to-source $(ip route show | awk '/ (linkdown)/ {next;} /br-/ {print $1}')

最终结果应与下一个示例类似。请注意所用源网络的差异:

sudo iptables -j SNAT -t nat -I POSTROUTING 1 \
    -o eth0 \
    -d "10.0.10.0/24" \
    -s "172.20.0.0/16" \
    --to-source "10.0.10.111"

答案3

根据@ChristopherBrichford的回答,我设法将公式归结为以下命令。这是基于我对所涉及的各种活动部件的有限理解。

sudo iptables -j SNAT -t nat -I POSTROUTING 1 \
    -o $(ip route show | grep -e "^default" | awk -- "{ print \$5 }") \
    -d $(ip route list table 220 | grep -o '^[0-9.]*/[0-9]*') \
    -s $(ip route show | grep -e ":space:dev:space:docker" | awk -- "{ print \$1 }") \
    --to-source $(ifconfig | grep -o 'P-t-P:[^ ]*' | cut -d: -f2)

一个更实用和更新的版本是可用作要点

相关内容