ローカルのJava(JDBC)からSSM ポートフォワーディング + IAM認証でプライベートサブネットのRDS PostgreSQLに接続してみた
はじめに
みなさんこんにちは、クラウド事業本部コンサルティング部の浅野です。
プライベートサブネットに配置したRDSに対して、ローカルの開発環境から JDBCで接続したいケースがあります。
セキュアにやる方法はいくつか存在しますが、今回は最もコスト効率が良くパスワードレスで接続できる「SSM + ポートフォワーディング + IAM認証」を用いた方法で実際に接続してみたいと思います。
構成
今回は以下の構成でローカルクライアントからRDSに接続します。

この構成の「セキュリティ、コスト、管理」それぞれのメリットは以下です。
セキュリティ
| ポイント | 説明 |
|---|---|
| EC2・RDS の配置 | 全てプライベートサブネットでインターネットから直接アクセス不可 |
| 通信の暗号化 | SSM + ポートフォワーディングにて全経路をTLSで暗号化。SSHキー管理不要 |
| DB 認証情報 | IAM認証によってパスワードレス(マスターパスワードの保管は必要) |
| アクセス制御 | IAMポリシーでポートフォワーディングの許可対象を制限可能 |
インターネットを通してSSMのパブリックエンドポイントに接続し、IAM認証を用いてTLSでのDB接続への強制が可能で、長時間セッション接続の抑制、IAMポリシーを用いた接続対象の制限なども可能です。
AWS JDBCドライバーはIAM認証をサポートしており、パスワードレスな方法なのでSecrets Managerの管理を軽減できるのもポイントです。
ただし接続数や実行時間制限があるので採用する場合は要件を確認しましょう。
ちなみに、IAM認証トークン作成などの記録はCloudWatchやCloudTrailにて確認することはできません。
コスト
| リソース | 内容 | 備考 |
|---|---|---|
| 踏み台用EC2 | スペックによる | SSM ポートフォワーディング中継用 |
| VPC Endpoints | SSM, SSM Messages | インターフェースエンドポイントx2 |
| CloudWatch Logs | ロググループ数による | SQL ログの保存先 |
既存のDB系リソース料金を除けば、ローカルから接続に必要なコスト考慮は「踏み台用EC2インスタンス」と「VPC Endpoint」です。
SQLの詳細内容の取得が必要な場合、「CloudWatch Logs」に流すログの料金考慮が別途必要です。
踏み台用EC2インスタンスに関しては、接続台数が増えるとスペックアップ、増台を考慮する必要があります。
管理項目
AWSサービスのみで実行SQLの取得と監査証跡の両方が可能です。
| 項目 | 内容 |
|---|---|
| セッション監査 | CloudTrail に開始・終了が自動記録(誰が・いつ・どのインスタンスに接続したか確認可能・詳細な内容は不可) |
| SQL監査 | CloudWatch LogsへRDSのPostgreSQLログ取得可能 |
| セッションタイムアウト | Session Manager の設定で変更可能(アイドル: デフォルト 20 分、最大セッション時間: デフォルト無制限、最大 24 時間) |
CDKスタック
実際に以下のCDKスタックで必要なインフラリソースをデプロイしました。
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as ec2 from 'aws-cdk-lib/aws-ec2';
import * as rds from 'aws-cdk-lib/aws-rds';
import * as iam from 'aws-cdk-lib/aws-iam';
import * as logs from 'aws-cdk-lib/aws-logs';
export class DemoRdsSsmStack 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: 0,
subnetConfiguration: [
{
cidrMask: 24,
name: 'PrivateEc2',
subnetType: ec2.SubnetType.PRIVATE_ISOLATED,
},
{
cidrMask: 24,
name: 'PrivateRds',
subnetType: ec2.SubnetType.PRIVATE_ISOLATED,
},
],
});
// VPCエンドポイント用セキュリティグループ
const vpceSecurityGroup = new ec2.SecurityGroup(this, 'VpceSg', {
vpc,
description: 'Security group for VPC Endpoints',
allowAllOutbound: true,
});
vpceSecurityGroup.addIngressRule(
ec2.Peer.ipv4(vpc.vpcCidrBlock),
ec2.Port.tcp(443),
'Allow HTTPS from VPC',
);
// SSM用VPCエンドポイント(プライベートサブネットからSSM接続するために必要)
vpc.addInterfaceEndpoint('SsmEndpoint', {
service: ec2.InterfaceVpcEndpointAwsService.SSM,
subnets: { subnetType: ec2.SubnetType.PRIVATE_ISOLATED },
securityGroups: [vpceSecurityGroup],
});
vpc.addInterfaceEndpoint('SsmMessagesEndpoint', {
service: ec2.InterfaceVpcEndpointAwsService.SSM_MESSAGES,
subnets: { subnetType: ec2.SubnetType.PRIVATE_ISOLATED },
securityGroups: [vpceSecurityGroup],
});
// EC2用セキュリティグループ
const ec2SecurityGroup = new ec2.SecurityGroup(this, 'Ec2Sg', {
vpc,
description: 'Security group for EC2 (SSM port forwarding relay)',
allowAllOutbound: true,
});
// RDS用セキュリティグループ
const rdsSecurityGroup = new ec2.SecurityGroup(this, 'RdsSg', {
vpc,
description: 'Security group for RDS PostgreSQL',
allowAllOutbound: false,
});
// EC2 → RDS へのPostgreSQL接続を許可
rdsSecurityGroup.addIngressRule(
ec2SecurityGroup,
ec2.Port.tcp(5432),
'Allow PostgreSQL from EC2',
);
// EC2用IAMロール(SSM接続用)
const ec2Role = new iam.Role(this, 'Ec2Role', {
assumedBy: new iam.ServicePrincipal('ec2.amazonaws.com'),
managedPolicies: [
iam.ManagedPolicy.fromAwsManagedPolicyName('AmazonSSMManagedInstanceCore'),
],
});
// EC2インスタンス(SSMポートフォワーディングの中継用、最小スペック)
const instance = new ec2.Instance(this, 'BastionInstance', {
vpc,
vpcSubnets: { subnetGroupName: 'PrivateEc2' },
instanceType: ec2.InstanceType.of(ec2.InstanceClass.T3, ec2.InstanceSize.MICRO),
machineImage: ec2.MachineImage.latestAmazonLinux2023({
cpuType: ec2.AmazonLinuxCpuType.X86_64,
}),
securityGroup: ec2SecurityGroup,
role: ec2Role,
});
// RDS PostgreSQL(IAM認証有効、SQLログをCloudWatch Logsに出力)
const dbInstance = new rds.DatabaseInstance(this, 'PostgresInstance', {
engine: rds.DatabaseInstanceEngine.postgres({
version: rds.PostgresEngineVersion.VER_17_6,
}),
instanceType: ec2.InstanceType.of(ec2.InstanceClass.T4G, ec2.InstanceSize.MICRO),
vpc,
vpcSubnets: { subnetGroupName: 'PrivateRds' },
securityGroups: [rdsSecurityGroup],
databaseName: 'demodb',
// IAMデータベース認証を有効化
iamAuthentication: true,
multiAz: false,
allocatedStorage: 20,
maxAllocatedStorage: 20,
backupRetention: cdk.Duration.days(0),
deleteAutomatedBackups: true,
removalPolicy: cdk.RemovalPolicy.DESTROY,
// PostgreSQLログをCloudWatch Logsにエクスポート
cloudwatchLogsExports: ['postgresql'],
cloudwatchLogsRetention: logs.RetentionDays.ONE_DAY,
// SQLログ出力用パラメータグループ
parameterGroup: new rds.ParameterGroup(this, 'PostgresParams', {
engine: rds.DatabaseInstanceEngine.postgres({
version: rds.PostgresEngineVersion.VER_17_6,
}),
parameters: {
'log_statement': 'all',
'log_min_duration_statement': '0',
'log_connections': '1',
'log_disconnections': '1',
},
}),
});
// 出力
new cdk.CfnOutput(this, 'InstanceId', {
value: instance.instanceId,
description: 'EC2 Instance ID(SSMポートフォワーディング用)',
});
new cdk.CfnOutput(this, 'RdsEndpoint', {
value: dbInstance.dbInstanceEndpointAddress,
description: 'RDS PostgreSQL エンドポイント',
});
new cdk.CfnOutput(this, 'RdsPort', {
value: dbInstance.dbInstanceEndpointPort,
description: 'RDS PostgreSQL ポート',
});
new cdk.CfnOutput(this, 'SsmPortForwardCommand', {
value: `aws ssm start-session --target ${instance.instanceId} --document-name AWS-StartPortForwardingSessionToRemoteHost --parameters '{"host":["${dbInstance.dbInstanceEndpointAddress}"],"portNumber":["5432"],"localPortNumber":["15432"]}'`,
description: 'SSMポートフォワーディングコマンド',
});
new cdk.CfnOutput(this, 'PostgresLogGroup', {
value: `/aws/rds/instance/${dbInstance.instanceIdentifier}/postgresql`,
description: 'PostgreSQL ログの CloudWatch Logs ロググループ',
});
}
}
作成リソース
- VPC(プライベートサブネット × 4)
- VPC Endpoints(SSM、SSM Messages)
- セキュリティグループ × 3(VPC Endpoint 用、EC2 用、RDS 用)
- IAM ロール(EC2 用、AmazonSSMManagedInstanceCore)
- EC2(t3.micro、Amazon Linux 2023、SSMポートフォワーディング中継用)
- RDS PostgreSQL 17.6(db.t4g.micro、シングルAZ、20GB、IAM 認証有効)
- RDS パラメータグループ(log_statement=all 等、SQLログ出力用)
- CloudWatch Logs ロググループ(PostgreSQL ログ、保持期間1日)
ローカルJavaファイルからRDSに接続してみる
実際にローカルクライアントから接続します。前提条件や必要なツールは以下です。
前提条件
-
AWS CLI がインストール・設定済み
-
Session Manager Pluginがインストール済み
-
JBangがインストール済み
- 今回はローカルのJava実行にJBangを用いてパッケージ管理を簡素化しています。
- 筆者の環境は
v0.137.0
-
psql がインストール済み
- ローカルからRDS for PostgreSQLに対して接続し、セットアップを行うため必要です。
- 筆者の環境は
v18.3
-
ローカルクライアントで使用するIAMユーザー/ロールに以下の最小権限が必要
| Statement | 用途 | 備考 |
|---|---|---|
| RdsIamAuth | IAM認証でRDSに接続 | データベースユーザーをiam_auth_userユーザーのみに限定 |
| SsmPortForwarding | SSMポートフォワーディング開始 | 対象インスタンス限定 |
| SsmSessionControl | セッション終了・再接続 | 自分のセッションのみ |
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "RdsIamAuth",
"Effect": "Allow",
"Action": "rds-db:connect",
"Resource": "arn:aws:rds-db:ap-northeast-1:<account-id>:dbuser:<RdsResourceId>/iam_auth_user"
},
{
"Sid": "SsmPortForwarding",
"Effect": "Allow",
"Action": "ssm:StartSession",
"Resource": [
"arn:aws:ec2:ap-northeast-1:<account-id>:instance/<InstanceId>",
"arn:aws:ssm:ap-northeast-1:<account-id>:document/AWS-StartPortForwardingSessionToRemoteHost"
]
},
{
"Sid": "SsmSessionControl",
"Effect": "Allow",
"Action": [
"ssm:TerminateSession",
"ssm:ResumeSession"
],
"Resource": "arn:aws:ssm:ap-northeast-1:<account-id>:session/${aws:userid}-*"
}
]
}
SSM + ポートフォワーディングでのセッション開始
CDKをデプロイした時点でSSM + ポートフォワーディングの条件は揃っているので、実際にローカルのAWS CLIからセッションを開始します。
今回はlocal:15432 -> remote:5432に転送します。あらかじめEC2インスタンスIDとRDSエンドポイントを確認してください。
aws ssm start-session \
--target <EC2インスタンスID> \
--document-name AWS-StartPortForwardingSessionToRemoteHost \
--parameters '{"host":["<RDSエンドポイント>"],"portNumber":["5432"],"localPortNumber":["15432"]}'
# 出力
Starting session with SessionId: 1776074205032474000-****************
Port 15432 opened for sessionId 1776074205032474000-****************
Waiting for connections...
エラーが出ずに「Waiting for connections...」になれば接続待ち状態です。
この状態でローカル環境からポートフォワーディングを通してデータベースユーザーの作成を行います。
IAM認証用データベースユーザーの作成と権限付与
通常ローカルクライアントからIAM認証を用いてDBに接続する場合はマスターユーザーではなく、それ専用のユーザーを作成して接続します。
そのため、管理者側でマスターユーザー/マスターパスワードを用いて新規データベースユーザーの作成と権限付与の作業が必要です。
MySQL とPostgreSQLで若干やり方が異なりますが、今回はPostgreSQLで設定します。
DBを作成した時点でマスターユーザー/マスターパスワードは確認しておいてください。
※マスターユーザー/マスターパスワードの管理にてSecrets Managerを使用することも検討してください。
ポートフォワーディングで接続コネクションが通ったターミナルとは別セッションで以下のコマンドを実行します。
# 環境変数にマスターパスワードを保存
PGPASSWORD=<マスターパスワード>
# ポートフォワーディング経由でpsqlを用いてDB接続
PGPASSWORD=$PGPASSWORD psql -h localhost -p 15432 -U <マスターユーザー名> -d demodb
psql (18.3, server 17.6)
SSL connection (protocol: TLSv1.3, cipher: TLS_AES_256_GCM_SHA384, compression: off, ALPN: postgresql)
Type "help" for help.
demodb=>
今回は検証用としてCDKでdemodbをあらかじめ作成していますが、適宜変更してください。
この時点で先ほどのポートフォワーディングセッションの接続が開始したことが確認できます。
Connection accepted for session [1776133454604472000-****************]
RDS内にマスターユーザーとパスワードで接続できたら以下のドキュメントに沿ってiam_auth_userを作成し、データベースの作成から読み書き権限を与えます。
-- IAM 認証用のデータベースユーザーを作成し、rds_iam ロールを付与
demodb=> CREATE USER iam_auth_user ;
CREATE ROLE
demodb=> GRANT rds_iam TO iam_auth_user;
GRANT ROLE
demodb=> GRANT ALL PRIVILEGES ON DATABASE demodb TO iam_auth_user;
GRANT
demodb=> GRANT ALL ON SCHEMA public TO iam_auth_user;
GRANT
iam_auth_userに強い権限を与えていますが、実際のワークロードでは調整してください。
これにてIAM認証用のデータベースユーザーを作成できました。
Javaファイルの作成と実行
続いてローカルで実行するテスト用のJavaファイルを作成します。
パッケージ管理とJavaの実行にJBangを使用しているのでインストールする必要があります。brew経由で簡単にインストールできます。
利点は簡素なパッケージ管理と1ファイルのみで実行まで完結できる点です。
以下の内容で RdsTestIam.java を作成します。
///usr/bin/env jbang "$0" "$@" ; exit $?
//DEPS software.amazon.jdbc:aws-advanced-jdbc-wrapper:3.2.0
//DEPS org.postgresql:postgresql:42.7.10
//DEPS software.amazon.awssdk:rds:2.42.30
import java.sql.*;
import java.util.Properties;
public class RdsTestIam {
public static void main(String[] args) throws Exception {
// IAM 認証プラグインを使った接続 URL
// ホストは RDS の実際のエンドポイントを指定(IAM トークン生成に必要)
String rdsEndpoint = System.getenv("RDS_ENDPOINT");
if (rdsEndpoint == null || rdsEndpoint.isEmpty()) {
System.err.println("環境変数 RDS_ENDPOINT を設定してください");
System.exit(1);
}
String url = "jdbc:aws-wrapper:postgresql://localhost:15432/demodb";
Properties props = new Properties();
props.setProperty("wrapperPlugins", "iam");
props.setProperty("user", "iam_auth_user");
props.setProperty("ssl", "true");
props.setProperty("sslmode", "require");
// IAM トークン生成に実際の RDS エンドポイントが必要
props.setProperty("iamHost", rdsEndpoint);
props.setProperty("iamRegion", "ap-northeast-1");
props.setProperty("iamDefaultPort", "5432");
System.out.println("RDS に IAM 認証で接続中...");
try (Connection conn = DriverManager.getConnection(url, props)) {
// PostgreSQL バージョン確認
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT version()");
while (rs.next()) {
System.out.println("接続成功!");
System.out.println("PostgreSQL バージョン: " + rs.getString(1));
}
// 現在の接続ユーザー確認
rs = stmt.executeQuery("SELECT current_user, session_user");
while (rs.next()) {
System.out.println("接続ユーザー: " + rs.getString(1));
}
// テストテーブル作成・データ挿入・取得
stmt.execute("CREATE TABLE IF NOT EXISTS test_table (id SERIAL PRIMARY KEY, name VARCHAR(100), created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP)");
stmt.execute("INSERT INTO test_table (name) VALUES ('SSM + IAM認証 JDBC テスト')");
rs = stmt.executeQuery("SELECT * FROM test_table ORDER BY id DESC LIMIT 5");
System.out.println("\n--- test_table の内容 ---");
while (rs.next()) {
System.out.printf("id=%d, name=%s, created_at=%s%n",
rs.getInt("id"),
rs.getString("name"),
rs.getTimestamp("created_at"));
}
}
System.out.println("\n完了!");
}
}
ポイント:
//DEPSで必要な依存関係を記述するだけで実行時に必要な依存をjbang経由でダウンロードしてくれます。- software.amazon.jdbc:aws-advanced-jdbc-wrapper: AWS JDBC Driver本体。IAM認証プラグインを提供
- org.postgresql:postgresql: PostgreSQL JDBCドライバ。実際のDB通信部分に必要
- software.amazon.awssdk:rds: IAM認証トークンの生成に使用
- IAM認証のために以下の設定を入れています。
wrapperPluginsをiamに設定iamHostに RDS の実際のエンドポイントを環境変数経由で指定ssl/sslmodeを有効化(IAM 認証はSSL接続が必須)- IAM 認証プラグインを用いて
DriverManager.getConnection(url, props)の記述で「トークン取得->認証->DB接続」までを実行
- テストテーブル & データをインサート
jdbc:aws-wrapper:postgresql://localhost:15432/demodbエンドポイントを用いてSQLを操作
参考:
実際にローカル環境でJBangを実行します。
export RDS_ENDPOINT=<実際のRdsEndpoint>
jbang RdsTestIam.java
以下のようにjdbcを用いてiam_auth_user経由でIAM認証を通して接続できました。
jbang RdsTestIam.java
[jbang] Building jar for RdsTestIam.java...
RDS に IAM 認証で接続中...
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
接続成功!
PostgreSQL バージョン: PostgreSQL 17.6 on aarch64-unknown-linux-gnu, compiled by gcc (GCC) 12.4.0, 64-bit
接続ユーザー: iam_auth_user
--- test_table の内容 ---
id=1, name=SSM + IAM認証 JDBC テスト, created_at=2026-04-14 11:46:35.953623
完了!
その後以下のコマンドを実行してAWS CLIでIAM認証を行い、psqlクライアント経由で実際のテーブル内のデータも再度確認しました。
psql "host=localhost port=15432 dbname=demodb user=iam_auth_user sslmode=require password=$(aws rds generate-db-auth-token --hostname $RDSHOST --port 5432 --username iam_auth_user --region ap-northeast-1)"
# 出力
psql (18.3, server 17.6)
SSL connection (protocol: TLSv1.3, cipher: TLS_AES_256_GCM_SHA384, compression: off, ALPN: postgresql)
Type "help" for help.
# テーブル一覧確認
demodb=> \d
List of relations
Schema | Name | Type | Owner
--------+-------------------+----------+---------------
public | test_table | table | iam_auth_user
public | test_table_id_seq | sequence | iam_auth_user
(2 rows)
# データ確認
demodb=> select * from test_table;
id | name | created_at
----+---------------------------+----------------------------
1 | SSM + IAM認証 JDBC テスト | 2026-04-14 11:46:35.953623
(1 row)
CloudWatch Logs で SQL ログを確認
CDKでDBを作成する際にパラメータグループを以下のように設定し、すべてのSQL出力をCloudWatch ロググループに出力するようにしています。
parameters: {
'log_statement': 'all',
'log_min_duration_statement': '0',
'log_connections': '1',
'log_disconnections': '1',
}
上記のJavaを実行後、AWS CLIを用いてロググループ内のストリームを確認しました。
aws logs tail <ロググループ名> --since 3m --region ap-northeast-1
ログ全文(クリックで展開)
2026-04-14T02:46:14.000000+00:00 demordsssmstack-postgresinstance19cdd68a-pg1psds4qtku.0 2026-04-14 02:46:14 UTC:10.0.0.13(50980):[unknown]@[unknown]:[2728]:LOG: connection received: host=10.0.0.13 port=50980
* Trying 127.0.0.1:1108...
* Connected to rdsauthproxy (127.0.0.1) port 1108
> POST /authenticateRequest HTTP/1.1
Host: rdsauthproxy:1108
Accept: */*
Content-Length: 1787
Content-Type: multipart/form-data; boundary=------------------------GC9V4M6HGaq5oUnnklSv2p
* We are completely uploaded and fine
2026-04-14T02:46:14.000000+00:00 demordsssmstack-postgresinstance19cdd68a-pg1psds4qtku.0 < HTTP/1.1 200 OK
< Content-Type: text/html;charset=utf-8
< Content-Length: 0
<
* Connection #0 to host rdsauthproxy left intact
2026-04-14T02:46:34.000000+00:00 demordsssmstack-postgresinstance19cdd68a-pg1psds4qtku.0 2026-04-14 02:46:34 UTC:10.0.0.13(50980):iam_auth_user@demodb:[2728]:LOG: connection authenticated: identity="iam_auth_user" method=pam (/rdsdbdata/config/pg_hba.conf:13)
2026-04-14T02:46:34.000000+00:00 demordsssmstack-postgresinstance19cdd68a-pg1psds4qtku.0 2026-04-14 02:46:34 UTC:10.0.0.13(50980):iam_auth_user@demodb:[2728]:LOG: connection authorized: user=iam_auth_user database=demodb SSL enabled (protocol=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384, bits=256)
2026-04-14T02:46:35.000000+00:00 demordsssmstack-postgresinstance19cdd68a-pg1psds4qtku.0 2026-04-14 02:46:35 UTC:10.0.0.13(50980):iam_auth_user@demodb:[2728]:LOG: statement: SET application_name = 'PostgreSQL JDBC Driver'
2026-04-14T02:46:35.000000+00:00 demordsssmstack-postgresinstance19cdd68a-pg1psds4qtku.0 2026-04-14 02:46:35 UTC:10.0.0.13(50980):iam_auth_user@demodb:[2728]:LOG: duration: 55.194 ms
2026-04-14T02:46:35.000000+00:00 demordsssmstack-postgresinstance19cdd68a-pg1psds4qtku.0 2026-04-14 02:46:35 UTC:10.0.0.13(50980):iam_auth_user@demodb:[2728]:LOG: duration: 155.154 ms parse <unnamed>: SELECT (setting LIKE '%aurora_stat_utils%') AS aurora_stat_utils FROM pg_catalog.pg_settings WHERE name OPERATOR(pg_catalog.=) 'rds.extensions'
2026-04-14T02:46:35.000000+00:00 demordsssmstack-postgresinstance19cdd68a-pg1psds4qtku.0 2026-04-14 02:46:35 UTC:10.0.0.13(50980):iam_auth_user@demodb:[2728]:LOG: duration: 30.895 ms bind <unnamed>: SELECT (setting LIKE '%aurora_stat_utils%') AS aurora_stat_utils FROM pg_catalog.pg_settings WHERE name OPERATOR(pg_catalog.=) 'rds.extensions'
2026-04-14T02:46:35.000000+00:00 demordsssmstack-postgresinstance19cdd68a-pg1psds4qtku.0 2026-04-14 02:46:35 UTC:10.0.0.13(50980):iam_auth_user@demodb:[2728]:LOG: execute <unnamed>: SELECT (setting LIKE '%aurora_stat_utils%') AS aurora_stat_utils FROM pg_catalog.pg_settings WHERE name OPERATOR(pg_catalog.=) 'rds.extensions'
2026-04-14T02:46:35.000000+00:00 demordsssmstack-postgresinstance19cdd68a-pg1psds4qtku.0 2026-04-14 02:46:35 UTC:10.0.0.13(50980):iam_auth_user@demodb:[2728]:LOG: duration: 72.680 ms
2026-04-14T02:46:35.000000+00:00 demordsssmstack-postgresinstance19cdd68a-pg1psds4qtku.0 2026-04-14 02:46:35 UTC:10.0.0.13(50980):iam_auth_user@demodb:[2728]:LOG: duration: 31.557 ms parse <unnamed>: SELECT 1 FROM pg_catalog.pg_proc LIMIT 1
2026-04-14T02:46:35.000000+00:00 demordsssmstack-postgresinstance19cdd68a-pg1psds4qtku.0 2026-04-14 02:46:35 UTC:10.0.0.13(50980):iam_auth_user@demodb:[2728]:LOG: duration: 47.616 ms bind <unnamed>: SELECT 1 FROM pg_catalog.pg_proc LIMIT 1
2026-04-14T02:46:35.000000+00:00 demordsssmstack-postgresinstance19cdd68a-pg1psds4qtku.0 2026-04-14 02:46:35 UTC:10.0.0.13(50980):iam_auth_user@demodb:[2728]:LOG: execute <unnamed>: SELECT 1 FROM pg_catalog.pg_proc LIMIT 1
2026-04-14T02:46:35.000000+00:00 demordsssmstack-postgresinstance19cdd68a-pg1psds4qtku.0 2026-04-14 02:46:35 UTC:10.0.0.13(50980):iam_auth_user@demodb:[2728]:LOG: duration: 1.417 ms
2026-04-14T02:46:35.000000+00:00 demordsssmstack-postgresinstance19cdd68a-pg1psds4qtku.0 2026-04-14 02:46:35 UTC:10.0.0.13(50980):iam_auth_user@demodb:[2728]:LOG: duration: 2.444 ms parse <unnamed>: SELECT (setting LIKE '%aurora_stat_utils%') AS aurora_stat_utils FROM pg_catalog.pg_settings WHERE name OPERATOR(pg_catalog.=) 'rds.extensions'
2026-04-14T02:46:35.000000+00:00 demordsssmstack-postgresinstance19cdd68a-pg1psds4qtku.0 2026-04-14 02:46:35 UTC:10.0.0.13(50980):iam_auth_user@demodb:[2728]:LOG: duration: 2.109 ms bind <unnamed>: SELECT (setting LIKE '%aurora_stat_utils%') AS aurora_stat_utils FROM pg_catalog.pg_settings WHERE name OPERATOR(pg_catalog.=) 'rds.extensions'
2026-04-14T02:46:35.000000+00:00 demordsssmstack-postgresinstance19cdd68a-pg1psds4qtku.0 2026-04-14 02:46:35 UTC:10.0.0.13(50980):iam_auth_user@demodb:[2728]:LOG: execute <unnamed>: SELECT (setting LIKE '%aurora_stat_utils%') AS aurora_stat_utils FROM pg_catalog.pg_settings WHERE name OPERATOR(pg_catalog.=) 'rds.extensions'
2026-04-14T02:46:35.000000+00:00 demordsssmstack-postgresinstance19cdd68a-pg1psds4qtku.0 2026-04-14 02:46:35 UTC:10.0.0.13(50980):iam_auth_user@demodb:[2728]:LOG: duration: 22.606 ms
2026-04-14T02:46:35.000000+00:00 demordsssmstack-postgresinstance19cdd68a-pg1psds4qtku.0 2026-04-14 02:46:35 UTC:10.0.0.13(50980):iam_auth_user@demodb:[2728]:ERROR: schema "rds_tools" does not exist at character 56
2026-04-14T02:46:35.000000+00:00 demordsssmstack-postgresinstance19cdd68a-pg1psds4qtku.0 2026-04-14 02:46:35 UTC:10.0.0.13(50980):iam_auth_user@demodb:[2728]:STATEMENT: SELECT multi_az_db_cluster_source_dbi_resource_id FROM rds_tools.multi_az_db_cluster_source_dbi_resource_id()
2026-04-14T02:46:35.000000+00:00 demordsssmstack-postgresinstance19cdd68a-pg1psds4qtku.0 2026-04-14 02:46:35 UTC:10.0.0.13(50980):iam_auth_user@demodb:[2728]:LOG: duration: 0.682 ms parse <unnamed>: SELECT 1 FROM pg_catalog.pg_proc LIMIT 1
2026-04-14T02:46:35.000000+00:00 demordsssmstack-postgresinstance19cdd68a-pg1psds4qtku.0 2026-04-14 02:46:35 UTC:10.0.0.13(50980):iam_auth_user@demodb:[2728]:LOG: duration: 0.087 ms bind <unnamed>: SELECT 1 FROM pg_catalog.pg_proc LIMIT 1
2026-04-14T02:46:35.000000+00:00 demordsssmstack-postgresinstance19cdd68a-pg1psds4qtku.0 2026-04-14 02:46:35 UTC:10.0.0.13(50980):iam_auth_user@demodb:[2728]:LOG: execute <unnamed>: SELECT 1 FROM pg_catalog.pg_proc LIMIT 1
2026-04-14T02:46:35.000000+00:00 demordsssmstack-postgresinstance19cdd68a-pg1psds4qtku.0 2026-04-14 02:46:35 UTC:10.0.0.13(50980):iam_auth_user@demodb:[2728]:LOG: duration: 0.021 ms
2026-04-14T02:46:35.000000+00:00 demordsssmstack-postgresinstance19cdd68a-pg1psds4qtku.0 2026-04-14 02:46:35 UTC:10.0.0.13(50980):iam_auth_user@demodb:[2728]:LOG: duration: 0.824 ms parse <unnamed>: SELECT (setting LIKE '%rds_tools%') AS rds_tools, (setting LIKE '%aurora_stat_utils%') AS aurora_stat_utils FROM pg_catalog.pg_settings WHERE name OPERATOR(pg_catalog.=) 'rds.extensions'
2026-04-14T02:46:35.000000+00:00 demordsssmstack-postgresinstance19cdd68a-pg1psds4qtku.0 2026-04-14 02:46:35 UTC:10.0.0.13(50980):iam_auth_user@demodb:[2728]:LOG: duration: 0.095 ms bind <unnamed>: SELECT (setting LIKE '%rds_tools%') AS rds_tools, (setting LIKE '%aurora_stat_utils%') AS aurora_stat_utils FROM pg_catalog.pg_settings WHERE name OPERATOR(pg_catalog.=) 'rds.extensions'
2026-04-14T02:46:35.000000+00:00 demordsssmstack-postgresinstance19cdd68a-pg1psds4qtku.0 2026-04-14 02:46:35 UTC:10.0.0.13(50980):iam_auth_user@demodb:[2728]:LOG: execute <unnamed>: SELECT (setting LIKE '%rds_tools%') AS rds_tools, (setting LIKE '%aurora_stat_utils%') AS aurora_stat_utils FROM pg_catalog.pg_settings WHERE name OPERATOR(pg_catalog.=) 'rds.extensions'
2026-04-14T02:46:35.000000+00:00 demordsssmstack-postgresinstance19cdd68a-pg1psds4qtku.0 2026-04-14 02:46:35 UTC:10.0.0.13(50980):iam_auth_user@demodb:[2728]:LOG: duration: 1.588 ms
2026-04-14T02:46:35.000000+00:00 demordsssmstack-postgresinstance19cdd68a-pg1psds4qtku.0 2026-04-14 02:46:35 UTC:10.0.0.13(50980):iam_auth_user@demodb:[2728]:LOG: duration: 2.023 ms parse <unnamed>: SELECT version()
2026-04-14T02:46:35.000000+00:00 demordsssmstack-postgresinstance19cdd68a-pg1psds4qtku.0 2026-04-14 02:46:35 UTC:10.0.0.13(50980):iam_auth_user@demodb:[2728]:LOG: duration: 0.038 ms bind <unnamed>: SELECT version()
2026-04-14T02:46:35.000000+00:00 demordsssmstack-postgresinstance19cdd68a-pg1psds4qtku.0 2026-04-14 02:46:35 UTC:10.0.0.13(50980):iam_auth_user@demodb:[2728]:LOG: execute <unnamed>: SELECT version()
2026-04-14T02:46:35.000000+00:00 demordsssmstack-postgresinstance19cdd68a-pg1psds4qtku.0 2026-04-14 02:46:35 UTC:10.0.0.13(50980):iam_auth_user@demodb:[2728]:LOG: duration: 0.010 ms
2026-04-14T02:46:35.000000+00:00 demordsssmstack-postgresinstance19cdd68a-pg1psds4qtku.0 2026-04-14 02:46:35 UTC:10.0.0.13(50980):iam_auth_user@demodb:[2728]:LOG: duration: 0.785 ms parse <unnamed>: SELECT current_user, session_user
2026-04-14T02:46:35.000000+00:00 demordsssmstack-postgresinstance19cdd68a-pg1psds4qtku.0 2026-04-14 02:46:35 UTC:10.0.0.13(50980):iam_auth_user@demodb:[2728]:LOG: duration: 0.033 ms bind <unnamed>: SELECT current_user, session_user
2026-04-14T02:46:35.000000+00:00 demordsssmstack-postgresinstance19cdd68a-pg1psds4qtku.0 2026-04-14 02:46:35 UTC:10.0.0.13(50980):iam_auth_user@demodb:[2728]:LOG: execute <unnamed>: SELECT current_user, session_user
2026-04-14T02:46:35.000000+00:00 demordsssmstack-postgresinstance19cdd68a-pg1psds4qtku.0 2026-04-14 02:46:35 UTC:10.0.0.13(50980):iam_auth_user@demodb:[2728]:LOG: duration: 0.013 ms
2026-04-14T02:46:35.000000+00:00 demordsssmstack-postgresinstance19cdd68a-pg1psds4qtku.0 2026-04-14 02:46:35 UTC:10.0.0.13(50980):iam_auth_user@demodb:[2728]:LOG: duration: 5.215 ms parse <unnamed>: CREATE TABLE IF NOT EXISTS test_table (id SERIAL PRIMARY KEY, name VARCHAR(100), created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP)
2026-04-14T02:46:35.000000+00:00 demordsssmstack-postgresinstance19cdd68a-pg1psds4qtku.0 2026-04-14 02:46:35 UTC:10.0.0.13(50980):iam_auth_user@demodb:[2728]:LOG: duration: 0.013 ms bind <unnamed>: CREATE TABLE IF NOT EXISTS test_table (id SERIAL PRIMARY KEY, name VARCHAR(100), created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP)
2026-04-14T02:46:35.000000+00:00 demordsssmstack-postgresinstance19cdd68a-pg1psds4qtku.0 2026-04-14 02:46:35 UTC:10.0.0.13(50980):iam_auth_user@demodb:[2728]:LOG: execute <unnamed>: CREATE TABLE IF NOT EXISTS test_table (id SERIAL PRIMARY KEY, name VARCHAR(100), created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP)
2026-04-14T02:46:35.000000+00:00 demordsssmstack-postgresinstance19cdd68a-pg1psds4qtku.0 2026-04-14 02:46:35 UTC:10.0.0.13(50980):iam_auth_user@demodb:[2728]:LOG: duration: 112.432 ms
2026-04-14T02:46:35.000000+00:00 demordsssmstack-postgresinstance19cdd68a-pg1psds4qtku.0 2026-04-14 02:46:35 UTC:10.0.0.13(50980):iam_auth_user@demodb:[2728]:LOG: duration: 21.385 ms parse <unnamed>: INSERT INTO test_table (name) VALUES ('SSM + IAM認証 JDBC テスト')
2026-04-14T02:46:35.000000+00:00 demordsssmstack-postgresinstance19cdd68a-pg1psds4qtku.0 2026-04-14 02:46:35 UTC:10.0.0.13(50980):iam_auth_user@demodb:[2728]:LOG: duration: 2.678 ms bind <unnamed>: INSERT INTO test_table (name) VALUES ('SSM + IAM認証 JDBC テスト')
2026-04-14T02:46:35.000000+00:00 demordsssmstack-postgresinstance19cdd68a-pg1psds4qtku.0 2026-04-14 02:46:35 UTC:10.0.0.13(50980):iam_auth_user@demodb:[2728]:LOG: execute <unnamed>: INSERT INTO test_table (name) VALUES ('SSM + IAM認証 JDBC テスト')
2026-04-14T02:46:35.000000+00:00 demordsssmstack-postgresinstance19cdd68a-pg1psds4qtku.0 2026-04-14 02:46:35 UTC:10.0.0.13(50980):iam_auth_user@demodb:[2728]:LOG: duration: 8.432 ms
2026-04-14T02:46:36.000000+00:00 demordsssmstack-postgresinstance19cdd68a-pg1psds4qtku.0 2026-04-14 02:46:36 UTC:10.0.0.13(50980):iam_auth_user@demodb:[2728]:LOG: duration: 6.922 ms parse <unnamed>: SELECT * FROM test_table ORDER BY id DESC LIMIT 5
2026-04-14T02:46:36.000000+00:00 demordsssmstack-postgresinstance19cdd68a-pg1psds4qtku.0 2026-04-14 02:46:36 UTC:10.0.0.13(50980):iam_auth_user@demodb:[2728]:LOG: duration: 6.984 ms bind <unnamed>: SELECT * FROM test_table ORDER BY id DESC LIMIT 5
2026-04-14T02:46:36.000000+00:00 demordsssmstack-postgresinstance19cdd68a-pg1psds4qtku.0 2026-04-14 02:46:36 UTC:10.0.0.13(50980):iam_auth_user@demodb:[2728]:LOG: execute <unnamed>: SELECT * FROM test_table ORDER BY id DESC LIMIT 5
2026-04-14T02:46:36.000000+00:00 demordsssmstack-postgresinstance19cdd68a-pg1psds4qtku.0 2026-04-14 02:46:36 UTC:10.0.0.13(50980):iam_auth_user@demodb:[2728]:LOG: duration: 0.040 ms
2026-04-14T02:46:36.000000+00:00 demordsssmstack-postgresinstance19cdd68a-pg1psds4qtku.0 2026-04-14 02:46:36 UTC:10.0.0.13(50980):iam_auth_user@demodb:[2728]:LOG: disconnection: session time: 0:00:21.606 user=iam_auth_user database=demodb host=10.0.0.13 port=50980
2026-04-14T02:47:19.000000+00:00 demordsssmstack-postgresinstance19cdd68a-pg1psds4qtku.0 2026-04-14 02:47:19 UTC::@:[1341]:LOG: checkpoint starting: time
2026-04-14T02:47:26.000000+00:00 demordsssmstack-postgresinstance19cdd68a-pg1psds4qtku.0 2026-04-14 02:47:26 UTC::@:[1341]:LOG: checkpoint complete: wrote 58 buffers (0.3%); 0 WAL file(s) added, 0 removed, 0 recycled; write=6.406 s, sync=0.004 s, total=6.545 s; sync files=46, longest=0.003 s, average=0.001 s; distance=65583 kB, estimate=65583 kB; lsn=0/1C000028, redo lsn=0/1800D550
ログから重要な部分だけ抽出します。
# IAM 認証成功 + SSL 接続確立
connection authenticated: identity="iam_auth_user" method=pam
connection authorized: user=iam_auth_user database=demodb SSL
enabled (protocol=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384)
# Java コードから実行した SQL が全て記録されている
execute <unnamed>: SELECT version()
execute <unnamed>: SELECT current_user, session_user
execute <unnamed>: CREATE TABLE IF NOT EXISTS test_table (id SERIAL PRIMARY KEY, name VARCHAR(100), created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP)
execute <unnamed>: INSERT INTO test_table (name) VALUES ('SSM + IAM認証 JDBC テスト')
execute <unnamed>: SELECT * FROM test_table ORDER BY id DESC LIMIT 5
# 切断記録
disconnection: session time: 0:00:21.606 user=iam_auth_user
database=demodb
以下の情報が適切に記録されていました。
- 誰が: iam_auth_user
- どの認証方式で: IAM 認証(method=pam)+ TLSv1.3
- 何を実行したか: 全SQL文がexecuteとして記録
- いつ切断したか: セッション時間含めて記録
最後に
今回はSSM ポートフォワーディングとIAM認証を組み合わせることで、プライベートサブネットの RDS に対してパスワードレスかつセキュアに JDBC接続できることを確認しました。
既にSSMを利用している環境であれば導入しやすく、IAMポリシーによる接続先の制限やCloudTrail、CloudWatch Logsによる監査もAWSサービスの範囲内で完結します。
ローカルJava開発環境から RDS への接続方法ではおすすめの方法ですので参考になれば幸いです。今回は以上です。







