🐧

Docker で OpenSSH を使う:Linux 使い(略)Advent Calendar 2024

2024/12/09に公開

はじめに

これは「Linux 使いになりたい人のための Advent Calendar 2024」の記事です。

筆者は、Web エンジニアを志望する人には、セルフホスト Git サービスを稼働させて利用することをオススメしています。もし Git を使ったことがないなら、Git を学ぶときに、セルフホスト Git サービスを稼働させることも視野に含めながら学習するのが効率的だと考えています。

Docker で OpenSSH を使う

前回はセルフホスト Git サービスを使うにあたり、OpenSSH を使うことが多いといった説明の流れから、Ubuntu / WSL Ubuntu で ssh コマンドを使えるようにする方法や、ssh コマンドを使うときによく使っているものとして tmuxbyobuautossh を紹介しました。

今回は、ssh コマンドの実行を試せる環境を Docker コンテナーを使って用意する方法について紹介します。Docker コンテナーを使うと、Docker ホストの環境とは隔離された環境でサーバーを稼働させたり、アプリケーション実行ができて便利です。

openssh-server の利用

ここでは、linuxserver/openssh-server のイメージを使って OpenSSH を使ってみます。最終的に用意するディレクトリは次のようになります。

openssh-server/
├── init/
│   └── compose.yaml
├── openssh-agent/
│   ├── .env
│   └── compose.yaml
├── sample/
│   ├── dot.ssh/
│   │   ├── id_ed25519
│   │   ├── id_ed25519_pass
│   │   ├── id_ed25519_pass.pub
│   │   └── id_ed25519.pub
│   └── ssh_host_keys/
│       ├── ssh_host_ecdsa_key
│       ├── ssh_host_ecdsa_key.pub
│       ├── ssh_host_ed25519_key
│       ├── ssh_host_ed25519_key.pub
│       ├── ssh_host_rsa_key
│       └── ssh_host_rsa_key.pub
├── README.md
├── .env
├── compose.yaml
└── sample.env

環境の用意

ファイルを用意する順番を説明します。必要なディレクトリーを用意します。

mkdir -p openssh-server/init
mkdir -p openssh-server/sample

初期化用 compose.yaml

必要なファイルを用意するために、次の openssh-server/init/compose.yaml ファイルを用意して、コンテナーを動かします。

name: openssh-server
services:
  openssh-server:
    image: linuxserver/openssh-server:latest
    container_name: openssh-server
    hostname: openssh-server
    restart: unless-stopped
    ports:
      - 127.0.0.1:2222:2222

必要なファイルの準備をする手順

docker compose コマンドで、初期化用コンテナーの起動をします。

docker compose -f init/compose.yaml up -d

最初に使用する鍵を作成。ここではパスフレーズなしの鍵を用意。パスフレーズの入力と確認時に何も入力せずに Enter キーを入力することでパスフレーズなしの鍵を作成できます。

docker compose -p openssh-server exec openssh-server \
    ssh-keygen -t ed25519 -f /config/.ssh/id_ed25519 -C "user001@openssh-server"

パスフレーズありの鍵も用意。パスフレーズは pass001 にしたとします。

docker compose -p openssh-server exec openssh-server \
    ssh-keygen -t ed25519 -f /config/.ssh/id_ed25519_pass -C "user001@openssh-server pass"

後でバインドマウント時に使えるように、作成した鍵ファイルを Docker ホストへコピーします。

docker compose -p openssh-server cp \
    openssh-server:/config/.ssh \
    ./sample/dot.ssh

コピーで入手したものに authorized_keys が含まれている場合、これは不要なので削除。残しておいても良いです。

rm ./sample/dot.ssh/authorized_keys

コンテナーを起動するたびに SSH サーバーホストのフィンガープリントが変更されると利用時に面倒なので、SSH サーバー用のホストキー関連ファイルも Docker ホストへコピーして、後でバインドマウントして使えるようにします。

docker compose -p openssh-server cp \
    openssh-server:/config/ssh_host_keys \
    ./sample/

SSH ログイン時の認証で使う公開鍵の情報は .env ファイルで指定します。次のように id_ed25519.pub の内容を .envPUBLIC_KEY= へ指定するものにします。

echo "PUBLIC_KEY=\"$(cat ./sample/dot.ssh/id_ed25519.pub)"\" > .env

これで作成される .env ファイルは次のようになります。コマンドでうまく作れなかった場合はテキストエディタで作成して下さい。

$ cat .env
PUBLIC_KEY="ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGiL8rnDO1D/SW5p3WnZ+0ctlek2PU1rUoJ9sGXMBF9j user001@openssh-server"

後で、ssh-agent を試すときはパスフレーズありの鍵を使うので、それに対応する openssh-agent/.env ファイルを用意します。

echo "PUBLIC_KEY=\"$(cat ./sample/dot.ssh/id_ed25519_pass.pub)"\" > .env

作成した openssh-agent/.env ファイルの確認をすると次のようになります。

$ cat openssh-agent/.env
PUBLIC_KEY="ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPu5/vlBHnJsuDr2zxDncVPGZ5jjbUUhKeyKOQhkFo0e user001@openssh-server pass"

動作確認

ファイルの準備ができたら、OpenSSH サーバーを Docker で動かして使ってみましょう。

動作確認用 compose.yaml

動作確認用に openssh-server/compose.yaml ファイルを作成します。一応、ローカルホストのポート番号 2222 で接続できるようにしていますが、Docker コンテナー内で動作確認を完結できるように、バインドマウントで秘密鍵を用意したり、user001 ユーザーを用意するための環境変数 USER_NAME を指定したりしています。作成した SSH サーバー用のホストキー関連ファイルもボリュームバインドしています。

name: openssh-server
services:
  openssh-server:
    image: linuxserver/openssh-server:latest
    container_name: openssh-server
    hostname: openssh-server
    restart: unless-stopped
    ports:
      - 127.0.0.1:2222:2222
    volumes:
      - type: bind
        source: ./sample/dot.ssh/id_ed25519
        target: /config/.ssh/id_ed25519
        read_only: true
      - type: bind
        source: ./sample/ssh_host_keys
        target: /config/ssh_host_keys
        read_only: true
    environment:
      PUID: 1000
      PGID: 1000
      PUBLIC_KEY: ${PUBLIC_KEY}
      SUDO_ACCESS: false
      PASSWORD_ACCESS: false
      USER_NAME: user001

動作確認の手順

用意した動作確認用の compose.yaml ファイルを使ってコンテナーを起動します。

docker compose -f compose.yaml up -d

docker compose exec コマンドでコンテナーへ user001 ユーザーとしてアタッチ。警告が表示されますが、無視して良いです。

docker compose -p openssh-server \
    exec -it -u user001 -w /config openssh-server \
    bash

コンテナーにアタッチできて、コンテナー内のプロンプトが表示されたら、コンテナー内で ssh サーバーへ接続します。

ssh -p 2222 localhost

実行結果は次のようになります。

user001@openssh-server:/config$ ssh -p 2222 localhost
The authenticity of host '[localhost]:2222 ([::1]:2222)' can't be established.
ED25519 key fingerprint is SHA256:eY6cxc6/SSkXBVA+ThyY7XoVGNkrWTAt2okgnSWhUaw.
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '[localhost]:2222' (ED25519) to the list of known hosts.
Welcome to OpenSSH Server

初めて接続する SSH サーバーについてはフィンガープリントの確認が必要です。yes を入力すると SSH ログインができます。

この確認を省略したい場合は ssh-keyscan コマンドの標準出力の結果を ~/.ssh/known_hosts へあらかじめ保存しておきます。具体的には次のコマンドを実行します。

ssh-keyscan -p 2222 openssh-server > ~/.ssh/known_hosts

このコマンドを実行したときの結果は、次のようになります。

openssh-client:~$ ssh-keyscan -p 2222 openssh-server > ~/.ssh/known_hosts
# openssh-server:2222 SSH-2.0-OpenSSH_9.7
# openssh-server:2222 SSH-2.0-OpenSSH_9.7
# openssh-server:2222 SSH-2.0-OpenSSH_9.7
# openssh-server:2222 SSH-2.0-OpenSSH_9.7
# openssh-server:2222 SSH-2.0-OpenSSH_9.7

なお、既存のファイルを上書きせずに、エントリーを追加する場合は > ~/.ssh/known_hosts>> ~/.ssh/known_hosts とします。

ssh-agent

ssh-agent を使うと、パスフレーズの入力を省略できたり、多段 SSH サーバー接続をするときに認証情報の自動転送を有効化することができたりします。とても便利なのでよく使っています。

ssh-agent を起動するには次のようにします。

eval $(ssh-agent)

使用する秘密鍵をエージェントへ登録するには ssh-add コマンドを使います。

ssh-add /config/.ssh/id_ed25519

エージェントに登録されている秘密鍵の一覧を表示するには ssh-add コマンドへ -L オプションを指定します。

ssh-add -L

これらの実行例は次のようになります。

openssh-server:~$ eval $(ssh-agent)
Agent pid 250
openssh-server:~$ ssh-add -L
The agent has no identities.
openssh-server:~$ ssh-add /config/.ssh/id_ed25519 
Identity added: /config/.ssh/id_ed25519 (user001@openssh-server)
openssh-server:~$ ssh-add -L
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGiL8rnDO1D/SW5p3WnZ+0ctlek2PU1rUoJ9sGXMBF9j user001@openssh-server

エージェント情報の転送をしながら SSH 接続するには -A オプションを指定します。

ssh -A -p 2222 localhost

ssh-agent 確認用 compose.yaml

ssh-agent 確認用に次の openssh-server/openssh-agent/compose.yaml ファイルを用意します。

name: openssh-agent
services:
  openssh-client:
    image: linuxserver/openssh-server:latest
    container_name: openssh-client
    hostname: openssh-client
    working_dir: /config
    volumes:
      - type: bind
        source: ../sample/dot.ssh/id_ed25519
        target: /config/.ssh/id_ed25519
        read_only: true
      - type: bind
        source: ../sample/dot.ssh/id_ed25519_pass
        target: /config/.ssh/id_ed25519_pass
        read_only: true
    environment:
      PUID: 1000
      PGID: 1000
      USER_NAME: user001

  openssh-server:
    image: linuxserver/openssh-server:latest
    container_name: openssh-server
    hostname: openssh-server
    restart: unless-stopped
    ports:
      - 127.0.0.1:2222:2222
    volumes:
      - type: bind
        source: ../sample/ssh_host_keys
        target: /config/ssh_host_keys
        read_only: true
    environment:
      PUID: 1000
      PGID: 1000
      PUBLIC_KEY: ${PUBLIC_KEY}
      SUDO_ACCESS: false
      PASSWORD_ACCESS: false
      USER_NAME: user001

  openssh-server2:
    image: linuxserver/openssh-server:latest
    container_name: openssh-server2
    hostname: openssh-server2
    restart: unless-stopped
    environment:
      PUID: 1000
      PGID: 1000
      PUBLIC_KEY: ${PUBLIC_KEY}
      SUDO_ACCESS: false
      PASSWORD_ACCESS: false
      USER_NAME: user001

OpenSSH クライアント用に openssh-client、OpenSSH サーバー用に openssh-server と openssh-server2 のコンテナーを動かせるようにしています。Docker を使うと、こうやって必要なクライアントマシン、複数のサーバーマシン相当のものを手軽に用意できて便利ですよね。

openssh-agent 起動

openssh-agent を起動します。これで OpenSSH クライアント用に openssh-client、OpenSSH サーバー用に openssh-server と openssh-server2 のコンテナーが起動します。

docker compose -f openssh-agent/compose.yaml up -d

ssh-agent 動作確認

openssh-client コンテナーへアタッチします。ここでは、アタッチしてから user001 へユーザーを切り替えます。

docker compose -p openssh-agent \
    exec -it -w /config openssh-client \
    bash

コンテナー内で su コマンドを使って user001 ユーザーへ切り替えます。この後、openssh-server と openssh-server2 へ SSH ログインするので、ssh-keyscan~/.ssh/known_hosts をあらかじめ用意しておきます。

root@openssh-client:/config# su - user001
openssh-client:~$ ls -al .ssh/
total 16
drwx------ 2 user001 users 4096 Dec  9 03:10 .
drwxr-xr-x 6 user001 users 4096 Dec  9 03:10 ..
-rw------- 1 user001 users    0 Dec  9 03:10 authorized_keys
-rw------- 1 user001 users  419 Dec  9 02:13 id_ed25519
-rw------- 1 user001 users  464 Dec  9 02:52 id_ed25519_pass
openssh-client:~$ ssh-keyscan -p 2222 openssh-server > ~/.ssh/known_hosts
# openssh-server:2222 SSH-2.0-OpenSSH_9.7
# openssh-server:2222 SSH-2.0-OpenSSH_9.7
# openssh-server:2222 SSH-2.0-OpenSSH_9.7
# openssh-server:2222 SSH-2.0-OpenSSH_9.7
# openssh-server:2222 SSH-2.0-OpenSSH_9.7
openssh-client:~$ ssh-keyscan -p 2222 openssh-server2 >> ~/.ssh/known_hosts
# openssh-server2:2222 SSH-2.0-OpenSSH_9.7
# openssh-server2:2222 SSH-2.0-OpenSSH_9.7
# openssh-server2:2222 SSH-2.0-OpenSSH_9.7
# openssh-server2:2222 SSH-2.0-OpenSSH_9.7
# openssh-server2:2222 SSH-2.0-OpenSSH_9.7

それから、openssh-server へ SSH ログイン後、続いて openssh-server2 へ SSH ログインします。

このとき、ssh-agent コマンドでエージェントを有効にして、ssh-add コマンドで秘密鍵のファイル ~/.ssh/id_ed25519_pass を追加します。このとき、 ~/.ssh/id_ed25519_pass 用のパスフレーズの入力が必要です。

openssh-client:~$ eval $(ssh-agent)
Agent pid 245
openssh-client:~$ ssh-add .ssh/id_ed25519_pass 
Enter passphrase for .ssh/id_ed25519_pass: 
Identity added: .ssh/id_ed25519_pass (user001@openssh-server pass)

それから ssh コマンドに -A オプションを指定して、エージェント転送を有効化しながら openssh-server へ SSH ログインします。ssh-agent の機能によりパスフレーズ入力が省略されます。

openssh-client:~$ ssh -A -p 2222 openssh-server
Welcome to OpenSSH Server

openssh-server へ SSH ログインができたら、引き続き、openssh-server2 への SSH ログインします。この時も、ssh-agent の機能が有効化され、転送されているため、パスフレーズの入力が省略できます。

openssh-server:~$ ssh -p 2222 openssh-server2
Welcome to OpenSSH Server
openssh-server2:~$ 

ssh-agent を利用すると、パスフレーズの入力が省略できて便利だということがわかったでしょうか。

おわりに

Docker を使うと OpenSSH の動作確認が簡単にできることを紹介しました。OpenSSH サーバーを用意したり、OpenSSH 関連で確認したいことがあると、こうやってコンテナーを使って動作確認しています。

特に、セルフホスト Git サービスで動かす CI/CD で OpenSSH を使ったデプロイを自動化するときなどに、重宝しています。最近だと、autossh がうまく動作しなかったときにも、Docker を使って確認して使えるようにしました。

いまやファイル転送も ftp ではなく sftp や scp を使うのが普通です。そういったものを使うためのコードを書くにあたり、わざわざサーバーを用意しなくても Docker コンテナーで済ませたいものですよね。

Discussion