GKEでのsecretの扱いにkubernetes-external-secretsを導入した話

[ミクシィグループ Advent Calendar 2020](https://qiita.com/advent-calendar/2020/mixi) の25日目の投稿です。

こんにちは, @Taillookです。
普段の業務ではRailsやGo,React,GKEなどを触っています。

## はじめに

k8sのマニフェストをGitリポジトリで扱う場合、secretの情報をセキュアな状態で管理する必要があります。
自分はkubesec,SealedSecretをこれまでに使ってきましたがkubesecではArgoCD等を使ったGitOpsとの併用がしづらかったり、SealedSecretでは暗号化後のファイルのレビューができないという理由で別の手段を探していました。
今回これらを解決する手段としてkubernetes-external-secretsがあると知り導入してみました。

## kubernetes-external-secretsとは

[Kubernetes External Secrets — GoDaddy Engineering Blog](https://jp.godaddy.com/engineering/2019/04/16/kubernetes-external-secrets/)

kubernetes-external-secretsはGoDaddyのチームが開発した、AWSのSecret Manager等、k8sのクラスタ外部にあるsecret情報にアクセスしk8sのsecretとして扱うようにするソリューションです。

## GKEへの導入
### [本家のHelmChart](https://github.com/external-secrets/kubernetes-external-secrets/tree/master/charts/kubernetes-external-secrets)を手元にダウンロード

(これを使い続けるのはvertion追従しづらいので最新のmasterのChartを取得するコマンドを書いておくとよさそうです。

### サービスアカウントの追加

Secret Managerの`secretmanager.versions.access`権限のあるサービスアカウントを追加します。

### kubernetes-external-secretsのChartにGCP Secret Managerのためのsecretを追加する

[external-secretsのためのsecrets](https://github.com/external-secrets/kubernetes-external-secrets#deploy-kubernetes-external-secrets-using-a-service-account-key)を設定するためGit管理できるようにテンプレートから生成できるようにします。

```yaml:secret.yaml.template
apiVersion: v1
kind: Secret
metadata:
  name: external-secrets-secret
type: Opaque
stringData:
  gcp-creds.json: |-
```

`service_account_key.json`という名前で先ほど追加したサービスアカウントの鍵のjsonを作成し以下スクリプトでテンプレートからsecretのyamlを生成するようにしました。

```sh:create-secret.sh
#!/bin/sh
sed -i '' 's/^/    /g' service_account_key.json && cat service_account_key.json >> secret.yaml.template && cp secret.yaml.template secret.yaml
```

### valuesにexternal-secrets-secretを追加する
valuesにある

```yaml
# GOOGLE_APPLICATION_CREDENTIALS: /app/gcp-creds/gcp-creds.json
```
と

```yaml
# filesFromSecret:
#   gcp-creds:
#     secret: gcp-creds
#     mountPath: /app/gcp-creds
```
のコメントを外し`/app/gcp-creds/gcp-creds.json`に設定したexternal-secrets-secretをマウントしGOOGLE_APPLICATION_CREDENTIALSから参照できるようにします。

この状態で

```sh:install-external-secrets.sh
#!/bin/sh
helm install external-secrets ./chart --skip-crds
```
を実行しexternal-secretsをインストールします

### secretを適用する
GCP Secret Managerで画像のようにシークレットを作成します。
<img width="400" alt="スクリーンショット 2020-12-24 17.41.54.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/165916/bb9646df-a26b-ecc8-4e7e-4b3e005059b4.png">

作成したシークレットの名前で以下のyamlのsecretが適用できます。
dataのkeyはGCPのSecret Managerで設定したシークレットの名前で、nameはk8sのsecretでの名前になり、versionが同一シークレット名内でのバージョンとなります。バージョンはlatestでも指定できます。

```yaml:secret.yaml
apiVersion: kubernetes-client.io/v1
kind: ExternalSecret
metadata:
  name: gcp-secrets-manager-example
spec:
  backendType: gcpSecretsManager
  projectId: GCP-project
  data:
    - key: GCP_PROJECT_RAILS_ENV
      name: RAILS_ENV
      version: 1
```

これでk8s内でsecretとして扱えるようになります。

## まとめ

SealedSecretと比較して、`key: GCP_PROJECT_RAILS_ENV`のように記述でき、GCPコンソール上で平文で確認もできるのでyamlのレビューが可能になったので読めない文字列のapproveをしてsecret適用できなかった！という事態も少なくなり治安も良くなりました。
kubesecとの比較としてもsecretへの変換をk8s上でハンドルできるのでArgoCDを使ったGitOpsの相性が良くなる利点もあります。

---
GKEやKubernetesについてはまだまだ勉強不足なのでより深く知れたら良いなと常々感じています。
来年はKubernetes周りのコードリーディングとかしてみたいです。