Amazon-RDS

AWS SDK for PHPでAuroraにIAM DB認証を使ってPDOしてみた

はじめに

こんにちは、omkです。
前回はSecrets Managerで認証情報を取得してAuroraにログインしましたが、今度はIAM DB認証で認証してログインしてみようと思います。

これはDBユーザーへのログインをIAM権限で認証する機能で、これを使うことでパスワードを利用せずにログイン出来ます(トークンを発行します)。
IAM権限でアクセスを制御出来るので管理が楽です。

やってみた

ユーザーガイドを見ながら進めます。
https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/AuroraUserGuide/UsingWithRDS.IAMDBAuth.html

前提

環境情報

  • PHP7.4
  • Aurora3.0(MySQL8.0互換)

PHPが動作するEC2インスタンスを用意します。
PHPサーバにAWS SDK for PHPをインストールします。インストール手順はこちらの最初の方をご覧ください。
IAMロールは後で説明します。

Aurora作成

Auroraでは「パスワードと IAM データベース認証」を有効にします。有効にしたところで特に詳細設定も何も出ないですがそのまま作って大丈夫です。

IAMロール作成

まずポリシーを作成します。
必要な権限は以下です。リソースや条件は適宜絞ってください。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": "rds-db:connect",
            "Resource": "*"
        }
    ]
}

ビジュアルエディタで作成するときは「RDS IAM Authentication」で「アクセス権限の管理」から「connect」を選択します(APIが1つしかないのでサービスのフル権限になります)。

このポリシーをEC2用のロールに割り当ててロールをEC2に割り当てます。

DBのユーザー作成

Auroraにログインユーザーを作成します。
DBにログインしてIAMDB認証でのログイン先ユーザーを作成します。

CREATE USER {USERNAME}@{HOST} IDENTIFIED WITH AWSAuthenticationPlugin AS 'RDS';

ついでに確認用DBも作りました。

MySQL [testdb]> select * from testdb.testtable;
+------+------+
| ID   | Name |
+------+------+
|    0 | hoge |
+------+------+
1 row in set (0.002 sec)

作成したユーザーにこのDBに対する権限をGRANTします。

SSL証明書の配置

IAM DB認証を用いてのAuroraへのログインにはTLSでの通信が必須みたいです。
↓こちらから必要な証明書を取得してPHPサーバに配置しましょう。
https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/AuroraUserGuide/UsingWithRDS.SSL.html

PHPのコード

さて、準備が整ったのでPHPのコードを記述します。

<?php

require 'vendor/autoload.php';

use Aws\Rds\AuthTokenGenerator;
use Aws\Credentials\CredentialProvider;

$provider = CredentialProvider::defaultProvider();
$generator = new Aws\Rds\AuthTokenGenerator($provider);

#必要情報を設定
$dbhost = getenv('DB_HOST');
$dbport = '3306';
$endpoint = $dbhost . ':' . $dbport;

$dbregion = 'ap-northeast-1';
$dbuser = getenv('DB_USER');

#トークン発行
try {
    $token = $generator->createToken(
        $endpoint,
        $dbregion,
        $dbuser
    );
} catch (Exception $e) {
    echo 'エラー: ',  $e->getMessage(), "\n";
    exit(500);
}

#テキトーに決めました
$dbname = 'testdb';
$tablename = 'testtable';

$dsn = 'mysql:dbname=' . $dbname . ';host=' . $dbhost .";charset=utf8mb4";

try {
    $pdo = new PDO(
        $dsn,
        $dbuser,
        $token,
        [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
            PDO::MYSQL_ATTR_SSL_CA => __DIR__ . '/ap-northeast-1-bundle.pem'
        ]
    );
} 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);

?>

解説

コードの内容を解説していきます。

必要なクラスを宣言します。
IAMロールを利用するので鍵は割り当てずにデフォルトのプロバイダーを利用します。

require 'vendor/autoload.php';

use Aws\Rds\AuthTokenGenerator;
use Aws\Credentials\CredentialProvider;

$provider = CredentialProvider::defaultProvider();
$generator = new Aws\Rds\AuthTokenGenerator($provider);

DBの情報を設定してトークンを発行します。
Auroraのエンドポイントと利用するユーザーは環境変数に設定しておきました。

#必要情報を設定
$dbhost = getenv('DB_HOST');
$dbport = '3306';
$endpoint = $dbhost . ':' . $dbport;

$dbregion = 'ap-northeast-1';
$dbuser = getenv('DB_USER');

#トークン発行
try {
    $token = $generator->createToken(
        $endpoint,
        $dbregion,
        $dbuser
    );
} catch (Exception $e) {
    echo 'エラー: ',  $e->getMessage(), "\n";
    exit(500);
}

最後に取得したトークンを利用してPDOします。
SSL証明書の指定を忘れずにしておきます。

#テキトーに決めました
$dbname = 'testdb';
$tablename = 'testtable';

$dsn = 'mysql:dbname=' . $dbname . ';host=' . $dbhost .";charset=utf8mb4";

try {
    $pdo = new PDO(
        $dsn,
        $dbuser,
        $token,
        [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
            PDO::MYSQL_ATTR_SSL_CA => __DIR__ . '/ap-northeast-1-bundle.pem'
        ]
    );
} 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);

確認

コードを実行します。

正しい結果が返ってきたので正常に機能していることがわかります。

$ php pdo2.php
array(2) {
  ["ID"]=>
  string(1) "0"
  ["Name"]=>
  string(4) "hoge"
}

おわりに

IAMの権限でアクセス出来るようになりました。
パスワードを管理せず確実にIAM権限を利用できる範囲でのみDBにアクセス出来るので便利です。
一方でIAM権限をより厳格に管理しないと誰でもアクセス出来る状況に陥ることも考慮されますので注意が必要だとも感じます。

以上、ありがとうございました。

返信を残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA