GitOps環境にExternal Secret Operatorを導入した
FluxなどGitOps環境でSecretリソースを安全に扱う方法を考え、External Secret Operatorを導入しました。
External Secret Operator ( https://external-secrets.io/latest/ ) は、GCPのSecret Managerなど外部のSecret管理ツールからデータを読み取って、kubernetesのsecretを生成してくれます。 external secretリソースで、SecretManagerのkeyを指定すると、SecretManagerに保存してあるデータでsecretを作成します。
具体的には、こういうマニフェストを作ると、exampleという名前のsecretが、SecretManagerに保存済みの"sm-key" の値で作成されます。
apiVersion: external-secrets.io/v1beta1 kind: ExternalSecret metadata: name: es-example spec: secretStoreRef: kind: ClusterSecretStore name: gcp-cluster-secret-store target: name: example creationPolicy: Owner dataFrom: - extract: key: sm-key
mozilla/sops のようにマニフェストを暗号化/複合化するアプローチの場合、GitOpsのツールが複合化できるように設定せなばならず、それが面倒ですが、External Secret Operatorの場合、External Secret ControllerがSecretの生成まで行ってくれるため、GitOpsのツールは暗号化を気にすることなく、Secretを利用できます。 最終的にはkuberentes標準のSecretリソースになるため、扱いやすいです。
インストール
External Secret Operatorのインストールは、Helmを利用するのが一般的なようです。今回は Fluxを導入済みのため、FluxでExternal Secret Operatorをインストールしました。
examplesにFluxを利用したインストール例が載っているため、これを参考にしてインストールしました。 https://external-secrets.io/v0.9.8/examples/gitops-using-fluxcd/
まず、レポジトリの定義を行います。GitRepostitoryのURLがhttpでかつmainブランチになっていなのですが、https にしたうえで、現時点で最新のv0.9.8 を指定します。
apiVersion: source.toolkit.fluxcd.io/v1beta1 kind: HelmRepository metadata: name: external-secrets namespace: flux-system spec: interval: 10m url: https://charts.external-secrets.io --- apiVersion: source.toolkit.fluxcd.io/v1beta1 kind: GitRepository metadata: name: external-secrets namespace: flux-system spec: interval: 10m ref: tag: v0.9.8 url: https://github.com/external-secrets/external-secrets
次にCRDを定義します。CRDは差分が検出できないのか、毎回リソースの更新 ( CustomResourceDefinition/clustersecretstores.external-secrets.io configured というログがでる)が発生してしまいます。頻繁に更新が行われても特に問題は出なそうでしたが、気になったので、作成後にintervalを十分に長くすることで対応しました。
apiVersion: kustomize.toolkit.fluxcd.io/v1beta2 kind: Kustomization metadata: name: external-secrets-crds namespace: flux-system spec: interval: 10m path: ./deploy/crds prune: true sourceRef: kind: GitRepository name: external-secrets
次に、Helmを利用してoperatorをインストールします。ここでも現時点で最新の0.9.8を指定しました。namespaceは別途手動で作成していたのでcreateNamespaceはfalseにします。
apiVersion: helm.toolkit.fluxcd.io/v2beta1 kind: HelmRelease metadata: name: external-secrets namespace: flux-system spec: # Override Release name to avoid the pattern Namespace-Release # Ref: https://fluxcd.io/docs/components/helm/api/#helm.toolkit.fluxcd.io/v2beta1.HelmRelease releaseName: external-secrets targetNamespace: external-secrets interval: 10m chart: spec: chart: external-secrets version: 0.9.8 sourceRef: kind: HelmRepository name: external-secrets namespace: flux-system values: installCRDs: false # Ref: https://fluxcd.io/docs/components/helm/api/#helm.toolkit.fluxcd.io/v2beta1.Install install: createNamespace: false
最後にClusterSecretStoreを定義します。このマニフェストはExternal Secrets OperatorのCRDが追加済みでないとエラーになるので、 dependsOnで依存関係を明示しておきます。こうすることで、Fluxが意図した順番に処理することができます。
apiVersion: kustomize.toolkit.fluxcd.io/v1beta2 kind: Kustomization metadata: name: external-secrets-crs namespace: flux-system spec: dependsOn: - name: external-secrets-crds interval: 10m path: ./infrastructure/external-secrets/crs prune: true sourceRef: kind: GitRepository name: flux-system
ClusterSecretStoreを定義するマニフェストは、例にしたがって /infrastructure/external-secrets/crs 配下に配置します。今回はGCPのSecretManagerを利用し、WorkloadIdentityで認証を行うので、サービスアカウントにアノテーションを追加します。WorkloadIdentityの設定については省略します。
apiVersion: v1 kind: ServiceAccount metadata: name: external-secrets namespace: external-secrets annotations: iam.gke.io/gcp-service-account: {serviceaccount-email}@{gcp-projectid}.iam.gserviceaccount.com --- apiVersion: external-secrets.io/v1beta1 kind: ClusterSecretStore metadata: name: gcp-cluster-secret-store spec: provider: gcpsm: projectID: {projectid} auth: workloadIdentity: clusterLocation: {cluster location} clusterName: {cluster name} serviceAccountRef: name: external-secrets namespace: external-secrets
ここまで設定すると、ExternalSecretリソースを利用してSecretManagerに登録済みの値でSecretが作成できるようになります。マニフェストにはSecretManagerのkeyしか記載されないので、Gitレポジトリにcommitすることができ、GitOpsフローでSecretが管理できるようになりました。
GKEのSecret管理について調べると、Seald Secret、Berglas など出てきますが、どちらも現在は古いようなので、External Secret Operatorがよいんじゃないかと思います。 特にBerglasはSecretManagerリリース後は利用は推奨されていないようでした。
今までは、secretのマニフェスト自体を暗号化して、デプロイ直前に復号してapplyする方法が使われていて、これはGitOpsと相性悪くてどうしたものかと思っていたのですが、External Secret Operatiorで良い感じに解決できました