プライベートサブネットにあるEC2にEC2 Instance Connectを利用して接続する環境をAWS CDKで構築してみた
こんにちは!製造ビジネステクノロジー部の小林です。
今回は、プライベートIPアドレスとEC2 Instance Connect(EIC)を使って、EC2インスタンスへ接続する環境をAWS CDKで実装してみました。
EC2 Instance Connectとは?
EC2 Instance Connectは、踏み台ホストやVPNなしで、インターネットからパブリック/プライベートIPアドレスを使用してEC2インスタンスにSSH、RDP接続することをサポートするサービスです。
EC2 Instance Connect使うと嬉しいこと
EC2 Instance Connectを使用することで、以下のようなメリットがあります。
- パブリックIPアドレスをEC2インスタンスに割り当てる必要がなくなり、インターネットからの直接アクセスを排除できる
- キーペアやインスタンスプロファイルの作成が不要
- EC2 Instance Connect エンドポイントの料金は無料
プライベートIPアドレスとEC2 Instance Connect Endpointを使用した、EC2への接続を行う
プライベートIPアドレスとEC2 Instance Connect Endpointを使用した、EC2への接続は以下のようになります。
やってみた
ではAWS CDKで以下のリソースを構築していきます。
- VPCを作成
- EC2インスタンス用セキュリティグループを作成
- EICエンドポイント用セキュリティグループを作成
- セキュリティグループの通信設定
- EICエンドポイントを作成
- EC2を作成
VPCの作成
まず、VPCを作成し、PRIVATE_ISOLATEDタイプのサブネットを定義します。
/**
* VPCの作成
*/
const vpc = new ec2.Vpc(this, 'Vpc', {
maxAzs: 2,
subnetConfiguration: [
{
cidrMask: 24,
name: 'PrivateIsolated',
subnetType: ec2.SubnetType.PRIVATE_ISOLATED,
},
],
});
セキュリティグループの作成
EC2インスタンスとEICエンドポイントそれぞれに専用のセキュリティグループを作成します。
EC2インスタンス用セキュリティグループを作成
/**
* EC2インスタンス用のセキュリティグループ
* EC2インスタンスへのSSH接続は、EC2 Instance Connect Endpoint エンドポイントのセキュリティグループからのみ許可
*/
const instanceSg = new ec2.SecurityGroup(this, 'Ec2InstanceSg', {
vpc,
allowAllOutbound: false, // アウトバウンドトラフィックを制限
});
EICエンドポイント用セキュリティグループを作成
/**
* EC2 Instance Connect Endpoint 用のセキュリティグループ
*/
const eicSg = new ec2.SecurityGroup(this, 'EicEndpointSg', {
vpc,
allowAllOutbound: false, // アウトバウンドトラフィックを制限
});
セキュリティグループの通信設定
EICエンドポイント用セキュリティグループからのSSH(ポート22)のみをEC2インスタンス用セキュリティグループに許可する設定を行います。これにより、EICエンドポイントを介した接続だけを許可します。
// EC2 Instance Connect Endpoint エンドポイント -> EC2インスタンスへのSSH接続を許可
eicSg.connections.allowTo(
instanceSg,
ec2.Port.tcp(22),
);
今回は、Connections という便利な機能を使ってシンプルに記述しています。詳細は以下をご覧ください。
EICエンドポイントを作成
VPC内にEC2 Instance Connect Endpointを作成します。subnetIdには、先ほど作成したサブネットのIDを指定します。
// EC2 Instance Connect Endpointの作成
const eicEndpoint = new ec2.CfnInstanceConnectEndpoint(this, 'EicEndpoint', {
subnetId: vpc.isolatedSubnets[0].subnetId,
securityGroupIds: [eicSg.securityGroupId],
});
EC2を作成
PRIVATE_ISOLATEDサブネット内に、Amazon Linux 2023のEC2インスタンスを作成します。
/**
* EC2インスタンスの作成
*/
const ec2Instance = new ec2.Instance(this, 'Ec2Instance', {
vpc,
vpcSubnets: {
subnetType: ec2.SubnetType.PRIVATE_ISOLATED,
},
instanceType: ec2.InstanceType.of(
ec2.InstanceClass.T3,
ec2.InstanceSize.MICRO
),
machineImage: new ec2.AmazonLinuxImage({
generation: ec2.AmazonLinuxGeneration.AMAZON_LINUX_2023,
}),
securityGroup: instanceSg,
disableApiTermination: false, // 検証用のためインスタンスの削除を許可
});
ここまでに説明したリソースをすべて含んだ、CDKの完全なソースコードは以下の通りです。
CDK全体のソース
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as ec2 from 'aws-cdk-lib/aws-ec2';
export class Ec2InstanceConnectStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
/**
* VPCの作成
*/
const vpc = new ec2.Vpc(this, 'Vpc', {
maxAzs: 2,
subnetConfiguration: [
{
cidrMask: 24,
name: 'PrivateIsolated',
subnetType: ec2.SubnetType.PRIVATE_ISOLATED,
},
],
});
/**
* EC2インスタンス用のセキュリティグループ
* EC2インスタンスへのSSH接続は、EC2 Instance Connect Endpoint エンドポイントのセキュリティグループからのみ許可
*/
const instanceSg = new ec2.SecurityGroup(this, 'Ec2InstanceSg', {
vpc,
allowAllOutbound: false, // アウトバウンドトラフィックを制限
});
/**
* EC2 Instance Connect Endpoint 用のセキュリティグループ
*/
const eicSg = new ec2.SecurityGroup(this, 'EicEndpointSg', {
vpc,
allowAllOutbound: false, // アウトバウンドトラフィックを制限
});
// EC2 Instance Connect Endpoint エンドポイント -> EC2インスタンスへのSSH接続を許可
eicSg.connections.allowTo(
instanceSg,
ec2.Port.tcp(22),
);
// EC2 Instance Connect Endpointの作成
const eicEndpoint = new ec2.CfnInstanceConnectEndpoint(this, 'EicEndpoint', {
subnetId: vpc.isolatedSubnets[0].subnetId,
securityGroupIds: [eicSg.securityGroupId],
});
/**
* EC2インスタンスの作成
*/
const ec2Instance = new ec2.Instance(this, 'Ec2Instance', {
vpc,
vpcSubnets: {
subnetType: ec2.SubnetType.PRIVATE_ISOLATED,
},
instanceType: ec2.InstanceType.of(
ec2.InstanceClass.T3,
ec2.InstanceSize.MICRO
),
machineImage: new ec2.AmazonLinuxImage({
generation: ec2.AmazonLinuxGeneration.AMAZON_LINUX_2023,
}),
securityGroup: instanceSg,
disableApiTermination: false, // 検証用のためインスタンスの削除を許可
});
}
}
EC2を作成
PRIVATE_ISOLATEDサブネット内に、Amazon Linux 2023のEC2インスタンスを作成します。EC2 Instance Connect経由のSSH接続を可能にするため、SSMと通信するためのIAMロールをインスタンスにアタッチします。
TypeScript
/**
- EC2インスタンスの作成
*/
const ec2Instance = new ec2.Instance(this, 'Ec2Instance', {
vpc,
vpcSubnets: {
subnetType: ec2.SubnetType.PRIVATE_ISOLATED,
},
instanceType: ec2.InstanceType.of(
ec2.InstanceClass.T3,
ec2.InstanceSize.MICRO
),
machineImage: new ec2.AmazonLinuxImage({
generation: ec2.AmazonLinuxGeneration.AMAZON_LINUX_2023,
}),
securityGroup: instanceSg,
disableApiTermination: false, // 検証用のためインスタンスの削除を許可
});
これで、EC2インスタンスが安全なプライベートネットワーク内に配置されました。
全体コード
ここまでに説明したリソースをすべて含んだ、CDKの完全なソースコードは以下の通りです。このコードを実行することで、今回の構成を一度にデプロイできます。
CDK全体のソース
TypeScript
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as ec2 from 'aws-cdk-lib/aws-ec2';
export class Ec2InstanceConnectStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
/**
* VPCの作成
*/
const vpc = new ec2.Vpc(this, 'Vpc', {
maxAzs: 2,
subnetConfiguration: [
{
cidrMask: 24,
name: 'PrivateIsolated',
subnetType: ec2.SubnetType.PRIVATE_ISOLATED,
},
],
});
/**
* EC2インスタンス用のセキュリティグループ
* EC2インスタンスへのSSH接続は、EC2 Instance Connect Endpoint エンドポイントのセキュリティグループからのみ許可
*/
const instanceSg = new ec2.SecurityGroup(this, 'Ec2InstanceSg', {
vpc,
allowAllOutbound: false, // アウトバウンドトラフィックを制限
});
/**
* EC2 Instance Connect Endpoint 用のセキュリティグループ
*/
const eicSg = new ec2.SecurityGroup(this, 'EicEndpointSg', {
vpc,
allowAllOutbound: false, // アウトバウンドトラフィックを制限
});
// EC2 Instance Connect Endpoint エンドポイント -> EC2インスタンスへのSSH接続を許可
eicSg.connections.allowTo(
instanceSg,
ec2.Port.tcp(22),
);
// EC2 Instance Connect Endpointの作成
const eicEndpoint = new ec2.CfnInstanceConnectEndpoint(this, 'EicEndpoint', {
subnetId: vpc.isolatedSubnets[0].subnetId,
securityGroupIds: [eicSg.securityGroupId],
});
/**
* EC2インスタンスの作成
*/
const ec2Instance = new ec2.Instance(this, 'Ec2Instance', {
vpc,
vpcSubnets: {
subnetType: ec2.SubnetType.PRIVATE_ISOLATED,
},
instanceType: ec2.InstanceType.of(
ec2.InstanceClass.T3,
ec2.InstanceSize.MICRO
),
machineImage: new ec2.AmazonLinuxImage({
generation: ec2.AmazonLinuxGeneration.AMAZON_LINUX_2023,
}),
securityGroup: instanceSg,
disableApiTermination: false, // 検証用のためインスタンスの削除を許可
});
}
}
以上でリソースの作成は完了です!ターミナルで cdk deploy コマンドを実行して、リソースをデプロイしましょう。
動作確認
それでは、AWSコンソールとAWS CLIを使って、実際に接続できるか確認してみましょう。
AWSコンソールでの接続
AWSマネージメントコンソールからEC2の画面に移動し、作成したインスタンスを選択します。
画面右上の「接続」を選択し、EC2 Instance Connectタブに移動します。「プライベート IP を使用して接続」にチェックが入っていることを確認し、「接続」ボタンを選択します。
これで、ブラウザベースのターミナルが開き、無事接続できることを確認できました!
AWS CLIでの接続
次に、AWS CLIを使って接続してみましょう。今回は aws ec2-instance-connect ssh コマンドを使用します。
# 環境変数の設定
export INSTANCE_ID="i-XXXXXXXXXXXXXXXX"
# プライベートIPで接続(最大20同時接続、最大1時間セッション)
aws ec2-instance-connect ssh \
--instance-id $INSTANCE_ID \
無事接続できましたね!
おわりに
今回は、EC2 Instance Connect Endpointを使って、EC2への接続環境をAWS CDKで構築する方法をご紹介しました。キーペアやEC2インスタンスプロファイルが不要で簡単にEC2に接続できる環境を構築できるのは魅力的だと思いました。
この記事が少しでも皆様の参考になれば幸いです。
参考資料