シークレットの管理は、セキュアかつ動的なコンテナ化されたアプリケーションを大規模に実行する上で、困難ではあるものの重要な側面を持ちます。稼働するアプリケーションにシークレットをセキュアに配布するニーズをサポートするために、Kubernetes は Kubernetes Secret という形でシークレットを管理するネイティブな機能を提供します。しかし、多くのお客様はセキュリティ、管理、シークレット使用時の監査のために AWS Secrets Manager といった外部のシークレットストアを用いて、Kubernetes クラスターの外部でシークレットの管理を一元化することを選択します。
外部のシークレットストアからシークレットを利用する場合、アプリケーション実行時にシークレットを受け取る API ベースの呼び出しをサポートするために、多くの場合はアプリケーションコードの変更が必要となります。Amazon EKS 上でアプリケーションが動作している場合、Kubernetes の柔軟性を利用してアプリケーションのコードを変更をせず Pod に直接シークレットを公開できます。これを達成する方法の一つの例として Kubernetes Secrets Store CSI Driver に AWS Secrets and Configuration Provider (ASCP) を使うことが挙げられます。ASCP は Secrets Store CSI Driver を使って AWS Secrets Manager から Pod に対してマウントされたストレージボリュームとしてシークレットを公開します。
本記事では、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 クラスターに対して、定義されたシークレットを同期するようになります。シークレットは定期的に自動同期され、認証情報のローテーションなどの変更や更新を取り込みます。
このウォークスルーは、External Secrets Operator を使用してシークレットを AWS Secrets Manager から EKS Fargate クラスターに同期し、Fargate で動作するアプリケーションの Pod でシークレットを使用する方法を解説します。
External Secrets はデフォルトでは “external-secrets” Namespace にリソースをデプロイします。External Secrets の Pod が Fargate 上で動作するように、Fargate プロファイルのセレクターで、この Namespace を指定することにします。
eksctl がインストールされたターミナルから、以下のコマンドを実行し Fargate プロファイルを作成します。“CLUSTERNAME” はご自身の EKS クラスター名に置き換えてください。
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 コマンドを実行します。
指定した 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 が作成されたことを確認できます。
有効なステータスの 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 として動作しているリソースをクラスターから削除してください。元のリソースのデプロイに使用したマシンから、以下のコマンドを実行してください。
また、このウォークスルーのために特別に作成した IAM や Secrets Manager のリソースも削除します。
まとめ
このブログ記事では、EKS Fargate を使用しているお客様が External Secrets Operator を使用して AWS Secrets Manager に保存されたシークレットを、アプリケーションのコードを変更せずに使用する方法を紹介しました。これと同じソリューションは、EC2 や EC2/Fargate のハイブリッドクラスターにも適用できます。
External Secrets Operator のより詳しい情報は、External Secrets Operator のドキュメント、または External Secrets GitHub を参照してください。
翻訳はプロフェッショナルサービスの後藤が担当しました。原文はこちらです。