概要
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とかなら動くかもしれませんが)
ということで作ります。
$ 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.yaml
を secrets.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で管理したい
- デプロイ時にだけ復号されて自動で値を埋めてくれる
使っているとまた不満が出てくると思いますが、しばらく使ってみようと思います。