ブログ

AWS Secrets Manager で実現する RDS パスワードの自動ローテーション

この記事をSNSでシェア!

はじめに

AWS 環境において RDS(PostgreSQL)の認証情報を AWS Secrets Manager で管理している場合でも、固定パスワードのままでは十分なセキュリティ対策とは言えません。セキュリティベストプラクティスでは、認証情報の定期的なローテーションが推奨されています。

本記事では、セキュリティ監視ツールからのアラートをきっかけに、既存システムのリファクタリングとして Secrets Manager の自動ローテーション機能を実装し、セキュリティ要件を満たした事例をご紹介します。

本記事のポイント

  • VPC プライベートサブネット内の RDS に対するパスワード自動ローテーションの実現方法
  • カスタム Lambda 関数を用いたローテーション処理の実装
  • RDS Proxy を使用したダウンタイムゼロでのローテーション

背景・課題

既存システムでは、RDS(PostgreSQL)のログイン情報を Secrets Manager のシークレットに保存していました。しかし、以下のような問題がありました。

既存実装の問題点

  1. 固定パスワードの使用:シークレットには固定のログインユーザ・パスワードを格納
  2. ハードコードされた環境変数:Lambda 関数などでアクセスする際、環境変数にパスワードを直接指定
  3. ローテーション未実施:パスワードの定期更新が行われていない状態

上記の問題点より、セキュリティ監視ツールから「パスワードローテーションが実施されていない」という内容のアラートが発生していました。セキュリティベストプラクティスに照らし合わせると、「パスワード漏洩時の影響範囲が長期化」、「セキュリティ監査での指摘事項」、「内部統制上の課題」といったリスクになっていました。

既存システムの構成と問題点(固定パスワード、環境変数ハードコード)

・参考:Amazon RDSのセキュリティに関するベストプラクティス

システム構成の制約

  • RDS は VPC のプライベートサブネットに配置
  • 接続プーリング、高可用性確保のために RDS Proxy 経由でのみアクセス可能
  • Lambda 関数も VPC 内に配置され、全 API が DB Proxy 経由で RDS に接続
VPC プライベートサブネット内の RDS 構成図

このような構成において、セキュリティベストプラクティスに沿ったパスワードの自動ローテーションが課題でした。この課題に対するアプローチとして、既存システムへの影響とセキュリティ要件のバランスを考慮し、AWS Secrets Manager の自動ローテーション機能を採用しました。次のセクションでは、この機能について詳しく解説します。

Secrets Manager の自動ローテーション機能

Secrets Manager の自動ローテーション機能は、保存したシークレット(認証情報)を指定した周期で自動的に更新できる AWS の標準機能です。認証情報の定期的なローテーションは、パスワード漏洩リスクの低減やセキュリティ監査対応の観点から、AWS のベストプラクティスとして強く推奨されています。

ローテーション方式の種類

Secrets Manager のローテーション機能には、主に2つの方式が存在します。

  1. マネージドローテーション:AWS が自動で設定・管理(Lambda 関数の実装不要)
  2. 独自の Lambda 関数によるローテーション:カスタム Lambda 関数でシークレットとデータベースのログイン情報を更新

今回は VPC プライベートサブネット内の RDS への接続が必要という理由から、カスタム Lambda 関数を用いたローテーションを採用しました。(理由は実装内容に後述)

ローテーション戦略の選択

Secrets Manager では 代表的な 2 つのローテーション戦略があります。

1. シングルユーザー戦略(今回採用)

シングルユーザー戦略は、1つのユーザーアカウントでローテーションを行うシンプルな構成です。

  • 1 つのシークレット内の 1 ユーザーの認証情報を更新
  • 最もシンプルで、多くのユースケースに適している
  • RDS Proxy 使用時は接続が維持されるため、ダウンタイムが発生しない
シングルユーザー戦略のローテーション

2. 代替ユーザー戦略

代替ユーザー戦略では、2つのユーザーアカウントを交互に切り替えることで、ローテーション中も常に有効な認証情報が存在し、より高い可用性を実現できます。

  • 1 つのシークレット内の 2 ユーザーの認証情報を更新
  • ローテーション時に常に有効な認証情報が存在
  • 高可用性が必須のアプリケーションに適している
代替ユーザー戦略のローテーション

今回は RDS Proxy 経由でのみ接続するシステム構成であり、シングルユーザー戦略で十分な可用性が確保できると判断しました。RDS Proxy は、認証情報の切り替え時も既存の接続を維持できるため、ローテーションによるダウンタイムが発生しません。一方で、RDS Proxy を利用していない場合や、システム要件として一切のダウンタイムが許容されない場合は、代替ユーザー戦略の導入が有効です。

実装内容

Secrets Manager の自動ローテーションを実現する方法として、まずは AWS が提供するマネージドローテーション機能の利用を検討しました。これは、AWS が自動生成する Lambda 関数によって、シークレットのローテーション処理を自動化できる便利な仕組みです。しかし、以下の理由で断念しました。

  • 自動生成される Lambda 関数の IAM ロールに対して、VPC プライベートサブネット内の RDS へのパスワード更新権限を CDK で指定できない
  • マネージド Lambda 関数のカスタマイズに制約がある

そのため、カスタム Lambda 関数によるローテーション処理の実装手法に切り替えました。

1. カスタム Lambda 関数による実装

カスタム Lambda 関数を用いることで、VPC 内のリソースへのアクセス権限やネットワーク設定を細かく制御できるほか、ローテーション処理の各ステップ(パスワード生成、DB更新、接続テスト、ラベル更新)に独自のエラーハンドリングや監視・通知機能を組み込むことも可能になります。

Lambda 関数の 4 つのステップ

AWS のベストプラクティスに従い、以下の 4 つのステップを実装しました。Secrets Manager は4つのステップを用いてシークレットのローテーションを行います。

ステップ1. createSecret(新しいシークレットの作成)

Python
# 新しいパスワードを生成し、AWSPENDING ステージングラベルとして保存
new_password = secrets_client.get_random_password(
    PasswordLength=32,
    ExcludeCharacters='/@"\'\\'
)
secrets_client.put_secret_value(
    SecretId=secret_arn,
    ClientRequestToken=token,
    SecretString=json.dumps(new_secret),
    VersionStages=['AWSPENDING']
)

ステップ2. setSecret(データベースの認証情報を更新)

Python
# AWSPENDING バージョンの認証情報を使用して RDS のパスワードを変更
# セキュリティ対策として、クエリパラメータ化を使用
cursor.execute(
  "ALTER USER %s WITH PASSWORD %s",
  (username, new_password)
)

ステップ3. testSecret(新しいシークレットのテスト)

Python
# AWSPENDING バージョンで実際にデータベース接続をテスト
test_connection = psycopg2.connect(
  host=db_host,
  port=db_port,
  user=username,
  password=new_password,
  database=db_name
)

ステップ4. finishSecret(ローテーションの完了)

Python
# AWSCURRENT ラベルを新バージョンに移動
# 前バージョンに AWSPREVIOUS ラベルが自動付与される
secrets_client.update_secret_version_stage(
  SecretId=secret_arn,
  VersionStage='AWSCURRENT',
  MoveToVersionId=token,
  RemoveFromVersionId=current_version
)
Lambda 関数による自動ローテーションの 4 ステップ処理フロー図

2. IAM 権限の設定

実装した ローテーション Lambda 関数に必要な権限を付与しました。
以下は今回付与した権限の例です。

  • VPC 内の Lambda 実行に必要な基本ポリシー
  • RDS への接続権限(VPC エンドポイント経由)
  • Secrets Manager へのアクセス権限(`GetSecretValue`、`PutSecretValue` 等)
  • DB Proxy 経由の接続に必要な権限

3. ローテーション設定

Secrets Manager からLambda を呼び出し、更新する周期や戦略を指定します。今回は以下のように設定を行いました。

  • ローテーション周期:90日ごとに更新(組織のセキュリティポリシーに合わせて設定可能)
  • ローテーション戦略:シングルユーザー

アプリケーション側の修正

Lambda 関数などのアプリケーションコード側にも修正を行いました。環境変数に登録された値を取得するのではなく、Secrets Manager から最新のログイン情報を取得するように変更しました。これにより自動ローテーションが完了すると同時に更新後のログイン情報を使用することが可能となります。

Python
# 修正前:環境変数から取得
db_password = os.environ['DB_PASSWORD']

# 修正後:Secrets Manager から取得
secret = secrets_client.get_secret_value(SecretId=secret_arn)
credentials = json.loads(secret['SecretString'])
db_password = credentials['password']
Secrets Manager 自動ローテーション導入後の改善されたシステム構成図

改善されたポイント

  1. 自動ローテーション(緑):Secrets Manager が定期的に自動でローテーション用 Lambda 関数を起動
  2. 動的な認証情報取得(青):アプリケーション Lambda は起動時に Secrets Manager から最新の認証情報を取得
  3. ダウンタイムゼロ(青):RDS Proxy 経由の接続により、ローテーション中もアプリケーションは継続稼働
  4. 4 ステップ自動処理(オレンジ):ローテーション用 Lambda が新パスワード生成 →RDS 更新 → テスト → ラベル更新を自動実行

実装後の効果と考察

セキュリティの向上

  • セキュリティアラートの解消:パスワードローテーション未実施のアラートが解消
  • 自動パスワード更新:手動でのパスワード変更作業が不要に
  • 定期的なセキュリティ強化:設定した周期で自動的にパスワードが更新される
  • セキュリティベストプラクティスへの準拠:認証情報の定期ローテーションというセキュリティ要件を満たす

運用負荷の変化

  • メンテナンス作業の削減:定期的なパスワード変更作業が自動化
  • アラート対応の削減:セキュリティ監視ツールからのアラート対応が不要に
  • 初回構築コスト:カスタム Lambda 実装のため、初回は開発コストが発生

アプリケーションへの影響

  • ダウンタイムなし:RDS Proxy 経由接続のため、ローテーション中も接続が維持される
  • 透過的なローテーション:アプリケーションは常に最新の認証情報を取得するため、特別な対応不要

今後の改善点

シークレットの自動ローテーションは実現できましたが、現時点ではローテーション失敗時の特別な通知体制は設けていません。今後の改善案として以下を検討しています。

  • ローテーション失敗時の SNS / Slack 通知
  • 自動ロールバック処理の実装
  • CloudWatch アラームによる監視強化

まとめ

本記事では、AWS Secrets Manager の自動ローテーション機能を用いて、VPC プライベートサブネット内の RDS パスワードを定期的に更新する機能を実装した事例をご紹介しました。

実装のポイント

  1. カスタム Lambda 関数:VPC 内 RDS へのアクセスには、権限を明示的に付与したカスタム Lambda 関数が必要
  2. 4 つのステップcreateSecretsetSecrettestSecretfinishSecret の実装が必須
  3. シングルユーザー戦略:RDS Proxy 使用時はシンプルな戦略で十分
  4. ダウンタイムゼロ:RDS Proxy により、ローテーション中も接続が維持される

マネージドローテーション機能が VPC 内の RDS に対応していれば簡単に実装できたはずですが、カスタム Lambda 関数を実装する過程で、Secrets Manager のローテーション機能の仕組みや運用上の注意点を実践的に理解できたのは大きな収穫でした。初回実装には想定以上の時間を要しましたが、一度構築してしまえば、自動化による人為的ミスの排除やセキュリティレベルの継続的な向上が実現でき、長期的な運用を考えると十分に見合う投資だったと実感しています。

セキュリティベストプラクティス

  • 固定パスワードの使用を避け、定期的なローテーションを実施
  • 環境変数へのハードコードではなく、Secrets Manager から動的に取得
  • セキュリティ監視ツールのアラートに適切に対応

Secrets Manager の自動ローテーション機能を活用することで、セキュリティを強化しながら、運用負荷を削減することができます。同様の課題を抱えている方の参考になれば幸いです。

おまけ:IAM 認証を用いた方法について

本記事では、Secrets Manager を用いたパスワード認証でローテーションを実装しましたが、より高度なセキュリティを求める場合、IAM 認証を利用する方法もあります。RDS Proxy は、クライアント – Proxy 間と Proxy – データベース間の両方で IAM 認証を使用できます。IAM ベースで認証が可能となるため、Secrets Manager でのパスワード管理が不要になり、より強固なセキュリティ要件に対応可能となります。

IAM 認証のメリット

  • パスワード不要:データベースへのアクセスに ID とパスワードが不要
  • ローテーション不要:IAM 認証トークンは自動的に管理される
  • 一元管理:IAM ポリシーでアクセス制御を一元管理できる

今回採用しなかった理由

既存システムからの移行コストとパスワードローテーションで業務要件を満たせていたため、今回は Secrets Manager によるパスワード認証を採用しました。将来的にセキュリティ要件が厳しくなった場合は、IAM 認証への移行も検討しようと思います。

参考リンク

投稿者プロフィール

羽賀 夢馬
羽賀 夢馬
2023年入社。BS事業部所属。
AWS を活用したシステム開発・インフラ構築を主に担当しています。
この記事をSNSでシェア!