Kubernetesを運用する際の話題の1つとして、「機密情報をどのように管理してアプリケーションから利用するのか」というものがきっとあります。

KubernetesのSecretは文字列をbase64エンコードしただけの状態で保存されていますので、容易にデコードが可能です。
RBACを利用してアクセス制御をするのも非常に面倒なので、Secretを利用せずに済む方法を探します。

筆者がすぐに思いつくものとして、 BerglasExternal Secretsがあります。

この記事では、Berglasをアプリケーションから使う方法を紹介して、そのメリットやデメリットを考えていきます。

Berglasはwebhookの管理が大変

詳しくは後述しますが、BerglasではKubernetesのMutatingAdmissionWebhookというものを利用して、Cloud Functionsへデプロイした関数へコンテナを作る度にリクエストをします。

この関数はサンプルをそのまま利用することができますが、このサンプルは admissionregistration.k8s.io/v1beta1 のものであり、admissionregistration.k8s.io/v1ではそのまま利用することができません。
また、httpトリガーによる呼び出しの際にBerglas側を認証して関数の呼び出しを制限する方法もわかりませんでした。
関数のメンテナンスとその呼び出しのアクセス制御に課題が残ります。

この点を除けばSecretを利用せずに、任意のコンテナの環境変数にSecret Managerに保存した値を簡単に設定できるのでとても便利に感じました。

Berglasの使い方

クラスタの作成とコンテナの用意

ここではGKEでの利用を想定しています。
Workload Identityを有効にしたクラスタを用意しますが、この時autopilotクラスタは利用しないでください。
autopilotクラスタではまだadmission webhookが利用できません。
berglasコマンドはインストール済みとします。

gcloud beta container clusters create berglas \
    --disk-size=20 \
    --num-nodes=1 \
    --region=asia-northeast1 \
    --node-locations=asia-northeast1-a \
    --network=<YOUR_NETWORK> \
    --subnetwork=<YOUR_SUBNETWORK> \
    --workload-pool=<YOUR_PROJECTID>.svc.id.goog

Workload Identityを有効にするのは、PodがSecret Managerに保存された機密情報を読めるようにするためです。

動作検証として、環境変数SECRET_VALUEだけを返すwebサーバを用意して使うものとします。
このコンテナイメージを、 gcr.io/<YOUR_PROJECTID>/http:v1としてGCRに保存しています。

KSAとGSAの紐付け

以降
– KSA = Kubernetes Service Account
– GSA = Google Service Account

とします。

Podに設定したKSAがSecret Managerに保存した値を読むために、対応する権限を持ったGSAを作成してKSAと紐付けます。

まずはKSAhttpを作成します。

kubectl create serviceaccount http

続いてGSAberglass-accessorを作成し、KSAをGSAと紐付けます。

gcloud iam service-accounts create berglas-accessor
gcloud iam service-accounts add-iam-policy-binding \
    --role "roles/iam.workloadIdentityUser" \
    --member "serviceAccount:$PROJECT_ID.svc.id.goog[default/http]" \
    berglas-accessor@$PROJECT_ID.iam.gserviceaccount.com

これでWorkload Identityの設定は終わりです。

機密情報の作成

SECRET_VALUEという名前でSecret Managerに機密情報を作成します。
その後、任意の値でバージョンを作成します。ここでは値は “SECRET”としました。

gcloud secrets create SECRET_VALUE

printf "SECRET" | gcloud secrets versions add SECRET_VALUE --data-file=-

gcloud secrets versions access 1 --secret=SECRET_VALUE
SECRET

webhookのデプロイ

サンプルの関数をそのまま利用することにします。
コードをクローンして、このディレクトリで以下を実行してください。

gcloud functions deploy berglas-secrets-webhook \
  --project <YOUR_PROJECTID> \
  --allow-unauthenticated \
  --runtime go113 \
  --entry-point F \
  --trigger-http

マニフェストファイルのデプロイ

以下のマニフェストファイルberglas.yamlを用意しました。
名前や<YOUR_PROJECTID>の部分を置き換えてください。

apiVersion: v1
kind: Pod
metadata:
  name: http
  labels:
    name: http
spec:
  serviceAccountName: http
  containers:
  - name: http
    image: gcr.io/<YOUR_PROJECTID>/http:v1
    command: ["./main"]
    resources:
      limits:
        memory: "128Mi"
        cpu: "100m"
    env:
      - name: SECRET_VALUE
        value: sm://<YOUR_PROJECTID>/SECRET_VALUE
    ports:
      - containerPort: 8080
---
apiVersion: admissionregistration.k8s.io/v1beta1
kind: MutatingWebhookConfiguration
metadata:
  name: berglas-webhook
  labels:
    app: berglas-webhook
    kind: mutator
webhooks:
- name: berglas-webhook.cloud.google.com
  clientConfig:
    url: https://us-central1-<YOUR_PROJECTID>.cloudfunctions.net/berglas-secrets-webhook
    caBundle: ""
  rules:
  - operations: ["CREATE"]
    apiGroups: [""]
    apiVersions: ["v1"]
    resources: ["pods"]

これをデプロイし、コンテナにアクセスして機密情報が取得できていれば成功です。

$ kubectl apply -f berglas.yaml
pod/http created
Warning: admissionregistration.k8s.io/v1beta1 MutatingWebhookConfiguration is deprecated in v1.16+, unavailable in v1.22+; use admissionregistration.k8s.io/v1 MutatingWebhookConfiguration
mutatingwebhookconfiguration.admissionregistration.k8s.io/berglas-webhook created

$ kubectl port-forward http 8080:8080
Forwarding from 127.0.0.1:8080 -> 8080
Forwarding from [::1]:8080 -> 8080

$ curl localhost:8080
SECRET_VALUE is SECRET

Pod作成時にMutatingWebhookConfigurationが作成されていないとうまくいかないので、その場合はPodを再作成してみてください。

まとめ

BerglasはGCPのSecret Managerへ保存した情報をアプリケーションから便利に利用できることは間違いないと思いますが、冒頭でも述べた通りwebhookが必要なこと、そのメンテナンスが必要なこと、関数の呼び出し元を制御する方法がすぐに見つからないことなど、いくつか課題があるという感想です。

次回はExternal Secretsを利用してみて、Berglasと比べてみます。