Lambda - RDS Proxy(TLSあり) - Auroraの構成でRDS（Aurora）に接続する

## はじめに
Lambda→RDS Proxy→RDSの接続をやってみたのでその備忘録を残す。

全体の構成イメージは以下。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372684/41017c28-5f6c-795c-97ec-b64ba0a46393.png)

※料金について、Auroraはインスタンスの起動時間で料金が発生するので特に注意。Auroraではなく、MySQLとかにすれば無料枠があるのでそちらの方がいいかもしれない。

## Auoraのインスタンスを作成
以下の箇所以外はデフォルトのまま設定。

| 設定項目 | 画像 |
|:-:|:-:|
| テンプレート | ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372684/f92c1631-3a07-bcca-02c3-b82922eb7e3f.png) |
| インスタンスの設定 | ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372684/aa15f3c8-4d17-27a7-1fc3-fa40e5249822.png) |

※RDS（Aurora）のインスタンスを作成するために、また、RDS Proxyの設定のための前提条件（[Setting up network prerequisites](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/rds-proxy-setup.html#rds-proxy-network-prereqs)）として、VPCが必要だがこれはデフォルトのものを利用した。

> Using RDS Proxy requires you to have a common virtual private cloud (VPC) between your Aurora DB cluster or RDS DB instance and RDS Proxy. This VPC should have a minimum of two subnets that are in different Availability Zones.（RDS Proxyを使用するには、Aurora DBクラスタまたはRDS DBインスタンスとRDS Proxyの間に共通の仮想プライベートクラウド(VPC)を用意する必要があります。このVPCは、異なるAvailability Zoneにある最低2つのサブネットを持っている必要があります。）

## Secrets Managerで認証情報を作成
[AWS Secrets Manager でのデータベース認証情報の設定](https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/AuroraUserGuide/rds-proxy-setup.html#rds-proxy-secrets-arns)を参考に、Secrets Managerにユーザー名とパスワードの認証情報のセットを保存する。これはRDS ProxyがRDSにアクセスする際の認証情報になる。

| ステップ | 画像 |
|:-:|:-:|
| シークレットのタイプを選択  | ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372684/e52f8845-8504-d585-2af4-311aec954dc7.png) |
| シークレットを設定 | ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372684/901b173c-7287-9a96-e288-3f8159e84fc3.png) |

上記の設定で暗号化キーはデフォルトにしているが、これを自分で生成したものにするとキー1つにつき`1$/月`かかるっぽいので注意。また、ローテーションの設定は今回はしない設定にした。

一応シークレットに正しい情報を格納できている事を確認してみると、以下のようにちゃんとRDS用のシークレットが作成できている事が確認できる（今回はRDS　Proxyを使った接続のためだけに作ったので、パスワードはマスクしてない）。

```console
[study@localhost .aws]$ aws secretsmanager get-secret-value --secret-id dev/db/mysql
{
    "ARN": "arn:aws:secretsmanager:ap-northeast-1:**********:secret:dev/db/mysql-azibNI",
    "Name": "dev/db/mysql",
    "VersionId": "07181751-2208-4048-aa6a-36616c123fd1",
    "SecretString": "{\"username\":\"admin\",\"password\":\"testpassword\",\"engine\":\"mysql\",\"host\":\"database-1.cluster-cl1btn8tdegb.ap-northeast-1.rds.amazonaws.com\",\"port\":3306,\"dbClusterIdentifier\":\"database-1\"}",
    ...
}
```

※RDSへの接続時の認証方式としては[パスワード認証・IAM認証などある](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/database-authentication.html)が、RDS Proxyを利用する場合のIAM認証は、RDS ProxyがSecrets Managerから認証情報を取り出す際に使用される（詳細は[Connecting to a proxy using IAM authentication](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/rds-proxy-setup.html#rds-proxy-connecting-iam)に書かれている通り）。

> The IAM authentication applies to RDS Proxy retrieving the user name and password credentials from Secrets Manager. The connection from RDS Proxy to the underlying database doesn't go through IAM.（IAM認証は、RDS ProxyがSecrets Managerからユーザー名とパスワードの資格情報を取得する際に適用されます。RDS Proxyから基礎となるデータベースへの接続は、IAMを経由しません。）

- 参考：[Create a database secret](https://docs.aws.amazon.com/secretsmanager/latest/userguide/create_database_secret.html)
- 参考：[AWS Key Management Service の料金](https://aws.amazon.com/jp/kms/pricing/)
- IAM認証に関する補足情報：[Using the function's permissions for authentication](https://docs.aws.amazon.com/lambda/latest/dg/configuration-database.html#configuration-database-auth)

## IAMロールを作成する
[Setting up AWS Identity and Access Management (IAM) policies](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/rds-proxy-setup.html#rds-proxy-iam-setup)に沿って、RDS ProxyがSecrets Managerにアクセスし、RDSへ接続するのための認証情報を取り出せるようにする設定を行う。

まずは以下の設定でIAMポリシーを作成する。

```json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": "secretsmanager:GetSecretValue",
            "Resource": [
                "arn:aws:secretsmanager:ap-northeast-1:************:secret:dev/db/mysql-azibNI"
            ]
        },
        {
            "Sid": "VisualEditor1",
            "Effect": "Allow",
            "Action": "kms:Decrypt",
            "Resource": "arn:aws:kms:ap-northeast-1:************:key/5b01b7c4-7556-496c-be8c-c30bf1676c7f",
            "Condition": {
                "StringEquals": {
                    "kms:ViaService": "secretsmanager.ap-northeast-1.amazonaws.com"
                }
            }
        }
    ]
}
```

`kms:Decrypt`についてはシークレットの復号を行う事なので、同じSecrets Managerからの呼び出しのみを許可する設定になっている（より制限をする場合、Conditionの設定が望ましい）。あとは、このポリシーをアタッチできるサービスを設定する（信頼

ちなみに、各ARNが分からなければそれは各ページを見に行けば分かる。

| 1 | 2 |
|:-:|:-:|
| Secrets Manager | ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372684/8dccca98-05c1-75be-2535-491ad570e46b.png) |
| KMS | ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372684/38ad5219-7951-f0c1-3509-9286cd6f5318.png) |

※マネジメントコンソールではなく、AWS CLIからでも以下のように確認できるだろう。

```console
[study@localhost .aws]$ aws secretsmanager list-secrets
{
    "SecretList": [
        {
            "ARN": "arn:aws:secretsmanager:ap-northeast-1:************:secret:dev/db/mysql-azibNI",
            "Name": "dev/db/mysql",
            "Description": "for RDS Proxy test",
            ...
        }
    ]
}

[study@localhost .aws]$ aws kms list-aliases
{
    "Aliases": [
        ...
        {
            "AliasName": "alias/aws/secretsmanager",
            "AliasArn": "arn:aws:kms:ap-northeast-1:************:alias/aws/secretsmanager",
            "TargetKeyId": "5b01b7c4-7556-496c-be8c-c30bf1676c7f",
            ...
        },
        ...
    ]
}
[study@localhost .aws]$ aws kms list-keys
{
    "Keys": [
        ...
        {
            "KeyId": "5b01b7c4-7556-496c-be8c-c30bf1676c7f",
            "KeyArn": "arn:aws:kms:ap-northeast-1:************:key/5b01b7c4-7556-496c-be8c-c30bf1676c7f"
        }
    ]
}
```

IAMポリシーが作成できたら、今度はそのポリシーを適用したIAMロールを作成する。IAMロール作成時には`信頼されたエンティティを選択`というのがあるが、これについては[IAMロールと信頼されたエンティティの関係性](https://qiita.com/yuta-katayama-23/items/7055c1a2e5a9cddf7a76)を参照ください。設定内容は以下の通り（「信頼されたエンティティを選択」は「カスタム信頼ポリシー」にJSONを書けるのでJSONを書くのが楽）。

| ステップ | 画像 |
|:-:|:-:|
| 信頼されたエンティティを選択  | ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372684/87527515-f64c-913e-522f-41a6e4665c08.png) |
| 許可を追加 | ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372684/a7219395-f39d-8f04-7984-877aacc1a8bb.png) |

- 参考：[kms:ViaService](https://docs.aws.amazon.com/kms/latest/developerguide/policy-conditions.html#conditions-kms-via-service)

## RDS Proxy の作成
[Creating an RDS Proxy](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/rds-proxy-setup.html#rds-proxy-creating)に沿って作成していく。

| ステップ | 画像 |
|:-:|:-:|
| ナビゲーションペインのプロキシ | ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372684/93f57763-9fa1-0235-836c-7fa4382d5599.png) |
| プロキシ設定 | ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372684/f5703360-337f-8218-0eb3-3459b6281129.png) |
| ターゲットグループの設定 | ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372684/2c7789cf-9a54-1c35-67da-21ab8c4638e6.png) |
| 接続 | ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372684/283912dd-76df-6477-69b0-938d7de0338d.png) |

接続の項目はそれぞれ、

- シークレット  
[Secrets Managerで認証情報を作成](#secrets-managerで認証情報を作成)の際に作成したシークレットを設定
これを使ってRDS（Aurora）に認証する
- IAM ロール
[IAMロールを作成する](#iamロールを作成する)で作成したIAMロールを設定
Secrets Managerのシークレットを復号化して実際にRDS（Aurora）に接続する際の認証情報を利用できるようにする
- IAM 認証
今回は[パスワード認証](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/database-authentication.html#password-authentication)で認証するのでここはデフォルトのまま許可しないにする

のように設定する。

1点注意として、このRDS Proxyは有効になるまで20分程度かかるようで、作成後すぐに使えない。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372684/5849793c-f900-67d6-7a7d-5eedf78032a6.png)

ちなみに、AWS CLIでRDS Proxyの状態確認でき、`TargetHealth`の`State`が`AVAILABLE`であれば問題なく作成できていると判断できる。

```console
[study@localhost .aws]$ aws rds describe-db-proxy-targets --db-proxy-name aurora-proxy
{
    "Targets": [
        {
            "Endpoint": "database-1-instance-1.cl1btn8tdegb.ap-northeast-1.rds.amazonaws.com",
            "TrackedClusterId": "database-1",
            "RdsResourceId": "database-1-instance-1",
            "Port": 3306,
            "Type": "RDS_INSTANCE",
            "Role": "READ_WRITE",
            "TargetHealth": {
                "State": "AVAILABLE"
            }
        },
        {
            "RdsResourceId": "database-1",
            "Port": 3306,
            "Type": "TRACKED_CLUSTER"
        }
    ]
}
```

※上記のEndpointはRDS（Aurora）のエンドポイントであり、RDS Proxyのエンドポイントではないので注意。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372684/4e494936-e582-30ad-b7b6-1a2d506d2405.png)

RDS ProxyのエンドポイントはRDS Proxyの設定確認画面から確認できる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372684/a89342b0-64e7-c94a-1540-f25ffd73f6f3.png)

AWS CLIだと以下。

```console
[study@localhost .aws]$ aws rds describe-db-proxy-endpoints --db-proxy-name aurora-proxy
{
    "DBProxyEndpoints": [
        {
            "DBProxyEndpointName": "default",
            "DBProxyName": "aurora-proxy",
            "Status": "available",
            ...
            "Endpoint": "aurora-proxy.proxy-cl1btn8tdegb.ap-northeast-1.rds.amazonaws.com",
            ...
            "TargetRole": "READ_WRITE",
            "IsDefault": true
        },
        {
            "DBProxyEndpointName": "aurora-proxy-read-only",
            ...
            "DBProxyName": "aurora-proxy",
            "Status": "available",
            ...
            "Endpoint": "aurora-proxy-read-only.endpoint.proxy-cl1btn8tdegb.ap-northeast-1.rds.amazonaws.com",
            ...
            "TargetRole": "READ_ONLY",
            "IsDefault": false
        }
    ]
}
```

## ひとまずEC2からRDS（Aurora）に接続してみる
VPC内からでないと接続できないので、最も楽そうなEC2を踏み台にしてRDS（Aurora）に接続してみる。また、RDS Proxy経由でもRDS（Aurora）に接続できるか？を検証してみる。

接続の経路としては以下のようなイメージになる（今回はAWSアカウントを作成した際に自動的に作成されるサブネットを利用するので、パブリックサブネットにRDS（Aurora）も属させるような形式になるが、本来はプライベートサブネットとパブリックサブネットに分けるべきだろう。また、EC2は同じサブネットに属していても、セキュリティグループがRDS（Aurora）と別だと、以下で取り上げる[【事前準備③】RDSのセキュリティグループにEC2のプライベートIPを追加する](#事前準備rdsのセキュリティグループにec2のプライベートipを追加する)のような追加設定が必要になる）。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372684/0152fbdb-1e1a-9417-61b2-6df5a50ceeb4.png)


※今回のRDS（Aurora）のインスタンスは以下のように、`パブリックアクセス可能`を「なし」しているため（パブリックアクセス可能にしているのであれば、[MySQL Workbench からの接続](https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/UserGuide/USER_ConnectToInstance.html#USER_ConnectToInstance.MySQLWorkbench)などで簡単に接続できるだろう）。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372684/fd52622d-533e-98c2-a369-6362c8aea6d3.png)

### 【事前準備①】EC2のインスタンスを作成し、SSHでEC2にローカルから接続する
特にEC2のインスタンス作成については詳細を書かないが、作成した後は以下のようにして接続できる（今回、ローカルマシンはWindows10上に構築しているVirutalBoxのCentOS7.9で、そこからEC2に接続している。WindowsにVirtualBoxでLinux環境を作成する方法は[Windows上にLinux(CentOS)のWebアプリ開発環境をvirtualboxで構築する](https://qiita.com/yuta-katayama-23/items/6baf167464eca3376ea0)を参照。）。1点、Amazon Linux 2のユーザー名はインスタンスを立ち上げた際にデフォルトで`ec2-user`になるので注意。詳細は[インスタンスに関する情報を取得する](https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/connection-prereqs.html#connection-prereqs-get-info-about-instance)に書かれている。

```console
[study@localhost .ssh]$ ls 
aurora-stool-server-key.pem  authorized_keys  config  github  github.pub  known_hosts
[study@localhost .ssh]$ chmod 400 aurora-stool-server-key.pem 
[study@localhost .ssh]$ ssh -i aurora-stool-server-key.pem ec2-user@ec2-18-183-53-112.ap-northeast-1.compute.amazonaws.com 
Last login: Wed Aug 17 10:30:37 2022 from 133-32-218-61.east.xps.vectant.ne.jp

       __|  __|_  )
       _|  (     /   Amazon Linux 2 AMI
      ___|\___|___|

https://aws.amazon.com/amazon-linux-2/
2 package(s) needed for security, out of 10 available
Run "sudo yum update" to apply all updates.
[ec2-user@ip-172-31-11-41 ~]$ 
```

- 参考：[Amazon EC2 のキーペアと Linux インスタンス](https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/ec2-key-pairs.html?icmpid=docs_ec2_console#having-ec2-create-your-key-pair)
- 参考：[Linux インスタンスへの接続](https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/AccessingInstances.html)
- 参考：[インスタンスに関する情報を取得する](https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/connection-prereqs.html#connection-prereqs-get-info-about-instance)

### 【事前準備②】Amazon Linux 2にMySQLのクライアントをインストール
インスタンスを作成した後はまだMySQLクライアントがインストールされていない状態なのでインストールを行う。

```console
[ec2-user@ip-172-31-11-41 ~]$ mysql --version
-bash: mysql: コマンドが見つかりません
```

MySQLのクライアントのインストール方法は[MySQL データベースエンジンを実行している DB インスタンスへの接続](https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/UserGuide/USER_ConnectToInstance.html)に書かれている通り。

```console
[ec2-user@ip-172-31-11-41 ~]$ sudo yum install mariadb
...

[ec2-user@ip-172-31-11-41 ~]$ mysql --version
mysql  Ver 15.1 Distrib 5.5.68-MariaDB, for Linux (x86_64) using readline 5.1
```

### 【事前準備③】RDSのセキュリティグループにEC2のプライベートIPを追加する
MySQLクライアントをインストールした状態でEC2からRDSに接続しようとしても、セキュリティグループの制約で接続できない。

```console
[ec2-user@ip-172-31-11-41 ~]$ mysql -h database-1-instance-1.cl1btn8tdegb.ap-northeast-1.rds.amazonaws.com -P 3306 -u admin -p
Enter password: 

ERROR 2003 (HY000): Can't connect to MySQL server on 'database-1-instance-1.cl1btn8tdegb.ap-northeast-1.rds.amazonaws.com' (110)
```
というわけでセキュリティグループのインバウンドを変更して、EC2から接続できるようにする。まず、RDS（Aurora）のインスタンス情報を確認すると、セキュリティグループというのがあるのでそれをクリックするとセキュリティグループ一覧が見れる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372684/6ec15d2e-a6f1-38f7-f5eb-346dd6ec646a.png)

インバウンドルールのタブから、`インバウンドのルールを編集`をクリックして新しくEC2のプライベートIPからの接続を追加する（MySQLのアクセスしかしないのでタイプを`MySQL/Aurora`にする）。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372684/8fdc07c7-5d77-2b2e-4f1d-ec649f5dee47.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372684/21887fe3-dc13-80a9-2a66-a4dd35d92346.png)

EC2のプライベートIPは以下のように確認できる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372684/9ef211dc-f58f-38a8-89b7-0eb997deaf58.png)

### 実際にEC2からRDS（Aurora）へ接続してみる
上記の事前準備をした後は、いつものようにDBのホスト・ポート・ユーザー名を指定して接続できるようになる（`mydb`が今回Auroraを立ち上げる時に作成していたデータベース）。

```console
[ec2-user@ip-172-31-11-41 ~]$ mysql -h database-1-instance-1.cl1btn8tdegb.ap-northeast-1.rds.amazonaws.com -P 3306 -u admin -p
Enter password: 
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MySQL connection id is 24
Server version: 5.7.12 MySQL Community Server (GPL)

Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MySQL [(none)]> SHOW DATABASES;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mydb               |
| mysql              |
| performance_schema |
| sys                |
+--------------------+
5 rows in set (0.00 sec)
```

※今回セキュリティグループでプライベートIPを指定したが、EC2が属するセキュリティグループを追加するという方法もとれる。

- 参考：[RDSのセキュリティグループを設定します。](https://ponsuke-tarou.hatenablog.com/entry/2020/02/27/214335#RDS%E3%81%AE%E3%82%BB%E3%82%AD%E3%83%A5%E3%83%AA%E3%83%86%E3%82%A3%E3%82%B0%E3%83%AB%E3%83%BC%E3%83%97%E3%82%92%E8%A8%AD%E5%AE%9A%E3%81%97%E3%81%BE%E3%81%99)

#### RDS Proxy経由で接続した際にRead・Writeするためのテーブル・データを事前作成する
以下のSQLを実行して、データベースにテーブルとデータを作成しておく（一度行を作成して削除した関係でIDは3～になっている）。

```sql
CREATE TABLE `users` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(128) DEFAULT NULL,
  `created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

INSERT INTO `users` (`name`) VALUES ('yamada tarou'), ('山田 太郎');
```
```console
MySQL [(none)]> USE `mydb`;

MySQL [mydb]> CREATE TABLE `users` (
    ->   `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
    ->   `name` varchar(128) DEFAULT NULL,
    ->   `created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
    ->   `updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    ->   PRIMARY KEY (`id`)
    -> ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
Query OK, 0 rows affected (0.07 sec)

MySQL [mydb]> INSERT INTO `users` (`name`) VALUES ('yamada tarou'), ('山田 太郎');
Query OK, 2 rows affected (0.01 sec)
Records: 2  Duplicates: 0  Warnings: 0

MySQL [mydb]> SELECT * FROM `users`;
+----+---------------+---------------------+---------------------+
| id | name          | created_at          | updated_at          |
+----+---------------+---------------------+---------------------+
|  3 | yamada tarou  | 2022-08-17 08:58:35 | 2022-08-17 08:58:35 |
|  4 | 山田 太郎     | 2022-08-17 08:58:35 | 2022-08-17 08:58:35 |
+----+---------------+---------------------+---------------------+
2 rows in set (0.00 sec)
```

ちなみに、RDS Proxy経由ではなく、Auroraのインスタンスに直でアクセスしているので、Read/Writeの両方ができている（RDS Proxy経由ではReadOnlyがあるのでそうはいかない、詳細は[EC2からRDS Proxy経由でRDS（Aurora）に接続してみる](#ec2からrds-proxy経由でrdsauroraに接続してみる)で）。

## EC2からRDS Proxy経由でRDS（Aurora）に接続してみる
イメージとしては以下（AZ・サブネットなどは省略している）。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372684/df954faf-6c88-9861-a573-8b5074b6f51b.png)

アクセスエンドポイントが2つあるのでそれぞれで試してみる。エンドポイントは[RDS Proxy の作成](#rds-proxy-の作成)の章でRDS Proxyを作成した時に確認した以下の2つ。

1. aurora-proxy.proxy-cl1btn8tdegb.ap-northeast-1.rds.amazonaws.com
1. aurora-proxy-read-only.endpoint.proxy-cl1btn8tdegb.ap-northeast-1.rds.amazonaws.com

1点、RDS Proxy経由で接続する場合、RDS Proxyを作成する際にTLSを有効にしたので、その証明書が必要になる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372684/6fd3426f-f3fd-c945-3618-6b24f767db7f.png)

そのため以下のようにして証明書をEC2上に落としておく。

```console
[ec2-user@ip-172-31-11-41 ~]$ wget https://www.amazontrust.com/repository/AmazonRootCA1.pem
[ec2-user@ip-172-31-11-41 ~]$ ls
AmazonRootCA1.pem
```

※RDSのMySQLへ直に接続する時には[SSL を使用した MySQL コマンドラインクライアントからの接続 (暗号化)](https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/UserGuide/USER_ConnectToInstance.html#USER_ConnectToInstanceSSL.CLI)に書かれている方法（`--ssl-ca=global-bundle.pem`オプションを追加する）で接続する必要があるが、今回はRDS Proxy経由だったので証明書が`AmazonRootCA1.pem`と別物になる（MySQL・Auroraへの接続時の証明書自体は[SSL/TLS を使用した DB インスタンスへの接続の暗号化](https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/UserGuide/UsingWithRDS.SSL.html)のページにある`global-bundle.pem`がそれ）。RDS Proxyに接続する際に`global-bundle.pem`を指定すると、以下のようにエラーになる。

```console
[ec2-user@ip-172-31-11-41 ~]$ mysql -h aurora-proxy.proxy-cl1btn8tdegb.ap-northeast-1.rds.amazonaws.com --ssl-ca=global-bundle.pem -P 3306 -u admin -p
Enter password: 
ERROR 2026 (HY000): SSL connection error: error:14090086:SSL routines:ssl3_get_server_certificate:certificate verify failed
```
- 参考：[Amazon RDS プロキシを使用して、Amazon RDS MySQL DB インスタンスまたは Aurora MySQL DB クラスターに接続する方法を教えてください。](https://aws.amazon.com/jp/premiumsupport/knowledge-center/rds-aurora-mysql-connect-proxy/)
- 参考：[IAM認証で RDS Proxy 接続を利用する際は証明書にお気をつけて](https://www.skyarch.net/blog/?p=18784)

### 1つ目のエンドポイントへ接続してみる
接続自体は[ひとまずEC2からRDS（Aurora）に接続してみる](#ひとまずec2からrdsauroraに接続してみる)の章の最後で取り上げた方法と同じで、エンドポイントだけRDS Proxyのものに変わる。

```console
[ec2-user@ip-172-31-11-41 ~]$ mysql -h aurora-proxy.proxy-cl1btn8tdegb.ap-northeast-1.rds.amazonaws.com --ssl-ca=AmazonRootCA1.pem -P 3306 -u admin -p
...

MySQL [(none)]> USE `mydb`;

MySQL [mydb]> SELECT * FROM `users`;
+----+---------------+---------------------+---------------------+
| id | name          | created_at          | updated_at          |
+----+---------------+---------------------+---------------------+
|  3 | yamada tarou  | 2022-08-17 08:58:35 | 2022-08-17 08:58:35 |
|  4 | 山田 太郎     | 2022-08-17 08:58:35 | 2022-08-17 08:58:35 |
+----+---------------+---------------------+---------------------+
2 rows in set (0.00 sec)

MySQL [mydb]> INSERT INTO `users` (`name`) VALUES ('山田 花子');

MySQL [mydb]> SELECT * FROM `users`;
+----+---------------+---------------------+---------------------+
| id | name          | created_at          | updated_at          |
+----+---------------+---------------------+---------------------+
|  3 | yamada tarou  | 2022-08-17 08:58:35 | 2022-08-17 08:58:35 |
|  4 | 山田 太郎     | 2022-08-17 08:58:35 | 2022-08-17 08:58:35 |
|  5 | 山田 花子     | 2022-08-17 10:58:11 | 2022-08-17 10:58:11 |
+----+---------------+---------------------+---------------------+
3 rows in set (0.00 sec)
```

特にRead/Writeを問題なく実行できる事が確認できる。

※[Amazon RDS プロキシを使用して、Amazon RDS MySQL DB インスタンスまたは Aurora MySQL DB クラスターに接続する方法を教えてください。](https://aws.amazon.com/jp/premiumsupport/knowledge-center/rds-aurora-mysql-connect-proxy/)では`--ssl-mode=REQUIRED`というオプションを設定するように書かれていたが、このオプションがあると以下のようなエラーになったが、インストールしたものがmariadbだったからだと思っている。ちなみに、[MySQL](https://dev.mysql.com/doc/refman/5.7/en/connection-options.html#option_general_ssl)のリファレンスを見ると、

> Use --ssl-mode=REQUIRED instead of --ssl=1 or --enable-ssl.

となっているので、`--ssl=1`をオプションに付けて実行したらどうなるのか試したところ、問題なく実行できた。が、MySQLとMariaDBは祖先は同じだが今は違う部分もあるのでMariaDBのリファレンスをちゃんと読むのがいいだろう・・・。

```console
[ec2-user@ip-172-31-11-41 ~]$ mysql --version
mysql  Ver 15.1 Distrib 5.5.68-MariaDB, for Linux (x86_64) using readline 5.1

[ec2-user@ip-172-31-11-41 ~]$ mysql -h aurora-proxy.proxy-cl1btn8tdegb.ap-northeast-1.rds.amazonaws.com --ssl-mode=REQUIRED --ssl-ca=AmazonRootCA1.pem -P 3306 -u admin -p
mysql: unknown variable 'ssl-mode=REQUIRED'

[ec2-user@ip-172-31-11-41 ~]$ mysql -h aurora-proxy.proxy-cl1btn8tdegb.ap-northeast-1.rds.amazonaws.com -u admin -p --ssl=1 --ssl-ca=AmazonRootCA1.pem
Enter password: 
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MySQL connection id is 1941881813
Server version: 5.7.12 MySQL Community Server (GPL)
...
```

### 2つ目のエンドポイント（read-only）へ接続してみる
こちらも[1つ目のエンドポイントへ接続してみる](#1つ目のエンドポイントへ接続してみる)と同じように接続するまでは全く同じ。ただし、INSERT文（Write）はread-onlyのエンドポイントなので実行できない。

```console
[ec2-user@ip-172-31-11-41 ~]$ mysql -h aurora-proxy-read-only.endpoint.proxy-cl1btn8tdegb.ap-northeast-1.rds.amazonaws.com --ssl=1 --ssl-ca=AmazonRootCA1.pem -u admin -p
Enter password: 
...

MySQL [(none)]> USE `mydb`;

MySQL [mydb]> SELECT * FROM `users`;
+----+---------------+---------------------+---------------------+
| id | name          | created_at          | updated_at          |
+----+---------------+---------------------+---------------------+
|  3 | yamada tarou  | 2022-08-17 08:58:35 | 2022-08-17 08:58:35 |
|  4 | 山田 太郎     | 2022-08-17 08:58:35 | 2022-08-17 08:58:35 |
|  5 | 山田 花子     | 2022-08-17 10:58:11 | 2022-08-17 10:58:11 |
+----+---------------+---------------------+---------------------+
3 rows in set (0.01 sec)

MySQL [mydb]> INSERT INTO `users` (`name`) VALUES ('田中 太郎');
ERROR 1290 (HY000): The MySQL server is running with the --read-only option so it cannot execute this statement
```

#### RDS（Aurora）のリーダーエンドポイント
もしRDSのクラスター・インスタンスを作成する際に、リーダーを作成していなかった場合、以下のように`Target group doesn't have any associated read-only instances`というエラーが出る。

```console
[ec2-user@ip-172-31-11-41 ~]$ mysql -h aurora-proxy-read-only.endpoint.proxy-cl1btn8tdegb.ap-northeast-1.rds.amazonaws.com --ssl=1 --ssl-ca=AmazonRootCA1.pem -u admin -p
Enter password: 
...

MySQL [(none)]> SHOW DATABASES;
ERROR 9501 (HY000): Target group doesn't have any associated read-only instances
```

これはRDS（Aurora）にリーダーのエンドポイントがないからで、単にリーダーエンドポイントを追加すればいい（スクショがない部分はデフォルトのまま）。

| 1 | 2 |
|:-:|:-:|
| アクション | ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372684/b824e12d-c493-f686-705e-cac02dea15d0.png) |
| リーダーの追加 | ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372684/5d55082b-26f0-28ed-776f-9426301ad403.png) |

作成が完了すると、もし既にRDS Proxyが存在していれば自動で紐づく（RDS Proxyを作成する際に指定したのがAurora DB クラスター（今回だと以下のスクショの`database-1`）だったため）。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372684/14b1e7cd-de4c-e074-c008-e6b565ffb3b0.png)

- 参考：[リーダーエンドポイントの使用](https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/AuroraUserGuide/Aurora.Overview.Endpoints.html#Aurora.Endpoints.Reader)
- 参考：[Aurora クラスターでのリーダーエンドポイントの使用](https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/AuroraUserGuide/rds-proxy-endpoints.html#rds-proxy-endpoints-reader)

## Lambdaを作成する
今回は手動で設定する部分がある関係で、serverlessFWなどIaCではなくZipを自分で上げるスタイルでLambdaを作成する。まずはデフォルトの設定でマネージメントコンソールから関数を作成する。

コード全体は以下。

https://github.com/yuta-katayama-23/lambda-rds-proxy-rds

webpackのoutputの設定については[Module Definition Systems](https://webpack.js.org/configuration/output/#module-definition-systems)を参照。

[README]()の通りの手順で、Build→Deployを行えばLambdaのコードの部分の設定はOK。

以下では、RDS Proxy経由でRDS（Aurora）に接続するための設定を行っていく。

### ランタイム設定の変更
デフォルトのハンドラーは`index.handler`なので、自分の実装に合わせてここを変更する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372684/301f36ff-01a4-077b-e144-dc4ef2d74c78.png)

Lambdaのハンドラーは`ファイル名.関数名`になるので、今回だとファイルがルートディレクトリにないので相対パスも含めて書く必要があり、`./dist/lambda.handler`になる（詳細は[Node.js の AWS Lambda 関数ハンドラー](https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/nodejs-handler.html)を参照）。

> 関数を設定すると、ハンドラー設定の値はファイル名とエクスポートしたハンドラーメソッドの名前をドットで区切ったものになります。コンソールのデフォルトと、このガイドの例では、index.handler です。これは、handler ファイルからエクスポートされた index.js メソッドを示します。

### 環境変数を設定
以下のように環境変数を設定する。これを使ってLambdaは動く。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372684/91773875-ffa4-b1ec-8c65-6a34a822fe87.png)

※ここまでの状態ではRDS Proxyのエンドポイントにアクセスできない。続きの[VPCの設定](#vpcの設定)などをやってやっと接続できるようになる。

### VPCの設定
LambdaからRDS Proxyにアクセスするには、LambdaはRDS Proxyと同じVPCに属している必要がある（LambdaにVPCの設定をせずにデータベースプロキシの設定を追加すると以下のようにInfoが表示される）。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372684/d0c5d7f5-b000-7e01-30d6-2080951990b3.png)

というわけで設定していくが、まずLambdaにはLambdaのVPCがあり、AWSアカウントで作成したVPCとは別物なので、LambdaをアカウントのVPCに接続させるにはIAMが追加で必要になる（以下[公式](https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/foundation-networking.html)からの引用）。

> Lambda 関数は、常に Lambda サービスが所有する VPC 内で実行されます。Lambda はこの VPC にネットワークアクセスとセキュリティルールを適用し、VPC を自動的に維持および監視します。Lambda 関数がアカウント VPC 内のリソースにアクセスする必要がある場合は、VPC にアクセスするための関数を設定します。

必要なIAMは[実行ロールとユーザーアクセス許可](https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/configuration-vpc.html#vpc-permissions)に書かれている。今回はマネージメントコンソールでLambdaを作成し、その時に特に何も設定しなかったのでデフォルトで新しいLambdaの実行ロールが作成されているので、そこに新しくポリシー（`AWSLambdaVPCAccessExecutionRole`）をアタッチする。

| 1 | 2 |
|:-:|:-:|
| Lambdaの実行ロール | ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372684/27f9553b-ac15-45b2-8642-f47adcb976f0.png) |
| IAMロール | ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372684/9647add9-83d0-8938-0853-72c1e6b2c608.png) |
| ポリシーを rds-proxy-lambda-func-role-jdg11om4 にアタッチ | ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372684/1e6b3bd6-b8c9-0c7b-f566-831c54be9e91.png) |

次にVPCの設定をしていく。RDS ProxyのVPCと同じVPC（AWSアカウントを作成した際にデフォルトで作成されるVPC）になるので、以下のように設定していけばいい。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372684/2476a4ad-bda6-9480-30f6-209c174bd2aa.png)

※Lambdaの実行ロールについては[Lambdaの実行ロール　Lambda実行時のAWS_ACCESS_KEY_IDとかは何になる？](https://qiita.com/yuta-katayama-23/items/d942e62cd2f7d7ff53dd)も参照ください。

- 参考：[チュートリアル: Amazon VPC の Amazon RDS にアクセスする Lambda 関数の設定](https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/services-rds-tutorial.html)
- 参考：[Lambda 関数のデータベースアクセスの設定](https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/configuration-database.html)

### データベースプロキシの設定
VPCの設定ができたので、最後にデータベースプロキシの設定もする。これは既に作成済みのRDS Proxyを設定するだけなので以下のように簡単にできる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372684/43211dcb-4ca1-7d32-8777-2992b1de890a.png)

#### 2022-08-23追記
IAM認証ではなく、パスワード認証の場合にはLambdaにデータベースプロキシの設定をする必要はないようである。実際にデータベースプロキシの設定をせずに、RDS Proxyへ経由でAuroraへの接続もできた。

- 参考：[AWS LambdaからRDS Proxy経由でAmazon RDSに接続してみる](https://blog.serverworks.co.jp/2022/01/20/093110#lambda_rds_proxypy)

## LambdaからRDS Proxy経由でRDS（Aurora）に接続する
後は実際にLambdaを実行するだけ。実行してみると以下のように問題なくデータが取得できている事が確認できた。あとはread-onlyのエンドポイントを使い分けて実装すれば実装として完成になるだろう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372684/ee5d2225-b5c9-40b8-6f1c-3fcea53d7ec2.png)

```console
study@localhost:~/workspace/rds-proxy-lambda (main *)
$ aws lambda invoke --function-name rds-proxy-lambda-func response.json
{
    "StatusCode": 200,
    "ExecutedVersion": "$LATEST"
}
```
```json:response.json
{
	"statusCode": 200,
	"body": [
		{
			"id": 3,
			"name": "yamada tarou",
			"created_at": "2022-08-17T08:58:35.000Z",
			"updated_at": "2022-08-17T08:58:35.000Z"
		},
		{
			"id": 4,
			"name": "山田 太郎",
			"created_at": "2022-08-17T08:58:35.000Z",
			"updated_at": "2022-08-17T08:58:35.000Z"
		},
		{
			"id": 5,
			"name": "山田 花子",
			"created_at": "2022-08-17T09:47:59.000Z",
			"updated_at": "2022-08-17T09:47:59.000Z"
		}
	]
}
```

※コードについて1点補足として、[EC2からRDS Proxy経由でRDS（Aurora）に接続してみる](#ec2からrds-proxy経由でrdsauroraに接続してみる)でEC2から接続する時もそうだったが、今回のRDS ProxyはTLSを有効化しているので証明書が必要になる。そのため、myslqのコネクションを作成する時に[SSL options](https://github.com/mysqljs/mysql#ssl-options)（[API and Configuration](https://github.com/sidorares/node-mysql2#api-and-configuration)から飛べるページ）に書かれているようなSSLのための追加実装が必要になっている。

```js
	const connection = await mysql.createConnection({
		host: process.env.HOST,
		port: 3306,
		user: process.env.USER_NAME,
		password: process.env.PASSWORD,
		database: 'mydb',
		ssl: {
			ca: fs.readFileSync(path.join(__dirname, 'AmazonRootCA1.pem'))
		}
	});
```

## まとめとして
TLSの証明書が必要でそれがどこにあるのかわからず少し躓いた部分があったが、公式の説明通りに比較的に簡単に構築できた。

## 参考文献
- [AWS LambdaでAmazon RDS Proxyを使用する](https://aws.amazon.com/jp/blogs/news/using-amazon-rds-proxy-with-aws-lambda/)

## おまけ
### IAMユーザーで請求情報を見れるようにする
[AWS Billing コンソールへのアクセスのアクティブ化](https://docs.aws.amazon.com/ja_jp/awsaccountbilling/latest/aboutv2/control-access-billing.html#ControllingAccessWebsite-Activate)の https://console.aws.amazon.com/billing/home#/account をクリックし、rootアカウントの設定ページに飛ぶ。

ページの真ん中の方にある`IAM ユーザー/ロールによる請求情報へのアクセス`を編集すればOK。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372684/fe377ed9-2afc-a6ee-fe18-a152b26283fb.png)

### CICD
S3にzipをアップロードするという古典的な方法だが…。以下のソースがそれ。

https://github.com/yuta-katayama-23/lambda-rds-proxy-rds/blob/main/.github/workflows/cicd.yaml