1. はじめに
「AWSコンテナ設計・構築[本格]入門」という書籍に、
AWS Fargateを使ったbastion設計の章があります。(Chapter5-7)
個人開発している環境において、AWS Fargateとセッションマネージャーを使ったbastionを導入しようとしたのですが、
なかなかうまく実装できませんでした。
この記事では、実装するうえで行き詰った個所を振り返り、共有することを目的としています。
2. つまずいた箇所
今回つまずいたポイントは2か所です。
2.1. private subnet上のECSから、ECRに保存されているコンテナイメージがpullできなかった
2.2. Session Managerのアクティベーションコードを発行できなかった
2.1. private subnet上のECSから、ECRに保存されているコンテナイメージがpullできなかった
ECRはリージョナルサービスのため、VPC内には存在しません。
また、今回のECSはprivate subnet上に配置されています。
つまりECSとECRの間に、次のいずれかがないと通信できません。
(1)VPC Endpont
(2)NAT Gateway
(1)VPC Endpontを使う場合、インターネットへ出ることなくECRにアクセスでき、よりセキュアです。
(2)の場合、インターネットへの接続が必要になるのと、データ通信料がコストとしてかかってきてしまいます。
なるべくコストは抑えたかったので、(1)VPC Endpointを採用することにしました。
ECS ⇔ ECR間の通信に必要なEndpointは次の3つです。
- ECR API ( com.amazonaws.<リージョン名>.ecr.api )
「aws ecr get-login-password」などのECR APIを呼び出す際に利用 - ECR DKR ( com.amazonaws.<リージョン名>.ecr.dkr )
「docker push」などのDockerクライアントコマンドの呼び出しに利用 - S3 ( com.amazonaws.<リージョン名>.s3 )
Dockerイメージの取得に利用
これらのエンドポイントは、ECSタスク用のセキュリティグループに対して設定されている必要があります。
※S3はゲートウェイ型のためコストは発生しませんが、ECR APIおよびECR DKRはインターフェース型のため、コストがかかります。
また、ECSタスクのログをCloudWatchへ保管するためのVPC Endpointも作成しました。
- CloudWatch Logs ( com.amazonaws.<リージョン名>.logs )
2.2. セッションマネージャーからECSタスクへ接続ができなかった
2.1. で無事にECSタスクが起動できるようになったのですが、
次はセッションマネージャーからECSタスクへ接続ができない事象が発生しました。
原因としては2つ考えられます。
(1)IAM権限が不足している
(2)ネットワーク設定が不足している
(1)IAM権限が不足している
セッションマネージャーからECSタスクへ接続するためには、
アクティベーションコードとIDを払い出し、
タスク内のSSMエージェントに対して登録する
必要があります。
ECSタスク内でアクティベーションコードを発行するので、ECSタスクに対してSystems Managerを操作する権限が必要でした。
また、アクティベーションコードを発行するコマンドでSystems Manager用のロールを指定する必要がありました。
したがって、次の権限が必要になります。
- ECSタスクが利用するロールとポリシー
ECSタスクのロールにアタッチ
# ECS TaskのIAMロールにSSM Activationのポリシーを追加
resource "aws_iam_role_policy" "ssm_activation_policy" {
name = "ssm-activation-policy"
role = aws_iam_role.ecs_task_role.id
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Action = [
"ssm:CreateActivation",
"ssm:DeleteActivation",
"iam:PassRole"
]
Resource = "*"
}
]
})
}
# SSM Service Role (if not already existing)
resource "aws_iam_role" "ssm_service_role" {
name = "SSMServiceRole"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = {
Service = "ssm.amazonaws.com"
}
}
]
})
}
- ECSタスクがアクティベーションコード発行時に指定するロール
resource "aws_iam_role_policy_attachment" "ssm_service_role_policy" {
role = aws_iam_role.ssm_service_role.name
policy_arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
}
(2)ネットワーク設定が不足している
Systems ManagerへのVPCエンドポイントとして、次の2つを作成する必要があります。
- ssm ( com.amazonaws.<リージョン名>.ssm )
アクティベーションを作成するAPIなどで利用 - ssmmessages ( com.amazonaws.<リージョン名>.ssmmessages )
セッションマネージャーの接続を確立するために利用
これらのエンドポイントは、VPCエンドポイント用のセキュリティグループを選択する必要があります。
# VPCエンドポイント用のセキュリティグループ
resource "aws_security_group" "vpc_endpoint_sg" {
name = "vpc-endpoint-sg"
description = "Security group for VPC endpoints"
vpc_id = aws_vpc.main.id
ingress {
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = [aws_vpc.main.cidr_block]
}
}
また、ECSとセッションマネージャーの通信を確立させるために、次のセキュリティグループも必要でした。
resource "aws_security_group_rule" "ecs_to_vpc_endpoints" {
type = "egress"
from_port = 443
to_port = 443
protocol = "tcp"
security_group_id = aws_security_group.ecs_security_group.id
source_security_group_id = aws_security_group.vpc_endpoint_sg.id
}
まとめ
上記の対応をすることで、無事にpraivate subnet上のECSタスクから、セッションマネージャーを利用して
praivate subnet上のRDSへ接続することができました。
大きくはネットワークの問題と権限周りの問題でした。
また、VPCエンドポイントは存在しているだけでコストがかかるので、bastionを利用しないときは
Terraformでコメントアウトして反映させています。
この辺も自動化できる仕組みを考えたいです。
本記事は「AWSコンテナ設計・構築[本格]入門」を参考にしていますので、まだ読んでない方はぜひ一読をおすすめします。
Comments
Let's comment your feelings that are more than good