EC2 Instance Connect Endpointを経由してVSCodeのRemoteSSH機能からEC2へ接続してみた

2024.01.31

VSCodeのRemoteSSH機能からEC2へ接続するのにEC2 Instance Connect Endpointを使用してみたのでその時の設定をブログに残します。

やること

プライベートサブネットで起動したEC2へEC2 Instance Connect Endpoint経由でSSH接続ができる環境を作成します。
最後にVSCodeのRemoteSSHから接続する設定を行い接続の確認を行います。

AWSの構成は以下の通りです。

AWSリソースの作成

AWSリソースの作成は以下のCloudFormationテンプレートを使用しました。

AWSTemplateFormatVersion: "2010-09-09"

Description: EC2 Instance Connect Endpoint Test Stack

Metadata:
# ------------------------------------------------------------#
# Metadata
# ------------------------------------------------------------# 
  AWS::CloudFormation::Interface:
    ParameterGroups:
      - Label: 
          default: Parameters for VPC
        Parameters:
          - VPCCIDR
      - Label: 
          default: Parameters for Subnet
        Parameters:
          - PrivateSubnet01CIDR
      - Label: 
          default: Parameters for ec2
        Parameters:
          - EC2VolumeSize
          - EC2VolumeIOPS
          - EC2AMI
          - EC2InstanceType
          - EC2Keypair

Parameters:
# ------------------------------------------------------------#
# Parameters
# ------------------------------------------------------------# 
  VPCCIDR:
    Default: 172.30.0.0/16
    Type: String

  PrivateSubnet01CIDR:
    Default: 172.30.1.0/24
    Type: String

  EC2VolumeSize:
    Default: 32
    Type: Number

  EC2VolumeIOPS:
    Default: 3000
    Type: Number

  EC2AMI:
    Default: ami-05a03e6058638183d
    Type: AWS::EC2::Image::Id

  EC2InstanceType:
    Default: t3.micro
    Type: String

  EC2Keypair:
    Type: String

Resources:
# ------------------------------------------------------------#
# VPC
# ------------------------------------------------------------# 
  VPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: !Ref VPCCIDR
      EnableDnsSupport: true
      EnableDnsHostnames: true
      Tags: 
        - Key: Name
          Value: test-vpc

# ------------------------------------------------------------#
# Subnet
# ------------------------------------------------------------# 
  PrivateSubnet01:
    Type: AWS::EC2::Subnet
    Properties:
      AvailabilityZone: ap-northeast-1a
      CidrBlock: !Ref PrivateSubnet01CIDR
      MapPublicIpOnLaunch: true
      Tags: 
        - Key: Name
          Value: !Sub test-private-subnet-01
      VpcId: !Ref VPC

# ------------------------------------------------------------#
# Security Group
# ------------------------------------------------------------# 
  EicESG:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: for EC2 Instance Connect Endpoint
      GroupName: test-sg-eice
      Tags: 
        - Key: Name
          Value: test-sg-eice
      VpcId: !Ref VPC

  EC2SG:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: for ec2
      GroupName: test-sg-ec2
      SecurityGroupIngress:
        - FromPort: 22
          IpProtocol: tcp
          SourceSecurityGroupId: !Ref EicESG
          ToPort: 22
      Tags: 
        - Key: Name
          Value: test-sg-ec2
      VpcId: !Ref VPC

# ------------------------------------------------------------#
# EC2 Instance Connect Endpoint
# ------------------------------------------------------------# 
  EicE:
    Type: "AWS::EC2::InstanceConnectEndpoint"
    Properties: 
      SecurityGroupIds: 
        - !Ref EicESG
      SubnetId: !Ref PrivateSubnet01

# ------------------------------------------------------------#
# EC2
# ------------------------------------------------------------# 
  EC2:
    Type: AWS::EC2::Instance
    Properties:
      BlockDeviceMappings: 
        - DeviceName: /dev/xvda
          Ebs:
            DeleteOnTermination: true
            Encrypted: true
            Iops: !Ref EC2VolumeIOPS
            VolumeSize: !Ref EC2VolumeSize
            VolumeType: gp3
      DisableApiTermination: false
      ImageId: !Ref EC2AMI
      InstanceType: !Ref EC2InstanceType
      KeyName: !Ref EC2Keypair
      NetworkInterfaces: 
        - DeleteOnTermination: true
          DeviceIndex: 0
          GroupSet: 
            - !Ref EC2SG
          SubnetId: !Ref PrivateSubnet01
      Tags:
        - Key: Name
          Value: test-ec2-web

EC2のキーペアは作成済みのものを使用します。
EC2のAMIはAmazon Linux2023を使用しています。
EC2に設定するセキュリティグループではEC2 Instance Connect EndpointからのSSHアクセスを許可するようにインバウンドルールでSSHを許可するようにしています。
118行目から123行目の設定でEC2 Instance Connect Endpointを作成します。
AWS::EC2::InstanceConnectEndpoint

デプロイは以下のコマンドを実行します。

aws cloudformation create-stack --stack-name CloudFormationスタック名 --template-body file://CloudFormationテンプレートファイル名 --parameters ParameterKey=EC2Keypair,ParameterValue=キーペアの名前

デプロイが完了したら以下のコマンドでローカルから接続できるか確認します。

ssh -i キーペア名.pem ec2-user@EC2インスタンスID -o ProxyCommand='aws ec2-instance-connect open-tunnel --instance-id EC2インスタンスID'

ローカルから接続するためには以下のドキュメントに記載されているIAMポリシーが設定されたIAMユーザーからアクセスキーを発行して「aws configure」コマンドなどで設定を行ってください。
ユーザーに EC2 Instance Connect Endpoint を使用したインスタンスへの接続を許可

今回は検証用なので"Condition"は削除したポリシーにしました。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "EC2InstanceConnect",
            "Action": "ec2-instance-connect:OpenTunnel",
            "Effect": "Allow",
            "Resource": "*"
        },
        {
            "Sid": "SSHPublicKey",
            "Effect": "Allow",
            "Action": "ec2-instance-connect:SendSSHPublicKey",
            "Resource": "*"
        },
        {
            "Sid": "Describe",
            "Action": [
                "ec2:DescribeInstances",
                "ec2:DescribeInstanceConnectEndpoints"
            ],
            "Effect": "Allow",
            "Resource": "*"
        }
    ]
}

アクセスキーの設定は以下のドキュメントを参考に設定してください。
設定ファイルと認証情報ファイルの設定

接続が成功すると以下のようにAmazon Linux2023のシェルが確認できます。

   ,     #_
   ~\_  ####_        Amazon Linux 2023
  ~~  \_#####\
  ~~     \###|
  ~~       \#/ ___   https://aws.amazon.com/linux/amazon-linux-2023
   ~~       V~' '->
    ~~~         /
      ~~._.   _/
         _/ _/
       _/m/'
Last login: Wed Jan 31 13:48:46 2024 from 172.30.1.245
[ec2-user@ip-172-30-1-68 ~]$

VSCodeの設定

接続まで確認ができたら「~/.ssh/config」に以下の設定を行います。

Host test-ec2
    HostName EC2インスタンスID
    User ec2-user
    IdentityFile .\キーペア名.pem
    ProxyCommand aws ec2-instance-connect open-tunnel --instance-id EC2インスタンスID

VSCodeにRemoteSSHの拡張機能を追加します。
VSCodeの拡張機能でRemoteSSHを検索してインストールしてください。

インストールができたらVSCodeのリモートエクスプローラーからtest-ec2を選択して接続します。
  

接続に成功すると以下の画像のように「ec2-user」のディレクトリを表示することができます。

さいごに

以下のブログのようにSystems ManagerのSession ManagerでもVSCodeからRemoteSSHでEC2に接続することが可能です。
接続元の端末にSession Manager Pluginを導入している場合はこちらの方法での接続でもよいと思います。
AWS Systems Manager と VS Code Remote SSH を組み合わせて快適なリモート開発環境を作る方法