ARTICLE DETAIL

资讯详情

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

Salt 的 ssh 执行模块全解:授权密钥与 known_hosts 的自动化生命周期管理

Salt 的 ssh 执行模块全解:授权密钥与 known_hosts 的自动化生命周期管理 运维配置管理后端【免费下载链接】saltSoftware to automate the management and configuration of infrastructure and applications at scale.项目地址https://gitcode.com/gh_mirrors/sa/salt点击查看免费下载本篇文章围绕 doc/ref/modules/all/salt.modules.ssh.rst 所引用的salt.modules.ssh执行模块源码位于 salt/modules/ssh.py展开。该模块是 Salt 中面向客户端minion本机 SSH 组件管理的统一入口覆盖两大类核心场景用户authorized_keys授权密钥的增删改查与批量文件同步以及known_hosts已知主机记录的探测、校验、写入、删除与哈希。阅读完本文后你将掌握通过salt * ssh.xxx命令逐台管理 SSH 密钥的完整方法理解每个函数返回值的语义与底层实现原理并能进一步借助ssh_auth、ssh_known_hosts两个 state 模块将其纳入声明式配置管理。模块定位与加载条件salt.modules.ssh是一个标准的 Salt 执行模块其__virtual__函数决定了模块是否可用def __virtual__(): if not salt.utils.path.which(ssh): return False, The module requires the ssh binary. return True即只有目标 minion 的 PATH 中存在ssh二进制时模块才会被加载salt/modules/ssh.py#L32-L35。因此该模块无法在未安装 OpenSSH 客户端的主机上工作执行时会得到模块不可用的提示。模块 docstring 顶部有一个重要提示该模块依赖 MD5 哈希某些安全审计场景禁止使用 MD5此时应禁用或移除该模块salt/modules/ssh.py#L1-L9。同时模块定义了常量DEFAULT_SSH_PORT 22所有涉及端口的功能均以此作为默认值。模块函数大体可分为四组授权密钥管理auth_keys、check_key、check_key_file、set_auth_key、set_auth_key_from_file、rm_auth_key、rm_auth_key_from_fileknown_hosts 管理get_known_host_entries、recv_known_host_entries、check_known_host、set_known_host、rm_known_host、hash_known_hosts主机密钥与用户密钥查看host_keys、user_keys、key_is_encrypted内部工具函数_refine_enc、_fingerprint、_format_auth_line、_expand_authorized_keys_path、_get_config_file、_validate_keys、_get_known_hosts_file、_parse_openssh_output等授权密钥authorized_keys管理查看用户的授权密钥auth_keysauth_keys用于返回指定用户或所有用户authorized_keys文件中已生效的公钥信息salt * ssh.auth_keys salt * ssh.auth_keys root salt * ssh.auth_keys userroot salt * ssh.auth_keys user[user1, user2]函数签名与行为要点salt/modules/ssh.py#L371-L409user缺省时通过__salt__[user.list_users]()枚举系统全部用户config参数指定相对于用户家目录的授权密钥文件路径默认.ssh/authorized_keys返回值为字典key - {enc: 加密类型, comment: 注释, options: 选项列表, fingerprint: 指纹}内部通过_get_config_file把相对路径结合用户家目录解析为绝对路径同时支持%u、%h通配符见后文再调用_validate_keys逐行解析当只查询单个用户时直接返回该用户的密钥字典而非以用户名为键的嵌套字典便于脚本消费。_validate_keyssalt/modules/ssh.py#L189-L244是解析authorized_keys文件的核心它会跳过空行与#注释行用正则提取每行的{options} {enc} {key} {comment}结构并调用_fingerprint校验 key 是否为合法 base64——非法条目会被静默跳过这正是set_auth_key写入前先做同样校验的原因。密钥类型规范化_refine_enc用户在调用set_auth_key等函数时可传入简写加密类型内部统一由_refine_encsalt/modules/ssh.py#L38-L86规范化用户输入规范化结果r、rsa、ssh-rsassh-rsad、dsa、dss、ssh-dssssh-dsse、ecdsaecdsa-sha2-nistp256默认 256 位ecdsa-sha2-nistp384/521原样返回高位曲线需写全称ed25519、ssh-ed25519ssh-ed25519rsa-sha2-256、rsa-sha2-512及各类*-cert-v01openssh.com、sk-*证书类型原样返回不在此列的类型会抛出CommandExecutionError(Incorrect encryption key type ...)。从代码可见 OpenSSH 8.7 以来的新类型如sk-ecdsa、sk-ssh-ed25519也已被支持。检查密钥是否需要变更check_key 与 check_key_filecheck_keysalt/modules/ssh.py#L453-L501是幂等判断的核心返回三态字符串add密钥不在文件中exists密钥已存在且整行含 enc、comment、options完全一致update密钥存在但所在行与期望行不一致。它先把传入参数按_format_auth_line组装成期望行再与auth_keys读取到的实际行逐字符比较。值得注意的是代码中曾考虑删除文件中未被 Salt 管理的其他密钥但被注释掉并给出了明确理由不能删除文件中非 Salt 管理的密钥否则多个 state 同时定义同一用户的密钥时会互相开战salt/modules/ssh.py#L480-L491——这是理解 Salt 密钥管理安全性的关键设计决策。check_key_filesalt/modules/ssh.py#L412-L450则针对文件来源先通过__salt__cp.cache_file从 Salt 文件服务器拉取支持salt://、http://等源然后逐 key 调用check_key。若文件中没有解析出任何合法密钥会向日志写入错误并把错误信息存入__context__[ssh_auth.error]供上层 state 读取salt * ssh.check_key_file root salt://ssh/keyfile写入授权密钥set_auth_key 与 set_auth_key_from_fileset_auth_keysalt/modules/ssh.py#L678-L785是写入单个密钥的入口salt * ssh.set_auth_key user key encdsa函数要求key参数只能包含 base64 编码部分不能带ssh-rsa前缀或userhost后缀若传入的 key 含空白多于一段立即返回invalid。随后依次执行_refine_enc规范化加密类型通过__salt__[user.info]确认用户存在否则返回fail用_fingerprint校验 key 能否按 base64 解码与读取文件时采用同一过滤标准确保能写进去的都能被读回来失败返回Invalid public key调用check_key决定走哪条分支update→ 调用_replace_auth_key原位替换旧行返回replaceexists→ 返回no changeadd→ 追加新行返回new。追加分支中还包含一套完善的目录/文件引导逻辑salt/modules/ssh.py#L735-L785若目标位于用户家目录下而家目录不存在返回fail.ssh目录不存在时自动os.makedirsroot 执行时把属主改为用户 uid/gid 并chmod 448即0o700检测到 SELinux 且存在restorecon时对目录执行restorecon追加前确保文件末尾有换行符避免把新密钥拼接在旧行末尾新建文件时 root 设置属主并chmod 384即0o600同样执行restorecon。set_auth_key_from_filesalt/modules/ssh.py#L622-L675批量处理文件中的全部密钥salt * ssh.set_auth_key_from_file user salt://ssh_keys/user.id_rsa.pub它先cp.cache_file拉取文件并_validate_keys解析无合法密钥时返回fail并记录ssh_auth.error。由于一个文件可能包含多个密钥单次调用可能同时发生新增与替换因此返回值按优先级归并failreplacenewno change。可选参数options通过**kwargs传入若指定会覆盖文件中每个密钥自身的 options。删除授权密钥rm_auth_key 与 rm_auth_key_from_filerm_auth_keysalt/modules/ssh.py#L549-L619删除指定密钥salt * ssh.rm_auth_key user key返回值为字符串包括Key removed删除成功写入时保留注释行与无关密钥行Key not removed文件读写异常记录 warning 日志Key not present目标密钥本就不存在Authorized keys file ... not present授权文件本身缺失。删除逻辑与_validate_keys共用同一行解析正则逐行重写文件仅跳过与目标 key 匹配的行因此不会影响其他密钥和注释。rm_auth_key_from_filesalt/modules/ssh.py#L504-L546以文件为来源批量删除返回值同样做了优先级归并Key not removedKey removedKey not present文件无合法密钥时返回fail。密钥指纹_fingerprint_fingerprintsalt/modules/ssh.py#L247-L289根据公钥的 base64 编码计算指纹格式遵循 RFC 4716 第 4 章即xx:xx:...:xx冒号分隔形式fingerprint_hash_type参数指定哈希算法自 2017.7.0 起默认sha2562016.11.4 引入该参数时默认仍为md5此前固定使用 MD5通过hashlib.getattr动态取哈希函数不支持的算法抛出CommandExecutionError使用base64.b64decode(..., validateTrue)严格解码解码失败非法 base64返回None调用方据此判定 key 有效性。known_hosts 已知主机记录管理known_hosts 系列函数多数自 2018.3.0 加入负责维护known_hosts文件其默认路径规则由_get_known_hosts_filesalt/modules/ssh.py#L292-L315统一处理指定了user时默认~/.ssh/known_hosts未指定user时默认全局/etc/ssh/ssh_known_hosts相对路径会结合用户家目录解析无法解析时返回{status: error, ...}。读取本地记录get_known_host_entriessalt * ssh.get_known_host_entries user hostname内部调用ssh-keygen -F hostname -f file要求系统存在ssh-keygen由装饰器salt.utils.decorators.path.which(ssh-keygen)保证再经_parse_openssh_output解析输出返回{hostname, key, enc, fingerprint}字典列表无匹配记录时返回Nonesalt/modules/ssh.py#L827-L856。port参数会通过_hostname_and_port_to_ssh_hostname转换为[hostname]:port形式参与匹配。从远端拉取记录recv_known_host_entriessalt * ssh.recv_known_host_entries hostname encenc portport基于ssh-keyscan实现salt/modules/ssh.py#L859-L931参数语义hostname远端主机名如github.comenc限定密钥类型可为ed25519、ecdsa、ssh-rsa、ssh-dss或 OpenSSH 8.7 支持的其他类型映射为ssh-keyscan -tport远端 SSH 端口默认 22映射为-phash_known_hosts : True对 known_hosts 中的主机名做哈希映射为-Htimeout : 5连接超时秒数映射为-T特殊兼容CentOS-5等老系统上的 OpenSSH 客户端需要显式-t rsa拉取最多重试 5 次attempts 5循环。返回结构与get_known_host_entries相同无结果返回None。校验记录check_known_hostsalt * ssh.check_known_host user hostname keyAAAA...FAaQ按 key 值或 fingerprint 校验二者至少提供一个都不提供则只做存在性检查。返回值三态add无该主机记录、update记录存在但 key/fingerprint 不匹配、exists匹配缺少hostname时返回{status: error, error: hostname argument required}salt/modules/ssh.py#L934-L989。写入记录set_known_hostset_known_hostsalt/modules/ssh.py#L1027-L1277是 known_hosts 管理的主力函数salt * ssh.set_known_host user fingerprintxx:xx:..:xx encssh-rsa config.ssh/known_hosts完整流程校验hostname必填当port非 22 且hash_known_hostsTrue时直接报错哈希与自定义端口不可同时使用因为-H会哈希[host]:port形式的主机名调用get_known_host_entries读取现有记录判断是否需要更新无记录 / fingerprint 不匹配 / key 不匹配 → 需更新记录已存在且未提供 key 与 fingerprint 时进入仅校验模式未提供key时调用recv_known_host_entries从远端拉取拉取失败返回{status: error, error: Unable to receive remote host keys}若提供了fingerprint但远端所有 key 的指纹都不匹配返回对应错误写入前若文件已存在先用ssh-keygen -F定位同主机、同 enc 类型的旧行从后往前逐行删除避免索引失效并保留原文件权限origmode构造新行使用非默认端口时按[hostname]:port enc key格式写入否则按hostname enc key格式若哈希则主机名已被-H处理确保~/.ssh目录存在root 时 chown 用户并chmod 0o700追加写入后用chmod 0o600或恢复原权限保护文件若提供了key且开启哈希最后调用ssh.hash_known_hosts对写入内容补哈希返回{status: updated, old: 旧记录, new: 新记录}或{status: exists, keys: ...}。删除与哈希记录rm_known_host 与 hash_known_hostssalt * ssh.rm_known_host user hostname salt * ssh.hash_known_hostsrm_known_hostsalt/modules/ssh.py#L992-L1024调用ssh-keygen -R host -f file删除某主机全部密钥返回{status: removed, comment: ssh-keygen 输出}文件不存在时返回对应 error。由于ssh-keygen会重建文件root 且指定 user 时会重新chown恢复属主。hash_known_hosts自 2014.7.0 加入salt/modules/ssh.py#L1358-L1399调用ssh-keygen -H -f file将文件中所有主机名哈希化同样保留原权限并恢复属主返回{status: updated, ...}。主机密钥与用户密钥查看主机密钥host_keyssalt * ssh.host_keys salt * ssh.host_keys keydir/etc/ssh salt * ssh.host_keys keydir/etc/ssh privateFalse salt * ssh.host_keys keydir/etc/ssh certsFalse返回 minion 自身的主机密钥salt/modules/ssh.py#L318-L368。Linux 下默认扫描/etc/ssh其他平台必须显式传keydir否则抛出SaltInvocationError。通过正则ssh_host_(?Ptype.)_key(?Ppub(?Pcert-cert)?\.pub)?识别文件privateFalse跳过私钥文件默认读取certsFalse跳过-cert.pub证书文件默认读取返回{类型[.pub|-cert.pub]: 密钥内容}字典兼容处理遇到旧式二进制格式SSH PRIVATE KEY FILE FORMAT 1.1只读取首行避免把二进制内容读成文本详见 issue #20708 的讨论代码中以注释形式保留。用户密钥user_keyssalt * ssh.user_keys salt * ssh.user_keys useruser1 salt * ssh.user_keys useruser1 pubfile/home/user1/.ssh/id_rsa.pub prvfile/home/user1/.ssh/id_rsa salt * ssh.user_keys useruser1 prvfileFalse salt * ssh.user_keys user[user1,user2] pubfileid_rsa.pub prvfileid_rsa返回用户家目录.ssh下的密钥对自 2014.7.0 加入salt/modules/ssh.py#L1280-L1355默认扫描的公钥id_rsa.pub、id_dsa.pub、id_ecdsa.pub、id_ed25519.pub默认扫描的私钥id_rsa、id_dsa、id_ecdsa、id_ed25519通过pubfile/prvfile自定义路径绝对路径取 basename 作为键名相对路径拼接在~/.ssh/下将pubfile/prvfile设为False可跳过读取对应文件——docstring 特别强调这可以防止 Salt 经 Salt Mine 或其他途径发布私钥数据是安全使用的重要开关。私钥是否加密key_is_encryptedsalt * ssh.key_is_encrypted /root/id_rsa自 2015.8.7 加入直接委托给底层工具函数__utils__[ssh.key_is_encrypted]salt/modules/ssh.py#L1409-L1425通过检查密钥文件头部Proc-Type字段是否含ENCRYPTED判断私钥是否设置了 passphrase返回布尔值。与 state 系统的集成ssh_auth 与 ssh_known_hosts以上执行模块函数本身即可通过salt *命令驱动但更常见的做法是搭配声明式 state 使用实现密钥的幂等配置管理。ssh_auth statesalt/states/ssh_auth.py 提供present、absent、manage三种状态底层调用ssh.check_key/ssh.check_key_file/ssh.set_auth_key_from_file/ssh.rm_auth_key_from_file等函数。典型用法thatch: ssh_auth.present: - user: root - source: salt://ssh_keys/thatch.id_rsa.pub - config: %h/.ssh/authorized_keys sshkeys: ssh_auth.present: - user: root - enc: ssh-rsa - options: - option1value1 - option2value2 flag2 - comment: myuser - names: - AAAAB3NzaC1kc3MAAACBAL0sQ9fJ5bYTEyY - ssh-dss AAAAB3NzaCL0sQ9fJ5bYTEyY userdomain几个值得注意的使用细节与源码中present的实现对应salt/states/ssh_auth.py#L211-L319YAML 显式键由于 YAML 规范把简单键长度限制在 1024 字符以内而 RSA 公钥往往更长state 的 name 需要写成? ... :显式键形式key:形式才能完整容纳整把公钥name支持{options} {enc} {key} {comment}复合字符串state 内部用与执行模块一致的正则拆解并覆盖默认的enc/comment/optionsconfig支持%u用户名与%h家目录通配符展开如%h/.ssh/authorized_keys这一能力来自执行模块的_expand_authorized_keys_pathsalt/modules/ssh.py#L100-L128该函数按sshd_config(5)手册定义的 AuthorizedKeysFile 表达式语法解析%u、%h与转义符%%source指定文件时enc/comment会被忽略以文件内容为准文件必须包含enc key comment格式的 OpenSSH 兼容公钥docstring 还给出了从 PuTTYgen 导出 OpenSSH 公钥的具体步骤testTrue模式下走_present_test/_absent_test干跑分支只报告将发生的变化而不实际写入。ssh_known_hosts statesalt/states/ssh_known_hosts.py 提供present/absent状态注意不支持 Windows__virtual__会直接拒绝加载github.com: ssh_known_hosts: - present - user: root - fingerprint: 16:27:ac:a5:76:28:2d:36:63:1b:56:4d:eb:df:a6:48 - fingerprint_hash_type: md5 example.com: ssh_known_hosts: - absent - user: rootpresent的name只支持单一主机名若foo.example.com与bar.example.com共享同一主机密钥需要写两个 state 分别管理在 OpenSSH 4 及更老版本的系统上必须显式设置enc只有 OpenSSH 5 及以上才能自动探测密钥类型。测试与验证仓库中为该模块及其 state 配套了多层测试可作为理解行为的补充证据模块集成测试tests/integration/modules/test_ssh.pystate 集成测试tests/integration/states/test_ssh_auth.py、tests/integration/states/test_ssh_known_hosts.pystate 单元测试tests/pytests/unit/states/test_ssh_auth.py、tests/pytests/unit/states/test_ssh_known_hosts.py其他相关测试还包括pytests/unit/utils/test_sshpki.py底层ssh.key_is_encrypted等工具函数与pytests/unit/roster/test_sshknownhosts.py。实践要点总结返回值约定授权密钥函数返回字符串状态add/exists/update、new/replace/no change、Key removed等known_hosts 函数返回带status字段的字典updated/exists/removed/error脚本化调用前务必区分两种风格幂等与安全check_key/check_known_host提供干跑判断set_*函数只在必要时变更文件权限由模块自动收敛为0o700目录与0o600文件root 执行时自动修正属主SELinux 环境自动执行restoreconMD5 依赖提醒模块整体依赖 MD5 哈希严格安全审计环境下需评估禁用该模块的影响隐私保护user_keys支持pubfileFalse/prvfileFalse阻止私钥经 Salt Mine 等通道外泄批量场景密钥以salt://文件形式集中下发时优先使用check_key_file/set_auth_key_from_file/rm_auth_key_from_file它们天然支持一个文件多把密钥并通过__context__[ssh_auth.error]向上层传递解析失败原因。赞分享运维配置管理后端【免费下载链接】saltSoftware to automate the management and configuration of infrastructure and applications at scale.项目地址https://gitcode.com/gh_mirrors/sa/salt点击查看免费下载相关推荐Salt 执行模块 npm 完全指南用 SaltStack 自动化管理 NPM 包的生命周期Salt 执行模块 npm 完全指南用 SaltStack 自动化管理 NPM 包的生命周期 本指南围绕 Salt 执行模块 npm 源码位于 salt/m运维配置管理后端如何在 Windows 上用 MinGW/DevKit 从源码构建 Ruby如何在 Windows 上用 MinGW/DevKit 从源码构建 Ruby 如果你在 Windows 上想用 MinGW 工具链而不是 Visual C运维配置管理后端Salt 执行模块 postgres用 Salt 自动化管理 PostgreSQL 数据库、角色、扩展与权限Salt 执行模块 postgres用 Salt 自动化管理 PostgreSQL 数据库、角色、扩展与权限 导读 本文以当前仓库 doc/ref/modul运维配置管理后端创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表