事象の水平線

横を無理やり伸ばしたので、デザインがおかしいけど、気にしない。完璧に時代に取り残されたHTMLをいまさらいじるのがめんどくさい。個人的ブックマーク代わりなメモ書きブログ。

バックアップをcron.dailyとrsyncで自動化して、うかつにコマンドラインからshutdown -h now出来ないので、もっとお気楽で安全に電源を切れるようにウェブ上からスクリプトを走らせることにした。

で、phpでやろうと思ったけど、phpなんて当の昔(←あほime変換:疾うの昔と書くらしい)に忘れてしまったので、色々調べるとビンゴなポストが
『LinuxQuestions.org』さんの『Shutdown the System remotely via PHP-Script?
英語読むのってめんどくさい・・・でも、文字数少なくて助かった;;

が、このままではうんともすんとも言わない。正確には、echo exec("whoami");とかphpの中に埋め込めばHTMLには帰ってくるけど、exec('echo "test">/var/www/html/test.txt');とかでファイルが出力されない。
で、先ほどのフォーラムのリプライでvia visudoとか言ってるんで、visudoって調べてみると、今までCentOSにはsudoが無いと思っていた(どこかで見た)のだけれど、そうではなくて設定でroot以外は使えなくなっていたようで、Apacheにsudoさせる方法を調べる。
と、
『anysense-devel』さんの『PHPからsudoする方法(ハマってしまった人向け)
『memorycraft』さんの『Apacheユーザーでsudo
『the Snow Color』さんの『Apache PHP から sudo する方法

あたりがなんかビンゴな予感。
で、試すが、うごかん・・・・・orz
で、更に調べると、SELinuxな予感。 またか・・ (echoがsudo必要なわけないよね・・)

で、たどり着いたのが
『Linuxメモ』さんの『SELinuxローカルポリシー作成方法
『ごらくリアン』さんの『CentOS 5.2 でSELinuxを設定する方法

ここいらを複合して、手順をメモメモ。

sudoが入ってることを確認
[root@NAS ~]# yum list installed | grep sudo
sudo.i686 1.7.2p2-9.el6 installed


apacheユーザーがsudo出来るようにパスワードを設定する。
[root@NAS ~]# sudo passwd apache
ユーザー apache のパスワードを変更。
新しいパスワード:       ←『お好きなパスワード』
新しいパスワードを再入力してください:
passwd: 全ての認証トークンが正しく更新できました。
[root@NAS ~]#


apacheユーザーに/sbin/shutdown -h now だけパスワードなしでsudo出来るようにする。
ちなみにvisudoはcrontabみたいにviと同じコマンドで操作するらしい。
でも、知ってるのは『:w』保存、『:q』終了、『:q!』保存せず終了、くらいなもん。orz
[root@NAS ~]# visudo
## Sudoers allows particular users to run various commands as
## the root user, without needing the root password.
##
## Examples are provided at the bottom of the file for collections
## of related commands, which can then be delegated out to particular
## users or groups.
##
## This file must be edited with the 'visudo' command.

## Host Aliases
## Groups of machines. You may prefer to use hostnames (perhaps using
## wildcards for entire domains) or IP addresses instead.
# Host_Alias FILESERVERS = fs1, fs2
# Host_Alias MAILSERVERS = smtp, smtp2

## User Aliases
## These aren't often necessary, as you can use regular groups
## (ie, from files, LDAP, NIS, etc) in this file - just use %groupname
## rather than USERALIAS
# User_Alias ADMINS = jsmith, mikem


## Command Aliases
## These are groups of related commands...

## Networking
# Cmnd_Alias NETWORKING = /sbin/route, /sbin/ifconfig, /bin/ping, /sbin/dhclient, /usr/bin/net, /sbin/iptables, /usr/bin/rfcomm, /usr/bin/wvdial, /sbin/iwconfig, /sbin/mii-tool

## Installation and management of software
# Cmnd_Alias SOFTWARE = /bin/rpm, /usr/bin/up2date, /usr/bin/yum

## Services
# Cmnd_Alias SERVICES = /sbin/service, /sbin/chkconfig

## Updating the locate database
# Cmnd_Alias LOCATE = /usr/bin/updatedb

## Storage
# Cmnd_Alias STORAGE = /sbin/fdisk, /sbin/sfdisk, /sbin/parted, /sbin/partprobe, /bin/mount, /bin/umount

## Delegating permissions
# Cmnd_Alias DELEGATING = /usr/sbin/visudo, /bin/chown, /bin/chmod, /bin/chgrp

## Processes
# Cmnd_Alias PROCESSES = /bin/nice, /bin/kill, /usr/bin/kill, /usr/bin/killall

## Drivers
# Cmnd_Alias DRIVERS = /sbin/modprobe

# Defaults specification

#
# Disable "ssh hostname sudo ", because it will show the password in clear.
# You have to run "ssh -t hostname sudo ".
#
#Defaults requiretty   ←端末の無いユーザーからのsudo禁止  を無効化

#
# Preserving HOME has security implications since many programs
# use it when searching for configuration files.
#
Defaults always_set_home

Defaults env_reset
Defaults env_keep = "COLORS DISPLAY HOSTNAME HISTSIZE INPUTRC KDEDIR LS_COLORS"
Defaults env_keep += "MAIL PS1 PS2 QTDIR USERNAME LANG LC_ADDRESS LC_CTYPE"
Defaults env_keep += "LC_COLLATE LC_IDENTIFICATION LC_MEASUREMENT LC_MESSAGES"
Defaults env_keep += "LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER LC_TELEPHONE"
Defaults env_keep += "LC_TIME LC_ALL LANGUAGE LINGUAS _XKB_CHARSET XAUTHORITY"

#
# Adding HOME to env_keep may enable a user to run unrestricted
# commands via sudo.
#
# Defaults env_keep += "HOME"

Defaults secure_path = /sbin:/bin:/usr/sbin:/usr/bin

## Next comes the main part: which users can run what software on
## which machines (the sudoers file can be shared between multiple
## systems).
## Syntax:
##
## user MACHINE=COMMANDS
##
## The COMMANDS section may have other options added to it.
##
## Allow root to run any commands anywhere
root ALL=(ALL) ALL
apache ALL=(ALL) NOPASSWD: /sbin/shutdown -h now

## Allows members of the 'sys' group to run networking, software,
## service management apps and more.
# %sys ALL = NETWORKING, SOFTWARE, SERVICES, STORAGE, DELEGATING, PROCESSES, LOCATE, DRIVERS

## Allows people in group wheel to run all commands
# %wheel ALL=(ALL) ALL

## Same thing without a password
# %wheel ALL=(ALL) NOPASSWD: ALL

## Allows members of the users group to mount and unmount the
## cdrom as root
# %users ALL=/sbin/mount /mnt/cdrom, /sbin/umount /mnt/cdrom

## Allows members of the users group to shutdown this system
# %users localhost=/sbin/shutdown -h now
[root@NAS ~]#


sudo関係は以上。

続いて、phpスクリプトを作る。
1 : inex.htmlにフォームを付け、
2 : ボタンが押されたら/var/www/html/remoteshutdown.phpを呼び出して、そこで他のサーバー接続ユーザーにシャットダウンの告知をし、適当な時間が経ったらjavascriptで/var/www/html/phphalt.phpを呼び出す。
3 : /var/www/html/phphalt.phpでは、backup.shが走っていないかを判断して、走っていない場合はそのままシャットダウンする。
という、二段構えの動作をします。
他の接続ユーザーを気にしない環境ならいきなり3番を呼んでもいいけれど、まぁ、なんとなく;;

1 : /var/www/html/index.htmlindex.shtml)とかに、Shutdownのボタンをつける。
[root@NAS ~]# vi /var/www/html/index.shtml
<html>
<head>
<MEtA http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>CentOS6.0 Server NAS</title>
</head>
<body>
<form action='remoteshutdown.php' method='post'>
<input type='submit' name='submit' value='Shutdown'><br>
</form>

<a href="/mrtg/">MRTG</a> Time : <!--#echo var="DATE_LOCAL"-->
<img src="./bootchart.png">
SHTML test<br>
<!--#echo var="DATE_LOCAL" -->
</body>
</html>
[root@NAS ~]#

2 : 他のサーバー接続ユーザーにシャットダウンの告知と、1分後にphphalt.phpに処理を移す。
→phpファイル remoteshutdown.txt
 (使うときは拡張子を.phpに)
中身をうだうだ書いても読み解く気にはなれないけど、一応書いておく。
php,javascriptはもう何年も使ってないので、あんまよくないかも。

[root@NAS ~]# vi /var/www/html/remoteshutdown.php
<html>
<head>
<META http-equiv="Content-Type" content="text/html; charset=UTF-8">
<?php
if(isset($_POST['submit']))
//if submit has been pressed
{
#サーバーにアクセスしているターミナルにアラートを送る。
exec("wall '
#######################################
Apache User did shutdown
System is going down after 1min !!
#######################################'");
#HTMLの出力 ジャバスクリプトで1分後にphphalt.phpに飛ばす。
print ('<meta http-equiv="refresh" content="120;URL=http:./">
<title>Shutting Down Now : CentOS Nas</title>
<script language="JavaScript">
<!--
function relocale(){
time = 60000;
setTimeout(function(){document.frm.submit.click()},time);
}
//-->
</script>
</head>
<body onLoad="relocale()">
サーバーはシャットダウンの準備中です。       '.date("Y/m/d H:i:s").'<br>
他のユーザーにシャットダウンを通知しています。<br>
<font color="#FF0000"><b>1分後</b></font>に終了します。このままお待ちください。<br>
<br>
<small>※javascriptを使っています。javascriptが無効の場合シャットダウンせずに元のページに戻ります。</small><br>
<br>
<br>
<br>
<br>
<br>
他のユーザーを待たずにシャットダウンする場合は下のボタンを押してください。
<form name="frm" action="phphalt.php" method="post">
<input type="submit" name="submit" value="Shutdown Now">
</form>
');

}
else{

print ('<meta http-equiv="refresh" content="10;URL=http:./">
<title>Remote ShutDown 用 php : CentOS Nas</title>
</head>
<body>
リモートシャットダウン用のphpファイルです。<br>
不正に呼び出されました。<br>
直接呼び出してもシャットダウンは実行されません。<br>
10秒後にHomeに戻ります。');
}
php?>

</body>
</html>

[root@NAS ~]#

3 : backup.shが走っていないことを確認してシャットダウンする。
→phpファイル phphalt.txt
 (使うときは拡張子を.phpに)

[root@NAS ~]# vi /var/www/html/phphalt.php
<html>
<head>
<MEtA http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta http-equiv="refresh" content="15;URL=http:./">
<?php
if(isset($_POST['submit']))
//if submit has been pressed
{
#ログファイル名
$log="/home/share/log/shutdown.log";
#remoteshutdown.phpより優先すべきシェルスクリプト名
$override_exec_file="backup.sh";
exec("/usr/bin/pgrep -f $override_exec_file",$pid);
if(empty($pid)){
#シャットダウン ログかけるかな?
exec("sudo /sbin/shutdown -h now 2>&1");
$message="<<<<<<<<<< ".date("Y/m/d H:i:s")." Done Shutdown by Apache >>>>>>>>>>";
}
else{
$message="/// Another script is running. Shutdown was suspended. ///";
exec('wall "
###############################
Another script is running.
Shutdown was suspended.
###############################"');
}
#HTML
echo('<title>'.$message.'</title>
</head>
<body>'
.$message.'<br>
15秒後にHomeに戻ります。<br>
サーバーがシャットダウンしてる場合、接続がリセットされます。
');
#ログ書き出し
$fp = fopen($log, "a"); #a=追記モード
fwrite($fp, $message);
fclose($fp);
}
php?>

</body>
</html>

[root@NAS ~]#


phpファイルの作成は以上。
phpとjavascriptの解説は省略

続いてSELinux関連の設定
ここが一番厄介・・
はっきり言ってSELinux関連はさっぱり解ってません。 orz
まず、『Linuxメモ』さんの『SELinuxローカルポリシー作成方法』を参考に(というかまんま・・)


[root@NAS ~]# semodule -R  ←ポリシーモジュールリロード
[root@NAS ~]# setenforce 0  ←SELinux無効化

ここで、SELinuxではじかれてうまく動かないスクリプトを実際に走らせる
SELinuxの無効化がされているので、スクリプトは想定どおりに動く。はず。
(動かないときはパーミッションとかSELinux以外の問題。たぶん。)
で、ログが出来る。らしい。

そのログを元にポリシーを作る。らしい。

[root@NAS ~]# mkdir /etc/local-selinux-policy
[root@NAS ~]# audit2allow -l -a -m local001 > /etc/local-selinux-policy/local001.te

で、出来たポリシーを有効化する。
[root@NAS ~]# cd /etc/local-selinux-policy/
[root@NAS local-selinux-policy]# checkmodule -M -m -o local001.mod local001.te
checkmodule: loading policy configuration from local001.te
checkmodule: policy configuration loaded
checkmodule: writing binary representation (version 10) to local001.mod
[root@NAS local-selinux-policy]# semodule_package -o local001.pp -m local001.mod
[root@NAS local-selinux-policy]# semodule -i local001.pp

[root@NAS local-selinux-policy]# setenforce 1  ←SELinux有効化

一応これでSELinuxがEnforcingでも動くらしい。
ただ、一連の動作をいっぺんにこれで処理しようとしたらうまくいかなかったので、remoteshutdown.phpの動作と、phphalt.phpの動作(backup.shが走ってshutdownしない場合)を個別に、local001,local002というように分けて登録した。

また、phphalt.phpから実際にshutdown -h nowが走るときは、上記の手法では出来ないので、『ごらくリアン』さんの『CentOS 5.2 でSELinuxを設定する方法 』を参考に、以下のようにした。

まずは、普通にSELinuxが有効な状態で、SELinuxのログを見る。
CentOS6.0では ログは/var/log/audit/audit.logらしい。
で、phphalt.phpから実際にshutdownが走る状況を作り出し、再度サーバーを起動後ログを見て、増分を/etc/local-selinux-policy/local003.logに取り出す。
この中の、avc : deniedが含まれる行がSELinuxがはじいてるログらしいので、それ以外の行は削除して、そこからポリシーファイルを作る。

[root@NAS ~]# cd /etc/local-selinux-policy/
以下のコマンドで内容が確認できるらしい。見てもわかんないすけど・・
[root@NAS local-selinux-policy]# audit2allow -i local003.log -m local003
module local003 1.0;
require {
type httpd_t;
class process setrlimit;
class netlink_audit_socket create;
}
#============= httpd_t ==============
#!!!! This avc can be allowed using the boolean 'allow_httpd_mod_auth_pam'
allow httpd_t self:netlink_audit_socket create;
#!!!! This avc can be allowed using the boolean 'httpd_setrlimit'
allow httpd_t self:process setrlimit;
実際にポリシーファイルを作る
[root@NAS local-selinux-policy]# audit2allow -i local003.log -M local003
******************** IMPORTANT ***********************
To make this policy package active, execute:

semodule -i local003.pp
コンパイル済みのポリシーがカレントディレクトリにできるので読み込ませる。らしい。
[root@NAS local-selinux-policy]# semodule -i local003.pp


手順的には以上なんだけど、うまくいかずに何度もやり直しました。
で、いらない(うまく動かなかった)ポリシーは以下のコマンドで削除。
[root@NAS local-selinux-policy]# semodule -r local003.pp

phpでexecとか使うと、OSインジェクションの危険があるらしいです。
SQLインジェクションの言葉くらいは知ってますけど、確かに言われてみれば危険ですね・・・特にsudoで権限委譲して、phpの中身なんて公開したら超危険ですね・・・
まぁ、Wanに公開してないんでいいですけど・・・

SELinuxも何とか無効化せずに使ってますけど、よく解らずにポリシー作ってると穴だらけになって危険です・・おそらく。
ちなみに、SELinux関連の役に立ちそうなHPをメモ。
『アイティメディア』さんの『Red Hat Enterprise Linux 5で始めるSELinux
『アイティメディア』さんの『SELinux Policy EditorでSELinuxを簡単に

SEEditはインストール時に競合するとか言われたので、あきらめました。
スポンサーサイト



PageTop

コメント

 ※
 ※
管理者にだけ表示を許可する
  ※ 必須項目です