Helm 插件之 helm-secrets:利用 PGP 加密你的 Values 文件 | IDCF

IDCF

在使用 Helm 部署应用程序时,我们经常会遇到需要为所要部署的应用程序设定敏感信息值的情况,如:为应用程序自身设定登陆名和密码信息,或设定应用程序连接数据库时所需的信息等等。

若将这些包含了敏感信息的值直接以明文的方式存储在自定义的 Values 文件中,尤其是当使用如 Git 等代码版本控制工具追踪 Values 文件时,将会带来非常大的安全隐患。

因此,我们通常不会将这些包含有敏感信息的值保存在 Values 文件中,而是在部署的过程中,通过 HELM 命令的 --set 参数,以命令行的形式为这些变量设定值。

但使用这种方式同样也有它自身的局限性:首先,如果要设定的敏感字段过多,则在命令行中需要指定的参数就越多,这将使得命令行过于冗长且容易出错,同时部署人员需要记住的部署参数也变得复杂起来;其次,通过查看系统执行过的命令历史记录,同样能够获取到在执行 HELM 命令时所有指定的敏感信息参数,这在一定程度上同样存在安全隐患。

而本文将介绍另一种相对来说比较完美的解决方案:利用 Helm 的 secrets 插件,将这些包含了敏感信息的值通过某种加密手段加密之后,在保存到 Values 文件中去。

HELM SECRETS 插件简介

helm-secrets 插件可以帮助我们将定义在 values.yaml 文件中的值进行加密之后重新存储到 Values 文件中,被加密后的 Values 文件可以被随意分发、存储到代码版本管理工具中而不用担心敏感信息被暴露。下面是一个加密后的 Values 文件示例:

#ENC[AES256_GCM,data:IHAqGPYHlUdD2+xSn5ZcYCo=,iv:1KKx8l1zl41LuNYcKw3biXm0vx+vjAeA7wdnNHYjQ6Y=,tag:MmWG4SIeXPt0o0HOHGtJeQ==,type:comment]
registry:
    url: ENC[AES256_GCM,data:sYON9+wBDq9jcmhy8iUaITpIjApLbys=,iv:z/ITKkJp2rS/jMyvxghweA+7W0QlZ98PR+4gDGhX+WI=,tag:TLbCkMcIfW30/80FpaozoA==,type:str]
    username: ENC[AES256_GCM,data:5Ju2bxk=,iv:hxRUoi0lViW7chOQTiyZyt4nGMS5V5YZyFNf19LmvpA=,tag:lb830A5pnZ4bI0HUosyc7Q==,type:str]
    password: ENC[AES256_GCM,data:1WmPZCSlzGbSn2LqMc7DmHjQKTjsaPUdn9nMKvbl+KIJ451EPkV0s3dqF+NVZ5E+T6reYN/lY7Ok3VmGvboVAbFs1IYzn7KenbGLMGgCT+JhUFaYz16TeGGsyWDk6YcIIw/XzR6lTjilpHF+DuZuepOyiAnCO0Q5k4aux2lICQh6P8mOezt8flP9/blnFGVZhaaE5r5vT6hsaQbsy7Rnk2lP926xT8NWcaXR85AleRvevQ/zwFIFjjk=,iv:ivA6U20LCHOoR9WGSmuvlJdhnYx/ZC8Pw9czMjNrrlI=,tag:wX3SiiBv8OjSZkpbCznDZw==,type:str]

jenkins:
    master:
        JCasC:
            configScripts:
                credentials-config: ENC[AES256_GCM,data:3dN7KBW...Ov/rsUA=,tag:qlfV/0x0vr45JxMYM1UdMQ==,type:str]
sops:
    kms: []
    gcp_kms: []
    lastmodified: '2019-07-10T06:21:36Z'
    mac: ENC[AES256_GCM,data:sKsL25V5yci+oD1PpfA5fU6zE7YCc6Sxg7myE4eqoDcA+guG8gUg4Hcj5yAB4APBq3+KtPIXoF0hNHVYZOOYqZXQrMpO0jASjWHmLAFTUb6FE6xOtb4mP3FBk8W6Km7TfNz3Te8WW4nsb/+c0WmFSQnIolaeXgbbZhZ23x+V9g=1,iv:Oha7rwD2y3xCc+UnI+xXwrnFByMhNJkF84TiYq4/LsWI=,tag:W3e9ox2G9QL5jQEV0VwGA==,type:str]
    pgp:
    -   created_at: '2019-07-10T06:21:34Z'
        enc: |
            -----BEGIN PGP MESSAGE-----

            hQEMA9Q2nDmrg55qAQf/aXiC7EXZlP5OZDrH3clCb0I9uqP8eNhVgAzqyfSaajGB
            ...
            =h7fE
            -----END PGP MESSAGE-----
        fp: AD331C18082B4669992805DDCB8EA0C7BC44A464
    unencrypted_suffix: _unencrypted
    version: 3.0.3

可以看到,加密操作仅仅是针对 YAML 文件中所有 Key 对应的值进行的,而保留了 Key 本真。相对于那种针对整个 YAML 文件进行整体加密的方式来说,通过这种加密方式加密后的文件,仍然保留了很强的可读性,这使得我们对加密后的 YAML 文件进行维护变的可能。

而当我们部署应用程序时,直接将加密后的 Values 文件传递给 HELM 命令,helm-secrets 插件则会自动负责为我们对加密的值进行解密操作,整个过程无需任何人为干预。

安装 helm-secrets 插件

在使用 helm-secrets 插件之前,首先确保该插件被安装到了本地 HELM 中,安装 HELM 插件非常简单,使用下面命令直接进行安装即可:

$ helm plugin install https://github.com/futuresimple/helm-secrets

安装完成后,可通过 HELM 的 list 命令查看插件是否安装成功:

$ helm plugin list
NAME       VERSION    DESCRIPTION
secrets    2.0.2      This plugin provides secrets values encryption for Helm charts secure storing

SOPS 命令简介

事实上,helm-secrets 插件本身并没有任何加密与解密的能力,也没有提供任何只针对于 YAML 文件的 Key 值进行加密和解密的工作方式,而它所有的工作,都是通过调用 SOPS 命令来帮助它完成的。

SOPS 是由 Mozilla 开发的一款开源的文本编辑工具,它支持对 YAML, JSON, ENV, INI 和 BINARY 文本格式的文件进行编辑,并利用 AWS KMS, GCP KMS, Azure Key Vault 或 PGP 等加密方式对编辑的文件进行加密和解密。

我们会在后面的小节中对 SOPS 做进一步的介绍。此时,你只需知道 SOPS 命令是确保 helm-secrets 插件可以正常工作的必不可少的依赖,因此,我们必须要确保系统中正确安装了 SOPS 命令。

幸运的是,helm-secrets 插件会自动检测并安装 sops 命令到我们的系统中,因此在插件安装完成后,你还应该可以运行 sops 相关命令,如获取 sops 的版本信息:

$ sops -v

当然,你也可以通过手动方式选择自行安装 sops 工具,如使用 brew install sops 命令为 Mac 系统安装 SOPS,或直接下载 SOPS 的可执行文件,更多信息请查看 SOPS 文档。

如果以上命令全部执行成功,那么恭喜你,你已经成功安装了 helm-secrets 插件及其相关依赖。但在真正介绍如何使用该插件之前,我们还需要了解一些加密解密相关的基础知识和命令。

PGP 简介

正如前面所讲,helm-secrets 插件是通过调用 SOPS 命令来对我们的 Values 文件进行加密和解密的,而 SOPS 本身又支持多种加密方式,如 AWS 云的 KMS,Google 云的 MKS,微软 Azure 云的 Key Vault,以及 PGP 等加密方式。本文将着重介绍如何利用 PGP 对我们的 Values 文件进行加密和解密。

PGP 与 GPG

在了解 PGP 加密之前,我们首先要区分好两个名词概念:PGP 与 GPG。

Pretty Good Privacy,也就是我们平时所说的 PGP,实际上通常指的是 OpenPGP。OpenPGP 只是一系列协议或标准,而并非某个特定的工具或命令,它规定了如何使用特定的方式和算法对文件或内容进行加密和解密,任何实现了 OpenPGP 协议的工具都拥有加密和解密的能力。而本文接下来所要使用的 gpg 命令,正是这样一个实现了 OpenPGP 协议的工具,或者说命令,利用 gpg 命令,我们可以实现 PGP 加密方式。为了避免混淆,后文将一律使用 GPG 代替。

安装 GPG

GPG,全名 GNU Privacy Guard,是隶属于 GNU 项目下的一款开源免费的加密工具。目前绝大部分 Linux 发行版本都默认安装了 GPG,直接运行下面命令查看你当前所在系统中是否已经正确安装了 GPG:

$ gpg --version

你也可以尝试使用下面命令手动安装 GPG 到你的系统中:

# Ubuntu,Debian 用户
$ sudo apt install gnupg

# CentOS,Fedora,RHEL 用户
$ sudo yum install gnupg

# MacOS 用户
$ brew install gnupg

生成 GPG 密钥对

如果你使用过 SSH,那么你一定熟悉公钥和私钥的概念,GPG 同样也使用了公钥和私钥的概念实现了非对称加密算法。简单来说:公钥用于加密,拥有公钥的人可以且仅仅可以进行加密操作,它可以分发给任何组织或个人;而私钥则用于解密,且仅能用于解密那些由该私钥与之配对的公钥加密的信息,任何拥有私钥的人都可以进行解密操作,因此,确保私钥不被泄漏对安全性起着至关重要的作用。

在使用 gpg 命令进行加密解密之前,首先需要生成 GPG 公钥和私钥。你可以使用命令 gpg --gen-key 来交互式地生成密钥对,之所以说交互式,是因为在生成密钥对时会要求用户交互式的输入一些相关信息,如如用户名、邮箱等等。你也可以使用下面命令直接生成出密钥对,注意,在执行命令之前,请注意修改下方 name、email 等字段信息为你自己相应的信息:

$ gpg --batch --generate-key <<EOF
%echo Generating a basic OpenPGP key for HELM Secret
Key-Type: RSA
Key-Length: 4096
Subkey-Type: RSA
Subkey-Length: 4096
Name-Real: HELM Secret
Name-Comment: Used for HELM Secret Plugin
Name-Email: helm-secret@email.com
Expire-Date: 0
%no-ask-passphrase
%no-protection
%commit
%echo done
EOF

该命令将会为我们生成一对长度为 4096 且永不过期的 RSA 密钥对,gpg 命令支持使用更多的参数来控制生成密钥对,如为生成的密钥对设定使用密码等等,更多关于 GPG 命令的使用参数,请参考官方文档。

当生成 GPG 密钥对以后,我们就可通过 gpg 的 --list-keys--list-secret-keys 命令分别列出当前系统中的公钥和私钥信息了:

$ gpg --list-key

pub   rsa4096 2020-04-24 [SCEA]
      13D525EEF0A5FA38F4E78F7900E0160999E3C663
uid           [ultimate] HELM Secret (Used for HELM Secret Plugin) <helm-secret@email.com>
sub   rsa4096 2020-04-24 [SEA]

上面命令列出了当前系统中的公钥信息,其中 13D525EEF0A5FA38F4E78F7900E0160999E3C663 或者后十六位 00E0160999E3C663 为公钥的 ID。

我们也可以通过传递密钥的用户名、邮箱或 ID 来查看某个特定的密钥信息:

$ gpg --list-key "HELM Secret"
# 或
$ gpg --list-key "helm-secret@email.com"
# 或
$ gpg --list-key 00E0160999E3C663

在生成了密钥对之后,就可以利用它们来为我们的文件进行加密和解密操作了。

使用 GPG 加密和解密文件

为了更好地理解 help-secrets(或 SOPS 命令)的优势,我们首先尝试直接使用 gpg 命令来加密我们的文件:

首先创建一个名为 secrets.values.yaml 的文件,并写入以下内容:

App:
 username: login-user
 password: login-pwd

使用 gpg 命令对该文件进行加密:

$ gpg -e -r 00E0160999E3C663 secrets.values.yaml
  • -e 参数指定本次为加密操作
  • -r 参数指定了加密时所要使用的公钥信息,这里直接通过公钥 ID 来指定,我们也可以使用密钥的用户名和邮箱等信息,如 HELM Secrethelm-secret@email.com

当加密完成后,GPG 命令会在 secrets.values.yaml 文件所在的目录下创建一个与它同名,且以 .gpg 结尾的新文件 secrets.values.yaml.gpg

该文件包含了加密后的内容,如果你尝试查看该文件内容,会发现整个文件内容将会以乱码的形式展现出来,而这正式 GPG 的工作方式。

当然,我们也可以使用 gpg 命令对某个加密过的文件进行解密操作:

$ gpg -d secrets.values.yaml.gpg
gpg: encrypted with 4096-bit RSA key, ID C79B2F32F9EE1A8E, created 2020-04-24
      "HELM Secret (Used for HELM Secret Plugin) <helm-secret@email.com>"
App:
  username: login-user
  password: login-pwd

-d参数用指定 GPG 命令执行的是解密操作。与加密操作不同,我们并没有指定解密时要使用的私钥 ID,这是因为 GPG 在加密的同时,将解密时所需的私钥信息一并写入到了加密后的文件中去。当然,你仍然可以使用 -r C8EABE3FAE86D256 参数明确指定解密时使用的私钥 ID。你可能已经注意到了,其实私钥 ID 与公钥 ID 完全相同。

当解密操作成功后,GPG 命令会将解密后的内容直接打印到屏幕上。

至此,我们成功地使用了 GPG 命令对文件进行了加密和解密操作,但不难看出,GPG 的加密操作是针对整个文件进行的,而加密后的文件内容也将变得完全无法维护。

接下来,就让我们简单看一下,如何使用 SOPS 命令对这种 Key-Value 类型的文件进行加密和解密,并同时保证加密后的文件的可读性和可维护性的。

SOPS 的简单使用

为了更好地理解 helm-secrets 插件,先让我们简单看一下如何通过 SOPS 命令使用我们刚刚生成的 GPG 密钥对来对 YAML 文件进行加密和解密操作:

依然使用我们前面创建好的 secrets.values.yaml 文件,并确保写入了以下内容:

App:
  username: login-user
  password: login-pwd

使用 sops 命令对 secrets.values.yaml 文件进行加密:

$ sops --encrypt --in-place --pgp 00E0160999E3C663 secrets.values.yaml
  • --encrypt, -e 参数告诉 sops 进行加密操作;
  • --in-place, -i 参数指定将加密后的内容直接替换掉原文件内容,若不指定该参数,则加密后的内容将会被输出到屏幕上;
  • --pgp, -p 参数指定我们加密时所要使用的 PGP 公钥 ID。

查看加密后的 secrets.values.yaml 文件内容:

$ cat secrets.values.yaml

App:
    username: ENC[AES256_GCM,data:VwLJ94VxsEBMZg==,iv:Qeb6aNT1oryPplmtUx1ufIeiqwJ9APllLnorqvkdjRU=,tag:CuWtAlLEZeOuQFoW34FiEQ==,type:str]
    password: ENC[AES256_GCM,data:jo8lGGOFZuDY,iv:5Wwt/T0SskFxgWXNRjpoIPq+PIuTGILGIeN/dYOJ/Fg=,tag:ncC3t67xArsXvwwEjAGyLg==,type:str]
sops:
    kms: []
    gcp_kms: []
    azure_kv: []
    lastmodified: '2020-04-24T03:48:37Z'
    mac: ENC[AES256_GCM,data:uFdXa2qWDSYqaeVsOLZiQos5K611uZYW91ZhLT00MJRb32TxE190RlJjhvl8+/GUOClZcIaU8DejebDP1TVqVFl6wpFqjVM3TLwW0JDm+b+zpCzMje9e17dNjLp7W2awBTPmrF3AXUopLi8oHOuopW89q2gKgFIUW215zjmQET0=,iv:A85xzE6gEXpcwUE6rIvHwHNhqmaCmFOHoYX3Y4qjaGI=,tag:VSB9b9vKLRJg4/klwliJbQ==,type:str]
    pgp:
    -   created_at: '2020-04-24T03:48:35Z'
        enc: |
            -----BEGIN PGP MESSAGE-----

            hQIMA8ebLzL57hqOAQ//fOzjkY5tW1/fGd/HWrxsgC02YxAjmHggI2ek5VacdhYP
            A9RUYhpipJpBt1LnwHq/B1rV1E4dkOu1lpyAmI9P0qIc+6o0+6jEhqEyjsDQSGn5
            kh31oBNYfLq8XpHQg33jOIHpv6/BU7tqzsVMum3HjvnsSrhc3gRtBq5LZoLP/smA
            3y36tRLHIGFGqOEwy3CdSiPmsyCKQBEYRK0+7mhXX+ulEMudYXKgXk4qCL1UAB0y
            X03K0UATNYp8fRkHqzcpf5nLDNzpCGI0BNbxBQYZdbcP3KFNyKGDFtDaNCcJq9jv
            d6yMnioNWYBCtDlrZXlQGzipheWKwZ7JnNa1nmYpCJ3uh6I3mbtkHjljD88QUm6Y
            czGAsTDYESJPl5y2wdKdMxHOyE++Ii5LvNr2UD3D+ePYvAIpK1TWjfokCe18ZvvD
            v4kHbqbJfffpLCmy0CRVFu/yLnGdZGqniPY/UPPRk28cnKF+fxpX7EmLvzCUgadC
            4emIrR6nBUgGvU+fInZrNOccRhYU2S4So45CW2EXW5E4uNj8ayfUgtaUeRwW8pRE
            ZMGe1yna7a8UC0syiubC1rr8KHKs8nITfRrelV/BtEkfFDI9sm77AMcaWaAaaBz8
            C1L3A1iPhnclDnt3USqOTioLnZs9CjysyNSeiTvehsTC1E3GqgmVbUGob+0Im2fS
            XgHaA9fXLtulXkRQGFYpaNEt6r0mkgdq0DXCCfba6EflHg9BvPfrK0dtXrchlCY/
            K154U0LkPNHtLBXB0rNwz0Z9aA1CwBdRZ6r8V67SJS1nbsiIvyHfc4dq8n3qhVM=
            =HOrp
            -----END PGP MESSAGE-----
        fp: 00E0160999E3C663
    unencrypted_suffix: _unencrypted
    version: 3.5.0

不难看出,SOPS 仅仅是对 YAML 文件中的值进行了加密,而保留了所有 Key 信息,这使得即使是加密后的文件仍然保留了很强的可读性。

此外,SOPS 还在文件中追加了一个新的 Key 值 sops,用于保存加密该文件时所使用的一些加密信息。

当然,我们还可以通过 sops 命令对加密后的文件进行解密操作:

$ sops --decrypt secrets.values.yaml

App:
    username: login-user
    password: login-pwd

--decrypt, -d 参数指定我们要对文件进行解密操作。当然,你也可以通过 -p 参数将解密时的私钥 ID 传递给 SOPS 命令,但这通常是不必要但,因为与 GPG 一样,SOPS 在加密文件时会自动解密时需要的私钥 ID 记录在内。

通过上面的操作,我们成功地使用 SOPS 命令对 YAML 文件进行了加密和解密操作,而这也正是 helm-secrets 插件背后的工作流程。在了解了 SOPS 工作模式之后,接下来,让我们开始正式步入 helm-secrets 插件的学习中去。

开始使用 helm-secrets 插件

在掌握了 GPG 和 SOPS 的一些基本原理和操作后,学习使用 helm-secrets 插件就将变得特别简单起来。

helm-secrets 插件会为 HELM 命令创建一个新的子命令: secrets,所有关于该插件的子命令,都是通过 helm secrets 格式所调用的。首先让我们通过 -h 参数打印出 helm-secrets 插件的帮助文档信息:

$ helm secrets -h

GnuPG secrets encryption in Helm Charts

This plugin provides ability to encrypt/decrypt secrets files
to store in less secure places, before they are installed using
Helm.

To decrypt/encrypt/edit you need to initialize/first encrypt secrets with
sops - https://github.com/mozilla/sops

Available Commands:
  enc           Encrypt secrets file
  dec           Decrypt secrets file
  view          Print secrets decrypted
  edit          Edit secrets file and encrypt afterwards
  clean         Remove all decrypted files in specified directory (recursively)
  install       wrapper that decrypts secrets[.*].yaml files before running helm install
  template      wrapper that decrypts secrets[.*].yaml files before running helm template
  upgrade       wrapper that decrypts secrets[.*].yaml files before running helm upgrade
  lint          wrapper that decrypts secrets[.*].yaml files before running helm lint
  diff          wrapper that decrypts secrets[.*].yaml files before running helm diff
                  (diff is a helm plugin)

帮助文档信息列出了 helm-secrets 插件支持的所有子命令,而这些子命令又可以被划分为两类:

  • 一类是由该插件提供的子命令,如 encdecviewedit 以及 clean 等,这些子命令由该插件所创建,而在 HEML 中并不存在;
  • 另一类则是在 HELM 中已经存在的子命令,如 encdecviewedit 以及 clean 等。对于这些本就存在于 HELM 中的子命令来说,helm-secrets 插件仅仅是对这些子命令做了一层封装操作,而背后真正调用的仍然是 HELM 自身的这些子命令,因此通过 helm-secrets 插件调用这些子命令时的用法与直接通过 HELM 调用的用法完全一致。

提示:你还可以为某个特定的子命令使用 -h 参数,来获取该子命令的帮助文档信息,如 helm secrets dec -h

下面让我们通过部署一个实际的例子,来演示 helm-secrets 插件是如何工作的。

PostgreSQL

PostgreSQL 是由伯克利大学开发的一款有着 30 多年历史的开源关系型数据库,如今仍被大量的公司和企业所使用。HELM 官方 Chart 仓库也提供了稳定版的 PostgreSQL Chart,来帮助我们在 Kubernetes 平台上快速部署 PostgreSQL 集群。接下来,就让我们尝试使用 helm-secret 插件部署一个 PostgreSQL 数据库集群到我们的 Kubernetes 平台上,来看看它是如何帮助我们保护我们的敏感信息不被泄漏的。

默认情况下,PostgreSQL Chart 会为部署的 PostgreSQL 实例创建一个名为 postgres 的管理员账号,并生成一个包含 10 位字符的随机字符串作为它的密码。我们可以通过为 Chart 设定 postgresqlUsernamepostgresqlPassword 的值,来手动指定管理员的用户名和密码信息。

首先创建一个包含如下内容的 secrets.yaml 文件:

postgresqlDatabase: my-database
postgresqlUsername: admin
postgresqlPassword: admin

该文件指定了为部署的 PostgreSQL 实例创建一个名为 admin 的管理员账号,同时设定它的密码为 admin。

尝试直接使用 HELM 命令通过该 Values 文件做一次测试部署:

$ helm install postgresql stable/postgresql -f secrets.yaml --dry-run --debug

上面的命令中由于我们使用了 --dry-run 参数,因此它只会将所有将要部署到 Kubernetes 平台上到资源全部以 YAML 的格式输出到屏幕上,而不会做真正的部署。

如果你仔细查看输出的 YAML 信息,会发现该 Chart 会创建一个包含了一个名为 postgresql-password Key 的 Kubernetes Secret 资源,其值为 “YWRtaW4=”,使用命令 echo "YWRtaW4=" | base64 -d - 将该值进行 base64 转译之后输出到屏幕上,我们会得到 “admin”,这说明我们在 secrets.yaml 文件中的配置已经生效。

加密 secrets.yaml 文件

正如你所看到的那样,PostgreSQL 的管理员用户名和密码都以明文的方式存储在了 secrets.yaml 文件中。接下来,就让我们使用 helm-secrets 插件的 enc 子命令对该文件进行加密:

$ helm secrets enc secrets.yaml

如果你直接执行上面命令,有可能会和我一样得到下面的错误信息:

Encrypting secrets.yaml
config file not found and no keys provided through command line options
Error: plugin "secrets" exited with error

该错误信息是由 SOPS 命令报错的,它提示我们说,当它尝试加密文件时,即没有在配置文件中找到加密时要使用的公钥信息,也没有在命令行中指定公钥信息。

换言之,就是 SOPS 不知道使用哪个 GPG 公钥对文件进行加密操作。通过前面的学习我们知道,可通过 --pgp, -p 参数将密钥对信息传递给 SOPS 命令,但由于这里是通过 helm-secrets 插件调用的 SOPS 命令,我们无法像使用 SOPS 命令本身那样通过参数的方式传递信息。

其实 SOPS 命令还提供另外两种设定 GPG 密钥对信息的方式:通过设定环境变量和配置文件的方式来指定 GPG 密钥对。

设定环境变量:

如果我们没有在命令行通过 --pgp, -p 参数为 SOPS 指定密钥信息,那么它则会尝试从 SOPS_PGP_FP 系统环境变量中获取该信息,因此我们可以将密钥对 ID 指定给该环境变量:

$ export SOPS_PGP_FP=00E0160999E3C663

SOPS 配置文件:

SOPS 还支持另一种更加灵活的方式来指定密钥信息 —— 配置文件。每当执行 SOPS 命令时,它都会在当前目录下查找名为 .sops.yaml 的文件作为它的配置文件,下面是一个最基本的 SOPS 配置文件:

creation_rules:
    - pgp: "13D525EEF0A5FA38F4E78F7900E0160999E3C663"

事实上,SOPS 的配置文件能做到的远远不止这些,我们可以在配置文件中设定多条规则,并为每条规则设定不同的密钥信息,以达到不同的文件使用不同的密钥进行加密和解密,更多关于 SOPS 配置信息,请参考 SOPS 文档。

当为 SOPS 配置好密钥信息后,再次执行 helm secrets enc secrets.yaml 命令重新为 secrets 文件加密。加密操作会讲加密后的内容将直接覆盖掉原 secrets.yaml 文件中的内容,查看加密后的 secrets.yaml 文件:

$ cat secrets.yaml

postgresqlDatabase: ENC[AES256_GCM,data:aZNFdHVIgJkWDiI=,iv:zsban/onWOllCou/lpgIUzZoIGlZAjTHcVmG+Xu+asI=,tag:lCbMEVCsR7Nhc+jBZTc1Ng==,type:str]
postgresqlUsername: ENC[AES256_GCM,data:/9ciQtc=,iv:+csoC+vx7wfT8WHrzECktG1UU/sVq+Q44zdJZHMPDaU=,tag:DHiCF1x7hyHnYi7gMFm3Lg==,type:str]
postgresqlPassword: ENC[AES256_GCM,data:9xAlAbg=,iv:EH+DmDZU9g/u6eCMkaNBDhxpPkDpX7HQrhcAFVPVqOw=,tag:snEhGpmkRAwjuVrxYHCOiA==,type:str]
sops:
    kms: []
    gcp_kms: []
    azure_kv: []
    lastmodified: '2020-04-24T04:56:24Z'
    mac: ENC[AES256_GCM,data:KQ5P2C55mdaUt1EFqV9411rXY7pi/dQlRQ/FcW6BqQe45u/ZqhCNR1VD8H2tZ4BRl1mQ4xOGzOYdzddKRc/D0vNHVyoEI4IRd2Jd+QnICINDwQ/Xq9kXysOSnV308DSV+EAabxreIDKdvV7RF3b5GGoEifXS8YDVhQmQjfGyhwU=,iv:sR2TI9W+1YAzfY5k82tnkrN4Q+vH3kxITGi9kzLOMq4=,tag:auUx5q6imy2nVfJLsBoGrg==,type:str]
    pgp:
    -   created_at: '2020-04-24T04:56:22Z'
        enc: |
            -----BEGIN PGP MESSAGE-----

            hQIMA8ebLzL57hqOAQ/+Oq3bVKIx8T9EqI1EyvBeuW2lN95MDAjZD/+77ntTixW/
            zNintG5dEPT9Uqi1mlwTCqZF9D1ptuQMgLgvddV/PPLG7afO60yy1kQW2+jGWons
            N0HuinVvESs7GCj+xMBd0cyKZ0iYqbafg4Mub60T0qX4/w9IuDV1o/vswceajmaC
            YkKF/ATMnLEV0UVVeXwzyVWFGrmworPWStyx9WgB3Vj0VEYGQPub6JzSRUyGdHVr
            2JljSKlTzAzlAUbcEvKCxjWWBypkvNTMua9AhMtYtagbRlcJuSFdOIAgbIVHW6Qd
            C06b/HL5+B8umeweN36g5BSjUiD8HmtpFEKBT92WKuYfRFijkCSME3VhVGvHm4Zo
            fPPLQRswf5dRu8IXBFW52/W36/ZgzzA1kqf1cGCTETMMQghxeRSuQU9caZ4L0UFm
            ZbXdB+w92XZjiNVR5EpA1hDLbBQVKqUjZ92dpmNql5bzKoBObT+p/7hb8ixNLc66
            EBgPbefbAdQBkwuelSdNIi2vfkSFVxMLDvsL5Zn8YLrEbjaresQ0rvugxIUj4190
            moZ/I08HmsfyC02xGReK0ZCmbWR/+NcSJJOqofNAFrK0ritcT8hUPN86VuhVS6xp
            NaT1FTLBCae8vqsn1FIK09f+u64hxfQiOBmtMQ7JrTG2xDo5BiHn+sydAdD6/I3S
            XgGLdaDg1ZPsT4peB56Qy1G8H2DlwGSmM+Drazfgvus+zyW8FaVNfIUbrydu1u2X
            79U4BXQhjhDDIXwf+ipttksfEEhpMN1JxzrLO7l9pYYe13/VRR1NmkI7p3eN+GM=
            =n0UG
            -----END PGP MESSAGE-----
        fp: 00E0160999E3C663
    unencrypted_suffix: _unencrypted
    version: 3.5.0

不难发现,加密后的文件内容与我们之前使用 sops 命令加密的文件内容在格式上完全一致,这也正说明了 SOPS 是 helm-secret 插件背后真正的 “幕后使者”。

加密后的 secrets.yaml 文件将被用于部署我们的 Chart,且该文件也可以被自由分发、存储到 Git 中去,因为我们只要确保 PGP 私钥不被泄漏,就无需担心该文件会被他人解密,从而导致敏感信息被泄漏的问题。

同样,helm-secrets 插件也提供了用于解密的 dec 子命令:

$ helm secrets dec secrets.yaml

且与 SOPS 一样,解密后的内容将被存储到新创建的 secrets.yaml.dec 文件中去。

你还可以使用 helm-secrets 插件提供的 view 子命令直接查看解密后的文件内容:

$ helm secrets view secrets.yaml

postgresqlDatabase: my-database
postgresqlUsername: admin
postgresqlPassword: admin

当生成好加密后的 secrets.yaml 文件后,就可以通过 helm-secrets 插件使用该文件进行部署工作了。

部署 PostgreSQL

helm-secrets 插件提供的 install 子命令用于部署 HELM Chart,而正如我们前面所讲那样,它仅仅是对 helm install 命令进行了一层封装操作,因此用法与 helm install 命令完全一致:

$ helm secrets install postgresql stable/postgresql -f secrets.yaml

当执行以上命令时, helm-secrets 插件首先会对 secrets.yaml 文件中所有 Key 值进行解密操作,再将解密后的 YAML 文件传递给 helm install 命令进行部署。

你可以通过执行命令 kubectl get secret --namespace default postgresql -o jsonpath="{.data.postgresql-password}" | base64 --decode 来查看刚刚部署的 PostgreSQL 实例的管理员密码信息,来验证密码是否我们之前设定的 admin。

Values 文件名

不知你是否已经注意到,在部署 PostgreSQL Chart 时,我们将所有自定义变量值全部保存到了一个名为 secrets.yaml 的文件中,而在通常情况下,我们都习惯于将这些自定义变量值保存到名为 values.yaml 的文件中去。

事实上,这是有意而为之的,因为 helm-secrets 插件仅仅会对 secrets.yaml 文件,以及任何以 secrets.为前缀名的 YAML 进行解密,例如:

$ helm secrets install XXXX -f values.yaml -f secrets.yaml -f secrets.env.yaml

当使用上面命令部署时,helm-secrets 插件仅仅会对 secrets.yamlsecrets.env.yaml 文件中的所有 Key 值进行解密操作,而保留 values.yaml 中的 Key 值不变。

另外需要切记的一点是,虽然 .yaml.yml 都可作为 YAML 类型文件的后缀来使用,但 helm-secret 只支持 .yaml 后缀,使用 .yml 作为后缀的文件将无法被解密。

在其它节点执行部署操作

到目前为止,从创建 PGP 密钥对,到对 secrets.yaml 文件的加密,在到部署 PostgreSQL 等等所有这一系列操作全部都在同一台机器上完成。但当团队内有其它成员需要与你一起开发和部署 Helm Chart,或者你希望可以在其它机器上做开发部署时,则你还需要了解如何导入与导出 PGP 密钥对。

公钥的导入与导出

通过前面对 PGP 的讲解我们了解到,公钥用于加密,私钥用于解密。因此,你若希望其他人或在其它工作机器上也有能够加密 Values 文件的能力,那么,你只需将你的 PGP 公钥导入到他的工作电脑上即可。

导入 PGP 公钥有两种方式:一种是手动导出导入公钥;另一种是上传你的公钥到公钥服务器中,其他人则可以直接从公钥服务器中导入你的公钥。下面就让我们分别看一下如何通过这两种方式导入与导出你的公钥。

手动导入导出公钥

首先将公钥导入到文件中:

$ gpg --export --armor "helm-secret@email.com" > helm.pub

该命令将公钥导出到了名为 helm.pub 的文件中,将导出的公钥传递给他人。

通过 import 命令导入到自己机器中即可:

$ gpg --import helm.pub

通过公钥服务器导入与导出公钥

为了便于导入他人公钥,世界上存在许多公钥服务器,专门用于存储公钥信息。任何人都可以上传自己的公钥到这些服务器上,以便他人下载使用。

上传公钥非常简单,仅需指定要使用的服务器,以及要上传的公钥 ID 即可:

$ gpg --keyserver keyserver.ubuntu.com --send-key 00E0160999E3C663

该命令会将我们的公钥 00E0160999E3C663 上传到 keyserver.ubuntu.com 服务器上。

一旦将公钥上传到某个服务器之后,就可以从该服务器导入公钥到本地电脑中:

$ gpg --keyserver keyserver.ubuntu.com --recv 00E0160999E3C663

gpg --keyserver keyserver.ubuntu.com --recv 00E0160999E3C663
gpg: key 00E0160999E3C663: public key "HELM Secret (Used for HELM Secret Plugin) <helm-secret@email.com>" imported
gpg: Total number processed: 1
gpg:               imported: 1

无论使用哪种导入方式,在导入完成后,你都应该可以通过 gpg --list-keys 命令查看到刚刚导入都公钥信息。

提示:导入公钥仅仅使你拥有了加密的能力,因此你无法在只导入了公钥的机器上使用 helm-secrets 进行解密或部署操作。

私钥的导入与导出

与加密流程类似,若你希望其他人或在其它工作机器上能够解密你的 Values 文件,那么你则需要将你的私钥导入到其它的工作机器上。私钥的导入与导出只能通过手动方式完成:

$ gpg --export-secret-key -a "helm-secret@email.com" > helm

我们首先使用 --export-secret-key 命令将私钥导入到了 helm 文件中,将该文件通过安全的方式传递给需要的人之后,使用下面命令再次导入即可:

$ gpg --import helm

gpg: key 00E0160999E3C663: "HELM Secret (Used for HELM Secret Plugin) <helm-secret@email.com>" not changed
gpg: key 00E0160999E3C663: secret key imported
gpg: Total number processed: 1
gpg:              unchanged: 1
gpg:       secret keys read: 1
gpg:   secret keys imported: 1

当私钥导入成功后,即可通过 gpg --list-secret-keys 命令查看当前系统中的私钥信息。

导入私钥,就意味着当前所在机器拥有了解密的能力,同样也就意味着拥有了部署的能力。

提示:私钥用于解密操作,因此你可以在导入了私钥的机器上使用 helm-secrets 进行部署。但是,你若想修改 Values 文件并加密,同样需要确保你正确地导入了公钥。

再次提醒,拥有私钥就等于拥有了解密的能力,因此,一定要确保私钥的安全而不被泄密。

结束语

源码中泄漏用户名密码等敏感信息的事件频频发送,工具只是手段,最重要的是时刻保持警惕的头脑。

最后的最后,你可以通过依次执行以下命令进行清理操作:

# 删除 PostgreSQL 部署
$ helm delete postgresql

# 删除 PGP 密钥对
$ gpg --delete-secret-key 00E0160999E3C663
$ gpg --delete-key 00E0160999E3C663

# 删除所有 Values 文件
$ rm -rf secrets*.yaml*

来源:DevSecOps SIG

作者:张泽亮

声明:文章获得作者授权在IDCF社区公众号(devopshub)转发。优质内容共享给思否平台的技术同伴,如原作者有其他考虑请联系小编删除,致谢。

6月每周四晚8点,【冬哥有话说】开心一“夏”。公众号留言“开心”可获取地址

  • 0603 无敌哥 《IDCF人才成长地图与5P》(《端到端DevOps持续交付(5P)精品课》第1课)
  • 0610 冬哥 《带你玩转创新设计思维》
  • 0617 无敌哥 《敏捷项目管理到底是个啥》
  • 0624 冬哥 《VUCA时代的敏捷领导力》
阅读 540
76 声望
11 粉丝
0 条评论
你知道吗?

76 声望
11 粉丝
宣传栏