容器打不开隧道,于是加上了特权标志,现在能用了。这一串操作只证明至少缺了一项权限,并没有说明缺的是哪一项。特权模式授予的能力和设备访问范围,远宽于一个隧道接口所需。

这个标志实际改变了什么

特权容器会获得宽泛的能力以及对宿主设备的访问,这比网络路径所需大得多。确切的范围由运行时文档界定,而不是由口耳相传决定;Docker 关于运行容器的文档 描述了可用的特权选项。读一遍就足以看出隧道只占其中很小一部分。

两项更窄的需求覆盖了大多数 VPN 容器。配置接口需要网络管理能力,打开隧道设备需要访问那个特定的设备节点。这是两项独立的授予,两者同时给仍比分笼统授予要窄。特权集合的其余部分与隧道毫无关系。它是为确实需要宿主级访问的工作负载准备的,而 VPN 容器通常不属于这一类。

把每个报错对应到一项前提

在压制报错之前先读它,它会告诉你缺的是哪项前提。关于设备节点的消息点明设备,关于操作不被允许的消息点明能力,关于模块或 sysctl 的消息点明内核支持。每一种的修法都不同,而其中只有一种属于权限。

缺失的配置文件在授予全部能力后依然缺失,这就是特权模式有时什么都没改变的原因。容器起来了,隧道仍然失败,而真正的错误与权限无关。内核支持是第三种情况,也最容易被误读。宿主上未加载的模块无法从非特权容器内部加载,而在容器内授予更多能力并不会改变宿主的状态。

查需求,不要只查症状

从镜像文档中的最小调用开始,运行它,并保留准确的报错文本。然后每次只加一项授予,并记下哪一项改变了结果。

ls -l /dev/net/tun
docker run --rm --cap-add NET_ADMIN --device /dev/net/tun image:tag
ip -br link show type tun

能看到设备、持有能力却仍然失败的容器,是在告诉你问题在别处。这是有用的结论,而特权模式会把它掩盖掉。

也要检查宿主上的设备权限,因为授予正确但组属主错误的设备节点,会产生与缺少授予相同的报错。

记录能生效的最窄授予

把部署需要的每个设备和每项能力写下来,并附上为它提供依据的报错。正是这份记录让下一个人能安全地删掉某项,也是审计员在容器被报告为特权时索要的东西。

也要审视周边环境,因为宿主网络、宿主挂载和运行时选项都可能放大某项授予的影响。把这些全都组合起来的隔离实验调用不是生产配置,无论它当时多好用。在宣布部署完成之前,测试应用、它的 DNS 行为以及一次隧道失败。创建接口的权限不是出口策略,而能完美隧道的容器仍旧会转发应用发出的所有流量。

相关文章