簡単にFargateのコンテナにSSH接続する(本番でSSHできるようにするのは非推奨)

追記　ーーーーーーーーーーーーーーーーー
一般論として、そもそもコンテナにssh接続するのは邪道なので、基本的にやらない方がいいです。
下記リンク先を読んでいただければ納得できるかと思います。
https://jpetazzo.github.io/2014/06/23/docker-ssh-considered-evil/

https://devops.stackexchange.com/questions/447/why-it-is-recommended-to-run-only-one-process-in-a-container

とは言っても開発中にデバッグするためにどうしてもsshしたくなる時もあるかと思うので、そういう時は参考にしてください。
本番リリースの時にsshできる状態になっているのは良くないので、検証が終わったらsshは削除することをオススメします。
ーーーーーーーーーーーーーーーーーーーー


Fargateでコンテナ開発をしていると、問題の切り分けとかする時にコンテナにsshできたらいいなと思うこともあるかと思います。

[fargateのコンテナにsshするのにはいくつか条件があります。](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/instance-connect.html)
例えば、同一VPCからでないと、fargateのコンテナにはsshできません。

sshトンネルというやり方もあるようですが、それよりも簡単なやり方を発見したのでメモとして残します。

##やり方を3行で
sshのpublic keyをParameter Storeに登録
そのpublic keyをTask Definitionで読み込む設定をしてコンテナに渡す
シェルスクリプトを使ってコンテナ内でそのpublic keyを`~/.ssh/authorized_key`に登録して同一VPCにあるEC2からそのコンテナにssh

##実現方法の流れ

1. Dockerfileにopensshをインストールするコマンドを加える
2. Parameter Storeにpublic keyを登録
3. 環境変数として受け取ったpublic keyをauthorized_keyに登録するシェルスクリプトを用意してENTRYPOINTで実行
4. Task DefinitionにParameter Storeの値の読み込み設定
5. Taskを実行してTaskのIPアドレスにssh

ざっとこんな感じです。これで良さそうだなと思ったら読み進めていただければと。

###Dockerfileにopensshをインストールするコマンドを加える

まずはDockerfileにopenssh-serverをインストールするよう記載。


```Dockerfile
RUN apt-get update && apt-get install -y openssh-server && apt-get clean all
```

###Parameter Storeにpublic keyを登録

sshするなら必要になるのが秘密鍵と公開鍵。接続元のサーバで`ssh-keygen`を実行してください。

```bash
$ ssh-keygen -t rsa -b 4096
```

とりあえず今回は~/.sshにid_rsa.pubができるのでそれをコピーしてAWSのParameter Storeに登録します。

AWS Systems Manager > Parameter Store

Create Parameterを押下し、任意のキーと共に公開鍵をペーストして保存します。

ここの記事ではキーを`PUB_KEY`とします。

###環境変数として受け取ったpublic keyをauthorized_keyに登録するシェルスクリプトを用意
以下のようなシェルスクリプトを用意。接続元の公開鍵を認証サーバに登録して、sshdを動かしてssh接続を受け付ける準備をします。

```sh
#!/bin/sh

[ ! -d "$USER_SSH_KEYS_FOLDER" ] && mkdir -p $USER_SSH_KEYS_FOLDER

echo $PUB_KEY > ~/.ssh/authorized_keys

unset PUB_KEY

/usr/sbin/sshd -D
```

他にも動かすプロセスがあるはずなので、このスクリプトはsupervisorを使って実行します。

```:supervisord.conf
[program:sshd]
command=/usr/local/bin/docker-entrypoint.sh
```
Dockerfileに下記の内容を追記。

```Dockerfile:Dockerfile
RUN apt-get update \
    && apt-get install -y openssh-server \
    && mkdir -p /var/run/sshd

COPY docker-entrypoint.sh /usr/local/bin/
RUN chmod +x /usr/local/bin/docker-entrypoint.sh

CMD supervisord -c /etc/supervisord.conf

EXPOSE 80 22
```

###Task DefinitionにParameter Storeの値の読み込み設定
Terraformを使っているなら、`aws_ecs_task_definition`のcontainer_definitionsの中にParameter Storeに登録した値を参照する設定をします。

```
      "Secrets": [
        {
          "ValueFrom": "arn:aws:ssm:your-region:your-aws-id:parameter/PUB_KEY",
          "Name": "PUB_KEY"
        }
      ],
```

それに併せてIAMも。

```
 {
          "Effect": "Allow",
          "Action": [
              "ssm:GetParameters",
              "ssm:GetParameter"
          ],
          "Resource": "arn:aws:ssm:your-region:your-aws-id:parameter/PUB_KEY"
 }
```

これによりコンテナ内で環境変数として値をParameter Storeから渡すことが可能になりました。


### Taskを実行してTaskのIPアドレスにssh
これで準備は整いました。全ての処理がうまくいっていればコンテナにsshできるでしょう。

Clusters > Your cluster > Task

と進んで、Network欄のPrivate IPに、fargateと同一VPCにあり、かつセキュリティグループで許可されているインスタンスからsshしてみてください。

```
$ ssh root@private-ipaddress
```

###上手くいかない場合

もし、すぐに接続が切れてしまう場合は何らかの原因でTaskが終了してしまい、起動と停止を繰り返していることが考えられますのでStopped Taskを見てその原因を調査してみてください。

まずはローカルできちんとコンテナが動くか、healthcheckのパスにGETリクエストを送って200が返ってくるようになっているかあたりを見てみると良いかと思います。

そもそも接続できない場合は、セキュリティグループの見直しをしてみてください。sshの22番ポートを許可するのをお忘れなく。
冒頭でも言いましたが、同一VPCからでないとsshできません。

もし上手くいかない場合はコメントで教えていただければお答えします。

これでこの記事は以上です。

##参考
https://docs.aws.amazon.com/AmazonECS/latest/developerguide/instance-connect.html
https://medium.com/ci-t/9-steps-to-ssh-into-an-aws-fargate-managed-container-46c1d5f834e2