Docker 端口发布与 VPN 来源:两个容易对错的地方
VPN 服务器 IP 不一定是 Docker 看到的来源,DNAT 之后的端口也不是发布的那个端口。把这两个事实都对照真实路径核对一遍。
规则写的是允许 VPN 服务器地址访问已发布的服务,连接却仍然被丢弃。这种结果很常见,也不是 Docker 的缺陷。两个彼此独立的匹配都错了:你写的来源未必是主机实际收到的来源,你写的端口未必是过滤器实际看到的端口。要同时修好这两处,必须看数据包的真实路径,而不是服务示意图。
误区:允许 VPN 服务器就算完成
路由式 VPN 可以把客户端隧道地址一直保留到 Docker 主机。做源地址转换的网关则会把该地址换成自己的地址。两种设计都合法,但对同一次用户操作产生的来源不同。
所以“VPN 服务器 IP”这个说法描述的是一种假设,而不是观测结果。在弄清眼前是哪一种设计之前,这条规则只是猜测。
来源:客户端地址池还是转换后的网关
按顺序核对四件事。第一,客户端地址池,例如 10.90.0.0/24。第二,网关是否在该地址池抵达主机之前做了转换。第三,主机对一条新连接实际观测到的来源。第四,应用层认证能否区分单个用户,因为共享的转换地址做不到这一点。
如果地址池被保留,只匹配网关公网地址的规则会漏掉每一个客户端。如果网关刻意做转换,允许该转换地址就等于放行它背后的所有用户;对于自带登录的服务可以接受,对其他任何东西都不行。
端口:DNAT 会改掉你匹配的那个数字
在常见的 Linux 网桥发布加 Docker iptables 的场景里,从 8443 到容器 443 端口的主机映射会在转发过滤器执行之前完成转换。数据包抵达过滤器时,目的端口是 443。此时匹配 8443 的规则永远不会命中,即便客户端输入的就是 8443。
已发布的端口仍然存在于连接跟踪里,所以真正需要时可以在那里匹配原始目的地址。
先决定哪个数字表达了意图。规则要保护容器内的服务,就匹配 DNAT 之后的容器端口。规则必须描述客户端请求了什么,就用 conntrack 选项匹配原始目的地址,并接受随之而来的额外匹配开销和对规则顺序的讲究。
链:是 DOCKER-USER,不是 INPUT
Docker 的 iptables 指南把用户转发策略放在 DOCKER-USER 链里,该链在 Docker 自身的转发规则之前求值。把主机服务的规则照搬进 INPUT 链,过滤的是发往主机的流量,而不是转发进容器的流量。这个错误会让规则看起来正确,却对容器路径毫无作用。
把来源范围、有效目的端口、协议和入口接口放在同一条规则里。对每个被转发的目的地址只按来源放行,比只放行一个已发布服务要宽得多。
用新建连接验证
用新连接测两个方向:一个被允许的 VPN 客户端,一个应当继续被拒绝的来源。已有连接状态可能在策略变化后仍让会话存活,所以旧连接证明不了新规则。然后 检查回程路径,因为容器仍然必须路由回它看到的那个来源。
关于这些检查背后的链顺序与端口转换行为,Docker 的 iptables 指南 有完整说明。在照抄配方之前先读它,并确认已安装的 Docker 使用哪个 backend,因为 nftables backend 集成策略的方式不同。