External Secret Operatorとは
External Secret Operator (ESO)とは外部のシークレットをKubernetes上で管理するためのソフトウェアです。
以下記事でいくつか特徴が挙げられていますが、一番はGitOpsを実施する際のシークレット管理がしやすくなることだと思います。GitHubにシークレット情報をコミットせずにGitOpsを実現できます。
https://atmarkit.itmedia.co.jp/ait/articles/2209/29/news015.html
今回はHashicorp Vaultを外部シークレットマネージャとして利用します。
前提
- Vaultを知っている
- Kubernetesを知っている
環境
- Windows11 (wsl2)
- Kubernetes version 1.25.3 (kind構築)
- Vault version 1.12
検証
今回やること
- ESOインストール
- Vault のKubernetes authを利用する認証設定
- 動作確認
ESOインストール
ESOのインストールはとても簡単で以下helmコマンドを実行するだけです。
helm repo add external-secrets https://charts.external-secrets.io
helm install external-secrets \
external-secrets/external-secrets \
-n external-secrets \
--create-namespace \
--set installCRDs=true
シークレットデータ準備
Vaultにテストデータを登録します。
今回はESOの機能を確認できれば良いので「my-value=mysecret」を登録してます。
/ $ vault secrets enable -path=eso -version=2 -description="This secret engine is used the ESO to get secrets" kv
Success! Enabled the kv secrets engine at: eso/
/ $ vault kv put eso/foo my-value=mysecret
== Secret Path ==
eso/data/foo
======= Metadata =======
Key Value
--- -----
created_time 2023-02-04T10:19:04.717624389Z
custom_metadata <nil>
deletion_time n/a
destroyed false
version 1
Kubernetes auth 準備
ESOが利用できる認証方式は5つあります。
- token-based
- appRole
- kubernetes-native
- ldap
- jwt/oidc
今回は一番運用しやすそうなKubernetes authを検証します。理由は最後に少し触れます。
検証にあたっては以下のページを参考にしてます。
https://cloud.redhat.com/blog/vault-integration-using-kubernetes-authentication-method#:~:text=Kubernetes%20Auth%20Method%20The%20Kubernetes%20authentication%20method%20can,%2Fvar%2Frun%2Fsecrets%2Fkubernetes.io%2Fserviceaccount%2Ftoken%20and%20is%20sent%20to%20Vault%20for%20authentication.
「system:auth-delegator」はKubernetesのデフォルトロールです。
https://kubernetes.io/ja/docs/reference/access-authn-authz/rbac/#%E4%BB%96%E3%81%AE%E3%82%B3%E3%83%B3%E3%83%9D%E3%83%BC%E3%83%8D%E3%83%B3%E3%83%88%E3%81%AErole
kubectl create sa vault-auth -n vault
kubectl create clusterrolebinding k8s-auth-client-for-vault --clusterrole=system:auth-delegator --serviceaccount=vault:vault-auth
Kubernetes authをするうえでVault側に必要なのは以下3つです。
- Service Accountのトークン
- kubernetes API Serverのエンドポイント
- Kubernetes ClusterのCA証明書
まずはService Accountのsecretを取得できるように以下のようなリソースを作成します。
kubectl apply -f vault-auth.yaml -n vault
apiVersion: v1
kind: Secret
metadata:
name: vault-auth
annotations:
kubernetes.io/service-account.name: "vault-auth"
type: kubernetes.io/service-account-token
2, 3はそれぞれKubernetes configファイルから取得できるのでこれで準備ができました。
以下のように整形しつつ値を取得します。
Service AccountのトークンとCA証明書はbase64でエンコードされているので注意が必要です。ここを間違えると認証がうまくいきません。
token_reviewer_jwt=$(kubectl get secret -n vault -o jsonpath='{.data.token}' vault-auth | base64 -d)
kubernetes_host=https://kubernetes.default.svc.cluster.local # vault がKubernetes API Serverにアクセスするために必要
kubernetes_ca_cert=$(kubectl config view -o jsonpath='{.clusters[].cluster.certificate-authority-data}' --minify=true --raw | base64 -d)
Kubernetes auth 設定
以下コマンドでkubernetes authを有効化します。
role名を「eso-vault」としてpolicyは先ほど作成したロールです。
vault auth enable -description="k8s secret engine for vault" kubernetes
vault write auth/kubernetes/config \
token_reviewer_jwt=$token_reviewer_jwt \
kubernetes_host=$kubernetes_host \
kubernetes_ca_cert=$kubernetes_ca_cert
vault write auth/kubernetes/role/eso-vault \
bound_service_account_names=vault-auth \
bound_service_account_namespaces=vault \
policies=default,eso-vault-client \
ttl=15m
ESO準備
ESOがリクエストするためのロールを設定します。
vault policy write eso-vault-client eso-vault-client.hcl
path "eso/data/*" {
capabilities = ["read"]
}
動作確認します。以下のような結果がかえってくればOKです。
/ $ vault write auth/kubernetes/login role=eso-vault jwt=$token_reviewer_jwt
Key Value
--- -----
token hvs.CAESIKQznRS1XNprF8eQjF_zZFlL8Da9jCHRmhCvTE3F5aP9Gh4KHGh2cy5IMGo1SHd0eU9ldzhIc00wZEJ2WG1MZGs
token_accessor dmRuQwUOUugmuXzqPEIwzVxl
token_duration 1m
token_renewable true
token_policies ["default" "eso-vault-client"]
identity_policies []
policies ["default" "eso-vault-client"]
token_meta_role eso-vault
token_meta_service_account_name vault-auth
token_meta_service_account_namespace vault
token_meta_service_account_secret_name vault-auth
token_meta_service_account_uid d35d56fd-24f3-45b8-a1e9-20b15a08f172
ここでやっとESOの設定が登場します。
ESOのリソースは3種類あり、それぞれClusterスコープのClusterSecretStoreとNamespaceスコープのSecretStore、ExternalSecretです。
ここではClusterSecretStoreとExternalSecretを組み合わせて利用します。
ESOサイトより引用
https://external-secrets.io/v0.7.2/provider/hashicorp-vault/
まずはそれぞれの設定をyamlで書きます。
apiVersion: external-secrets.io/v1beta1
kind: ClusterSecretStore
metadata:
name: vault-backend-k8s-auth
labels:
service: secret-manager
version: 1.0.0
env: prd
spec:
provider:
vault:
server: http://vault.vault.svc.cluster.local:8200 # 接続先のVault Cluster
path: eso
version: v2
auth:
kubernetes:
# Path where the Kubernetes authentication backend is mounted in Vault
mountPath: kubernetes # 有効にしたkubernetes auth。今回はデフォルトなのでkubernetes
# A required field containing the Vault Role to assume.
role: eso-vault # 作成したvaultのrole名
# Optional service account field containing the name
# of a kubernetes ServiceAccount
serviceAccountRef:
name: vault-auth
namespace: vault
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: vault-eso-example
spec:
refreshInterval: 15s
secretStoreRef:
name: vault-backend-k8s-auth # 利用するSecretStoreの名前
kind: ClusterSecretStore
target:
name: output-secret # 作成するsecretの名前
data:
- secretKey: foobar # 作成するSecretのKeyの名前
remoteRef:
# vault kv put eso/foo my-value=mysecretで作成したシークレットを取得する設定
key: eso/foo
property: my-value
適用してエラーが出なければOK。Secretの動機まで10秒程度かかります。
kubectl apply -f ./secret-store-k8s-es.yaml
kubectl apply -f ./secret-store-k8s-ss.yaml
root@mypc02:/mnt/e/work/eso# kc get clustersecretstores.external-secrets.io,externalsecrets.external-secrets.io
NAME AGE STATUS CAPABILITIES READY
clustersecretstore.external-secrets.io/vault-backend-k8s-auth 26m Valid ReadWrite True
NAME STORE REFRESH INTERVAL STATUS READY
externalsecret.external-secrets.io/vault-eso-example vault-backend-k8s-auth 15s SecretSynced True
動作確認
最後にSecretを取得してその値を確認します。
root@mypc02:/mnt/e/work/eso# kc get secret
NAME TYPE DATA AGE
output-secret Opaque 1 76s
root@mypc02:/mnt/e/work/eso# kc get secret output-secret -o yaml | grep data: -A1
data:
foobar: bXlzZWNyZXQ=
root@mypc02:/mnt/e/work/eso# echo bXlzZWNyZXQ= | base64 -d
mysecret
おわりに
今回GitHubにKubernetesのSecret情報をコミットするのをうまく回避したくてESOの検証をしました。
現場で利用する際には検討する箇所がいくつかあるかとは思いますが、なかなか使いやすいと思いました。途中でkubernetes authを選んだ理由を後回しにしていたので最後に少し触れます。ただ未検証ですのであっているかどうかはわからないので、参考程度に聞いていただければと思います。
まず、ldapとjwt/oidcはM2Mの認証には適さないので除外しました。
token-based を利用した場合はstaticな認証方式であることとttl、max-ttlの更新が気になりました。ttl、max-ttlによっては定期的に手動で更新が必要になりそうだと思いました。
approleはdynamicな認証方式であるもののやはりttl、max-ttlの更新が必要になり別途更新する仕組みが必要になりそうでした。
以上です。最後まで読んでいただきありがとうございます。