通常, 开发中会遇到存储隐私数据 (例如密钥, 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 变量中.
  • age 则可以非常轻松的通过包管理器进行安装, 例如 apt install age, pacman -S agebrew install age 等, 具体的安装命令的列表可以参考 age 文档.

在上方安装完成后, 可以分别尝试运行 sops -vage -v 命令来验证安装是否成功, 如果安装成功则会在控制台看到安装的 SOPSage 的版本.

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 等远端加密的方式.

通过使用这些远端加密, 我们可以省去自行管理相关的密钥的步骤, 但是可能会产生相关的费用. 同时, 使用远端加密会导致无法在离线环境下进行数据的解密.

不同的加密方式之间各有优劣, 建议按照项目的具体情况进行选择.