目次
はじめに
こんにちは、omkです。
DBの認証情報どこに置くか問題ですが、良くないのはコードにベタ書きすることで、一般的なのは環境変数に置いておくことだと思っています。
今回はSecrets Managerに認証情報を置いておき、そこから取得することで認証情報の管理を楽にしていきます。
Secrets Managerではありがたいことにプログラムを介してシークレットを取得するためのサンプルコードがコンソールから確認出来ます。
早速PHPで利用しようと思ったのですが、
PHP無いんですね……
ということで自分で書いていきま~す。
やってみた
前提
環境情報
- PHP7.4
- Aurora3.0(MySQL8.0互換)
PHPが動作するEC2と、接続するためのAuroraを用意します。
EC2にはIAMロールでシークレットを取得する権限(SecretsManagerReadWrite)を与えておきます。
今回は東京リージョンで実施します。
シークレット作成
Auroraの認証情報を管理するシークレットを作成します。
作成方法はこちらに記載していますので合わせて見ていただけると幸いです。
今回はシークレットの名前を「omk-db/admin」としてAurora「omk-db」のユーザー「admin」の認証情報を取得できるようにしておきます。
Composerインストール
次にPHPサーバで「AWS SDK for PHP(以降SDK)」を利用できるようにします。
SDKをインストールするためにComposerを導入します。
Composerの導入手順は以下のページに従います。
https://getcomposer.org/download/
$ php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
$ php -r "if (hash_file('sha384', 'composer-setup.php') === '906a84df04cea2aa72f40b5f787e49f22d4c2f19492ac310e8cba5b96ac8b64115ac402c8cd292b8a03482574915d1a8') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;"
$ php composer-setup.php
$ php -r "unlink('composer-setup.php');"
Composerがインストールされたことを確認します。
$ composer -v
SDKをインストールします。
$ composer require aws/aws-sdk-php
SDKがインストールされたことを確認します。
$ cd vendor
$ tree -L 3
.
├── autoload.php
├── aws
│ ├── aws-crt-php
│ │ ├── builder.json
│ │ ├── CODE_OF_CONDUCT.md
│ │ ├── composer.json
│ │ ├── config.m4
│ │ ├── config.w32
│ │ ├── CONTRIBUTING.md
│ │ ├── crt
│ │ ├── ext
│ │ ├── format-check.sh
│ │ ├── gen_api.php
│ │ ├── gen_stub.php
│ │ ├── LICENSE
│ │ ├── Makefile.frag
│ │ ├── Makefile.frag.w32
│ │ ├── NOTICE
│ │ ├── package.xml-template_post
│ │ ├── package.xml-template_pre
│ │ ├── php.ini
│ │ ├── php-win.ini
│ │ ├── prepare_package_xml.sh
│ │ ├── prepare_release.sh
│ │ ├── README.md
│ │ ├── run_tests
│ │ ├── run_tests.bat
│ │ ├── src
│ │ └── tests
│ └── aws-sdk-php
│ ├── CODE_OF_CONDUCT.md
│ ├── composer.json
│ ├── CRT_INSTRUCTIONS.md
│ ├── LICENSE
│ ├── NOTICE
│ ├── src
│ └── THIRD-PARTY-LICENSES
(ry
これでSDKが使えるようになりました。
PHPのコード
<?php
require 'vendor/autoload.php';
#AWS SDK設定
$sharedConfig = [
'region' => 'ap-northeast-1',
'version' => 'latest'
];
$sdk = new Aws\Sdk($sharedConfig);
#クライアント定義
$client = $sdk->createSecretsManager();
#シークレット取得
try {
$result = $client->getSecretValue([
'SecretId' => 'arn:aws:secretsmanager:ap-northeast-1:『ACCOUNTID』:secret:omk-db/admin-EZdTPc'
]);
} catch (Exception $e) {
echo 'エラー: ', $e->getMessage(), "\n";
exit(500);
}
$secret = json_decode($result['SecretString'],true);
#テキトーに決めました
$dbname = 'testdb';
#シークレットから取得
$dbhost = $secret['host'];
$dbuser = $secret['username'];
$dbpass = $secret['password'];
$dsn = 'mysql:dbname=' . $dbname . ';host=' . $dbhost .";charset=utf8mb4";
try {
$pdo = new PDO(
$dsn,
$dbuser,
$dbpass,
[
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
]
);
} catch (Exception $e) {
echo 'エラー: ', $e->getMessage(), "\n";
exit(500);
}
#確認用にクエリを投げてみます
$sql = 'select * from testdb.testtable;';
$stmt = $pdo->prepare($sql);
$stmt->execute([]);
$result = $stmt->fetch();
#結果出力
var_dump($result);
?>
解説
各箇所で何をしているか軽く解説します。
AWS SDKを以下の記述で読み込みます。
require 'vendor/autoload.php';
SDKの設定を行い、Secrets Managerにアクセスしてシークレットを取得します。
#AWS SDK設定
$sharedConfig = [
'region' => 'ap-northeast-1',
'version' => 'latest'
];
$sdk = new Aws\Sdk($sharedConfig);
#クライアント定義
$client = $sdk->createSecretsManager();
#シークレット取得
try {
$result = $client->getSecretValue([
'SecretId' => 'arn:aws:secretsmanager:ap-northeast-1:『ACCOUNTID』:secret:omk-db/admin-EZdTPc'
]);
} catch (Exception $e) {
echo 'エラー: ', $e->getMessage(), "\n";
exit(500);
}
取得したシークレットはこんな感じで返って来ます。『』内は見せられないので隠しています。
{
"ARN": "arn:aws:secretsmanager:ap-northeast-1:『ACCOUNTID』:secret:omk-db\/admin-EZdTPc",
"Name": "omk-db\/admin",
"VersionId": "e65c0c54-f8bd-4508-9576-c9263b9fe529",
"SecretString": "{\"username\": \"admin\", \"password\": \"『PASSWORD』\", \"engine\": \"mysql\", \"host\": \"『DBENDPOINT』\", \"port\": 3306, \"dbClusterIdentifier\": \"omk-db\"}",
"VersionStages": [
"AWSCURRENT",
"AWSPENDING"
],
"CreatedDate": "2022-01-06T09:29:58+00:00",
"@metadata": {
"statusCode": 200,
"effectiveUri": "https:\/\/secretsmanager.ap-northeast-1.amazonaws.com",
"headers": {
"x-amzn-requestid": "『ID』",
"content-type": "application\/x-amz-json-1.1",
"content-length": "482",
"date": "Thu, 06 Jan 2022 10:55:18 GMT"
},
"transferStats": {
"http": [
[]
]
}
}
}
よって「SecretString」の中身からDBの認証情報を取得します。
ここではJSONから配列にデコードして取り扱っています。
$secret = json_decode($result['SecretString'],true);
#テキトーに決めました
$dbname = 'testdb';
#シークレットから取得
$dbhost = $secret['host'];
$dbuser = $secret['username'];
$dbpass = $secret['password'];
あとは普通にPDOするだけです(dsnの書き方はこれが普通なのか疑問ですが)。
では、確認していきましょう。
確認
事前にtestdb.testtableにデータを仕込んでおきました。
MySQL [testdb]> select * from testdb.testtable;
+------+------+
| ID | Name |
+------+------+
| 0 | hoge |
+------+------+
1 row in set (0.001 sec)
PHPのプログラムからこれを取得できることを確認します。
$ php pdo.php
array(2) {
["ID"]=>
string(1) "0"
["Name"]=>
string(4) "hoge"
}
無事値が取れましたのでSecrets Managerから取得した認証情報でログイン出来ていることがわかりました。
おわりに
パスワードの自動ローテーションと合わせて利用すればコードやサーバ側での修正が必要なくパスワードを変更できますし、
環境が複数ある場合にはシークレットの名前に環境名を入れておけば環境変数からスムーズに個別に認証情報を取得できたりと工夫次第でかなり便利ですね。
以上、ありがとうございました。
アーキテクト課のomkです。
AWSについて雑多に取り組んだ内容を発信しています!!