RDS ProxyとIAM認証を使ってLambdaからRDSにアクセスする

# ポイントまとめ

 - RDSProxyを利用する際は、RDSはパスワード認証に設定する。
 - RDSProxyのIAM認証を利用するLambdaでは、RDS generate-auth-db-tokenを利用して認証トークンを取得する。
 

# 概要
ワークロードでLambdaとRDSを組み合わせる場合、LambdaがRDSのコネクションを食いつぶしてしまうという理由から長らくアンチパターンとされてきました。
しかし、[RDS Proxy](https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/UserGuide/rds-proxy.html)ができてからはその弱点も解消され、採用されているユースケースも増えているのではないかと思います。
この記事ではLambda(Python)からIAM認証を使ってRDSProxy経由でRDSにアクセスできるように、RDSその他を設定する方法を紹介します。
ハンズオン的に利用してもらえると幸いです。

また、今回は長くなりすぎないようにネットワークの設定や踏み台用インスタンスの作成、RDSサブネットグループの作成については省略し、下図赤枠で囲われている部分の設定方法のみを紹介します。
![001.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/681482/c6338ff2-841f-708a-31ec-117c657b3d08.png)

# 実践
### 1. RDSの作成
なにはともあれRDSがないと始まりません。
RDSを作成していきましょう。
RDSのマネジメントコンソールから「データベースの作成」をクリックします。
![007.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/681482/4e945e90-4b95-a5e6-30b3-0c0597566ada.png)
今回はMySQLでハンズオンを進めるので、「MySQL」を選択します。
ちなみに、現時点(2021年12月時点)でRDS ProxyがサポートしているのはPostgreSQL及びMySQLのみです。
詳細は、[Amazon RDS Proxyの制約事項](https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/UserGuide/rds-proxy.html#rds-proxy.limits)をご確認ください。
![008.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/681482/76e6ea32-a6a4-7c1b-dea2-2aae03f33792.png)
通常のRDSを作成する時と同じように、
DBインスタンス識別子やマスターユーザー名、パスワードを入力します。
![010.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/681482/2875c4b9-ec19-2f69-8c4c-a1d09bcd9de9.png)
VPCやサブネットグループは概要に記載した図の通り、NATGWにルーティングされてインターネットへのアウトバウンド接続が可能なプライベートサブネット上に配置しましょう。
また、セキュリティグループはこの後少し設定を変更するので、とりあえずここでは「新規作成」を選択します。
![011.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/681482/c8ceb049-bf5e-9315-f9f8-0a12c655a355.png)
その他は特にデフォルトのままで問題ないのですが、一点だけ、今回RDS ProxyでIAM認証を使用するからといって、データベース認証を「パスワード認証」から変更しないようにしましょう。
RDS Proxyを挟む場合は、
Client⇒(IAM認証)⇒RDS Proxy⇒(パスワード認証)⇒RDS
という形にする必要があるので、RDSではIAM認証を有効にしません。
![012.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/681482/a4b2343e-8f1b-ce05-77c9-c6e849597820.png)
数分待つとRDSが作成できると思います。
![013.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/681482/bdecb0fb-fa18-e3a9-4b6d-4c6e4c30a8c8.png)
一つだけ、追加で設定変更をします。
先ほど新規作成した、RDSにアタッチされているセキュリティグループのインバウンドグループで、

|タイプ|プロトコル|ポート番号|ソース|
|---|---|---|---|
|MySQL/Aurora|TCP|3306|(編集しているセキュリティグループ自体のID)|
を追加します。
今回の記事では、このあと作成するRDS ProxyやLambdaなどに同じセキュリティグループをアタッチしていくので、セキュリティグループ内へのRDSアクセスを許可するためのものです。
![014.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/681482/bfcd0b40-4019-f117-91b3-d0615a9edd55.png)


### 2. データベースユーザの作成
次に、RDS ProxyやLambdaからアクセスするために使うMySQL上のユーザを作成します。
MySQLクライアントがインストールされている踏み台サーバにログインします。
踏み台サーバからRDSに対して、3306番のポートでアクセスできるようにセキュリティグループは設定しておくようにしましょう。
今回は、Amazon Linux 2 のEC2インスタンスを起動してMySQLクライアントを導入しました。
![027.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/681482/67ef18b3-b990-c24f-794f-d4929fbf582b.png)

下記コマンドを実行して、RDSに接続します。
コマンド実行後にはパスワードの入力を求められますので、RDS作成時に入力したパスワードを入力してください。

```
mysql -h <rdsのエンドポイント名> -P 3306 -u admin -p
```

RDSに接続できたら、下記コマンドを入力して、「proxy_user」という名前のMySQL上のユーザを作成します。
パスワードは、後でSecret Managerのローテーションを実行するので、覚えることができるものならなんでも構いません。

```
CREATE USER proxy_user@'%' IDENTIFIED BY 'password';
```

![015.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/681482/2ce1cdda-3765-b361-54ff-0c82df9e3dda.png)

また、ついでにテストで参照する用のデータベースも作っておきましょう。

```
CREATE DATABASE testdb;
CREATE TABLE testdb.testtable (id int, name varchar(10));
INSERT INTO testdb.testtable VALUES (1, 'Suzuki');
INSERT INTO testdb.testtable VALUES (33, 'Kikuchi');
INSERT INTO testdb.testtable VALUES (63, 'Nishikawa');
SELECT * FROM testdb.testtable;
```

![add_001.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/681482/4404e2e5-bcdf-8fb0-77fa-041636443bff.png)
![add_002.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/681482/9accea9b-83ed-a1cc-f281-a7841dcb3ed6.png)

最後に、テスト用のデータベースに対してユーザに権限を付与します。
今回は、データベースに対する全権限を付与しています。

```
GRANT ALL ON testdb.* TO proxy_user;
```

![016.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/681482/a1f65e37-5cb7-5c24-f4e9-6073769810ed.png)


### 3. シークレットの作成

RDS Proxyを使ってRDSに接続するためには、接続ユーザのパスワードをSecrets Managerで管理している必要があります。
Secrets Managerのマネジメントコンソールを開いて、「新しいシークレットを保存する」をクリックします。
![017.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/681482/bdc01402-ea5c-963f-b42b-8a4557a1af95.png)
シークレットのタイプは「Amazon RDS データベースの認証情報」を選択して、
「2.データベースユーザの作成」で作成したユーザ名、パスワードを入力しましょう。
また、データベースでは、「1.RDSの作成」で作成したインスタンスを指定します。
![018.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/681482/731a27de-747f-9888-6751-1f4490bc4958.png)
シークレットの名前を入力します。
![019.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/681482/2674fa9d-36d2-2613-3e54-b785b8a5de40.png)
次に、シークレットのローテーションで、「自動ローテーションを有効にする」を選択します。
Lambdaローテーション関数の名前を入力して、「次へ」をクリックします。
※今回は、ローテーションを有効にしていますが、ここは有効化しなくても利用はできます。
![020.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/681482/c06e32ab-cfd3-8bec-c46e-252ee81e21db.png)
「保存」をクリックします。
![020_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/681482/f32e7c3f-b3a1-57ec-0298-cc2abfcb60a9.png)


### 4. RDS Proxyの作成
では、いよいよRDS Proxyを作成していきます。
RDSのマネジメントコンソールの左側に、「プロキシ」という項目があるのでそれをクリックして、「プロキシを作成」をクリックすると、RDS Proxyの作成画面に進みます。
プロキシ識別子を入力して、エンジンの互換性で「MySQL」を選択します。
また、Transport Layer Securityが必要のチェックを忘れずにオンにします。
RDS ProxyでIAM認証を利用する場合、TLSの有効化が前提となっています。
![021.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/681482/6ce2a5d2-5af6-1a7b-fa2e-432c18f13333.png)
ターゲットグループの設定では、作成したRDSインスタンスを指定します。
![022.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/681482/5241332b-f11c-9f7f-39b5-d98d5c6bcd6f.png)
サブネットは、RDSインスタンスが所属するVPCのサブネットがすべて選択されるのでそのままでOKですが、
セキュリティグループはRDSインスタンス作成時に新規作成したセキュリティグループを指定しましょう。
設定できたら、「プロキシを作成」で作成します。
![024.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/681482/cd075567-fa18-6423-2fd0-31577a9e19dd.png)
ここまで問題なければ、プロキシが作成されます。
プロキシエンドポイントの二つのエンドポイントと、ターゲットグループのステータスが「利用可能」となるまで待ちましょう。
※私の場合は10分程度かかりました。
![026.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/681482/cd09e827-8c99-889b-dcd7-6ccaaf4b1ccc.png)

### 5. Lambdaの作成
proxyが作成できたので、接続するためのLamdbaを作成していきましょう。
Lambdaを作成するにあたって、必要となるIAM Policyを作成します。
まずは、RDS ProxyのプロキシURLの中の、prx-XXXXXXXXXXXXXXXXXとなっている部分をメモします。
![032.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/681482/e087f31f-ec1c-e22e-c9d1-4da80eef6b5c.png)

IAM Policyを作成します。
ポリシーの内容には、下記JSONステートメントを追加してください。

```
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "rds-db:connect"
            ],
            "Resource": [
                "arn:aws:rds-db:ap-northeast-1:<アカウントID>:dbuser:<RDSProxyのARNの末尾>/proxy_user"
            ]
        }
    ]
}
```

![033.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/681482/d24268c0-9325-e55c-359e-9f5c6909ddf3.png)
IAM Policyができました。
このポリシーは、後ほどLambdaを構築する際に自動作成されるIAM Roleにアタッチします。
![034.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/681482/057220e9-9550-fc16-ecb9-6a797b89f6be.png)
Lambdaコンソールから、「関数」-「関数の作成」と進みます。
![035.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/681482/762f733a-d5f4-9ab7-c6f6-ac6b69c810ca.png)
「一から作成」を選択し、任意の関数名を入力します。
ランタイムは、今回はPython3.8を選択しましょう。
実行ロールについては、「実行するLambdaアクセス権限で新しロールを作成」にします。
![036.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/681482/073974ad-b334-ce02-0678-f1b296dfede8.png)
ここで通常のLambda関数であれば作成を押してしまいがちですが、今回はRDSというVPC上のリソースにアクセスをしないといけないので、「詳細設定」の欄を展開して、ネットワークを有効化にチェックを入れます。
RDSインスタンスが置いてあるVPCを選択し、
サブネットではプライベートサブネットを選択します。
セキュリティグループではRDSインスタンスやRDS Proxyと同じセキュリティグループを指定しましょう。
ここまで設定出来たら、「関数の作成」をクリックします。
![037.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/681482/8617315c-43c4-610d-5a96-3b9fcacb3ad2.png)
Lambda関数が作成されます。
![038.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/681482/2380558f-5436-0022-fc49-46d50be0ea03.png)
今回はpythonを利用してMySQLにアクセスするために、PyMySQLというパッケージを利用します。
ただ、LambdaでPythonのパッケージを追加するのは少し面倒な手順を実施しなくてはいけません。
今回は検証なので、既にPyMySQLをインストール済みの関数をレイヤとして提供してくださっている先人の方々の恩恵にあやかることにします。
下記を利用させていただきました。
https://github.com/keithrozario/Klayers
画面を下のほうにスクロールして、「レイヤーの追加」をクリックします。
![039.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/681482/8f864113-683e-8efa-41ce-da5516126da1.png)

レイヤーソースで、「arnを指定」を選択して、下記をコピペしてください。
「検証」を押したあと、「追加」をクリックして、レイヤを追加します。
これでPyMySQLが利用できるようになりました。
arn:aws:lambda:ap-northeast-1:770693421928:layer:Klayers-python38-PyMySQL:4
![040.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/681482/2edda0cd-81db-2543-033a-21b5356a2dcb.png)
次に、RDS ProxyにIAM認証でアクセスするためには、SSL通信を行う必要があります。
RDS Proxy作成時にもそのような設定を入れました。
SSL通信を実行するために、証明書としてAmazonが提供している証明書を指定する必要があります。
コードソースの中の「File」から新規ファイルを作成し、AmazonRootCA1.pemというファイル名でSaveします。
![041.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/681482/e1b0dcdd-5940-90c7-ad56-1d9453d2ed13.png)
証明書の内容は下記にアクセスして、内容をそのまま先ほどのファイルにコピペしましょう。
https://www.amazontrust.com/repository/AmazonRootCA1.pem
![042.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/681482/53fdeb85-d88a-a0d8-70ce-07a621d983af.png)

ここまでで、LamdbaからRDS Proxyにアクセスするための事前準備がほぼ完了しています。
下記サンプルコードを「Lambda_function.py」の中にコピペしましょう。
その上で、6行目 HOST = "XXXXXXXXXXXXXXXXXXXX"
のXXXの部分を、ご自身のRDS Proxyのエンドポイント名に置き換えてみてください。

```python
import json
import sys
import boto3
import pymysql

HOST = "rds-proxy-kensyo001.proxy-chxhfnz5l2b1.ap-northeast-1.rds.amazonaws.com"
USER = "proxy_user"
PORT = 3306
REGION = "ap-northeast-1"

def lambda_handler(event, context):
    # TODO implement
    print ("start!!")
    client = boto3.client('rds')
    token = client.generate_db_auth_token(DBHostname=HOST, Port=PORT, DBUsername=USER, Region=REGION)
    
    print (token)

    try:
        conn = pymysql.connect(host=HOST, user=USER, passwd=token, db="testdb", connect_timeout=5, ssl={'ca': 'AmazonRootCA1.pem'})
    except Exception as e:
        print("error")
        print(e)

    with conn.cursor() as cur:
        cur.execute("SELECT * FROM testtable")
        for row in cur:
            print(row)

    return {
        'statusCode': 200,
        'body': json.dumps('Hello from Lambda!')
    }

```

![043.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/681482/251cf00d-c94a-c080-69e1-93f7da33f521.png)
ctrl+s で保存し、「Deploy」でLambdaをデプロイします。
![044.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/681482/0981a0f3-40d4-99b2-64cd-c21765009649.png)

ここまででほぼ終わりなのですが、もう少しだけ設定が必要になる部分があります。
Lamdbaの「設定」タブから、「アクセス権限」を選んで、実行ロールのロール名をクリックしましょう。
![045.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/681482/82a0f9e7-f5b5-439d-4dc8-6763caa9ac1a.png)
「ポリシーをアタッチします」をクリックします。
![046.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/681482/56985bd4-87c1-b61e-315e-92e1ef210b2b.png)
この章「5. Lamdbaの作成」の最初に作成したIAM Policyをアタッチします。
![047.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/681482/ee666ee3-cb0d-1a35-71fe-0dce0a3965b3.png)
やっと準備ができました。
Lambdaの画面に戻って、「Test」をクリックしましょう。
Testパターンはなんでもよいので、適当に任意の名前を付けて実行します。
すると、実行時のログが表示されますが、中身を見てみると作成したテーブルのアイテムが表示されていることがわかります。
![048.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/681482/aa79c6c5-369c-a188-baa9-22075ce4cd22.png)

Lambdaで RDS Proxy経由で IAM認証を使ってRDSへアクセスすることができました！
この記事が、皆様のAWSライフに少しでもお役に立てば幸いです。