Amazon Web Services ブログ

External Secrets Operator を用いて EKS Fargate からシークレットストアを利用する

この記事は Leverage AWS secrets stores from EKS Fargate with External Secrets Operator (記事公開日: 2022 年 6 月 30 日) を翻訳したものです。

シークレットの管理は、セキュアかつ動的なコンテナ化されたアプリケーションを大規模に実行する上で、困難ではあるものの重要な側面を持ちます。稼働するアプリケーションにシークレットをセキュアに配布するニーズをサポートするために、Kubernetes は Kubernetes Secret という形でシークレットを管理するネイティブな機能を提供します。しかし、多くのお客様はセキュリティ、管理、シークレット使用時の監査のために AWS Secrets Manager といった外部のシークレットストアを用いて、Kubernetes クラスターの外部でシークレットの管理を一元化することを選択します。

外部のシークレットストアからシークレットを利用する場合、アプリケーション実行時にシークレットを受け取る API ベースの呼び出しをサポートするために、多くの場合はアプリケーションコードの変更が必要となります。Amazon EKS 上でアプリケーションが動作している場合、Kubernetes の柔軟性を利用してアプリケーションのコードを変更をせず Pod に直接シークレットを公開できます。これを達成する方法の一つの例として Kubernetes Secrets Store CSI DriverAWS Secrets and Configuration Provider (ASCP) を使うことが挙げられます。ASCP は Secrets Store CSI Driver を使って AWS Secrets Manager から Pod に対してマウントされたストレージボリュームとしてシークレットを公開します。

アプリケーションを動かすために Amazon EKS を利用しているお客様はしばしばコンピュートレイヤーに AWS Fargate を利用し、コンテナ化されたワークロード運用の管理の複雑性を軽減します。Secrets Store CSI を持つ ASCP は DaemonSet としてデプロイされます。現在のところ Fargate では DaemonSet はサポートされていないため、AWS Fargate ノードを持つ EKS クラスターを持つお客様は、外部のシークレットを使用するために別の方法が必要になります。

ひとつの選択肢はオープンソースの External Secrets Operator (External Secrets) プロジェクトです。External Secrets は Secrets Store CSI Driver とは異なる方法でシークレットを管理します。ボリュームとしてシークレットをマウントする代わりに、External Secrets は外部のシークレットストアからシークレットを読み取り、Kubernetes コントロールプレーンによってネイティブな Kubernetes Secret の値として自動的に保存します。External Secrets は Deployment としてクラスターにインストールされ、Fargate のみのクラスターでも Amazon EC2 ベースのノードを持つクラスターと同じように機能します。

本記事では、EKS Fargate クラスターで AWS Secrets Manager に保存されたシークレットを使用するために External Secrets Operator を使う方法を解説します。同じ方法を AWS Systems Manager Parameter Store に保存されたシークレットにも適用できますが、本記事では扱いません。

ソリューション概要

External Secrets は通常の Kubernetes の Deployment として EKS クラスターにデプロイされ、Fargate をコンテナコンピューティングプロバイダーとして使用できるようにします。一度設定すると、External Secrets は AWS Secrets Manager から EKS クラスターに対して、定義されたシークレットを同期するようになります。シークレットは定期的に自動同期され、認証情報のローテーションなどの変更や更新を取り込みます。

以下の図は同期処理の概要を示したものです。

  1. External Secrets は定期的に AWS Secrets Manager の API をコールし、指定されたシークレットの値をコピーします。
  2. External Secrets はコピーされたシークレットを取得し、外部のシークレットの値に基づいて Kubernetes Secret を作成します。
  3. Kubernetes Secret は EKS 上で動作するアプリケーションで利用可能になります。Kubernetes RBAC はアプリケーションがシークレットを利用できることを定義します。
  4. Pod は、Pod Spec で定義された環境変数あるいはボリュームマウントからシークレットを利用します。

figure-01
Figure 1. ソリューション概要

ウォークスルー

このウォークスルーは、External Secrets Operator を使用してシークレットを AWS Secrets Manager から EKS Fargate クラスターに同期し、Fargate で動作するアプリケーションの Pod でシークレットを使用する方法を解説します。

以下で概要を説明します。

  • EKS Fargate クラスター上に External Secrets Operator をデプロイする
  • External Secrets Operator リソースを構成する
  • アプリケーションの Pod 内で、同期された Kubernetes Secret を使用する

前提

このウォークスルーでは、以下の前提条件を満たしている必要があります。

Step 1. EKS Fargate プロファイルを作成する

Amazon EKS では、管理者は Fargate プロファイル によってクラスター内のどの Pod を Fargate 上で実行するかを指定することができます。プロファイルセレクターは Namespace とオプションで Label を定義し、それらは Pod が Fargate にスケジュールされるかどうかを決定するために評価されます。Fargate プロファイルは多くの CLI と Infrastructure as Code (IaC) によって作成できます。以下では eksctl を使った方法を紹介します。AWS コンソールを使って Fargate プロファイルを作成したい場合は、Amazon EKS ユーザーガイドの「Fargate プロファイルの作成」を参照してください。

External Secrets はデフォルトでは “external-secrets” Namespace にリソースをデプロイします。External Secrets の Pod が Fargate 上で動作するように、Fargate プロファイルのセレクターで、この Namespace を指定することにします。

eksctl がインストールされたターミナルから、以下のコマンドを実行し Fargate プロファイルを作成します。“CLUSTERNAME” はご自身の EKS クラスター名に置き換えてください。

eksctl create fargateprofile \
    --cluster <CLUSTERNAME> \
    --name externalsecrets \
    --namespace external-secrets

プロファイルの作成が完了したら、確認のために以下のコマンドを実行してください

eksctl get fargateprofile --cluster <CLUSTERNAME> -o yaml

“externalsecrets” プロファイルがリストアップされた、以下のような出力が期待されます。

- name: externalsecrets
  podExecutionRoleARN: arn:aws:iam::<ACCOUNTNUM>:role/eksctl-blogdemo-cluster-FargatePodExecutionRole-1GY5JZHKR1993
  selectors:
  - namespace: external-secrets
  status: ACTIVE
  subnets:
  - subnet-072cad201ce783be1
  - subnet-086d0835e05be3d89

Step 2. External Secrets Operator をデプロイする

External Secrets Operator はデプロイしやすいように Helm チャートを提供しています。Helm チャートはプロジェクトの GitHub リポジトリで入手できます。以下のコマンドはデフォルトの設定でインストールを実行します。概要は External Secrets Getting Started guide を参照してください。

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 \
   --set webhook.port=9443 

デプロイが完了したら、確認のために以下のコマンドを実行してください。

kubectl get pods -n external-secrets

External Secrets の Pod が実行されている場合、期待される出力は以下のようになります。

NAMESPACE          NAME                                                READY   STATUS    RESTARTS   AGE
external-secrets   external-secrets-8cdbf85cd-k4hvs                    1/1     Running   0          87s
external-secrets   external-secrets-cert-controller-655b7b7d45-bxh24   1/1     Running   0          87s
external-secrets   external-secrets-webhook-75db54d748-85l8p           1/1     Running   0          87s

Step 3. IAM roles for service accounts をセットアップする

IAM Roles for Service Accounts (IRSA) は、AWS IAM ロールKubernetes Service Account をマッピングする EKS の機能です。この機能は、静的な認証情報を直接管理することなく EKS 上で動作するアプリケーションの AWS 認証情報を管理するための戦略を提供します。IRSA の詳細については、AWS のブログ記事「Diving into IAM Roles for Service Accounts (日本語翻訳:詳解: IAM Roles for Service Accounts) 」を参照してください。

今回の External Secrets を使用するケースでは、IRSA を用いて External Secrets の Pod に AWS 認証情報を提供し、AWS Secrets Manager のシークレットへのきめ細やかなアクセスコントロールをおこないます。

IRSA を有効化する最初のステップは、もしまだ作成されていない場合はクラスターの IAM OIDC プロバイダーを作成することです。Amazon EKS ユーザーガイドの「クラスターの IAM OIDC プロバイダーを作成するには」で説明されている手順に従って、クラスターの IAM OIDC プロバイダーを作成するか、以下の eksctl コマンドを使用することができます。OIDC プロバイダーが EKS クラスターに関連付けられたら、External Secrets で使用する IRSA Service Account を作成することができます。

eksctl がインストールされたターミナル上で、以下のコマンドを実行してください。

eksctl utils associate-iam-oidc-provider --cluster=<CLUSTERNAME> --approve

eksctl がインストールされたターミナル上で、以下のコマンドを実行してください。IAMPOLICYARN は、AWS Secrets Manager のシークレットへのアクセス権限を持つ IAM ポリシーAmazon Resource Name (ARN) に置き換えます。

eksctl create iamserviceaccount \
    --name blogdemosa \
    --namespace default \
    --cluster <CLUSTERNAME> \
    --role-name "blogdemosa" \
    --attach-policy-arn <IAMPOLICYARN> \
    --approve \
    --override-existing-serviceaccounts

IRSA Service Account に関連付けられた IAM ポリシーは、AWS Secrets Manager に保存されたシークレットへのきめ細かいアクセスを提供するために使用されます。IAM ポリシーの範囲を制限することはセキュリティのベストプラクティスを遵守するために重要です。以下は、このブログ記事で使用する IAM ポリシーの例です。このサンプルポリシーは、アクションを指定された単一のシークレットの登録及び取得に制限します。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "secretsmanager:GetSecretValue",
                "secretsmanager:DescribeSecret"
            ],
            "Resource": "arn:aws:secretsmanager:<REGION>:<ACCOUNTNUM>:secret:prod/blogdemo/mysql1-Ft6k18"
        }
    ]
}

Kubernetes Service Account の作成が成功したことを確認するために、以下の kubectl コマンドを実行します。

kubectl get sa

指定した Service Account 名が表示されるはずです。

NAME         SECRETS   AGE
blogdemosa   1         2m49s
default      1         18h

さらに以下の kubectl コマンドを実行することで、Service Account を精査できます。

kubectl describe sa blogdemosa

Service Account の詳細が出力されます。eksctl コマンドを使うことで、作成された IAM ロールを参照している EKS のアノテーション (Annotations) が確認できるはずです。

Name:                blogdemosa
Namespace:           default
Labels:              app.kubernetes.io/managed-by=eksctl
Annotations:         eks.amazonaws.com/role-arn: arn:aws:iam::<ACCOUNTNUM>:role/blogdemosa
Image pull secrets:  <none>
Mountable secrets:   blogdemosa-token-8d46d
Tokens:              blogdemosa-token-8d46d
Events:              <none>

IRSA Service Account が作成されたので、External Secrets のリソースを設定することができます。

Step 4. External Secrets を設定し、AWS Secrets Manager のシークレットを同期する

External Secrets は、外部のシークレットの同期に必要なオペレーターの機能を設定するためのカスタムリソース (CRD) を提供します。

SecretStore は、外部のシークレットストアと、シークレットストアにアクセスするための認証の仕組みを定義するために使われます。ExternalSecret は SecretStore リソースで定義されたシークレットストアからどのようなデータを取得するかを定義します。詳細については External Secret リソースモデルのドキュメントを参照してください。

この例では、既存の AWS Secrets Manager を参照する SecretStore オブジェクトを作成します。先ほど作成した IRSA ベースの Service Account を指定して、シークレットストアにアクセスするために使用する AWS クレデンシャルを定義します。

以下のオブジェクトを定義する YAML を使用して、kubectl でリソースを作成してください。

apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
  name: blogdemo
spec:
  provider:
    aws:
      service: SecretsManager
      region: <REGION>
      auth:
        jwt:
          serviceAccountRef:
            name: blogdemosa

kubectl がインストールされ YAML ファイルが作成されたターミナル上で、以下のコマンドを実行してください。

kubectl apply -f secretstore.yaml

以下の kubectl コマンドを実行することで、SecretStore が作成されたことを確認できます。

kubectl get secretstore

有効なステータスの SecretStore が作成されたことが確認できるはずです。

NAME       AGE   STATUS
blogdemo   82s   Valid

ExternalSecret リソースを作成し、アクセスしたいシークレットを指定し、先ほど作成した SecretStore オブジェクトを参照します。ハイライトされた箇所には、既存の AWS Secrets Manager のシークレット名とキーを指定します。AWS Secrets Manager でシークレットを作成する方法の詳細については、AWS Secrets Manager ユーザーガイドの「AWS Secrets Manager でのシークレットの作成と管理」を参照してください。その他の External Secrets 設定オプションについては、External Secrets ドキュメントの AWS Secrets Manager provider を参照してください。

kubectl を使ってリソースを作成するために、以下のオブジェクトを定義する YAML を使用してください。ハイライトされた箇所を特定の Secrets Manager の値で置き換えてください。

apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: blogdemo
spec:
  refreshInterval: 1h
  secretStoreRef:
    name: blogdemo
    kind: SecretStore
  target:
    name: blogdemosecret
    creationPolicy: Owner
  data:
  - secretKey: blogdemo-mysql-username
    remoteRef:
      key: prod/blogdemo/mysql1 #AWS Secrets Manager secret name
      property: username #AWS Secrets Manager secret key
  - secretKey: blogdemo-mysql-password
    remoteRef:
      key: prod/blogdemo/mysql1 #AWS Secrets Manager secret name
      property: password #AWS Secrets Manager secret key

以下はこの記事で使用したシークレットの例で、Secrets Manager に保存されているものです。ユーザー名とパスワードだけが、先ほどの ExternalSecret の定義の中で参照されています。


Figure 2. AWS Secrets Manager のシークレットの例

kubectl がインストールされ YAML ファイルが作成されたターミナル上で、以下のコマンドを実行します。

kubectl apply -f externalsecret.yaml

ExternalSecret リソースの作成が完了したら、新しく作成された Kubernetes Secret を参照することができるようになります。これは Secrets Manager から同期されたものです。以下の kubectl コマンドを実行します。

kubectl describe secret blogdemosecret

出力には以下のようにシークレットが表示されます。

Name:         blogdemosecret
Namespace:    default
Labels:       <none>
Annotations:  reconcile.external-secrets.io/data-hash: a12b24f34338b71e8d118c6a04107b0e

Type:  Opaque

Data
====
blogdemo-mysql-password:  8 bytes
blogdemo-mysql-username:  8 bytes

アプリケーションで使用するために Pod Spec で指定できる、同期された Kubernetes Secret を利用できるようになりました。

Kubernetes Secret で考慮すべき点は、デフォルトでは Base 64 でエンコードされ暗号化されずに etcd に保存されることです。EKS では、AWS Key Management Service (AWS KMS) を EKS で保存される Kubernetes Secret のエンベロープ暗号化に活用することができます。この機能は、AWSが管理する暗号化キーを使用してディスクレベルで暗号化された etcd ボリュームを運用することと合わせて、etcd に保存されている Kubernetes Secret を保護するための多層防御戦略を提供します。EKS における Kubernetes Secret のエンベロープ暗号化の詳細については、こちらの AWS ブログ記事 (日本語訳:高度な防御のために EKS 暗号化プロバイダーのサポートを利用する) を参照してください。

Step 5. Pod でシークレットを使用する

External Secrets が AWS Secrets Manager のシークレットを Kubernetes Secret に同期したので、このシークレットを Pod Spec 内で参照することで使用することができます。シークレットを使用する利用可能なオプションの詳細については、Kubernetes ドキュメントの Using a Secret を参照してください。

シークレットを環境変数で使う例として、以下の Pod Spec を使用します。

apiVersion: v1
kind: Pod
metadata:
  name: busybox
  namespace: default
spec:
  containers:
  - image: busybox
    command:
      - sleep
      - "3600"
    imagePullPolicy: IfNotPresent
    name: busybox
    env:
      - name: BLOG_SECRET_USERNAME
        valueFrom:
          secretKeyRef:
            name: blogdemosecret
            key: blogdemo-mysql-username

kubectl がインストールされ YAML ファイルが作成されたターミナル上で、以下のコマンドを実行します。

kubectl apply -f podexample.yaml

Pod がデプロイされたら、以下のコマンドを使って動作しているコンテナのシェルに直接アクセスします。

kubectl exec --stdin --tty busybox -- /bin/sh

そこからシークレットを参照するために環境変数を確認することができます。


/ # echo $BLOG_SECRET_USERNAME
blogdemo
/ # echo $BLOG_SECRET_PASSWORD
Test1234

環境変数としてシークレットを公開することで、必要に応じてアプリケーションでこれらのシークレットを使用することができます。

クリーンアップ

今後の課金を避けるために、作成されたリソース、特に Fargate の Pod として動作しているリソースをクラスターから削除してください。元のリソースのデプロイに使用したマシンから、以下のコマンドを実行してください。

kubectl delete -f podexample.yaml
kubectl delete -f externalsecret.yaml
kubectl delete -f secretstore.yaml
eksctl delete iamserviceaccount \
    --name blogdemosa \
    --namespace default \
    --cluster <CLUSTERNAME>
helm uninstall external-secrets -n external-secrets
eksctl delete fargateprofile --cluster <CLUSTERNAME> --name externalsecrets

また、このウォークスルーのために特別に作成した IAM や Secrets Manager のリソースも削除します。

まとめ

このブログ記事では、EKS Fargate を使用しているお客様が External Secrets Operator を使用して AWS Secrets Manager に保存されたシークレットを、アプリケーションのコードを変更せずに使用する方法を紹介しました。これと同じソリューションは、EC2 や EC2/Fargate のハイブリッドクラスターにも適用できます。

External Secrets Operator のより詳しい情報は、External Secrets Operator のドキュメント、または External Secrets GitHub を参照してください。

翻訳はプロフェッショナルサービスの後藤が担当しました。原文はこちらです。