こんにちは。
アプリケーションサービス本部ディベロップメントサービス1課の八鍬です。
Amazon Aurora PostgreSQL + Amazon RDS Proxy の構成で、DB 接続周りのパスワード管理を完全になくし、よりセキュアで運用負荷の低い構成に移行しました。
具体的には以下の 2 つを導入しています。
- End-to-End IAM 認証: アプリケーション(AWS Lambda)から Aurora までの全経路を IAM 認証にし、接続用パスワードの管理を不要にする
- Managed Master User Password: マスターユーザーのパスワードを RDS サービスに自動管理させ、ローテーション用の Lambda や AWS CloudFormation スタックを不要にする
この記事では、それぞれの仕組みと AWS CDK での設定手順を紹介します。なお、本記事の内容は 2026 年 5 月時点の情報に基づいています。AWS サービスや CDK の仕様は変更される可能性があるため、最新のドキュメントも併せてご確認ください。
背景: パスワード管理の運用課題
従来の構成では、以下のようなパスワード管理の課題がありました。
- Lambda が DB 接続するたびに AWS Secrets Manager からパスワードを取得する必要がある
- DB ユーザーごとにシークレットを作成・管理し、RDS Proxy に登録する必要がある
- マスターパスワードのローテーションに専用の Lambda と AWS CloudFormation スタックが必要
- ローテーション用 Lambda のランタイムがサポート切れになるリスクがある

これらを解消するため、「アプリケーション接続はパスワードレス」「マスターパスワードは RDS に自動管理させる」構成を目指しました。

対象読者
- Amazon Aurora + Amazon RDS Proxy を使っていて、パスワード管理の運用負荷を下げたい方
- AWS CDK で IAM 認証や Managed Master User Password を設定しようとしている方
- Lambda から RDS Proxy 経由で DB 接続している方
検証環境
- Amazon Aurora PostgreSQL 17.6(Aurora Serverless v2)
- Amazon RDS Proxy
- AWS Lambda(Node.js 22.x)
- AWS CDK v2.236.0
- TypeScript
1. End-to-End IAM 認証でアプリケーション接続をパスワードレスに
Amazon RDS Proxy の IAM 認証には 2 種類ある
RDS Proxy の IAM 認証には Standard と End-to-End の 2 種類があります(RDS Proxy concepts and terminology)。
Standard IAM authentication: Enforce IAM authentication for connections to your proxy while the proxy connects to the database using credentials stored in Secrets Manager.
End-to-end IAM authentication: Enforces IAM authentication for connections directly from your applications to your database through the proxy. End-to-end IAM authentication simplifies your security configuration and avoids database credential management in Secrets Manager.
Standard IAM 認証
Lambda --[IAMトークン]--> RDS Proxy --[パスワード認証]--> Aurora
- クライアント → Proxy 間のみ IAM 認証
- Proxy → Aurora 間は Secrets Manager のパスワードで接続
- 各 DB ユーザーのシークレットを Proxy に登録する必要がある
End-to-End IAM 認証
Lambda --[IAMトークン]--> RDS Proxy --[IAM認証]--> Aurora
- 全経路が IAM 認証
- Proxy は自身の IAM ロール(
rds-db:connect権限)で Aurora に接続 - アプリケーション接続用のパスワード管理が不要
今回はアプリケーション接続にパスワードを使わない構成にしたかったので、End-to-End IAM 認証を選択しました。
End-to-End IAM 認証の設定手順(AWS CDK)
1. Aurora クラスタで IAM 認証を有効化
const cluster = new rds.DatabaseCluster(this, "Cluster", { // ... iamAuthentication: true, });
2. RDS Proxy で End-to-End IAM 認証を有効化
CDK で iamAuth: true を設定するだけでは Standard IAM 認証にしかなりません。End-to-End にするには defaultAuthScheme の指定が必要です。
const proxy = new rds.DatabaseProxy(this, "Proxy", { proxyTarget: rds.ProxyTarget.fromCluster(cluster), vpc, iamAuth: true, defaultAuthScheme: rds.DefaultAuthScheme.IAM_AUTH, });
defaultAuthScheme は CDK v2.236.0 時点で L2 プロパティとしてサポートされています。
なお、defaultAuthScheme: DefaultAuthScheme.IAM_AUTH を指定する場合、secrets プロパティは不要です。CDK の型定義でも「One or more secrets are required when defaultAuthScheme is DefaultAuthScheme.NONE」と記載されており、End-to-End IAM 認証では Secrets Manager を経由しないため省略できます。
ハマりポイント:
iamAuth: trueだけで End-to-End になると思い込んでいました。ドキュメントを読み込むまで Standard IAM と End-to-End IAM の違いに気づけず、検証の途中までCfnDBProxyへの Override で対応していたのですが、L2 で対応済みでした。
3. Proxy の IAM ロールに rds-db:connect 権限を追加
End-to-End IAM 認証では、Proxy 自身が IAM で Aurora に接続します。CDK はこの権限を自動付与してくれないため、Proxy に渡すロールを自前で作成し、クラスタリソース ID を指定して rds-db:connect 権限を付与します。
const proxyRole = new iam.Role(this, "ProxyRole", { assumedBy: new iam.ServicePrincipal("rds.amazonaws.com"), }); const proxy = new rds.DatabaseProxy(this, "Proxy", { // ... role: proxyRole, defaultAuthScheme: rds.DefaultAuthScheme.IAM_AUTH, }); proxyRole.addToPolicy( new iam.PolicyStatement({ actions: ["rds-db:connect"], resources: [ `arn:aws:rds-db:${this.region}:${this.account}:dbuser:${cluster.clusterResourceIdentifier}/app_user`, ], }), );
ハマりポイント 1: これがないと Proxy → Aurora 間の接続で
PAM authentication failedエラーになります。エラーメッセージからは原因が分かりにくく、特定に時間がかかりました。ハマりポイント 2: CDK の
proxy.grantConnect(proxyRole, "app_user")を使いたくなりますが、これは Proxy リソース ID ベースの ARN(dbuser:{proxyResourceId}/app_user)を生成します。End-to-End IAM 認証で Proxy が Aurora に接続する際に必要なのは クラスタリソース ID ベースの ARN(dbuser:{clusterResourceId}/app_user)です。そのため、Proxy ロールへの権限付与にはaddToPolicyでクラスタリソース ID を明示する必要があります。grantConnectはクライアント(Lambda 等)→ Proxy 間の権限付与にのみ使用してください。
4. Lambda に rds-db:connect 権限を付与
proxy.grantConnect(lambda, "app_user");
5. DB ユーザーに rds_iam ロールを付与
Aurora 側でも、IAM 認証で接続するユーザーに rds_iam ロールを付与する必要があります。CDK デプロイだけでは DB 内部のユーザー設定は変わらないので、踏み台経由などで手動実行します。
GRANT rds_iam TO app_user;
ハマりポイント: これがないと同じく
PAM authentication failedになります。手順 3 と同じエラーが出るため、どちらが原因か切り分けが必要です。
6. Lambda から IAM トークンで接続
@aws-sdk/rds-signer で IAM トークンを生成し、pg の password フィールドに渡します。
公式には完全な接続サンプルコードは提供されておらず、@aws-sdk/rds-signer の README に getAuthToken() の使用例と「Use this token as the password for connecting to your RDS instance」という注記があるのみです。以下のコードはこの注記に従って実装しました。
import { Signer } from "@aws-sdk/rds-signer"; import { Client } from "pg"; const signer = new Signer({ hostname: process.env.PROXY_ENDPOINT, port: 5432, username: "app_user", }); const token = await signer.getAuthToken(); const client = new Client({ host: process.env.PROXY_ENDPOINT, port: 5432, user: "app_user", password: token, // IAM トークンを password に渡す database: "mydb", ssl: { rejectUnauthorized: false }, }); await client.connect();
password フィールドに渡していますが、中身は固定パスワードではなく有効期限 15 分の一時トークンです。PostgreSQL のプロトコル上、認証情報は password フィールドで送る仕組みしかないため、このような形になります。
AWS 公式ドキュメントには Python の接続サンプルはありますが、TypeScript/Node.js で RDS Proxy 経由の End-to-End IAM 認証を実装したサンプルは見当たりませんでした。
2. Managed Master User Password でマスターパスワードの管理も自動化
アプリケーション接続をパスワードレスにしても、マスターユーザーのパスワードは依然として存在します。従来はこのローテーションにも運用コストがかかっていたため、Managed Master User Password を導入して自動化しました。

従来の方式の課題
- Secrets Manager のローテーションに Lambda が必要
- ローテーション用の AWS CloudFormation スタックが自動作成される(サポート切れの Python ランタイムの Lambda が含まれる場合がある)
Managed Master User Password の設定
RDS サービスがシークレット(rds!cluster-xxx)を自動作成・管理し、デフォルト 7 日間隔で自動ローテーションします。ローテーション用の Lambda / AWS CloudFormation スタックは作成されません。
CDK での設定は、L2 未対応のため CfnDBCluster への Override が必要です(L2 対応の PR がレビュー中: https://github.com/aws/aws-cdk/pull/35734 )。
const cfnCluster = cluster.node.defaultChild as rds.CfnDBCluster; cfnCluster.addPropertyOverride("ManageMasterUserPassword", true); cfnCluster.addPropertyOverride("MasterUserPassword", undefined);
注意:
ManageMasterUserPasswordを有効にすると、CDK が自動生成するDatabaseClusterSecretと RDS が管理するrds!cluster-xxxの 2 つのシークレットが作られます。実際に有効なのはrds!cluster-xxxの方で、パスワードの値が異なります。検証中にこれに気づかず混乱しました。
ローテーション検証
実際にローテーションを実行して、旧パスワードで接続できなくなることを確認しました。
aws rds modify-db-cluster \ --db-cluster-identifier <cluster-id> \ --rotate-master-user-password \ --apply-immediately
| 確認項目 | 結果 |
|---|---|
| ローテーション前: 旧パスワードで接続 | ✅ 成功 |
| ローテーション後: 旧パスワードで接続 | ❌ password authentication failed |
| ローテーション用 Lambda が作成されていない | ✅ 確認 |
| ローテーション用 AWS CloudFormation スタックが作成されていない | ✅ 確認 |
CDK コード全体
上記の設定をまとめた CDK スタックの全体像です。
import * as cdk from "aws-cdk-lib"; import * as ec2 from "aws-cdk-lib/aws-ec2"; import * as iam from "aws-cdk-lib/aws-iam"; import * as lambda from "aws-cdk-lib/aws-lambda"; import * as rds from "aws-cdk-lib/aws-rds"; import { Construct } from "constructs"; export class IamAuthStack extends cdk.Stack { constructor(scope: Construct, id: string, props?: cdk.StackProps) { super(scope, id, props); // VPC const vpc = new ec2.Vpc(this, "Vpc", { maxAzs: 2, natGateways: 1, }); // セキュリティグループ const dbSg = new ec2.SecurityGroup(this, "DbSg", { vpc }); const proxySg = new ec2.SecurityGroup(this, "ProxySg", { vpc }); dbSg.addIngressRule(proxySg, ec2.Port.tcp(5432)); // Aurora クラスタ(IAM 認証有効 + Managed Master User Password) const cluster = new rds.DatabaseCluster(this, "Cluster", { engine: rds.DatabaseClusterEngine.auroraPostgres({ version: rds.AuroraPostgresEngineVersion.VER_17_6, }), serverlessV2MinCapacity: 0, serverlessV2MaxCapacity: 2, writer: rds.ClusterInstance.serverlessV2("Writer"), defaultDatabaseName: "mydb", vpc, vpcSubnets: { subnetType: ec2.SubnetType.PRIVATE_ISOLATED }, securityGroups: [dbSg], iamAuthentication: true, storageEncrypted: true, }); // Managed Master User Password const cfnCluster = cluster.node.defaultChild as rds.CfnDBCluster; cfnCluster.addPropertyOverride("ManageMasterUserPassword", true); cfnCluster.addPropertyOverride("MasterUserPassword", undefined); // Proxy 用 IAM ロール const proxyRole = new iam.Role(this, "ProxyRole", { assumedBy: new iam.ServicePrincipal("rds.amazonaws.com"), }); // RDS Proxy(End-to-End IAM 認証) const proxy = new rds.DatabaseProxy(this, "Proxy", { proxyTarget: rds.ProxyTarget.fromCluster(cluster), vpc, role: proxyRole, securityGroups: [proxySg], vpcSubnets: { subnetType: ec2.SubnetType.PRIVATE_ISOLATED }, iamAuth: true, defaultAuthScheme: rds.DefaultAuthScheme.IAM_AUTH, }); // Proxy ロールに rds-db:connect 権限を付与(End-to-End IAM 認証に必要) // grantConnect は Proxy リソース ID ベースの ARN を生成するため使用不可。 // End-to-End ではクラスタリソース ID を指定する必要がある。 proxyRole.addToPolicy( new iam.PolicyStatement({ actions: ["rds-db:connect"], resources: [ `arn:aws:rds-db:${this.region}:${this.account}:dbuser:${cluster.clusterResourceIdentifier}/app_user`, ], }), ); // Lambda const fn = new lambda.Function(this, "AppFn", { runtime: lambda.Runtime.NODEJS_22_X, handler: "index.handler", code: lambda.Code.fromAsset("lambda"), environment: { PROXY_ENDPOINT: proxy.endpoint, }, vpc, vpcSubnets: { subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS }, timeout: cdk.Duration.seconds(30), }); // Lambda に rds-db:connect 権限を付与 proxy.grantConnect(fn, "app_user"); // Lambda → Proxy のセキュリティグループ許可 proxySg.addIngressRule( fn.connections.securityGroups[0], ec2.Port.tcp(5432), ); } }
注意: 上記に加えて、Aurora 側で
GRANT rds_iam TO app_user;の実行が必要です(踏み台経由等で手動実行)。
まとめ
今回の構成変更により、以下のパスワード管理が不要になりました。
| 項目 | 従来 | 今回 |
|---|---|---|
| アプリケーション接続 | Secrets Manager でパスワード管理 | End-to-End IAM 認証(パスワード不要) |
| マスターパスワード | Lambda + CloudFormation でローテーション | Managed Master User Password(RDS が自動管理) |
End-to-End IAM 認証の設定一覧です。
| # | 設定 | 方法 |
|---|---|---|
| 1 | Aurora クラスタで IAM 認証を有効化 | iamAuthentication: true |
| 2 | RDS Proxy で End-to-End IAM を有効化 | defaultAuthScheme: rds.DefaultAuthScheme.IAM_AUTH |
| 3 | Proxy IAM ロールに rds-db:connect 権限を追加 | proxyRole.addToPolicy(...) でクラスタリソース ID を指定 |
| 4 | Lambda に rds-db:connect 権限を付与 | proxy.grantConnect(lambda, "app_user") |
| 5 | DB ユーザーに rds_iam を付与 | GRANT rds_iam TO app_user; |
| 6 | Lambda から IAM トークンで接続 | @aws-sdk/rds-signer の Signer.getAuthToken() |
#3 と #4 で権限付与の方法が異なる点に注意してください。proxy.grantConnect() は Proxy リソース ID ベースの ARN を生成するため、クライアント → Proxy 間(#4)には使えますが、Proxy → Aurora 間(#3)にはクラスタリソース ID が必要なため addToPolicy で明示する必要があります。
設定箇所が複数のレイヤー(AWS CDK / IAM / DB / Lambda コード)に分散しているため、全体像を把握した上で進めるのがポイントです。同じ構成でパスワード管理の運用負荷を下げたい方の参考になれば幸いです。
参考資料
- RDS Proxy concepts and terminology
- Password management with Amazon Aurora and AWS Secrets Manager
- Managed rotation for AWS Secrets Manager secrets
- Configuring IAM authentication for RDS Proxy
- Creating an IAM policy for end-to-end IAM authentication
- Moving from standard IAM to end-to-end IAM authentication for RDS Proxy
- Connecting to a database through RDS Proxy
- @aws-sdk/rds-signer (npm)