强制容器出站流量通过另一个容器(没有主机 iptables 规则)

强制容器出站流量通过另一个容器(没有主机 iptables 规则)

我有两个容器,A& B

它们应该存在于新创建的 docker(私有)桥接网络上,以便:

  • A可以与网桥外部的世界对话,但只能通过指定的网关 IP
  • B无法与桥接器外通信,只能A通过特定的 IP + 端口进行通信
  • 不想要求使用 iptables 规则主持人
  • 不想要求--privileged其中之一AB但具体一点--device可能就可以了
  • 我同意使用--cap-dropB 来实现所需的隔离

我尝试了一些docker network create bridge private-bridge各种组合,docker run -net=private-bridge但无法获得我想要的行为。

有什么办法可以:

  • 使用 dockerbridge网络执行此操作?或者
  • 使用不同的标准网络类型来执行此操作?或者
  • 我应该考虑编写自定义 docker 网络插件吗?

答案1

看起来像一些需要某种 SDN,但是https://github.com/jpetazzo/pipework比 Contiv 等其他公司的产品启动起来要简单得多。

不需要额外的容器权限,并且容器(即使作为 root)也无法使用默认的 docker 容器权限来操作网桥或其自己的接口。

首先,安装pipework&brctl

sudo curl https://raw.githubusercontent.com/jpetazzo/pipework/master/pipework \
          > /usr/local/bin/pipework
sudo chmod u+x /usr/local/bin/pipework
sudo apt-get install bridge-utils

在我们开始之前,请检查当前定义的接口和桥接器:

brctl show | tail -n+2 | awk '{ print $1 }' | xargs echo 
# docker0

ifconfig | grep encap | awk '{ print $1 }' | xargs echo
# eth0 docker0 lo

对于此演示,我将创建一个名为的图像,net-tester其中包含基本的网络测试功能:

docker run -itd --name=jtest debian:jessie
docker exec -it jtest apt-get update
docker exec -it jtest apt-get install -y traceroute curl dnsutils \
                                         netcat-openbsd jq nmap \
                                         net-tools isc-dhcp-client telnet
docker exec -it jtest apt-get clean
docker commit -p jtest net-tester
docker stop jtest && docker rm jtest

在标准docker网络上启动两个容器none

docker run -itd --net=none --name=node-a net-tester
docker run -itd --net=none --name=node-b net-tester

docker exec -it node-a ifconfig | grep encap | awk '{ print $1 }' | xargs echo
# lo

docker exec -it node-b ifconfig | grep encap | awk '{ print $1 }' | xargs echo
# lo

创建br0桥接和,并在和br0上添加接口。node-anode-b

sudo pipework br0 node-a 192.168.10.1/24
sudo pipework br0 node-b 192.168.10.2/24

docker exec -it node-a ifconfig | grep encap | awk '{ print $1 }' | xargs echo
# eth1 lo

docker exec -it node-b ifconfig | grep encap | awk '{ print $1 }' | xargs echo
# eth1 lo

eth1在两个容器上都创建了(隐式命名的)接口。

现在让我们看看主机桥和接口:

brctl show | tail -n+2 | awk '{ print $1 }' | xargs echo
# br0 veth1pl31667 docker0

ifconfig | grep encap | awk '{ print $1 }' | xargs echo
# br0 docker0 eth0 lo veth1pl31645 veth1pl31667

让我们看看这些容器上的路由:

docker exec -it node-a route -n
# Kernel IP routing table
# Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
# 192.168.10.0    0.0.0.0         255.255.255.0   U     0      0        0 eth1

docker exec -it node-b route -n
# Kernel IP routing table
# Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
# 192.168.10.0    0.0.0.0         255.255.255.0   U     0      0        0 eth1

最后我们将eth0接口添加到node-b主机的子网。

就我而言:eth0 10.0.0.0/24 gw 1​​0.0.0.1。

sudo pipework eth0 -i eth0 node-b 10.0.0.99/[email protected]

显然这也可以通过 dhcp 来实现。

现在,检查接口node-b得到:

docker exec -it node-b ifconfig | grep encap | awk '{ print $1 }' | xargs echo
# eth0 eth1 lo

路由看起来是这样的:

docker exec -it node-b route -n
# Kernel IP routing table
# Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
# 0.0.0.0         10.0.0.1        0.0.0.0         UG    0      0        0 eth0
# 10.0.0.0        0.0.0.0         255.255.255.0   U     0      0        0 eth0
# 192.168.10.0    0.0.0.0         255.255.255.0   U     0      0        0 eth1

测试连通性node-a

docker exec -it node-a ping -c 3 192.168.10.2
# PING 192.168.10.2 (192.168.10.2): 56 data bytes
# 64 bytes from 192.168.10.2: icmp_seq=0 ttl=64 time=0.124 ms
# 64 bytes from 192.168.10.2: icmp_seq=1 ttl=64 time=0.101 ms
# 64 bytes from 192.168.10.2: icmp_seq=2 ttl=64 time=0.092 ms
# --- 192.168.10.2 ping statistics ---
# 3 packets transmitted, 3 packets received, 0% packet loss
# round-trip min/avg/max/stddev = 0.092/0.106/0.124/0.000 ms

docker exec -it node-a ping -c 3 10.0.0.1
# PING 10.0.0.1 (10.0.0.1): 56 data bytes
# ping: sending packet: Network is unreachable

docker exec -it node-a ping -c 3 8.8.8.8
# PING 8.8.8.8 (8.8.8.8): 56 data bytes
# ping: sending packet: Network is unreachable

测试连通性node-b

docker exec -it node-b ping -c 3 192.168.10.1
# PING 192.168.10.1 (192.168.10.1): 56 data bytes
# 64 bytes from 192.168.10.1: icmp_seq=0 ttl=64 time=0.102 ms
# 64 bytes from 192.168.10.1: icmp_seq=1 ttl=64 time=0.086 ms
# 64 bytes from 192.168.10.1: icmp_seq=2 ttl=64 time=0.087 ms
# --- 192.168.10.1 ping statistics ---
# 3 packets transmitted, 3 packets received, 0% packet loss
# round-trip min/avg/max/stddev = 0.086/0.092/0.102/0.000 ms

docker exec -it node-b ping -c 3 10.0.0.1
# PING 10.0.0.1 (10.0.0.1): 56 data bytes
# 64 bytes from 10.0.0.1: icmp_seq=0 ttl=64 time=0.312 ms
# 64 bytes from 10.0.0.1: icmp_seq=1 ttl=64 time=0.314 ms
# 64 bytes from 10.0.0.1: icmp_seq=2 ttl=64 time=0.289 ms
# --- 10.0.0.1 ping statistics ---
# 3 packets transmitted, 3 packets received, 0% packet loss
# round-trip min/avg/max/stddev = 0.289/0.305/0.314/0.000 ms

docker exec -it node-b ping -c 3 8.8.8.8
# PING 8.8.8.8 (8.8.8.8): 56 data bytes
# 64 bytes from 8.8.8.8: icmp_seq=0 ttl=56 time=19.309 ms
# 64 bytes from 8.8.8.8: icmp_seq=1 ttl=56 time=18.279 ms
# 64 bytes from 8.8.8.8: icmp_seq=2 ttl=56 time=19.827 ms
# --- 8.8.8.8 ping statistics ---
# 3 packets transmitted, 3 packets received, 0% packet loss
# round-trip min/avg/max/stddev = 18.279/19.138/19.827/0.643 ms

奇怪的是,虽然node-b可以 ping 主机以太网子网上的其他 IP,但它无法 ping 主机eth0本身的 IP。这实际上是我想要的,所以我不介意。

当两个容器都停止后,我们可以清理桥接:

sudo ifconfig br0 down
sudo brctl delbr br0

对于我的具体情况,如果我将某个守护进程绑定到,0.0.0.0该守护node-b进程将对主机以太网上的所有人都可见(通过10.0.0.99),所以我必须小心地专门绑定到192.168.10.2

相关内容