Windows10標準のOpenSSHクライアントを試してみる ＆ 日本語フォルダに注意

最近会社で見かけたこと、驚いたことをまとめてみました。

Windows10標準のOpenSSHクライアントについての話です。

# はじめに

今どきのWindows10には"OpenSSHサーバー"や"OpenSSHクライアント"がついています。

"OpenSSHクライアント"は標準で入っていると思います。クライアント、サーバー、どちらも"設定"＞"アプリ"＞"オプション機能の管理"で入っているか確認できます。入っていないなら、"機能の追加"から入れることができます。

# きっかけ

会社で「サーバー上でコマンド実行するだけなら、オプション機能の追加で"OpenSSHサーバー"を入れればできる。リモートデスクトップでの作業競合を避けることもできる」と聞きました。そのとき、"OpenSSHクライアント"も用意されていることを知りました。

最初、入っているのは"ssh.exe"だけだと思っていました。"ssh"からのキーワード補完で"ssh-keygen.exe"などが候補に出てきました。そこで"ssh.exe"以外もインストールされていることを知りました。

# インストール場所

"ssh.exe"のインストール場所を確認してみます。

まず、お手軽SFTPサーバー[Rebex Tiny SFTP Server](https://labs.rebex.net/tiny-sftp-server)を起動して接続します。サーバー鍵の受け入れ確認やパスワード入力画面が表示されたら、そのままの状態でタスクマネージャーを起動します。タスクマネージャー上で"ssh.exe"を探してプロパティ表示します。"全般タブ"の"場所"を見ると"ssh.exe"が格納されているフォルダが表示されます。

![ssh.exeのプロパティ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/381550/7c57a3d4-ee71-767a-25fc-aad66e71589f.png)

インストール先は"%SYSTEMROOT%\System32\OpenSSH\"でした。他にどのようなコマンドがインストールされているでしょうか。

```
C:\Windows\System32\OpenSSH>Get-ChildItem                                                                      
                                                                                      
                                                                                      
    ディレクトリ: C:\Windows\System32\OpenSSH                                               
                                                                                      
                                                                                      
Mode                LastWriteTime         Length Name                                 
----                -------------         ------ ----                                 
-a----       2018/09/15     18:09         322560 scp.exe                              
-a----       2018/09/15     18:09         390144 sftp.exe                             
-a----       2018/09/15     18:09         491520 ssh-add.exe                          
-a----       2018/09/15     18:09         384512 ssh-agent.exe                        
-a----       2018/09/15     18:09         637952 ssh-keygen.exe                       
-a----       2018/09/15     18:09         530432 ssh-keyscan.exe                      
-a----       2018/09/15     18:09         882688 ssh.exe                              
                                                                                      
                                                                                      
C:\Windows\System32\OpenSSH>
```

ちなみに"OpenSSHサーバー"をインストールすると同じフォルダにインストールされます。サービスマネージャーで開始や停止を行えるようになります。Windows Subsystem for Linux(WSL)でOpenSSHサーバーを動かすよりもWindowsらしいアプリ管理を行えます。

```
C:\Windows\System32\OpenSSH>Get-ChildItem


    ディレクトリ: C:\Windows\System32\OpenSSH


Mode                LastWriteTime         Length Name
----                -------------         ------ ----
-a----       2018/09/05     15:07         322048 sftp-server.exe
-a----       2018/09/05     15:07         149504 ssh-shellhost.exe
-a----       2019/04/18     23:59         974848 sshd.exe
-a----       2018/09/05     15:07           2253 sshd_config_default


C:\Windows\System32\OpenSSH>
```

バージョンを確認してみます。"OpenSSH 7.7p1 + [LibreSSL](https://www.libressl.org/) 2.6.5"です。およそ1年前のバージョンです。つい先日の8.0に追いついていないのは仕方がないでしょう。しかし昨年10月リリースの7.9にも追いついていません。それほど頻繁に追従するつもりはないのかもしれません。

```
C:\Windows\System32\OpenSSH>ssh -V
OpenSSH_for_Windows_7.7p1, LibreSSL 2.6.5

C:\Windows\System32\OpenSSH>
```

# ssh.exe

sshクライアントです。標準でCUIクライアントが用意されていれば、自動タスクに使うことも簡単にできそうです。

"-F/-i/-l"オプションも使えます。そのため接続に必要な情報を単一フォルダに閉じ込める"ポータブル"に近い使い方ができます。

- -F : 設定ファイルを指定します。これを指定した場合は"ssh_config"や"~/.ssh/config"は使われません。設定ファイルを一切使用しないために"-F NUL"を指定することができます。
- -i : 公開鍵認証に使用する秘密鍵を指定します。
- -l : ユーザー名を指定します。
 - "-l"の代わりに"user@host"形式を使用できます。 
- -p : ポート番号を指定します。

但し"known_hosts"は"%USERPROFILE%/.ssh"を見に行くようです。完全にポータブルとは言えないようです。

なお7.7ならssh_configにIncludeを使うこともできます。

# ssh-keygen.exe

前に会社で「SSHの鍵認証がうまくいかない」と聞かれたことがあります。そのときの原因は鍵の作り間違い、鍵の置き間違いでした。

LinuxサーバーにWindowsから鍵認証接続するというシチュエーションでした。Windowsで使用する秘密鍵(SSHクライアント用のファイル)を、接続先のLinux(SSHサーバー)のssh-keygenで作っていました。そのため、どのファイルがサーバー用かクライアント用か混乱していました。

Windowsでssh-keygen.exeを実行すれば、勘違いは避けられただろうと思います。使う端末上で秘密鍵を作るという、本来の在り方(*補足)という意味でも、Windowsでssh-keygen.exeを使う意味は大きいです。

LinuxはputtygenがCUIとなっています。そのため、鍵生成からPPKに変換するところまでをコマンドラインだけで行うことができます。Windowsのputtygen.exeはGUIとしてしか動作できないようです。そのため、コマンドラインだけで完結できなそうです。

## *補足

下に書いた使い方1,2,3のうち、下に行くほど秘密鍵の扱いに気を付けないといけない要素が増えます。そのため、下に行くほどより危険でしょうという意味です。

秘密鍵をアプリケーションに閉じ込めることができるかは、使うアプリケーションによります。なので1を必ず選ぶことができるわけではありません。そして、1を選べないならせめて2を、という意味です。

1. 秘密鍵をアプリケーションに閉じ込めて使用する。
 - 使う端末上で使うアプリケーションを使用して鍵ペアを生成する。
 - 秘密鍵をアプリケーションから取り出さない。
 - 公開鍵のみを取り出して、接続先サーバーに配置するなどして使用する。
2. 秘密鍵を使う端末に閉じ込めて使用する。
 - 使う端末上でssh-keygenなどの鍵生成アプリケーションを使用して鍵ペアを生成する。
 - 秘密鍵をその端末外に持ち出さない。
 - 公開鍵のみを取り出して使用する。
3. 秘密鍵を使う端末の外で扱うタイミングが存在する。
 - 使う端末以外でssh-keygenなどの鍵生成アプリケーションを使用して鍵ペアを生成する。
 - 秘密鍵を安全な経路であることを確認しながら使う端末に移動する。
 - 公開鍵も移動して使用する。

# その他のコマンド

その他のコマンドも普通に使うことができます。普通過ぎるので以下省略。

# 日本語フォルダに注意

SSHクライアントに含まれる各プログラムは、日本語フォルダへの対応が適切にできていないようです。少し実験してみます。

## 実験環境

実験環境にはRebex Tiny SFTP Serverを使用します。SFTPサーバーの制約上、ED25519は使えません。そのためRSA-2048を用いています。

以下の手順で行う接続確認では、共通した鍵を使用しています。事前に作成しておきます。

```
D:\Tmp\ssh-test>ssh-keygen.exe -q -t rsa -f id_rsa -C "" -N ""

D:\Tmp\ssh-test>
```

これをSFTPサーバーのauthorized_keysにコピーします。そしてSFTPサーバーを起動します。

## id_rsaの読み込み

まず、普通に接続してみます。

```
D:\Tmp\ssh-test>echo ls -l | sftp.exe -i id_rsa test@localhost
Connected to test@localhost.
sftp> ls -l
-rw------- 1 test users          0 Apr 20 08:07 11111.txt
-rw------- 1 test users          0 Apr 20 08:07 22222.txt
-rw------- 1 test users          0 Apr 20 08:07 33333.txt
-rw------- 1 test users          0 Apr 20 08:07 44444.txt
-rw------- 1 test users          0 Apr 20 08:07 55555.txt
-rw------- 1 test users        111 Apr 20 08:07 make-files.cmd

D:\Tmp\ssh-test>
```

接続してファイルの一覧を取得できました。id_rsaのフルパスに日本語が含まれている状態だと、以下のようになります。

```
D:\Tmp\ssh-test>mkdir 日本語

D:\Tmp\ssh-test>mkdir 日本語\test

D:\Tmp\ssh-test>copy id_rsa* 日本語\test
id_rsa
id_rsa.pub
        2 個のファイルをコピーしました。

D:\Tmp\ssh-test>cd 日本語\test

D:\Tmp\ssh-test\日本語\test>echo ls -l | sftp.exe -i id_rsa test@localhost
Warning: Identity file id_rsa not accessible: No such file or directory.
Password:
   : (略)
test@localhost: Permission denied (password,keyboard-interactive,publickey).

D:\Tmp\ssh-test\日本語\test>
```

"id_rsa"にアクセスできないというメッセージが表示されています。"id_rsa"の指定が日本語含みのフルパスならあり得る話だと思います。相対パスでアクセスできないと言われるのはどうしてでしょうか。

id_rsaのフルパスに日本語が含まれなければ、カレントフォルダのフルパスに日本語が含まれていても大丈夫です。

```
D:\Tmp\ssh-test\日本語\test>echo ls -l | sftp.exe -i D:\Tmp\ssh-test\id_rsa test@localhost
Connected to test@localhost.
sftp> ls -l
-rw------- 1 test users          0 Apr 20 08:07 11111.txt
-rw------- 1 test users          0 Apr 20 08:07 22222.txt
-rw------- 1 test users          0 Apr 20 08:07 33333.txt
-rw------- 1 test users          0 Apr 20 08:07 44444.txt
-rw------- 1 test users          0 Apr 20 08:07 55555.txt
-rw------- 1 test users        111 Apr 20 08:07 make-files.cmd

D:\Tmp\ssh-test\日本語\test>
```

## 受信したファイルの書き込み

カレントフォルダのフルパスに日本語が含まれている場合、カレントフォルダにファイルをgetできなくなります。

```
D:\Tmp\ssh-test\日本語\test>echo get 55555.txt | sftp.exe -i D:\Tmp\ssh-test\id_rsa test@localhost
Connected to test@localhost.
sftp> get 55555.txt
Fetching /55555.txt to 55555.txt
Couldn't open local file "55555.txt" for writing: No such file or directory

D:\Tmp\ssh-test\日本語\test>
```

ローカルファイル"55555.txt"を作れないようです。

## ファイルの送信

送信も同じ状況になることは容易に予想できます。

```
D:\Tmp\ssh-test\日本語\test>echo put id_rsa.pub | sftp.exe -i D:\Tmp\ssh-test\id_rsa test@localhost
Connected to test@localhost.
sftp> put id_rsa.pub
stat id_rsa.pub: No such file or directory

D:\Tmp\ssh-test\日本語\test>
```

## ssh_configの読み込み

"-F"でssh_configを指定できます。ここにも同じ状況が発生します。

```
D:\Tmp\ssh-test\日本語\test>echo ls -l | sftp.exe -i D:\Tmp\ssh-test\id_rsa -F ssh_config test@localhost
Can't open user config file ssh_config: No such file or directory
Connection closed

D:\Tmp\ssh-test\日本語\test>
```

## カレントパスの認識

一応ローカルのカレントフォルダのパスを正しく認識しているようです。ファイルの読み書きだけが影響を受けているようです。

```
D:\Tmp\ssh-test\日本語\test>echo !cd | sftp.exe -i D:\Tmp\ssh-test\id_rsa test@localhost
Connected to test@localhost.
sftp> !cd
D:\Tmp\ssh-test\日本語\test

D:\Tmp\ssh-test\日本語\test>
```

# 他のプロダクトに含まれているOpenSSH

他のプロダクトに含まれているOpenSSHだとどうなるでしょうか。同じ端末内にある複数のOpenSSHで試してみました。

「ssh.exeが日本語を含むパスに置かれたid_rsaを読むことができるか」に限定して確認しています。

| 種類                        | ssh -V                                      | 日本語 |
|:----------------------------|:--------------------------------------------|:-------|
| Windows標準                 | OpenSSH_for_Windows_7.7p1, LibreSSL 2.6.5   | NG     |
| Git 2.21.0 付属             | OpenSSH_7.9p1, OpenSSL 1.1.1a  20 Nov 2018  | OK     |
| Docker Toolbox 18.3 付属    | OpenSSH_6.6.1p1, OpenSSL 1.0.1m 19 Mar 2015 | OK     |
| Cmder(Full) 1.3.11.843 付属 | OpenSSH_7.7p1, OpenSSL 1.0.2p  14 Aug 2018  | OK     |

この感じだとLibreSSLを使っているくらいしか特徴はなさそうです。とはいえLibreSSLが登場してから5年近く経っています。いまさら感はあります。

それとLibreSSLは思ったほど使われていない？と思いました。