RDSにローカルから接続する踏み台EC2をTerraformで作成する #490
大まかな手順
以下の手順で実行できます。
踏み台サーバーを起動する
SSMでポートフォワーディングを実行する
ローカルからmysqlコマンドでRDSに接続する
前提
ローカルでaws cliを実行できる状態にしておく必要があります。当然認証も突破していないといけません。今回はこの部分は割愛します。
また、RDSはMySQLを使っている想定です。
それでは各手順を見ていきます。
踏み台サーバーを起動する
踏み台サーバーの設定(EC2)
EC2サーバーを定義します。ちょっとした工夫として、amiはハードコードせずに動的に取得しています。
resource "aws_instance" "bastion" {
ami = data.aws_ssm_parameter.amazon_linux_2_arm.value
instance_type = var.ec2_instance_type
subnet_id = var.subnet_id
associate_public_ip_address = false
iam_instance_profile = aws_iam_instance_profile.ssm_profile.name
vpc_security_group_ids = [aws_security_group.bastion.id]
user_data = file("${path.module}/user_data.sh")
tags = {
Name = "${var.project}-bastion"
}
}
# fetch newest amazon linux2 instance profile
data "aws_ssm_parameter" "amazon_linux_2_arm" {
name = "/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-arm64-gp2"
}
user_dataには以下のシェルファイルを設定します。サーバー起動時に、RDSへの接続に必要なソフトをインストールして使える状態にしています。
#!/bin/bash
set -eux
echo "STARTING SETUP..."
yum update -y
sudo yum install -y mysql
# update ssm-agent
sudo yum install -y https://s3.ap-northeast-1.amazonaws.com/amazon-ssm-ap-northeast-1/latest/linux_arm64/amazon-ssm-agent.rpm
sudo systemctl restart amazon-ssm-agent
echo "FINISH SETUP"
IAMロールの設定
SSMを実行するためのロールを付与します。
resource "aws_iam_role" "bastion_ssm" {
name = "BastionHostSSMRole"
assume_role_policy = jsonencode({
"Version" : "2012-10-17",
"Statement" : [{
"Action" : "sts:AssumeRole"
"Effect" : "Allow",
"Principal" : {
Service : "ec2.amazonaws.com"
}
}]
})
}
resource "aws_iam_role_policy_attachment" "ssm_attach" {
role = aws_iam_role.bastion_ssm.name
policy_arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
}
resource "aws_iam_instance_profile" "ssm_profile" {
name = "BastionHostSSMProfile"
role = aws_iam_role.bastion_ssm.name
}
セキュリティグループの設定(踏み台サーバー側)
以下は1例です。最低限の権限を付与します。
# 踏み台サーバーが外部や他リソースにアクセスする際の制限を定義する
resource "aws_security_group" "bastion" {
name = "${var.project}-bastion"
vpc_id = var.vpc_id
egress {
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"] # SSMエージェントがインターネットと通信するため
}
# RDSインスタンスに対してポートフォワーディングを行うため、BastionホストがRDSのポート3306に直接通信できるようにする
egress {
from_port = 3306
to_port = 3306
protocol = "tcp"
security_groups = [var.db_security_group_id] # このセキュリティグループに属するリソースへのアクセスが許可される
}
tags = {
Name = "${var.project}-bastion"
}
}
セキュリティグループの設定(RDS側)
踏み台サーバー用の設定のみを書き出します。MySQL
# 踏み台サーバーがRDSに接続することを許可するためのルール
resource "aws_security_group_rule" "db_from_bastionr" {
type = "ingress"
from_port = 3306
to_port = 3306
protocol = "tcp"
security_group_id = var.db_security_group_id
source_security_group_id = aws_security_group.bastion.id # このセキュリティグループに属するリソースからのアクセスが許可される
}
これで準備が整いました。
SSMでポートフォワーディングを実行する
肝となるコマンド部分だけ記載します。シェルファイルで諸々の情報を動的に取得するようにしています(詳細は次の記事でまとめます)。
ssm_port_forward() {
HOST=$1
PORT=$2
INSTANCE_ID=$3
aws ssm start-session \
--target ${INSTANCE_ID} \
--document-name AWS-StartPortForwardingSessionToRemoteHost \
--parameters '{"host":["'"${HOST}"'"],"portNumber":["3306"], "localPortNumber":["'"${PORT}"'"]}' \
--profile ${PROFILE}
}
aws ssm start-sessionの、AWS-StartPortForwardingSessionToRemoteHostという機能を使ってポートフォワーディングしています。
parametersのHOSTにはRDSのエンドポイントを、PORTにはローカルのポート番号(任意)を指定します。
profileはaws cliの認証に使っているものを指定します。
コマンドが無事に実行されれば、DBがConnection待ちの状態になるはずです。
ローカルからmysqlコマンドでRDSに接続する
ローカルのポートにRDSをポートフォワーディングしているので、いつも通りmysqlにアクセスコマンドを叩けばOKです。hostにはlocalhostを、portには先ほどのポート番号を指定します。
mysql --user=your_user_name --password='your_password' --host=127.0.0.1 --port=ポートフォワーディング時に指定したポート番号
DataGripなどを使用している場合でも、上記の内容で設定すればもちろん使えます。
ここまでお読みいただきありがとうございました!