プライベートサブネットに配置しているDBにローカルのDBクライアントからセキュアに接続する方法

プライベートサブネットに配置しているAuroraなどのDBに、ローカルのDBクライアント（DBeaverやA5:SQL Mk2など）からセキュアに接続する方法を紹介します。
## 従来の接続方法
以前は、プライベートサブネット上のDBに接続する際、パブリックサブネット上に設置された踏み台ホストを経由して接続する方法が一般的でした。
しかし、この方法は一時的にパブリックサブネットを作成する必要があり、セキュリティ面での不安がありました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3694674/d81161bd-8e72-b69a-5ee5-30d4ff57e5d5.png)
## 今回構築する接続方法
System Managerの機能を活用し、プライベートサブネット上の踏み台ホスト経由で、DBに接続したいと思います。
この構成の場合、パブリックサブネットが無くてもプライベートサブネット上のDBに接続可能です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3694674/4d25f828-f0e1-c059-f8f8-16e58bdd0ab5.png)



## 構築手順
### 1. セキュリティグループを作成
踏み台ホスト用とVPCエンドポイント用のセキュリティグループを作成します。
- 踏み台ホスト用セキュリティグループ
インバウンドルール：設定不要
アウトバウンドルール：すべてのトラフィック
- VPCエンドポイント用のセキュリティグループ
インバウンドルール：ポート443、踏み台ホスト用セキュリティグループからのアクセスを許可
アウトバウンドルール：すべてのトラフィック

### 2. プライベートサブネットを作成
踏み台ホスト用とVPCエンドポイント用のプライベートサブネットを作成します。

### 3. VPCエンドポイントを作成
System Manager用のVPCエンドポイントを作成します。※東京リージョンの場合
- com.amazonaws.ap-northeast-1.ssmmessages
- com.amazonaws.ap-northeast-1.ssm

 VPCエンドポイントには手順1で作成したVPCエンドポイント用のセキュリティグループと、手順2で作成したVPCエンドポイント用のプライベートサブネットを指定します。

### 4. VPCのDNS設定を有効化
VPCの設定で「DNS 解決を有効化」と「DNS ホスト名を有効化」を有効化します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3694674/a5495be9-35af-fa7e-29f2-1eb9349a4023.png)

### 5. DBのセキュリティグループを変更
DBのセキュリティグループのインバウンドルールに、踏み台ホスト用セキュリティグループからのアクセスを許可します。

### 6. ECRを作成
踏み台ホストのイメージを保存するリポジトリを作成します。
設定はデフォルトで問題ないです。

### 7. ECSに指定するロールを作成
タスクロールに、下記ポリシーがアタッチされているロールを指定します。
```
{
	"Version": "2012-10-17",
	"Statement": [
		{
			"Sid": "",
			"Effect": "Allow",
			"Action": "iam:PassRole",
			"Resource": "*",
			"Condition": {
				"StringEquals": {
					"iam:PassedToService": "ssm.amazonaws.com"
				}
			}
		},
		{
			"Sid": "",
			"Effect": "Allow",
			"Action": [
				"ssm:RemoveTagsFromResource",
				"ssm:DeleteActivation",
				"ssm:CreateActivation",
				"ssm:AddTagsToResource"
			],
			"Resource": "*"
		}
	]
}

```
タスク実行ロールに、下記ポリシーがアタッチされているロールを指定します。
- AmazonECSTaskExecutionRolePolicy
### 8. SystemManager実行時のIAMロールを作成
下記ポリシーがアタッチされているロールを作成します。
AmazonSSMManagedInstanceCore


### 9. ECSのクラスターとタスク定義を作成
クラスターのインフラストラクチャはFargateを選択します。
タスク定義の主な設定は下記になります。残りはデフォルトの設定で問題ありません。
- 起動タイプ：AWS Fargate
- オペレーティングシステム/アーキテクチャ：Linux/X86_64
- タスクサイズ：0.5vCPU、1GBメモリ
- タスクロール：手順7で作成したタスクロール
- タスク実行ロール：手順7で作成したタスク実行ロール
- イメージ URI：手順6で作成したECRのURIとlatestタグ
- コンテナポート：80

### 10. ECRにイメージをプッシュ
Dockerfileとrun.shファイルを同じディレクトリに作成してECRにlatestタグでプッシュします。
なお、Dockerfileとrun.shは下記の参考書を引用しています。
https://www.amazon.co.jp/dp/4815607656

Dockerfile
```Dockerfile
FROM amazonlinux:2
RUN yum install -y sudo jq awscli shadow-utils htop lsof telnet bind-utils yum-utils && \
    yum install -y https://s3.ap-northeast-1.amazonaws.com/amazon-ssm-ap-northeast-1/latest/linux_amd64/amazon-ssm-agent.rpm && \
    rpm -ivh --nodeps https://download.postgresql.org/pub/repos/yum/reporpms/EL-8-x86_64/pgdg-redhat-repo-latest.noarch.rpm && \
    sed -i 's/\$releasever/7/g' /etc/yum.repos.d/pgdg-redhat-all.repo && \
    sudo yum install -y postgresql14 && \
    adduser ssm-user && echo "ssm-user ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/ssm-agent-users && \
    mv /etc/amazon/ssm/amazon-ssm-agent.json.template /etc/amazon/ssm/amazon-ssm-agent.json && \
    mv /etc/amazon/ssm/seelog.xml.template /etc/amazon/ssm/seelog.xml
COPY run.sh /run.sh
CMD ["sh", "/run.sh"]
```
SSM_SERVICE_ROLE_NAMEの部分は、手順8で作成したロール名を指定してください。
```run.sh
#/bin/sh

# Preparation
SSM_SERVICE_ROLE_NAME="XXXXXXXXXXXX"
SSM_ACTIVATION_FILE="code.json"
AWS_REGION="ap-northeast-1"

# Create Activation Code on Systems Manager
aws ssm create-activation \
--description "Activation Code for Fargate Bastion" \
--default-instance-name bastion \
--iam-role ${SSM_SERVICE_ROLE_NAME} \
--registration-limit 1 \
--tags Key=Type,Value=Bastion \
--region ${AWS_REGION} | tee ${SSM_ACTIVATION_FILE}

SSM_ACTIVATION_ID=`cat ${SSM_ACTIVATION_FILE} | jq -r .ActivationId`
SSM_ACTIVATION_CODE=`cat ${SSM_ACTIVATION_FILE} | jq -r .ActivationCode`
rm -f ${SSM_ACTIVATION_FILE}

# Activate SSM Agent on Fargate Task
amazon-ssm-agent -register -code "${SSM_ACTIVATION_CODE}" -id "${SSM_ACTIVATION_ID}" -region ${AWS_REGION}

# Delete Activation Code
aws ssm delete-activation --activation-id ${SSM_ACTIVATION_ID}

# Execute SSM Agent
amazon-ssm-agent
```
### 11. コマンドプロンプトから接続
下記URLからSession Managerプラグインのインストールと実行を行います。
https://s3.amazonaws.com/session-manager-downloads/plugin/latest/windows/SessionManagerPluginSetup.exe
<br>
踏み台サーバを起動
```
$ aws ecs run-task --cluster "<クラスタ名>" --task-definition "<タスク定義のARN>" --network-configuration "awsvpcConfiguration={subnets=[<手順2で作成した踏み台ホスト用のサブネットID>],securityGroups=[<手順1で作成した踏み台ホスト用のセキュリティグループID>],assignPublicIp=DISABLED} --launch-type FARGATE
```
※マネジメントコンソール上のECSのページから起動してもOK
<br>
インスタンスIDを取得
下記コマンドを実行すると"InstanceId": "XXXXXXXXXXXXX"と表示されるので、インスタンスIDをメモしておきます。
```
$ aws ssm describe-instance-information --instance-information-filter-list key=PingStatus,valueSet=Online
```
※マネジメントコンソール上のSystems Manager（フリートマネージャー）からインスタンスIDを確認してもOK
<br>
ポートフォワーディング
```
$ aws ssm start-session --target <インスタンスID> --document-name AWS-StartPortForwardingSessionToRemoteHost --parameters host="<DBのホスト名>",portNumber="<DBのポート>",localPortNumber="<任意のポート番号>"
```

### 12. 接続確認
Dbeaverから接続します
- Host：localhost
- Port：ポートフォワーディングで指定した任意のポート番号
- Database、ユーザー名、パスワードは各自で設定した内容を入力してください

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3694674/236d731f-2af6-c3ce-6bf3-79645b2f6908.png)

正しく設定できていれば接続できると思います。

## 最後に
踏み台サーバは課金が発生しますので不要になったら削除してください。

## 参考
AWSコンテナ設計・構築[本格]入門
https://www.amazon.co.jp/dp/4815607656