AWS Session Managerでローカル環境からセキュアにRDSへ接続する方法

これまで、踏み台EC2を通じたSSHトンネルを利用してDB(RDS)に接続し、DBクライアントツール(MySQL Workbenchなど)の接続やローカル環境からのマイグレーション操作を行っていました。

踏み台EC2のセキュリティグループのインバウンドルールでIPアドレスを制限することで、外部からの安全な接続を確保していました。
しかし、チームメンバーの追加や作業場所の変更に伴うIPアドレスの変更が面倒で、できる限りリソースはプライベートサブネット内に保持したいという考えから、AWS Systems Manager Session Managerを用いたDB接続の方法に移行することにしました。

Session Managerの導入によって、以下のメリットを享受できました。
- SSHキーの作成・管理作業の不要化
- より安全なDBへの接続
- CloudTrailによる各セッションのアクションの記録・監査

# 接続プロセス
Session Managerを使用したDB接続プロセスは、主に以下のステップから成り立っています。

1. ポートフォワーディングセッションの開始: AWS CLIまたはAWS Management Consoleを使用して、Session Managerを介したポートフォワーディングセッションを開始する。
2. ローカルマシンの設定: セッションが確立されたら、ローカルマシンはSession Managerから提供される情報を基に、DBインスタンスとの間で通信を行うエンドポイントとして動作する。
3. DB接続: 確立されたポートフォワーディングセッションを通じて、DBへ接続する。この際、適切な認証情報と接続パラメータが必要となる。

# 導入手順

## 事前確認
ターゲットとなるEC2インスタンス上で、AWS Systems Managerエージェント(SSMエージェント)が最新バージョンで動作していることを確認します。
SSMエージェントは、インスタンスとSystems Manager間のセキュアな通信を実現します。

## IAMユーザーの作成
`AssumeSSMRolePermissionPolicy`という名前のカスタムIAMポリシーを作成して、特定のIAMユーザー(もしくはIAMユーザーが所属するIAMグループ)にアタッチします。
これにより、IAMユーザーは特定のSSM関連のIAMロールを引き受ける権限を得ることができます。

```json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "sts:AssumeRole",
            "Resource": "arn:aws:iam::<Account ID>:role/AmazonSSMRoleForInstancesQuickSetup"
        }
    ]
}
```
`AmazonSSMRoleForInstancesQuickSetup`はEC2インスタンスにアタッチするIAMロールです（後ほど作成します)。
このロールはEC2インスタンスにSSMエージェントが適切に機能するための必要な権限を付与するために使用されます。

IAMユーザーがこのロールを引き受けることで、そのユーザーはSession Managerを通じてEC2インスタンスを管理するための一時的な資格情報(アクセスキー、シークレットアクセスキー、セッショントークン)を取得できます。
そのため、IAMユーザー自身にアクセスキーとシークレットアクセスキーを発行する必要はありません。

## AWS CLIのインストール
ポートフォワーディングセッションを開始するために、事前にAWS CLIをインストールします。

https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/cli-chap-getting-started.html

## セキュリティグループ(SG)の作成
EC2を踏み台サーバーとして使用していたときと違い、SGのインバウンドルールは不要となります。
また、アウトバウンドルールは、RDSとSSMエージェントへの接続を許可するようにします。

以下がTerraformのコードです。

```tf
resource "aws_security_group" "rds_ssm_connector_sg" {
  name        = "rds-ssm-connector-sg"
  description = "Security group for RDS SSM Connector"
  vpc_id      = data.aws_ssm_parameter.vpc_id.value
}

resource "aws_security_group_rule" "rds_ssm_connector_sg_egress" {
  type              = "egress"
  from_port         = 3306
  to_port           = 3306
  protocol          = "tcp"
  security_group_id = aws_security_group.rds_ssm_connector_sg.id
  cidr_blocks       = ["RDSインスタンスのIP範囲"]
}

resource "aws_security_group_rule" "rds_ssm_connector_sg_egress_ssm" {
  type              = "egress"
  from_port         = 443
  to_port           = 443
  protocol          = "tcp"
  security_group_id = aws_security_group.rds_ssm_connector_sg.id
  cidr_blocks       = ["0.0.0.0/0"]
}
```

## IAMロールの作成
EC2インスタンスに適用するIAMロール`AmazonSSMRoleForInstancesQuickSetup`を作成します。
このロールには、SSMセッションを作成および管理するために以下のマネージドポリシーを適用します。
- `AmazonSSMManagedInstanceCore`：インスタンスがSSMサービスと通信するための基本的な権限
- `AmazonSSMPatchAssociation`：パッチバスの適用を自動化するための権限

また、「信頼関係」タブの「信頼されたエンティティ」に以下を記述します。
これにより、EC2とIAMユーザーがこのIAMロールを引き受けられるようになります。

```json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Service": "ec2.amazonaws.com",
                "AWS": "arn:aws:iam::<Account ID>:user/<Specific IAM User>"
            },
            "Action": "sts:AssumeRole"
        }
    ]
}
```

## Host Managementの設定
AWS Systems Manager > Quick SetupからHost Managemantを選択します。
![スクリーンショット 2023-10-21 16.09.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/18c32e4e-5c99-da72-eb39-e4a2e467d060.png)

ターゲットから該当のEC2インスタンスを選択し、Host Managementを作成します。
![スクリーンショット 2023-10-21 16.11.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/1130272f-a361-ea2f-91aa-cacf520b42f8.png)

# 接続手順

## 一時的な資格情報の取得
IAMロールとAWS Security Token Service(STS)を利用することで、アクセスキーを使用せずにAWS CLIでリソースを操作することができます。

まず、以下のコマンドで一時的な資格情報を取得します。
    
```bash
$ aws sts assume-role --role-arn "arn:aws:iam::<Account ID>:role/AmazonSSMRoleForInstancesQuickSetup" --role-session-name "RDSPortForwardingSession"
```
    
出力された`AccessKeyId`、`SecretAccessKey`、`SessionToken`をコピーします。    
```yaml
{
    "Credentials": {
        "AccessKeyId": "XXXXXXXXXXXXX",
        "SecretAccessKey": "XXXXXXXXXXXXX",
        "SessionToken": "XXXXXXXXXXXXX",
        "Expiration": "2023-10-19T05:55:50+00:00"
    },
    "AssumedRoleUser": {
        "AssumedRoleId": "XXXXXXXXXXXXX:RDSPortForwardingSession",
        "Arn": "arn:aws:sts::XXXXXXXXXXXXX:assumed-role/AmazonSSMRoleForInstancesQuickSetup/RDSPortForwardingSession"
    }
}
```
資格情報(`AccessKeyId`、`SecretAccessKey`、`SessionToken`)を環境変数に設定します。

```bash
export AWS_ACCESS_KEY_ID=<AccessKeyId>
export AWS_SECRET_ACCESS_KEY=<SecretAccessKey>
export AWS_SESSION_TOKEN=<SessionToken>
```
    
## ポートフォワーディングのセッション開始
以下のコマンドを実行します。

```bash
$ aws ssm start-session --target [インスタンスID] --document-name AWS-StartPortForwardingSessionToRemoteHost --parameters host=[DBエンドポイント],portNumber="3306",localPortNumber=[任意のポート]
```
任意のポートには使用していないポート(`8888`など)を設定します。

こんな感じのものが表示されれば成功です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/df215726-c456-e8ed-b29d-0082fe224920.png)

また、接続に成功すると、Session Managerのコンソール画面にセッションが表示されるようになります。
![スクリーンショット 2023-10-21 16.28.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/27aea321-88da-e986-317b-4c88fcbac113.png)

## DBクライアントツール(MySQL Workbench他)の設定
以下の設定を行います。
- Connection Method: `Standard (TCP/IP)`
- Hostname: `127.0.0.1` (`localhost`だと接続失敗するので注意)
- Port: フォワーディングしたポート番号
- Username: DBユーザー名

![スクリーンショット 2023-10-21 16.38.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/089dcf5e-8d44-1ea7-b6f6-48a12f4f0d31.png)

「Test Connection」を押して、Password入力を求められたらDBユーザーのパスワードを入力します。
以下のダイアログが表示されれば接続成功です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/619f5c81-3c89-aec0-b691-59a1508c268c.png)