とある装置で定期的にコマンドを実行したいが、sshでログインする際にパスワードが求められ、コマンド実行も手入力しか受け付けない。
そもそもWebインターフェースがあるのに、コマンドが常に いいですか?→はい と答えるまで実行できない、なんていわれてもにわかには信じがたい。でも、実際そうなんですと言われて、はぁそうですかと。どうにかしてこれを定期実行させたいのだが…
環境
とりあえず、以下で試してみる。
- temp : Ubuntu 18.04 : 定期的にworkに接続し、コマンドを実行を依頼する。
- work : Ubuntu 20.04 : tempからのSSH接続を受け付けて、実際のコマンドを実行する。
どちらもSSHサーバーが動いていて、パスワード接続できるテスト環境。
tempからworkにssh接続をするときは、こんな感じ。
$ ssh -l rohhie work
rohhie@work's password: <パスワードを入力>[Enter]
elcome to Ubuntu 20.04.2 LTS (GNU/Linux 5.4.0-73-generic x86_64)
* Documentation: https://help.ubuntu.com
…
実際にどうなるのか。
ログイン
リモートサーバーにSSH接続するとき、sshコマンドで自動ログインができないなんてことが本当にあるのかよく分からないけれども、できないと言われたので、sshpassを使ってログインできるようにしよう。
Qiita / sshpass で秘密鍵のパスフレーズを自動入力する
$ sudo apt install sshpass
コマンドラインパラメーターで接続してみる。
$ sshpass -p 'password' -P "rohhie@work's password:" ssh -l rohhie work
Welcome to Ubuntu 20.04.2 LTS (GNU/Linux 5.4.0-73-generic x86_64)
* Documentation: https://help.ubuntu.com
…
ログインできた。
-Pで指定するのは、サーバーに接続し、パスワードを求められたときに表示される文字列。接続先が何を返してくるのかを確認し、それをここに入れてしまえばいい。
コマンド実行
リモート接続先のコマンドを用意
workの側にテスト用のコマンドを作っておく。
work:/home/rohhie/expect_test
#!/bin/bash echo -n "Are you sure (yes/no)? " read str echo ${str}
このコマンドに実行権を付けておく。
リモート接続先のコマンドを実行
リモート接続先のコマンドを実行するには、sshコマンドの後ろに実行するコマンドをくっつければ良かった。
$ sshpass -p 'password' -P "rohhie@work's password:" ssh -l rohhie work /home/rohhie/expect_test Are you sure (yes/no)? yes yes
※もちろんコマンドにパラメーターを渡すこともできる。
といった具合で実行する。
定期的にリモート接続してコマンドを実行
本題。コマンド実行時に、コンソールに出力された質問に自動で答えたい。
MY ROBOTICS / expectで対話式のコマンドを自動化する
expectが使えるらしいのでインストールする。
$ sudo apt install expect
こんなスクリプトを書いて実行してみた。
#!/bin/bash command="sshpass -p \"password\" -P \"rohhie@work's password:\" ssh -l rohhie work /home/rohhie/expect_test" expect <<EOF set timeout 10 spawn ${command} expect "Are you sure (yes/no)?" send yes\r expect "$" EOF
※send yes の後に、 expect "$" を書いて、コマンド終了で $ が表示されるのを待てるとのこと。
実行してみると、こんな結果になる。
$ ./sample.sh spawn sshpass -p password -P rohhie@work's password: ssh -l rohhie work /home/rohhie/expect_test Are you sure (yes/no)? yes yes
※sshpassのパラメーターはダブルクォーテーションで囲んでいるが、表示上は出てこない。出てこないけど上手く動く。
このスクリプトをcronで定期的に起動すれば良さそう。
なお、タイムアウトはexpectで何かが表示されるのを待つ時間だった。workに設置したコマンドにsleepを入れて確かめてみるのも良いかと思う。
定期的にリモート接続してコマンドを実行(sudo)
とりあえずlsしてみる。
$ sshpass -p 'password' -P "rohhie@work's password:" ssh -l rohhie work ls -la
ここに sudo をくっつけて実行してみる。
$ sshpass -p 'password' -P "rohhie@work's password:" ssh -l rohhie work sudo ls -la
sudo: a terminal is required to read the password; either use the -S option to read from standard input or configure an askpass helper
きっと、中ではこんなことを聞かれているだろう。
[sudo] password for rohhie:
まずはexpectを利用したスクリプトを書いた。
#!/bin/bash command="sshpass -p \"password\" -P \"rohhie@work's password:\" ssh -l rohhie work sudo ls -la" expect <<EOF set timeout 10 spawn ${command} expect "\[sudo\] password for rohhie:" send password\r expect "$" EOF
実行結果はこれ。
$ ./sample2.sh spawn sshpass -p password -P rohhie@work's password: ssh -l rohhie work sudo ls -la sudo: a terminal is required to read the password; either use the -S option to read from standard input or configure an askpass helper send: spawn id exp4 not open while executing "send password\r"
コマンドラインで実行したものが再現できた。
発生しているエラーを解消するため、エラーメッセージに言われるままに sudo に -S パラメーターを付けてみた。
#!/bin/bash
command="sshpass -p \"password\" -P \"rohhie@work's password:\" ssh -l rohhie work sudo -S ls -la"
expect <<EOF
set timeout 10
spawn ${command}
expect "\[sudo\] password for rohhie:"
send password\r
expect "$"
EOF
実行してみると…
$ ./sample2.sh
spawn sshpass -p password -P rohhie@work's password: ssh -l rohhie work sudo -S ls -la
[sudo] password for rohhie: password ← 10秒後に入力される、表示もされる。
total 268
drwxr-xr-x 8 rohhie rohhie 4096 Jul 14 00:18 .
drwxr-xr-x 3 root root 4096 Jun 6 2020 ..
-rw------- 1 rohhie rohhie 13245 Jul 13 21:47 .bash_history
…
となった。
パスワードの入力待ちは最低限にするけれども、コマンド実行が終わるまで待ちたい場合は、
#!/bin/bash
command="sshpass -p \"password\" -P \"rohhie@work's password:\" ssh -l rohhie work sudo -S ls -la"
expect <<EOF
set timeout 1
spawn ${command}
expect "\[sudo\] password for rohhie:"
send password\r
set timeout 10
expect "$"
EOF
といった具合に、expectの手前でタイムアウト時間を変えてあげれば良さそうだ。
これをcronで実行すると、rootユーザーに実行結果がメールで報告されると思うので、メールの取り扱いには注意が必要になるかも。
さいごに
どのサーバーで何をするのかというのを考えたとき、目先の工数を選ぶのか、長い目で見て良さそうなものを選ぶのか、というのは迷いどころ。
やり方を知ってさえいれば選択肢が広がって、一番良いものを選択できるのだから、知ることは良いことだと思う。
最初にsudoで引っかかったので3時間ほど掛かったこの調査、現物にはsudoがいらなかったのなら短時間で調べることができていた。
さて、どのサーバーで実行させようか。
コメントはこちらから お気軽にどうぞ ~ 投稿に関するご意見・感想・他