expectコマンドで、sshパスワードログインの自動化をする。

前回、expectコマンドの基本的な使い方を解説し、簡単なサンプルコードを紹介しました。
今回は、より実践的な多段SSHやsudo suでユーザーを切り替えるといったところまで実行してくれるexpectコードを紹介します。

パート毎に詳しく記載した記事があるので、基礎知識を身につけたい場合は、参考にしてください。

やりたいこと

  1. 踏み台サーバーにSSH (認証鍵:パスフレーズ付き)
  2. 本番サーバーにSSH (パスワード)
  3. sudo su でユーザー変更 (パスワード)
  4. 対話シェルとして維持する

パスワード認証ではなく、鍵認証で行けるところもあるとは思いますが、
本番サーバーに好きに公開鍵を置けないところも多いかと思います。

また、鍵認証を使った場合でも、鍵にパスフレーズを設定している場合は入力が必要になります。
sudoを利用する場合も、パスワード入力が必要になるでしょう。

sshパスワードログインを自動化する。

前述の要件を満たすには、下記のようなコードを作成すると良い。

login.tcl

#!/usr/bin/expect --

set key_pw  "hogehoge" # 認証鍵のパスフレーズ
set ssh_pw  "piyopiyo" # 本番SSH用のパスワード
set sudo_pw "foofoo"   # sudo用のパスワード

# 踏み台サーバーへSSH
# プロセスを起動、以降このプロセスと対話していく。
spawn ssh xxx@192.168.0.1

# 認証鍵のパスフレーズに答える。
# Enter passphrase for key '/home/xxx/.ssh/id_rsa':
expect "passphrase"
send -- "${key_pw}\r"

# ログインができたら(プロンプト($)が表示されたら)、本番サーバーにSSH
expect "\\\$"
send "ssh xxx@192.168.0.2\r"

# パスワードを聞かれたら入力。
# xxx@192.168.0.2's password:
expect "password"
send -- "${ssh_pw}\r"

# ログインができたら(プロンプト($)が表示されたら)、ユーザーを切り替え。
expect "\\\$"
send "sudo su - yyy\r"

# sudo用のパスワードを聞かれたら入力。
# [sudo] xxx のパスワード:
expect "sudo*:"
send -- "${sudo_pw}\r"

# 制御をユーザーに渡す。
interact

上記のスクリプトを自分の環境に合わせて編集したら、
実行権限を付与して実行してみよう。

# 実行権限を付与
chmod +x login.tcl

# 実行する
./login.tcl

改善ポイント

expect "password" は、環境によっては 先頭大文字の"Password"で来る場合もあります。
どちらにも対応できるように、expect "*?assword" としておくでも良いでしょう。

expect "sudo*:" sudo用のパスワードが聞かれる部分は、
日本語で来る場合も想定して、マッチさせています。

ログイン後の待機で、プロンプト($)が表示されるまで待機しています。
しかし、環境や場合によって"#"や"%"、">"なども許容したければ、
expect "\[$%#>\]" などと書くこともできます。

ターミナルサイズに追従させる。

expectを利用してsshログインした場合、
ターミナルのウィンドウサイズ変更に追従してくれない場合があります。

その場合は、下記コードをスクリプトの冒頭か、
interactする前あたりに追記しておくと改善します。

trap {
  set XZ [stty rows   ]
  set YZ [stty columns]
  stty rows $XZ columns $YZ < $spawn_out(slave,name)
} WINCH

Expectが、SIGWINCH(ウィンドウサイズ変更)を受けとると、
端末サイズに反映するというものです。

まとめ

expectを利用すると、多段SSHで毎回パスワード入力してる場合に有効です。
特に自分はパスワードが12文字以上はあるので、毎回入力するのはとても大変でした。

expectコマンドは、"-c"オプションを使ってシェルスクリプト内に書いているケースをよく見かけますが、
エスケープが大変だったりもするので、今回のようにTclファイルにShebangをつけて実行するのがおすすめです。

Tclでもif文などの制御文はかけるので、案外凝ったこともできます。

タイトルとURLをコピーしました