AWS Fargate + System Manager で構成するサーバーレスな Bastion を導入したのでその仕組みと運用を解説
プライベートなネットワーク環境に配置されたデータベースに接続するための踏み台の仕組みとして知られる Bastion を Fargate + System Manager(以降、SSMと呼称) で構成する取り組みを社内で行いました。
すでに同じ取り組みに関する記事が世の中に出回っており、目新しいものでは無いと思いますが、今回対応した内容に関し、その仕組みと運用手順を解説していきます。
対象読者/話さないこと
ECS、IAMに関する知識を一定保有している方を対象読者として想定しているため、ポイントを絞って解説します。
ハンズオンのような詳細な手順の解説は行いません。
モチベーション
元々 Bastion に相当する仕組みは EC2 + System Manager の構成で存在していました。
EC2、AMI といったリソースの管理コストや、セキュリティ対策等のメンテナンスコストを削減していきたいというのが主なモチベーションでした。
Fargate を SSM で使用可能にする
Fargate を SSM で使用できるよう、コンテナ起動時に SSM Agent を実行する必要があります。
はじめに、上記を実現する Docker の実装について解説していきます。
Docker Image に SSM Agent インストール
SSM Agent の他、Bastion にログインして作業を行う上で必要になるパッケージインストールを行うよう Dockerfile を構成します。
今回は RDS(MySQL) に接続、AWS CLI を経由した作業をすることを想定し、各種 CLI をインストールしました。
コンテナ起動時に SSM Agent 実行
Fargate を SSM で使用可能にするためハイブリットアクティベーションを作成し、SSM Agent を起動します。
以前に SSM に登録したインスタンスは何もしないと実行が終了したものも含め、SSMに残り続けてしまいます。
今回、Bastion は一台常時稼働させることとしたかったので、SSM Agent 起動の前処理として Bastion として登録されている既存のインスタンス および 期限切れのアクティベーション の削除を行うこととしました。
以上のことを行うための entrypoint.sh の実装は以下のようになります。
アクティベーション作成時に指定するIAMサービスロール(上記のコードでは、ssm-service-role としている)に関しては、以下を参照してください。
ECS による実行管理
タスク定義
特段な変わった設定が必要ということはありません。Fargate で動作するように構成します。
コンテナ起動時にSSM関連の操作を行うため taskRoleArn に指定する IAM Role には必要なSSMの操作を許可するポリシーを割り当てます。
ECS Service による実行管理
Fargate は AWS でメンテナンスを行う事情で定期的に停止することがあるため ECS Service で実行管理し、常時1台稼働するようにしています。
# 仮に AWS CLI から ECS Service 作成する場合
aws ecs create-service --cluster claster_name \
--service-name service_name \
--task-definition serverless-bastion \
--desired-count 1 \
--launch-type FARGATE \
--platform-version LATEST \
--network-configuration "awsvpcConfiguration={subnets=[subnet-123],securityGroups=[sg-123],assignPublicIp=DISABLED}"
Bastion から RDS 等、VPC内に配置したAWSリソースに問い合わせる運用を想定している場合は、適時サブネットやセキュリティーグループに適切な情報を割り当てます。
Bastion の稼働状況を ECS の基盤に任せられるのは良いなと感じています。
手元のPCから接続
手元のPCから実行中の Fargate へ接続するための手順を解説します。
事前準備
手元から SSM の操作を行うためにプラグインのインストールが必要となります。
SSM経由でコンテナに接続
SSMに登録されている Fargate インスタンスのインスタンスIDを指定してコンテナに接続することができます(要 AWS Creadentials )。
# 以下のコマンドで接続するインスタンスのIDを確認できます
aws ssm describe-instance-information --query "InstanceInformationList[?PingStatus=='Online' && Name=='funds-bastion'].InstanceId" --output text
mi-xxx
# 前段のコマンドで取得したインスタンスIDを指定して接続します
aws ssm start-session --target mi-xxx
SSHを使用せずともAWSの認証情報のみでインスタンスにログインでき、Public IP の発行など不要な点がとても良いなと感じています。
ポートフォワーディングしてRDSへ接続
SSMではリモートホストをポートフォワーディングする機能を備えています。https://aws.amazon.com/jp/blogs/news/use-port-forwarding-in-aws-systems-manager-session-manager-to-connect-to-remote-hosts-jp/
これを活用し、手元から RDS のリモートホストへの接続することとしています。
export INSTANCE_ID=<インスタンスID>
export DB_HOST=<RDSのホスト>
aws ssm start-session --target $INSTANCE_ID \
--document-name AWS-StartPortForwardingSessionToRemoteHost \
--parameters "{\"host\":[\"$DB_HOST\"],\"portNumber\":[\"3306\"], \"localPortNumber\":[\"3306\"]}"
別途、ターミナルセッションを立ち上げて MySQL CLI などを使ってアクセスすることができます。
mysql -h 127.0.0.1 -P 3306 -u <ユーザー名> -p
導入後の変化(嬉しみ)
セキュリティ対策等のメンテナンスコスト削減
Fargate ないし、サーバーレスアーキテクチャを採用しているAWSのサービスはOSレイヤの管理をAWS側の責任範囲として任せることができます。
そのため、EC2 や AMI のセキュリティ対策といったメンテナンス作業のコストを削減することができました。
SSH鍵の管理が不要に
EC2での運用していた際は、ローカルから Bastion への接続は SSH にて行っていました。
今回の対応で上記の操作はAWSの認証情報のみで良くなったため、結果として、SSH鍵は不要、管理する機密情報を削減でき、セキュリティの向上に繋げることができました。
デプロイにインフラプロビジョニングが不要に
EC2で運用していた際は、インスタンス、AMI を Terraform にて管理していたことに加えて、terraform apply する度にインスタンスを再作成する仕組みになっていました。
今回の対応で Terraform では ECS の構成のみ管理し、Fargate インスタンスのデプロイは Terraform からは切り出して個別デプロイできるようになり、デプロイ時の影響範囲を最小化することができました。
副次的に terrafom の構成管理から EC2、AMI等のリソースをごっそり削除できるようになったことも嬉しいポイントでした。
おわりに
Bastion の基盤に Fargate を採用したことで、管理対象のリソースとメンテナンスコストを同時に削減することができ、大変満足しています。
ソフトウェアの開発に運用作業はつきものですが、なるべくコストはかけたくないですし、アプリケーション開発に集中したいものですよね。
まだまだ、社内ではやりたいことは山積みですが、今回の対応で目指すところへ一歩近づけたのかなと思っています。
最後まで読んでいただきありがとうございました。
参考にした記事
この記事が気に入ったらサポートをしてみませんか?