Docker 容器桥接模式禁用互联网
最近在 Docker 容器中用到了一些第三方的闭源程序,考虑到隐私和安全问题决定将容器禁用外网,但该容器同时又需要访问宿主机及宿主机网段下的其它主机,所以最终决定将容器禁用互联网保留指定局域网,本文将详细介绍实现过程。
1. 环境
- CentOS Linux release 7.9.2009 (Core);
- Docker 23.0.1;
2. 方案
2.1 方案一
利用 docker network 的--internal
参数限制 docker 网络外部访问,--internal
参数官方解释如下:
$ docker network create --help
Usage: docker network create [OPTIONS] NETWORK
Create a network
Options:
--attachable Enable manual container attachment
--aux-address map Auxiliary IPv4 or IPv6 addresses used by Network driver (default map[])
--config-from string The network from which to copy the configuration
--config-only Create a configuration only network
-d, --driver string Driver to manage the Network (default "bridge")
--gateway strings IPv4 or IPv6 Gateway for the master subnet
--ingress Create swarm routing-mesh network
--internal Restrict external access to the network
--ip-range strings Allocate container ip from a sub-range
--ipam-driver string IP Address Management Driver (default "default")
--ipam-opt map Set IPAM driver specific options (default map[])
--ipv6 Enable IPv6 networking
--label list Set metadata on a network
-o, --opt map Set driver specific options (default map[])
--scope string Control the network's scope
--subnet strings Subnet in CIDR format that represents a network segment
Restrict external access to the network 即限制 docker 网络的外部访问,经过测试该方式可以隔离互联网,但同时隔离了宿主机的访问,故不满足需求。
2.2 方案二
利用 docker network 的驱动选项com.docker.network.bridge.enable_ip_masquerade
禁用 docker 网络的 IP 伪装,官方解释如下:
Option | Equivalent | Description |
---|---|---|
com.docker.network.bridge.name |
- | Bridge name to be used when creating the Linux bridge |
com.docker.network.bridge.enable_ip_masquerade |
--ip-masq |
Enable IP masquerading |
com.docker.network.bridge.enable_icc |
--icc |
Enable or Disable Inter Container Connectivity |
com.docker.network.bridge.host_binding_ipv4 |
--ip |
Default IP when binding container ports |
com.docker.network.driver.mtu |
--mtu |
Set the containers network MTU |
com.docker.network.container_iface_prefix |
- | Set a custom prefix for container interfaces |
Enable IP masquerading 即是否开启 IP 伪装,使用方法如下:
docker network create --subnet 10.9.2.0/28 -o com.docker.network.bridge.enable_ip_masquerade=false my-test-network
# docker-compose.yml
version: "3.9"
services:
networks:
test:
driver: bridge
driver_opts:
com.docker.network.bridge.enable_ip_masquerade: false
经过测试该方式可以隔离互联网的同时访问宿主机,但无法访问宿主机网段下的其它主机,故不满足需求。
2.3 方案三
通过修改容器内部的路由表实现禁止访问互联网及允许访问指定局域网,具体思路如下:
- 删除容器内部的默认网关实现禁止访问互联网;
- 添加指定局域网段的路由实现指定局域网的访问;
该方式通过路由表的方式控制可访问的资源,灵活方便,完全满足笔者的需求。
3. 实现
本文仅介绍方案三的实现,方案一与方案二因不满足笔者需求,故此处不多介绍,感兴趣的读者自行研究。
下面将以 busybox 容器为例介绍如何通过修改路由表的方式实现容器禁用互联网及允许指定局域网。
网络 | IP 地址 |
---|---|
docker0 | 10.2.0.1/24 |
容器 | 10.2.0.5 |
宿主机局域网 | 172.20.2.1/24 |
宿主机 | 172.20.2.7 |
创建 busybox 容器;
# -it 交互式终端 # --rm 容器停止后自动删除 # --privileged 为容器提升权限,否则无法操作路由表 $ docker run -it --rm --privileged busybox sh
提示: 此处必须添加
--privileged
参数,否则无法在容器内部操作路由表。修改 busybox 容器路由表;
# 以下操作皆在容器交互式终端中执行 # 获取默认网关地址,例如 10.2.0.1 $ gw=$(route -n | tail -n +3 | grep U | grep G | awk '{print $2}') # 删除默认网关 $ route del default gw $gw # 添加指定局域网路由 $ route add -net 172.20.2.0 netmask 255.255.255.0 gw $gw
提示: 容器内部可能找不到
route
命令,根据容器系统版本选择合适的包管理工具手动安装即可。测试网络连通情况;
# 以下操作皆在容器交互式终端中执行 # 测试访问互联网,禁止访问 $ ping -c 4 www.baidu.com ping: bad address 'www.baidu.com' # 测试访问宿主机,允许访问 $ ping -c 4 172.20.2.7 PING 172.20.2.7 (172.20.2.7): 56 data bytes 64 bytes from 172.20.2.7: seq=0 ttl=64 time=0.091 ms 64 bytes from 172.20.2.7: seq=1 ttl=64 time=0.083 ms 64 bytes from 172.20.2.7: seq=2 ttl=64 time=0.077 ms 64 bytes from 172.20.2.7: seq=3 ttl=64 time=0.116 ms --- 172.20.2.7 ping statistics --- 4 packets transmitted, 4 packets received, 0% packet loss round-trip min/avg/max = 0.077/0.091/0.116 ms # 测试访问宿主机网段其它主机,允许访问 $ ping -c 4 172.20.2.5 PING 172.20.2.5 (172.20.2.5): 56 data bytes 64 bytes from 172.20.2.5: seq=0 ttl=63 time=0.279 ms 64 bytes from 172.20.2.5: seq=1 ttl=63 time=0.352 ms 64 bytes from 172.20.2.5: seq=2 ttl=63 time=0.345 ms 64 bytes from 172.20.2.5: seq=3 ttl=63 time=0.277 ms --- 172.20.2.5 ping statistics --- 4 packets transmitted, 4 packets received, 0% packet loss round-trip min/avg/max = 0.277/0.313/0.352 ms
# 以下操作皆在宿主机中执行 # 测试访问容器,允许访问 $ ping -c 4 10.2.0.5 PING 10.2.0.5 (10.2.0.5) 56(84) bytes of data. 64 bytes from 10.2.0.5: icmp_seq=1 ttl=64 time=0.051 ms 64 bytes from 10.2.0.5: icmp_seq=2 ttl=64 time=0.050 ms 64 bytes from 10.2.0.5: icmp_seq=3 ttl=64 time=0.064 ms 64 bytes from 10.2.0.5: icmp_seq=4 ttl=64 time=0.062 ms --- 10.2.0.5 ping statistics --- 4 packets transmitted, 4 received, 0% packet loss, time 2999ms rtt min/avg/max/mdev = 0.050/0.056/0.064/0.011 ms
路由表持久化;
至此已经实现了本文的需求,但是路由表的修改容器重建后便会失效,因此需要将路由表的修改进行持久化。持久化的方式视情况而定(例如添加到
entrypoint.sh
脚本中),本文不在进行赘述。
参考文章: