2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

AWS-SKILLS-GUILD Advent Calendar 2024

Day 15

EKSとSecret ManagerでRDSの自動パスワードローテーションを試してみた

Last updated at Posted at 2024-12-18

はじめに

セキュリティ強化のため、定期的にデータベースのパスワードをローテーションする必要があります。長期間同じパスワードを使用すると、漏洩や攻撃によるリスクが高まるため、定期的な更新は重要です。しかし、EKS上のPodが参照するシークレットも更新する必要があるため、運用負荷が高くなりがちです。

解決策の1つとして、AWS Secrets ManagerとExternal Secrets Operatorを活用した自動パスワードパスワードローテーションを試してみました。

  • AWS Secrets Manager
    • データベースのパスワードを安全に保管し、定期的なローテーションを自動化することができるだけでなく、RDSのパスワードの更新と保管も実施できる
  • External Secrets Operator(ESO)
    • AWS Secrets Managerに保存されたパスワードをEKS上のKubernetesシークレットに同期する

今回試した内容は公式ドキュメントの手順を元にに、Secrets ManagerによるRDSパスワードローテーションを追加した内容となっています。

構成

eso.drawio.png

AWS Secrets ManagerとExternal Secrets Operatorを組み合わせて、RDSのパスワードローテーションを自動化するようにしています。Secrets Managerが定期的にパスワードをローテーションし、その変更内容がExternal Secrets Operatorを通じてEKSのKubernetesシークレットに反映されます。これにより、Podが新しいパスワードでRDSに接続できるようになり、手動作業を削減できます。
今回はPostgreSQLへ接続するシンプルなアプリを利用しました。ヘルスチェックでDBに接続できるか確認するようになっており、DB接続に一定回数失敗するとpodが再起動し、新しいシークレットでDBに接続するようになります。イメージはdockerhubに格納しています。

前提

  • RDS PostgreSQL環境があること
  • RDSへ接続できるEKS環境があること
  • aws,kubectl,eksctl,helmコマンドが利用できること

流れ

  1. Secrets Manager接続用のポリシーとサービスアカウント作成
  2. ESOをデプロイ
  3. SecretStoreを作成
  4. ExternalSecretを作成
  5. アプリのデプロイ
  6. パスワードローテーションをSecert Manager から実施
  7. アプリが再起動することを確認

1.Secret Manager用のポリシーを作成

1.1 IAM OIDCプロバイダー作成

(作成済みの場合は実行不要)

eksctl utils associate-iam-oidc-provider --cluster=[クラスター名] --approve

1.2 secretmanagerへのアクセス許可を付与するiamポリシーを作成する

コンソールからSecret Managerのシークレットarnとシークレット名を確認します。

Secret Managerの確認.png

secretmanager-access-policy.jsonのSecret Managerのシークレットarnをを書き換えます。

secretmanager-access-policy.json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "secretsmanager:GetSecretValue",
                "secretsmanager:DescribeSecret"
            ],
            "Resource": "arn:aws:secretsmanager:ap-northeast-1:[AWSアカウントID]:secret:[認証情報が保管されているSecret-managerのシークレットARN]"
        }
    ]
}

以下のコマンドを実行し、Secret Managerへのアクセスを許可するポリシーを作成する(あるいはコンソールから作成することもできます。)

aws iam create-policy --policy-name SecretsManagerRDSPostgreReadWrite --policy-document file://secretmanager-access-policy.json

1.3 Secret ManagerへアクセスするIAMロールとサービスアカウントを作成する

アカウントIDとクラスター名を書き換えてから実行します。

eksctl create iamserviceaccount \
    --name secretmanager-service-account \
    --namespace default \
    --cluster クラスター名 \
    --role-name "secretmanager-service-account" \
    --attach-policy-arn arn:aws:iam::[AWS アカウントID]:policy/SecretsManagerRDSPostgreReadWrite \
    --approve \
    --override-existing-serviceaccounts

正しく作成されているか確認します。

kubectl get sa
kubectl describe sa secretmanager-service-account

2.ESO をデプロイ

今回はHelmを使用してExternal Secrets OperatorをEKS環境にデプロイします。

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

3.SecretStoreを作成

SecretStoreは、ESOがシークレットデータを取得するためのプロバイダ設定を定義するリソースです。今回は、AWS Secrets Managerをプロバイダとして使用し、新しく作成したサービスアカウントを利用して認証を行います。

secretStore.yaml
apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
  name: secretmanager-secret-store
spec:
  provider:
    aws:
      service: SecretsManager
      region: ap-northeast-1
      auth:
        jwt:
          serviceAccountRef:
            name: secretmanager-service-account

作成

kubectl apply -f secretstore.yaml

動作確認

kubectl get secretstore
kubectl describe secretstore secretmanager-secret-store 

※権限が不足している場合やシークレットARNの指定が正しくない場合にエラーが発生するので、エラー内容を確認して修正してください。

4.ExternalSecretを作成

ExternalSecretは、AWS Secrets Managerから取得したシークレットをEKSのKubernetesシークレットとして同期するための設定を行うリソースです。refreshIntervalはシークレットの同期間隔を指定し、dataでは取得するプロパティ(例: username や password)を定義します。
Secret-managerのシークレットARNを書き換えてから実行してください。

externalSecret.yaml
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: secretmanager-external-secret
spec:
  refreshInterval: 30s                 # 更新間隔
  secretStoreRef:
    name: secretmanager-secret-store
    kind: SecretStore
  target:
    name: secret-manager-value
    creationPolicy: Owner
  data:
  - secretKey: postgre-mydb-username   # シークレットに登録する名称
    remoteRef:
      key: rds!db-xxx                  # [認証情報が保管されているSecret-managerのシークレットARN]
      property: username               # [Secret-managerに保管されているプロパティ名]
  - secretKey: postgre-mydb-password   
    remoteRef:
      key: rds!db-xxx                  
      property: password               

作成

kubectl apply -f externalsecret.yaml

動作確認

  kubectl get externalsecrets
  kubectl describe externalsecrets secretmanager-external-secret

secret manager から取得したユーザ名, パスワードがシークレットに登録されていることを確認します。

kubectl get secrets secret-manager-value

デコードしてSecret-managerとシークレットの値が一致することを確認します。

kubectl get secret secret-manager-value -o jsonpath='{.data.postgre-mydb-username}' | base64 -d
kubectl get secret secret-manager-value -o jsonpath='{.data.postgre-mydb-password}' | base64 -d

5.アプリのデプロイ

PostgreSQLに接続するサンプルアプリをデプロイします。デプロイしたアプリは、Kubernetesシークレットに保存されたRDSの接続情報を環境変数として読み込みます。deploymentのヘルスチェックの設定で、定期的にデータベースに接続できるかを確認します。RDSのパスワードが更新されると、ヘルスチェックが失敗してPodが再起動し、更新されたシークレットを更新される仕組みです。

deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: flask-app
  labels:
    app: flask-app
spec:
  replicas: 1
  selector:
    matchLabels:
      app: flask-app
  template:
    metadata:
      labels:
        app: flask-app
    spec:
      containers:
      - name: flask-app
        image: jeff0525/postgre-app:v1.0                 # postgre-appイメージのパス
        ports:
        - containerPort: 5000
        env:
        - name: DB_HOST
          value: xxxx.ap-northeast-1.rds.amazonaws.com  # DBエンドポイント
        - name: DB_NAME
          value: xxx                                    # データベース名
        - name: DB_USER
          valueFrom:
            secretKeyRef:
              name: secret-manager-value
              key: postgre-mydb-username
        - name: DB_PASSWORD
          valueFrom:
            secretKeyRef:
              name: secret-manager-value
              key: postgre-mydb-password
        livenessProbe:
          httpGet:
            path: /health
            port: 5000
          initialDelaySeconds: 10
          periodSeconds: 10

デプロイ

kubectl apply -f deployment.yaml

podの起動確認

kubectl get pods

pod起動後にシークレットを確認

kubectl exec [flask-app-ポッド名] -- printenv |grep DB

healthチェックが成功していることを確認

kubectl logs [flask-app-ポッド名] -f

ヘルスチェック1.png

6. DBのパスワードローテーションを実施

Secrets Managerでパスワードをローテーションすると、External Secrets Operatorがその変更を検知し、Kubernetesシークレットを自動的に更新します。その後、Podが再起動することで、新しいパスワードが環境変数に反映されます。この一連の動作により、手動操作を必要とせずにパスワードローテーションが完結します。

Secret Managerからローテーションタブを選択し、"すぐにシークレットローテーションさせる"をクリックで実行できます。

シークレットローテーション実施.png

Secret Manager上のシークレット値が更新されていることを確認します。
シークレットローテーション後のパスワード.png

7. アプリが再起動することを確認

パスワードローテーションを実行すると、podのヘルスチェックでエラーが発生するようになります。しばらくすると、自動でpodが再起動し、db接続できるようになります。

ヘルスチェック-2.png

シークレットが更新されていることを確認します。

kubectl get secret secret-manager-value -o jsonpath='{.data.postgre-mydb-username}' | base64 -d
kubectl get secret secret-manager-value -o jsonpath='{.data.postgre-mydb-password}' | base64 -d

おわりに

AWS Secrets ManagerとExternal Secrets Operatorを組み合わせることで、EKS環境におけるデータベースのパスワードローテーションを自動化することができました。これにより、運用負荷が軽減されるだけでなく、手動操作によるミスのリスクも低減することができます。また、パスワードローテーションをより頻繁に実施できるようになり、セキュリティが向上するメリットもあります。

Appendix

作成したリソースの削除

kubectl delete -f deployment.yaml
kubectl delete -f externalsecret.yaml
kubectl delete -f secretstore.yaml
eksctl delete iamserviceaccount \
    --name secretmanager-service-account \
    --namespace default \
    --cluster [クラスター名]
helm uninstall external-secrets -n external-secrets

参考資料

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?