# 个人服务器组网与防护实践(三):Docker 发布端口的访问控制与持久化
家庭小主机需要保留“二级域名 + 端口”的公网访问方式,同时让 Redis 等内部服务只供局域网和 WireGuard 使用。这里保留 Docker 自动生成的规则,在 DOCKER-USER 中维护允许访问的来源和公网 TCP 端口。
第一篇记录组网配置,
第二篇解释报文路径。
本篇记录最终采用的家庭防护脚本,以及云主机的安全组分工。
1. 防护范围
1.1 家庭小主机
- 物理网卡:
enp1s0;局域网:192.168.0.0/24。 - WireGuard:
wg0,网段10.10.0.0/24。 - 宿主机服务:由 UFW 管理。
- Docker 发布端口:局域网、WireGuard 可访问;其他从物理网卡进入的转发流量只允许列表中的 TCP 端口及已有连接回包。
- 公网入口:家庭路由器按需转发端口,无需新增 Nginx。
本方案使用 Docker bridge 与 iptables 后端,仅配置 IPv4。物理网卡上的 DROP 会影响其他转发流量,不应直接用于还承担其他路由任务的网关主机。
1.2 当前公网端口
10088:springboot附件服务。10081:springboot CAS SSO。10085:springboot lite-task服务。10085:springboot console服务。61208:Glances。40002:FileBrowser,对应容器端口80。9000:Portainer。
以上是当前明确需要开放的列表。管理页面仍需启用各自的认证;Redis 不加入列表,路由器也不转发 Redis。
2. 防火墙脚本
文件:/usr/local/sbin/docker-user-firewall.sh。网卡、网段和端口数组按实际环境修改。
bash
#!/usr/bin/env bash
set -e
# 对公网开放的宿主机 TCP 发布端口。
# FileBrowser 的映射为 40002:80,这里填写 40002。
PUBLIC_TCP_PORTS=(10088 10081 10085 10087 61208 40002 9000)
# 检查 Docker 已创建链;-w 等待 iptables 锁释放。
iptables -w -nL DOCKER-USER >/dev/null
# 允许 WireGuard 与局域网来源继续进入 Docker 后续规则。
iptables -w -C DOCKER-USER -i wg0 -s 10.10.0.0/24 -j RETURN 2>/dev/null \
|| iptables -w -I DOCKER-USER 1 -i wg0 -s 10.10.0.0/24 -j RETURN
iptables -w -C DOCKER-USER -i enp1s0 -s 192.168.0.0/24 -j RETURN 2>/dev/null \
|| iptables -w -I DOCKER-USER 1 -i enp1s0 -s 192.168.0.0/24 -j RETURN
# 拒绝其他从物理网卡进入的转发流量,插在白名单之后。
iptables -w -C DOCKER-USER -i enp1s0 -j DROP 2>/dev/null \
|| iptables -w -I DOCKER-USER 3 -i enp1s0 -j DROP
# 以下规则插入链首,保证排在 DROP 前面。
# 允许容器访问外部服务的响应流量,以及已有连接。
iptables -w -C DOCKER-USER \
-m conntrack --ctstate ESTABLISHED,RELATED -j RETURN 2>/dev/null \
|| iptables -w -I DOCKER-USER 1 \
-m conntrack --ctstate ESTABLISHED,RELATED -j RETURN
# 放行公网端口列表。
# 此处已经发生 DNAT,因此用 --ctorigdstport 匹配原始宿主机端口。
for port in "${PUBLIC_TCP_PORTS[@]}"; do
iptables -w -C DOCKER-USER -i enp1s0 -p tcp \
-m conntrack --ctdir ORIGINAL --ctorigdstport "$port" \
-j RETURN 2>/dev/null \
|| iptables -w -I DOCKER-USER 1 -i enp1s0 -p tcp \
-m conntrack --ctdir ORIGINAL --ctorigdstport "$port" \
-j RETURN
done
2.1 规则作用与顺序
-C避免重复添加相同规则,-I ... 1插入链首。RETURN返回调用链,后续仍由 Docker 规则决定是否放行。--ctorigdstport匹配宿主机发布端口,因此40002:80填写40002。- 最终应先匹配公网端口、已有连接及可信来源,再匹配物理网卡 DROP;公网端口之间的先后顺序不影响本方案。
- 脚本不会自动修复已有规则的错误顺序,执行后需查看实际规则。
2.2 增删端口
新增端口:加入 PUBLIC_TCP_PORTS,然后重新运行脚本或重启下文的 systemd 服务。
删除端口:从数组移除,另行删除已存在的规则。例如关闭 9000:
bash
sudo iptables -w -D DOCKER-USER -i enp1s0 -p tcp \
-m conntrack --ctdir ORIGINAL --ctorigdstport 9000 -j RETURN
- 数组不包含某端口,不会自动删除已经加载的旧规则。
- 已建立连接仍受回包规则放行,不会因为移除端口立即断开。
- 路由器映射也同步调整;若公网端口与小主机端口不同,数组填写小主机收到的目标端口。
3. systemd 开机加载
文件:/etc/systemd/system/docker-user-firewall.service。
ini
[Unit]
Description=Docker User Firewall Rules
Requires=docker.service
After=docker.service
[Service]
Type=oneshot
ExecStart=/usr/local/sbin/docker-user-firewall.sh
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target
首次安装:
bash
sudo chmod +x /usr/local/sbin/docker-user-firewall.sh
# 只检查 Bash 语法,不执行防火墙规则。
sudo bash -n /usr/local/sbin/docker-user-firewall.sh
sudo systemctl daemon-reload
sudo systemctl enable --now docker-user-firewall.service
只更新脚本:
bash
sudo bash -n /usr/local/sbin/docker-user-firewall.sh
sudo systemctl restart docker-user-firewall.service
- 服务在开机时等待 Docker 启动,再执行规则脚本;不用保存、恢复整套 Docker 动态规则。
RemainAfterExit=yes让一次性任务保留已完成状态,不能用再次start代替重新执行,应使用restart。- 此单元不会在每次 Docker 单独重启时自动重跑。重启 Docker 后检查规则,缺失时手动重启本服务。
4. 检查与验证
bash
sudo systemctl status docker-user-firewall.service
# 查看顺序与命中计数,确认允许规则位于物理网卡 DROP 之前。
sudo iptables -L DOCKER-USER -n -v --line-numbers
docker ps --format 'table {{.Names}}\t{{.Ports}}'
按来源验证:
- 局域网:访问所需 Docker 服务。
- WireGuard:通过
10.10.0.2访问所需服务。 - 真正外网:验证公网列表内的端口;内部端口应不可访问。
- 容器内部:验证访问外部服务正常,避免回包被 DROP。
- 服务器或 Docker 重启后:检查规则和实际访问结果。
验证时注意:
- 路由器未转发某端口导致访问失败,不能单独证明小主机的 DROP 命中,应结合计数判断。
- NAT 回环或路由器 SNAT 可能让请求显示为可信 LAN 来源,不能用家庭 Wi-Fi 代替真正外网测试。
- 当前脚本不控制 IPv6。若容器显示
:::端口,需另行配置 IPv6 防护,或明确关闭不使用的 IPv6 发布及入口。
5. 云主机使用安全组控制公网入口
云主机采用阿里云安全组管理 Docker 的公网入口,无需部署家庭机这套脚本。
- 开放
22/TCP、80/TCP、443/TCP、51820/UDP,其他确需公开的服务再逐项添加。 - 不对公网开放 MySQL
3306、Redis16379、Meilisearch7700;通过 WireGuard 访问。 - 云端由 Nginx 代理的应用,不再公开后端
10086、10088。 - UFW 保留宿主机入站与 WireGuard 规则,具体命令见第一篇第 3、4 节。
- 检查全部关联安全组及 IPv6 规则,避免另一条宽泛允许规则重新开放端口。
- 对内部服务分别验证:隧道访问成功,公网访问失败。
Docker 在主机内添加转发规则,并不能绕过云平台侧的安全组。WireGuard 解密后的内层服务访问,则继续由主机上的监听地址、防火墙和应用认证决定。
家庭机维护来源和端口列表,云机维护安全组;两边都保留服务认证,并在端口或网络变更后检查实际入口。
GitHub 登录
Gitee 登录
QQ 登录
请围绕文章内容交流,勿发布广告、违法信息或他人隐私。