AWS JDBC Driver for PostgreSQL がリリースされたのでローカルホスト Web アプリケーションから Secrets Manager に格納した認証情報を使ってみた

2022.10.13

いわさです。

先日のアップデート情報で AWS JDBC Driver for PostgreSQL なるものが紹介されていました。

JDBC ドライバーのラッパーとして機能しつついくつかの独自プラグイン機能で便利な機能を使うことが出来ます。

一番メインとして紹介されていた高速フェイルオーバーを評価中です。ただどうもうまく DNS 切り替わりの影響を受け続けている感じがします。
もう少し調査出来たらまとめようと思っています。

このラッパーは高速フェイルオーバー以外にもいくつか便利機能があります。 本日はその中の一つ、AWS Secrets Manager に格納した RDS への接続情報を使いつつ AWS JDBC Driver for PsotgreSQL の導入方法を紹介できればと思っています。

ベースとなる Web アプリケーション

まずはベースとなるアプリケーションを用意します。
JDBC を使ってデータベースへ接続するシンプルなアプリケーションを用意し、そこに AWS JDBC Driver for PostgreSQL を導入してみましょう。

Spring Boot Initializer で単純な Web アプリケーションを用意します。
Gradle か Maven どちらでも良いですが、今回は Maven を使いました。

まず AWS JDBC Driver for PostgreSQL はorg.postgresql:postgresqlに依存しているので、こちらを使うデータベース接続処理を作成してみたいと思います。

pom.xml に dependency を追加し、適当なコントローラーを新規作成します。
かなり雑ですが接続情報などはハードコードしていて、対象の Aurora for PostgreSQL のパブリッククラスターが実行されている状態です。

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.7.4</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.tak1wa.devio</groupId>
	<artifactId>hoge1013maven</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>hoge1013maven</name>
	<description>Demo project for Spring Boot</description>
	<properties>
		<java.version>17</java.version>
	</properties>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>

		<dependency>
			<groupId>org.postgresql</groupId>
			<artifactId>postgresql</artifactId>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>
</project>

HogeController.java

package com.tak1wa.devio.hoge1013maven;

import java.sql.*;
import java.util.Properties;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HogeController {

    private static final String CONNECTION_STRING =  "jdbc:postgresql://hoge1011aurorapostgres.cluster-cpnu9ipu74g4.ap-northeast-1.rds.amazonaws.com:5432/hoge";
    private static final String USERNAME = "postgres";
    private static final String PASSWORD = "password";
    
    @GetMapping("/healthcheck")
    public String index() throws SQLException {
        final Properties properties = new Properties();
        properties.setProperty("user", USERNAME);
        properties.setProperty("password", PASSWORD);

        try (Connection conn = DriverManager.getConnection(CONNECTION_STRING, properties);
                Statement stmt = conn.createStatement();
                ResultSet rs = stmt.executeQuery("select inet_server_addr() as hoge;")) {
            rs.next();
            return rs.getString("hoge") + "\n";
        }
    }
}

ポイントとしては後述の AWS JDBC Driver for PostgreSQL では Connection か DataSource が利用出来るので JdbcTemplate でも利用出来るかと思います。今回は Connection を使っています。

データベースインスタンスのプライベート IP アドレスを取得するクエリを発行しています。
元々フェイルオーバーを確認するために用意したものなのでこんな感じになっています。

こちらを実行して、リクエストを送信してみましょう。

% curl http://localhost:8080/healthcheck
172.31.20.72

取得出来ました。

AWS JDBC Driver for PostgreSQL と Secrets Manager Connection Plugin を導入

さてここから AWS JDBC Driver for PostgreSQL を使うように改造していきましょう。
以下はライブラリのリポジトリです。基本的にこちらに実装方法などの情報がまとまっているので参考にするのが良いでしょう。

Secrets Manager Connection Plugin については以下に記述があります。
ラッパーを実装しつつプラグインを指定することで拡張機能が利用出来るようなるようなイメージですね。

フェイルオーバー機能を使う時は不要ですが Secrets Manager を利用する場合には依存ライブラリの software.amazon.awssdk/secretsmanager の導入が必要です。
この記事ではその実装も含んでいます。

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.7.4</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.tak1wa.devio</groupId>
	<artifactId>hoge1013maven</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>hoge1013maven</name>
	<description>Demo project for Spring Boot</description>
	<properties>
		<java.version>17</java.version>
	</properties>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>

		<dependency>
			<groupId>org.postgresql</groupId>
			<artifactId>postgresql</artifactId>
		</dependency>

		<dependency>
			<groupId>software.amazon.jdbc</groupId>
			<artifactId>aws-advanced-jdbc-wrapper</artifactId>
			<version>1.0.0</version>
		</dependency>

		<dependency>
			<groupId>software.amazon.awssdk</groupId>
			<artifactId>secretsmanager</artifactId>
			<version>2.17.290</version>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

HogeController.java

package com.tak1wa.devio.hoge1013maven;

import java.sql.*;
import java.util.Properties;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HogeController {

    private static final String CONNECTION_STRING =  "jdbc:aws-wrapper:postgresql://hoge1011aurorapostgres.cluster-cpnu9ipu74g4.ap-northeast-1.rds.amazonaws.com:5432/hoge";
    
    @GetMapping("/healthcheck")
    public String index() throws SQLException {
        final Properties properties = new Properties();
        properties.setProperty("wrapperPlugins", "awsSecretsManager");
        properties.setProperty("secretsManagerRegion", "ap-northeast-1");
        properties.setProperty("secretsManagerSecretId", "hoge1013aurora");

        try (Connection conn = DriverManager.getConnection(CONNECTION_STRING, properties);
                Statement stmt = conn.createStatement();
                ResultSet rs = stmt.executeQuery("select inet_server_addr() as hoge;")) {
            rs.next();
            return rs.getString("hoge") + "\n";
        }
    }
}

上記ハイライト部分がポイントです。

まず接続文字列ですがjdbc:aws-wrapper:postgresqlという形式でサブプロトコルを追加しています。

そして、wrapperPluginsで Secrets Manager 機能を有効化しています。
プラグインは複数指定することが可能です。

さらに Secrets Manager 機能を使う場合にはsecretsManagerRegionsecretsManagerSecretIdを指定します。
リージョンはそのままですが、SecretId はシークレット名かシークレット ARN を指定します。
先程までハードコーディングされていたユーザー名とパスワードが排除されていますね。

Secrets Manager 自体は通常どおり RDS の認証情報を指定する形で以下のようになっています。
また、RDS についても通常のパスワード認証が設定された Aurora for PostgreSQL です。

ラッパーがうまいことやってシークレットを取得してくれるのですが、前提としてクライアントがシークレットマネージャーへアクセスするための IAM ポリシーが付与されている必要があります。
AWS で稼働させるのであればインスタンスプロファイルやサービスの実行ロール、ローカルホストを含むオンプレミスで実行する場合はプロファイル情報を確認しておきましょう。

権限が問題なければ先程と同じようにデータベースへのアクセスが確認出来るはずです。

% curl http://localhost:8080/healthcheck
172.31.20.72

簡単に Secrets Manager を使った認証が出来るようになりました。

さいごに

本日は AWS JDBC Driver for PostgreSQL でローカルホスト Web アプリケーションから Secrets Manager に格納した認証情報を使ってみました。

Secrets Manager だけでもなかなか良い機能ですね、他にも良い機能がいくつかありましたよ。
本丸のフェイルオーバー周りをどうにか評価終わらせて紹介できればと思っていますが。