用材料证明没有对外通信
目标
仅凭产品配置文件和三种日志,判定“不存在未经授权的对外通信”,并生成可以直接提交给监理方的摘要。确认的范围和未能确认的内容必须在同一份文档中。
为什么重要
在网络隔离环境的交付中,“不会向外发送”这句话是不会被接受的。产品中内置了更新检查、许可证校验、远程诊断、使用统计和时间同步,大多数默认是开启的。
这个实验用的 Pod 没有任何内核权限,tcpdump 和 iptables 都无法运行。意思是根本没有抓包并展示的途径。在真实现场,这个位置同样由网络团队提供的防火墙策略副本和设备日志来填补,而开发者能提交的材料,就是配置和日志。所以本实验以设置和日志为依据来判定。这就是本实验的假设。
比猜对数字更重要的,是区分三件事:被注释遮住的条目与默认开启的条目,被阻断的尝试与已经发出的通信,以及因为无法按名称核对而尚不知道的内容。
步骤
- 用
python3 /root/egress/mkdata.py生成作为判定基础的数据。会生成 3 个配置、白名单,以及代理、DNS、配置变更日志。 - 从配置中提取全部指向外部的地址,以
host port scheme state source的格式写入 /root/egress/endpoints.tsv。 - 与白名单比对,分成三类,以
host port state verdict的格式写入 /root/egress/classify.tsv。 - 从代理日志中统计前往未允许目的地的尝试次数,以
host port attempts ok auth blocked的格式写入 /root/egress/attempts.tsv。 - 把只存在于查询日志中的目的地,以
host queries nxdomain的格式写入 /root/egress/dns-only.tsv,并亲自观察这个 Pod 的名称解析,把结果留在 /root/egress/resolver.txt 中。 - 用时间把三份日志连起来,以
host change_id first_dns first_proxy的格式写入 /root/egress/timeline.tsv。 - 保留原件,在 /root/egress/conf-after/ 中关闭配置,然后用
python3 /root/egress/collect.py重新提取日志,并把关闭前后的数字写入 /root/egress/after.txt。 - 在 /root/egress/egress-report.json 和 /root/egress/egress-report.txt 中输出供监理提交的摘要。
参考
- 材料全部创建在
/root/egress之下。配置有conf/agent.conf、conf/collector.conf、conf/plugins.json三个,白名单是allowlist.txt,日志是proxy.log、dns.log、change.log。 - 会同时创建两个工具。
mkdata.py负责生成数据,collect.py根据conf-after/中的配置重新提取日志,并留下proxy-after.log和dns-after.log。两者都不使用随机数,所以运行多少次结果都一样。 - 表格用制表符或空格分隔各列,不设表头行。空的位置用一个连字符表示。
- ini 配置按段来配对
url和enabled。段中没有enabled就视为开启(产品说明书第 4.3 节,本实验的假设)。 - 如果用
jq读取 JSON,不要使用.enabled // true。替代运算符在值恰好为false时也会使用右边。要先用has("enabled")查看键是否存在。 - 响应码 200 表示实际发出去了,407 表示代理要求认证,403 表示代理将其阻断。不要把三者混为一谈。
- 常见错误:把用注释遮住的目的地从清单中去掉,只看代理日志而漏掉 UDP 通信,把以地址书写的目的地硬归入允许或未允许一边,通过删除日志来做出 0 笔。
在网络内生成作为判定基础的数据
保存并运行 /root/egress/mkdata.py,同时获取重新收集工具 /root/egress/collect.py。
这是隔离网络,无法从外部获取数据。生成脚本就原样放在本步骤的参考答案中,请保存后运行。必须直接使用不含随机数的脚本,这样无论谁来运行,得到的数据都相同,彼此的判断才能比对。如果手工修改文件,后面步骤的数字就会全部对不上。
从配置中不遗漏地提取外部地址
从三个配置文件中找出全部指向外部的地址,以 host port scheme state source 的格式写入 /root/egress/endpoints.tsv。
段外的 listen 是接收的位置,不属于外部地址。用注释遮住的目的地要作为关闭列入表中,不要漏掉。在 plugins.json 中,没有 enabled 键与值为 false 是不同的。source 只写文件名。
与白名单比对,分成三类
以 host port state verdict 的格式写入 /root/egress/classify.tsv。verdict 是 allowed、denied、unknown 中的一个。
白名单是以名称和端口书写的。配置中直接写着地址的目的地,没有办法与该名单核对,所以既不是允许也不是未允许。“因为是私有网段,所以发送到外部的可能性低”与“已经确认过”,是两回事。
从代理日志中按响应统计实际尝试
在 /root/egress/attempts.tsv 中,以 host port attempts ok auth blocked 的格式写入前往未允许目的地的尝试。
ok 是 200 的次数,auth 是 407 的次数,blocked 是 403 的次数,attempts 是三者之和。白名单中的目的地不要放入这张表。如果有配置中没有、只出现在日志里的目的地,那也是未允许的。
找出没有连接、只查询了名称的目的地
在 /root/egress/dns-only.tsv 中写入 host queries nxdomain,并在 /root/egress/resolver.txt 中写入亲自观察这个 Pod 名称解析的结果。
不经过代理的通信,在访问日志中什么也不会留下。查询日志中有、访问日志中没有的名称,就是它的痕迹。已批准的目的地要去掉。resolver.txt 有 nameserver、query、status 三行,status 照抄 dig 输出的字符串。
用时间把三份日志连起来,指出从什么时候开始
在 /root/egress/timeline.tsv 中,为每个未允许的目的地写入 host change_id first_dns first_proxy。没有的位置用一个连字符表示。
对象是第 4 步和第 5 步中出现的目的地的合集。看 change.log 的 item 值指向配置的哪一段,变更和目的地就能连起来。没有可配对的变更时用连字符。揭示在配置管理之外产生的目的地,正是这张表的用处。
关闭后重新收集,证明它们已经消失
在 /root/egress/conf-after/ 中关闭未允许的目的地,用 python3 /root/egress/collect.py 重新提取日志,然后在 /root/egress/after.txt 中写入四个数字。
原始配置是证据,所以在副本上修改。不要删除条目,只要关闭。确实必需的功能,不要关闭,而是改道指向白名单内的目的地。不能靠手工删除日志来做出 0 笔。评分器还会检查已批准的目的地是否仍然保留。
给出供监理提交的摘要
在 /root/egress/egress-report.json 和 /root/egress/egress-report.txt 中,给出同时写明确认的范围和未能确认的内容的摘要。
数字全部来自前面步骤的表格。unverified_hosts 中只放无法按名称核对的目的地。direct_egress_checked 和 packet_capture_checked 是用这份材料无法确认的内容,请如实填写。供人阅读的表格中,连只出现在日志中的目的地也必须各占一行。