通常, 开发中会遇到存储隐私数据 (例如密钥, API key 等) 的情况. 出于安全考虑, 一般会避免将隐私数据存入代码库中, 并会搭配安全扫描来减少出错的风险. 但是, 将隐私数据存储在别处, 又会由于分散存储, 从而增加隐私数据管理和使用的时间和人力成本.
本文将会介绍如何使用 SOPS 来加密数据, 进而可以在代码库中存储加密后的隐私数据, 以此来简化隐私数据的管理和使用.
SOPS
SOPS 是一个加密文件编辑器, 支持 YAML, JSON, ENV, INI 和 BINARY 格式, 并可以使用 AWS KMS, GCP KMS, Azure Key Vault, age 和 PGP 进行加密.
以上是 SOPS 在 CNCF 网站的介绍. 其中, SOPS 在2023年5月7日被 CNCF 接受, 成为了 CNCF 项目之一.
在接下来的内容里, 我们将会演示如何使用 SOPS (使用 age 加密方式). 因此, 如有使用其他加密方式的要求, 也可以按照官方文档进行设置.
SOPS 和 age 命令的安装
- SOPS 需要手动从它的 release 页面中下载二进制执行文件进行安装.
- 如果使用的是 macOS, 则无需手动下载, 可以直接通过
brew install sops
来安装. - Linux 在下载并解压文件后可以运行命令:
chmod +x sops && sudo mv sops /usr/local/bin/sops
来设置文件权限并放入bin
目录. - Windows 可以将文件解压后放入
C:\Program Files (x86)\Sops
目录, 再将该目录加入环境变量中的 PATH 变量中.
- 如果使用的是 macOS, 则无需手动下载, 可以直接通过
- age 则可以非常轻松的通过包管理器进行安装, 例如
apt install age
,pacman -S age
和brew install age
等, 具体的安装命令的列表可以参考 age 文档.
在上方安装完成后, 可以分别尝试运行 sops -v
和 age -v
命令来验证安装是否成功, 如果安装成功则会在控制台看到安装的 SOPS
和 age
的版本.
age key 的设置
由于我们计划使用 age 进行加密, 因此在初始化前需要运行 age-keygen
命令来生成 age 密钥. 在运行该命令后, 控制台会有如下格式的输出:
# created: 2024-11-11T11:11:11+00:00
# public key: age123js4vs5c6sul78p9jk39xa9jvc9kcany4kd6kew6t004rqde4rqk0uvay
AGE-SECRET-KEY-1W2NYZ3YJ4FU5CXZMKZ6UJDD7PZRY8C9DVY5XXUWS88W78TPYVYGQASHQ6A
其中, 公钥 (age123xxxx
部分) 将会用于初始化 SOPS 和加密. 私钥 (AGE-SECRET-KEY-1Wxxxx
部分) 用于解密, 需要小心保存.
在下方的例子中, 我将使用 age123xxxx
代指公钥, AGE-SECRET-KEY-1Wxxxx
代指私钥. 在实际操作时, 请注意替换为真实生成的值.
注意: 任何拥私钥的人都可以解密加密后的数据, 同时, 如果丢失私钥将无法访问原始数据.
在生成公钥和私钥后, 我们需要依据 SOPS 的规则配置私钥. 此时可以先创建一个用于存储密钥等隐私数据等文件夹,
并将 SOPS_AGE_KEY_FILE
环境变量为该文件夹对应的路径, 随后将私钥存入 key.txt
并放入创建的文件夹中.
例如, 将私钥存入 /etc/sops/key.txt
中, 对应的 SOPS_AGE_KEY_FILE
环境变量则为: SOPS_AGE_KEY_FILE=/etc/sops
.
也可以将私钥直接存入环境变量中, 例如: SOPS_AGE_KEY=AGE-SECRET-KEY-1Wxxxx
.
加密文件
现在已经可以使用 SOPS 来加密和编辑已有的文件了, 假设有如下文件 /tmp/file.yaml
:
my_secret:
key: 123
value: 456
我们可以使用该命令进行加密: sops encrypt --age age123xxxx /tmp/file.yaml --output /tmp/file.enc.yaml
.
运行后将会得到 /tmp/file.enc.yaml
文件, 其中包含了加密后的数据.
在不泄漏私钥以及 age 加密方式未被破解的前提下, 将加密后的文件存储在代码库中是安全的.
同时请避免直接修改加密后的文件, 防止修改出错导致后续文件无法成功解密.
当需要修改加密文件时, 只需使用 sops <encrypted file>
命令就可以直接编辑.
SOPS 会解密文件并自动地打开编辑器, 而在编辑完成并退出编辑器后 SOPS 又会自动的将修改后的内容加密并更新回加密文件中.
SOPS 会尝试使用 EDITOR
环境变量中配置的编辑工具, 如果没有设置的话默认使用 vim. 不过需要注意的是, SOPS 需要等待编辑器退出后才会执行加密和保存的操作,
而一些编辑器会在打开时创建子进程并退出主进程, 这会导致 SOPS 工作异常.
因此, 建议将 EDITOR
变量设置为常用的终端编辑器 (如 vim 或 nano), 以避免潜在的问题.
在 terraform 中使用
在 使用 terraform 管理你的云资源 中, 我们介绍了 terraform 和一些基础的配置. 在真实的场景下我们可能会需要在部署时设置一些 API key 等数据. 同样, 由于密钥泄露的风险, 数据是不能直接存入 terraform config 中的.
这个时候我们可以使用 terraform carlpett/sops provider 来从加密后的的文件中直接读取内容.
使用 carlpett/sops
provider 的优点在于可以直接解密配置,不需要额外脚本处理加密文件. 这将极大降低使用的复杂度.
不过需要注意的是: 使用 carlpett/sops 需要确保已经正确的配置了 private key, 否则运行时会报错.
在使用时, 我们需要先在 required_provider
中引入, 之后才能使用相关的功能:
# 引入 provider
terraform {
# 其他定义
required_providers {
# 其他定义
sops = {
source = "carlpett/sops"
version = "~> 1.1.1"
}
}
}
# 引入要使用的加密文件
data "sops_file" "secrets" {
source_file = "${path.module}/file/secret.enc.yml"
}
# 使用解密后的值, 假设解密后的文件内容为:
# my:
# value: here is the value
resource "aws_ssm_parameter" "test_parameter" {
name = "/config/test"
type = "String"
value = data.sops_file.secrets.data["my.value"]
}
使用 github action 进行部署
使用 github action 进行部署时需要通过某种方式来解密代码库中的加密文件. 我这里使用的是 github 仓库中的 secrets 功能.
只需在 repo -> settings -> secrets and variables -> actions 中配置好 name 和对应的 age私钥后, 我们便可以在 github action 中通过
${{secrets.SOPS_AGE_KEY}}
进行使用 (这里假设配置的 name 为 SOPS_AGE_KEY
).
对应的 github action (workflow) 的定义如下:
jobs:
deploy:
runs-on: ubuntu-latest
env:
# 通过环境变量配置 private key
SOPS_AGE_KEY: ${{secrets.SOPS_AGE_KEY}}
steps:
# ... 一些 setup 和 build
- name: deploy changes
working-directory: deployment/terraform
run: |
terraform init -backend-config="bucket=${{secrets.TF_BACKEND_BUCKET}}" -backend-config="key=${{vars.TF_BACKEND_KEY}}" --backend-config="region=${{vars.AWS_REGION}}"
terraform plan -input=false -out=tf-plan
terraform apply -auto-approve -input=false tf-plan
一些其他的建议
到这里本文其实已经结束了, 但是如果你认为使用 age 加密并在 github 中存储私钥仍有安全风险, 那么也可以考虑使用 AWS KMS 等远端加密的方式.
通过使用这些远端加密, 我们可以省去自行管理相关的密钥的步骤, 但是可能会产生相关的费用. 同时, 使用远端加密会导致无法在离线环境下进行数据的解密.
不同的加密方式之间各有优劣, 建议按照项目的具体情况进行选择.