Skip to content

External Secrets Operatorの導入 GCP編

本章では、Qmonus Value Streamの応用として、Official Cloud Native AdapterであるExternal Secrets Operator AdapterとGKE Cluster Secret Store Adapterを使用して、GCPのSecret Managerの秘密情報をGKEのSecretリソースへ自動連携する手順を解説します。

External Secrets Operatorは、秘密情報を管理するプロバイダ(GCP Secret Manager, Azure Key Vault, AWS Secrets Managerなど)と連携し、Kubernetes環境のSecretリソースと同期できます。 External Secrets Operatorが提供する主なCustom Resourceとして、Cluster Secret StoreリソースとExternal Secretリソースが存在します。Cluster Secret Storeはクラスタ単位でデプロイされ、外部プロバイダへ認証し、External SecretはCluster Secret Storeを指定して外部プロバイダの秘密情報を取得し、Secretリソースを生成します。

公式イメージ: External Secrets Operatorの導入 GCP編

Qmonus Value Streamを使用したExternal Secrets Operator導入の公式の手順として、Official Cloud Native Adapterを使用してCluster Secret StoreをGKEへデプロイし、GCP Secret ManagerがGKEのSecretリソースへ同期できる環境を提供します。
以下のプライベートリポジトリが作成されていることを前提とします。

  • 持ち込みのGitリポジトリ
    • GitHub、GitLabなどのインターネットからアクセス可能なGitリポジトリをご用意ください。
    • 本チュートリアルではGitHubを使って説明しますが、適宜ご利用されているサービスに読み替えてください。
    • GitHubリポジトリへの認証は Personal Access Token (GitLabの場合は Personal access tokens)を使用することを前提とします。

以降のステップでは主に下記の作業を通じて、External SecretリソースにSecret Managerの秘密情報が連携されていることを確認します。

  • External Secrets Operator AdapterとGKE Cluster Secret Store AdapterをQmonus Values Streamでデプロイする
  • GCP Secret Managerへ値を登録し、サンプルExternal SecretリソースをApplyして自動作成されるSecretリソースに秘密情報が連携されていることを確認する

0-1. 準備:External Secrets OperatorのCustom Resource Definition の適用

External Secrets OperatorのCustom Resource Definitionから、v0.5.0以上の任意のバージョンを指定してGKEにApplyしてください。

bash
# v0.5.0以上の任意のバージョンをcloneする
git clone -b ${v0.5.0_or_more} https://github.com/external-secrets/external-secrets.git

# kubectl の現在のコンテキストを確認する
kubectl config current-context
(External Secrets Operatorをデプロイするクラスタ名)

# Custom Resource DefinitionをApplyする
kubectl apply -f external-secrets/deploy/crds/bundle.yaml

0-2. 準備:Workload Identityの構成

Workload Identityを使用することで、使用しているGCPのService Accountの権限をGKEのワークロードに付与することが可能になります。 これにより、ワークロードがGCPリソースにアクセス出来るようになります。 以下を参考に、Workload Identityの設定準備を行ってください。

  1. Workload Identityを有効にしてください。
  2. Kubernetes Service Account(以下、KSA)と紐づけるKSAと紐づけるGoogle Service Account(以下、GSA)の作成とIAMによるWorkloadIdentityを設定してください。
    • GSAの作成
      • GSAを任意の名前(${gsaName})で作成してください。default値を使用する場合はGSAの名前を"external-secrets-operator"として作成してください。
      • GSAにSecret Managerへの読み取り権限として、roles/secretmanager.secretAccessor を付与してください。
    • Workload Identityの設定
      • KSAにGSAのポリシーをバインディング、およびroles/iam.serviceAccountTokenCreatorを付与してください。
      bash
      # gcloudコマンド例(Secret Managerが存在するプロジェクトで実行してください)
      gcloud iam service-accounts add-iam-policy-binding ${gsaName}@${gsaGcpProject}.iam.gserviceaccount.com --role roles/iam.serviceAccountTokenCreator --member "serviceAccount:${k8sClusterGcpProject}.svc.id.goog[${ksaNamespace}/${appName}]"
      ここで使用したパラメータについての詳細は以下の通りです。特に変更する必要がなければ、default値のあるものはdefault値を使用することを推奨します。また、このパラメータは手順5で再度使用します。
      Parameter NameDefaultDescription
      appNamegcp-secret-managerKSAおよびCluster Secret Storeのリソース名
      gsaGcpProject-GSAが存在するGCPプロジェクトID
      k8sClusterGcpProject-GKEクラスタが存在するGCPプロジェクトID
      gsaNameexternal-secrets-operatorGSA名
      ksaNamespaceqmonus-systemKSAをデプロイするksaNamespace

1. Kubeconfigの作成 (CLI)

Qmonus Value Streamは、Kubeconfigと呼ばれる認証ファイルを利用してKubernetesへアプリケーションをデプロイします。 ここでは、クラスタ・スコープのKubeconfigを生成します。

以下の手順にしたがって、Kubernetesクラスタにアクセスし、External Secrets Operator AdapterとGKE Cluster Secret Store Adapter によって定義されるリソースをデプロイするためのNamespaceを作成し、Kubeconfigを生成してください。Namespaceは、default値を使用するのであればqmonus-systemとなります。 ここで、${k8sClusterGcpProject}はKubernetesクラスタが存在するGCP Project ID、${k8sClusterName}はKubernetesクラスタ名、${zone}はGKEのZone名を指定します。

bash
# 利用するKubernetesへ接続
gcloud --project=${k8sClusterGcpProject} container clusters get-credentials ${k8sClusterName} --zone ${zone}

# Kunbernetes Service Accountをデプロイする、手順[0-2]で使用した${ksaNamespace}と同じNamespaceを作成する。
# ESOと同じであれば、skipしてください。
kubectl create ns ${ksaNamespace}

# External Secrets Operator AdapterとGKE Cluster Secret Store Adapter デプロイ用のKubeconfigを生成
# Namespaceがクラスタに存在しない場合は新たに作成されます。
# Namespaceは、default値を使用するのであれば"qmonus-system"
qvsctl plugin gen-kubeconfig -p -o cluster.kubeconfig.yaml [-n ${esoNamespace}]

WARNING

クラスタ・スコープのKubeconfigは権限が非常に強力なため、お取り扱いには注意してください。

なお、gcloudコマンドをはじめて実行する場合は、以下の手順によりgcloudコマンドを初期化してください。

bash
gcloud auth login

2. Applicationの登録 (GUI)

今回利用するサンプルアプリケーションを登録します。

  1. 左メニューより、Applicationを選択します。
  2. 画面右上の NEW APPLICATION ボタンを押下します。
  3. 各フォームに以下の値を入力し、画面右下の NEXT ボタンを押下します。
    • Display Name: External Secrets Operator
    • Description: (任意の文章または空白)
    • QVS Config Repository: + Create New Repositoryを選択し、手順2-aに従いRepositoryを作成
    • QVS Config File Path: eso-clustersecretstore/.valuestream/qvs.yaml

以下は、必要な値を入力した状態のApplicationの登録画面になります。

Applicationの登録

2-a. Repositoryの登録

今回用意したプライベートリポジトリをQmonus Value Streamに登録します。

以下のパラメータの値を変更し、画面右下のCREATEボタンを押下します。

  • Repository Kind: github
  • Git Clone Protocol: https
  • Repository Visibility: private
  • Git Clone URL: (チュートリアルで利用するリポジトリのURLを入力、例:https://github.com/sample-org/sample-repo-name.git)
  • Description: (任意の文章または空白)
  • Git Token:(プライベートリポジトリへの認証に利用するTokenを入力)

3. Deploymentの登録 (GUI)

手順1で生成した、クラスタ・スコープのkubeconfigを使用してDeploymentを作成します。

各フォームに以下の値を入力し、画面右下の CREATE ボタンを押下します。

  • Display Name: staging
  • Name: (Environment選択時に自動入力)
  • Environment: + Create New Environmentを選択し、手順3-aに従いEnvironmentを作成
    • 注) Application単位で、同一Environmentを選択できません
  • Credentials
    • kubeconfig: 手順1で生成した、cluster.kubeconfig.yamlの内容

以下は、必要な値を入力した状態のDeploymentの登録画面になります。

Deploymentの登録

3-a. Environmentの登録

デプロイ先となる環境を登録します。検証環境、商用環境といった、プロジェクト/プロダクトに応じて任意の環境を定義してください。 また、Qmonus Value Streamが推奨する環境の考え方については、環境定義を参考にしてください。

各フォームに以下の値を入力し、画面右下のCREATEボタンを押下します。

  • Display Name: (staging など、任意の環境名)
  • Description: (任意の文章または空白)
  • Provisioning Target:
    • Kind:kubernetes
    • DisplayName: Provisioning Targetを一意に指定できるNameやID
    • Alias:(空白)
    • 同じタイプのProvisioning Targetを複数登録する際は、2つ目以降については Aliasのフォームに そのProvisioning Targetを特定できるエイリアス名を付与してください。

4. Official Cloud Native Adapterのダウンロード (CLI)

プライベートリポジトリでOfficial Cloud Native AdapterのCI/CD AdapterとInfrastructure Adapterをqvsctlコマンドでダウンロードします。

bash
# プライベートリポジトリをcloneする
git clone https://gihub.com/${your_organization}/${your_repository}

# プライベートリポジトリに移動
cd ${your_repository}

# 本チュートリアル用のブランチを作成する
git checkout -b qvs_eso_clustersecretstore

# KubeconfigをGitリポジトリの管理から除外する
echo 'cluster.kubeconfig.yaml' >> .gitignore

# 本チュートリアル用のディレクトリを作成する
mkdir -p eso-clustersecretstore/.valuestream
cd eso-clustersecretstore/.valuestream

# qvsctl auth コマンドを使用して認証する(出力されたurlへアクセスしてください)
qvsctl auth

# Official Cloud Native Adapter でダウンロードできるパッケージの確認
qvsctl adapter list

NAME                         LATEST  RELEASED DATE
qmonus.net/adapter/official  vx.x.x  YYYY-MM-DD hh:mm:ss

# Official Cloud Native Adapterをダウンロードする
qvsctl adapter get qmonus.net/adapter/official

5. QVS Configの作成 (CLI)

ダウンロードしたOfficial Cloud Native Adapterから、Tekton Pipeline/Task を構成するCI/CD Adapterと、Kubernetesリソースを提供するInfrastructure Adapter(External Secrets Operator AdapterとGKE Cluster Secret Store Adapter)を指定するQVS Configを作成します。

bash
# QVS Configを作成する
vim qvs.yaml
yaml
params:
  - name: version
    type: string
  - name: esoNamespace
    type: string
  - name: appName
    type: string
  - name: smGcpProject
    type: string
  - name: gsaGcpProject
    type: string
  - name: k8sClusterGcpProject
    type: string
  - name: gsaName
    type: string
  - name: ksaNamespace
    type: string
  - name: k8sClusterLocation
    type: string
  - name: k8sClusterName
    type: string

modules:
  - name: qmonus.net/adapter/official
    local: 
      path: .

designPatterns:
  - pattern: qmonus.net/adapter/official/kubernetes/secrets/eso
    params:
      version:      $(params.version)
      k8sNamespace: $(params.esoNamespace)

  - pattern: qmonus.net/adapter/official/kubernetes/secrets/gke/clustersecretstore
    params:
      appName:              $(params.appName)
      smGcpProject:         $(params.smGcpProject)
      gsaGcpProject:        $(params.gsaGcpProject)
      k8sClusterGcpProject: $(params.k8sClusterGcpProject)
      gsaName:              $(params.gsaName)
      ksaNamespace:         $(params.ksaNamespace)
      k8sClusterLocation:   $(params.k8sClusterLocation)
      k8sClusterName:       $(params.k8sClusterName)

  - pattern: qmonus.net/adapter/official/pipeline/deploy:simple

6. Pipeline Manifestの生成および登録 (CLI)

手順5で作成したQVS Configを用いてPipeline Manifestを生成し、Qmonus Value Streamへ登録します。 projectName はQmonus Value StreamのProject Nameです。Project Nameは、qvsctl project list で取得したPROJECT NAMEを指定してください。

bash
# コンパイルする
qvsctl pipeline compile -m . -c qvs.yaml -o pipelines --prefix external-secrets-operator

# Project Nameを取得する
qvsctl project list

# Pipeline Manifestを登録する
qvsctl pipeline apply -p ${projectName} -f pipelines/manifests.yml

7. AssemblyLineの登録(CLI)

作成したPipelineを実行するためのAssemblyLineを作成し、Qmonus Value Streamへ登録します。

bash
# AssemblyLineを作成する
vim pipelines/eso-clustersecretstore-deploy-assemblyline.yaml
yaml
apiVersion: vs.axis-dev.io/v1
kind: AssemblyLine
metadata:
  name: eso-clustersecretstore-deploy
spec:
  params:
    - name: gitRevision
      description: ""
  results: []
  artifacts: []
  stages:
    - name: eso-deploy
      spec:
        deployment:
          app: external-secrets-operator
          name: staging
        params:
          - name: gitRevision
            value: $(inputs.gitRevision)
          - name: providerType
            value: kubernetes-helm
          - name: deployStateName
            value: externalsecretsoperator
        pipeline: external-secrets-operator-deploy
    - name: clustersecretstore-deploy
      spec:
        deployment:
          app: external-secrets-operator
          name: staging
        params:
          - name: gitRevision
            value: $(inputs.gitRevision)
          - name: deployStateName
            value: clustersecretstore
        pipeline: external-secrets-operator-deploy
      runAfter:
        - eso-deploy

info

ここでは、deployStateNameの値をPipeline毎に分けることで、Qmonus Value Streamでのリソース管理の単位を分割しています。基本的にはPipelineのデフォルトの値で定まっているため、今回は例外として、基本的には修正しないことを推奨します。

作成したAssemblyLineを登録します。

bash
# AssemblyLineを登録する
qvsctl pipeline apply -p ${projectName} -f pipelines/eso-clustersecretstore-deploy-assemblyline.yaml

リモートリポジトリに変更を反映します。

bash
git add --all
git commit -m "Qmonus Value Stream Official Introduction for External Secrets Operator"
git push origin qvs_eso_clustersecretstore

8. Deployment Configの登録 (GUI)

CLIで登録したAssemblyLineをGUIから確認し、AssemblyLine詳細画面より、不足しているパラメータを設定します。

  1. 左メニューより、AssemblyLineを選択します。
  2. 画面中央に表示される eso-clustersecretstore-deploy を選択します。
  3. AssemblyLinePipeline Stageseso-deploy パネルを選択します。
  4. AssemblyLine Stage 画面右に表示される Edit Deployment Config リンクを選択します。
  5. YAMLモードのEditorが表示されますので、Infrastructure Adapterに必要なパラメータをYAML形式で入力します。
yaml
version:               ${version}
esoNamespace:          ${esoNamespace}   
appName:               ${appName}
smGcpProject:          ${smGcpProject}
gsaGcpProject:         ${gsaGcpProject}
k8sClusterGcpProject:  ${k8sClusterGcpProject}
gsaName:               ${gsaName}
ksaNamespace:          ${ksaNamespace}
k8sClusterLocation:    ${k8sClusterLocation}
k8sClusterName:        ${k8sClusterName}

ここで、パラメータは以下の通りです。 External Secrets Operator Adapter:

  • ${version}: 手順0-1で指定したExternal Secrets OperatorのCRDのバージョン (0.5.0 など"v"を除いた状態で指定してください)
  • ${esoNamespace}: 手順0-2で指定かつ手順1で作成した${esoNamespace}

GKE Cluster Secret Store Adapter:

  • ${appName}: 手順0-2で指定したKSAおよびCluster Secret Storeのリソース名
  • ${smGcpProject}: 手順0-2で指定したSecret Managerが存在するGCPプロジェクトID
  • ${gsaGcpProject}: 手順0-2で指定したGSAが存在するGCPプロジェクトID
  • ${k8sClusterGcpProject}: GKEが存在するプロジェクトID
  • ${gsaName}: 手順0-2で作成したGSA名
  • ${ksaNamespace}: 手順0-2で指定かつ手順1で作成した${ksaNamespace}
  • ${k8sClusterLocation}: GKEクラスタが存在するリージョン名
  • ${k8sClusterName}: GKEクラスタ名
  1. Saveボタンを押下し、Input Parameters 一覧全てが緑で表示されていることを確認します。

9. AssemblyLineの実行 (GUI)

登録したAssemblyLineを実行し、External Secrets OperatorとCluster Secret StoreをKubernetesにデプロイします。 以下のInput Parameterを入力し、eso-clustersecretstore-deploy AssemblylineのRUN ボタンを押下してCI/CDを開始します。

  • gitRevision: qvs_eso_clustersecretstore

10. デプロイされたアプリケーションの確認(CLI)

Kubernetesにアクセスして、デプロイに成功したExternal Secrets Operator と Cluster Secret Storeを確認します。 ここで、${esoNamespace}${appName}はそれぞれ手順8で指定したExternal Secrets Operatorが使用するnamespaceとCluster Secret Storeのリソース名です。

bash
# kubectl の現在のコンテキストを確認する
kubectl config current-context
(External Secrets Operatorをデプロイしたクラスタ名)

## External Secrets Opeartorのデプロイによって作成されたDeploymentリソースを確認する
kubectl get deployment -n ${esoNamespace}

NAME                                        READY   UP-TO-DATE   AVAILABLE   AGE
external-secrets-operator                   1/1     1            1           
external-secrets-operator-cert-controller   1/1     1            1           
external-secrets-operator-webhook           1/1     1            1           

# clustersecretstoreがデプロイされていることを確認する
kubectl get clustersecretstore

NAME                 AGE
${appName}           

# clustersecretstoreのステータスを確認する
kubectl describe clustersecretstore ${appName}

[...]
Events:
  Type     Reason                 Age                 From                  Message
  ----     ------                 ----                ----                  -------
  Normal   Valid                                      cluster-secret-store  store validated

11. External Secret の動作確認(CLI)

External Secretをデプロイし、GCP Secret Managerの秘密情報がKubernetes Secretリソースへ連携できていることを確認します。

以下の値の通り、動作確認用のSecret Managerのシークレットを作成します。

  • シークレットの名前: qvs-secret-manager
  • シークレットの値: Hello, Qmonus Value Stream!

External Secretリソースをデプロイします。以下のサンプルManifestを使用してください。

bash
vim qvs-sample-externalsecret.yaml
yaml
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: example
spec:
  refreshInterval: "0"
  secretStoreRef:
    name: ${appName}
    kind: ClusterSecretStore
  target:
    name: qvs-kubernetes-secret
    creationPolicy: Owner
  data:
  - secretKey: greeting
    remoteRef:
      key: qvs-secret-manager
      version: "1"
bash
# External SecretをDefault namespaceにApplyする
kubectl apply -f qvs-sample-externalsecret.yaml

# External SecretのStatusを確認する
kubectl get externalsecret.external-secrets.io/example

NAME      STORE                REFRESH INTERVAL   STATUS
example   ${appName}           0                  SecretSynced

# Secretに値が連携できているかを確認する
kubectl get secrets qvs-kubernetes-secret -o json | jq -r .data.greeting | base64 --decode
Hello, Qmonus Value Stream!