Ubuntu

手入力しているかのようにリモート接続先でコマンドを実行する

とある装置で定期的にコマンドを実行したいが、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がいらなかったのなら短時間で調べることができていた。

さて、どのサーバーで実行させようか。

コメントはこちらから お気軽にどうぞ ~ 投稿に関するご意見・感想・他