本記事は下記記事のWindows対応版です。
あるホストにSSHで接続する際に、LAN内からかインターネットからかで使用するIPやポート番号などが異なるのはよくあることかと思う。Linuxでのやり方は過去に上げたが、Windowsにはないコマンドを利用していたため、Windowsで使えなかった。判定時に外部アクセスしない点を引き継ぎ、PowerShellの Get-NetRoute コマンドレットを活用する方式を使っている。
なお、ホスト名は example アドレスは 192.168.0.5/24 であり、それが外部からだと 203.0.113.10 で接続できるものとする。
Match host example exec "powershell -NoProfile -Command if((Get-NetRoute -DestinationPrefix 192.168.0.0/24 -NextHop 0.0.0.0 -ErrorAction SilentlyContinue) -eq $null) {exit 1}"
Hostname 192.168.0.5
Host example
Hostname 203.0.113.10
Linux版と比べるとかなりトリッキーになっている。まず、核となるネットワーク判定は Get-NetRoute -DestinationPrefix 192.168.0.0/24 -NextHop 0.0.0.0 により、ローカルサブネットで検索している。ここを工夫すれば様々な複雑な条件に変更可能だ。問題は ssh.exe にその結果をどう伝えるかだ。 ssh.exe がこの設定ファイルの exec で呼び出すのは、 cmd.exe であるため、PowerShellコマンドを実行するには powershell.exe を実行する必要がある。また、 exec は実行したコマンドの終了コードで分岐するので、PowerShellがいい感じの終了コードで終わってくれる必要がある。
こちらのドキュメントを見ると、最後に実行したコマンドの終了コードが返されるとあるのだが、ここでややこしいのが、PowerShellにおけるエラーの扱いだ。PowerShellには例外を発し、スクリプトを停止させるエラーや、エラー出力を表示はするが続行するエラー等、少々変わったエラーがあり、コマンドレットのそれらの挙動を変える ErrorAction というオプションがある。
色々作戦は考えられるが、今回は他のコマンドレットを組合せた拡張などを見据え、 SilentlyContinue として、該当ネットワークがないときに単純に $null を返してそれを判定するというアプローチをとった。
このSSHの設定ファイルの exec に関しては、エスケープの構文が無かったりと、そもそもの仕様面でかなり頭が痛いため、外部プログラムを用意したくもなるが、そうなると今度はパスをどうするか問題が勃発し(execのカレントディレクトリはsshを実行した場所)、設定ファイルの可搬性問題に波及する。 localnetwork オプションのサポートがもうちょいまともなら…