SSHでログイン出来るコンテナの作り方(Ubuntu18対応版)

開発環境の構築時にDockerを使う機会が度々有ります。通常コンテナへのログインは`docker exec -it`か`docker-compose exec`を使用することで実現出来ます。但し、VSCodeからリモート上のコンテナにログインしたい場合はこの方法が使えません。故に本記事ではSSHでリモートログイン可能なコンテナの作り方を取り上げたいと思います。

# 実はDockerのドキュメントにも解説が有る

[SSH デーモン用サービスの Docker 化](http://docs.docker.jp/engine/examples/running_ssh_service.html)に解説が記載されています。また[参考記事1](https://qiita.com/YumaInaura/items/1d5c18a9e55484ccad89)、[参考記事2](https://docs.docker.com/engine/examples/running_ssh_service/)にも同様の解説が有ります。しかし、これらの記事で解説されている方法でUbuntu18.04のコンテナを起動し、SSHでログインしようとしても`permission denied`とエラーが表示されログイン出来ません。本記事ではUbuntu18.04イメージをベースとしたSSHログイン可能[^pw]なコンテナの構築を目指します。

# 新旧UbuntuのDockerfile比較

まず先述の参考記事に記載されているDockerfileの中身です。

```dockerfile:Ubuntu16.04イメージをベースとしたオリジナルのDockerfile
FROM ubuntu:16.04

RUN apt-get update && apt-get install -y openssh-server
RUN mkdir /var/run/sshd
RUN echo 'root:THEPASSWORDYOUCREATED' | chpasswd
RUN sed -i 's/PermitRootLogin prohibit-password/PermitRootLogin yes/' /etc/ssh/sshd_config

# SSH login fix. Otherwise user is kicked off after login
RUN sed 's@session\s*required\s*pam_loginuid.so@session optional pam_loginuid.so@g' -i /etc/pam.d/sshd

# 以下の二行は動作に直接関係ない部分なので削除しても構わない
ENV NOTVISIBLE "in users profile"
RUN echo "export VISIBLE=now" >> /etc/profile

EXPOSE 22
CMD ["/usr/sbin/sshd", "-D"]
```

以下にUbuntu18.04をベースにしたDockerfileの記載内容を示します。Ubuntu18.04？から
`/etc/ssh/sshd_config`内の各々のオプションがデフォルトではコメントアウトされており、SSHの認証を有効化するためにはコメントアウトを解除する必要[^pubkey]が有ります。その操作を考慮した内容になっています。

```dockerfile:Ubuntu18.04イメージをベースとしたDockerfile
FROM ubuntu:18.04

RUN apt-get update && apt-get install -y openssh-server
RUN mkdir /var/run/sshd
RUN echo 'root:THEPASSWORDYOUCREATED' | chpasswd
RUN sed -i 's/#PermitRootLogin prohibit-password/PermitRootLogin yes/' /etc/ssh/sshd_config
RUN sed -i 's/#PasswordAuthentication/PasswordAuthentication/' /etc/ssh/sshd_config

# SSH login fix. Otherwise user is kicked off after login
RUN sed 's@session\s*required\s*pam_loginuid.so@session optional pam_loginuid.so@g' -i /etc/pam.d/sshd

EXPOSE 22
CMD ["/usr/sbin/sshd", "-D"]
```

# パスワードを外出しにする
このままイメージビルドを行えばSSHでログイン可能なコンテナが起動します。しかし、このままではSSHログイン時に入力するパスワードがDockerfile内に記載された状態になります。セキュリティー上好ましくないと言う理由でコンテナ起動後パスワードを変更するのは煩雑になる為、外出しにしてビルド時にオプションとして渡せる様に変更します。

```dockerfile:パスワードを外出しにしてオプションで渡せる様に変更したDockerfile
FROM ubuntu:18.04

ARG PASSWD

RUN apt-get update && apt-get install -y openssh-server
RUN mkdir /var/run/sshd
RUN echo 'root:'${PASSWD} | chpasswd
RUN sed -i 's/#PermitRootLogin prohibit-password/PermitRootLogin yes/' /etc/ssh/sshd_config
RUN sed -i 's/#PasswordAuthentication/PasswordAuthentication/' /etc/ssh/sshd_config

# SSH login fix. Otherwise user is kicked off after login
RUN sed 's@session\s*required\s*pam_loginuid.so@session optional pam_loginuid.so@g' -i /etc/pam.d/sshd

EXPOSE 22
CMD ["/usr/sbin/sshd", "-D"]
```

イメージビルド時に`docker image build --build-arg PASSWD=<PASSWORD> -t <IMAGE_NAME> .`とすれば、任意のパスワードを設定したコンテナイメージがビルド出来ます。但し、Docker Composeを利用するとより手間無くコンテナの起動が可能となります。

```yml:docker-compose.ymlの記載例
version: "2.4"
services:
    example_service:
        build:
            context: .
            dockerfile: Dockerfile
            args:
                - PASSWD=${PASSWD}
        ports:
            - '2222:22'
        restart: always

```

`docker-compose.yml`を格納したディレクトリ上で`docker-compose build --build-arg PASSWD=<PASSWORD>`を実行し、任意のパスワード(PASSWD=\<PASSWORD\>部分)を--build-argオプションとして渡します。ビルドが完了したら、`docker-compose up -d`コマンドを実行し、起動が完了したら`ssh -p 2222 root@localhost`(リモート上で起動した場合はリモートのIPアドレスを指定)コマンドを実行することで、コンテナにログイン出来ます。

# まとめ
Ubuntu18.04イメージをベースとしたSSHログイン可能なコンテナの構築を試みた時に`permission denied`エラーでログイン出来ないトラブルに阻まれてしまい、解決に時間を要しました。故に備忘録も兼ねて本記事をまとめました。

[^pw]: SSHの認証方法はパスワード認証とします。キーファイルを用いてのログインを実現することも可能ですが、手順が煩雑になります。

[^pubkey]: キーファイルでの認証を有効化する場合は`RUN sed -i 's/#PubkeyAuthentication yes/PubkeyAuthentication yes/' /etc/ssh/sshd_config`を追加します。