いまっぽいEC2へのSSH接続方法

EC2 Instance ConnectとAWS Systems ManagerのSession Managerのトンネリングを使って、安全かつ簡単にEC2へのSSH接続をします。

概要
---
**EC2 Instance Connect**
任意のSSH公開鍵をアップロードして、対応する秘密鍵でログインできる便利な機能。
しかもアップロードした公開鍵は1分のみ有効で毎回登録が必要なため、鍵の流出なども気にしなくて良いです。
公開鍵の登録はIAMで制御可能なため、つまりSSHのアクセス制御をIAMで行えるということになります。

**AWS Systems ManagerのSession Managerのトンネリング**
SystemsManagerのSessionManagerではSSHのトンネリングをサポートしているので、この機能を使うことでサブネットやセキュリティグループの設定に関わらず
SessionManagerの管理対象となっているインスタンスへトンネルを通じダイレクトにSSHが可能となります。
<br><br>

まとめるとこんな感じ。

|サービス|いいところ|ちょっとなぁなところ|
|:--|:--|:--|
|EC2 Instance Connect|鍵管理がいらなくなる|接続先はフツーにパブリックサブネットでSSH要の穴あけが必要|
|Session Managerのトンネリング|接続先のインスタンスがパブリックサブネットだろうがプライベートサブネットだろうが直接続が可能|鍵管理は普通に必要|

という感じでそれぞれ帯に短し・・的な感じですが、2つのいいところを組み合わせると


**鍵管理がいらなくなる + パブリックサブネットだろうがプライベートサブネットだろうが直接続が可能**

となり、インフラ野郎の自分としてはかなり捗るかな、と。


<br><br>
**つまりこんな人におすすめ**

- もうssh鍵管理したくない
- パブリックサブネットのインスタンスのセキュリティグループの接続元IPの管理が辛い
- 踏み台インスタンスの管理をせずにプライベートサブネットのインスタンスにも直接SSHしたい
- ACLやセキュリティグループをはじめ、通信経路上のNW機器などにSSH用のポートを開けたくないけどSSHしたい
- SSHの接続権限管理もIAMでやりたい

※会社のクソみたいなプロキシとかも突破できちゃうかもしれませんが自己責任でお願いします


コマンド化してみて使った感じ
---
**接続したい対象インスタンスの番号を選択してEnterするのみ**

```
turbo5522mame-no-MacBook-Pro:~ turbo5522mame$ AWSSSH 
[0]Linux-com000 i-06f85999999999999 ap-northeast-1a
[1]Linux-com001 i-05406999999999999 ap-northeast-1a
[2]Linux-com002 i-07c56999999999999 ap-northeast-1a
[3]Linux-com003 i-01443999999999999 ap-northeast-1a
[4]Linux-com004 i-0840a999999999999 ap-northeast-1c
[5]Linux-com005 i-0c676999999999999 ap-northeast-1a
Please select ssh connection number(0-5): 0
----start ssh-keygen
----end   ssh-keygen
----start rsa_key_upload
{
    "RequestId": "0b1b2abb-fd51-476a-9580-7565dc39cbe4",
    "Success": true
}
----end   rsa_key_upload
----start ssh with ssm Linux-com000
Last login: Sat Feb  1 15:19:15 2020 from localhost

       __|  __|_  )
       _|  (     /   Amazon Linux 2 AMI
      ___|\___|___|

https://aws.amazon.com/amazon-linux-2/
15 package(s) needed for security, out of 38 available
Run "sudo yum update" to apply all updates.
[ec2-user@ip-172-xx-xx-xx ~]$
```

セットアップ手順
---
**以下4手順。時間にして10分くらい**

1. EC2 Instance Connect のセットアップ
1. Session Managerを通して SSH 接続を有効にする
1. .ssh/configの設定
1. .bashrcにfunction作成


1. EC2 Instance Connect のセットアップ
---
新し目のAmazonLinuxは設定済みのため、設定不要な人もいるかもです。
Amazon Linux 2 2.0.20190618 以降には、EC2 Instance Connect が事前設定されています、とのこと。

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

2. Session Managerを通して SSH 接続を有効にする
---
SystemsManagerを有効化したりなんだりと初めての場合は色々あるかもですがまあやるだけ。
この辺を参考に

https://docs.aws.amazon.com/ja_jp/systems-manager/latest/userguide/session-manager-getting-started-enable-ssh-connections.html


3. .ssh/configの設定
---
- Mac/Linuxの場合

```bash:~/.ssh/config
Host i-* mi-*
    ProxyCommand sh -c "aws ssm start-session --target %h --document-name AWS-StartSSHSession --parameters 'portNumber=%p'"
    User ec2-user
    IdentityFile ~/.ssh/AWSSSH_rsa_key
```

- windowsの場合

```bash:~/.ssh/config
Host i-* mi-*
  ProxyCommand /c/Program\ Files/Amazon/AWSCLI/bin/aws.exe ssm start-session --target %h --document-name AWS-StartSSHSession --parameters "portNumber=%p"
  User ec2-user
  IdentityFile ~/.ssh/AWSSSH_rsa_key
```

4. .bashrcにfunction作成
---
- .bashrcに関数を作成(コピペ)
- 保存後の再読み込み(`source .bashrc`)は忘れずに

```bash:.bashrc
function AWSSSH() {

    # ローカル変数定義
    declare -a instance_name
    declare -a instance_id
    os_user=ec2-user


    # ssmからAWSCLIで接続先候補のInstanceIdをとってくる
    ssm_instance_id_list=`aws ssm describe-instance-information \
        --query 'InstanceInformationList[].InstanceId' --output text`

    if [ -n "${ssm_instance_id_list}" ]
    then
        # ssmからとってきたInstancdIdが" "区切りなので検索用に","区切りにする
        filter_keys=`echo ${ssm_instance_id_list} | tr " " ","`
    else
        echo "No SSM enabled instances"
        return 1
    fi

    # 接続先候補のNameタグとAvailabilityZoneとInstanceIdをとってくる
    ssh_target_info=`aws ec2 describe-instances \
        --filters "Name=instance-id,Values=${filter_keys}" \
        --query 'Reservations[].Instances[].[Tags[?key=="Name"].Value[],InstanceId,Placement.AvailabilityZone]' \
        --output text \
        | awk '{if(NR%2==0){print $0}else{printf $1" "$2" "}}' | sort -k3`


    # メニュー表示
    i=0
    while read instance_id[$i] availability_zone[$i] instance_name[$i]
    do
        echo "["$i"]"${instance_name[$i]} ${instance_id[$i]} ${availability_zone[$i]}
        let i++
    done << END
    ${ssh_target_info}
END
    let i--
    # 接続先選択
    read -p "Please select ssh connection number(0-$i): "  ssh_target_no


    if [ -n "${ssh_target_no}" ]
    then
        # 選択された番号のインスタンスとかをセットする
        instance_name=${instance_name[$ssh_target_no]}
        instance_id=${instance_id[$ssh_target_no]}
        availability_zone=${availability_zone[$ssh_target_no]}
    fi

    if [ -n "${instance_id}" ] && [ -n "${availability_zone}" ]
    then
        # EC2 InstanceConnect用のssh接続用のカギ作成
        echo "----start ssh-keygen"
        mkdir -p ~/.ssh
        rm -f ~/.ssh/AWSSSH_rsa_key
        rm -f ~/.ssh/AWSSSH_rsa_key.pub
        ssh-keygen -t rsa -f ~/.ssh/AWSSSH_rsa_key -N "" > /dev/null 2>&1
        echo "----end   ssh-keygen"

        # EC2 InstanceConnect用のssh接続用のカギのUPLOAD
        echo "----start rsa_key_upload"
        aws ec2-instance-connect send-ssh-public-key \
                    --instance-id ${instance_id} \
                    --availability-zone ${availability_zone} \
                    --instance-os-user ${os_user} \
                    --ssh-public-key file://~/.ssh/AWSSSH_rsa_key.pub
        echo "----end   rsa_key_upload"


        # AWS SystemsManagerのセッションマネージャーを使ったSSH接続
        echo "----start ssh with ssm ${instance_name}"
        ssh ${instance_id}

    else
            echo "Usage: Please select ssh connection number(0-$i)"
    fi


}

```