以下为通用 SOPS CLI 示例;子命令支持与参数位置以所安装版本为准。私钥的读取权限与机器配置决定是否需要管理员权限。
子命令总览#
sops edit 编辑加密文件(最常用,解密→编辑器→自动加密)
sops set 设置某个 key 的值
sops unset 删除某个 key
sops decrypt 解密输出到 stdout / -i 原地写回
sops encrypt 加密(需指定 recipient)
sops filestatus 检查文件加密状态
sops updatekeys 按 .sops.yaml 同步接收者
sops rotate 生成新数据密钥重加密
sops exec-env 解密后注入环境变量执行命令编辑明文:核心用法#
方式一:sops edit(推荐,标准姿势)
sops edit host/secrets/secrets.yaml它会自动解密 → 打开 $EDITOR 编辑明文 → 保存后自动加密写回。你永远不直接碰密文,也不用手动「解密→改→加密」。
编辑权限 管理员需先确认解密身份与文件写权限。专用用户 age 身份可避免日常编辑依赖系统 host key;不要把私钥放进仓库或命令参数。
方式二:受限临时文件中编辑,再加密
# Bash 示例;在含 .sops.yaml 的配置仓库根目录中执行
# 先确认私钥可用,且解密目标与 creation_rules 的路径匹配
umask 077
task_secret_dir=$(mktemp -d "${TMPDIR:-/tmp}/sops-edit.XXXXXXXX")
trap 'rm -rf -- "$task_secret_dir"' EXIT
sops decrypt host/secrets/secrets.yaml > "$task_secret_dir/plain.yaml" &&
"${EDITOR:-nano}" "$task_secret_dir/plain.yaml" &&
sops encrypt --filename-override host/secrets/secrets.yaml \
"$task_secret_dir/plain.yaml" > "$task_secret_dir/encrypted.yaml" &&
sops filestatus "$task_secret_dir/encrypted.yaml" &&
cp -- "$task_secret_dir/encrypted.yaml" host/secrets/secrets.yaml
# 成功或失败退出后都清理私有临时目录;不要提交明文临时明文要删 临时文件应使用随机私有目录和 0700/0600 权限,设置清理退出钩子,并防止编辑器备份、swap 和日志泄漏。删除不是存储介质上的安全擦除保证;需要时选择合适的受保护内存文件系统。加密失败时不要覆盖原密文。
方式三:set/unset(只改某个 key,不整文件编辑)
# 设置 app_env 的值(value 是 JSON 字符串)
sops set host/secrets/secrets.yaml '["app_env"]' '"NON_SECRET_EXAMPLE_VALUE"'
# 删除某个 key
sops unset host/secrets/secrets.yaml '["app_env"]'加解密与检查#
# 解密到 stdout
sops -d host/secrets/secrets.yaml
# 解密并提取某个 key
sops -d --extract '["app_env"]' host/secrets/secrets.yaml
# 原地解密成明文文件(危险,别提交 git)
sops -d -i host/secrets/secrets.yaml
# 加密一份明文文件(需指定 recipient,或依赖 .sops.yaml)
sops -e --age "$PUB_KEY" "$task_secret_dir/plain.yaml" > "$task_secret_dir/encrypted.yaml"
# PUB_KEY 是已核对的 recipient;先检查加密成功再替换目标密文
# 检查文件是否加密
sops filestatus host/secrets/secrets.yaml密钥与接收者管理#
新增接收者(多机授权):
# 1. 在 .sops.yaml 的 keys 添加新公钥,creation_rules 里加上
# 2. 用 updatekeys 同步(需有旧 key 权限解密)
sops updatekeys host/secrets/secrets.yaml移除接收者:从 .sops.yaml 删掉该 key,再 sops updatekeys。
轮换数据密钥(生成新 AES 密钥重加密,旧接收者仍有效):
sops rotate --in-place host/secrets/secrets.yaml换 key 安全流程 先加新接收者(双保险)→ 验证新 key 能解密 → 确认稳定后再移除旧接收者。在保留旧密文、旧密钥和可用系统恢复路径的前提下,可在过渡期回退。完整流程见 sops 迁移指南。
.sops.yaml 配置解读#
.sops.yaml 是 sops 的客户端配置文件,决定「哪个文件用哪把公钥加密」。通用示例(占位符不可作为有效密钥):
keys:
- &server-key AGE_RECIPIENT_PLACEHOLDER_NOT_VALID
creation_rules:
- path_regex: host/secrets/secrets\.yaml$
key_groups:
- age:
- *server-key逐字段含义:
| 字段 | 含义 |
|---|---|
keys: |
用于保存 YAML anchor 的辅助区域,不是独立的 SOPS 授权字段 |
&server-key |
YAML anchor(锚点):给公钥串贴标签 |
creation_rules: |
加密规则数组,每条规则匹配某些文件 |
path_regex: |
路径正则,决定规则作用于哪些文件 |
key_groups: |
密钥组;组内通常任一身份即可取得组份额,跨组受 Shamir threshold 控制 |
*server-key |
YAML alias(别名引用):展开成完整公钥 |
多 secrets 文件:creation_rules 是数组,可加多条规则匹配不同文件,文件名随意——只要 path_regex 匹配。运行时在 host/sops.nix 用 sopsFile 声明对应:
sops = {
defaultSopsFile = ./secrets/secrets.yaml;
secrets = {
another = { sopsFile = ./secrets/another.yaml; };
};
};
.sops.yaml与host/sops.nix的分工.sops.yaml管加密时用哪把公钥(CLI 侧);host/sops.nix管解密时从哪个文件取哪个 secret(运行时侧)。两者 recipient/私钥必须配对。
正则 vs glob:* 是重复符不是通配符
正则(regex)里 * 是重复前一个字符,不是 shell glob 的「任意匹配」:
secrets\.yaml$ # 精确:只匹配 secrets.yaml(\. 转义点号,$ 锚定结尾)
.*secrets.* # 任意路径含 "secrets" 的都匹配(.* 才是 glob 的 *)
secrets-[0-9]+\.yaml$ # secrets-1.yaml, secrets-2.yaml...YAML 的 & / * 不是指针
&name(anchor)和 *name(alias)是 YAML 的声明/引用,不是内存指针——是 YAML 节点的锚点与别名关系:*server-key 在解析时引用已定义的公钥节点。作用:定义一次、引用多次,改时只改 & 处一处。类似「变量」,比「指针」更贴切。
语法来源分层
.sops.yaml 有两层逻辑:
| 层 | 谁定义的 | 例子 |
|---|---|---|
| 格式层(YAML 语法) | YAML 规范 | &anchor、*alias、缩进、列表、键值 |
| 语义层(配置字段) | sops 定义的 | creation_rules、path_regex、key_groups、age |
anchor/alias 是 YAML 的能力,sops 只是用户;path_regex 等字段才是 sops 的业务逻辑。这层区分适用于任何「YAML 做配置」的工具。
.sops.yaml 的查找机制:核对工作目录与显式配置
当前上游 CLI 默认从进程当前目录 . 向上查找配置(还可能受配置路径环境变量影响),并非从目标文件所在目录起找。跨目录调用时,可在仓库根执行,或用 --config /path/to/config-repo/.sops.yaml 显式指定。path_regex 按配置文件所在目录解释目标路径;对临时文件重新加密时,用 --filename-override 指定预期仓库内文件名,避免规则不匹配。
.sops.yaml 与 sops.nix 的 sopsFile 分工
.sops.yaml |
sops.nix 的 sopsFile |
|
|---|---|---|
| 谁读它 | SOPS CLI | sops-nix 模块 |
| 管什么 | 加密/更新接收者规则与 CLI 存储选项 | 运行时要解密哪个来源、导出哪个 secret |
| 何时需要 | 创建密文、更新授权等 | 系统激活部署 secrets |
SopsFile 不决定 CLI 加密的 recipient。已有密文携带密钥封装元数据,解密本身不依赖 creation_rules;但 .sops.yaml 也可定义存储或编辑选项,不能概括为仅在两个时刻读取。
NixOS 场景速查#
命令行解密与 sops-nix 运行时解密使用的是不同入口:sops-nix 会按 age.sshKeyPaths 导入 SSH key,而 SOPS CLI 应显式使用可读的 age 身份。--age 接收的是加密 recipient,不能传私钥文件或进程替换路径来代替解密身份。
# 管理员在已授权读取 host key 的受控 shell 内执行;不要打印私钥
umask 077
task_key_dir=$(mktemp -d /run/sops-key-check.XXXXXXXX)
trap 'rm -rf -- "$task_key_dir"' EXIT
ssh-to-age -private-key -i /etc/ssh/ssh_host_ed25519_key > "$task_key_dir/identity.txt" &&
SOPS_AGE_KEY_FILE="$task_key_dir/identity.txt" \
sops decrypt host/secrets/secrets.yaml > /dev/null
# 检查退出码;不将明文或派生私钥写到终端这只是可执行示例,没有在当前机器运行。若存在其他默认 age 身份,需在隔离受控环境下禁用额外身份,再验证目标私钥,避免假阳性。
# 经授权激活后检查运行时产物,不读取内容
ls -la /run/secrets/app_env
systemctl status example-app.serviceset 的 JSON 值会进入 shell 历史或进程参数;示例只使用非秘密字符串。真实值优先通过 sops edit 或当前版本支持的受控文件/标准输入接口提供。sops -d -i 会把原仓库文件变成明文,通常应避免;如误用,要停止提交并恢复受保护密文。filestatus 检查格式中的加密状态,不证明无泄漏、可解密或运行部署成功。
相关#
- 原理 → sops 原理
- 运行时 → sops-nix 集成
- 换密钥/迁移操作指南 → sops 迁移指南
参考与延伸
根据技术笔记重新整理,示例使用通用名称。