helm-secretsを使ってみた


概要

Kubernetesを使っているとSecretsをどう管理するか問題に悩まされます。

Secretsに書く値はBase64エンコードしないといけないし、平文のパスワードなどをYAMLに書いてGitHubに置くのは嫌でどう管理するのか困ってる人も多いと思います。

暗号化してGitHubに置きたいけど、そうするとSecretsと繋ぐためのスクリプトとか必要だし...など。

自分が関わっているプロジェクトでは Helm を使っており、そのプラグインとして helm-secrets というものがあったので使ってみました。

ちなみにまだ使い始めて数時間なので全然使いこなせてないと思います。


環境


  • helm-secrets: v1.3.1

% helm version

Client: &version.Version{SemVer:"v2.9.1", GitCommit:"20adb27c7c5868466912eebdf6664e7390ebe710", GitTreeState:"clean"}
Server: &version.Version{SemVer:"v2.9.1", GitCommit:"20adb27c7c5868466912eebdf6664e7390ebe710", GitTreeState:"clean"}

$ sops -v

sops 3.0.5 (latest)

% aws --version

aws-cli/1.15.50 Python/3.7.0 Darwin/17.7.0 botocore/1.10.49


helm-secrets

Helmの説明はこの記事ではしません。

helm-secretsはhelmでSecretsを扱いやすくするためのwrapperになります。


実際に暗号化を行うのはsopsなので、helm-secretsは単にHelmからsopsを扱いやすくしてSecrets管理を便利にするツールという感じです(多分)。


機能

色々あるので自分にとってありがたいと感じた部分だけ書きます


  • Helmコマンドの実行時に暗号化されたファイルを復号してくれる

  • YAML/JSONを暗号化可能

  • 平文でdiffを見ることが可能

  • PGP, AWS KMS, GCP KMSが利用可能


インストール

helmコマンドが利用可能ならば1行打つだけです。

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


暗号化してみる

使ってみたほうが理解しやすいと思うので使ってみます。

今回はAWS KMSで暗号化します。上に書いたとおり、他の方法でも可能です。

以下のようなのディレクトリ構成だとします。

$ tree .

└── test_chart
├── Chart.yaml
├── templates
│   ├── NOTES.txt
│   ├── _helpers.tpl
│   ├── deployments
│   │   └── api.yaml
│   ├── ingress
│   │   └── api.yaml
│   ├── secretss
│   │   └── api.yaml
│   └── svc
│   └── api.yaml
└── values.yaml

test_chartとは別の階層にhelm_varsを作ります。

$ mkdir helm_vars

ディレクトリは自由に切ることができます。

例えばdev/prodで切ってみましょう。

$ mkdir helm_vars/{dev,prod}

Secretで暗号化したいYAMLを作ります。


この時ファイル名は重要で、 secrets.yaml にしておく必要があります。

以下にあるように、 secrets.yaml という文字列をsedで置換しているので、別の名前にすると動きません(hoge-secrets.yamlとかなら動くかもしれませんが)

https://github.com/futuresimple/helm-secrets/blob/eb8bbfe9294838909b7b3029bec6288c6919d18c/wrapper.sh#L102

ということで作ります。

$ vim helm_vars/dev/secrets.yaml

db:
user: postgres
password: postgres

暗号化するための鍵を指定する必要があります。

AWS KMSの鍵を指定します。鍵は事前に作っておいてください。

鍵の指定は .sops.yaml 内に書きます。

これはディレクトリごとに異なる値が指定できるので、dev/prodで分けることが可能です。

$ vim helm_vars/dev/.sops.yaml

creation_rules:
- kms: 'arn:aws:kms:ap-northeast-1:1111111111:key/xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx'

暗号化してみます。

$ helm secrets enc helm_vars/dev/secrets.yaml

Encrypting helm_vars/dev/secrets.yaml
Encrypted helm_vars/dev/secrets.yaml

暗号化できました。

暗号化すると元の平文ファイルは消えて暗号化されたファイルだけになります。

$ cat helm_vars/dev/secrets.yaml

db:
user: ENC[AES256_GCM,data:/XXXXXXXX=,iv:XXXXXXXXXXXXXXXXXX/XXXXXXXXXXXXXX,tag:XXXXXXXXXXXXXXXXX,type:str]
password: ENC[AES256_GCM,data:/XXXXXXXX=,iv:XXXXXXXXXXXXXXXXXX/XXXXXXXXXXXXXX,tag:XXXXXXXXXXXXXXXXX,type:str]
sops:
kms:
- arn: arn:aws:kms:ap-northeast-1:1111111111:key/xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx
created_at: '2018-07-30T13:21:53Z'
enc: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
gcp_kms: []
lastmodified: '2018-07-30T13:21:53Z'
mac: ENC[AES256_GCM,data:XXXXXXXXXXXXXXXXXXXXXXXXX,tag:XXXXXXXXXXXXXXXXXXXXXX,type:str]
pgp: []
unencrypted_suffix: _unencrypted
version: 3.0.5

暗号化に使ったAWS KMSの鍵などがファイル中に書かれています。


元の平文を見る

これはviewサブコマンドを使うと簡単です。

helm secrets view helm_vars/dev/secrets.yaml

db:
user: postgres
password: postgres


編集する

いちいち復号して暗号化して編集するの面倒です。そういう場合はeditが使えます。

エディタが立ち上がって編集できるようになります。保存して閉じると勝手に暗号化されます。

$ helm secrets edit helm_vars/dev/secrets.yaml

db:
user: postgres
password: postgres


復号する

$ helm secrets dec helm_vars/dev/secrets.yaml

Decrypting helm_vars/dev/secrets.yaml

今度は secrets.yaml には変化はなく、 secrets.yaml.dec が生成されます。

$ cat helm_vars/dev/secrets.yaml.dec

db:
user: postgres
password: postgres


平文を削除する

cleanを使うと再帰的に .dec のファイルを削除してくれます。

$ helm secrets clean 

removed './helm_vars/dev/secrets.yaml.dec'

とは言え消し忘れると嫌なのでeditなどで触るのが良いのではないかなと思います。


Helmから利用する

helmの代わりにhelm-wrapperコマンドを利用します。

その時に暗号化されたファイルを渡すだけで、それ以外は通常のHelmのtemplateと同じ要領で使えます。

以下のようにSecretsを定義します。

$ vim test_chart/templates/secrets/secrets.yaml

apiVersion: v1
kind: Secret
metadata:
name: database
type: Opaque
data:
DB_USER: {{ .Values.db.user | b64enc | quote }}
DB_PASSWORD: {{ .Values.db.password | b64enc | quote }}

Helmを使っている人なら分かると思いますが、 values.yaml の値を利用するのと同じように書くだけです。

最後に helm-wrapper コマンドで通常の helm コマンド同様に呼び出します。

-f に暗号化されたファイルを渡すだけです。

$ export RELEASE_NAME=test

$ helm-wrapper upgrade -f ./helm_vars/dev/secrets.yaml $RELEASE_NAME ./test_chart
>>>>>> Decrypt
Decrypting helm_vars/dev/secrets.yaml

...

>>>>>> Cleanup
helm_vars/dev/secrets.yaml.dec

出力を見ると分かるように、普通にHelmを実行する前後で復号と平文の削除をやってくれています。実装を見ると非常にシンプルで上に張ったように secrets.yamlsecrets.yaml.dec にsedで置き換えています( --set とかでsecrets.yamlとか書いてても大丈夫なのか...?という不安はありつつ)。

-f はHelmではvalues.yamlを渡すためのオプションなので、復号されたYAMLがHelmに渡っているようです。

パッと読んだだけなので間違ってるかもしれませんが。。

裏側はsopsなのでsops詳しい人ならすぐ使えると思います。


その他

.gitattributes

*.yaml diff=sopsdiffer

を書くことでdiffを見られるようになったり誤って平文をコミットに入れないようにする方法だったり、他にも色々細かいTipsがあります。

興味を持った方はREADMEを読んでみてください。

https://github.com/futuresimple/helm-secrets


example

公式のexampleが付属しています。 secrets.yaml 以外にも values.yaml 書いたり階層で .sops.yaml 書いたりとかしてるので見てみると良いと思います。

https://github.com/futuresimple/helm-secrets/tree/master/example


KustomizeのsecretGeneratorとの比較

Secrets管理で困っていてKustomizeも少しだけ触ってみたりしました。

secretGeneratorの仕組みは便利で感動していたのですが、複数のパスワードを入れたファイルを暗号化して管理する良い方法が分かりませんでした。

一旦以下のようにしていたものの、パスワードが増えるごとにファイルが増えちゃって困ってました。

所詮コマンドなのでファイルからjqで取り出すとか色々やりようはありますが、helm-secretsの方がその辺り考えなくて良くて楽でした。

secretGenerator:

- name: database
commands:
PASSWORD: "aws kms decrypt --ciphertext-blob fileb://dbpassword.enc --query Plaintext --output text | base64 -d"

ただ、KustomizeだとSecretsのハッシュを取って付与してくれるので異なるSecrets扱いになる点は良かったです。Helmのテンプレート工夫すればできると思いますが。


まとめ

helm-secretsを軽く触ってみました。

少なくとも以下の自分のニーズは満たしてくれました。


  • Secretsの値を自分で書きたくない

  • 暗号化したファイルをGitHubで管理したい

  • デプロイ時にだけ復号されて自動で値を埋めてくれる

使っているとまた不満が出てくると思いますが、しばらく使ってみようと思います。