🎃

EC2 Instance Connect Endpoint (EIC Endpoint)を使ってEC2に接続

2023/07/02に公開

https://www.youtube.com/watch?v=aTlrJk50NWA

今回は最近アップデートされた
EC2 Instance Connect Endpoint (EIC Endpoint)を使ったEC2インスタンスへの接続方法を説明します。

Connect to your instances without requiring a public IPv4 address using EC2 Instance Connect Endpoint

Introduction

本日のAgenda

今回のハンズオンを通して、以下のことを理解しましょう。

  1. EC2 Instance Connect Endpointの特徴・使い方
  2. 実際の構築の方法
  3. EICエンドポイントを利用した外部からEC2インスタンスへのSSH接続方法

はじめに

eic-work01
EC2インスタンスへの接続の安全性を担保したい。
そこで便利なのがAWS Systems ManagerのSession Managerですよね。
SessionManagerではSSHポートを空けず、インスタンスへ接続ができるので、セキュリティ面でも安全ですよね。

でも、SessionManagerを使っている時に、こんなことを思ったことはないでしょうか。

『今までSSHを使ってサーバーを運用してきたので、SCPやSFTPなどのコマンドが使えないのは不便』
『通常のSSH接続と挙動が違うので、少し使いづらい』
『より閉塞的な環境でSSMを使う場合、VPCエンドポイントを作成する必要があるのでコストは増加してしまう』

SystemsManagerの機能はもちろん、セッションマネージャー接続以外にも、様々な便利な機能があるが、
サーバーへの接続という観点だけでみると、従来のSSHやRDPと、ちょっと扱いづらい部分はあると思います。

eic-work02

そんなあなたにおすすめなのが、最近発表されたEC2 Instance Connect Endpointです。
これはセッションマネージャーの様な独自の設定を行わなくても、SSH接続を安全に行えるサービスです。
一番のポイントは、インターネット向けにSSHポートを開放しなくても良いという点です。

今回はこのEC2 Instance Connect Endpointについて、
実際のハンズオンを通して学習していきましょう。

EC2 Instance Connect Endpointとは?

eic-work03

EC2 Instance Connectの機能をより拡張したイメージです。
従来のEC2 Instance ConnectはIAMにてインスタンスのアクセスを制御し、
SSH公開鍵を必要な時だけインスタンスメタデータ上に登録・保管させることで、よりセキュアにSSH接続を外部から行うことができるという機能です。

常時EC2インスタンス上に公開鍵情報を保持するのではなく、外部から接続が必要時だけ
公開鍵を一時的に登録することで、必要な時にSSH接続が行えるというものです。

IAMでもアクセス制御を行えるので、これだけでもセキュリティが担保できそうですが、
いくつかのデメリットがあります。

  1. インスタンスにパブリックIPの保持が必要
  2. Instance Connectに対応しているOSが少ない
    • Instance Connnectに対応しているのはAmazon Linux2とUbuntuの特定バージョン以降だけ
    • Amazon Linux 2 2.0.20190618 or later
    • Ubuntu 20.04 or later
  3. 外部むけの通信経路が必要
    • PublicサブネットやVPCエンドポイントを使って、インターネットなどの外部への出口を作る必要がある

従来のEC2 Instance Connect Endpointとの違い

eic-work04

このデメリット補うようにあるのが、今回のトピックのEC2 Instance Connect Endpoint(EIC Endpoint)
EC2 Instance Connect Endpointと異なる点は以下のようなものがあります

  1. パブリックIPが必要ない
    • 外部への接続性を持たせなくても良い
    • 完全なプライベートな環境を構築することも可能
  2. 踏み台・Agentレスで使える
    • SSMでも踏み台を使わずに接続できるが、SSM Agentのインストールが必要
    • EIC EndpointはAgentのインストールなしで、すぐに利用できる
  3. 外部へのSSHポートの開放が不要
    • インターネット向けにSSHポートやRDPポートを開けなくても、インスタンスにSSH接続やRDP接続が行える

さらに、EIC Endpointはエンドポイント費用もかからないので、
VPCエンドポイントを使うよりもコストを削減できます。

How it works

eic-work05

ではどうやって使っていくのかについてですが、
公式ドキュメントにもある図を拝借して順に見ていきましょう。

まず、あるVPCに外部への接続性がないプライベートサブネットが2つと
その中にEC2インスタンスが起動しているとしましょう。

このプライベートな環境で外部からインスタンスにSSH接続をしたいとします。

まず、どちらかのプライベートサブネットにEICエンドポイントを作成して紐付けます。
EICエンドポイントは、1つのVPCにつき1つ作成が可能。

作成したEICエンドポイントはサブネット間で共有することができますので、
各サブネットのインスタンスは、EICエンドポイントを経由して通信を行えます。

Security groups for EC2 Instance Connect Endpoint

eic-work06

また、EICエンドポイントとインスタンス間は、
セキュリティグループを通して接続するように設定が必要です。

EICエンドポイント作成時にセキュリティグループを指定しない場合、
VPCに登録されているデフォルトのセキュリティグループが適用される。

デフォルトのSGはアウトバウンドルールが全許可されていたりと、セキュリティ的にあまりよろしくないので
特定の宛先のみを許可するように設定しましょう。

また、インスタンス側のセキュリティグループでは、EICエンドポイントからの通信を許可する
インバウンドルールを1つ以上設定する必要があります。

公式サイトで推奨されるインバウンドルールはいくつかあります。

  1. EC2 Instance Connect Endpointに設定したセキュリティグループからのトラフィック
  2. 特定クライアントIPからのトラフィック (Preserve Client IPが有効の場合)
  3. VPC CIDR (Preserve Client IPが無効の場合)

ここで注目するのは、preserveClientIpという箇所です。
これは、EICエンドポイントに、クライアントIPを使用するかどうかという設定です。
有効化すると、EICエンドポイントは、クライアントIPのアドレスを保持する様になるため、
インバウンドルールにて、特定のクライアントIPを許可する設定が可能になります。
ただし、このPreserve Client IPですが、この機能をサポートしているインスタンスタイプが
限られていますので、使用するインスタンスが対応しているかは公式ドキュメントで確認をしておきましょう。

詳細は、以下のページを参照。

Connect using EC2 Instance Connect Endpoint to an instance

ハンズオン概要

eic-work07

今回はAWS公式ドキュメントにも掲載されているサンプルをもとにハンズオンを行います。

まず、今回のハンズオンを行う上で、いくつか前提条件があります。

  1. 必要な権限を持つユーザーを事前に作成
    • 今回はAdministrator権限をもつユーザーで事前にやっていきます
  2. PCでAWS CLIコマンドを使えるようにしておく
    • 今回は自分のPCからAWSへCLIを実行するので、まだの方はインストール
    • また、各自のAWS環境にCLIコマンドが実行できるよう、CLIアクセスキーの発行・登録もしておきましょう
  3. AWS CLIバージョンは最新版にしておく
    • 今回利用するAWSコマンドが古いバージョンだと入っていない可能性がある
    • 現在のバージョンを確認して、古ければアップデートしておきましょう
  4. OpenSSHはインストール済みであること
    • SSH接続時に利用するのでインストールされていない場合はインストールしておきましょう。

今回のハンズオンの手順

  1. VPCネットワークの作成
    • VPC、プライベートサブネット、ルートテーブル
  2. セキュリティグループ作成
  3. キーペア、EC2インスタンス作成
  4. EICエンドポイント作成

なお、リソースは全てCloudShellにて、CLIコマンドを使って作成をしていきます。

前提条件

  • Administrator権限を持つユーザーを作成
  • AWS CLIをインストールしていること
  • AWS CLIバージョンは最新にしておく
    • 古いバージョンだと、今回使用するAWSコマンド(ec2-instance-connect)が使用できない可能性がある
    • 以下の公式ページを参考に、AWS CLIを最新バージョンにする
      AWS CLI の最新バージョンをインストールまたは更新します。
    • 但し、公式手順以外でCLIをインストールした場合は、そのインストール手法でアップデートも行う必要があります
      (macであればbrewでAWS CLIをインストールした場合、brew upgradeコマンドでアップデートをかけてください)

前準備

まずはエンドポイント作成に必要なリソースを作成していきます。

今回作成が必要なリソースは以下のものです。

  • EICエンドポイント用セキュリティグループ
  • EC2用セキュリティグループ
  • EC2インスタンス
    • t2.microのAmazon Linux 2023を使用
    • キーペアも合わせて作成

リソース作成

以下スクリプトを使って必要なリソースを作成してください。
シェルスクリプトとして実行するか、手動で上から順に実行をしていきましょう。

#!/bin/bash

# 変数の設定
VPC_CIDR_BLOCK="10.0.0.0/16"   # VPCのCIDRブロック
AVAILABILITY_ZONE="ap-northeast-1a"
VPC_NAME="eic-vpc"

SUBNET_CIDR_BLOCK="10.0.0.0/24" # サブネットのCIDRブロック
SUBNET_NAME="eic-subnet"

RTB_NAME="eic-rt"

EICE_SG_NAME="eic-sg"
EC2_SG_NAME="ec2-sg"

KEY_NAME="eic-key" # キーペア名
INSTANCE_TYPE="t2.micro"
INSTANCE_NAME="eic-ec2"

EICE_NAME="eic-edp"

# VPCの作成
vpc_id=$(aws ec2 create-vpc --cidr-block $VPC_CIDR_BLOCK --output text --query 'Vpc.VpcId')
aws ec2 create-tags --resources $vpc_id --tags Key=Name,Value=$VPC_NAME
echo "VPC created: $vpc_id"

# プライベートサブネットの作成
subnet_id=$(aws ec2 create-subnet --vpc-id $vpc_id --availability-zone $AVAILABILITY_ZONE --cidr-block $SUBNET_CIDR_BLOCK --output text --query 'Subnet.SubnetId')
aws ec2 create-tags --resources $subnet_id --tags Key=Name,Value=$SUBNET_NAME
echo "Private suvnet created: $subnet_id"

# ルートテーブルの作成
route_table_id=$(aws ec2 create-route-table --vpc-id $vpc_id --output text --query 'RouteTable.RouteTableId')
aws ec2 create-tags --resources $route_table_id --tags Key=Name,Value=$RTB_NAME
echo "Route table created: $route_table_id"

# ルートテーブルにプライベートサブネットを関連付ける
aws ec2 associate-route-table --subnet-id $subnet_id --route-table-id $route_table_id

# EIC用セキュリティグループ作成
eic_sg_id=$(aws ec2 create-security-group --description 'Security group for EC2 Instance Connect Endpoint' --group-name 'eic-sg' --vpc-id $vpc_id --output text --query 'GroupId')
aws ec2 create-tags --resources $eic_sg_id --tags Key=Name,Value=$EICE_SG_NAME
echo "EIC endpoint security group created: $eic_sg_id"

# EC2用セキュリティグループ作成
ec2_sg_id=$(aws ec2 create-security-group --description 'Security group for EC2 Instance' --group-name 'ec2-sg' --vpc-id $vpc_id --output text --query 'GroupId')
aws ec2 create-tags --resources $ec2_sg_id --tags Key=Name,Value=$EC2_SG_NAME
echo "EC2 instance security group created: $ec2_sg_id"

# セキュリティグループルール作成
## EIC用アウトバウンドルール
aws ec2 authorize-security-group-egress --group-id $eic_sg_id --protocol all --cidr $VPC_CIDR_BLOCK
## EC2用インバウンドルール
aws ec2 authorize-security-group-ingress --group-id $ec2_sg_id --protocol all --source-group $eic_sg_id

# EC2インスタンス作成
## キーペア作成
aws ec2 create-key-pair --key-name $KEY_NAME --query 'KeyMaterial' --output text > eic-key.pem

## AMI ID取得
ami_id=$(aws ssm get-parameters --names /aws/service/ami-amazon-linux-latest/al2023-ami-kernel-6.1-x86_64 --output text --query "Parameters[].Value")
echo "Amazon Linux 2023 Latest vertion AMI ID got: $ami_id"

## インスタンス作成
ec2_id=$(aws ec2 run-instances --image-id $ami_id --count 1 --instance-type $INSTANCE_TYPE --security-group-ids $ec2_sg_id --subnet-id $subnet_id --key-name $KEY_NAME --output text --query 'Instances[].InstanceId')
aws ec2 create-tags --resources $ec2_id --tags Key=Name,Value=$INSTANCE_NAME
echo "EC2 instance created: $ec2_id"

# EICエンドポイント作成
eice_id=$(aws ec2 create-instance-connect-endpoint --subnet-id $subnet_id --security-group-id $eic_sg_id --output text --query 'InstanceConnectEndpoint.InstanceConnectEndpointId')
aws ec2 create-tags --resources $eice_id --tags Key=Name,Value=$EICE_NAME
echo "EC2 instance connect endpoint created: $eice_id"

echo "finished"

EICエンドポイントまでのリソースを作成したら、SSH接続の準備が完了です。
また、キーペア作成後は秘密鍵ファイルが作成されるので、予めファイルをダウンロードしておきましょう。

テスト

これでEICエンドポイントを経由してEC2インスタンスに接続できるようになりましたので
試しにSSH接続をしてみましょう。

ここでは2種類のSSH接続方法を紹介

One-click command

1つはec2-instance-connect sshコマンド。
AWS CLIコマンドを使ったコマンドで、SSHキーを作成せずにSSH接続がおこなえる。
コマンドを使う際はPCに事前にOpenSSHクライアントをインストールしておく必要があります。

aws ec2-instance-connect ssh --instance-id ${インスタンスID}

また、AWS CLIコマンド実行のためにCLIアクセスキーを登録しておく必要があります
以下は実際の実行例である。

% aws ec2-instance-connect ssh --instance-id i-02d9943988cd5f664 --profile=eic-test
The authenticity of host '10.0.0.214 (<no hostip for proxy command>)' can't be established.
ED25519 key fingerprint is SHA256:iXwe9Hq9Sg+ppuG2Bcr20Ikt7tIJOMt+QB+xrtBJl+Y.
This key is not known by any other names
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '10.0.0.214' (ED25519) to the list of known hosts.
   ,     #_
   ~\_  ####_        Amazon Linux 2023
  ~~  \_#####\
  ~~     \###|
  ~~       \#/ ___   https://aws.amazon.com/linux/amazon-linux-2023
   ~~       V~' '->
    ~~~         /
      ~~._.   _/
         _/ _/
       _/m/'

Open-tunnel command

2つ目は従来のsshコマンドを使って、SSH接続を行う方法です。
ProxyCommandオプションを使って、EC2インスタンスへのプライベートトンネル(TCP Proxy)を確立することで
接続が可能になる。

予めAWS CLIプロファイルを環境変数で外出ししておく必要があります。

ssh ec2-user@[INSTANCE] -i ${秘密鍵ファイル} -o ProxyCommand='aws ec2-instance-connect open-tunnel --instance-id ${インスタンスID}'
% ssh ec2-user@i-02d9943988cd5f664 -i ./eic-key.pem -o ProxyCommand='aws ec2-instance-connect open-tunnel --instance-id i-02d9943988cd5f664'

The authenticity of host 'i-02d9943988cd5f664 (<no hostip for proxy command>)' can't be established.
ED25519 key fingerprint is SHA256:iXwe9Hq9Sg+ppuG2Bcr20Ikt7tIJOMt+QB+xrtBJl+Y.
This host key is known by the following other names/addresses:
    ~/.ssh/known_hosts:179: 10.0.0.214
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added 'i-02d9943988cd5f664' (ED25519) to the list of known hosts.
   ,     #_
   ~\_  ####_        Amazon Linux 2023
  ~~  \_#####\
  ~~     \###|
  ~~       \#/ ___   https://aws.amazon.com/linux/amazon-linux-2023
   ~~       V~' '->
    ~~~         /
      ~~._.   _/
         _/ _/
       _/m/'

また、scpコマンドなどの、sshを利用したコマンドも利用ができる。
SSMのSession Managerだと、少し工夫をしないとSSH接続などが行えないが、
EICエンドポイント経由であれば従来通りのSSH接続が行えるので便利。

# test.txtを転送
% ls
eic-key.pem	test.txt

# EC2インスタンスへSCP転送
% scp -i ./eic-key.pem -o ProxyCommand='aws ec2-instance-connect open-tunnel --instance-id i-02d9943988cd5f664' ./test.txt ec2-user@i-02d9943988cd5f664:/home/ec2-user/
test.txt                                                                       100%    0     0.0KB/s   00:00

# 転送確認
% ssh ec2-user@i-02d9943988cd5f664 -i ./eic-key.pem -o ProxyCommand='aws ec2-instance-connect open-tunnel --instance-id i-02d9943988cd5f664'
   ,     #_
   ~\_  ####_        Amazon Linux 2023
  ~~  \_#####\
  ~~     \###|
  ~~       \#/ ___   https://aws.amazon.com/linux/amazon-linux-2023
   ~~       V~' '->
    ~~~         /
      ~~._.   _/
         _/ _/
       _/m/'
Last login: Sun Jul  2 05:37:41 2023 from 10.0.0.252
[ec2-user@ip-10-0-0-214 ~]$ ll
total 0
-rw-r--r--. 1 ec2-user ec2-user 0 Jul  2 05:39 test.txt

後片付け

以下にリソース削除用のスクリプトも用意したので、
こちらを使って削除いただいてもOKです。

タイミングによってはセキュリティグループが依存関係エラーで
削除失敗することがありますが、その場合は再度スクリプトを実行してください。

#!/bin/bash

# 変数の設定
VPC_NAME="eic-vpc"
SUBNET_NAME="eic-subnet"
RTB_NAME="eic-rt"
EICE_SG_NAME="eic-sg"
EC2_SG_NAME="ec2-sg"
KEY_NAME="eic-key"
INSTANCE_NAME="eic-ec2"
EICE_NAME="eic-edp"

# リソースIDの取得
vpc_id=$(aws ec2 describe-vpcs --filters "Name=tag:Name,Values=$VPC_NAME" --query "Vpcs[*].VpcId" --output text)
subnet_id=$(aws ec2 describe-subnets --filters "Name=tag:Name,Values=$SUBNET_NAME" --query "Subnets[*].SubnetId" --output text)
route_table_id=$(aws ec2 describe-route-tables --filters "Name=tag:Name,Values=$RTB_NAME" --query "RouteTables[*].RouteTableId" --output text)
eic_sg_id=$(aws ec2 describe-security-groups --filters "Name=tag:Name,Values=$EICE_SG_NAME" --query "SecurityGroups[*].GroupId" --output text)
ec2_sg_id=$(aws ec2 describe-security-groups --filters "Name=tag:Name,Values=$EC2_SG_NAME" --query "SecurityGroups[*].GroupId" --output text)
ec2_id=$(aws ec2 describe-instances --filters "Name=tag:Name,Values=$INSTANCE_NAME" --query "Reservations[*].Instances[*].InstanceId" --output text)
eice_id=$(aws ec2 describe-instance-connect-endpoints --filters "Name=tag:Name,Values=$EICE_NAME" --query "InstanceConnectEndpoints[*].InstanceConnectEndpointId" --output text)



# EICエンドポイント削除
aws ec2 delete-instance-connect-endpoint --instance-connect-endpoint-id $eice_id
echo "EC2 instance connect endpoint deleted: $eice_id"

## インスタンス削除
aws ec2 terminate-instances --instance-ids $ec2_id
echo "EC2 instance deleted: $ec2_id"

## キーペア削除
aws ec2 delete-key-pair --key-name $KEY_NAME
echo "Key pair deleted: $KEY_NAME"

# セキュリティグループ削除
aws ec2 revoke-security-group-ingress --group-id $ec2_sg_id --protocol all --source-group $eic_sg_id
aws ec2 delete-security-group --group-id $eic_sg_id
aws ec2 delete-security-group --group-id $ec2_sg_id
echo "Security group deleted: $ec2_sg_id, $eic_sg_id"

# プライベートサブネットの削除
aws ec2 delete-subnet --subnet-id $subnet_id
echo "Subnet deleted: $subnet_id"

# ルートテーブルの削除
aws ec2 delete-route-table --route-table-id $route_table_id
echo "Route table deleted: $route_table_id"

# VPCの削除
aws ec2 delete-vpc --vpc-id $vpc_id
echo "VPC deleted: $vpc_id"

echo "finished"

Ending

ここまでご視聴いただき、ありがとうございました。
今回は、EICエンドポイントの概要とその使い方について解説いたしました。

少しでもEICエンドポイントについて理解していただけたら幸いです。

Session Manager接続とはまた違う、今までのSSH接続のような感覚で使える
サービスが出たのは、結構嬉しいアップデートですね。

今後もより、便利でセキュアなアクセス環境の整備が行えるように
是非利用してみてください。

それでは終了します。
またよろしければお会いしましょう。

参考リンク

公式ドキュメント
Connect to your instances without requiring a public IPv4 address using EC2 Instance Connect Endpoint

クラメソさん
AWS CLIの実装からEC2 Instance Connect Endpointを読み解いてみた

Discussion