禁止〖 IP + 端口 〗访问〖 Docker 〗〖 汇总版 〗
概况
分为以下几种情形。第一种,使用 host 网络模式,则直接通过 iptables INPUT 规则即可限制,缺点很明显,容器越来越多,则必然端口冲突。第二种,服务器厂家自带外置防火墙,例如 linode 防火墙,可以直接定义端口的规则,可以无视容器规则带来的控制问题,缺点的话,不是每一个厂家都提供这种服务。第三种,直接不暴露端口,改用 Nginx 容器所在网络,必须反代使用,缺点几乎没有,非常特殊的需求下会有冲突,例如这几天鼓捣的最终目的就是把反代迁移出去,因为 443 和 80 端口需要让位给 X-UI 使用。第四种,使用 bridge 网络模式,包括自定义 bridge 网络,需要定义 iptables DOCKER-USER 规则,特别重要的是屏蔽端口不能是映射到宿主的端口,必须是容器内部端口,这是核心关键点所在,还需要固定容器 IP 地址,不然的话多个容器同时存在时会开机打乱地址,而且开机自启动需要晚于系统创建 DOCKER-USER 规则链的时间,否则不生效,这一种可以说是最完美的方案,没有任何缺点,硬要说,也只有一点,就是必须手动创建规则,不过可以借助脚本,同时可以开机自动运行,因为每次重启,Docker 都会自动重新创建规则。大致无外乎就是这四种情形。第三种之前文章已经记录过,前两种不需要刻意记录,搜索即可看到大把教程。说是〖最终汇总版〗,其实重点就是记录最后一种情况的步骤,也算是这几天没有白忙活,翻遍各种网络教程,结合 ChatGPT 之助攻,感觉自己有所收获。下面开始记录,同时把 IPv6 一起涵盖记录。
由于把 IPv6 情形涵盖在内,首先部署 robbertkl/ipv6nat 容器,实现 IPv6 的 NAT 转发功能,分配内部 IPv6 地址,创建自定义 Docker 网络,把容器加入到这个自定义网络中。同时需要定义固定的 IP 地址,包括 IPv4 和 IPv6 两者。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
# 部署 robbertkl/ipv6nat 容器 docker run -d --name ipv6nat --privileged --network host --restart unless-stopped -v /var/run/docker.sock:/var/run/docker.sock:ro -v /lib/modules:/lib/modules:ro robbertkl/ipv6nat # 创建自定义 Docker 网络 docker network create my-net-ipv6 --ipv6 --subnet="fd00:1::/80" --gateway="fd00:1::1" # 定义容器 IP 地址并加入到这个自定义网络中 docker run -d \ ······ -p 6789:8000 \ --net my-net-ipv6 \ --ip 172.22.0.3 \ --ip6 fd00:1::3 \ ······ 或者 version: '3' services: app: image: your_image_name ports: - 6789:8000 networks: my-net-ipv6: ipv4_address: 172.22.0.3 ipv6_address: fd00:1::3 # 其他参数 networks: my-net-ipv6: external: true |
创建规则脚本,并创建开机自启动服务,其中规则脚本开机自启动需要晚于系统创建 DOCKER-USER 规则链的时间,否则不生效。故需要定义脚本中循环等待检查的参数。另外 IPv4 和 IPv6 需要分别定义。屏蔽端口不能是映射到宿主的端口,必须是容器内部端口,这是核心关键点所在。同样是不定义本地 IP 地址,但是,通过 iptables INPUT 规则限制的端口,就算是本地节点,亦不能访问,而,通过 iptables DOCKER-USER 规则限制的端口,外界无法访问,本地节点可以访问,这类似于 linode 商家的外置防火墙给我的感觉。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
# 安装 ipset 地址管理工具 apt-get install -y ipset # 新建 add_ips.sh 脚本 nano /root/add_ips.sh # 在 add_ips.sh 脚本中添加以下内容 #!/bin/bash # 创建 ipset allowed_ips #!/bin/bash # 最大等待时间为 60 秒,每 2 秒检查一次 MAX_WAIT=60 INTERVAL=2 TIME_WAITED=0 while [ $TIME_WAITED -lt $MAX_WAIT ]; do if iptables -L DOCKER-USER &> /dev/null; then # 创建 ipset 地址合集 ipset create allowed_ips hash:ip ipset create allowed_ipv6_ips hash:ip family inet6 # 添加 IP 地址到 ipset 合集 ipset add allowed_ips 地址 ipset add allowed_ips 地址 ipset add allowed_ips 地址 ipset add allowed_ipv6_ips 地址 ipset add allowed_ipv6_ips 地址 ipset add allowed_ipv6_ips 地址 # 定义 iptables 规则 iptables -I DOCKER-USER 1 -m set --match-set allowed_ips src -d 172.22.0.3 -p tcp --dport 8000 -j ACCEPT iptables -I DOCKER-USER 2 -d 172.22.0.3 -p tcp --dport 8000 -j DROP ip6tables -I DOCKER-USER 1 -m set --match-set allowed_ipv6_ips src -d fd00:1::3 -p tcp --dport 8000 -j ACCEPT ip6tables -I DOCKER-USER 2 -d fd00:1::3 -p tcp --dport 8000 -j DROP echo "Rules applied successfully." exit 0 fi sleep $INTERVAL TIME_WAITED=$((TIME_WAITED + INTERVAL)) done echo "Error: DOCKER-USER chain not found after $MAX_WAIT seconds." exit 1 # 赋予 add_ips.sh 执行权限 chmod +x ./add_ips.sh # 新建 add_ips.service 开机自动运行服务文件 nano /etc/systemd/system/add_ips.service # 在 add_ips.service 文件中添加以下内容 [Unit] Description=Add IP addresses to ipset and set iptables rules After=network.target [Service] Type=oneshot ExecStart=/root/add_ips.sh [Install] WantedBy=multi-user.target # 重新加载配置,设置开机自动运行,启动服务,查看运行状态。验证配置是否已经生效。 systemctl daemon-reload systemctl enable add_ips.service systemctl start add_ips.service systemctl status add_ips.service # 重启服务器,验证是否开机自动运行。 reboot iptables -L -n #验证是否 IPv4 规则自动成功运行 ip6tables -L -n #验证是否 IPv6 规则自动成功运行 |
1 2 3 4 5 6 7 8 |
iptables -L INPUT -n ip6tables -L INPUT -n iptables -L -n ip6tables -L -n iptables -t nat -L ip6tables -t nat -L docker network inspect bridge ipset list allowed_ips |