« DHCPが使えない環境でも使える「IoT機器の管理者画面」を作る | トップページ | WebのユーザインタフェースからZYNQのboot.binを更新する »

2022.03.25

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

だけで、再起動ができるようになります。

Reboot

よりセキュリティを高めるには、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を操作対象としました。

Service 

ボタンが押されたら動く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から操作できる管理画面ができました。

 

|

« DHCPが使えない環境でも使える「IoT機器の管理者画面」を作る | トップページ | WebのユーザインタフェースからZYNQのboot.binを更新する »

コメント

コメントを書く



(ウェブ上には掲載しません)




« DHCPが使えない環境でも使える「IoT機器の管理者画面」を作る | トップページ | WebのユーザインタフェースからZYNQのboot.binを更新する »