🐧

Linux 使いになりたい人向けの Intel N100 ミニ PC で構築する開発環境(9)- OpenSSH

2024/04/06に公開

はじめに

これは、Linux 使いになりたい人向けに Intel N100 ミニ PC を使って開発環境を構築する方法を解説する記事の第9弾です。第1弾はLinux 使いになりたい人向けの Intel N100 ミニ PC で構築する開発環境(1) - 構築する開発環境について にあり、そこから第2弾へと続いています。そちらからご覧ください。

ここで使用する Intel N100 ミニ PC の仕様は次のものを前提とします。

項目 内容
OS Windows 11 Pro
CPU Intel N100
メモリ 16GB
ストレージ SSD 512 GB
画面出力端子 HDMI×2
WiFi 5G/2.4G
イーサネット RJ45×1
Bluetoot BT4.2
USB USB3.0×2/USB2.0×2

このマシンで最終的に Windows と Ubuntu Desktop が使えるように環境構築することを目指します。zenn.dev を購読している人のレベルを考えると、画面キャプチャはそれほど必要がないと考えているため少なめです。また、説明についても明示しないとわかりにくいと思われるものに絞っているので少なめです。

なお、実際に作業するときは、ネットワーク回線については数GBのデータをダウンロードしても問題ないものを使うようにしてください。作業の中には、数GBのデータをダウンロードするものも含まれてますので、テザリング環境では使用可能なパケットを使い切ってしまうこともあります。

今回は OpenSSH について説明します。コマンド実行にあたっては Linux に慣れるということも考慮して、基本的に Git Bash や WSL を使うようにします。

OpenSSH は、リモートにあるコンピュータを利用する際に必須となるソフトウェアです。また、Git のリモートリポジトリを利用するときにもよく使われています。そのため、ソフトウェア開発者なら使えるようになっておきたいところです。

ここで、Windows 環境での OpenSSH については、Git Bash、PowerShell、WSL Ubuntu のそれぞれで違うものが使われるため、これらを使っていると混乱しやすくなります。自分がどれを使っているのか、きちんと把握できるように、環境についての理解が必要です。

これらを一緒に説明しているドキュメントは、あまりみかけません。これらのうち、どれかひとつが使えれば良いからです。しかし、これらのちょっとした違いについて知らないと、他の開発ツールと組み合わせて OpenSSH を使いたいときに、環境構築時に無駄な苦労をすることになります。

たとえば、WSL Ubuntu で動作させるツールと組み合わせるなら、WSL Ubuntu で使える OpenSSH の方が PowerShell のものよりも楽ですし、VS Code for Windows と組み合わせるなら PowerShell の OpenSSH の方が楽です。

本記事の説明を読んで、Git Bash、PowerShell、WSL Ubuntu、それぞれの OpenSSH について正しく使えるようになりましょう。

OpenSSH とは

OpenSSH は、SSH プロトコルを利用するためのオープンソースのソフトウェアです。 OpenBSDプロジェクトによって開発され、BSD ライセンスで公開されています。公式サイトは https://www.openssh.com/ にあります。

OpenSSH の主な機能

OpenSSH の主な機能は次のとおりです。

機能 説明
リモートログイン ネットワーク経由で別のコンピュータにログイン
ファイル転送 異なるコンピュータ間でファイルを安全に転送
ポートフォワーディング ローカルポートをリモートポートに転送
リモートコマンド実行 リモートコンピュータ上でコマンドを実行
SFTP セキュアなファイル転送プロトコルを提供

主にリモートからコンピュータを遠隔操作するときに使われますが、安全なファイル転送やポートフォワーディングでも使われます。ポートフォワーディングは、リーモトコンピュータで動作しているサーバー用ソフトウェアと通信するときに、暗号化された通信路を SSH で用意して利用するときに使うものです。

なお、OpenSSH は、バージョン管理システムの Git のリモートリポジトリ用の認証にもよく使われていて、Git を使うのなら使えるようになっておきたいソフトウェアのひとつとなっています。

ここで、OpenSSH はクライアント・サーバー方式で動作します。OpenSSH のサーバーが稼働するコンピュータへ OpenSSH のクライアントで接続して、リモートからコンピュータを操作することになります。このとき、OpenSSH が暗号化通信をするために、秘密鍵と公開鍵(鍵ペア)というものを使います。この鍵ペアは、サーバー側とクライアント側の両方に必要で、とても重要なものなので、覚えておきましょう。

OpenSSH を使用するメリット

OpenSSH を使用するメリットとしては次のことがあります。

  • コンピュータのリモート操作が可能
  • 高い安全性
  • 無料で利用可能

OpenSSH の機能については、主な機能で紹介したように、コンピュータのリモート操作が可能で、そのために役に立つ様々な機能が提供されています。リモートサーバーの管理をするにあたっては必須のソフトウェアといえるくらい普及しており、ネットワーク機器の設定変更、異なるコンピュータ間でのファイル転送、といった、様々な用途でも利用されています。

なお、OpenSSH を使った通信は安全です。これは、通信内容が暗号化されるため、情報が漏洩するリスクが低くなるからです。また、内容の改竄もしにくくなっています。

また、OpenSSH はオープンソースソフトウェアとして開発されていてソースコードが公開されているため、誰でもコードを自由に確認することができます。データの暗号化や改竄への対策をしているコードについて、開発者が抜け道の処理をいれていないことのチェックができます。多くの開発者がプロジェクトに参加してレビューがされているので、安心して使うことができます。

さらに、無償で利用することができ、主要な OS では標準で使えるようになっています。

ssh 関連のコマンド

OpenSSH で使うコマンドはいくつかあります。まずは次の3つについて覚えておくと良いでしょう。

コマンド 機能
ssh OpenSSH サーバーへのリモート接続
ssh-keygen 鍵ペアの生成
ssh-keyscan OpenSSH サーバーの公開鍵情報収集

ssh コマンドは、OpenSSH サーバーへリモート接続するためのコマンドです。このコマンドを安全に利用するためには、ssh-keygen コマンドと ssh-keyscan コマンドを使って環境を用意する必要があります。なお、OpenSSH サーバーへリモート接続してログインすることを SSH ログインといいます。

ssh-keyscan コマンドは、複数の OpenSSH サーバーの公開鍵情報を収集し、~/.ssh/known_hosts ファイルに保存するためのコマンドです。接続先の OpenSSH サーバーが安全なものか確認するときに使います。

ssh-keygen は OpenSSH で利用する公開鍵認証に必要な鍵ペア (秘密鍵と公開鍵) を生成するためのコマンドです。SSH ログインをするときの認証を安全なものにするためには、鍵ペアが必要なので、このコマンドを使って用意します。また、~/.ssh/known_hosts ファイルに保存されている OpenSSH サーバーの公開鍵情報を削除するときにも使います。OpenSSH サーバーの公開鍵が更新されたときに、古い OpenSSH サーバーの公開鍵があるとエラーになります。そのときにエラーをなくすために使います。

OpenSSH の環境を用意するときは、ssh-keyscanssh-keygen を使うので、これらについて先に説明します。それから ssh コマンドについて説明します。

ssh-keyscan コマンド

ssh-keyscan コマンドは、OpenSSH サーバーの公開鍵情報(ホストの公開鍵情報)を収集し、~/.ssh/known_hosts ファイルに保存するためのコマンドです。ssh コマンドを使う前に、あらかじめ、接続する OpenSSH サーバーの公開鍵情報について確認しておくのが安心です。

ssh-keyscan コマンドを利用することで、known_hosts ファイルにサーバーのホストの公開鍵情報を保存できるようになります。known_hosts ファイルに保存されているサーバーについては、ホストの公開鍵の確認が不要となるので、ログイン時間が短縮されます。また、known_hosts ファイルに保存されている公開鍵と接続先の公開鍵が一致しない場合は、エラーとなります。そのため、偽のホストに接続するリスクを減らすことができます。

このコマンドを使うには、ホスト名か IP アドレスをパラメータに指定します。

ssh-keyscan <ホスト名か IP アドレス>

コマンドの使用例をいくつか紹介します。

次のコマンドは、www.example.com の公開鍵情報を取得し、~/.ssh/known_hosts ファイルに保存できるフォーマットで出力します。

ssh-keyscan www.example.com

複数のホストについて、公開鍵情報を収集することもできます。次のコマンドは、www.example.com192.168.1.10 の公開鍵情報を収集し、~/.ssh/known_hosts ファイルに保存できるフォーマットで出力します。

ssh-keyscan www.example.com 192.168.1.10

次のコマンドは、www.example.com のポート 2222 の公開鍵情報を収集し、~/.ssh/known_hosts ファイルに保存できるフォーマットで出力します。

ssh-keyscan -p 2222 www.example.com

OpenSSH では鍵ペアを作成するときに鍵のタイプを指定できます。指定可能なタイプとしては ed25519 などがあり、OpenSSH サーバーの公開鍵情報はタイプごとに用意されます。

次のコマンドは、www.example.com の ed25519 用の公開鍵情報のみを収集し、~/.ssh/known_hosts ファイルに保存できるフォーマットで出力します。

ssh-keyscan -t ed25519 www.example.com

ssh-keyscan コマンドで出力される値について、正しいものかどうかは、OpenSSH サーバーの管理者から公開鍵やフィンガープリントの情報をもらって確認することになります。ここでは自分が OpenSSH サーバーの管理者となるので、これについても説明します。

OpenSSH サーバー用のホストの公開鍵は、OpenSSH サーバーが稼働するマシンに用意されます。使用する暗号化方式に応じて、次のようなファイルがあります。Ubuntu だと、これらは /etc/ssh/ のディレクトリーにあります。

  • ssh_host_ecdsa_key.pub
  • ssh_host_ed25519_key.pub
  • ssh_host_rsa_key.pub

内容は、次のように cat コマンドで確認できます。

$ cat /etc/ssh/ssh_host_ed25519_key.pub
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOApKbWtQcwR6(略)

フィンガープリントは、これらを使って作成されます。フィンガープリントを確認するには、ssh-keygen コマンドに -l オプションを指定します。このとき、使用する公開鍵ファイルのパスの指定も必要なので -f オプションも指定します。

ssh-keygen -lf <公開鍵ファイルのパス>

確認時には、自分が使う秘密鍵に応じて確認しておけば十分でしょう。たとえば、ed25519 のタイプを指定して生成した鍵ペアを使うなら、ssh_host_ed25519_key.pub を使うことになるので、それだけ確認します。

手元の環境での実行例は次のようになりました。

$ ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub
256 SHA256:WbHna1DLsODv5GOjrbFxjHPtBR/sNm0ulEQCiZsLx90 (略)

この例だと、ed25519 タイプを使う場合のフィンガープリントが SHA256:WbHna1DLsODv5GOjrbFxjHPtBR/sNm0ulEQCiZsLx90 だということがわかります。

フィンガープリントは SSH ログイン時に表示されるものなので、ここではメモしておけば良いです。

なお、あらかじめ公開鍵を ~/.ssh/known_hosts へ追加エントリーしておくと、SSH ログイン時にフィンガープリントの確認をしなくても済むようになります。ここでサーバーのファイルで確認した公開鍵の値が ssh-keyscan コマンドで出力した値と一致していることを確認したら、ファイルの末尾へテキストを追加するリダイレクト機能(>>)で ~/.ssh/known_hosts へ追加エントリーしておきます。

たとえば www.example.com の公開鍵を ~/.ssh/known_hosts へ追加エントリーするには、次のようにします。

ssh-keyscan www.example.com >> ~/.ssh/known_hosts

ssh-keygen コマンド

ssh-keygen は OpenSSH で利用する公開鍵認証に必要な鍵ペア (秘密鍵と公開鍵) を生成するためのコマンドです。ed25519、ecdsa などのタイプを指定して鍵ペアを作成することができます。鍵ペアを使った認証はパスワード認証よりも安全です。ただし、秘密鍵を厳重に管理する必要があります。

秘密鍵については、パスフレーズを登録しておくことで、これを使うときに秘密鍵の所有者が使おうとしているかチェックすることができます。ここで、スクリプトなどでリモート接続を自動化したいときには、秘密鍵だけで自動接続できるように、パスフレーズを空文字にした自動接続専用の秘密鍵を用意することもあります。

ssh-keygen コマンドの使い方は次のようになります。

ssh-keygen <オプション>

オプションには、次のものがあります。

  • -t: 鍵のタイプを指定します。デフォルトは RSA です。
  • -b: 鍵の長さを指定します。デフォルトは 2048 ビットです。
  • -f: 鍵ファイルの名前を指定します。デフォルトは ~/.ssh/id_rsa です。
  • -C: 鍵ファイルにコメントを追加します。

たとえば、タイプ ed25519 を指定して、~/.ssh/id_ed25519_openssh_evalという名前の鍵ファイルに保存し、鍵ファイルに "OpenSSH Eval"というコメントを追加するには、次のコマンドを実行します。

ssh-keygen -t ed25519 \
  -f ~/.ssh/id_ed25519_openssh_eval \
  -C "OpenSSH Eval"

なお、コマンド実行時には、対話的に処理が進みます。ファイルの指定など、変更が必要ないものについては、そのまま「Enterキー」を入力します。パスフレーズなど、入力が必要なプロンプトが表示されたら、値を入力してから「Enterキー」で確定します。パスフレーズは空文字の指定をすることもできます。

コマンドの実行が完了すると、秘密鍵ファイル ~/.ssh/id_ed25519_openssh_eval と、公開鍵ファイル ~/.ssh/id_ed25519_openssh_eval.pub の作成ができます。公開鍵ファイルの名前は秘密鍵ファイルに .pub がついたものとなります。

作成された公開鍵の内容を確認するには cat コマンドを使います。

cat ~/.ssh/id_ed25519_openssh_eval.pub

なお、鍵ファイルを削除する場合は、該当するファイルを削除します。たとえば、ここで例として作成した id_ed25519_openssh_eval の鍵ペアを削除する場合は、 rm コマンドで次のように削除します。

rm ~/.ssh/id_ed25519_openssh_eval
rm ~/.ssh/id_ed25519_openssh_eval.pub

ssh-keygen コマンドと known_hosts ファイル

ssh-keygen コマンドは、~/.ssh/known_hosts ファイルに登録されているエントリーを削除するのにも使います。

~/.ssh/known_hosts から、特定のホスト名のエントリーを削除するには、-R オプションを次のように使います。

ssh-keygen -R <ホスト名か IP アドレス>

SSH ログインをする対象について、OpenSSH のポート番号が 22 以外の場合は、[<ホスト名か IP アドレス>]:<ポート番号> のフォーマットでパラメータを指定します。

ssh-keygen -R [<ホスト名か IP アドレス>]:<ポート番号>

ちなみに、known_hosts ファイルへのエントリーがこのフォーマットになっていることは、ssh-keyscan コマンドや ssh コマンドを使うときに出力されるメッセージからわかります。[] の位置がわからなくなったときは、このコマンドで確認できることを覚えておくと困りません。

$ ssh-keyscan -p 2222 www.example.com
# www.example.com:2222 SSH-2.0-OpenSSH_8.2p1 Ubuntu-4ubuntu0.11
# www.example.com:2222 SSH-2.0-OpenSSH_8.2p1 Ubuntu-4ubuntu0.11
[www.example.com]:2222 ssh-rsa AAAA(略)
(略)
$ ssh -p 2222 www.example.com
The authenticity of host '[www.example.com]:2222 ([192.168.1.10]:2222)' can't be established.
(略)

コマンドの実行例をいくつか示します。

known_hosts ファイルから www.example.com というホスト名のエントリーを削除するには、次のコマンドを実行します。

ssh-keygen -R www.example.com

known_hosts ファイルから 192.168.1.10 というホスト名のエントリーを削除するには、次のコマンドを実行します。

ssh-keygen -R 192.168.1.10

known_hosts ファイルから [www.example.com]:2222 というエントリーを削除するには、次のコマンドを実行します。

ssh-keygen -R [www.example.com]:2222

ssh コマンド

ssh コマンドは、Secure Shell (SSH) プロトコルを利用して、リモートコンピュータにログインしたり、コマンドを実行したりするためのコマンドです。

ssh コマンドの基本的な使い方は以下のとおりです。

ssh [オプション] ユーザ名@ホスト名

オプションには、接続方法やポート番号などを指定することができます。ユーザ名には、リモートコンピュータのログインユーザ名を指定します。ホスト名には、リモートコンピュータのホスト名または IP アドレスを指定します。

コマンドの実行例をいくつか示します。

www.example.com へローカルマシンへログインしているユーザーと同じユーザー名で SSH ログインします。

ssh www.example.com

www.example.comuser001 ユーザとして SSH ログインします。

ssh user001@www.example.com

ちなみに、@ を使わずに -l オプションでユーザーを指定することもできます。

ssh -l user001 www.example.com

www.example.com へポート 2222 で user001 ユーザーとして SSH ログインします。

ssh -p 2222 user001@example.com

秘密鍵のファイルを指定する場合は、-i オプションを使います。現在のユーザーと同じユーザー名で www.example.com~/.ssh/id_ed25519 の秘密鍵でログインします。

ssh -i ~/.ssh/id_ed25519 user@example.com

OpenSSH 関連のファイル

OpenSSH 関連のファイルとしては、次のものが重要なので覚えておきましょう。

ファイルのパス 説明
~/.ssh/known_hosts 接続先ホストの公開鍵管理用
~/.ssh/config クライアントの設定
~/.ssh/authorized_keys 接続ユーザーの公開鍵管理用

~/.ssh/known_hosts については、ここまでの説明に出てきているので説明は簡単に済ませます。接続エラー時に、このファイルが関係することがあります。すでに説明したように、接続先ホストの公開鍵管理用のもので、ssh-keyscan コマンドを使ってエントリーの登録ができますし、ssh-keygen -R コマンドを使ってエントリーの削除ができます。

残りの2つについては、少し詳しく説明します。

config ファイル

~/.ssh/config ファイルを使うと、OpenSSH クライアントを実行する時に指定するオプションをコマンドラインで省略できるようになります。詳細を説明すると長くなるので、例を使って簡単に説明します。

たとえば、www.example.com へポート 2222 でユーザー user001 として SSH 接続したいとします。また、このとき、秘密鍵には ~/.ssh/id_ed25519_openssh_eval を使うとします。そういった時は ~/.ssh/config へ下記の内容を指定します。

Host gitea-user001-www.example.com
  HostName www.example.com
  User user001
  Port 2222
  IdentityFile ~/.ssh/id_ed25519_openssh_eval

この指定があると、ssh コマンドで Host に指定した値 gitea-user001-www.example.com をホスト名として指定することができるようになります。つまり、次のようにコマンド実行ができます。

ssh gitea-user001-www.example.com

このとき、Host 以外のオプションについても適用されるので、これは、次のコマンドを実行しているのと同じことになります。

ssh -p 2222 -i ~/.ssh/id_ed25519_openssh_eval user001@www.example.com

この ~/.ssh/config 設定ファイルは「VS Code の Remote SSH による SSH ログイン」や「Git で SSH クローン」をするときによく使うので、覚えておきましょう。

authorized_keys ファイル

~/.ssh/authorized_keys ファイルは、OpenSSH サーバーがクライアントからの接続を許可するときに使われる接続ユーザーの公開鍵管理用ファイルです。このファイルへ接続時に使用する秘密鍵に対応する公開鍵の内容を登録して、公開鍵認証時に使います。

内容はテキストファイルで、単純に1行に1つの公開鍵のフォーマットになっています。一般的には、サーバー管理者が SSH ログインを許可するユーザーから公開鍵のテキストを受け取って、このファイルを用意することで、SSH ログインができるようになります。

このファイルのモードは、ls -l コマンドで表示したときに -rw------- となっている必要があります。chmod コマンドで 600 を指定すると、その後に指定したファイルをこのモードにすることができます。

chmod 600 ~/.ssh/authorized_keys

実際にモードを指定して確認すると、次のようになります。

user001@Win11Eval:~$ chmod 600 ~/.ssh/authorized_keys
user001@Win11Eval:~$ ls -l .ssh/authorized_keys
-rw------- 1 user001 user001 94 Apr  4 21:56 .ssh/authorized_keys

このファイルを手作業で作成するには少し手間がかかりますが、ssh-copy-id コマンドを使うと、クライアントにある鍵ペアから SSH ログインで使うユーザーのサーバー側にある ~/.ssh/authorized_keys ファイルを適切なモードで作成することができます。すでに ~/.ssh/authorized_keys ファイルがあって、そこに登録されていない鍵ペアを指定した場合は、このファイルへ新しく使う鍵ペアの公開鍵を追加することができます。

このコマンドで、ファイル編集をしなくてもユーザーがコマンドで簡単に公開鍵認証のための設定ができるので、次のような OpenSSH サーバーの運用がしやすくなります。

OpenSSH サーバーを用意したホストマシンについて、初期設定時はパスワード認証を有効化しておき、利用する予定のユーザーが自分で ~/.ssh/authorized_keys ファイルを用意できるようにします。各ユーザーの ~/.ssh/authorized_keys ファイルの用意が済んだら、OpenSSH サーバーのパスワード認証を無効化して、安全な OpenSSH サーバーとして運用ができるようになります。

ということで、ssh-copy-id コマンドは覚えておくと便利です。接続先とユーザー名については ssh コマンドと同じです。-i オプションで使用する秘密鍵を指定することもできます。

ssh-copy-id -i <秘密鍵のパス> <ユーザー名>@<ホスト名または IP アドレス>

パスワード認証よりも公開鍵認証の方が安全なので、できるだけ ~/.ssh/authorized_keys ファイルを用意して公開鍵認証を使うようにしましょう。

たとえば、~/.ssh/id_ed25519 の秘密鍵に対応する公開鍵を www.example.jp のユーザー user001 の ~/.ssh/authorized_keys へ登録するには次のようにします。

ssh-copy-id -i ~/.ssh/id_ed25519 user001@www.example.jp

これで、クライアント側の ~/.ssh/id_ed25519.pub の内容が、www.example.jp の /home/user001/.ssh/authorized_keys へ追加されます。

OpenSSH クライアント

Windows マシンで OpenSSH クライアントを使うにあたっては選択肢がいくつかあるので注意しましょう。OpenSSH 関連のファイルの管理を考えると、慣れるまでは使用する環境を決めて、そこで動作するものを使うようにするのが良いでしょう。

  • OpenSSH Client for Windows
  • Git Bash に同梱されている mingw64 版 OpenSSH Client
  • WSL Ubuntu で使える openssh-client

他にも Docker コンテナで動く openssh-client もありますが、ここでは実際に理解しておく必要がある上記3つについて説明します。

OpenSSH Client for Windows(PowerShell)

Windows では、Windows 版 OpenSSH クライアントが使えます。Linux 使いになりたい人向けの Intel N100 ミニ PC で構築する開発環境(6)- Windows 版 Gitea でも簡単に説明しています。

Windows 版 OpenSSH クライアントについて確認するには、最初に管理者として実行する Git Bash を起動します。

PowerShell -Command "Start-Process 'C:\Program Files\Git\bin\bash.exe' -Verb RunAs"

管理者として実行する Git Bash のターミナルが表示されたら、そこで、Get-WindowsCapabilityWhere-Object コマンドをパイプライン | で組み合わせて使って、OpenSSH のオブジェクト名を確認します。

PowerShell -Command "Get-WindowsCapability -Online | Where-Object Name -like 'OpenSSH*'"

OpenSSH にはクライアント用のものと、サーバー用のものがあり、どちらもインストールされていないと、このコマンドの実行結果は次のようになります。

Name  : OpenSSH.Client~~~~0.0.1.0
State : NotPresent

Name  : OpenSSH.Server~~~~0.0.1.0
State : NotPresent

今回使用している Windows 評価版では、クライアント用の OpenSSH.Client~~~~0.0.1.0 の方はインストールされていました。インストールされていない場合は、クライアント用の OpenSSH.Client~~~~0.0.1.0 の方をインストールします。コマンドは次のようになります。

PowerShell -Command "Add-WindowsCapability -Online -Name OpenSSH.Client~~~~0.0.1.0"

インストールには少し時間がかかります。気長に待ちましょう。インストールが終わったら、OpenSSH for Windows は /c/Windows/System32/OpenSSHC:\Windows\System32\OpenSSH)に用意されます。ls コマンドで確認できます。

ls /c/Windows/System32/OpenSSH

なお、PowerShell が参照する SSH クライアント用のフォルダは $env:USERPROFILE\.ssh となります。この後に説明する mingw64 版 OpenSSH Client と同じになるので注意が必要です。Windows 版の OpenSSH と、mingw64 版 OpenSSH のコマンドについてバージョンがちがっていると、対応可能なオプションやファイルに差がでるため、どちらかでしか動かなくなったり、挙動がちがったり、といったことが起きやすくなります。

PowerShell を起動してインストールされている OpenSSH クライアントの ssh コマンドのバージョンを確認してみましょう。手元では次のようになりました。

> ssh -V
OpenSSH_for_Windows_8.6p1, LibreSSL 3.4.3

mingw64 版 OpenSSH Client(Git Bash)

Git Bash は mingw64 というソフトウェアを使っていて、これには OpenSSH クライアントが同梱されています。同梱されている sshssh-keygenssh-keyscan といったコマンドは Git Bash の環境では /usr/bin/ にあります。

Git Bash でも PowerShell で確認した Windows 版の OpenSSH を使うことはできますが、初期設定では、同梱されているものが優先的に使われるようになっています。Git Bash が参照する SSH クライアント用のフォルダは ~/.ssh/ となり、これは PowerShell の$env:USERPROFILE\.ssh と一致します。

Git Bash を起動してインストールされている OpenSSH クライアントの ssh コマンドのバージョンを確認してみましょう。手元では次のようになりました。

$ ssh -V
OpenSSH_9.6p1, OpenSSL 3.2.1 30 Jan 2024

openssh-client(WSL Ubuntu)

WSL Ubuntu で OpenSSH クライアントを使うには openssh-client パッケージをインストールします。手元ではデフォルトでインストールされていました。もしインストールされていない場合は、apt コマンドでインストールします。

このパッケージがインストールされているかを確認するには、dpkg コマンドと grep コマンドをパイプラインと組み合わせてします。出力行の先頭が ii であればインストールされています。

$ dpkg -l | grep openssh-client
ii  openssh-client  (略)

もしインストールされていない場合は、WSL Ubuntu の環境で次のように apt コマンドを使ってインストールします。

sudo apt update && sudo apt -y install openssh-client

パッケージがインストールされている WSL Ubuntu では、Git Bash と同じ様に /usr/bin/sshssh-keygenssh-keyscan といったコマンドが用意されます。動作確認としては、WSL Ubuntu の ssh コマンドのバージョンを確認しておけば良いでしょう。

手元の環境では次のようになりました。

$ ssh -V
OpenSSH_8.9p1 Ubuntu-3ubuntu0.4, OpenSSL 3.0.2 15 Mar 2022

なお、WSL Ubuntu に限らず Ubuntu での OpenSSH クライアント用のフォルダは ~/.ssh/ となります。

こちらは、OpenSSH Client for Windows や mingw64 版 OpenSSH Client とは別の環境である WSL Ubuntu で実行されます。そのため、OpenSSH Client for Windows や mingw64 版 OpenSSH Client が使う鍵ペアと同じものを使う場合は、/mnt/c/Users/<ユーザー名>/.ssh にあるものをオプションで指定するなどして、コマンド実行する必要があります。

逆に言うと、鍵ペアを別途生成して ~/.ssh/ において使うのは簡単にできます。秘密鍵の管理やトラブル時の対応を考慮すると、別にしておいた方が安全な場合もあります。使用頻度や環境に応じて、どのように鍵ペアを扱うか決めると良いでしょう。

鍵ペアの作成と登録

この後の動作確認用に鍵ペアを作成しておきます。動作確認用なので、すでに作成してある鍵ペアがあった場合でも、それとは別のものを用意して使います。ここでは Git Bash と、これに同梱されている OpenSSH クライアントを使うことにします。

Git Bash を起動して ssh-keygen コマンドで ed25519 タイプの鍵ペアを生成します。ここで作成する秘密鍵は OpenSSH の動作確認用のものなので、~/.ssh/id_ed25519_openssh_eval というファイルパスで用意し、コメントには「OpenSSH Eval」といれておきます。

ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519_openssh_eval -C "OpenSSH Eval"

このコマンドを実行すると、Git Bash の環境で ~/.ssh/id_ed25519_openssh_eval~/.ssh/id_ed25519_openssh_eval.pub が作成されます。

OpenSSH サーバー

OpenSSH クライアントを使うにあたっては、OpenSSH サーバーを実際に用意する必要があります。OpenSSH サーバーを Windows マシンで稼働させる方法はいくつかあります。

  • openssh-server (Docker コンテナ)
  • openssh-server (WSL Ubuntu)
  • OpenSSH Server for Windows

ここでは、本格的に動かすのではなく、試しに各環境で OpenSSH サーバーを動作させてみる程度という前提で説明します。本格的に動かす場合は、きちんと OpenSSH の詳細設定について理解してからにしてください。あまり知識がない状態で常時稼働をさせるのはリモートから攻撃される危険性を考えるとやめておいた方が良いです。

Docker コンテナーで OpenSSH サーバー

試しに使う OpenSSH サーバーを簡単に起動するには Docker を使うのが一番です。

ここでは VS Code を使って Docker Compose 用のファイルを編集します。これから作成する Docker Compose 用ファイルの compose.yaml などは、WSL Ubuntu との共用を考えると、改行コードを \n (LF) としておくのが良いのですが、Windows 環境では基本的にテキストファイルの改行コードは \r\n (CRLF) となるので注意が必要です。VS Code だと、改行コードを \n (LF) へ変更してテキストファイルを作成することが簡単にできるので、おすすめです。

VS Code では、ステータスバーに改行コードが表示されています。Windows だと改行コードが「CRLF」となっているので、その部分をクリックします。すると、改行コードを CRLF、LF のどちらにするか選択ができます。新しいファイルをエディタで開いたら、これを「LF」へ変更してから編集しましょう。

OpenSSH Server を試すための compose.yaml ファイルは次の内容で用意します。ここでは、/c/Users/User/workspace/openssh-server/compose.yaml に作成したとします。

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
    environment:
      PUID: 1000
      PGID: 1000
      PUBLIC_KEY: ${PUBLIC_KEY}
      SUDO_ACCESS: false
      PASSWORD_ACCESS: false
      USER_NAME: user001

Docker イメージは linuxserver/openssh-server というものを使います。environment: で指定している USER_NAME 環境変数の値でユーザー名が決まります。PUIDPGID で openssh-server コンテナーで使用するユーザー ID とグループ ID が指定できるので、それぞれ 1000 としてあります。
 SUDO_ACCESS は true にすると、openssh-server コンテナーで使用するユーザーについて、パスワードなしで sudo コマンドが実行できるようになります。これによりコンテナーで使用するユーザーへ管理者権限を付与することができます。オプションとして USER_PASSWORD でパスワードを指定することもできます。または、USER_PASSWORD_FILE を使ってファイルでパスワードを指定することもできます。
 公開鍵認証だけではなくパスワード認証を有効にしたい場合は PASSWORD_ACCESS を true にします。ここでは無効化しておくので false にしています。

PUBLIC_KEY にはコンテナーで使用するユーザー用の公開鍵を指定します。ここでは compoes.yaml 用の環境変数 ${PUBLIC_KEY} を使って指定します。compoes.yaml 用の環境変数の値は compoes.yaml と同じディレクトリーにある .env ファイル(/c/Users/User/workspace/openssh-server/.env)で指定することができます。内容としては次のようになります。

.env
PUBLIC_KEY=<`~/.ssh/id_ed25519_openssh_eval.pub` のファイルの内容>

ここでは、手元で作成した id_ed25519_openssh_eval.pub のファイルの内容を指定します。具体的には次のようになりました。

PUBLIC_KEY=ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGczhBGLxwRdGZXc/dq6SarzOpjXCEEzk/8WBxcI2xkt OpenSSH Eval

OpenSSH 用コンテナーの起動

必要なファイルを用意したら、Docker Desktop を起動します。コマンドで起動するには次のようにします。

PowerShell -Command "Start-Process 'C:\Program Files\Docker\Docker\Docker Desktop.exe'"

コンピュータへ変更を加えることを許可する画面が出た場合は「はい」をクリックします。

Docker Desktop が起動したら、Git Bash で compose.yaml ファイルがあるディレクトリーをカレントディレクトリーにしてから、docker compose コマンドを使って openssh-server コンテナーを起動します。

docker compose up -d

実際に実行すると次のようになります。

$ docker compose up -d
[+] Running 1/2
 - Network openssh-server_default  Created   4.7s
 ✔ Container openssh-server        Started   3.7s

コンテナーが起動したらら、docker compose logs でログを確認します。compose.yaml の name に指定したものがプロジェクト名になります。ここでは openssh-server としてあるので、これを -p オプションで指定すると、openssh-server に含まれるサービスのログが確認できます。

docker compose -p openssh-server logs

実際に実行すると、次のように出力されます。

$ docker compose -p openssh-server logs
openssh-server  | [migrations] started
openssh-server  | [migrations] no migrations found
openssh-server  | ───────────────────────────────────────
openssh-server  |
openssh-server  |       ██╗     ███████╗██╗ ██████╗
openssh-server  |       ██║     ██╔════╝██║██╔═══██╗
openssh-server  |       ██║     ███████╗██║██║   ██║
openssh-server  |       ██║     ╚════██║██║██║   ██║
openssh-server  |       ███████╗███████║██║╚██████╔╝
openssh-server  |       ╚══════╝╚══════╝╚═╝ ╚═════╝
openssh-server  |
openssh-server  |    Brought to you by linuxserver.io
openssh-server  | ───────────────────────────────────────
openssh-server  |
openssh-server  | To support LSIO projects visit:
openssh-server  | https://www.linuxserver.io/donate/
openssh-server  |
openssh-server  | ───────────────────────────────────────
openssh-server  | GID/UID
openssh-server  | ───────────────────────────────────────
openssh-server  |
openssh-server  | User UID:    1000
openssh-server  | User GID:    1000
openssh-server  | ───────────────────────────────────────
openssh-server  |
openssh-server  | User name is set to user001
openssh-server  | sudo is disabled.
openssh-server  | ssh-keygen: generating new host keys: RSA ECDSA ED25519
openssh-server  | sshd is listening on port 2222
openssh-server  | User/password ssh access is disabled.
openssh-server  | Public key from env variable added
openssh-server  | [custom-init] No custom files found, skipping...
openssh-server  | [ls.io-init] done.

プロジェクトのサービスが動作していることを確認するために docker compose ls コマンドを実行します。プロジェクト名が NAME に、状態が STATUS に表示されます。次のように openssh-server の状態が running(1) となっていれば、openssh-server のサービスが動いています。

$ docker compose ls
NAME                STATUS              CONFIG FILES
openssh-server      running(1)          (略)/openssh-server/compose.yaml

コンテナーの OpenSSH サーバー公開鍵

これで ssh コマンドを使って openssh-server コンテナーへログインできます。先に openssh-server のフィンガープリントを確認しておきます。docker compose exec コマンドを使って、コンテナー内で ssh-keygen コマンドを実行することでフィンガープリントを表示します。

docker compose -p openssh-server exec openssh-server \
  bash -c 'ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub'

ここで表示された値は、後で SSH ログインするときにフィンガープリントの確認で使います。メモしておきましょう。手元では次のような実行結果になりました。

$ docker compose -p openssh-server exec openssh-server \
  bash -c 'ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub'
256 SHA256:pWNxc3sudC5F5vhS3ymMxhHMsdQN1aOBg1QZ2+lXJoE root@openssh-server (ED25519)

次に、SSH ログインする前にあらかじめホスト公開鍵を ~/.ssh/known_hosts ファイルへ登録してみます。登録する値について確認が必要なので、openssh-server のホスト公開鍵の値を確認しておきます。こちらも docker compose exec コマンドを使います。ただし、コンテナー内では cat コマンドを実行します。

$ docker compose -p openssh-server exec openssh-server \
  bash -c 'cat /etc/ssh/ssh_host_ed25519_key.pub'
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKEWj/pFYnLSuiNPuKDmw0+/CBFe+0GYsm37vbe8h6ML root@openssh-server

ssh-keyscan コマンドで、ホストの公開鍵が表示できることを確認します。

ssh-keyscan -t ed25519 -p 2222 127.0.0.1

なお、127.0.0.1 の IP アドレスと対応するホスト名は localhost です。こちらを指定するときは、名前解決されるときに IPv4 の IP アドレスになるようにオプション -4-o AddressFamily=inet のオプション指定が必要です。これがないと IPv6 の IP アドレスで名前解決がされてしまい、compose.yaml で指定した 127.0.0.1:2222 へのアクセスができません。

ssh-keyscan -4 -t ed25519 -p 2222 localhost

実際に実行すると、次のように openssh-server:/etc/ssh/ssh_host_ed25519_key.pub の値が [<ホスト名または IP アドレス>]:2222 に続いて表示されます。ssh_host_ed25519_key.pub ファイルの公開鍵の値と、ssh-keyscan コマンドが出力する公開鍵部分の値が一致していない場合は、接続先が違っているということになります。

$ ssh-keyscan -t ed25519 -p 2222 127.0.0.1
# 127.0.0.1:2222 SSH-2.0-OpenSSH_9.6
[127.0.0.1]:2222 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKEWj/pFYnLSuiNPuKDmw0+/CBFe+0GYsm37vbe8h6ML
$ ssh-keyscan -4 -t ed25519 -p 2222 localhost
# localhost:2222 SSH-2.0-OpenSSH_9.6
[localhost]:2222 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKEWj/pFYnLSuiNPuKDmw0+/CBFe+0GYsm37vbe8h6ML

きちんとホストの公開鍵が一致していたら、ssh-keyscan の結果を ~/.ssh/known_hosts ファイルへ追加登録します。ここでは 127.0.0.1 のものだけ登録しておきます。次のようにリダイレクト >> を使うのが簡単です。

ssh-keyscan -t ed25519 -p 2222 127.0.0.1 >> ~/.ssh/known_hosts

OpenSSH 用コンテナーへの SSH ログイン

これで準備ができたので、ssh コマンドで SSH ログインします。

秘密鍵は、PUBLIC_KEY で指定したものとペアのものを使うので、ここでは -i オプションで ~/.ssh/id_2519_openssh_eval を指定します。

また、Docker で動かしている openssh-server コンテナーは、ホストは localhost(IP は 127.0.0.1)、ポート番号 2222 で動作していて、ユーザー user001 でログイン可能です。ポート番号は、-p オプションで指定し、パラメーターには user001@127.0.0.1 を指定することでログインができします。

以上をまとめると、openssh-server コンテナーへ SSH ログインするためのコマンドは、次のようになります。

ssh -i ~/.ssh/id_ed25519_openssh_eval -p 2222 user001@127.0.0.1

実際に実行して、次のように SSH ログインできることを確認します。

$ ssh -i ~/.ssh/id_ed25519_openssh_eval -p 2222 user001@127.0.0.1
Welcome to OpenSSH Server
openssh-server:~$

SSH ログイン接続を終了するには exit コマンドを実行します。

openssh-server:~$ exit

localhost:2222 への SSH ログイン

次は、ホストの公開鍵を事前に登録していない localhost を使ってみましょう。IPv4 の IP アドレスを使うには -4-o AddressFamily=inet のオプション指定が必要です。localhost へ SSH ログインするときは、次のようになるはずです。

$ ssh -4 -i ~/.ssh/id_ed25519_openssh_eval -p 2222 user001@localhost
The authenticity of host '[localhost]:2222 ([127.0.0.1]:2222)' can't be established.
ED25519 key fingerprint is SHA256:pWNxc3sudC5F5vhS3ymMxhHMsdQN1aOBg1QZ2+lXJoE.
This host key is known by the following other names/addresses:
    ~/.ssh/known_hosts:1: [127.0.0.1]:2222
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
openssh-server:~$

localhost の場合は、127.0.0.1 の場合と違って、ホストの公開鍵を先に登録する必要があるため、「ED25519 key fingerprint is SHA256:pWNxc3sudC5F5vhS3ymMxhHMsdQN1aOBg1QZ2+lXJoE.」とフィンガープリントが表示されます。

それから、「Are you sure you want to continue connecting (yes/no/[fingerprint])?」とプロンプトが表示されます。ここで事前に確認したフィンガープリントと、ここで表示されているフィンガープリントを照合して一致していたら、プロンプトへ yes と入力して Enter キーを入力します。

すると、~/.ssh/known_hosts へ localhost のエントリーが登録されて SSH ログインが成功します。ログインが確認できたら exit コマンドでログアウトします。

今回用意した compose.yaml ファイルでは openssh-server のホスト用公開鍵については、新規に起動する度に作成されます。そのため、~/.ssh/known_hosts ファイルからエントリーを削除しておいた方が良いでしょう。ssh-keygen コマンドで対応しておきます。

ssh-keygen -R [127.0.0.1]:2222
ssh-keygen -R [localhost]:2222

実際に実行すると次のようになります。

$ ssh-keygen -R [127.0.0.1]:2222
# Host [127.0.0.1]:2222 found: line 1
# Host [127.0.0.1]:2222 found: line 2
# Host [127.0.0.1]:2222 found: line 3
/c/Users/User/.ssh/known_hosts updated.
Original contents retained as /c/Users/User/.ssh/known_hosts.old
$ ssh-keygen -R [localhost]:2222
# Host [localhost]:2222 found: line 1
# Host [localhost]:2222 found: line 2
# Host [localhost]:2222 found: line 3
/c/Users/User/.ssh/known_hosts updated.
Original contents retained as /c/Users/User/.ssh/known_hosts.old

Docker コンテナーの破棄

動作確認が済んだら openssh-server のコンテナーは docker compose down コマンドで破棄します。

docker compose -p openssh-server down

実際に実行すると次のようになります。

$ docker compose -p openssh-server down
[+] Running 2/2
 ✔ Container openssh-server        Removed    3.7s
 ✔ Network openssh-server_default  Removed    0.5s

それから、Docker Desktop を停止します。Docker Desktop の画面を閉じただけでは、Docker Desktop は停止しません。停止するには、タスクバーで Docker Desktop のメニューを表示して、「Quit Docker Desktop」をクリックします。

WSL Ubuntu OpenSSH サーバー

ここでは WSL Ubuntu で OpenSSH サーバーを起動して使えるようにしてみます。

なお、WSL Ubuntu で動作する OpenSSH サーバーを、Windows OS の Git Bash や PwoerShell から使えるようにするには、Windows OS でネットワークの設定が必要になります。それについても説明します。

WSL Ubuntu では systemd を使用するので、有効にしていない場合は /etc/wsl.conf を編集して、次のように [boot] セクションへ systemd=true を指定します。

/etc/wsl.conf
[boot]
systemd=true

それから Git Bash で wsl コマンドを使って WSL Ubuntu を停止します。

wsl -t Ubuntu

その後に Git Bash で WSL Ubuntu を起動すると、systemd が有効になります。

wsl -d Ubuntu

openssh-server のインストール

Ubuntu で OpenSSH サーバーを使うには openssh-server パッケージをインストールします。apt update コマンドでパッケージ情報を更新してから、apt install コマンドでインストールします。

sudo apt update
sudo apt -y install openssh-server

openssh-server をインストールすると自動で systemd で sshd サービスが有効化されて OpenSSH サーバーのソフトウェアが起動します。systemctl status コマンドで、OpenSSH サーバーのソフトウェアの sshd が稼働していることが確認できます。

sudo systemctl status sshd

実際に実行すると次のようになります。Active: の行で active (running) となっていることから、コマンドが実行されて稼働していることがわかります。実際に実行されているコマンドは /usr/sbin/sshd になります。

● ssh.service - OpenBSD Secure Shell server
     Loaded: loaded (/lib/systemd/system/ssh.service; enabled; vendor preset: enabled)
     Active: active (running) since Wed 2024-04-03 21:57:08 JST; 1min 11s ago
       Docs: man:sshd(8)
             man:sshd_config(5)
   Main PID: 1558 (sshd)
      Tasks: 1 (limit: 2357)
     Memory: 1.7M
     CGroup: /system.slice/ssh.service
             └─1558 "sshd: /usr/sbin/sshd -D [listener] 0 of 10-100 startups"

Apr 03 21:57:08 Win11Eval systemd[1]: Starting OpenBSD Secure Shell server...
Apr 03 21:57:08 Win11Eval sshd[1558]: Server listening on 0.0.0.0 port 22.
Apr 03 21:57:08 Win11Eval sshd[1558]: Server listening on :: port 22.
Apr 03 21:57:08 Win11Eval systemd[1]: Started OpenBSD Secure Shell server.

sshd の停止と起動

sshd サービスを停止するには systemctl stop コマンドを使います。管理者権限が必要なので WSL Ubuntu を一般ユーザーで使っている場合は sudo コマンドをつけて実行します。

sudo systemctl stop sshd

停止した sshd サービスを開始するには systemctl start コマンドを使います。

sudo systemctl start sshd

再起動するには systemctl restart コマンドを使います。

sudo systemctl restart sshd

WSL Ubuntu への SSH ログイン

WSL Ubuntu の OpenSSH サーバーが起動したら、WSL Ubuntu の ssh コマンドで動作確認ができます。Docker を使った場合の動作確認と同じようにすれば良いです。

まず、WSL Ubuntu でホストの公開鍵を確認します。

user001@Win11Eval:~$ cat /etc/ssh/ssh_host_ed25519_key.pub
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHon6sarmvkGXFZTkuW7NoUpHEhRR6wf4/cNduyVTQWY root@Win11Eval

次に、WSL Ubuntu でホストのフィンガープリントを確認します。

user001@Win11Eval:~$ ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub
256 SHA256:QlRcU7B8hx8diSLID/nZ6JFPslI00zgqqADEBDlnp1Q root@Win11Eval (ED25519)

WSL Ubuntu の openssh-server パッケージでは、初期設定でパスワード認証が有効になっています。WSL Ubuntu のターミナルで ssh コマンドを実行して localhost へ user001 ユーザーとして SSH ログインしてみましょう。

ssh user001@localhost

実際に実行すると次のようになります。「Are you sure you want to continue connecting (yes/no/[fingerprint])?」に対しては yes とし、次のパスワード入力用のプロンプト「user001@localhost's password:」では、パスワードを入力します。これで SSH ログインができます。

user001@Win11Eval:~$ ssh user001@localhost
The authenticity of host 'localhost (127.0.0.1)' can't be established.
ED25519 key fingerprint is SHA256:QlRcU7B8hx8diSLID/nZ6JFPslI00zgqqADEBDlnp1Q.
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' (ED25519) to the list of known hosts.
user001@localhost's password:
Welcome to Ubuntu 22.04.3 LTS (GNU/Linux 5.15.146.1-microsoft-standard-WSL2 x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage

Welcome to Ubuntu 22.04.3 LTS (GNU/Linux 5.15.146.1-microsoft-standard-WSL2 x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage

Last login: Fri Mar 15 21:09:06 2024
user001@Win11Eval:~$

SSH ログインすると、わかりにくいですが、WSL Ubuntu と同じ画面になります。ps コマンドを実行したときに ssh user001@localhost の文字列を含むものがあれば、SSH ログインは成功しています。パイプライン(|)と grep コマンドを組み合わせて実行すると確認ができます。

ここでは次のように確認しました。

user001@Win11Eval:~$ ps ax | grep "[s]sh user001"
   2728 pts/0    S+     0:00 ssh user001@localhost

確認が済んだら、exit コマンドで SSH ログアウトします。すると、また WSL Ubuntu と同じ画面になります。このとき、WSL Ubuntu の画面にならなかった場合は、SSH ログインができていなかったということになります。

user001@Win11Eval:~$ exit

もう一度 exit コマンドで WSL Ubuntu からログアウトします。Git Bash から WSL Ubuntu へログインしていたら Git Bash の画面になり、Windows ターミナルから WSL Ubuntu へログインしていたら画面が閉じます。

以上で、WSL Ubuntu から localhost(WSL Ubuntu 自身)の OpenSSH サーバーへの SSH ログインはできるようになりました。

WSL Ubuntu の IP アドレス

現時点の設定では PowerShell と Git Bash からは WSL Ubuntu の OpenSSH サーバーへの SSH ログインができません。ここでは、この問題を解決するための設定をします。

これを解決のためには、ネットワークの設定でポートフォワードのルールを追加します。すると、Windows OS のあるポート番号へのアクセスを WSL Ubuntu の指定したポート番号へ転送することができるようになります。この設定をするにあたり、転送先とする WSL Ubuntu の IP アドレスが必要になります。

ということで、WSL Ubuntu の IP アドレスの確認をする方法について説明します。Ubuntu では hostname コマンドへ -I オプションを指定すると IP アドレスの一覧が表示されます。

hostname -I

Docker コンテナーを起動していると IP アドレスが複数表示されることがあります。その場合は、先頭のものを使うと良いでしょう。

hostname -I | awk '{print $1}'

この方法では正しい IP アドレスが取得できない場合もあります。その場合は ip コマンドを使います。コマンドが少し複雑になりますが、次のようなスクリプトにして実行すれば取得できるはずです。

ip.sh
#!/bin/sh
ip a | grep -A 3 '<BROADCAST' | grep inet | awk '{print $2}' | awk -F '/' '{print $1}'

次のように WSL Ubuntu 内でスクリプトを echo コマンドなどで作成したら、chmod コマンドで実行可能とし、/usr/local/bin へ移動します。

echo "#!/bin/sh" > ip.sh
echo "ip a | grep -A 3 '<BROADCAST' | grep inet | awk '{print $2}' | awk -F '/' '{print $1}'" >> ip.sh
chmod 755 ip.sh
sudo mv ip.sh /usr/local/bin/

用意ができたら、ip.sh を実行してみましょう。すると、スクリプトが動作して、IP アドレスが出力されます。

ip.sh

これらのコマンドを Git Bash から wsl exec コマンド経由で実行することができます。例えば hostname -I を実行するなら、次のようにします。

wsl -d Ubuntu exec hostname -I

手元で実際に実行すると次のようになりました。

$ wsl -d Ubuntu exec "hostname -I"
172.22.192.181

これで、転送先のアドレスが 172.22.192.181 だということがわかりました。

ポートフォワード

それでは、Git Bash や PowerShell から SSH ログインができるように、ポートフォワードのルールを追加します。WSL Ubuntu の IP アドレスについては、ここでは 172.22.192.181 だとして説明します。

Windows OS でポートフォワードの設定をするには、管理者で実行するターミナルを起動して、netsh.exe interface portproxy コマンドを使います。

ここでは、Windows OS での IP アドレス 127.0.0.1 の 22 番ポートへのアクセスを WSL Ubuntu の IP アドレス 172.22.192.181 の 22 番ポートへ転送するポートフォワードのルールを追加するので、netsh.exe interface portproxy add のコマンドを実行します。オプションとしては、次のものを指定することができます。

オプション 説明
v4tov4 IPv4 から IPv4 への転送
listenaddress Windows OS の IP アドレス
listenport Windows OS のポート番号
connectaddress 転送先の IP アドレス
connectport 転送先のポート番号

最初に、管理者として実行する Git Bash を起動します。

PowerShell -Command "Start-Process 'C:\Program Files\Git\bin\bash.exe' -Verb RunAs"

管理者として実行する Git Bash の画面が表示されたら、netsh.exe コマンドで転送のルールを追加します。

netsh.exe interface portproxy add \
    v4tov4 \
    listenaddress=127.0.0.1 listenport=22 \
    connectaddress=172.22.192.181 connectport=22

ルールを追加したら確認しましょう。ポートフォワードのルールを確認するには netsh.exe interface portproxy show コマンドを使い、パラメーターへ v4tov4 を指定すると IPv4 アドレスから IPv4 アドレスへ転送しているものが表示されます。

netsh.exe interface portproxy show v4tov4

手元の環境で実際に実行すると、次のようになりました。

$ netsh.exe interface portproxy show v4tov4

ipv4 をリッスンする:         ipv4 に接続する:

Address         Port        Address         Port
--------------- ----------  --------------- ----------
127.0.0.1       22          172.22.192.181  22

ルールを削除するには、ルール追加時の adddelete とします。ただし、オプションには listenportlistenaddressprotocol だけが指定可能です。

netsh.exe interface portproxy delete v4tov4 listenport=22 listenaddress=127.0.0.1

なお、WSL Ubuntu の IP アドレスは固定ではないので、パソコンや WSL Ubuntu を再起動すると変わることがあります。

次のような wsl_openssh_port_forward.sh を用意しておくと、Git Bash で簡単にポートフォワードのルール追加ができるようになります。

wsl_openssh_port_forward.sh
#!/bin/sh
IP=$(wsl -d Ubuntu exec hostname -I)
netsh.exe interface portproxy delete v4tov4 listenport=22 listenaddress=127.0.0.1
netsh.exe interface portproxy add v4tov4 listenaddress=127.0.0.1 listenport=22 connectaddress=$IP connectport=22
netsh.exe interface portproxy show v4tov4

このスクリプトを利用するには、管理者として実行する Git Bash でスクリプトのあるディレクトリーをカレントディレクトリーとして、次のように実行します。なお、シェルスクリプトなので PowerShell では動作しません。

sh ./wsl_openssh_port_forward.sh

これで、ポートフォワードのルールが追加され、その内容が表示されます。なお、ポートフォワードのルールがされていない場合は「指定されたファイルが見つかりません。」といったメッセージが表示されますが、続いて表示されるポートフォワードのルールに問題がなければ無視して大丈夫です。

実際に、ポートフォワードの指定がされていない状態で実行した場合は、次のようになりました。

$ sh ./wsl_openssh_port_forward.sh
指定されたファイルが見つかりません。
(略)
ipv4 をリッスンする:         ipv4 に接続する:

Address         Port        Address         Port
--------------- ----------  --------------- ----------
127.0.0.1       22          172.22.192.181  22

ここまでの作業でも Git Bash から localhost へ SSH ログインができるようになりますが、手元では SSH ログイン中に、Connection to localhost closed by remote host. のエラーが発生して自動ログアウトしてしまうことがありました。そこで、WSL Ubuntu の /etc/ssh/sshd_config で OpenSSH サーバーの設定を見直して、クライアントの生存チェックに関係する ClientAliveInterval を 0 から 60 へ変更して有効化しました。この指定で 60 秒毎にクライアントの生存チェックがされます。

WSL Ubuntu の /etc/ssh/sshd_config
ClientAliveInterval 60

それから WSL Ubuntu の systemctl コマンドで sshd サービスを再起動してからポートフォワードのルールの再設定をしたところ、エラーが発生しなくなりました。

wsl -d Ubuntu -u root systemctl restart sshd
sh ./wsl_openssh_port_forward.sh

ここまで設定したら、Git Bash や PowerShell から localhost へ SSH ログインしてみましょう。IPv4 の IP アドレスを使うため -4 オプションを指定します。

ssh -4 user001@localhost

SSH ログインが成功すると、WSL Ubuntu の bash のプロンプトである user001@Win11Eval:~$ が表示されます。

$ ssh -4 user001@localhost
(略)
user001@Win11Eval:~$

このとき、次のように WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! というメッセージが出る場合は known_hosts のエントリーに WSL Ubuntu のホスト公開鍵ではないものが登録されています。

$ ssh -4 user001@localhost
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@    WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!     @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!
Someone could be eavesdropping on you right now (man-in-the-middle attack)!
It is also possible that a host key has just been changed.
The fingerprint for the ED25519 key sent by the remote host is
SHA256:QlRcU7B8hx8diSLID/nZ6JFPslI00zgqqADEBDlnp1Q.
Please contact your system administrator.
Add correct host key in /c/Users/User/.ssh/known_hosts to get rid of this message.
Offending ECDSA key in /c/Users/User/.ssh/known_hosts:3
Host key for localhost has changed and you have requested strict checking.
Host key verification failed.

Git Bash のターミナルで ssh-keygen -R コマンドを使ってエントリーを削除します。実際に実行した場合の例を次に示します。

$ ssh-keygen -R localhost
# Host localhost found: line 2
# Host localhost found: line 3
/c/Users/User/.ssh/known_hosts updated.
Original contents retained as /c/Users/User/.ssh/known_hosts.old

それから SSH ログインをすると、WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! というメッセージはでなくなります。

ssh-copy-id で authorized_keys の設定

ここでは ssh-copy-id で authorized_keys の設定をしてみましょう。WSL Ubuntu へ SSH ログインしている場合は exit コマンドでログアウトします。

Git Bash のターミナルで ssh-copy-id コマンドを使って、WSL Ubuntu の ${HOME}/.ssh/authorized_keys へユーザーの公開鍵をエントリーします。

ここで、Windows OS では、ホスト名に localhost を使うと IPv6 の IP アドレスが優先されて IPv4 の IP アドレスにならない場合があります。ssh コマンドでは -4 のオプションがありましたが、ssh-copy-id コマンドにはありません。そのかわり、-o オプションで ssh コマンドの -o オプションで指定できるものが使えるので、ここでは -o AddressFamily=inet を指定することにします。これで IPv4 の IP アドレスで名前解決がされるようになります。

以上をまとめると、実行するコマンドは次のようになります。

ssh-copy-id -i ~/.ssh/id_ed25519_openssh_eval -o AddressFamily=inet user001@localhost

現時点では WSL Ubuntu の user001 用の authorized_keys には公開鍵が登録されていないので、OpenSSH サーバーはパスワード認証を使ったチェックをします。パスワードを入力して認証が通ると、user001 の id_ed25519_openssh_eval.pub の公開鍵が ${HOME}/.ssh/authorized_keys へ保存されます。

実際にこのコマンドを実行した時は、次のようになりました。

User@Win11Eval MINGW64 ~
$ ssh-copy-id -i .ssh/id_ed25519_openssh_eval -o AddressFamily=inet user001@localhost
/usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: ".ssh/id_ed25519_openssh_eval.pub"
/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
/usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys
user001@localhost's password:

Number of key(s) added: 1

Now try logging into the machine, with:   "ssh 'user001@localhost'"
and check to make sure that only the key(s) you wanted were added.

それから ssh コマンドで SSH ログインをすると、OpenSSH サーバーは今度は公開鍵認証を使ったチェックをします。公開鍵認証の方がパスワード認証よりも優先的に実行されるからです。

ここで、パスフレーズが設定されている秘密鍵を使うとパスフレーズの入力をするためのプロンプトが表示されます。正しいパスフレーズを入力すると、WSL Ubuntu へ SSH ログインができます。

$ ssh -i .ssh/id_ed25519_openssh_eval -o AddressFamily=inet user001@localhost
Enter passphrase for key '.ssh/id_ed25519_openssh_eval':
(略)
user001@Win11Eval:~$

ちなみに、パスフレーズが設定されていない秘密鍵を使うと、パスフレーズの入力は必要ないので、そのまま WSL Ubuntu へ SSH ログインができます。

sshd サービスの無効化

動作確認ができたら、ポートフォワードのルールを削除し、WSL Ubuntu の sshd サービスも無効にしておきましょう。管理者として実行する Git Bash で次のコマンドを実行します。

netsh.exe interface portproxy delete v4tov4 listenport=22 listenaddress=127.0.0.1
wsl -d Ubuntu -u root systemctl stop sshd
wsl -d Ubuntu -u root systemctl disable sshd

再度 sshd サービスを有効化する場合は、systemctl enable コマンドを使います。ただし、サービス名を ssh として指定します。ここまで、ssh コマンドと混同しないように sshd サービスと説明してきましたが、Ubuntu ではサービス名としては、sshd と ssh の療法が使えます。

wsl -d Ubuntu -u root systemctl enable ssh

ちなみに、サービス有効化と同時に sshd サービスを開始するには、--now オプションを指定します。

wsl -d Ubuntu -u root systemctl enable ssh --now

それから、ポートフォワードを設定します。

sh ./wsl_openssh_port_forward.sh

OpenSSH Server for Windows

Windows では、OpenSSH Server for Windows が使えます。Linux 使いになりたい人向けの Intel N100 ミニ PC で構築する開発環境(6)- Windows 版 Gitea でも簡単に説明しています。それでは、OpenSSH Server for Windows をインストールします。
 
 最初に管理者として実行する Git Bash を起動します。

PowerShell -Command "Start-Process 'C:\Program Files\Git\bin\bash.exe' -Verb RunAs"

管理者として実行する Git Bash のターミナルが表示されたら、そこで、Get-WindowsCapabilityWhere-Object コマンドをパイプライン | で組み合わせて使って、OpenSSH をインストールするにあたってオブジェクト名を確認します。

PowerShell -Command "Get-WindowsCapability -Online | Where-Object Name -like 'OpenSSH*'"

OpenSSH にはクライアント用のものと、サーバー用のものがあり、どちらもインストールされていないと、このコマンドの実行結果は次のようになります。

Name  : OpenSSH.Client~~~~0.0.1.0
State : NotPresent

Name  : OpenSSH.Server~~~~0.0.1.0
State : NotPresent

今回使用している Windows 評価版では、クライアント用の OpenSSH.Client~~~~0.0.1.0 の方はインストールされていました。ということで、ここではサーバー用の OpenSSH をインストールします。インストールするには Add-WindowsCapability コマンドを使います。

PowerShell -Command "Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0"

インストールには少し時間がかかります。気長に待ちましょう。

OpenSSH for Windows は /c/Windows/System32/OpenSSHC:\Windows\System32\OpenSSH)へインストールされます。ls コマンドで確認することができます。

ls /c/Windows/System32/OpenSSH

インストールが終わったら、Git Bash でホストの公開鍵とフィンガープリントを確認します。具体的には次のコマンドを実行します。

PROFILE_DIR_PATH=$(PowerShell -Command 'echo $env:ALLUSERSPROFILE' | sed 's%C:\\%/c/%' | sed 's%\\%/%g')
cat ${PROFILE_DIR_PATH}/ssh/ssh_host_ed25519_key.pub
ssh-keygen -lf ${PROFILE_DIR_PATH}/ssh/ssh_host_ed25519_key.pub

手元で実際に実行すると次のようになりました。

$ PROFILE_DIR_PATH=$(PowerShell -Command 'echo $env:ALLUSERSPROFILE' | sed 's%C:\\%/c/%' | sed 's%\\%/%g')
$ cat ${PROFILE_DIR_PATH}/ssh/ssh_host_ed25519_key.pub
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMyBkAoRF8PioiLpSaEUHvJLnK4H5CXzFdUtCOEk5ctw system@Win11Eval
$ ssh-keygen -lf ${PROFILE_DIR_PATH}/ssh/ssh_host_ed25519_key.pub
256 SHA256:AKbZ779uqKmdQFPKZ7KSV6ETIxLPNhsZBL/khGBoTr8 system@Win11Eval (ED25519) 

次に OpenSSH サーバーを起動します。OpenSSH サーバーをインストールすると、sshd サービスが Windows のサービスへ追加されます。これを起動するには、Start-Service コマンドを使います。また、自動起動を有効にするには、Set-Service コマンドを使います。

具体的に実行するコマンドは次のようになります。

PowerShell -Command "Start-Service sshd -Verb RunAs"
PowerShell -Command "Set-Service -Name sshd -StartupType 'Automatic'"

OpenSSH for Windows 用の authorized_keys ファイル

Windows で動作する OpenSSH サーバーへ接続するときに使用する公開鍵のファイルについては注意が必要です。一般ユーザーは $env:USERPROFILE\.ssh\authorized_keys、Windows の管理者は $env:ALLUSERSPROFILE\ssh\administrators_authorized_keys が使われるようになっていて、ファイルが違っています。公開鍵の登録をするときに間違えないようにしましょう。

Windows 評価版の User ユーザーは管理者なので、$env:ALLUSERSPROFILE\ssh\administrators_authorized_keys を使います。

そのため、~/.ssh/id_ed25519_openssh_eval.pub の内容を確認してコピーし、administrators_authorized_keys (一般ユーザーの場合は authorized_keys)へ追加します。管理者でメモ帳を開いて編集するなどします。

管理者として実行する Git Bash を起動します。

PowerShell -Command "Start-Process 'C:\Program Files\Git\bin\bash.exe' -Verb RunAs"

次に管理者として実行する Git Bash のターミナルで次のコマンドを実行すると追加することができます。環境変数 AUTH_KEY_DIR_PATH へ OpenSSH Server for Windows が使う ssh 用ディレクトリーを設定し、それを使って authorized_keys ファイルのパスを AUTH_KEY_PATH へ設定しています。それから、cat コマンドで参照する公開鍵のファイルを指定して、環境変数 AUTH_KEY_PATH のファイルへエントリー追加をしています。

AUTH_KEY_DIR_PATH=$(PowerShell -Command 'echo $env:ALLUSERSPROFILE' | sed 's%C:\\%/c/%' | sed 's%\\%/%g')
AUTH_KEY_PATH=${AUTH_KEY_DIR_PATH}/ssh/administrators_authorized_keys
cat ${HOME}/.ssh/id_ed25519_openssh_eval.pub >> ${AUTH_KEY_PATH}

一般ユーザーの場合は Git Bash で次のコマンドを実行すると追加することができます。

AUTH_KEY_DIR_PATH=$(PowerShell -Command 'echo $env:USERPROFILE' | sed 's%C:\\%/c/%' | sed 's%\\%/%g')
if [ ! -e ${AUTH_KEY_DIR_PATH}/.ssh ]; then mkdir ${AUTH_KEY_DIR_PATH}/.ssh; fi
AUTH_KEY_PATH=${AUTH_KEY_DIR_PATH}/.ssh/authorized_keys
cat ${HOME}/.ssh/id_ed25519_openssh_eval.pub >> ${AUTH_KEY_PATH}

Windows 版 sshd への SSH ログイン

準備ができたら、Git Bash で ssh コマンドを使って SSH 接続をしてみましょう。ユーザー名には Windows のユーザーを指定するので、ここでは評価版 Windows の User を指定しています。ポート番号は SSH が使用するデフォルトの 22 番なので、指定を省略できますが、ここでは明示的に -p オプションを使って指定します。

ssh -i ~/.ssh/id_ed25519_openssh_eval -p 22 -l User localhost

SSH ログインが成功すると、コマンドプロンプトの画面になります。exit すると Git Bash の画面に戻ります。

次に WSL Ubuntu から ssh コマンドを使って SSH 接続をしてみましょう。ここで、WSL Ubuntu で sshd サービスを起動したときとは逆に、WSL Ubuntu から Windows の sshd へアクセスするには、次の設定が必要になります。

  • Windows OS のファイアウォールで WSL Ubuntu から Windows の 22 番ポートへアクセスを許可
  • WSL Ubuntu で Windows OS の IP アドレスの名前解決

ファイアウォールの設定については、New-NetFirewallRule コマンドを使って対応します。管理者として実行する Git Bash のターミナルで次のコマンドを実行することで、ファイアウォールのルール WSL2 SSH が追加されて、WSL Ubuntu から Windows の 22 番ポートへアクセスできるようになります。

PowerShell -Command 'New-NetFirewallRule -Name "WSL2 SSH" -DisplayName "WSL2 SSH" -LocalPort 22 -Protocol TCP -Action Allow -Direction Inbound'

なお、ファイアウォールのルール WSL2 SSH を無効化するには、Remove-NetFirewallRule コマンドを使ってルールを削除します。管理者として実行する Git Bash のターミナルで次のコマンドを実行すれば、ルールを削除できます。

PowerShell -Command 'Remove-NetFirewallRule -Name "WSL2 SSH"'

次に、WSL Ubuntu で Windows OS の IP アドレスの名前解決をする方法については、いくつか方法があります。ssh コマンド関連だけ対応できれば良いので、ここでは ~/.ssh/config を使う方法を紹介します。

WSL Ubuntu から Windows OS へアクセスするための IP アドレスは、/etc/resolv.conf の nameserver の行を見るとわかります。ホスト名は host.wsl.internal など自分で決めて良いのですが、ここでは hostname コマンドで表示できる WSL Ubuntu のホスト名を使うことにします。

そこで、ここでは WSL Ubuntu で次のような簡単なスクリプト gen_wsl_ssh_config.sh を用意します。

gen_wsl_ssh_config.sh
ip=$(cat /etc/resolv.conf | grep nameserver | awk '{print $2}')
hostname=$(hostname)

cat << EOS | tee
Host ${hostname}
  HostName ${ip}
EOS

これを実行すると、~/.ssh/config へエントリーするときの Host の設定テキストを出力します。実行例は次のようになります。

user001@Win11Eval:~$ sh gen_wsl_ssh_config.sh
Host Win11Eval
  HostName 172.22.192.1

この内容を次のように ~/.ssh/config へ追加します。

sh gen_wsl_ssh_config.sh >> ~/.ssh/config

WSL Ubuntu の IP アドレスが変わると、/etc/resolv.conf の nameserver の行も変わるので、~/.ssh/config へエントリーした Host の設定テキストを削除してから、このコマンドを再実行します。

これで ssh 関連のコマンドを実行するときに Host の設定テキストで指定した値(ここでは Win11Eval)をホスト名に指定すると、HostName に指定した IP アドレス(ここでは 172.22.192.1)でアクセスするようになります。

秘密鍵は WSL Ubuntu のユーザー用に新しく用意しても良いですし、Windows のファイルシステムにあるものを使うこともできます。その場合は、/mnt/c/Users/<ユーザー名>/.ssh に秘密鍵があるので、それを -i オプションで指定します。

手元では WSL Ubuntu のターミナルで次のようにコマンドを実行して、WSL Ubuntu から Windows 版の sshd へ SSH ログインできることを確認しました。

ssh -i /mnt/c/Users/User/.ssh/id_ed25519_openssh_eval User@Win11Eval

この結果、Windows のコマンドプロンプトの画面になれば成功です。確認ができたら exit コマンドでログアウトします。

WSL Ubuntu から Windows 版 sshd へのアクセスについて、動作確認が済んだら、ファイアウォールの WSL2 SSH ルールを削除します。

PowerShell -Command 'Remove-NetFirewallRule -Name "WSL2 SSH"'

Windows 版 sshd サービスの停止

Windows 版 sshd サービス全体の動作確認が済んだら、Windows 版 sshd サービスを Stop-Service コマンドを使って停止します。また、自動起動も Disabled を指定して無効化します。

PowerShell -Command "Stop-Service sshd"
PowerShell -Command "Set-Service -Name sshd -StartupType 'Disabled'"

なお、ファイアウォールにも影響が出ています。ファイアウォールの設定を確認して、OpenSSH Server について「ファイアウォールによるアプリケーションの許可」を無効化します。

再度、Windows 版 sshd サービスを有効にする場合は、Set-Service コマンドを使って手動起動(Manual)か自動起動(Automatic)にして無効(Disabled)の状態から変更します。それから、Start-Service コマンドを使って sshd サービスを開始します。

PowerShell -Command "Set-Service -Name sshd -StartupType 'Manual'"
PowerShell -Command "Start-Service sshd"

おわりに

以上で Windows で OpenSSH サーバーを試す環境を用意する方法について説明しました。Windows では、WSL、Git Bash を使っていると、OpenSSH クライアントが 3 種類も使える環境になってしまい、混乱しやすくなります。自分がどれを使っているのか、きちんと把握できるようにしましょう。

また、何かトラブルが起きたときや、OpenSSH 関連の設定で試してみたいことが出てきたときは、ここで説明した方法で Windows マシンで OpenSSH サーバーを試す環境を用意することができます。自分が管理しているマシンで、こういったことができるようになると、開発環境構築のスキルもぐっとアップしやすくなります。ぜひ、できるようになってください。

Discussion