EC2 Instance Connect EndpointでPrivateなEC2の踏み台サーバーを作ってみた

概要

RDSを仕事で本格的に触る機会が増え、自宅でも検証など実施したくEC2で踏み台サーバー(いわゆるBastion)を作ってみたいと思いました。 検証する際はpublicなRDSではなくPrivateなRDSでいろいろ弄りたい。 (別にpublicなRDSならendpointさえ分かれば接続できる) ただできるだけお金はかけたくなく、PrivateなEC2、かつローカルのLinuxからアクセスして作業することはできないかと調べていたところ「EC2 Instance Connect Endpoint」というものがあることを知りました。 しかも料金は無料!

https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/connect-with-ec2-instance-connect-endpoint.html

こちらの記事を参考にterraformを利用して構築してみたいと思います。

https://blog.denet.co.jp/ec2-instance-connect-endpoint/

https://blog.denet.co.jp/aws-cli-ec2-instance-connect-endpoint/

この記事はそのメモやまとめです。 すでに設定済みの項目がいくつかある。 試行錯誤しながらやったので、手順として不要かもしれない。あくまでメモ用。

必要なリソースとterraformでも記載方法

必要なリソースは

  • ECIエンドポイント用SG
  • EC2用SG
  • EICエンドポイント(VPC endpoint)
  • EC2

で実現できるようです。

terraformについては

https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ec2_instance_connect_endpoint#availability_zone

https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group#security_groups

あたりを使えばできそうですね。 terraformに落とし込むに当たりこちらを参考にもしました。

https://dev.classmethod.jp/articles/ec2-instance-connect-endpoint-by-terraform/

terraformで実装

試行錯誤して最終的には以下のようになりました。

# EIC Endpoint SG
resource "aws_security_group" "bastion_eci" {
  vpc_id = aws_vpc.vpc.id
  name   = "bastion_for_eci_allow_ssh"
  egress {
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

# Bastion ec2 SG
resource "aws_security_group" "bastion_ec2" {
  vpc_id = aws_vpc.vpc.id
  name   = "bastion_for_ec2_allow_ssh_from_eci"
  ingress {
    from_port       = 22
    to_port         = 22
    protocol        = "tcp"
    security_groups = [aws_security_group.bastion_eci.id]
  }

  egress {
    from_port        = 0
    to_port          = 0
    protocol         = "-1"
    cidr_blocks      = ["0.0.0.0/0"]
    ipv6_cidr_blocks = ["::/0"]
  }
}

# EC2 Instance Connect Endpoint
resource "aws_ec2_instance_connect_endpoint" "for_bastion_eic" {
  subnet_id          = aws_subnet.subnet_public_AZa.id
  security_group_ids = [aws_security_group.bastion_eci.id]
  preserve_client_ip = true
}

# Amazon Linuxの最新AMIを取得
data "aws_ami" "amazon-linux-2" {
  most_recent = true #最新版を指定
  owners      = ["amazon"]
  filter {
    name   = "owner-alias"
    values = ["amazon"]
  }
  filter {
    name   = "name"
    values = ["amzn2-ami-hvm-*-x86_64-gp2"]
  }
}

# bastion ec2
resource "aws_instance" "bastion" {
  ami                    = data.aws_ami.amazon-linux-2.id # Amazon Linux 2
  instance_type          = "t3.nano"
  subnet_id              = aws_subnet.subnet_private_AZa.id # private subnet
  vpc_security_group_ids = [aws_security_group.bastion_ec2.id]
  tags = {
    Name = "bastion-ec2"
  }
  lifecycle {
    ignore_changes = [
      ami,
    ]
  }
}

appyします。 ここでつまずき。 terraformのaws providersが古く、aws_ec2_instance_connect_endpointを認識できなかった。 以下のように最新のproviderを指定して上げることで回避することができました。

  required_version = ">= 0.15"
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "5.19.0"
    }
  }
}

接続してみる(コンソール)

EC2 →instanceを選択して「接続」

繋がった!

(どうでもいいけどEC2ってでかでかと出てきていた記憶あるのですが今は鳩?になっているのですね)

接続してみる(ターミナル)

aws ec2-instance-connect ssh --instance-id i-xxxxxxx --connection-type eice --region ap-northeast-1

ここでつまずき2。

~ via 🐍 v3.10.8 
at 16:55:17 ⬢ [Docker] ❯ aws-vault exec xxxxx -- aws ec2-instance-connect ssh --instance-id i-xxxxxx

usage: aws [options] <command> <subcommand> [<subcommand> ...] [parameters]
To see help text, you can run:

  aws help
  aws <command> help
  aws <command> <subcommand> help

aws: error: argument operation: Invalid choice, valid choices are:

send-ssh-public-key                      | send-serial-console-ssh-public-key      
help                                    

コマンドが認識されていないので、多分aws cliが古いのではと予想。

~ via 🐍 v3.10.8 
at 16:57:31 ⬢ [Docker] ❯ aws --version
aws-cli/2.7.14 Python/3.9.11 Linux/5.10.16.3-microsoft-standard-WSL2 exe/x86_64.almalinux.8 prompt/off

~ via 🐍 v3.10.8 
at 17:02:01 ⬢ [Docker]

予想通り古かった。 最新に更新する。

https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/getting-started-install.html

curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
unzip awscliv2.zip
sudo ./aws/install --update

versionを確認してみる。

~ via 🐍 v3.10.8 
at 18:07:07 ⬢ [Docker] ❯ aws --version
aws-cli/2.13.22 Python/3.11.5 Linux/5.10.16.3-microsoft-standard-WSL2 exe/x86_64.almalinux.8 prompt/off

~ via 🐍 v3.10.8 
at 18:07:09 ⬢ [Docker]

新しくなっているので再度実行。

~ via 🐍 v3.10.8 
at 18:07:09 ⬢ [Docker] ❯ aws ec2-instance-connect ssh --instance-id i-xxxxxx --connection-type eice --region ap-northeast-1
Last login: Fri Sep 29 09:01:50 2023 from 88.15.62.130.shared.user.transix.jp
   ,     #_
   ~\_  ####_        Amazon Linux 2
  ~~  \_#####\
  ~~     \###|       AL2 End of Life is 2025-06-30.
  ~~       \#/ ___
   ~~       V~' '->
    ~~~         /    A newer version of Amazon Linux is available!
      ~~._.   _/
         _/ _/       Amazon Linux 2023, GA and supported until 2028-03-15.
       _/m/'           https://aws.amazon.com/linux/amazon-linux-2023/

[ec2-user@ip-10-0-10-62 ~]$ 

繋がった!

ハマったところとか

  • ツールなどのversion upしていなかったのでソレが原因で躓く場面がちょいちょいあった
  • terraformdやろうとするとそこについても調べなくてはならないので、時間がかかってします

次やりたいこと

  • privateなRDSを作成してみて今回の方法を利用して接続をしてみる