1. 为什么要在Ceph集群里引入SOPS这一层做运维和平台工程的朋友应该都有这种经历某个半夜Ceph集群的告警电话把你从被窝里薅起来排查半天发现是某条配置项写错了但当你打开ceph.conf准备改的时候发现里面躺着一串明文的管理密钥。更崩溃的是这个配置文件还躺在公司的Git仓库里任何一个能拉到代码的同事都能看到整个存储集群的管理员权限。我见过不少团队花了大把精力去调Ceph的性能参数、规划CRUSH map、折腾网络调优却把最核心的密钥安全问题晾在一边。Ceph本身的加密能力是有的RBD支持LUKS加密和原生加密RGW也支持SSE-S3服务端加密但这些都是数据面data plane的加密。真正容易被忽略的是控制面control plane的配置和密钥管理——也就是那些教你如何访问集群、如何认证、如何注入管理权的明文文件。SOPSSecrets OPerationS最初是某大型互联网公司的内部工具后来开源出来目的就是解决“配置文件里不能有明文密钥”的问题。它和传统的整文件加密不一样SOPS的思想是文件还是那个文件结构还是那个结构我只把里面敏感的字段值替换成密文。你打开一个SOPS加密后的ceph.conf还是能看到所有的参数名只不过keyring的值、密钥的值变成了一串不可读的密文。把SOPS放在Ceph的配置管理链路里能解决几件非常实际的事。第一配置文件可以安全地进Git了。以前我们为了保证密钥不泄露只能把配置文件用.tar.gz加密后放在私有对象存储里或者干脆不纳入版本管理全靠手工同步。引入SOPS之后ceph.conf、keyring这些文件可以直接提交到Git仓库每个字段的变更历史一目了然控制了明文泄露的面又把版本管理的便利性留住了。第二密钥的访问权限可以和集群管理权限解耦。Ceph的管理员可能有好几个人但真正需要拿到admin keyring明文的人可以控制在极少数。其他人看到的是加密后的文件需要用到的时候临时解密用完内存就释放不会在磁盘上留下明文副本。第三自研脚本和自动化工具链可以统一走一套密钥解密的标准化接口。无论是Ansible、Shell脚本还是CI/CD流水线都调用同一个解密方式不需要各自维护一套自己的密钥存储逻辑。这个集成方案并不是什么惊天动地的黑科技它更像是一种工程习惯的补齐。Ceph把分布式存储的底座做得很扎实但它管不了你的运维习惯SOPS正好把配置安全这块短板补上。两者组合起来才算是一个完整的安全闭环。2. SOPS的工作机制拆解它到底加密了什么要想在Ceph场景里用好SOPS先得理解SOPS的设计思路和核心机制否则你只能停留在“照着文档敲命令”的层面遇到问题就抓瞎。2.1 SOPS的加密模型非对称加密为骨架SOPS的加密模型可以分成两层数据加密层和密钥加密层。数据加密层处理的是你文件里的那些敏感字段。它对每个字段值生成一个随机的数据密钥Data Encryption KeyDEK用对称加密算法默认是AES-256-GCM把明文值加密成密文。这样做的原因是对称加密速度快而且在同一个文件里不同字段会使用不同的随机密钥避免“一个密钥加密所有内容”带来的风险。密钥加密层处理的是那些数据密钥本身。SOPS会把你生成的每个字段的数据密钥用你指定的主密钥Master Key再加密一遍然后以metadata的形式存放在文件中。主密钥可以来自多个后端比如云厂商的KMS服务、PGP私钥或者年龄age密钥。你解密文件的时候只需要持有主密钥SOPS会自动用主密钥解开数据密钥再用数据密钥解开字段密文。这套“信封加密”模型在云安全领域是标准做法。它带来的好处是主密钥可以随时更换数据密钥可以随时重新生成但文件内容不需要全量重新加密。如果你需要轮转主密钥SOPS会重新加密metadata部分数据部分不用动效率很高。2.2 SOPS的文件格式与加密策略SOPS对文件格式的支持很灵活这也是它能在Ceph配置场景里“优雅落地”的原因。它支持YAML、JSON、ENV.env文件、INI甚至二进制文件。在处理不同格式时SOPS的加密策略不一样对于YAML/JSON你可以通过creation_rules定义哪些路径下的值需要加密比如ceph_conf: keyring这种嵌套路径SOPS会精准定位到那个字段只加密值不改变结构。对于ENV文件通常直接在.sops.yaml里指定env_file: true默认加密等号后面的所有值。对于INI文件比如Ceph的ceph.conf本身SOPS会把每个配置项的值都视为潜在敏感值你需要通过自定义规则来指定哪些section和key需要加密。2.3 多主密钥与密钥轮换机制SOPS很有意思的一点是它支持在同一个文件中配置多个主密钥。也就是说你可以同时配置两个age公钥或者一把PGP公钥加一个KMS密钥。只要持有其中任意一个对应的私钥就能解密文件。这个机制在团队协作里非常实用。比如你的Ceph集群由一个基础设施团队负责同时安全审计团队也需要偶尔读取配置里的密钥状态那就可以给两个团队各提供一个独立的主密钥。任何一个团队手上的密钥被泄露你只需要从文件metadata里移除对应的加密条目其他团队不受影响不需要大规模重发密钥。密钥轮换的操作也很简单。运行sops -r命令旋转文件的metadata重新生成数据密钥并加密。被移除的主密钥自然失效被新增的主密钥立即生效。整个过程对文件的可读性影响很小非常适合作为定期安全巡检的一部分。2.4 加解密过程中的明文处理SOPS在解密文件时会用到临时目录和内存缓冲默认会尽量保证明文不落地。但注意如果你用sops -d把解密结果重定向到一个文件那明文就落盘了。实际使用中我们通常把解密结果直接通过管道交给Ceph的命令行工具而不是先写文件再读取。以加载Ceph的管理密钥为例你不需要把client.admin.keyring的明文写到某个配置文件里而是可以通过进程替换的方式直接把解密后的内容作为标准输入传给相关命令。这样在系统进程列表里看不到密钥内容在临时目录里也不会残留明文文件。3. 方案选型Ceph与SOPS集成的前置设计在动手装软件之前我建议先想清楚几个问题否则后面返工成本会很高。3.1 加密后端怎么选age还是KMSSOPS支持的后端很多但主流的无非三种age一个现代、简单的文件加密工具、PGP传统GPG、云厂商KMS比如AWS KMS、GCP KMS、Azure Key Vault。如果你的Ceph集群是跑在物理机机房、私有云环境或者内网隔离环境我的建议是优先考虑age。原因很简单age的密钥是一个文本字符串方便分发和存储而且它不依赖外部网络服务完全离线可用。你既不需要维护一个GPG密钥环也不需要为KMS的API调用付费唯一要做的是把私钥文件放在足够安全的位置。如果你用的是公有云上的Ceph集群KMS后端有天然优势。主密钥托管在云厂商那里有完整的审计日志、自动轮转和访问控制。但这里要提醒一点一旦你选定了KMS作为主密钥后端那么CI/CD流水线和运维脚本的执行环境必须能够稳定访问KMS API否则在断网或IAM权限出问题的时候就连解个配置文件都会卡住。我用一个简单的表格来对比一下后端类型是否依赖外部服务密钥存储方式适合场景风险点age否文本私钥文件私有云、物理机、离线环境私钥文件一旦泄露影响全部加密文件PGP/GPG否密钥环文件团队已重度使用GPG的存量环境密钥管理复杂私钥导入导出麻烦云KMS是托管密钥服务公有云、混合云场景网络/权限故障时无法解密Vault Transit是Vault服务已有Hashicorp Vault团队的场景额外维护一套服务故障点增多3.2 Ceph集成点怎么设计控制面与数据面SOPS和Ceph的集成可以从两个维度去理解。第一个维度是控制面集成。也就是对Ceph集群描述文件、配置文件和认证密钥进行加密管理。具体对象包括ceph.conf主配置文件ceph.client.admin.keyring管理员密钥环ceph.mon.keyring监控节点密钥环ceph.osd.keyringOSD节点密钥环部署工具比如cephadm、Ansible的变量文件里面往往带着初始的cluster token这些文件都是明文敏感的重灾区。有了SOPS之后它们可以安全地入库。在部署的时候只需要在目标节点上临时解密通过进程替换直接加载让Ceph进程读取到的是内存中的明文而不是磁盘上的明文文件。第二个维度是数据面集成。Ceph的数据面加密通常指的是RBD镜像的加密和RGW对象存储的加密。RBD支持Linux内核的dm-crypt也就是LUKS以及Ceph原生加密两种方式。RGW则支持SSE-S3也就是服务端加密。这一层加密不仅保护存储在底层OSD中的数据还承担着“数据离开存储设备即不可读”的合规要求。SOPS在这一层的作用是作为密钥管理的前端工具。比如LUKS加密RBD镜像时需要使用一个passphrase这个passphrase不能明文写在部署脚本里。用SOPS加密一个YAML文件来保存这个passphrase然后由部署脚本在合适的时候解密并把passphrase注入到加密流程中是一个很顺滑的组合。3.3 配置库的组织方式我建议把SOPS加密后的Ceph配置文件统一放在一个单独的子目录里比如inventory/ceph/secrets/和一个保存非敏感配置的目录区分开。这样有几个好处Git权限控制可以做得很细。敏感目录只对核心运维人员开放其他人下载代码包时根本拿不到这个目录。CI/CD流水线可以针对这个目录设置单独的缓存放行规则避免敏感的密文文件在流水线中间产物中扩散。审计时一眼就能看出现有哪些加密文件、哪些文件应当加密但还没有纳入管理。另外.sops.yaml这个规则文件本身是要放进Git的。它不包含任何密钥只是定义了哪些文件用什么规则、用什么主密钥加密。有新成员加入时只需要给他一把本地的age私钥然后在.sops.yaml里把新公钥加入对应的creation rule即可。4. 实操从零搭建SOPS与Ceph的加密链路4.1 环境准备与版本选择本次实操我以私有机房环境为例操作系统为CentOS Stream 9Ubuntu 22.04/24.04同样适用Ceph版本为Quincy或Reef系列SOPS版本选择3.8以上的V3版本在输出格式和稳定性方面都更成熟。同时需要准备一个独立节点作为管理机不建议在生产Ceph节点上直接安装SOPS。安装SOPS和age很简单有两种方式方式一直接下载编译好的二进制文件。SOPS官方GitHub的release页面会提供多平台的二进制包下载后放到/usr/local/bin并赋予执行权限即可。方式二通过包管理器安装。在你的Linux发行版上sops可能已经进入官方源比如在Debian/Ubuntu上可以用apt install sops。但上游源里的版本经常落后我建议优先使用官方二进制毕竟SOPS版本迭代较快老版本对某些加密后端支持有差异。age的安装同理在Linux发行版上age包一般都有也可以直接下载二进制。安装完成后验证一下版本sops --version age --version4.2 生成age密钥对并配置.sops.yaml创建age的主密钥对建议在管理机上完成并把私钥文件放到一个独立目录比如/opt/sops-keys/keys.txt权限设为600。mkdir -p /opt/sops-keys AGE_KEY_FILE/opt/sops-keys/keys.txt age-keygen -o /opt/sops-keys/keys.txt cat /opt/sops-keys/keys.txt输出会包含一段public key: age1xxxxxxxx...这就是你的公钥。公钥可以随便分享最终会写进.sops.yaml里。在项目仓库根目录创建.sops.yamlcreation_rules: - path_regex: inventory/ceph/secrets/.*\.ya?ml$ key_groups: - age: - age1xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx - path_regex: inventory/ceph/secrets/.*\.env$ env_file: true key_groups: - age: - age1xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx这里我定义了两种规则YAML文件和ENV文件。如果你用的是INI格式的ceph.conf也可以把规则扩展到.conf但要注意SOPS对INI文件的默认行为是加密所有键值如果你只想加密部分section需要后续用--encrypt时传入具体的路径映射或者升级到SOPS新版本利用更细粒度的规则语法。4.3 用SOPS加密Ceph配置文件的全过程假设你在一个模拟的Ceph环境中初始有一个明文的管理密钥环文件ceph.client.admin.keyring内容大致是[client.admin] key AQD9QF1kC7UWHhAA5JfZ2oJ8vQ0z1x2y3z4a5b6c7d8e9f caps mon allow * caps mgr allow * caps osd allow *这里有一个很重要的技术细节SOPS原生不支持ini格式的嵌套选择加密但我们可以直接把这个keyring文件当作一般文本文件用sops --encrypt对整个文件进行加密。不过我更推荐的方式是先把keyring文件改造成YAML或者env结构cat ceph.client.admin.keyring.yaml EOF client_name: client.admin key: AQD9QF1kC7UWHhAA5JfZ2oJ8vQ0z1x2y3z4a5b6c7d8e9f caps: mon: allow * mgr: allow * osd: allow * EOF然后对这个YAML文件执行加密命令sops --encrypt --input-type yaml --output-type yaml \ inventory/ceph/secrets/ceph.client.admin.keyring.yaml命令执行后会生成一个加密后的YAML文件内容是每个敏感字段都被替换为ENC[AES256_GCM,...]形式的密文。SOPS会在文件末尾自动追加sops元数据段里面记录了使用的age公钥指纹、数据密钥的加密信息等。再举一个处理ceph.conf的例子。如果ceph.conf里包含了admin socket路径、集群名称等非敏感信息可以直接用--encrypt整体加密解密时通过管道还原给需要读取的程序sops --decrypt inventory/ceph/secrets/ceph.conf /tmp/ceph-deploy/ceph.conf这样虽然明文在/tmp目录短暂存在但至少源仓库和CI系统里不会长期存放明文。4.4 在Ceph节点上解密加载配置而不落地明文生产环境的操作模式我强烈不建议解密到临时文件。推荐的做法是用bash进程替换特性让解密结果以虚拟文件的形式出现在文件系统描述符里然后直接被命令读取。比如你需要用加密后的keyring文件配置某个客户端ceph -c (sops --decrypt inventory/ceph/secrets/ceph.conf) \ --name client.admin \ --keyring (sops --decrypt inventory/ceph/secrets/ceph.client.admin.keyring.yaml) \ status这样整条链路中明文密钥永远不会以磁盘文件的形式落地。进程替换的文件描述符只存在于当前Shell进程中命令执行完毕Shell退出描述符自动释放。在systemd服务单元里也可以做到这一点。比如你在部署一个需要连接Ceph的应用程序可以在systemd unit的ExecStartPre里调用一个脚本脚本将SOPS解密内容通过环境变量的形式传给主进程环境变量随着进程生命周期结束而销毁。4.5 与cephadm集成容器化部署的解密方案现在Ceph的部署大多数已经走向cephadm容器化。cephadm在引导和使用过程中最关键的认证文件是/etc/ceph/ceph.client.admin.keyring。如果你不想让明文密钥躺在每个管理节点上可以考虑一个折中方案保持cephadm生成一个初始明文keyring用于集群日常操作权限尽量收紧比如600。把一份加密副本提交到Git仓库的secrets目录。在CI/CD发布流程里解密这份副本并自动更新到目标服务器。具体操作时你的Ansible或者脚本可以这样写# 拉取加密后的文件 sops --decrypt /opt/repo/inventory/ceph/secrets/ceph.client.admin.keyring.yaml | \ grep -A1 key: | tail -1 | awk {print $2} /etc/ceph/client.key.tmp chmod 600 /etc/ceph/client.key.tmp有朋友可能会问既然节点上最终还是要落一份明文keyring才能让cephadm正常工作那SOPS的意义在哪里意义在于源头控制。你不希望每一个能访问Git仓库的运维人员手里都有一份可直接使用的明文密钥也不希望CI日志里把密钥打得满天飞。SOPS解决的是“密钥如何被管理、如何被分享、如何被追踪”的问题至于运行时的明文落盘可以配合文件系统权限、SELinux策略和磁盘加密再加一层防护。4.6 拓展用SOPS管理RBD加密密钥和RGW SSE密钥前面说的是控制面这里补一个数据面的实际场景。如果你在使用RBD的LUKS加密创建一个加密镜像的典型流程是rbd create mypool/luks-image --size 10G rbd feature disable mypool/luks-image object-map fast-diff deep-flatten然后通过rbd map时指定--encryption-passphrase-file指向一个文件。这个passphrase文件可以从SOPS加密的YAML中解出来。例如用sops加密一个包含passphrase的YAML文件rbd_luks_passphrase: MySuperSecretPassphrase123解密时这样使用进程替换rbd map mypool/luks-image \ --encryption-format luks2 \ --encryption-passphrase-file (sops --decrypt inventory/ceph/secrets/rbd-luks-passphrase.yaml)RGW服务端加密类似。Ceph的RGW支持通过Vault或者直接配置固定密钥的SSE-KMS。为了不在ceph.conf里明文写上AES密钥可以先用SOPS加密该字段然后在RGW启动前解密注入环境变量RGW配置中引用环境变量。Ceph的配置系统本身支持$environment_variable的引用语法在ceph.conf里写rgw_encryption_secret_key ${RGW_SSE_SECRET_KEY}然后在启动RGW进程前从SOPS解密结果中导出这个环境变量。原始密钥只存在于环境空间中不在配置文件中持久化。这是我很推荐的一个模式。5. 常见问题、踩坑经验与团队落地建议5.1 SOPS操作中的高频问题问题1用多个age密钥加密后只有一把密钥能解密是怎么回事这个大概率是.sops.yaml的creation rule没有更新。如果你新增加了一个公钥但只对新的文件生效对已经加密过的文件需要用sops updatekeys命令把新key加进去否则老的加密文件只有旧密钥能解开。sops updatekeys inventory/ceph/secrets/ceph.client.admin.keyring.yaml问题2加密大文件时SOPS卡住或很慢SOPS的加密性能瓶颈在数据密钥的生成与metadata序列化通常情况下不会太慢。如果你的文件特别大而且开启了严格的格式校验可能会觉得慢。解决办法是尽量保证配置文件的精简把该拆分的配置拆开不要把一个几百行的配置文件全部塞进加密目录。问题3CI环境里没有本地age私钥怎么办建议CI系统集成密钥管理系统把age私钥注入到CI的secret环境中。在运行时把私钥内容写到/tmp/sops-age-key.txt设置环境变量SOPS_AGE_KEY_FILE/tmp/sops-age-key.txt然后执行解密。用完后立刻删除私钥文件。密钥文件的读写权限一定要设置为只有当前用户可读。问题4sops解密输出包含警告“metadata invalid”或“key not found”先检查私钥是否对应该文件的加密主密钥。用命令sops --decrypt --verbose --show-progress可以看到文件头部的metadata里面会列出所有参与加密的主密钥指纹对照一下本地私钥的指纹。如果指纹不匹配那说明这个文件就不是用你手上的私钥加密的需要找到原始加密人。5.2 Ceph侧集成时的坑点坑点1ceph.conf解密后用ceph命令读取时会报“unable to parse config file”这是因为SOPS加密整个INI文件后解密时输出的是恢复的原始INI但进程替换场景下Ceph可能读不到[global]这个section。建议在加密前先把ceph.conf标准化确认[global]在最前面且没有多余的空行或BOM标记。坑点2私钥文件被反斜杠转义或存在Windows换行符导致age密钥解析失败这个问题在Linux服务器上不常见但如果文件从Windows传过来注意用dos2unix清理换行符。age-keygen生成的密钥是一个严格的UTF-8文本文件任何多余字符都会导致解析失败。坑点3把解密后的ceph.client.admin.keyring误写入日志脚本中如果执行set -x那么进程替换的参数也会被完整打印毫无疑问会把明文密钥打到CI日志里。破除的方法是在全脚本中禁用日志输出敏感区域或者把密钥解密放进独立的脚本文件中外围的日志只记录退出码。这个问题在实战中非常容易出我甚至把它写进了团队的代码规范。坑点4节点重启后环境变量注入的解密密钥失效服务起不来如果你采用环境变量方式给RGW或应用注入密钥一定要在systemd unit里加上环境文件依赖。systemd的环境文件可以由部署脚本在启动前临时生成并做权限锁定但注意systemd unit里引用的变量文件在unit执行期间必须存在否则服务直接启动失败。更稳妥的做法是使用ExecStartPre先解密生成环境文件启动后再在ExecStopPost里删除兼顾可用性与安全性。5.3 团队落地与流程建议我自己在帮几个团队落地SOPS加Ceph的经验里有几点值得分享。第一加密范围宁全勿缺。刚开始搭建的时候很多团队会想着“只加密最核心的keyring就行”后来发现各种周边脚本里散落着admin socket路径、token、甚至cephadm的初始密码。建议把配置治理的范围一次性划大一点宁可多加密几个文件也别漏掉任何一个。第二操作前先备份私钥。age私钥文件一旦丢失所有加密过的文件都变成永久不可读。建议把私钥文件用组织的密码管理工具保存一份或者加密后放在内部离线存储里并注明保管人。密钥文件的丢失事件在团队里太常见了而且通常发生在最需要解密的时候。第三制定一个密钥轮换周期。哪怕没有泄露事件也建议每个季度跑一次sops updatekeys或者直接重新加密关键配置文件轮换数据密钥。配合Git提交历史可以做比较完整的变更追溯。第四关注Ceph版本升级带来的兼容问题。Ceph大版本升级时某些配置项会被废弃或者改名。如果你的SOPS加密配置文件长期未更新升级过程中很容易出现“解密后包含未知参数”的报错。建议每次版本升级前对所有加密配置做一次全量解密审查剔除废弃参数后再重新加密入库。最后聊点我自己的体会做配置安全这件事最大的障碍往往不是技术而是习惯。Ceph集群运维的同学习惯了“把keyring放在/root下、权限设600就走人”的做法觉得只要权限够了就安全。但分布式存储环境很特殊集群节点多、参与角色多、自动化脚本多稍不留神一个密钥就会通过日志、配置仓库、打包备份泄露出去。我见过一个真实的案例某团队在给客户交付Ceph环境时把client.admin.keyring的明文打进了一个Ansible的role后来这个role被上传到公开的自动化分享平台整个集群的管理权直接暴露。如果当时这套配置是用SOPS加密后入库的这个问题根本不会发生。所以我个人是强烈建议新上线的Ceph集群从一开始就把SOPS纳入配置管理链路而不是等出了安全问题再去补。切换的成本很低无非是多一道解密步骤但换来的是整个配置生命周期的可控性。分布式存储的加密不应该只停留在数据盘那一层控制面的配置安全同样值得认真对待。