容器监听 UDP 1195、宿主发布 1194,只要映射说明了这一点就是正确的。这些数字构成一张转换表:客户端拨号宿主端口,Docker 把目的地改写为容器端口,而 OpenVPN 在自己配置所声明的端口上回应。其中任何一环对不上,都会表现为看似防火墙的静默失败。证书与密码套件位于下游,无法修复它。

在改动任何认证设置之前,先让一个数据包从客户端走到进程。

三个数字,一条连接

这三个数字是客户端使用的远端目的地、宿主机上发布的映射,以及容器内的监听者。它们不必相等,但必须串成一条链。数字相等只是恰好最常见的一种特例,所以错配很容易写下一次,之后却很难看出来。

调试前把这条链写成一行:客户端地址与端口、宿主映射、容器监听者、传输协议。链上的缺陷会表现为同一个数字以不同取值出现两次。

LocationExample
Remote client destinationHost address, UDP 1194
Published mappingHost UDP 1194 to container UDP 1195
Container listenerOpenVPN on UDP 1195

短语法究竟说了什么

短格式里左侧是宿主端口、右侧是容器端口,斜杠后缀选择传输协议。省略后缀时 Docker 会把映射当作 TCP,因此没有协议后缀的映射背后那个 UDP 监听者,是任何人都到不了的监听者。这个默认值有文档依据,却仍然让那些把数字对齐就以为完事的人措手不及。

长格式会显式固定绑定地址与协议,对于任何面向网络的部署都值得多写那几行。回读已发布的映射,而不是相信生成它的文件。

ports:
  - 1194:1195/udp

数据包可能停在哪里

请求可能在 Docker 之前、Docker 之中或监听者处被丢弃。Docker 之前可能有云防火墙或自行转发的路由器,那台设备需要面向宿主的端口和正确的传输协议。Docker 内部若映射到错误的容器端口,就会转发到一个已关闭的套接字。

接着是监听者:OpenVPN 的配置声明了端口与协议,而在容器内到达另一端口上的客户端什么也碰不到。用 TCP 测试 UDP 服务在这些阶段都证明不了任何事,所以要使用客户端将要使用的传输协议。

绑定地址决定可达性

绑定到 loopback 的发布映射只能从宿主访问,而绑定到所有接口的映射在防火墙允许的任何地方都能访问。在省略地址的 Compose 文件里这两者看起来完全一样,而 Docker 的默认值是更宽的那个。

先确定部署想要哪种暴露方式,再显式写出来。通过 loopback 连接的内网测试和连不上的远程客户端,可以同时是对同一个容器的准确描述。

读一读运行状态

检查运行中容器的实际映射,以及它网络命名空间里的监听者。Compose 文件可能在容器启动后被改过,镜像元数据可能声明了端口却没有发布,第二个容器也可能已经占用了宿主端口。在镜像里声明端口不等于发布它,而这两者都不等于监听。

三种不同状态、三条不同命令,只有最后一条与 OpenVPN 有关。

映射正确之后

数据包到达 OpenVPN 之后,它的日志才是阅读 TLS 与认证信息的地方。为了修一个从未到达的数据包而重新生成证书,是白费功夫,还会引入第二个变量。把映射备注放在客户端配置旁边,下一个人读一行就够,不必重新发现那三个数字。在宿主侧抓一次包就能判定三环中哪一环错了,代价比重新签发证书更低。

这里描述的是桥接发布的情形。宿主网络与路由式容器地址会跳过转换,所以同样的数字在那些设计里含义不同。

相关文章