EC2 + RDS 環境へインストールした WordPress 上で AWS Secrets Manager Agent 経由で DB 接続情報のシークレットを取得してみた
いわさです。
3 年近く前になりますが WordPress の DB 接続情報を AWS Secrets Manager から取得する方法を試したことがあります。
その際は取得出来なくはないのですが、wp-config.php モジュールがロードされる度に Secrets Manager API を呼び出す形となってしまい、AWS Secrets Manager のベストプラクティスから外れた状態となっていました。
解決策としては環境変数やストレージなど一時的な領域にシークレットを格納する方法が考えられますが、シークレットのローテーションに追従する仕組みなども考える必要があり、自前で色々考えるのはちょっと面倒だなぁと思っていました。
そんな中、先日 AWS Secrets Manager Agent というものが登場しました。
このエージェントは取得したシークレットをキャッシュし、TTL を自由に設定が出来るのでシークレットのローテーションにも追従しやすいです。
これを使うと冒頭の課題を解決出来るのではと思い、WordPress 側で取得して設定してみました。
WordPress をセットアップ
上記 WordPress の記事を参考にまずは検証環境を用意します。
CloudFormation テンプレートが用意されていましたのでこちらを使いましょう。WordPress のインストールまで行ってくれるので、すぐに WordPress のセットアップ画面から開始することが出来ます。3 年前の自分、えらい。
ただ、3 年前ということもあって Amazon Linux 2 + MariaDB 10.5.12 というちょっと古いバージョンが使われていました。
MariaDB 10.5.12 は、現在はもうデプロイ出来ないようなので、エンジンバージョンを 10.11.6 にだけアップデートしておきました。
ブログに従って MariaDB 側で Create Database だけすると次のようにセットアップを開始することが出来ました。DB 接続情報が Secrets Manager に格納されているのでコンソールからマニュアルで取得して WordPress 上へまずはポチポチと入力していきます。
セットアップが完了しました。デフォルトのサイトにアクセスすることも出来ました。
なお、この時点での WordPress 構成ファイルには次のように接続情報などがハードコーディングされています。
<?php
/**
* The base configuration for WordPress
*
* The wp-config.php creation script uses this file during the installation.
* You don't have to use the website, you can copy this file to "wp-config.php"
* and fill in the values.
*
* This file contains the following configurations:
*
* * Database settings
* * Secret keys
* * Database table prefix
* * ABSPATH
*
* @link https://wordpress.org/documentation/article/editing-wp-config-php/
*
* @package WordPress
*/
// ** Database settings - You can get this info from your web host ** //
/** The name of the database for WordPress */
define( 'DB_NAME', 'wordpress' );
/** Database username */
define( 'DB_USER', 'hoge' );
/** Database password */
define( 'DB_PASSWORD', '6XUK-9}fxO%E(e)f' );
/** Database hostname */
define( 'DB_HOST', 'hoge-rds.cpnu9ipu74g4.ap-northeast-1.rds.amazonaws.com' );
/** Database charset to use in creating database tables. */
define( 'DB_CHARSET', 'utf8mb4' );
/** The database collate type. Don't change this if in doubt. */
define( 'DB_COLLATE', '' );
:
Secrets Manager Agent 経由で取得するように変更
では、Secrets Manager Agent 経由で取得するように変更していきたいと思います。
Secrets Manager Agent のインストール
まずは EC2 上に Secretes Manager Agent をセットアップする必要がありますが、それは先日の記事と手順が同じなので省略します。dnf が yum になるくらいで、後は同じ感じです。
この時点でローカルホストへ HTTP リクエストを送信してみると...
[root@ip-10-0-0-86 html]# curl -H "X-Aws-Parameters-Secrets-Token: $(</var/run/awssmatoken)" 'http://localhost:2773/secretsmanager/get?secretId=hoge-secret' | jq
% Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed100 438 100 438 0 0 389k 0 --:--:-- --:--:-- --:--:-- 427k{
"ARN": "arn:aws:secretsmanager:ap-northeast-1:123456789012:secret:hoge-secret-IKn9tn",
"Name": "hoge-secret",
"VersionId": "624cbc68-d1fb-437b-95b6-fb642f675db5",
"SecretString": "{\"password\":\"6XUK-9}fxO%E(e)f\",\"engine\":\"mariadb\",\"port\":3306,\"dbInstanceIdentifier\":\"hoge-rds\",\"host\":\"hoge-rds.cpnu9ipu74g4.ap-northeast-1.rds.amazonaws.com\",\"username\":\"hoge\"}",
"VersionStages": [
"AWSCURRENT"
], "CreatedDate": "1720908102.569"
}
良いですね。取得出来ています。
wp-config.php の修正
ではこの仕組みを WordPress 上で使ってみます。
具体的には wp-config.php で Secrets Manager Agent に対してシークレット取得リクエストを送信してみます。
<?php
/**
* The base configuration for WordPress
*
* The wp-config.php creation script uses this file during the installation.
* You don't have to use the website, you can copy this file to "wp-config.php"
* and fill in the values.
*
* This file contains the following configurations:
*
* * Database settings
* * Secret keys
* * Database table prefix
* * ABSPATH
*
* @link https://wordpress.org/documentation/article/editing-wp-config-php/
*
* @package WordPress
*/
// ** Database settings - You can get this info from your web host ** //
$token = file_get_contents('/var/run/awssmatoken');
$response = file_get_contents('http://localhost:2773/secretsmanager/get?secretId=hoge-secret', false, stream_context_create([
'http' => [
'header' => "X-Aws-Parameters-Secrets-Token: $token"
]
]));
$secrets = json_decode($response, true);
$dbSettings = json_decode($secrets['SecretString'], true);
/** The name of the database for WordPress */
define( 'DB_NAME', 'wordpress' );
/** Database username */
//define( 'DB_USER', 'hoge' );
define( 'DB_USER', $dbSettings['username'] );
/** Database password */
//define( 'DB_PASSWORD', '6XUK-9}fxO%E(e)f' );
define( 'DB_PASSWORD', $dbSettings['password'] );
/** Database hostname */
//define( 'DB_HOST', 'hoge-rds.cpnu9ipu74g4.ap-northeast-1.rds.amazonaws.com' );
define( 'DB_HOST', $dbSettings['host'] );
/** Database charset to use in creating database tables. */
define( 'DB_CHARSET', 'utf8mb4' );
/** The database collate type. Don't change this if in doubt. */
define( 'DB_COLLATE', '' );
:
WordPress 画面にアクセスしてみると、無事表示することが出来ました。
ちなみにシークレットをローテーションした直後は次のようにデータベース接続エラーが発生しました。
TTL 値の調整やローテーションのタイミングは考える必要がありますね。
さいごに
本日は EC2 + RDS 環境へインストールした WordPress に対して AWS Secrets Manager Agent 経由でシークレットを取得してみました。
エージェント構成ファイルをあまりカスタマイズしていないので適切な設定にはまだなっていないですが、冒頭の記事のように直接シークレットを取得するよりも改善されていそうな気がしますね。負荷テストして評価してみたいところです。
SSRF 対策用のトークンをストレージから毎回取得してしまっているのでストレージ I/O が発生すると思いますが、これはどうなのだろうとちょっと思っています。これもインメモリに出来ないものか。