Docker で OpenSSH を使う:Linux 使い(略)Advent Calendar 2024
はじめに
これは「Linux 使いになりたい人のための Advent Calendar 2024」の記事です。
筆者は、Web エンジニアを志望する人には、セルフホスト Git サービスを稼働させて利用することをオススメしています。もし Git を使ったことがないなら、Git を学ぶときに、セルフホスト Git サービスを稼働させることも視野に含めながら学習するのが効率的だと考えています。
Docker で OpenSSH を使う
前回はセルフホスト Git サービスを使うにあたり、OpenSSH を使うことが多いといった説明の流れから、Ubuntu / WSL Ubuntu で ssh
コマンドを使えるようにする方法や、ssh
コマンドを使うときによく使っているものとして tmux
、byobu
、autossh
を紹介しました。
今回は、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
の内容を .env
の PUBLIC_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