摘要
- 历史文件
rpkic_run.sh的两项操作都使用rpkic_rpkiv5_cache,但容器内挂载位置从/var/cache/rpki-client变成/root/cache。 - 同一个命名卷可以继续存在,却出现在容器内的另一条路径。仅凭脚本,无法确认所选镜像实际在哪里读取缓存,也无法确认任何一次执行丢失了数据。
- 两项操作保留相同的镜像字符串、共用参数和信任锚目录绑定;另三个经过查看的启动脚本保留各自的缓存挂载位置。这些反证不能省略。
- 资料固定在 2024 年 4 月 1 日的提交,于 2026 年 9 月 14 日查看。本文没有执行容器或验证器测试,不报道新的迁移,也不认定生产切换失败。
一个实验,需要带过去的不是名字
LACNIC 这个公开工具的目标很具体:先让依赖方软件连接现有服务,完成第一轮处理,把缓存填起来;然后停止原来的容器,让软件连接替代服务,同时带上先前积累的缓存。所谓依赖方,是处理 RPKI 材料并产生已验证路由信息的软件。这里要比较的不是两次随意启动,而是服务改变之后,一个已有状态的程序会看到什么。
因此,缓存并非收尾时顺便保存的附件,而是实验的初始条件。带着此前取得的材料继续运行,与从空白状态重新发现全部材料,回答的是不同问题。后一种测试同样可能很有价值,但不能不加说明地代替前一种。判断一项测试是否实现了它自己描述的目标,首先要知道它究竟保留了什么。
在 rpki-client 的启动文件里,保留的对象有一个清楚的名字:rpkic_rpkiv5_cache。执行 current 时,这个卷挂载在容器内的 /var/cache/rpki-client;执行 rpkiv5 时,同一个卷挂载在 /root/cache。表达式左侧仍然选择同一个存储对象,右侧则指定另一个容器目录。问题不是名字是否稳定,而是程序是否仍然经过新的目录访问原有状态。
Docker 的官方文档把卷的来源和容器内目的位置分开定义。命名卷可以在使用它的容器被移除之后继续存在,并供另一个容器使用。挂载的目的路径决定卷内容出现在那个容器的什么地方。选择同一个卷,是存储连续性的有用证据;却不是应用必然读取这个位置的证据。两种连续性之间还隔着应用的实际配置。
这个差异也不能被倒过来使用。新的位置可能通过镜像内部配置、目录别名或入口安排,仍然对应程序实际使用的缓存。本文没有检查这些可能性,也没有证明它们不存在。没有拉取所选镜像,没有观察实际读写路径,更没有观察缓存丢失。能确认的是公开指令中的位置变动,不能确认的是某次执行背后的数据命运。
把资料的日期留在结论里
本次查看的是固定提交 fdbecab2890e0da2f9a393ac4be2659d14f54ca2,提交日期为 2024 年 4 月 1 日,查看日期为 2026 年 9 月 14 日。这是一份历史迁移验证工具的静态材料,不是当日生产环境的快照。发现它仍可公开访问,不会让它自动变成一次正在进行的迁移公告,也不能替代对当年实际执行过程的调查。
README 描述的替代操作,会通过主机名覆盖,把应用引向替代 RRDP 服务。RRDP 是这些脚本中指向仓库获取的一项接口名称。文档随后建议人工比较输出,例如有效路由起源负载,也就是 VRP 的数量。它还注明当时有一个子证书颁发机构尚未复制。这条限制属于那个历史描述,不能被挪到今天,变成当前仍有机构缺失的指控。
README 的优点恰恰在于说明了顺序。第一轮必须完成,缓存必须已填充,之后才进入替代服务操作。这个顺序使缓存状态成为可以追问的变量。若无法确认第二个容器实际用了先前的缓存,那么后一份输出可能同时受服务变化与初始状态变化影响。它仍然是一份输出,但未必是文档希望取得的那种比较。
本文并不声称作者或维护者没有做过内部测试。所查看的资料包没有提供一份足以确定上述关系的配对执行记录,这与“从来没有执行过”是两回事。公开脚本告诉读者怎样选择卷和路径;实际镜像、实际初始状态、实际获取行为和实际输出,需要另一类证据。只有把这两类材料分开,才不会把代码阅读写成运行审计。
同一个脚本也提供了重要反证
rpki-client 的两项操作并不是处处不同。它们都选择 rpki/rpki-client:8.2 这个镜像字符串,都把本地信任锚目录绑定到 /etc/tals,都使用共用参数 -s 480 -c -v -v -v,也保留相同的后台启动和自定义 DNS 选项。文件里注释掉的其他镜像版本不是这两项操作实际选择的版本,不能拿来拼出一次发生过的版本切换。
这些相同项缩小了问题。脚本至少在文字层面保留了镜像选择、信任锚输入、若干启动参数和存储卷来源。不能把它描述成一次毫无控制、所有条件都被替换的实验。特别是卷来源相同这一点,直接反对“换了位置就删掉了原数据”的推断。数据可能仍在同一个卷里,只是应用能否通过新位置看见并使用它尚待确认。
镜像字符串相同,也不等于本文已经核验实际镜像内容。这里没有确认镜像摘要,没有读取入口程序,没有核查符号链接、目录权限或实际缓存配置。共用参数相同,不代表已经解释每个参数在这个版本里的语义。本文记录的是两项操作中的字符串一致,而不是替软件说明它运行之后做了什么。
替代操作额外把 RRDP 主机名指向 96.126.99.186。这是历史代码中的实验输入,本文没有访问这个地址。迁移实验改变服务地址本来合理,但改变服务地址与改变卷的容器内位置,是两项可分辨的条件。如果实际镜像让两个位置等效,缓存连续性疑问可能消失;如果没有,输出差异就可能不能只归因于服务替换。脚本本身没有足够材料在两者之间作出选择。
相邻脚本阻止我们扩大范围
FORT 的启动文件,在现有和替代操作中都把卷放在 /root/cache。替代操作还加入 RRDP 与仓库的主机名映射。较新的 Routinator 启动文件,在两项操作中都保留 /home/routinator/.rpki-cache。面向 0.12 之前版本的那个启动文件,也在自己的两项操作中保留这一位置,并选择 v0.10.1 的镜像字符串。
这些并不是三个程序已正确复用缓存的执行证明,却是三组公开指令的反证。它们表明,这个仓库描述迁移顺序时,并不普遍要求把缓存换到另一个容器目录。本文看到的位置差异集中在 rpki-client 那一对操作,不能推广成“LACNIC 的验证器都丢缓存”,更不能变成注册机构生产容器群存在某种共同缺陷的结论。
相邻脚本还提醒我们,配对实验应记录其他参数。较新的 Routinator 文件,现有操作使用带有 --refresh=120 的共用服务器参数;替代操作另写一组参数,没有显式带上这一选项。这里没有调查所选版本的有效默认值,因此不认定发生了某种刷新延迟。观察到参数写法不同,与观察到行为异常,仍然相隔一道证据边界。
dnsmasq 配置包含替代 RRDP 和仓库地址映射,也保留一些注释掉的旧映射。README 同时提到,不使用这个 DNS 服务时需要修改相应选项。配置文件不能证明某次实验中实际运行了哪个 DNS 服务,也不能证明参考操作已经指向替代服务。若围绕这些文字另造一个“基准早已改变”的故事,反而会丢掉本文唯一确认的核心差异。
持久、可见、使用,是三件事
Docker 文档还解释,向容器内原本非空的目录挂载卷,会遮住镜像在该目录原有的内容;一个初始为空的卷,在默认复制行为下又可能接收容器目录原有的内容。这些平台规则说明目的目录并非无关紧要,却不能告诉我们这里的镜像包含什么,更不能替代对这个版本实际缓存路径的查看。
因此至少要分开三种身份。卷是由宿主管理的存储对象;挂载是这个对象在某一个容器里暴露的位置;缓存是应用真正读取并当作历史状态处理的材料。同一个卷名可以证明选取了同一个来源,不能单独证明后两者。挂载位置变化可以证明指令中有空间差异,不能单独证明第一者被删除。保持这三层区别,比一句“缓存被保留”更麻烦,也更准确。
输出还构成第四层。README 举例用 VRP 数量进行人工比较,作为第一眼检查并不奇怪。但两个集合可以具有相同元素数量,却包含不同元素。这个逻辑事实不表示本文观察到 VRP 有差异,只表示总数的证明范围有限。输出规模相同,不会自动说明来源状态相同,也不会自动说明每个待比较条件都得到了保留。
真正的实验归因需要把输入、状态和观察连起来。是哪个镜像,哪个卷,哪个有效缓存目录,哪一组信任锚和服务映射,在哪一种起始条件下产生哪份输出?这些问题并非给测试增加装饰字段,而是确定结果能够回答什么。若只是需要一项全新状态的发现测试,可以明确如此命名;若要声称带着已有缓存跨过服务变化,就应提供相应证据。
同一项输入,需要相同的解释条件
面对这两条路径,最容易提出的修改是把它们写成一样。那可能改善指令一致性,却不能替代对镜像实际行为的说明。如果应用本来通过内部配置使用不同位置,那么字面一致也可能不代表相同关系;如果应用只使用某一个位置,那么新的挂载位置是否可见就成为关键。先确定应用与卷之间的关系,再决定是否修改指令,才不会把一项看起来整齐的补丁误当成已经完成的验证。
这也是源码控制与执行控制的区别。固定提交让读者知道正在讨论哪一组公开文字,并能分辨注释与活动指令。但固定提交不会固定宿主上当时有哪一个同名卷,不会固定镜像标签在某次执行时对应的内容,也不会固定程序已经读过哪些对象。本文不因这些情况未被调查,就断言它们发生了变化;只是说明,若结论涉及一次实际运行,不能让源码身份独自承担所有执行身份。
初始状态也不是一句“等待完成”就能永远代替的事实。README 这个顺序表达了维护者希望操作者实现的条件,而一份配对记录应说明这个条件在所报告的运行中如何被观察到。是卷中已有材料,是应用已经读取那些材料,还是只看见一个容器退出前打印了结果?这些观察的强弱不同。明确观察对象,不需要把工具写得更神秘,反而使后来的人更容易判断自己是否重复了同一种试验。
对输出的解释同样需要这份耐心。有效条目数量可以提示明显差异,却不会说明程序先前是否拥有同一组材料。即使结果与预期吻合,也只能证明已比较的那一项观察吻合;若尚未连接起缓存路径与初始状态,就不能追加一项更宽的连续性承诺。相反,即使看见差异,也不能在尚未定位输入差异时,直接给替代服务贴上缺陷标签。证据链的两端都应服从中间已经建立的关系。
因此,一份有用记录可以很紧凑,但不能把重要区别压掉。卷的身份说明哪些数据被保存,容器目的位置说明这些数据在哪里暴露,应用有效路径说明它是否使用那个位置,输出观察说明比较了什么。它不必变成一份覆盖所有网络行为的认证报告。只要让这四件事与同一对运行对应起来,读者就可以知道一项“保留缓存的比较”究竟凭什么使用这个名称。
这个要求也没有把公开材料不足变成维护者的过错。历史工具可能有当时合理的环境假设,而公开文件未必记录了全部上下文。合适的追问,是请补充能够解释两条目的路径的版本和环境依据,而不是要求作者替一个未观察到的故障承担责任。若依据证明关系等效,结论应随之收窄;若依据证明条件不同,也应把问题定位在具体实验解释上,而不是把它扩展成机构级别的安全评价。
还应保留能够反驳疑问的材料。一份证明两条路径实际等效的镜像说明,不应因为它让文章的判断变小,就被排除在后续记录之外。一份证明特定运行确实使用旧缓存的观察,也应只约束那份运行,而不是自动覆盖所有版本与环境。这里需要的不是让疑问永久成立,而是让它有明确的解决条件,并让后来得到的证据决定结论究竟停在哪里。
资料来源
本文使用七份文档:历史 README、rpki-client 启动文件、FORT 启动文件、较新 Routinator 启动文件、较旧版本启动文件、DNS 配置与 Docker 卷文档。六份固定提交文件加一份官方平台文档,来自两个发布站点,不是七项独立调查。本文没有执行脚本、删除容器、清理卷或运行验证器。
会员简报
档案背景详情
使用相应会员等级登录,即可解锁完整简报与来源注释。
仅限 Strategic Circle
Strategic Circle
所有读者均可浏览。加入并登录后可解锁档案简报。
加入 Strategic Circle仅限 Leadership Alliance
Leadership Alliance
符合条件的 IP 资产所有者和管理层可登录查看 Leadership Alliance 简报。
加入 Leadership Alliance
