2023/6/28現在
AWSが想定した動作ではなかったのか、ssh(22)かrdp(3389)以外では利用できなくなったようです。。
前置き
2023年6月13日に開催されたAWS re:InforceでEC2 Instance Connect Endpoint (EIC エンドポイント) によるパブリックIP不要でEC2インスタンスにSSHやRDPができる新機能が発表されました。
リリースを読んだ当初はEC2に対してのみ使用可能なものと思っていましたが、クラスメソッドさんの↓の記事によりRDSにも直接繋げる事ができるということで、今まで踏み台コンテナを立ててポートフォワーディングしていたものを置き換えられるのではと思い、接続用スクリプトを作ってみました。
因みにこれまでは
↓のやり方で踏み台用コンテナを立ててポートフォワーディングしていました。読めば分かりますが要素が多くパッとこれで出来るよと言ってもハードルを感じられる事が多かったです。
前提条件
- EC2 Instance Connect Endpoint(EICE)が接続先RDSと同様のVPC内に作成されていること
- RDSのセキュリティグループはEC2 Instance Connect Endpointで使用しているセキュリティグループからのインバウンドが許可されていること
- EC2 Instance Connect Endpointで指定するセキュリティグループはRDSセキュリティグループへのアウトバウンドが許可されていること
- EICE名及びRDSクラスター名に環境名(prod/stg/dev)が含まれていること
EC2 Instance Connect Endpointの作成
- VPCエンドポイント作成画面より[EC2 Instance Connect Endpoint]を選択
- VPC及びセキュリティグープを選択
※EC2 Instance Connect Endpointで使用するセキュリティグループのインバウンドルールは不要で、ただし、接続先RDSに使われているSGへのアウトバウンドルールは必須
- Subnetを選択して「エンドポイントを作成」を押下
※SubnetはプライベートサブネットでもOK
スクリプト
※スクリプト内で選択するポートは一般的な番号を入れてます。お好みで変えてください。
※DBへのアクセスは環境毎に可否があると思うので、現場に合わせて使ってください。ここではprodへの書き込みは制限するパターンで書いてます。
#!/bin/bash
##-----------------
## 第1引数にAWScliのprofileを指定
##-----------------
PROFILE=$1
echo "対象環境を数字から選択してください"
select ENV in "prod" "stg" "dev"
do
echo "${ENV}が選択されました。"
ENV=${ENV}
break
done
if [ -z $PROFILE ]; then
echo "aws cli プロファイル名を数字から選択してください"
PROFILES=$(aws configure list-profiles)
select PROFILE in $PROFILES
do
echo "${PROFILE}が選択されました。"
PROFILE=${PROFILE}
break
done
fi
##-----------------
## EICE選択
##-----------------
echo "EC2 Instance Connect Endpointを数字から選択してください"
TARGET_EICE_NAMES=$(aws ec2 describe-instance-connect-endpoints \
--query 'InstanceConnectEndpoints[]' \
--profile $PROFILE \
--output text | grep $ENV | awk '{print $3}')
if [[ -z "$TARGET_EICE_NAMES" ]]; then
echo "選択した環境にEC2 Instance Connect Endpointは存在しません。"
exit 1
fi
select TARGET_EICE_NAME in $TARGET_EICE_NAMES
do
echo "${TARGET_EICE_NAME}が選択されました。"
TARGET_EICE_NAME=${TARGET_EICE_NAME}
break
done
TARGET_EICE_ID=$(aws ec2 describe-instance-connect-endpoints \
--query 'InstanceConnectEndpoints[].InstanceConnectEndpointId' \
--filters Name=tag:Name,Values=$TARGET_EICE_NAME \
--profile $PROFILE \
--output text)
##-----------------
## ReaderWriter選択
##-----------------
echo "接続するエンドポイントの種類を数字から選択してください"
select RDS_ENDPOINT in "Writer" "Reader"
do
if [ "${ENV}" = "prod" ] && [ "${RDS_ENDPOINT}" = "Writer" ]; then
echo "Writerはprodでは選択できません。"
else
echo "${RDS_ENDPOINT}が選択されました。"
if [ "${RDS_ENDPOINT}" = "Writer" ]; then
RDS_ENDPOINT="Endpoint"
else
RDS_ENDPOINT="ReaderEndpoint"
fi
break
fi
done
##-----------------
## 接続先RDSエンドポイント選択
##-----------------
echo "接続するDatabaseを数字から選択してください"
TARGET_HOSTS=$(aws rds describe-db-clusters \
--profile $PROFILE \
--query 'DBClusters[?contains(Endpoint, `'$ENV'`)].['$RDS_ENDPOINT']' \
--output text)
select TARGET_HOST in $TARGET_HOSTS
do
echo "${TARGET_HOST}が選択されました。"
TARGET_HOST=${TARGET_HOST}
break
done
TARGET_HOST_IP=$(dig $TARGET_HOST +short | grep '^[0-9]')
##-----------------
## 接続先ポート
##-----------------
echo "接続先ポートを入力してください:"
select TARGET_PORT_OPTION in "入力" "選択"
do
case ${TARGET_PORT_OPTION} in
"入力")
read -p "接続先ポートを入力してください: " TARGET_PORT
while true
do
if [[ ${TARGET_PORT} =~ ^[0-9]+$ ]]; then
echo "${TARGET_PORT}が選択されました。"
break
else
echo "無効な入力です。ポート番号を入力してください。"
read -p "接続先ポートを入力してください: " TARGET_PORT
fi
done
break
;;
"選択")
select PORT_SELECT in "5432" "3306" "3389"
do
echo "${PORT_SELECT}が選択されました。"
TARGET_PORT=${PORT_SELECT}
break
done
break
;;
*)
echo "無効な選択です。もう一度選択してください。"
;;
esac
done
##-----------------
## localポート
##-----------------
echo "localポートを入力してください:"
select LOCAL_PORT_OPTION in "入力" "選択"
do
case ${LOCAL_PORT_OPTION} in
"入力")
read -p "localポートを入力してください: " LOCAL_PORT
while true
do
if [[ ${LOCAL_PORT} =~ ^[0-9]+$ ]]; then
echo "${LOCAL_PORT}が選択されました。"
break
else
echo "無効な入力です。ポート番号を入力してください。"
read -p "localポートを入力してください: " LOCAL_PORT
fi
done
break
;;
"選択")
select PORT_SELECT in "15432" "13306" "13389"
do
echo "${PORT_SELECT}が選択されました。"
LOCAL_PORT=${PORT_SELECT}
break
done
break
;;
*)
echo "無効な選択です。もう一度選択してください。"
;;
esac
done
##-----------------
## RDS接続
##-----------------
echo "RDSに接続します。"
aws ec2-instance-connect open-tunnel \
--instance-connect-endpoint-id $TARGET_EICE_ID \
--private-ip-address $TARGET_HOST_IP \
--local-port $LOCAL_PORT \
--remote-port $TARGET_PORT \
--profile $PROFILE
終わりに
ローカルからClinet経由で動作確認しましたが問題なくRDSに接続できました!これまでのやり方ではセットアップに必要な要素が多く若干面倒くささがありましたがそれらが今回の新機能でグッと減り楽になりました。
また、EC2 Instance Connect Endpointは利用料がかからないのも嬉しいポイントです。セキュリティ要件的に使用して問題ないかきちんと確認した上で活用していきたいですね。
おまけ
EC2 Instance Connect Endpointによる接続を制限したい場合は、以下の様なポリシーを適切な箇所(SSOアクセス許可セットやSCP、IAMRoleなど)に付与する事で制限可能です。条件付けはお好みで。
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DenyEICE_OpenTunnel",
"Effect": "Deny",
"Action": "ec2-instance-connect:OpenTunnel",
"Resource": "arn:aws:ec2:*:xxxxxxxxxxxx:instance-connect-endpoint/*"
}
]
}
弊社でQiitaEngineerFesta2023の記事投稿キャンペーンを実施してます。
参加&投稿お待ちしております!