シェルスクリプトを作成していると、rootで実行したいコマンドがよく出てきます。
rootに自動スイッチする方法は色々あり、状況によって使い分けたくなります。
本記事ではその助けになるように、suのパスワード入力を自動化するあれこれをまとめたものです。
コマンド検証環境: CentOS8
結論
・root権限コマンドの実行が少数でよく、sudoが使用できる場合
→ echo 'パスワード' | sudo -S コマンド
を使う
・root権限コマンドの実行が少数でよく、sudoが使用できない場合
→ ヒアドキュメント
を使う
・root権限コマンドを大量に実行する必要がある場合
→expectコマンド
を使う
以降ではそれぞれの方法について解説します。
sudoを使う
sudoが使用できるコマンドであればecho 'パスワード' | sudo -S コマンド
でコマンドの実行を自動化できます。
メリットはrootのパスワードを使用する必要がなく、現在ログインしているユーザーのパスワードを記述すれば良いことです。つまりrootのパスワードをどこにも書かなくて良いのです。
デメリットはコマンド実行が単発になることです。
下記例ではhoge
ユーザー(一般ユーザー)でログインしており、そのパスワードがhogehoge
としています。この例ではtomcatに対してsystemctlコマンドを実行します。
echo 'hogehoge' | sudo -S systemctl --no-pager status tomcat
パスワードを暗号化する
上記の方法だと、平文のパスワードをベタ書きしないといけないのでパスワードを暗号化します。(※この方法は管理するファイルが2つ増えます)
事前準備としてコンソールでopensslコマンドを実行し、2048ビットのRSA秘密鍵と暗号化したパスワードを保存するファイルを作成します。
暗号化するパスワードはhogehoge
とします。
openssl genrsa -out password.key 2048
echo 'hogehoge' | openssl rsautl -encrypt -inkey password.key > password.txt
ls
# password.key password.txt
それぞれ秘密鍵がpassword.key
、パスワード保存ファイルがpassword.txt
となります。
あとはスクリプトにパスワード復号コマンドを記述すればパスワードのベタ書きを回避できます。
#!/bin/bash
PASSWROD=openssl rsautl -decrypt -inkey password.key -in password.txt
echo $PASSWROD | sudo -S systemctl --no-pager status tomcat
ヒアドキュメントを使う
sudo が使えないコマンドについてはヒアドキュメントを使用するとroot権限コマンドの実行を自動化できます。
メリットはrootのパスワードさえ分かれば、だいたいどこの環境でも使用できる点です。
デメリットはどこかのファイルにrootのパスワードを書く必要があるところです。
root
ユーザーのパスワードをhogeroot
とした時、ヒアドキュメントの記述は次のようになります。
この例ではtomcatに対してsystemctlコマンドを実行しています。
su -c 'systemctl --no-pager status tomcat' << EOF
hogeroot
EOF
パスワードを暗号化する
sudoパターンと同じ方法でパスワードを暗号化します。
事前準備としてコンソールでopensslコマンドを実行し、2048ビットのRSA秘密鍵と暗号化したパスワードを保存するファイルを作成します。
暗号化するパスワードはhogeroot
とします。
openssl genrsa -out password.key 2048
echo 'hogeroot' | openssl rsautl -encrypt -inkey password.key > password_r.txt
ls
# password.key password_r.txt
それぞれ秘密鍵がpassword.key
、パスワード保存ファイルがpassword_r.txt
となります。
あとはスクリプトにパスワード復号コマンドを記述すればパスワードのベタ書きを回避できます。
#!/bin/bash
PASSWROD=openssl rsautl -decrypt -inkey password.key -in password_r.txt
su -c 'systemctl --no-pager status tomcat' << EOF
$PASSWROD
EOF
expectを使う
特権コマンドを同時に複数実行したい場合はexpectコマンドを使用します。
su以外にも対話式コマンドの自動実行が行えるので非常に便利なコマンドです。
しかし、expectコマンドはCentOSにデフォルトでインストールされていないのと(※)、若干あつかいに慣れが必要なのが欠点と言えます。
※ 著者の経験上での話で、特にエビデンスはありません。間違っていたらすみません。
expectコマンドのインストール
次のコマンドを実行すると、expectコマンドをインストールできます。
yum install expect
dnf install expect
expectでrootに自動ログインする
次のスクリプト記述例のsend右側パスワード(ここではhogeroot
)を実際のパスワードに書き換えれば、rootユーザーに自動スイッチすることができます。
ここでは、rootユーザーで実行したいコマンドをsystemctlとしています。
#!/bin/bash
# test.sh
expect -c "
set timeout 1
spawn env LANG=C su
expect \"Password:\"
send \"hogeroot\n\"
"
echo ""
# rootユーザーで実行いたいコマンドを記述していく
systemctl --no-pager status tomcat
systemctl --no-pager status postgresql
exit 0
[hoge@centos8 ~]$ ./test.sh
spawn env LANG=C su
Password:
● tomcat.service - Apache Tomcat application server.
Loaded: loaded (/usr/lib/systemd/system/tomcat.service; disabled; vendor preset: disabled)
Active: active (running) since Thu 2021-07-22 11:40:44 JST; 6h ago
# 中略
● postgresql.service - PostgreSQL database server
Loaded: loaded (/usr/lib/systemd/system/postgresql.service; disabled; vendor preset: disabled)
Active: inactive (dead)
# 以下略
注意点としては、シェルスクリプトではなくコマンドラインでexpectコマンドを実行する場合は最後の行にinteract
を記述する必要があります。
逆にシェルスクリプトでinteract
を記述するとうまく動作しなくなります。
interactは現プロセスの制御をexpectからユーザーに返す処理です。
expect -c "
set timeout 1
spawn env LANG=C su
expect \"Password:\"
send \"hogeroot\n\"
interact # この処理を追加
"
あと、これは余談ですが、このコマンドは次のように一行で書くこともできます。
expect -c "set timeout 1 ; spawn env LANG=C su ; expect \"Password:\" ; send \"hogeroot\n\" ; interact"
パスワードを暗号化する
sudoパターンと同じ方法でパスワードを暗号化します。
事前準備としてコンソールでopensslコマンドを実行し、2048ビットのRSA秘密鍵と暗号化したパスワードを保存するファイルを作成します。
暗号化するパスワードはhogeroot
とします。
openssl genrsa -out password.key 2048
echo 'hogeroot' | openssl rsautl -encrypt -inkey password.key> password_r.txt
ls
# password.key password_r.txt
それぞれ秘密鍵がpassword.key
、パスワード保存ファイルがpassword_r.txt
となります。
あとはスクリプトにパスワード復号コマンドを記述すればパスワードのベタ書きを回避できます。
#!/bin/bash
#追加
PASSWROD=openssl rsautl -decrypt -inkey password.key -in password.txt
expect -c "
set timeout 1
spawn env LANG=C su
expect \"Password:\"
# 変更
send $PASSWROD\"\n\"
"
echo ""
systemctl --no-pager status tomcat
systemctl --no-pager status postgresql
exit 0
expectの書式解説
expectコマンドの書式は次のようになっています。
expect -c "
set timeout [expectのタイムアウト時間]
spawn [実行したいコマンド]
expect [コマンドの返答]
send [コマンドへの回答]
"
spawn に入力待ちを行うコマンド(env LANG=C su)を設定します。
expect に入力待ち時に表示される文字列(Password:)を設定します。
send に入力する文字列(rootのパスワード)と改行文字を設定します。
timeoutはexpectの処理をタイムアウトする時間です。
先ほどの例で言うと、rootユーザーでスクリプトを実行した際にはexpectで期待する文字列Password:
が返ってこないので10秒の受付待ち(デフォルト設定)が発生してしまいます。
それを防ぐためにtimeoutを1としていました。
env LANG=C コマンド
はコマンド
で実行した文字列をcentOSのデフォルト言語(英語)に変換するコマンドです。
こうすることでプロンプトに返ってくる文字列をPassword:
に統一しています。
これは言語設定によってPassword:
が日本語で表示されてしまうためその対策となります。