1. 概要

システム管理者として、私たちは定期的にさまざまな種類のサーバーをリモートで操作しています。 言うまでもなく、ローカルマシン上のスクリプトを使用してリモートサーバーにアクセスする必要があります。 特に、リモートサーバーからデータを取得するために利用できるLinuxベースのツールはたくさんあります。 したがって、これらは、オーケストレーションを容易にするために、何百ものリモートサーバーの管理に広く使用されています。

このチュートリアルでは、リモートSSHマシンでコマンドにアクセスして実行する最も簡単な方法を説明します。

さて、それの核心に取り掛かりましょう。

2. expectツールの使用

通常、ユーザーインタラクティブCLIプロンプトを自動化するためにexpectスクリプトを使用します。 これは、CLIプロンプトパターンを期待することで機能します。このパターンは、ユーザーの操作なしで自動的に応答を送信します。

2.1. スクリプトを書く

この相互作用を自動化するために、4つの主要なexpectコマンドを使用します。

  • まず、spawnが新しいプロセスまたはセッションを呼び出します
  • 次に、 expected は、期待されるパターンで生成されたプロセス出力を待機します
  • 第三に、 send は、生成されたプロセスに書き込みます’ stdin
  • 最後に、相互作用して現在のプロセスに戻り、 stdin が現在のプロセスに送信され、続いてstdoutstderrが送信されます。 ]が返されます

簡単な例を見てみましょう。

$ cat remote_login.exp
#!/usr/bin/expect
set timeout 60
spawn ssh -p [lindex $argv 3] [lindex $argv 1]@[lindex $argv 0]
expect "*?assword" {
        send "[lindex $argv 2]\r"
        }
expect ":~$ " {
        send "hostname\r\r"
        }
expect ":~$ " {
        send "df -h | grep sd\r\r"
        }
expect ":~$ " {
        send "tail -2 /var/log/dpkg.log\r\r"
        }
expect ":~$ " {
        send "exit\r"
        }
interact

上記のスニペットは、SSHサーバーにログインする単純なexpectスクリプトを示しています。 スクリプトは、 shebang(#!)で始まり、その後に絶対インタープリターパスが続きます。 It は、Linuxベースの環境で実行可能ファイルのインタープリターを識別するのに役立ちます。

最初の行のサポートにより、プログラムローダーは指定されたインタープリタープログラムを実行し、スクリプトを最初の引数として渡します。 一部のサーバープロンプトの読み込みが遅いため、タイムアウトを設定することをお勧めします。 ここでは、無限の待機時間がリモートシステムをロードする間、1分に設定しました。 expectedのデフォルトのタイムアウトは10秒です。

次に、spawnを使用して新しいSSHセッションを呼び出します。 lindex は、位置引数リストから個々の要素を取得します。 スクリプトは、リモートSSHサーバー上で4つの位置引数を想定しています。

  • IPアドレス[位置0]
  • ユーザー名[位置1]
  • パスワード[位置2]
  • SSHポート[位置3]

4つの有効な位置パラメータすべてを使用してスクリプトを実行した後、リモートサーバーはパスワードを要求します。 「パスワード」プロンプトの最初の文字「P」は、一部のシステムでは大文字であり、他のシステムでは小文字である場合があります。 このようなあいまいさを排除するために、パターンの一部である「パスワード」のみを照合します。 次に、 send コマンドは、パスワードを端末stdinに送信します。 さらに、端末パターンに応じて、実行用のコマンドを送信し続けます。

この場合、ホスト名はリモートSSHマシンの名前を取得します。 df コマンドは、リモートサーバーのディスク使用量を提供します。 grep コマンドを使用して、「sda」ボリュームをフィルターで除外します。 最後に、リモートサーバーのログファイルで tail コマンドを実行し、exitコマンドを使用してセッションを閉じます。 前述のように、 interact はコントロールを現在の親プロセスに戻し、stdinが親プロセスにマップされます。

2.2. スクリプトの実行

次に、IPアドレス(10.149.20.11)、ユーザー名(tools)、パスワード(Bael @ 123)、SSHポート(4455)の4つの位置パラメーターを指定してスクリプトを実行しましょう。

$ ./remote_login.exp 10.149.20.11 tools Bael@123 4455
spawn ssh -p 4455 [email protected]
[email protected]'s password:
Welcome to Ubuntu 18.04.6 LTS (GNU/Linux 5.4.0-87-generic x86_64)
...
... output truncated ...
...
Last login: Thu Oct 21 15:55:11 2021 from 180.98.67.141
$ hostname
REMOTE-SERVER
$
$ df -h | grep sd
/dev/sda1        98G   15G   79G  16% /
$
$ tail -2 /var/log/dpkg.log
2021-10-19 05:17:21 status half-configured man-db:amd64 2.8.3-2ubuntu0.1
2021-10-19 05:17:24 status installed man-db:amd64 2.8.3-2ubuntu0.1
$
$ exit
logout
Connection to 10.149.20.11 closed.
$

ご覧のとおり、スクリプトはリモートSSHサーバーですべてのコマンド( hostname df tail )を実行しました。 最後に、ローカルサーバーとリモートサーバー間のセッションは、exitコマンドを使用して終了しました。

2.3. autoexpectを使用する

expect スクリプトを使用してプログラムを手動で作成する代わりに、autoexpectツールを使用してスクリプトを自動的に作成できます。 これは、別のプログラムまたはCLIプロンプトとの相互作用を監視し、相互作用を正確に複製するexpectedスクリプトを自動的に作成するためです。 ベテランのベテランでも、さまざまな無意識のインタラクションピースを自動化するため、スイートスポットになります。

説明のために、autoexpectツールを使用してexpectスクリプトを自動的に作成しましょう。 次に、 autoexpect と入力し、リモートSSHサーバーで必要なコマンドを実行します。

$ autoexpect ssh [email protected] -p4455 'hostname; df -h | grep sd; tail -2 /var/log/dpkg.log'
autoexpect started, file is script.exp
[email protected]'s password:
REMOTE-SERVER
/dev/sda1        98G   15G   79G  16% /
2021-10-19 05:17:21 status half-configured man-db:amd64 2.8.3-2ubuntu0.1
2021-10-19 05:17:24 status installed man-db:amd64 2.8.3-2ubuntu0.1
autoexpect done, file is script.exp
$

出力の最初の行は、ツールがインタラクションの記録を開始したことを示し、script.expは最終的なexpectスクリプトを格納します。

続いて、ファイルを開いて、expected形式のトランザクションのレコードを確認しましょう。

$ cat script.exp
#!/usr/bin/expect -f
...
... output truncated ...
...
set force_conservative 0  ;# set to 1 to force conservative mode even if
                          ;# script wasn't run conservatively originally
if {$force_conservative} {
        set send_slow {1 .1}
        proc send {ignore arg} {
                sleep .1
                exp_send -s -- $arg
        }
}
...
... output truncated ...
...
set timeout -1
spawn ssh [email protected] -p4455 {hostname; df -h | grep sd; tail -2 /var/log/dpkg.log}
match_max 100000
expect -exact "[email protected]'s password: "
send -- "Bael@123\r"
expect eof

最後に、入力引数を指定せずに、スクリプトを実行して前述のタスクを実行できます。

$ ./script.exp
spawn ssh [email protected] -p4455 hostname; df -h | grep sd; tail -2 /var/log/dpkg.log
[email protected]'s password:
REMOTE-SERVER
/dev/sda1        98G   15G   79G  16% /
2021-10-19 05:17:21 status half-configured man-db:amd64 2.8.3-2ubuntu0.1
2021-10-19 05:17:24 status installed man-db:amd64 2.8.3-2ubuntu0.1
$

3. sshpassを使用する

前のセクションでは、サーバー間のエンドツーエンドのトランザクションを自動化する方法を説明しました。 次に、sshpassを使用して有効にした非対話型SSH認証セッションを作成する方法を説明します。

sshpass ツールは、ログインに基づいてSSHのパスワードを自動的に提供し、最終的なサーバーターミナルに移動します。 このツールは、ユーザーのsshインタラクションとシェルスクリプトにも役立ちます。 簡単に言えば、 sshpass はSSHをサポートできますが、それに取って代わることはできません。 オプションsshpassのpは、パスワードをインラインで提供します。これは、リモートサーバーのsshパスワードプロンプトに同時に渡されます。

$ sshpass -p 'Bael@123' ssh [email protected] -p4455 'hostname; df -h | grep sd; tail -2 /var/log/dpkg.log'; 
Welcome to Ubuntu 18.04.6 LTS (GNU/Linux 5.4.0-87-generic x86_64) 
...
... output truncated ...
...
Last login: Thu Oct 19 12:15:31 2021 from 180.98.67.141
REMOTE-SERVER
/dev/sda1        98G   15G   79G  16% /
2021-10-19 05:17:21 status half-configured man-db:amd64 2.8.3-2ubuntu0.1
2021-10-19 05:17:24 status installed man-db:amd64 2.8.3-2ubuntu0.1

3.1. 環境変数の使用

上記の場合、コマンドラインでパスワードを指定しますが、これは安全ではなく、推奨される方法ではありません。 これを回避するには、SSHPASS環境変数をsshフラグ-eで使用するか、sshフラグでファイルを参照します。 ]-f

それでは、パスワードを送信するための環境変数の使用法を調べてみましょう。

$ export SSHPASS="Bael@123"
$ echo $SSHPASS
Bael@123

sshpassコマンドのオプション-eは、パスワードとしてデフォルト名SSHPASSのセッション環境変数を参照するようにツールに指示します。

$ sshpass -e ssh [email protected] -p4455 'hostname; df -h | grep sd; tail -2 /var/log/dpkg.log';
Welcome to Ubuntu 18.04.6 LTS (GNU/Linux 5.4.0-87-generic x86_64)
...
... output truncated ...
...
Last login: Sat Oct 23 12:14:42 2021 from 180.98.67.141
REMOTE-SERVER
/dev/sda1        98G   15G   79G  16% /
2021-10-19 05:17:21 status half-configured man-db:amd64 2.8.3-2ubuntu0.1
2021-10-19 05:17:24 status installed man-db:amd64 2.8.3-2ubuntu0.1
$

3.2. 暗号化された形式のファイルの使用

環境変数は、現在のセッションでのみ使用できます。 新しいセッションを開くか、現在のセッションからログアウトすると、すべての値が失われます。 それでも、パスワード参照を静的ファイルにリダイレクトするオプションがあります。

$ echo 'Bael@123' > .sshpasswd
$ cat .sshpasswd
Bael@123

それでは、 $HOMEディレクトリの.sshpasswdファイルにパスワードを保存しましょう。 sshpassのオプション-fを使用して、次のコマンドでパスワードファイルのパスを指定します。

$ sshpass -f .sshpasswd ssh [email protected] 'hostname; df -h | grep sd; tail -2 /var/log/dpkg.log';
Welcome to Ubuntu 18.04.6 LTS (GNU/Linux 5.4.0-87-generic x86_64)
...
... output truncated ...
...
Last login: Sat Oct 23 08:43:16 2021 from 180.98.67.141
REMOTE-SERVER
/dev/sda1        98G   15G   79G  16% /
2021-10-19 05:17:21 status half-configured man-db:amd64 2.8.3-2ubuntu0.1
2021-10-19 05:17:24 status installed man-db:amd64 2.8.3-2ubuntu0.1 
$

A プレーン静的ファイルは開いた本であり、そのローカルマシンにアクセスできるすべてのユーザーが読むことができます。 パスワードファイルを暗号化するのが賢明でしょう無条件の使用を制限します。

まず、 OpenPGP 暗号化および署名ツールを使用して、.sshpasswdファイルを暗号化します。 オプションgpgのcは、パスフレーズを使用して対称暗号でパスワードを暗号化します。 デフォルトではAES-128を使用しますが、 –cipher-algoオプションを使用して変更できます。

$ echo 'Bael@123' > .sshpasswd
$
$ gpg -c .sshpasswd
gpg: keybox '/home/tools/.gnupg/pubring.kbx' created
$
$ ls -la | grep sshpasswd
-rw-rw-r-- 1 tools tools   10 Oct 23 08:59 .sshpasswd
-rw-rw-r-- 1 tools tools   90 Oct 23 08:59 .sshpasswd.gpg

.sshpasswd.gpg ファイルを作成した後、マシンから.sshpasswdファイルを削除できます。

$ rm -f .sshpasswd
$ ls -la | grep sshpasswd
-rw-rw-r-- 1 tools tools   90 Oct 23 08:59 .sshpasswd.gpg

ここで、 .sshpasswd.gpg を開こうとすると、暗号化された形式になります。

$ cat .sshpasswd.gpg

-}▒▒▒Iſ▒Y▒▒:▒=8E0▒▒▒▒▒ 

▒▒:i(▒▒+▒mM▒},w.b=zi▒!▒▒ݴ▒t▒▒n▒▒*▒▒▒_YB9▒F▒7

ただし、 gpg コマンドを使用して、ファイル内のテキストを復号化できます。

$ gpg -d -q .sshpasswd.gpg
Bael@123 

それでは、 gpg、sshpass、およびsshを統合しましょう。

$ gpg -d -q .sshpasswd.gpg > inlinepass; sshpass -f inlinepass ssh [email protected] 'hostname; df -h | grep sd; tail -2 /var/log/dpkg.log';
Welcome to Ubuntu 18.04.6 LTS (GNU/Linux 5.4.0-87-generic x86_64)
...
... output truncated ...
...
Last login: Sat Oct 23 08:58:39 2021 from 180.98.67.141
REMOTE-SERVER
/dev/sda1        98G   15G   79G  16% /
2021-10-19 05:17:21 status half-configured man-db:amd64 2.8.3-2ubuntu0.1
2021-10-19 05:17:24 status installed man-db:amd64 2.8.3-2ubuntu0.1

まず、パスワードが復号化され、inlinepass変数にコピーされます。 さらに、 sshpass はこの変数を使用して、ログインを成功させるためにパスワードをsshセッションログインプロンプトに転送します。 最後に、セッション認証が行われ、システムがコマンドを実行します。

4. パスワードなしのSSH

このセクションでは、サーバー間にパスワードなしの ssh を作成する方法について簡単に説明します。これにより、リモートSSHサーバーへのログイン作業がさらに簡単になります。

4.1. 公開鍵と秘密鍵の生成

まず、マシン内の特定のユーザーの公開RSAキーと秘密RSAキーのペアを作成して、マシン間にパスワードなしのsshを設定しましょう。 ssh-keygen コマンドを使用すると、id_rsaファイルの秘密鍵とid_rsa.pubの公開鍵を簡単に作成できます。 これらのファイルは、 $HOMEパスの.sshディレクトリに配置されます。

$ ssh-keygen -t rsa
Generating public/private rsa key pair.
Enter file in which to save the key (/home/localuser/.ssh/id_rsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/localuser/.ssh/id_rsa.
Your public key has been saved in /home/localuser/.ssh/id_rsa.pub.
The key fingerprint is:
SHA256:d9AGZC4DdUPcgOwoX6duD65wxjFk2X7HU1gXWLjYxFg localuser@LOCAL-MACHINE
The key's randomart image is:
+---[RSA 2048]----+
|      .o.=Oo+E=oo|
...
... output truncated ...
...
+----[SHA256]-----+

4.2. 鍵交換と実行

それでは、ローカルマシンの公開鍵をリモートマシンにアップロードしましょう。 ssh-copy-id コマンドを使用すると、OpenSSHクライアントパッケージと一緒に出荷できます。 このコマンドを実行すると、ローカルマシンの公開鍵がリモートサーバーの .ssh /authorized_keysファイルに自動的に保存されます。

$ ssh-copy-id [email protected] -p4455
/usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "/home/tools/.ssh/id_rsa.pub"
/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
/usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys
[email protected]'s password:
Number of key(s) added: 1
Now try logging into the machine, with:   "ssh -p '4455' '[email protected]'"
and check to make sure that only the key(s) you wanted were added.

基盤となる鍵交換が行われると、リモートサーバーでコマンドまたはスクリプトを実行できます。

$ ssh -p '4455' '[email protected]’
Welcome to Ubuntu 18.04.6 LTS (GNU/Linux 5.4.0-87-generic x86_64)
...
... output truncated ...
...
tools@REMOTE-SERVER:~$ exit
logout
Connection to 10.149.20.11 closed.
$

パスワードなしのsshオプションを使用すると、SSHプロトコルを使用してリモートサーバーにアクセスしようとするたびに、パスワードを要求されなくなります。

$ ssh [email protected] 'hostname; df -h | grep sd; tail -2 /var/log/dpkg.log';
Welcome to Ubuntu 18.04.6 LTS (GNU/Linux 5.4.0-87-generic x86_64)
...
... output truncated ...
...
REMOTE-SERVER
/dev/sda1        98G   15G   79G  16% /
2021-10-19 05:17:21 status half-configured man-db:amd64 2.8.3-2ubuntu0.1
2021-10-19 05:17:24 status installed man-db:amd64 2.8.3-2ubuntu0.1
$

5. 結論

要約すると、リモートSSHサーバーにログインする複数の方法を見てきました。 その過程で、expectedスクリプトを使用してリモートマシンのコマンドにアクセスして実行する方法の詳細な図も見ました。 さらに、 autoexpect は、CLIの操作を記録することで expect スクリプトを自動的に作成するため、キャップの羽のようなものです。

さらに、sshpassが非対話型ログインでsshツールをどのように支援し、暗号化されたパスワードファイル参照をサポートするかについても調べました。

最後に、ワンストップソリューションとして、キーを交換することにより、マシン間でパスワードなしのsshを設定する手順についても検討しました。 全体として、ユースケースに応じて、リモートサーバーにアクセスして管理するためにこれらのオプションのいずれかを選択する場合があります。