Fargate+Session ManagerポートフォワードでRDSに接続する踏み台サーバをAWS CDKで構築する
はじめに
アルファドライブにて、エンジニアインターンをしております。toshi-bpと申します。
今回はFargate+Session Manager(ポートフォワード機能を使用)でRDS(Aurora Postgres)に接続する踏み台サーバをAWS CDKで実装しました。
導入背景・メリット
背景
以下の背景からFargate + Session Managerを用いた踏み台サーバの構築を行いました。
- 従来はClient VPNを用いた接続手法を採用していた(以下のような問題点が…)
- 初回セットアップ手順が煩雑
- 証明書を持っている人なら誰でも接続可能→チームを抜けた人も接続できる可能性がある
メリット
- AWS CLI・Session Managerプラグインをインストールすれば接続可能に→セットアップ手順の単純化
- IAMを用いた権限の管理→チームを抜けた人がRDSに接続できるリスクの解消
- Client VPNを廃止したことによるAWS費用の削減(月10万円!!!)
踏み台サーバとは
プライベートサブネット上に存在するサーバにアクセスするための中継地点の役割を担うサーバです。
今回はプライベートサブネット上に存在するRDSに接続するための踏み台サーバを実装します。
前提
- CDKプロジェクトは既に作成されているものとします
- 使用言語はTypeScriptです
- 簡単のため、環境指定の細かい部分についてはこの記事では割愛します
- VPC、RDS(Aurora Postgres)は既存のものを使用することを想定しております
- 存在しない場合は新たに作成するコードに置き換えてください
- RDSへの接続はMakefileにて定義したコマンドを用いて行うものとします
- Session Managerプラグインのインストール及びAWSプロファイルの設定は完了しているものとします
結論
ディレクトリ構造
ディレクトリ構造は以下となります。
参考:https://qiita.com/matsunao722/items/f18114c37f1667d171c4
cdkProject
├── bin/
│ └── fargate-bastion.ts
├── config/
│ └── index.ts
├── lib/
│ └── fargate-bastion-stack.ts
├── docker/
│ └── Dockerfile
├── Makefile
└── package.jsonなどの依存関係について記されたファイル群
全体的なコード
全体的なコードは以下です。
-
bin/fargate-bastion.ts
#!/usr/bin/env node import * as cdk from 'aws-cdk-lib'; import 'source-map-support/register'; import { FargateBastionStack } from '../lib/fargate-bastion-stack'; import { devConfig } from "../config" const app = new cdk.App(); const appEnv = app.node.tryGetContext("appEnv") as string; // "dev" new FargateBastionStack( app, devConfig, `${appEnv}-fargate-bastion-stack` );
-
config/index.ts
export type Config = { vpc: { id: string; vpcEndPointSecurityGroup: vpcEndPointSecurityGroup; vpcEndPointSubnets: vpcEndPointSubnet[]; }; fargateSubnets: subnetConfig[]; rdsSecurityGroups: rdsSecurityGroup[]; }; export type subnetConfig = { cidrBlock: string; availabiltyZone: string; }; export type vpcEndPointSubnet = { subnetId: string; availavilityZone: string; routeTableId: string; }; export type vpcEndPointSecurityGroup = { securityGroupId: string; }; export type rdsSecurityGroup = { instanceName: string; securityGroupId: string; }; export const devConfig: Config = { vpc: { id: "AWS上で確認してください", vpcEndPointSecurityGroup: { securityGroupId: "AWS上で確認してください", }, vpcEndPointSubnets: [ { subnetId: "AWS上で確認してください", availavilityZone: "ap-northeast-1a", routeTableId: "AWS上で確認してください", }, { subnetId: "AWS上で確認してください", availavilityZone: "ap-northeast-1c", routeTableId: "AWS上で確認してください", }, ], }, fargateSubnets: [ { cidrBlock: "任意のIPアドレス群", availabiltyZone: "ap-northeast-1a", }, { cidrBlock: "任意のIPアドレス群", availabiltyZone: "ap-northeast-1c", }, ], rdsSecurityGroups: [ { instanceName: "dev-db-cluster", securityGroupId: "AWS上で確認してください", }, ], };
-
lib/fargate-bastion-stack.ts
import * as cdk from "aws-cdk-lib"; import { aws_ec2 as ec2, aws_ecr as ecr, aws_ecs as ecs, aws_iam as iam, aws_logs as cwl, } from "aws-cdk-lib"; import { Destination, DockerImageDeployment, Source, } from "cdk-docker-image-deployment"; import { Construct } from "constructs"; import { join } from "path"; import { Config } from "../config"; export type FargateBastionStackProps = cdk.StackProps & Config & { appEnv: "dev" }; export class FargateBastionStack extends cdk.Stack { constructor( scope: Construct, id: string, props: FargateBastionStackProps ) { super(scope, id, props); const { appEnv } = props; const vpc = ec2.Vpc.fromLookup(this, `Vpc`, { vpcId: props.vpc?.id, }); const cluster = new ecs.Cluster(this, `FargateBastionCluster`, { vpc, clusterName: `${appEnv}-fargate-bastion-cluster`, }); const taskRole = new iam.Role(this, `FargateBastionTaskRole`, { assumedBy: new iam.ServicePrincipal("ecs-tasks.amazonaws.com"), roleName: `${appEnv}-fargate-bastion-task-role`, }); taskRole.addToPolicy( new iam.PolicyStatement({ effect: iam.Effect.ALLOW, actions: [ "ssmmessages:CreateControlChannel", "ssmmessages:CreateDataChannel", "ssmmessages:OpenControlChannel", "ssmmessages:OpenDataChannel", "logs:DescribeLogGroups", "logs:CreateLogStream", "logs:DescribeLogStreams", "logs:PutLogEvents", ], resources: ["*"], }) ); const taskExecutionRole = new iam.Role( this, `FargateBastionTaskExecutionRole`, { assumedBy: new iam.ServicePrincipal("ecs-tasks.amazonaws.com"), roleName: `${appEnv}-fargate-bastion-task-execution-role`, managedPolicies: [ iam.ManagedPolicy.fromAwsManagedPolicyName( "service-role/AmazonECSTaskExecutionRolePolicy" ), ], } ); taskExecutionRole.addToPolicy( new iam.PolicyStatement({ effect: iam.Effect.ALLOW, actions: ["ecs:ExecutionCommand"], resources: [cluster.clusterArn], }) ); // create Subnets for proxy-to-rds const fargateSubnets: ec2.Subnet[] | undefined = props.fargateSubnets?.map( (subnet) => { return new ec2.Subnet( this, `FargateBastionSubnet${subnet.availabiltyZone}`, { availabilityZone: subnet.availabiltyZone, vpcId: props.vpc?.id!, cidrBlock: subnet.cidrBlock, mapPublicIpOnLaunch: false, } ); } ); const fargateTaskDef = new ecs.FargateTaskDefinition( this, `FargateBastionTaskDefinition`, { taskRole: taskRole, executionRole: taskExecutionRole, family: `${appEnv}-fargate-bastion-task-definition`, cpu: 256, memoryLimitMiB: 512, runtimePlatform: { operatingSystemFamily: ecs.OperatingSystemFamily.LINUX, cpuArchitecture: ecs.CpuArchitecture.ARM64, }, } ); const fargateTaskLogGroup = new cwl.LogGroup( this, `FargateBastionLogGroup`, { logGroupName: `${appEnv}-fargate-bastion-log-group`, retention: cwl.RetentionDays.ONE_DAY, removalPolicy: cdk.RemovalPolicy.DESTROY, } ); const repository = new ecr.Repository(this, `FargateBastionRepository`, { repositoryName: `${appEnv}/fargate-bastion`, removalPolicy: cdk.RemovalPolicy.DESTROY, autoDeleteImages: true, }); const imageDeploy = new DockerImageDeployment( this, `FargateBastionDockerImageDeployment`, { source: Source.directory(join(__dirname, "../docker")), destination: Destination.ecr(repository, { tag: "latest", }), } ); fargateTaskDef.node.addDependency(imageDeploy); // Can't set readonlyRootFilesystem to true because using ECS Exec fargateTaskDef.addContainer(`FargateBastionContainer`, { containerName: `${appEnv}-fargate-bastion-container`, image: ecs.ContainerImage.fromEcrRepository(repository), logging: ecs.LogDrivers.awsLogs({ streamPrefix: `${appEnv}-fargate-bastion-container`, logGroup: fargateTaskLogGroup, }), }); const fargateSecurityGroup = new ec2.SecurityGroup( this, `FargateBastionSecurityGroup`, { vpc, allowAllOutbound: true, securityGroupName: `${appEnv}-fargate-bastion-security-group`, description: "Security group of fargate bastion server", } ); const vpcEndPointSecurityGroup = ec2.SecurityGroup.fromSecurityGroupId( this, `VpcEndPointSecurityGroup`, props.vpc?.vpcEndPointSecurityGroup.securityGroupId! ); vpcEndPointSecurityGroup.addIngressRule( fargateSecurityGroup, ec2.Port.tcp(443) ); const vpcEndpointSubnets = vpc.selectSubnets({ subnets: props.vpc?.vpcEndPointSubnets.map((vpcEndPointSubnet) => { return ec2.Subnet.fromSubnetAttributes( this, `VpcEndPointSubnets${vpcEndPointSubnet.availavilityZone}`, { subnetId: vpcEndPointSubnet.subnetId, availabilityZone: vpcEndPointSubnet.availavilityZone, routeTableId: vpcEndPointSubnet.routeTableId, } ); }), }); // ECRからコンテナを取得するために必要なVPCエンドポイント vpc.addInterfaceEndpoint(`ECREndpoint`, { service: ec2.InterfaceVpcEndpointAwsService.ECR, subnets: vpcEndpointSubnets, securityGroups: [vpcEndPointSecurityGroup], privateDnsEnabled: true, }); vpc.addInterfaceEndpoint(`ECRDockerEndpoint`, { service: ec2.InterfaceVpcEndpointAwsService.ECR_DOCKER, subnets: vpcEndpointSubnets, securityGroups: [vpcEndPointSecurityGroup], privateDnsEnabled: true, }); vpc.addInterfaceEndpoint(`SSMEndpoint`, { service: ec2.InterfaceVpcEndpointAwsService.SSM, subnets: vpcEndpointSubnets, securityGroups: [vpcEndPointSecurityGroup], privateDnsEnabled: true, }); vpc.addInterfaceEndpoint(`SSMMessagesEndpoint`, { service: ec2.InterfaceVpcEndpointAwsService.SSM_MESSAGES, subnets: vpcEndpointSubnets, securityGroups: [vpcEndPointSecurityGroup], privateDnsEnabled: true, }); vpc.addGatewayEndpoint(`S3Endpoint`, { service: ec2.GatewayVpcEndpointAwsService.S3, subnets: [ { subnets: fargateSubnets, }, ], }); const fargateService = new ecs.FargateService( this, `FargateBastionService`, { cluster, serviceName: `${appEnv}-fargate-bastion-service`, vpcSubnets: { subnets: fargateSubnets, }, taskDefinition: fargateTaskDef, securityGroups: [fargateSecurityGroup], enableExecuteCommand: true, desiredCount: 1, } ); props.rdsSecurityGroups?.map((rdsSecurityGroup) => { const securityGroup = ec2.SecurityGroup.fromSecurityGroupId( this, `${rdsSecurityGroup.instanceName}SecurityGroup`, rdsSecurityGroup.securityGroupId ); securityGroup.addIngressRule( fargateSecurityGroup, ec2.Port.tcp(5432), `inbound from ${fargateService.serviceName}` ); }); } }
-
docker/Dockerfile
# 任意のコンテナを指定(この例ではubuntu 22.04) FROM public.ecr.aws/ubuntu/ubuntu:22.04 WORKDIR / ENTRYPOINT ["tail", "-F", "/dev/null"]
-
Makefile
.DEFAULT_GOAL := help APP_ENV:=dev AWS_REGION:=ap-northeast-1 MODE:=RO ifeq ($(APP_ENV), dev) AWS_PROFILE:=hoge_profile CLUSTER_NAME:=dev-fargate-bastion-cluster CONTAINER_NAME:=dev-fargate-bastion-container DB_NAME:=db_name DB_URL:=db_url.region.rds.amazonaws.com ifeq ($(MODE), RW) DB_URL:=rw_mode_db_url.region.rds.amazonaws.com endif DB_PORT:=5432 DB_USER:=db_user DB_PW=$(shell aws rds generate-db-auth-token --hostname "$(DB_URL)" --username $(DB_USER) --port $(DB_PORT) --profile $(AWS_PROFILE)) LOCAL_PORT:=55431 # get fargate task id .PHONY: get-task-id get-task-id: $(eval TASK_ID := $(shell aws ecs list-tasks \ --region ${AWS_REGION} \ --profile ${AWS_PROFILE} \ --cluster ${CLUSTER_NAME} |\ jq -r '.taskArns[]' | awk -F'/' '{print $$3}'\ )) @echo TASK_ID: ${TASK_ID} # get fargate runtime id .PHONY: get-runtime-id get-runtime-id: get-task-id $(eval RUNTIME_ID := $(shell aws ecs describe-tasks \ --region ${AWS_REGION} \ --profile ${AWS_PROFILE} \ --cluster ${CLUSTER_NAME} \ --task ${TASK_ID} |\ jq -r '.tasks[].containers[].runtimeId' \ )) @echo RUNTIME_ID: ${RUNTIME_ID} # connect to proxy server .PHONY: connect-proxy-server connect-proxy-server: get-runtime-id @aws ssm start-session --target ecs:${CLUSTER_NAME}_${TASK_ID}_${RUNTIME_ID} \ --document-name AWS-StartPortForwardingSessionToRemoteHost \ --parameters "{\"host\": [\"${DB_URL}\"], \"portNumber\":[\"${DB_PORT}\"], \"localPortNumber\":[\"${LOCAL_PORT}\"]}" \ --region ${AWS_REGION} \ --profile ${AWS_PROFILE} # connect to rds via proxy server .PHONY: connect-to-rds connect-to-rds: @PGPASSWORD="${DB_PW}" psql -h localhost -p ${LOCAL_PORT} -U ${DB_USER} -d ${DB_NAME}
実行コマンド
Session Managerのポートフォワード機能を用いてローカルからRDSに接続します。
(ターミナルのタブを2つ使用します)
-
踏み台サーバへの接続
make connect-proxy-server
-
RDSへの接続
make connect-to-rds
手順及びそこに該当するコード
lib/fargate-bastion-stack.ts
について、手順毎に該当するコードを示します。
全体的な手順
今回定義したStackでは、以下の処理が動くように実装を進めました。
(一部まとめたり、手順を入れ替えたりすることでもっと簡潔になるかもしれません。。)
- 既存VPCの参照
- 新たなECSクラスターの作成
- タスクロールの設定
- タスク実行ロールの設定
- 踏み台サーバ用のVPC サブネットの作成
- Fargateタスクの定義
- Fargateタスクのロググループ設定
- ECRリポジトリの作成、コンテナデプロイの設定
- Fargateタスクにコンテナを追加
- Fargate用のSecurityGroupを追加
- 既存エンドポイントのセキュリティグループ参照し・入力ルール追加
- 既存VPCサブネットを参照し、VPCエンドポイントを追加
- 新たなFargateサービスの作成
- 既存のRDSセキュリティグループとFargateサービスのSecurityGroupの関連付け
1. 既存VPCの参照
const vpc = ec2.Vpc.fromLookup(this, `Vpc`, {
vpcId: props.vpc?.id,
});
2. 新たなECSクラスターの作成
const cluster = new ecs.Cluster(this, `FargateBastionCluster`, {
vpc,
clusterName: `${appEnv}-fargate-bastion-cluster`,
});
3. タスクロールの設定
以下ドキュメントを参考にタスクロールの設定をしました。
ECS Execのログはタスクロールで行われるため、こちらにログの権限を付与しておきます。
const taskRole = new iam.Role(this, `FargateBastionTaskRole`, {
assumedBy: new iam.ServicePrincipal("ecs-tasks.amazonaws.com"),
roleName: `${appEnv}-fargate-bastion-task-role`,
});
taskRole.addToPolicy(
new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
actions: [
"ssmmessages:CreateControlChannel",
"ssmmessages:CreateDataChannel",
"ssmmessages:OpenControlChannel",
"ssmmessages:OpenDataChannel",
"logs:DescribeLogGroups",
"logs:CreateLogStream",
"logs:DescribeLogStreams",
"logs:PutLogEvents",
],
resources: ["*"],
})
);
4. タスク実行ロールの設定
コマンド上でFargateタスクが実行できるように権限を付与します。
const taskExecutionRole = new iam.Role(
this,
`FargateBastionTaskExecutionRole`,
{
assumedBy: new iam.ServicePrincipal("ecs-tasks.amazonaws.com"),
roleName: `${appEnv}-fargate-bastion-task-execution-role`,
managedPolicies: [
iam.ManagedPolicy.fromAwsManagedPolicyName(
"service-role/AmazonECSTaskExecutionRolePolicy"
),
],
}
);
taskExecutionRole.addToPolicy(
new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
actions: ["ecs:ExecutionCommand"],
resources: [cluster.clusterArn],
})
);
ちなみに、タスクロールとタスク実行ロールの違いは以下のイメージで考えれば良いみたいです。
以下記事より引用
タスク実行ロール
タスク実行時にアクセスしたいAWSリソースの権限を管理
タスクロール
タスク実行して起動したコンテナがアクセスしたいAWSリソースの権限を管理
5. 踏み台サーバ用のVPCサブネットの作成
// create Subnets for proxy-to-rds
const fargateSubnets: ec2.Subnet[] | undefined = props.fargateSubnets?.map(
(subnet) => {
return new ec2.Subnet(
this,
`FargateBastionSubnet${subnet.availabiltyZone}`,
{
availabilityZone: subnet.availabiltyZone,
vpcId: props.vpc?.id!,
cidrBlock: subnet.cidrBlock,
mapPublicIpOnLaunch: false,
}
);
}
);
6. Fargateタスクの定義・7. Fargateタスクのロググループ設定
const fargateTaskDef = new ecs.FargateTaskDefinition(
this,
`FargateBastionTaskDefinition`,
{
taskRole: taskRole,
executionRole: taskExecutionRole,
family: `${appEnv}-fargate-bastion-task-definition`,
cpu: 256,
memoryLimitMiB: 512,
runtimePlatform: {
operatingSystemFamily: ecs.OperatingSystemFamily.LINUX,
cpuArchitecture: ecs.CpuArchitecture.ARM64,
},
}
);
const fargateTaskLogGroup = new cwl.LogGroup(
this,
`FargateBastionLogGroup`,
{
logGroupName: `${appEnv}-fargate-bastion-log-group`,
retention: cwl.RetentionDays.ONE_DAY,
removalPolicy: cdk.RemovalPolicy.DESTROY,
}
);
8. ECRリポジトリの作成、コンテナデプロイの設定
- コンテナのデプロイには cdk-docker-image-deploymentを使用しました
- この機能は将来的にAWS CDKに追加される可能性があるそうです。https://github.com/aws/aws-cdk/issues/12597#issuecomment-1243879965
- 今回作成するFargateタスクはここで作成するコンテナを前提とするため、依存関係を追加しています
const repository = new ecr.Repository(this, `FargateBastionRepository`, {
repositoryName: `${appEnv}/fargate-bastion`,
removalPolicy: cdk.RemovalPolicy.DESTROY,
autoDeleteImages: true,
});
const imageDeploy = new DockerImageDeployment(
this,
`FargateBastionDockerImageDeployment`,
{
source: Source.directory(join(__dirname, "../docker")),
destination: Destination.ecr(repository, {
tag: "latest",
}),
}
);
fargateTaskDef.node.addDependency(imageDeploy);
9. Fargateタスクにコンテナを追加
コメント文にも記述していますが、ECS Execをするため、読み取り専用の設定はFalseとしています。
// Can't set readonlyRootFilesystem to true because using ECS Exec
fargateTaskDef.addContainer(`FargateBastionContainer`, {
containerName: `${appEnv}-fargate-bastion-container`,
image: ecs.ContainerImage.fromEcrRepository(repository),
logging: ecs.LogDrivers.awsLogs({
streamPrefix: `${appEnv}-fargate-bastion-container`,
logGroup: fargateTaskLogGroup,
}),
});
10. Fargate用のSecurityGroupを追加
const fargateSecurityGroup = new ec2.SecurityGroup(
this,
`FargateBastionSecurityGroup`,
{
vpc,
allowAllOutbound: true,
securityGroupName: `${appEnv}-fargate-bastion-security-group`,
description: "Security group of fargate bastion server",
}
);
11. VPCエンドポイントに関連づけるセキュリティグループの参照・入力ルール追加
VPCエンドポイントと関連付けを行うセキュリティグループを参照し、そのセキュリティグループにaddIngressRuleで踏み台サーバからの接続を許可
const vpcEndPointSecurityGroup = ec2.SecurityGroup.fromSecurityGroupId(
this,
`VpcEndPointSecurityGroup`,
props.vpc?.vpcEndPointSecurityGroup.securityGroupId!
);
vpcEndPointSecurityGroup.addIngressRule(
fargateSecurityGroup,
ec2.Port.tcp(443)
);
12. 既存VPCサブネットを参照し、VPCエンドポイントを追加
以下2つの目的を達成するためにVPCエンドポイントを追加
-
プライベートネットワークにてECR上のコンテナの取得
https://docs.aws.amazon.com/ja_jp/AmazonECR/latest/userguide/vpc-endpoints.html
com.amazonaws.*region*.ecr.dkr
com.amazonaws.*region*.ecr.api
- S3のゲートウェイエンドポイント
- ルートテーブルの指定が困難であること
- ゲートウェイエンドポイントを増やしても追加料金がないこと
プライベートネットワーク上でSession Managerを利用可能にする
https://dev.classmethod.jp/articles/privatesubnet_ecs/
- プライベートネットワーク上でSession Managerを利用可能にする(必要なVPCエンドポイントは以下)
ssm.*region*.amazonaws.com
ssmmessages.*region*.amazonaws.com
const vpcEndpointSubnets = vpc.selectSubnets({
subnets: props.vpc?.vpcEndPointSubnets.map((vpcEndPointSubnet) => {
return ec2.Subnet.fromSubnetAttributes(
this,
`VpcEndPointSubnets${vpcEndPointSubnet.availavilityZone}`,
{
subnetId: vpcEndPointSubnet.subnetId,
availabilityZone: vpcEndPointSubnet.availavilityZone,
routeTableId: vpcEndPointSubnet.routeTableId,
}
);
}),
});
// ECRからコンテナを取得するために必要なVPCエンドポイント
vpc.addInterfaceEndpoint(`ECREndpoint`, {
service: ec2.InterfaceVpcEndpointAwsService.ECR,
subnets: vpcEndpointSubnets,
securityGroups: [vpcEndPointSecurityGroup],
privateDnsEnabled: true,
});
vpc.addInterfaceEndpoint(`ECRDockerEndpoint`, {
service: ec2.InterfaceVpcEndpointAwsService.ECR_DOCKER,
subnets: vpcEndpointSubnets,
securityGroups: [vpcEndPointSecurityGroup],
privateDnsEnabled: true,
});
vpc.addInterfaceEndpoint(`SSMEndpoint`, {
service: ec2.InterfaceVpcEndpointAwsService.SSM,
subnets: vpcEndpointSubnets,
securityGroups: [vpcEndPointSecurityGroup],
privateDnsEnabled: true,
});
vpc.addInterfaceEndpoint(`SSMMessagesEndpoint`, {
service: ec2.InterfaceVpcEndpointAwsService.SSM_MESSAGES,
subnets: vpcEndpointSubnets,
securityGroups: [vpcEndPointSecurityGroup],
privateDnsEnabled: true,
});
vpc.addGatewayEndpoint(`S3Endpoint`, {
service: ec2.GatewayVpcEndpointAwsService.S3,
subnets: [
{
subnets: fargateSubnets,
},
],
});
13. 新たなFargateサービスの作成
Fargate上で動作するコンテナにSession Managerでアクセスしたいので、enableExecuteCommandをtrueにする。
const fargateService = new ecs.FargateService(
this,
`FargateBastionService`,
{
cluster,
serviceName: `${appEnv}-fargate-bastion-service`,
vpcSubnets: {
subnets: fargateSubnets,
},
taskDefinition: fargateTaskDef,
securityGroups: [fargateSecurityGroup],
enableExecuteCommand: true,
desiredCount: 1,
}
);
14. 既存のRDSセキュリティグループとFargateサービスのSecurityGroupの関連付け
踏み台サーバからRDSに接続する許可を追加
props.rdsSecurityGroups?.map((rdsSecurityGroup) => {
const securityGroup = ec2.SecurityGroup.fromSecurityGroupId(
this,
`${rdsSecurityGroup.instanceName}SecurityGroup`,
rdsSecurityGroup.securityGroupId
);
securityGroup.addIngressRule(
fargateSecurityGroup,
ec2.Port.tcp(5432),
`inbound from ${fargateService.serviceName}`
);
});
最後に
Session Managerのポートフォワード機能がFargateでも使用可能になったことで、比較的容易に踏み台サーバを構築できるようになりました。
踏み台サーバの構築により、セキュリティ的なリスクを低減しつつ、データベースに接続するためのセットアップ手順も簡単にでき、良いことづくめでした。
踏み台サーバ構築の際にこの記事が参考になれば幸いです。
参考文献
Discussion