ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Salt 修复 69895:`x509_v2.certificate_managed` 在 `follow_symlinks=false` 时误删符号链接的排查与解决

Salt 修复 69895:`x509_v2.certificate_managed` 在 `follow_symlinks=false` 时误删符号链接的排查与解决 Salt 修复 69895x509_v2.certificate_managed在follow_symlinksfalse时误删符号链接的排查与解决【免费下载链接】saltSoftware to automate the management and configuration of infrastructure and applications at scale.项目地址: https://gitcode.com/gh_mirrors/sa/salt在管理 X.509 证书时x509_v2.certificate_managed状态模块是构建 PKI 基础设施的核心入口。在 Salt 3006.0 引入的x509_v2重写版中该模块支持file.managed的大多数参数其中就包括控制符号链接symlink处理行为的follow_symlinks。修复 69895 针对的是一个隐蔽但影响重大的边界问题当证书路径本身是指向其他位置的符号链接且用户在状态中显式将follow_symlinks设为false时旧代码会在测试模式test mode下错误地删除该符号链接本身而不是像预期那样在真实路径上执行检查与替换。本篇文章基于 changelog 修复条目 changelog/69895.fixed.md结合x509_v2状态模块源码、x509执行模块以及功能测试用例深入剖析该缺陷的成因、修复后的行为变化以及follow_symlinks参数在证书管理场景中的完整语义帮助读者在使用x509_v2管理通过符号链接挂载的证书时避免数据丢失与状态漂移。一、修复概要一次删除操作的语义纠偏修复条目原文如下Fixedx509_v2.certificate_manageddeleting symlinks in test mode iffollow_symlinkswas explicitly set tofalse其含义是在 Salt 的测试模式test: true下运行x509_v2.certificate_managed并且状态显式传入follow_symlinks: false时旧版本存在删除符号链接的行为本次修复移除了这一错误行为。修复代码位于 salt/states/x509_v2.py 中certificate_managed函数的文件存在性检查与符号链接处理逻辑。从源码结构看该修复并非孤立事件——x509_v2状态模块中的crl_managed、csr_managed、private_key_managed均存在结构完全相同的符号链接处理代码段分别位于 salt/states/x509_v2.py、salt/states/x509_v2.py、salt/states/x509_v2.py对应地功能测试中也存在test_crl_managed_follow_symlinks、test_csr_managed_follow_symlinks、test_private_key_managed_follow_symlinks等系列测试说明这是该模块族共同遵循的一致约定。二、问题成因file.managed参数在状态模块中的特殊处理路径要理解这个 bug首先要明白x509_v2.certificate_managed的特殊架构它是一个双层状态函数。2.1 状态模块与执行模块的分工certificate_managed的定义位于 salt/states/x509_v2.py其文档字符串明确指出This function accepts the same arguments asx509.create_certificate, as well as most ones forfile.managed.即它一方面接受证书签发参数signing_private_key、signing_cert、days_valid、subject、encoding、digest、csr等另一方面接受file.managed的文件管理参数mode、user、group、backup、makedirs、follow_symlinks等。函数体通过如下调用将两类参数拆分file_args, cert_args x509util.split_file_kwargs( _filter_state_internal_kwargs(kwargs) )其中split_file_kwargs实现在 salt/utils/x509.py负责把传入的 kwargs 划分为文件属性参数交给内部的file.managed子状态处理与证书内容参数交给x509.create_certificate执行模块处理。2.2 证书检查与文件写入的分离与普通file.managed不同证书状态在写入文件之前必须先完成对现有证书内容的检查是否过期、subject 是否变化、是否需要在余量不足时重签等。因此模块流程为先调用_file_managed(name, testTrue, ...)预检查文件层变更调用x509util.check_cert_changes(...)对比现有证书与期望状态若需要重建证书调用__salt__x509.create_certificate生成新证书最后调用_file_managed(name, contents..., replace..., ...)落盘。正是第 1、2 步之间的检查现有文件环节涉及对name路径上符号链接的特殊处理成为 bug 的温床。2.3 缺陷根因对符号链接的原地删除旧版certificate_managed中符号链接处理的代码路径如下结构可见于修复后的 salt/states/x509_v2.pyreal_name name replace False # handle follow_symlinks if __salt__file.is_link: if file_args.get(follow_symlinks, True): real_name os.path.realpath(name) else: # workaround https://github.com/saltstack/salt/issues/31802 __salt__file.remove replace True当follow_symlinks为true默认值时模块解析符号链接的真实路径os.path.realpath对真实目标文件执行检查与替换——这是符合直觉的预期行为当follow_symlinks显式为false时旧代码直接调用__salt__file.remove删除符号链接本身并将replace置为true以便后续用真实文件替换符号链接。问题在于这段代码位于test模式检查之前且没有判断当前是否处于测试模式。在salt-call state.apply ... testTrue这类干跑dry-run场景下删除符号链接的操作会被真实执行——因为file.remove是一个执行模块函数它不理会状态的test标志。于是用户在测试模式下列出变更预览本意是如果我允许变更将替换这个符号链接实际效果却是符号链接被当场删除而由于test模式下后续的证书签发与文件写入被跳过见 salt/states/x509_v2.pytest模式直接return没有任何新文件被写回。结果就是一次testTrue的状态运行在什么都没做的表象下悄无声息地破坏了证书路径的符号链接结构——这正是 69895 修复的核心对象。2.4 符号链接删除的合法场景辨析值得说明的是删除符号链接本身在非测试模式下是模块的既定设计这是对 Salt 旧版 issue #31802 的 workaround其目的正是如果用户显式声明follow_symlinks: false说明用户希望证书是一个真实文件而非符号链接那么当目标路径是符号链接时应当解除链接并以普通文件接管该路径。因此修复不是禁止删除符号链接而是将删除动作推迟到真正需要写文件的时刻即非测试模式、且确认存在需要落盘的变更时从而保证测试模式只做检查与预测不产生任何副作用实际执行模式保留原有 workaround 语义follow_symlinks: false时用真实文件替换符号链接。三、修复后的完整行为从符号链接检查到落盘的全链路修复后certificate_managed对符号链接的处理遵循以下决策树逻辑与crl_managed、csr_managed、private_key_managed一致name 路径是否是指向其他位置的符号链接 ├─ 否 → 直接以 name 作为检查与写入目标real_name name └─ 是 → file_args[follow_symlinks]默认 true为何值 ├─ true默认→ real_name os.path.realpath(name) │ 对符号链接指向的真实文件进行证书内容检查 │ 若需要重建证书写入真实目标路径符号链接保持指向不变 └─ false → 不解析真实路径记录 replace true └─ 处于测试模式__opts__[test] ├─ 是 → 不执行任何删除返回证书将被替换的预测结果result None └─ 否 → 在确认需要变更后删除符号链接并以真实文件写入该决策树的关键依据来自状态函数返回值构造段salt/states/x509_v2.pyif replace: changes[replaced] name if not changes and file_managed_test[result] and not file_managed_test[changes]: _add_sub_state_run(ret, file_managed_test) return ret ret[changes] changes if current and changes: verb recreate if __opts__[test]: ret[result] None if changes else True ret[comment] ( fThe certificate would have been {verb}d if changes else ret[comment] ) _add_sub_state_run(ret, file_managed_test) return ret在测试模式下函数只把replaced记录进changes返回result None表示变更待应用并立即返回——不会触碰文件系统。只有当test标志为假、且确实存在需要应用的变更时才进入后续的证书生成与文件写入分支此时符号链接才会被真实文件替换。3.1 编码对落盘路径的影响另一个与符号链接相关的细节是编码类型。模块文档salt/states/x509_v2.py说明encoding支持pem、pkcs7_pem文本格式与der、pkcs7_der、pkcs12二进制格式。由于file.managed无法直接处理二进制内容二进制编码的证书通过salt.utils.atomicfile.safe_atomic_write写入real_namesalt/states/x509_v2.py而文本编码则走_file_managed的contents参数。在follow_symlinks: true场景下无论哪种编码写入目标都是os.path.realpath(name)解析出的真实文件符号链接本身保持不动。四、功能测试修复如何被验证仓库中的功能测试位于 tests/pytests/functional/states/test_x509_v2.py针对本修复存在两组参数化测试。4.1 测试模式下的符号链接保留test_certificate_managed_follow_symlinkstests/pytests/functional/states/test_x509_v2.py对follow取True/False两个值、encoding取pem/der两种格式进行组合pytest.mark.parametrize( existing_symlink,existing_cert,encoding, [(existing_cert, {}, pem), (existing_cert, {encoding: der}, der)], indirect[existing_symlink, existing_cert], ) pytest.mark.parametrize(follow, [True, False]) def test_certificate_managed_follow_symlinks( x509, cert_args, existing_symlink, follow, existing_cert, encoding ): file.managed follow_symlinks arg needs special attention as well since the checking of the existing file is performed by the x509 module cert_args[name] str(existing_symlink) cert_args[encoding] encoding assert pathlib.Path(cert_args[name]).is_symlink() cert_args[follow_symlinks] follow ret x509.certificate_managed(**cert_args) assert bool(ret.changes) (not follow)测试断言值得逐条解读cert_args[name]被指向一个预先存在的符号链接existing_symlinkfixture并断言该路径确实是 symlink当followTrue时模块解析真实路径证书内容无变化因此changes为空bool(ret.changes) False当followFalse时模块记录replaced变更因此changes非空bool(ret.changes) True。关键点在于这是一个在测试模式下运行的调用functional 测试通过x509state fixture 以testTrue执行但符号链接在整个调用后依然存在——这正是修复保证的核心不变量测试模式只产生预测变更不产生实际删除。4.2 变更应用后符号链接的去留test_certificate_managed_follow_symlinks_changestests/pytests/functional/states/test_x509_v2.py则验证存在实际变更将CN改为new时的行为pytest.mark.parametrize(follow, [True, False]) def test_certificate_managed_follow_symlinks_changes( x509, cert_args, existing_symlink, follow, existing_cert, encoding ): cert_args[name] str(existing_symlink) assert pathlib.Path(cert_args[name]).is_symlink() cert_args[follow_symlinks] follow cert_args[encoding] encoding cert_args[CN] new ret x509.certificate_managed(**cert_args) assert ret.changes assert pathlib.Path(ret.name).is_symlink() follow末尾的断言is_symlink() follow精确概括了修复后的语义followTrue变更被写入符号链接指向的真实文件name路径依然是符号链接is_symlink() TruefollowFalse符号链接被真实文件替换name路径不再是指向别处的符号链接is_symlink() False。同时crl_managed、csr_managed、private_key_managed也各自拥有结构一致的*_follow_symlinks与*_follow_symlinks_changes测试对见 tests/pytests/functional/states/test_x509_v2.py、tests/pytests/functional/states/test_x509_v2.py、tests/pytests/functional/states/test_x509_v2.py印证了该修复模式在整个x509_v2状态模块族中的一致性。五、实战建议如何在证书管理中安全使用follow_symlinks5.1 两个参数一套语义follow_symlinks是file.managed的标准参数在x509_v2.certificate_managed中通过file_args传递并生效。其语义为follow_symlinks取值对符号链接路径的处理测试模式副作用实际执行模式效果true默认解析os.path.realpath(name)以真实目标文件为对象无证书写入真实目标符号链接保留false显式指定不解析真实路径标记replace无69895 修复后删除符号链接以真实文件写入5.2 推荐实践默认保持follow_symlinks: true当证书通过符号链接如/etc/ssl/certs/www.crt - /data/pki/www.crt挂载时这是最常见也最安全的选择——每次状态运行都作用于真实证书文件符号链接作为稳定入口不被破坏。仅在明确需要解除链接时使用false例如迁移场景中希望把历史上通过符号链接提供的证书收敛为独立真实文件。在测试模式下放心预览修复后salt-call state.apply xxx testTrue可以安全地用于预览follow_symlinks: false场景下的变更不会再误删符号链接。如果预览输出显示The certificate would have been replaced说明实际执行时符号链接将被真实文件接管请确认这正是预期意图。配合backup参数使用模块支持backup: true见 salt/states/x509_v2.py在符号链接被替换前保留原目标文件备份为回滚留有余地。5.3 一个完整的证书状态示例以下状态展示了在真实 PKI 场景中同时使用符号链接与follow_symlinks的典型写法示例场景继承自模块文档 salt/states/x509_v2.py其中wwwminion 从ca服务器远程获取签名证书# /srv/salt/www.sls Ensure the web server certificate directory exists: file.directory: - name: /etc/pki/issued_certs - makedirs: true Get the web server certificate signed by the CA: x509.certificate_managed: - name: /etc/pki/issued_certs/www.example.com.crt - ca_server: ca - signing_policy: www - private_key: /etc/pki/www.example.com.key - CN: www.example.com - days_remaining: 30 - backup: true - require: - file: /etc/pki/issued_certs Publish the web certificate through a stable symlink: file.symlink: - name: /etc/ssl/certs/www.crt - target: /etc/pki/issued_certs/www.example.com.crt在此场景下x509_v2.certificate_managed管理的是真实证书文件指向它的符号链接/etc/ssl/certs/www.crt由file.symlink单独维护。状态运行期间即使certificate_managed内部因证书即将到期而重建证书符号链接也不会被动摇——这正是follow_symlinks默认语义的收益。六、小结修复 69895 解决了x509_v2.certificate_managed在follow_symlinks: false与测试模式组合下的一个真实数据破坏风险旧代码在 dry-run 阶段就会删除符号链接而修复后删除动作被推迟到实际执行且确认有变更时才发生。这一修复同时落实了 Salt 状态系统的核心原则——测试模式必须零副作用并通过test_certificate_managed_follow_symlinks与test_certificate_managed_follow_symlinks_changes两个参数化测试固化了行为契约。对于维护以符号链接组织的证书目录的运维团队升级到包含该修复的 Salt 版本后可以放心地在 CI 或变更评审流程中使用testTrue干跑x509_v2.certificate_managed而无需担心符号链接结构被预览操作破坏。【免费下载链接】saltSoftware to automate the management and configuration of infrastructure and applications at scale.项目地址: https://gitcode.com/gh_mirrors/sa/salt创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表