これまで TeraTerm でリモートサーバーに SSH ログインしていたのですが、最近は Windows Terminal を使うことも増えてきました。
でも一つ面倒なのは、パスワードを毎回手入力しないといけない、ということなんですよね。
TeraTerm はとても歴史的なツールで、これまで Windows 環境でリモートサーバーに接続するのには必須のツールでした。いや、過去形では失礼かもしれませんね。未だに必須のツールの一つとなっています。
しかし、Windows では WSL(WSL2) が Windows 上で使う Linux 環境のデファクトスタンダードとなってきた今、Windows Terminal というアプリケーションを使用して、これまでよりずっと簡単にリモートサーバーに接続できるようになりました。
しかし、TeraTerm とは色々使い勝手が違います。
パスワード入力の不要な Tera Term
外部のリモートサーバーに接続する場合は、SSH でセキュアな接続を行うと思うんですが、その際に最もよく使われるのが Tera Term だと思います。これさえあれば、
- SSH 接続
- TELNET 接続
- シリアル接続
など、リモートやローカルのサーバーから、CISCO のスイッチまでいろいろな端末に接続し、使用することができるようになります。
あと、Tera Term のウィンドウにファイルをドラッグアンドドロップする事で、ファイル転送も出来るので、リモートサーバーにファイルを転送するためだけに、FTP (しかもセキュアな) のセットアップをしたり、Win SCP をインストールしたりする必要もない優れものなんですよね。
また、Tera Term はマクロ機能もあって、SSH 接続のポートの指定、パスワードの入力までのコマンドをマクロで作成して Tera Term と紐付けるだけで、ショートカットダブルクリックで簡単にリモート接続させてくれる環境が出来上がります。
例えば、次のようなコマンドをメモ帳等で作成して、sshlogin.ttl
等のように拡張子が .ttl
のファイル名で保存します。
connect 'host.domain.tld:PORT /ssh /2 /auth=publickey /user=username /passwd=p@s5w0rd /keyfile=D:\path\to\key\id_rsa'
そしてこのファイルを、Tera Term Macro Interpreter で紐付けて、そのショートカットをデスクトップ等に貼りつけるだけです。
また、端末の背景色などをカスタマイズして、その設定を hoge.ini
ファイル等に保存し、先程のマクロで /f=hoge.ini
1 と指定することで、保存した設定で端末を立ち上げることもできます2。
connect 'host.domain.tld:PORT /ssh /2 /auth=publickey /user=username /passwd=p@s5w0rd /keyfile=D:\path\to\key\id_rsa /F=develop.ini'
Windows Terminal の登場
Windows 10 になって、Windows でも SSH2 が利用できるようになり、また、Microsoft から Windows Terminal が提供されるようになると、これまで Tera Term (他にも PuTTY などもありましたね) 一辺倒だったリモート接続界隈にも少し変化がでてきました。
私自身もご多分に漏れず、Windows Terminal にプロファイルを追加して、Windows Terminal からリモート接続する機会が増えてきたのです。
例えばこんな感じに、プロファイルを追加します 3 。
{
:
:
"profiles": {
:
"list": {
:
{
"guid": "{b17ec64e-079f-42d3-a7b8-b044eaa33c3e}",
"name": "host.domain.tld",
"hidden": false,
"tabTitle": "somehost",
"commandline": "ssh username@host.domainname.tld -p 12345 -i Z:\\Documents\\ssh\\id_rsa",
"fontFace": "Migu 1M",
"fontSize": 11,
"icon": "Z:\\lib\\icon\\Martz90-Circle-Ubuntu.ico"
},
}
}
}
この例を見ていただくとわかるように、commandline
プロパティに実行するコマンドをそのまま書いている形です。
このプロファイルを追加して、settings.json
を保存すると、Windows Terminal のプルダウンに項目が追加されるので、それを実行することで接続することが出来ます。
ただし、ひとつ言い忘れました。
パスワード入力を除いて…
少し面倒くさいのは、SSH 秘密鍵 にパスフレーズを設定していると、毎度接続する度、次のようなパスワード入力を促すプロンプトが表示されでしまうのです。
Enter passphrase for key 'Z:\Documents\ssh\id_rsa':
Windows Terminal の利点は、一つのウィンドウで複数のターミナルをマネジメントできるのがいいのですが、なんとかしていちいちパスワード入力しないで済むようにしたいものですね。
Windows Terminal から一発接続の方法
ここで、こんな声が聞こえてきそうです。
SSH 秘密鍵作成時のパスフレーズを「空」にすればよいのでは?
と。
ローカルに置いているクローズドなサーバーならともかく、外に向いているサーバーは、やはりパスフレーズを入れておきたいんで、流石にそういうわけにもいきません4。
そんな中、Twitter で VBScript を使って接続する方法を教示してくれたユーザーがいました。
多分vbsでできたのですが、
これであってますかね?https://t.co/qEyfhbBv5X— いなみ (@takataki_inami) February 8, 2021
なるほど、VBScript とはいかにも Windows らしい解決方法だなと思いました。それに、余計なアプリケーションをインストールすることなしに実行できます。
実際、私自身の悩みはこれで解決できます。
— しかしながら、試してみたい事がありました。
それは、WSL の Bash を呼び出して、expect
コマンドで SSH を投げることは出来ないかと・・。
それができれば、Windows Terminal でパスワードなしでリモートサーバーに接続できるかも知れない。
WSL を使って expect コマンドを実行する
ここからの部分は、以下の環境で作業をしています。
- Windows10 Pro バージョン 20H2
- WSL2 を有効にし、Ubuntu 20.04 を導入済み
- Ubuntu には
expect
コマンドをインストール済み5 - Windows 環境だけではなく、WSL 環境もリモートホストに対して公開キーを登録している
まず、Windows Terminal (Powershell あるいは CMD でも可) を起動してください。
上記環境であれば、次のコマンドで Bash を起動します。
PS C:\Users\someuser> bash
someuser@myhost:/mnt/c/Users/someuser$
もしも、プロンプトの表示が、上記のように /mnt/c/Users/...$
と表示されている場合は、cd
と入力しエンターを押して、ホームディレクトリ (~$
) に移動します6。
someuser@mylocalhost:/mnt/c/Users/someuser$ cd
someuser@mylocalhost:~$
そして、次のようなスクリプトファイルを作成します。
vi sshlogin.expect
ここでは、エディタに vi を使っていますが、お好みのエディタでOKです。
入力する内容は次のようになります。
#!/usr/bin/expect
if { [llength $argv] < 4 } {
puts "usage: sshlogin.expect host port user pass"
exit 1
}
set timeout 10
set host [lindex $argv 0]
set port [lindex $argv 1]
set user [lindex $argv 2]
set pass [lindex $argv 3]
spawn ssh -p $port $user@$host
expect {
timeout {puts "timed out"; exit 1}
"*yes/no*" {send "yes\r";exp_continue}
"Permission denied*" {puts "Permission denied."; exit 1}
"Enter pass*" {send "$pass\r"}
}
interact
ファイルを保存し、実行できるようにモードを変更します。
chmod +x sshlogin.expect
このコマンドの使用法は次のようになります。
sshlogin.expect リモートホスト ポート ユーザー パスワード
では、実際に正常に動作するのか確認してみます。
./sshlogin.expect myhost.domain.tld 12345 someuser p@s5w0rd
次のようにリモートホストのプロンプトが返ってくれば成功です。
someuser@myhost:~$
expect は何をやっているのか
これは、expect と呼ばれるコマンドを使って、ユーザーがシェルと対話形式で実行している内容を、スクリプトに対話させて実行させています。以下に簡単に説明していますが、詳しくは tcl の文法を参照されるといいかと思います。
まず、一番先頭の行ですが、このスクリプトを実行するプログラムを指定しています。
そして次に、引数のチェックをしています。引数が4つなければ、コマンドを使用法を表示して、exit します。
#!/usr/bin/expect
if { [llength $argv] < 4 } {
puts "usage: sshlogin.expect host port user pass"
exit 1
}
set timeout 10
では、タイムアウト時間を10秒に設定しています。
その後の4行は、引数を変数に割り当てています。
set timeout 10
set host [lindex $argv 0]
set port [lindex $argv 1]
set user [lindex $argv 2]
set pass [lindex $argv 3]
ここからが、expect の真骨頂です。
まず、spawn で SSH に、先程割り当てた変数を渡して起動します。
spawn ssh -p $port $user@$host
次に、expect
から始まるブロックでは、相手 (ここでは SSH) からのメッセージを待ち、それに応じてコマンドを実行していくようになっています。
expect {
timeout {puts "timed out"; exit 1}
"*yes/no*" {send "yes\r";exp_continue}
"Permission denied*" {puts "Permission denied."; exit 1}
"Enter pass*" {send "$pass\r"}
}
interact
timeout {puts "timed out"; exit 1}
では、この expect ブロック中のコマンド処理の中で応答がない場合、”timed out" を表示させてプログラムを終了させます。先にセットしている timeout
の時間だけ応答を待ちます。
"*yes/no*" {send "yes\r";exp_continue}
は。初めてホストにリモート接続した際に表示される、
The authenticity of host '[myhost.mydomain.tld]:12345 ([xxx.xxx.xxx.xxx]:12345)' can't be established.
ECDSA key fingerprint is SHA256:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.
Are you sure you want to continue connecting (yes/no/[fingerprint])?
の応答があった場合に実行されるもので、最後の文中に含まれる yes/no
に反応するようにしています。前後のアスタリスク *
は、ワイルドカードで、この場合は、「yes/no
」 を含む、という意味になります。
次の、"Permission denied*" {puts "Permission denied."; exit 1}
の部分は、SSHキーの認証が失敗した場合のメッセージをキャッチするようにしています。もちろん、このメッセージをキャッチしたら、プログラムを終了させます。
このブロック最後のコマンド、"Enter pass*" {send "$pass\r"}
は、SSHキーのパスフレーズを確認する際のメッセージをキャッチします。
Enter passphrase for key 'Z:\Documents\ssh\id_rsa':
このメッセージをキャッチしたら、パスワードと改行を SSH プロセスに send
します。
そして、ここまでのブロックでメッセージを何もキャッチできなかった場合は、timeout でプログラムを終了します。
最後の interact
で、対話モードに入ります。もしここで exit
とすると、expect のプロセスが終了し、リモート接続も終了してしまうので注意してください。
もし、上記のスクリプトが正常に動作しない場合は、ここまでの内容をヒントにして、環境に応じてスクリプトを変更すると良いと思います。例えば、SSH との接続に PAM を利用している場合は、メッセージが異なってくると思います。
Windows Terminal の command から実行してみる
では、最後に、Windows Terminal のプロファイルにこのスクリプトを登録して、実行できるようにしてみたいと思います。
先程のプロファイルの commandline
の値を書換えてみます。
{
:
:
"profiles": {
:
"list": {
:
{
"guid": "{b17ec64e-079f-42d3-a7b8-b044eaa33c3e}",
"name": "host.domainname.tld",
"hidden": false,
"tabTitle": "somehost",
"commandline": "bash -c \"/home/someuser/sshlogin.expect myhost.domain.tld 12345 someuser p@s5w0rd\"",
"fontFace": "Migu 1M",
"fontSize": 11,
"icon": "Z:\\lib\\icon\\Martz90-Circle-Ubuntu.ico"
},
}
}
}
ポイントは、ダブルクォーテーションをエスケープする必要があることですね。
これで settings.json
を保存してプルダウンから選択してみると、無事パスワード入力をせずにリモートログインできました。
まとめ
先のツイートをしたときに、expect みたいなツールが Windows で使えれば・・と思っていたのですが、WSL を通さないといけないのか、という部分が引っかかっていました。
結局の所、WSL は Windows 上でネイティブに動いているとは直感的に思えないからです。言ってみれば、Windows7 の時代に、XP モードを使っている時に感じていたあのモヤモヤ感です。
シームレスなようでシームレスではない。そんな感じ。
いずれにしても、割に簡単にできたので、とりあえず当面は Windows Terminal を使ってみたいと思います。
-
特にパスを指定しない場合は、Tera Term の実行ファイルがあるディレクトリ上の設定ファイルを読み込みにいきます。 ↩
-
多くのリモートサーバーの端末を開いて、ご操作しないよう本番用のサーバーかテスト用のサーバーか見分けるためには、背景の色を変えるのが一番良い事故防止策だと思います。 ↩
-
設定ファイルは、
%USERPROFILE%\Appdata\Local\Packages\Microsoft.WindowsTerminal_8wekyb3d8bbwe\LocalState\settings.json
にあります。 ↩ -
そもそもローカルネットワーク上のサーバーであれば、公開鍵認証ではなくして、PAM 使ってでログインするかな? ↩
-
sudo apt install expect
で、インストール可能です。 ↩ -
ホームディレクトリに移動しなくてもよいのですが、スクリプトを作成するディレクトリパスを覚えておく必要があります。 ↩
0件のコメント