Amazon RDS マルチ AZ 配置(2つの読み取り可能なスタンバイ)IAM データベース認証

# はじめに
こんにちは。株式会社ジールの@r_sasakiです。
今回は「Amazon RDS マルチ AZ 配置(2つの読み取り可能なスタンバイ)IAM データベース認証」についての検証結果をご紹介します。
# 1. 背景・目的
### 1.1. 背景
　2024年10月にAmazon RDSで、2つの読み取り可能なスタンバイを備えた Amazon RDS マルチAZ配置で、AWS IAM データベース認証がサポート開始されました。
　以前は、複数AZそれぞれでシングルAZ構成のRDSインスタンスを立てて、ロードバランサーでアクセスを振り分ける必要があったが、このアップデートにより、IAM データベース認証を利用することでEC2、Lambda等からRDSにアクセスする際、パスワードを利用した認証が不要となりました。
### 1.2. 目的
・Amazon RDS マルチ AZ 配置(2つの読み取り可能なスタンバイ)でのIAM データベース認証の設定方法を紹介する。
・マルチ AZ 配置のAmazon RDSインスタンス(2つの読み取り可能なスタンバイ)の構成で、既存のシングルスタンバイ構成と同様に、IAMデータベース認証が利用できるかを検証する。
・これらのサービスの解説と検証を通して、Amazon RDSを業務で利用する開発者やAWS導入企業担当者に対して、本アップデートに関連するサービスへの理解を深められるような発信をする。

# 2. アップデート関連サービス概要
### 2.1. Amazon RDS マルチ AZ 配置の概要
・プライマリDBインスタンスとは別のAZに、スタンバイDBインスタンスを持つDB構成
・スタンバイDBインスタンスはプライマリでの障害発生時の自動フェイルオーバーを提供し、DBインスタンスの可用性を向上させることができる
・スタンバイはホットスタンバイだが、アクセス不可でパフォーマンスには寄与しないため、パフォーマンス向上のためにはリードレプリカの設定が必要
### 2.2. 2つの読み取り可能なスタンバイを備えた Amazon RDS マルチAZ配置の特徴
・マルチAZ配置のスタンバイ数は原則1であるが、2つの読み取り可能なスタンバイを備えたマルチAZ配置を設定することもできる
・スタンバイが2つあることでフェイルオーバー時の可用性をより向上させるだけでなく、スタンバイを読み取り専用インスタンスとして利用できるためパフォーマンス向上が可能となる
・スタンバイはホットスタンバイであり読み取りアクセス可能のため、リードレプリカを設定することなく可用性とパフォーマンス向上を両立できる
### 2.3. IAM データベース認証の特徴
・DBへの接続にはユーザIDとパスワードの組み合わせが必要である
・IAMデータベース認証は、IAMユーザやIAMロールへのDBへの接続設定を事前にすることで、パスワードの代わりに一時的な認証トークンを利用してのDB接続を可能にする仕組み
・DB接続が必要なEC2インスタンスやLambda等のAWSサービスにパスワードを持たせる必要がないため、パスワード漏洩、パスワードのメンテ漏れ等のリスクを低減させることができる
# 3. 検証概要
### 3.1. 構成図
Lambda関数に、IAMデータベース認証を設定したロールをアタッチして、Amazon RDSにアクセス
※VPCは、デフォルトVPCを使用する
※Private subnet上にDBインスタンスを作成し、同subnet内にLambda関数を配置
構成図は以下の通りです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3884846/ecfce5ab-ad5f-4ce4-af43-defafcc35b6d.png)
### 3.2. コスト
|利用機能|料金|
|---|---|
|RDS 2つ読み取り可能なマルチAZ配置|$0.57/h|
|lambda|$0.2/100万リクエスト|
|Amzon VPC|$0.062/h  <br>※本検証は無料枠内で利用|

今回検証する、2つ読み取り可能なスタンバイを持つマルチAZ配置ではインスタンス数×インスタンスタイプで利用料金が求められます。また、2つ読み取り可能なスタンバイを持つマルチAZ配置では利用停止が不可能であり、利用を止めたい場合は「削除」する必要があります。

# 4. 検証
### 4.1. 環境構築
#### 4.1.1. サブネット作成
「ルートテーブル」を作成
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3884846/80281db3-412f-4837-bcbd-59cb91faf793.png)
デフォルトVPCを選択して、任意の名前でルートテーブルを作成
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3884846/9f48e3fc-5b07-4f19-b8e7-2665f87a7c57.png)
ルートテーブル作成完了
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3884846/55ff069f-9760-4e63-91c0-c69b4c1b0da9.png)
デフォルトVPCを選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3884846/882fb2a7-2ca2-4931-aa18-37cbd9d5fc12.png)
スクロールしてサブネットの設定値を埋めていく
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3884846/c45cf9c6-6f29-459a-956c-9b9dc66d26d9.png)
サブネットを作成
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3884846/c2f484e7-4291-46e0-8dc1-2c83dd4a98fc.png)
サブネット作成完了
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3884846/c2e9b80d-04bc-4ea7-bcfb-ea6a8b0f61dc.png)
アクション>ルートテーブルとの関連付けを編集
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3884846/779c4961-19cf-43fc-9403-14a1f958ff70.png)
先ほど作成したルートテーブルを選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3884846/cc6caa5e-0253-4e8e-a5f5-c282a0dad5d9.png)
紐づけの編集が完了
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3884846/616ff8d6-97cf-498e-ab5e-15bfd2ea1573.png)
「ルートテーブル」タブに「0.0.0.0/0」が無く、VPC内に閉じた状態(=プライベート)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3884846/a1bbed2b-82c7-4464-a4d2-d4e88cbd534b.png)
今回はマルチAZ配置、スタンバイインスタンス2つを選択するため追加でAZ2つ分のサブネットを作成する必要あり
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3884846/9554baaa-a7ef-477b-acfc-3fb677dae9d5.png)
RDSインスタンスで利用できるようにサブネットグループを作成
RDS>サブネットグループ>DB サブネットグループの作成　をクリック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3884846/ea58078f-c555-429e-8976-6060655a3567.png)
サブネットグループの詳細は任意の値を選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3884846/8f8ce5d0-2dbc-41de-9cea-6bdf1ef66c84.png)
AZを複数選択、サブネットも同様に複数選択して「作成」
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3884846/afdc3f28-5e95-4b4a-88a5-00eed1c3b424.png)
サブネットグループ作成完了
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3884846/df1f86fb-8604-4ea3-85ee-116d37318ee8.png)
#### 4.1.2. RDSインスタンス作成
RDS>データベース画面から「データベースの作成」をクリック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3884846/74fcf5ef-586d-4b24-8261-2fce930bfb99.png)
データベース作成方法を選択：標準作成、エンジンのオプション：MySQL
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3884846/17ab9dbe-d258-4c87-8036-61cb768ff539.png)
任意のバージョン、テンプレートを選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3884846/415c8fd2-e609-4703-88f7-0c9c55aa3320.png)
可用性と耐久性：マルチAZ DBクラスター　を選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3884846/10421063-2f08-4f13-a13c-1a8d1adf29c4.png)
以降、以下の画像の通りに設定を行う。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3884846/ad331bc3-52ed-4100-9e30-4d241b409754.png)
作成完了
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3884846/e9eb8c55-6b9c-4f11-8bda-92c257c25497.png)
#### 4.1.3. IAMロールの作成
AmazonRDSDataFullAccess、AWSLambdaBasicExecutionRole、AWSLambdaVPCAccessExecutionRoleを持っているロールを作成し、下記画像のようにカスタムポリシーをアタッチします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3884846/a3905984-a471-4719-8afb-77514c228c18.png)
カスタムポリシーでは、作成したデータベースに対して、検証用ユーザーからのアクセスを許可する内容となっています。
### 4.2. 検証用lambda関数の設定
#### 4.2.1. lambda関数を作成
「関数名」を記載し、「ランタイム」で「Python 3.13」を選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3884846/a1cb3b4e-262d-4e68-88b9-5118d51356d2.png)
コードを記載し、「Deploy」を押下
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3884846/d1e09d2e-b0eb-4e74-820f-826a8448a86f.png)
コードは以下の通り
```python:
import pymysql
import boto3
import os

def lambda_handler(event, context):
    # RDSインスタンスの情報
    ENDPOINT = "rds-multi-cluster-test-instance-1.cl3jq2kymj5z.ap-northeast-1.rds.amazonaws.com"
    PORT = 3306
    USER = "rds-multi-user1"
    REGION = "ap-northeast-1"
    DBNAME = "mydb"
    SSLCERTIFICATE = "/var/task/ap-northeast-1-bundle.pem"

    # 環境変数の設定
    os.environ['LIBMYSQL_ENABLE_CLEARTEXT_PLUGIN'] = '1'

    # Boto3クライアントの作成
    client = boto3.client('rds', region_name=REGION)

    # 認証トークンの生成
    token = client.generate_db_auth_token(DBHostname=ENDPOINT, Port=PORT, DBUsername=USER, Region=REGION)
    print("token = "+ token)
    # PyMySQLで接続
    try:
        conn = pymysql.connect(
            host=ENDPOINT,
            user=USER,
            password=token,
            port=PORT,
            database=DBNAME,
            ssl={'ca': SSLCERTIFICATE},
            auth_plugin_map={'mysql_clear_password': None}
        )
        print("SUCCESS")
        cur = conn.cursor()
        cur.execute("SELECT 1")
        query_results = cur.fetchall()
        print(query_results)
        return {
            'statusCode': 200,
            'body': query_results
        }
    except Exception as e:
        print(f"Database connection failed due to {e}")
        return {
            'statusCode': 500,
            'body': f"Database connection failed due to {e}"
        }
```
lambda関数の「New File」に各リージョンに適合するSSL証明書をダウンロードし、アップロードする。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3884846/6212b1f3-4e75-4610-9419-7245217d24d8.png)
#### 4.2.2. レイヤーの設定
pipコマンドを使用してpymysqlを所定ディレクトリにインストールする
その後、インストールした「python」フォルダをzip化する​
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3884846/afc96ae2-15ca-4126-b731-4735a03ff45d.png)
「レイヤー」の作成（「pymysql」を使用可能にする）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3884846/0d56dffa-0dc0-4c58-80f1-34db4d5e4909.png)
「名前」を記載し、「ファイルを選択」から先ほど作成したzipファイルを指定
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3884846/67e8768b-f521-43f1-8944-bc63d903c804.png)
「互換性のあるアーキテクチャ」、「互換性のあるランタイム」をlambda関数作成時と同様の値に設定し「作成」を押下
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3884846/e5e14665-0483-45d4-91f1-7b226962b392.png)
作成したlambda関数を再度開き、「レイヤーの追加」を押下
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3884846/723f4af8-20eb-48b2-9750-e9e713e76b31.png)
「カスタムレイヤー」を選択し、先ほど作成したレイヤーとバージョンを選択
※バージョンはデフォルトで「1」
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3884846/0b782a81-351b-4618-b109-9a72927a7cca.png)
「追加」を押下
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3884846/c9feb57d-12d3-48b6-9e7a-f25fe6e0e2d4.png)
作成したレイヤーが追加されていれば設定完了
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3884846/b6c55c71-814f-42d6-8be8-195db2863785.png)
#### 4.2.3. VPCの設定（lambda関数）
Lambda関数編集画面から「設定」→「VPC」→「編集」を押下
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3884846/f0306d2f-b12c-4161-8fb5-1f79a56f8ac9.png)
RDSクラスターと同様のVPCを選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3884846/11d71e01-6480-4561-9bf3-10d7fc28c0f8.png)
「サブネット」、「セキュリティグループ」​も同じくRDSクラスターと同様に設定
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3884846/ef04257d-c306-4d9d-89b5-a760ed736497.png)
「保存」を押下
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3884846/e7a1b33c-ffc5-497b-b309-655e0253ca61.png)
### 4.3. 検証実施・結果
#### 検証実施（資材作成）
前提として、資材作成時にはマスターユーザを利用し、ログイン方法は環境構築時に設定したパスワードを使用します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3884846/09bbc893-619f-42f7-87bc-90dd8511e4ff.png)
作成したRDSインスタンス内にデータベースを作成する。
```python:
CREATE DATABASE mydb;
```
次に、作成したDBにアクセスするための検証ユーザーを作成
※この時、IAMデータベース認証を用いてアクセスするユーザーとして作成
```python:
CREATE USER demouser IDENTIFIED WITH AWSAuthenticationPlugin as 'RDS'
```
ユーザー作成が完了後、ユーザーがデータベースに対して操作を行える権限を付与
```python:
cur.execute("GRANT ALL PRIVILEGES ON mydb.* TO 'rds-multi-user1' @'%';");
```

#### 検証実施（実行結果）
環境構築時に作成したlambda関数を実行します。
また、今回の検証で用いたlambda関数はアクセスできた場合に「1」を返すプログラムとなっています。
以下、実行結果になります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3884846/a88b4bc5-e63f-4e6d-8810-75cb0412b10b.png)
実際に「1」が返ってくることが確認できたため、IAMデータベース認証を用いて特定のデータベースにアクセスできることが分かりました。
# 所感
　lambda関数から2つの読み取り可能なスタンバイを持つRDSマルチAZ配置のデータベースにIAMデータベース認証を持ちいてアクセスすることが確認できました。
　このことから、可用性とパフォーマンスを担保した上で、セキュリティの向上が以前よりも容易に設定することができるようになりました。
# 参考まとめ
・[検証対象アップデート情報](https://aws.amazon.com/jp/about-aws/whats-new/2024/10/amazon-rds-multi-az-deployment-standbys-aws-iam-authentication/)
・[Amazon RDS マルチ AZ](https://aws.amazon.com/jp/rds/features/multi-az/)
・[Amazon RDSのIAMデータベース認証について](https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/UserGuide/UsingWithRDS.IAMDBAuth.html)
・[Amazon RDS でのデータベース認証の種類について](https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/UserGuide/database-authentication.html)
・[IAM 認証を使用した DB インスタンスへの接続方法(公式)](https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/UserGuide/UsingWithRDS.IAMDBAuth.Connecting.html)
・[2つの読み取り可能なスタンバイでのIAMデータベース認証設定方法(classmethod)](https://dev.classmethod.jp/articles/amazon-rds-multi-az-cluster-supports-iam-database-auth/)
・[RDSにIAMデータベース認証でログイン(サーバーワークス)](https://blog.serverworks.co.jp/rds-iamdblogin)
・[LambdaでのIAMデータベース認証参考記事](https://qiita.com/ist-m-k/items/ee594d8d30e04f7a5b38)
・[Lambda→RDSへの接続方法参考記事](https://qiita.com/kobayashim_21/items/d8bb6608d1343770b099)
・[pymysqlを使用したRDSを使用したAWSLambda](https://ichi.pro/pymysql-o-shiyoshita-rds-o-shiyoshita-awslambda-39226957514440)
・[料金 - Amazon RDS | AWS](https://aws.amazon.com/jp/rds/pricing/)