说“不会外传”并不等于证据
一句话总结
把产品部署到网络隔离环境时,“不会向外发送任何东西”这句话是不会被接受的。只有同时提交了配置文件被设定去调用什么、日志中实际留下了什么,以及未能确认的范围到哪里为止的材料,才能成为依据。
为什么需要它
交付临近尾声时,监理方问的问题总是类似的:“这个产品会向外发送什么?”开发者想回答“什么也不发送”。但现在的产品,几乎都内置了更新检查、许可证在线校验、远程诊断上传、崩溃报告、使用统计和时间同步。大多数默认是开启的,大多数在安装文档的表格里查不到。开发环境里有互联网,所以几年过去也没有人察觉。
问题在于,这个提问不是信任问题,而是流程问题。监理方发问并不是怀疑开发者。口头做出的承诺不会传达给下一任经办人,到下一次版本升级时就悄悄被推翻了。所以他们要求可以留成材料的形式。美国国家标准与技术研究院的 SP 800-53 Rev.5 在系统与通信保护(SC)系列中设置了边界保护,并把“只放行允许的,其余一律阻断”作为基本姿态。涉及国防供应链的 SP 800-171 Rev.3 以及涉及容器环境的 SP 800-190,也在同一位置以“通信默认拒绝”为前提。在这个前提下,我们能提交的材料,就是“我们的产品会调用什么”的完整清单。
工作原理
分别读取三层,再用时间把它们连起来。
第一,配置。产品被设定去调用什么?这里有两处每次都会骗人。一处是用注释屏蔽的条目。它不是被删除,而是被遮住了,所以会在下一次版本升级中复活。因此要把它作为“关闭”列入表中,但不要从清单中去掉。另一处是默认开启的条目。常见规则是:配置文件里如果根本没有 enabled,就认为产品是开启的,不了解这一点,就会整个从清单中漏掉。
第二处陷阱,在工具这边也同样会出现。jq 的替代运算符 // 不仅在左边为 null 时,在为 false 时也会使用右边。所以 .enabled // true 看起来像是在表达“没有键就是开启”,实际上却会把明确写着 "enabled": false、清清楚楚关闭的条目,也当作开启。没有键与值为 false,是两个不同的事实,而区分这两者,是这项工作的一半。
第二,日志。代理访问日志统计的是实际的尝试。这里不能把响应码笼统地混在一起。200 表示请求真的发出去了,407 表示代理要求认证而停住了,403 表示代理将其阻断了。有十行 403,不是“没有发送”,而是“尝试发送了十次,十次都被拦住了”。要提交给监理方的事实是后者。
第三,名称解析。不经过代理的通信,在代理日志中什么也不会留下。通过 UDP 运行的时间同步、不读取代理设置的库、在防火墙上被直接丢弃的直连,全都是这样。这类功能也会查询名称。所以会另外出现只存在于查询日志而不在访问日志中的目的地,而这是该代码路径确实运行过的唯一痕迹。反过来,如果配置中直接写着地址,查询日志中也不会留下记录。如果是 RFC 1918 中私有网段的地址,发送到外部的可能性较低,但只要白名单是以名称书写的,就无法核对该地址属于哪个服务。这类条目既不是允许也不是未允许,而是无法判断,要写下来并留在未能确认的内容中。
还有时间。把三份日志连成一张表,就能得出“这种尝试是从什么时候开始产生的”。如果配置变更记录的时间与第一次查询的时间紧挨着,就可以据此说那次变更是原因。这张表反过来也有用。变更记录中没有、日志中却出现的目的地,是安装脚本或人手留下的,它揭示了在配置管理之外发生的事。
本实验的假设
实验用的 Pod 没有任何内核权限。tcpdump 和 iptables 都无法运行,所以根本没有抓包并展示的途径。在真实的交付现场,开发者也几乎不会接触客户的防火墙,这个位置由网络团队提供的防火墙策略副本和设备日志来填补。本实验用代理访问日志和名称解析日志来代替它。配置文件中“段内没有 enabled 就视为开启”这条产品规则,同样是本实验的假设,实际上每个产品都不相同,所以在现场必须在说明书中确认。
在现场相遇的样子
在一个交付项目中,会议纪要上写着“遥测会关闭”,也确实关闭了,但半年后的版本升级中,默认配置文件被整个替换,它又复活了。没有人知道的原因很简单:关闭这件事只存在于人的记忆和会议纪要中,而不在机器检查的位置上。如果把清单和重新收集的日志作为材料留下来,下一次版本升级时就可以把同样的流程再运行一遍。
在另一个项目中,我见过为了提交“未允许 0 笔”,而把日志中相应的行删掉的情况。这不是材料,而是材料的反面。证明“没有”的方法不是删除,而是把关闭前和关闭后两份日志并排放置。关闭后的日志里,已批准的目的地仍然保留,也同时表明了那次收集是真实的。
实际工作中真正重要的事
- 把确认的范围和未能确认的内容写在同一份文档中。不写明范围的“没有问题”,会骗下一个人。
- 不要只写判定,要同时留下统计出的值以及来自哪个文件。要让监理人员自己也能重新统计。
- 对于确实必需的功能,不要关闭,而是改道指向已批准的内部目的地。把时间同步关掉,会在别的地方出事故。
- 材料会过时。要把它做成脚本并移交,以便每次版本升级都能把同样的流程重新运行一遍。
下一项实验要做什么
从三个配置文件中提取全部指向外部的地址,区分开启和关闭,再与白名单比对,分成允许、未允许、无法判断三类。从代理日志中按响应分别统计尝试,另外找出只存在于查询日志中的目的地。把三份日志按时间连起来,指出是哪次配置变更产生了这些尝试,然后真正关闭配置并重新收集日志,证明它们已经消失。最后给出供监理提交的摘要,既有机器可读的 JSON,也有供人阅读的表格。