helm-secretsを使ってみた

# 概要
Kubernetesを使っているとSecretsをどう管理するか問題に悩まされます。
Secretsに書く値はBase64エンコードしないといけないし、平文のパスワードなどをYAMLに書いてGitHubに置くのは嫌でどう管理するのか困ってる人も多いと思います。
暗号化してGitHubに置きたいけど、そうするとSecretsと繋ぐためのスクリプトとか必要だし...など。

自分が関わっているプロジェクトでは [Helm](https://github.com/helm/helm/) を使っており、そのプラグインとして [helm-secrets](https://github.com/futuresimple/helm-secrets) というものがあったので使ってみました。
ちなみにまだ使い始めて数時間なので全然使いこなせてないと思います。

# 環境
- helm-secrets: v1.3.1

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

```console
$ sops -v
sops 3.0.5 (latest)
```

```console
% 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](https://kubernetes.io/docs/concepts/configuration/secret/)を扱いやすくするためのwrapperになります。  
実際に暗号化を行うのは[sops](https://github.com/mozilla/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で暗号化します。上に書いたとおり、他の方法でも可能です。

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

```console
$ 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を作ります。

```console
$ mkdir helm_vars
```

ディレクトリは自由に切ることができます。
例えばdev/prodで切ってみましょう。

```console
$ 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.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で管理したい
- デプロイ時にだけ復号されて自動で値を埋めてくれる

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