CGIからrebootなどのroot権限が必要なコマンドを実行する
ZYNQで作る組み込みIoT機器向けの管理者パネルを作っています。
rebootやserviceといったコマンドをCGIから動かしたいのですが、ここには大きな壁が立ちはだかっています。
rebootはroot権限でしか動かすことができませんが、reboot.cgiの中でsudoをしてもリブートしてくれません。
#!/bin/bash
echo "Content-Type: text/plain"
echo ""
sudo reboot 2>&1
sudoの実行時にパスワードが求められるので、CGIの中からこのようには動かせないためです。
sudoのパスワードを自動的に与えるには-Sオプションを使います。-Sオプションを使うと標準入力からパスワードを入力するようになるので、
echo ●●●●● | sudo -S reboot 2>&1
として、CGIの引数にパスワードを渡せばこれでリブートできる・・と思ったのですが、これもダメでした。
apache2を実行しているユーザは、apacheのバージョンやディストリビューションにもよりますが、今の環境ではwww-dataというユーザになっているのですが、このユーザはsudoersに登録されていないためです。おそらく「reboot: ルートである必要があります」というエラーメッセージが闇へと消えていっているのでしょう。
cgiファイルの所有者をrootにしてsetuidしたりしてもダメでした。
結局どうするのが良いかというと、/etc/sudoersに
www-data ALL=(ALL:ALL) NOPASSWD: /sbin/reboot, /usr/bin/service
のように書いて、www-dataというユーザが、/sbin/rebootと/usr/bin/serviceをパスワードなしで使えるようにします。
これで、CGIの中から
sudo reboot 2>&1
だけで、再起動ができるようになります。
よりセキュリティを高めるには、sudoersファイルにNOPASSWD:を指定するのではなく、
www-data ALL=(ALL:ALL) /sbin/reboot, /usr/bin/service
としておいて、CGIには
echo $1 | sudo -S reboot 2>&1
と書き、JQueryのajaxで呼び出すときのdataフィールドでパスワードを送ればよいのです。
$.ajax({
timeout: 5000,
url: './cgi-bin/ping.cgi',
data: password+"&"+addr,
async: true,
success: function(text){
var obj = JSON.parse(text);
$("#dialog_result").text(obj.message);
},
error: function (xhr, textStatus, errorThrown) {
if(textStatus == "timeout"){
$("#dialog_result").text("タイムアウトしました");
}
}
});
なお、www-dataユーザには、デフォルトではパスワードは設定されていないので、passwdコマンドで適当なパスワードを設定する必要があります。
serviceコマンドでWeb UIからサービスを起動/停止する機能も同様に実装しました。
具体的にはCosmo-Zのサーバプログラムcszserverと、WebSocketのサービス、それからSamba関係のsmbdとnmbd、Linuxでの名前解決に使われるavahi-daemonを操作対象としました。
ボタンが押されたら動くJavaScriptを、
$.ajax({
timeout: 10000,
url: './cgi-bin/servicectl.cgi',
async: true,
data: password+"&"+name+"&"+enable ,
success: function(text){
$("#dialog").html(text);
},
error: function (xhr, textStatus, errorThrown) {
$("#dialog").html(textStatus);
}
})
としておき、CGIのプログラムは、
#!/bin/bash
export TZ=JST-9
echo "Content-Type: text/plain"
echo ""
PASSWORD=`echo $1 | sed -e 's/\\\\//g' | awk -F\& '{print $1}'`
NAME=`echo $1 | sed -e 's/\\\\//g' | awk -F\& '{print $2}'`
ENABLE=`echo $1 | sed -e 's/\\\\//g' | awk -F\& '{print $3}'`
if [ "$ENABLE" == "true" ];then
ENABLE="start";
fi
if [ "$ENABLE" == "false" ];then
ENABLE="stop";
fi
echo $PASSWORD | sudo -S /usr/bin/service $NAME $ENABLE 2>&1
で、Webのボタンからサービスの起動/停止ができました。
sudoersに記述したプログラムだけをパスワード付きで実行できるようにしたので、セキュリティ的にもあまり脆弱性を作ることなく、Webから操作できる管理画面ができました。
| 固定リンク
コメント