EC2インスタンスへ多段SSH接続

# やること
ローカルPCから、Publicサブネットに建てたEC2インスタンスを踏み台にして、Privateサブネット内のEC2インスタンスに直接アクセスする。

# EC2インスタンスセットアップ
## yamlファイル
以下yamlファイルをCloudFormationで作成する。

```
$ tree
.
├── ec2.yml
└── vpc.yml
```


```yaml:vpc.yml
AWSTemplateFormatVersion: 2010-09-09
Description: Lesson CloudFormation

Resources:
  VPC:
    # https://docs.aws.amazon.com/en_us/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-vpc.html
    Type: AWS::EC2::VPC
    Properties: 
      CidrBlock: 10.0.0.0/16
      Tags:
        - Key: Name
          Value: Lesson VPC
  
  PublicSubnet:
    # https://docs.aws.amazon.com/en_us/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-subnet.html
    Type: AWS::EC2::Subnet
    Properties:
      CidrBlock: 10.0.0.0/24
      # https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-ref.html
      VpcId: !Ref VPC
      # https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-select.html
      # https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-getavailabilityzones.html
      AvailabilityZone: !Select [ 0, !GetAZs ]
      Tags:
        - Key: Name
          Value: !Sub ${AWS::StackName}-public-subnet
  
  PrivateSubnet:
    Type: AWS::EC2::Subnet
    Properties:
      CidrBlock: 10.0.1.0/24
      VpcId: !Ref VPC
      AvailabilityZone: !Select [ 0, !GetAZs ]
      Tags:
        - Key: Name
          Value: !Sub ${AWS::StackName}-private-subnet
  
  InternetGateway:
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags:
        - Key: Name
          Value: !Ref AWS::StackName
  
  AttachGateway:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      InternetGatewayId: !Ref InternetGateway
      VpcId: !Ref VPC
  
  # 定義した時点で自分のVPC内へのルートがデフォルトで設定されている。そのためインターネットゲートウェイへのルートだけ追加すれば良い。
  PublicRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: !Sub ${AWS::StackName}-public-routetable
  
  PrivateRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: !Sub ${AWS::StackName}-private-routetable
  
  RouteToInternetGateway:
    Type: AWS::EC2::Route
    DependsOn: AttachGateway
    Properties:
      RouteTableId: !Ref PublicRouteTable
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref InternetGateway
  
  PublicSubnetRouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PublicSubnet
      RouteTableId: !Ref PublicRouteTable
  
  PrivateSubnetRouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PrivateSubnet
      RouteTableId: !Ref PrivateRouteTable

# https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/outputs-section-structure.html
Outputs:
  VpcId:
    Description: VPC ID
    Value: !Ref VPC
    Export:
      Name: VpcId
  
  PublicSubnet:
    Description: PublicSubnet
    Value: !Ref PublicSubnet
    Export:
      Name: PublicSubnet
  
  PrivateSubnet:
    Description: PrivateSubnet
    Value: !Ref PrivateSubnet
    Export:
      Name: PrivateSubnet
```

```yaml:ec2.yml
AWSTemplateFormatVersion: 2010-09-09
Description: Lesson CloudFormation

Parameters:
  EC2AMI:
    Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>
    Default: /aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2
  KeyName:
    # CloudFormation実行時の引数にKeyNameにsshキーを指定すると値が入る
    # https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/using-cfn-cli-creating-stack.html
    Type: AWS::EC2::KeyPair::KeyName
    Description: The EC2 Key Pair to allow SSH access to the instance

Resources:
  SecurityGroupForPublicServer:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: !Sub ${AWS::StackName}-SecurityGroupForPublicServer
      # https://qiita.com/tiibun/items/67aa74cdc17bc0b9812c
      VpcId: !ImportValue VpcId
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 22
          ToPort: 22
          CidrIp: 0.0.0.0/0
  PublicServer:
    Type: AWS::EC2::Instance
    Properties:
      ImageId: !Ref EC2AMI
      InstanceType: t2.micro
      # https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-network-iface-embedded.html#aws-properties-ec2-network-iface-embedded-deviceindex
      NetworkInterfaces:
        - SubnetId: !ImportValue PublicSubnet
          GroupSet: 
            - !Ref SecurityGroupForPublicServer
          AssociatePublicIpAddress: true
          DeviceIndex: 0
      Tags:
        - Key: Name
          Value: PublicServer
      KeyName: !Ref KeyName

  SecurityGroupForPrivateServer:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: !Sub ${AWS::StackName}-SecurityGroupForPrivateServer
      VpcId: !ImportValue VpcId
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 22
          ToPort: 22
          SourceSecurityGroupId: !Ref SecurityGroupForPublicServer
  PrivateServer:
    Type: AWS::EC2::Instance
    Properties:
      ImageId: !Ref EC2AMI
      InstanceType: t2.micro
      NetworkInterfaces:
        - SubnetId: !ImportValue PrivateSubnet
          GroupSet: 
            - !Ref SecurityGroupForPrivateServer
          DeviceIndex: 0
      Tags:
        - Key: Name
          Value: PrivateServer
      KeyName: !Ref KeyName
```

## stack作成（EC2インスタンス作成）
以下、vpc作成コマンド

```
$ aws cloudformation validate-template --template-body file://vpc.yml 
{
    "Parameters": [],
    "Description": "Lesson CloudFormation"
}

$ aws cloudformation create-stack --stack-name lesson-vpc --template-body file://vpc.yml 
```

以下、ec2インスタンス作成コマンド

```
$ aws cloudformation validate-template --template-body file://ec2.yml
{
    "Parameters": [
        {
            "ParameterKey": "KeyName",
            "NoEcho": false,
            "Description": "The EC2 Key Pair to allow SSH access to the instance"
        },
        {
            "ParameterKey": "EC2AMI",
            "DefaultValue": "/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2",
            "NoEcho": false
        }
    ],
    "Description": "Lesson CloudFormation"
}

$ aws cloudformation create-stack --stack-name lesson-ec2 --template-body file://ec2.yml --parameters ParameterKey=KeyName,ParameterValue=MyKeyPair
```

# 多段SSH接続
以下のコマンドでAWSのsshキーペアを作成する。

```
$ aws ec2 create-key-pair --key-name MyKeyPair --query 'KeyMaterial' --output text > MyKeyPair.pem

$ chmod 400 MyKeyPair.pem

$ ll ~/.ssh/aws/
total 8
-r--------  1 fukazawakeisuke  staff  1675  8 27 11:40 MyKeyPair.pem
```

以下のように多段接続用にssh/configファイルを編集する。
それぞれのHostNameの値はAWSのコンソール画面から確認した値を入力した。

```
$ cat ~/.ssh/config 

Host cfn-public-ec2
  HostName 13.231.152.186
  User ec2-user
  Port 22
  IdentityFile MyKeyPair.pem

Host cfn-private-ec2
  HostName 10.0.1.162
  User ec2-user
  Port 22
  IdentityFile MyKeyPair.pem
  ProxyCommand ssh -W %h:%p cfn-public-ec2
```

以下の環境で

```
$ tree
.
└── MyKeyPair.pem

0 directories, 1 file
```

以下のコマンドを使って多段SSH接続した。


```
$ ssh cfn-private-ec2

[ec2-user@ip-10-0-1-162 ~]$ 

[ec2-user@ip-10-0-1-162 ~]$ hostname
ip-10-0-1-162.ap-northeast-1.compute.internal

```


# Stackの削除（EC2削除）
以下のコマンドを実行すればok
※ec2のスタックから削除することに注意。

```
$ aws cloudformation delete-stack --stack-name lesson-ec2

$ aws cloudformation delete-stack --stack-name lesson-vpc
```