概要
たまにVPC内のリソース(RDSやElastiCache)に手元のクライアントだったり、シェル経由でアクセスしたいことがあります。
そんな時に踏み台サーバーを用意するかと思うのですが、ECSのタスクを使うと素早く起動できて便利です。
やり方
事前にセッションマネージャープラグインをインストールして、タスクロールに必要な権限を当ててECS Execが使える状態にしておきます。
デバッグ用にAmazon ECS Exec を使用 - Amazon ECS
まずタスク定義bastionを用意します。
踏み台サーバーに使いたいイメージを指定します。
ここではイメージにubuntu:latestを、コマンドはsleep 1hを指定しています。
(自分の場合、踏み台サーバーでの作業はそれほど長くないので1時間で終了するようにしています)
あとはsession manager経由で入れるように --enable-execute-command
オプションを指定してタスクを起動するだけです。
(節約したい場合はFargate Spotを使用した方が良いかと思います)
export CLUSTER=example-cluster
export SUBNETS=subnet-xxxxxxxf,subnet-xxxxxxxx,subnet-xxxxxxxx
export SECURITY_GROUPS=sg-xxxxxxxxxxxxxxxxx
export TASK_ARN=$(aws ecs run-task --cluster $CLUSTER --count 1 --launch-type FARGATE \
--enable-execute-command \
--network-configuration "awsvpcConfiguration={subnets=[$SUBNETS],securityGroups=[$SECURITY_GROUPS],assignPublicIp=ENABLED}" \
--task-definition bastion \
--output json \
| jq -r .tasks[0].containers[0].taskArn)
execute-commandでシェルを取ったり
aws ecs execute-command \
--cluster $CLUSTER \
--task $TASK_ARN \
--interactive \
--command "/bin/bash"
port forwarding もできます。
export TASK_ID=${TASK_ARN##*/}
export RUNTIME_ID=$(aws ecs describe-tasks --cluster $CLUSTER --task $TASK_ID | jq -r .tasks[0].containers[0].runtimeId)
aws ssm start-session \
--target ecs:${CLUSTER}_${TASK_ID}_${RUNTIME_ID} \
--document-name AWS-StartPortForwardingSession \
--parameters '{"portNumber":["80"], "localPortNumber":["8080"]}'
リモートホストへのfowarding は SSM agentのバージョンが SSM Agent version: 3.1.1260.0
だったのでまだ使えなかったですが、しばらくすれば使えるようになるかと思います。
それまではnetcat等で転送すると良いかと思います。
# ローカルの6379番ポートをElastiCacheに転送
apt install netcat
mkfifo fifo
nc -k -l -p 6379 < fifo | nc hoge-redis.xxxxxx.ng.0001.apne1.cache.amazonaws.com 6379 > fifo
関数定義
オプションが多くてコマンドが複雑なので、自分の場合は以下のような関数をbashrcに書いています。
export CLUSTER=example-cluster
export SUBNETS=subnet-xxxxxxxf,subnet-xxxxxxxx,subnet-xxxxxxxx
export SECURITY_GROUPS=sg-xxxxxxxxxxxxxxxxx
export TASK_DEFINITION=bastion
export ECS_BASTION_ENV_FILE=~/ecs-bastion.sh
function ecs-bastion-create {
local TASK_ARN=$(aws ecs run-task --cluster $CLUSTER --count 1 --launch-type FARGATE \
--enable-execute-command \
--network-configuration "awsvpcConfiguration={subnets=[$SUBNETS],securityGroups=[$SECURITY_GROUPS],assignPublicIp=ENABLED}" \
--task-definition $TASK_DEFINITION \
--output json \
| jq -r .tasks[0].containers[0].taskArn)
echo "wait for task running"
until [ "$(aws ecs describe-tasks --cluster $CLUSTER --tasks $TASK_ARN | jq -r .tasks[0].containers[0].lastStatus)" = "RUNNING" ]
do
echo -n "."
sleep 30
done
sleep 30
echo -e "\ncreate task $TASK_ARN"
echo "export TASK_ARN=$TASK_ARN" > $ECS_BASTION_ENV_FILE
}
function ecs-bastion-get-env {
if [ ! -r $ECS_BASTION_ENV_FILE ];then
echo "$ECS_BASTION_ENV_FILE not found"
return 1
else
source $ECS_BASTION_ENV_FILE
fi
}
function ecs-bastion-shell {
ecs-bastion-get-env || return 1
aws ecs execute-command \
--cluster $CLUSTER \
--task $TASK_ARN \
--interactive \
--command "/bin/bash"
}
function ecs-bastion-port-forwarding {
ecs-bastion-get-env || return 1
local TASK_ID=${TASK_ARN##*/}
local RUNTIME_ID=$(aws ecs describe-tasks --cluster $CLUSTER --task $TASK_ID | jq -r .tasks[0].containers[0].runtimeId)
local OPT
OPTIND=1
while getopts "hp:l:" OPT
do
case $OPT in
"h" )
echo "usage: ecs-bastion-port-fowarding -p ecsPort -l localPort"
return
;;
"l" )
local localport=$OPTARG
;;
"p" )
local ecsport=$OPTARG
;;
esac
done
echo "localPort=$localport"
echo "ecsPort=$ecsport"
aws ssm start-session \
--target ecs:${CLUSTER}_${TASK_ID}_${RUNTIME_ID} \
--document-name AWS-StartPortForwardingSession \
--parameters "{\"portNumber\":[\"$ecsport\"], \"localPortNumber\":[\"$localport\"]}"
}
function ecs-bastion-stop {
ecs-bastion-get-env || return 1
aws ecs stop-task --cluster $CLUSTER --task $TASK_ARN
rm $ECS_BASTION_ENV_FILE
}
使用例
以下はbastionタスクを立ち上げて、port forwarding => rsyncで踏み台サーバーの/etc配下をコピーするという例です。
# bastionタスク作成
$ ecs-bastion-create
# shellで入る
$ ecs-bastion-shell
# 踏み台サーバーでrsync立ち上げ
apt update && apt install -y rsync
cat <<EOF > /etc/rsyncd.conf
uid = root
gid = root
pid file = /tmp/rsyncd.pid
log file = /tmp/rsyncd.log
read only = yes
max connections = 10
[root]
comment = rsyncd server / directory
path = /
numeric ids = yes
EOF
rsync --daemon
# 別ターミナルでport fowarding
$ ecs-bastion-port-forwarding -l 8730 -p 873
# 別ターミナルでetc配下をコピー
$ rsync -av rsync://127.0.0.1:8730/root/etc/ /tmp/etc/
# 停止(忘れても1時間で消える)
$ ecs-bastion-stop