それマグで!

知識はカップより、マグでゆっくり頂きます。 takuya_1stのブログ

習慣に早くから配慮した者は、 おそらく人生の実りも大きい。

php のCGIモードを有効にする-suEXECには php の cgi モードが欠かせない

suEXECでphpを動かせばユーザー毎にファイルの権限がきっちり別れます.
つまり、PHPの設定で,『Apcheから書き込みできること』などと*1な設定指南書を納品されたのでかっとなって調べた.PHPが作ったファイルがwww-dataになって大慌てしなくて済みます

サーバーを複数人で使うのに結構大事な設定だったりしますphp-cgi

phpCGIモードを有効にする

ユーザーディレクトリ userdir で実行するときは mod_php でなく suexec のPHP-cgiを使うとファイルのパーミッションやセッションを読まれたりすることが無くなる.php.iniのsafe-modeは非推奨になったみたいなので各自のuserdir で実行出来るPHP環境を作りたい

全体の流れはこんな感じ

sudo aptitude install php5-cgi
sudo a2dismod php5 #モジュール版はとりあえずOFF(併存できるけどね
sudo aptitude install  apache2-suexec-custom
sudo a2enmod actions #php5のCGIは force-cgi-redirect の制約がある
sudo a2enmod suexec
sudo a2enmod userdir  

モジュールが3つ一気に有効になるので、振り回されずに一つずつ解決していく

いきなりphpCGIで直接叩くとこうなる.

php-cgi 出来た!と喜んでCGIを動かすとこうなる.

500 internal server error

index.php

#!/usr/lib/cgi-bin/php
phpinfo();

Apacheのエラーメッセージは次のような感じ

[Mon Aug 08 15:05:38 2011] [error] [client 192.168.22.195] <b>Security Alert!</b> The PHP CGI cannot be accessed directly.
[Mon Aug 08 15:05:38 2011] [error] [client 192.168.22.195]
[Mon Aug 08 15:05:38 2011] [error] [client 192.168.22.195] <p>This PHP CGI binary was compiled with force-cgi-redirect enabled.  This
[Mon Aug 08 15:05:38 2011] [error] [client 192.168.22.195] means that a page will only be served up if the REDIRECT_STATUS CGI variable is
[Mon Aug 08 15:05:38 2011] [error] [client 192.168.22.195] set, e.g. via an Apache Action directive.</p>
[Mon Aug 08 15:05:38 2011] [error] [client 192.168.22.195] <p>For more information as to <i>why</i> this behaviour exists, see the <a href="http://php.net/security.cgi-b>
[Mon Aug 08 15:05:38 2011] [error] [client 192.168.22.195] <p>For more information about changing this behaviour or re-enabling this webserver,
[Mon Aug 08 15:05:38 2011] [error] [client 192.168.22.195] consult the installation file that came with this distribution, or visit
[Mon Aug 08 15:05:38 2011] [error] [client 192.168.22.195] <a href="http://php.net/install.windows">the manual page</a>.</p>
[Mon Aug 08 15:05:38 2011] [error] [client 192.168.22.195] Premature end of script headers: index.cgi
[Mon Aug 08 15:05:38 2011] [error] [client 192.168.22.195] File does not exist: /var/www/favicon.ico, referer: http://192.168.22.210/users/takuya/index.cgi
[Mon Aug 08 15:22:54 2011] [notice] Graceful restart requested, doing restart
apache2: Could not reliably determine the server's fully qualifi

エラーメッセージに注意があるように cgicgi が許可されたディレクトリ内部で実行すること

userdir の public_htmlでphp-cgiを使うようにする

そしてphp-cgiがuserdirで動くようにする

/etc/php.ini

; cgi.force_redirect is necessary to provide security running PHP as a CGI under
; most web servers.  Left undefined, PHP turns this on by default.  You can
; turn it off here AT YOUR OWN RISK
; **You CAN safely turn this off for IIS, in fact, you MUST.**
; http://php.net/cgi.force-redirect
cgi.force_redirect = 0


; The directory under which PHP opens the script using /~username used only
; if nonempty.
; http://php.net/user-dir
user_dir =

拡張子phpcgi php になるように

      <Directory /home/*/public_html>
                AllowOverride FileInfo AuthConfig Limit Indexes
                Options MultiViews Indexes SymLinksIfOwnerMatch Includes ExecCGI
                Action php-script /cgi-bin/php
                AddHandler php-script .php .cgi

/cgi-bin/phpphp-script と言う名前をつけて, .phpphp-scriptに関連付け

かならず /usr/lib/cgi-bin を経由する

/cgi-bin/phpは何か

cgi-binはこういう形で有効になっている

sites-enabled/000-default (centosの場合のhttpd.conf)
 15
 16     ScriptAlias /cgi-bin/ /usr/lib/cgi-bin/
 17     <Directory "/usr/lib/cgi-bin">
 18         AllowOverride None
 19         Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch
 20         Order allow,deny
 21         Allow from all
 22     </Directory>
 23

/usr/lib/cgi-bin/phpCGIを設置して,http://localhost/cgi-bin/ 経由で起動したらExecCGIをするよって事.

cgi-binで指定したアプリケーションだけを動かすようにする.

では cgi-bin/php はどうなっているのか

takuya@debian00:/etc/apache2$ ls -alt /usr/lib/cgi-bin/
合計 7372
drwxr-xr-x   2 root root    4096 2011-08-08 14:14 .
lrwxrwxrwx   1 root root      29 2011-08-08 14:14 php -> /etc/alternatives/php-cgi-bin
drwxr-xr-x 196 root root   73728 2011-08-08 14:14 ..
lrwxrwxrwx   1 root root      36 2011-08-08 12:12 apt-cacher -> ../../share/apt-cacher/apt-cacher.pl
-rwxr-xr-x   1 root root 7451532 2011-03-19 02:59 php5

/cgi-bin/phpphp-cgiエイリアスされます

さらにphp-cgiエイリアスの先をたどる

takuya@debian00:/etc/apache2$ ls -alt  /etc/alternatives/php-cgi-bin
lrwxrwxrwx 1 root root 21 2011-08-08 14:14 /etc/alternatives/php-cgi-bin -> /usr/lib/cgi-bin/php5

ぐるっと回って戻ってきました /usr/lib/cgi-bin/phpCGI用のphp5でした

/usr/lib/cgi-bin/php5 は php-cgi.exe などで見られるCGI版のPHPの実体です.

suExecの設定.

次にSuEXECを有効にする.

suexec のエラーメッセージは次のよう感じ

[Mon Aug 08 15:37:35 2011] [notice] suEXEC mechanism enabled (wrapper: /usr/lib/apache2/suexec)
[Mon Aug 08 15:37:35 2011] [notice] Apache/2.2.16 (Debian) configured -- resuming normal operations

suExex2はphpファイルのオーナーとしてPHPファイルを実行すると言うこと.

suexecの場所の確認

takuya@debian00:/etc/apache2$ ls -alt /usr/lib/apache2/suexec
-rwsr-xr-- 1 root www-data 13872 2011-03-23 05:57 /usr/lib/apache2/suexec

suexecは(CGIの)実行ユーザーを切替える専用コマンドしてインストールされています.

suexecの設定を確認する.

suexecはコンパイル時にオプションとして、引数を和つぃていて,細かい設定はハードコーディング

takuya@debian00:/etc/apache2$ sudo /usr/lib/apache2/suexec -V
 -D AP_DOC_ROOT="/var/www"
 -D AP_GID_MIN=100
 -D AP_HTTPD_USER="www-data"
 -D AP_LOG_EXEC="/var/log/apache2/suexec.log"
 -D AP_SAFE_PATH="/usr/local/bin:/usr/bin:/bin"
 -D AP_UID_MIN=100
 -D AP_USERDIR_SUFFIX="public_html"

設定は変えられない.設定変えるにはビルドが必要.でも面倒なので・・・

設定を変えられるバイナリが用意してある.

sudo aptitude install apache2-suexec-custom

設定変更可能なSuEXECバイナリのデフォルト設定

takuya@debian00:~$ sudo /usr/lib/apache2/suexec -V
 -D SUEXEC_CONFIG_DIR=/etc/apache2/suexec/
 -D AP_GID_MIN=100
 -D AP_LOG_EXEC="/var/log/apache2/suexec.log"
 -D AP_SAFE_PATH="/usr/local/bin:/usr/bin:/bin"
 -D AP_UID_MIN=100

設定は/etc/apache2/suexec/でカスタマイズ可能
たとえば

  • /home/%u/public_html/
  • /home/%u/www

に換えたいときなどに重宝する.


クセはあるが設定はこんな感じ

/home/
public_html
# The first two lines contain the suexec document root and the suexec userdir
# 最初の2行は必ずUserDirで、SuEXECするディレクトリとその上位ディレクトリ
# suffix. If one of them is disabled by prepending a # character, suexec will
# refuse the corresponding type of request.
# This config file is only used by the apache2-suexec-custom package. See the
# suexec man page included in the package for more details.
#..略
SuexecUserGroup takuya hdusers

SuexecUserGroup 設定はVirtualhostやメインサイトなど /home/ 以外の場所で有効.
だれがプロセスを起動しているか不明になるので必要になる.(Virtualhostやメイン設定だと www-data でイイと思うけどね)

できあがった設定
<IfModule mod_userdir.c>
        UserDir public_html
        UserDir disabled root
        <Directory /home/*/public_html>
                AllowOverride FileInfo AuthConfig Limit Indexes
                Options MultiViews Indexes SymLinksIfOwnerMatch Includes ExecCGI
                AddHandler cgi-script  .cgi
        </Directory>
        <Directory /home/*/public_html/cgi-bin/>
                AllowOverride FileInfo AuthConfig Limit Indexes
                Options MultiViews Indexes SymLinksIfOwnerMatch Includes ExecCGI
                SetHandler cgi-script
        </Directory>
        <Directory /home/takuya/public_html >
                AddHandler php-script .php
                Action php-script /~takuya/cgi-bin/php
        </Directory>
</IfModule>

テストしてみる

#!/usr/bin/env php-cgi
<?php
var_dump(posix_getpwuid(posix_getuid()));

結果

array(7) {
  ["name"]=>
  string(6) "takuya"
  ["passwd"]=>
  string(1) "x"
  ["uid"]=>
  int(1000)
  ["gid"]=>
  int(1000)
  ["gecos"]=>
  string(10) "here,i am "
  ["dir"]=>
  string(12) "/home/takuya"
  ["shell"]=>
  string(9) "/bin/bash"
}

ちゃんとTakuyaさんになってます


perlも実験

  1 #!/usr/bin/env perl
  2
  3 print "Content-type: text/plain\n\n";
  4 printf "user = %s\n",(getpwuid($>))[0];
  5 exit;

結果

DOCUMENT_ROOT="/var/www"
GATEWAY_INTERFACE="CGI/1.1"
HTTP_ACCEPT="text/html, application/xml;q=0.9, application/xhtml+xml, image/png, image/webp, image/jpeg, image/gif, image/x-xbitmap, */*;q=0.1"
HTTP_ACCEPT_ENCODING="gzip, deflate"
HTTP_ACCEPT_LANGUAGE="en,ja-JP;q=0.9"
HTTP_CONNECTION="Keep-Alive"
HTTP_HOST="192.168.9.21"
HTTP_USER_AGENT="Opera/9.80 (Windows NT 6.1; U; ja) Presto/2.9.168 Version/11.50"
PATH="/usr/local/bin:/usr/bin:/bin"
QUERY_STRING=""
REMOTE_ADDR="xxx.xxx.xxx.xxx"
REMOTE_PORT="3779"
REQUEST_METHOD="GET"
REQUEST_URI="/~takuya/printenv.cgi"
SCRIPT_FILENAME="/home/takuya/public_html/printenv.cgi"
SCRIPT_NAME="/~takuya/printenv.cgi"
SERVER_ADDR="192.168.9.21"
SERVER_ADMIN="webmaster@localhost"
SERVER_NAME="192.168.9.21"
SERVER_PORT="80"
SERVER_PROTOCOL="HTTP/1.1"
SERVER_SIGNATURE="<address>Apache/2.2.16 (Debian) Server at 192.168.9.21 Port 80</address>\n"
SERVER_SOFTWARE="Apache/2.2.16 (Debian)"

CGIとしてもちゃんと動いてます.

cgiの動作原理について

http://localhot/~takuya/test.cgi

を起動したら

AddHandler cgi-script  .cgi

が反応して,プログラムを実行する。一行目の

#!/usr/bin/env php-cgi

ShebangPHPを起動する

拡張子PHPはどうしようか

このままだと sheban #!/usr/bin/env php-cgi が必須だし拡張子cgi必須だし。拡張子PHPにしたい
設定書いてみた

    AddHandler php-script .php
    Action php-script /cgi-bin/php

実行してみた

setuid 効いてないじゃん!ってそりゃそうだ

CGIファイルのオーナにSuExecするから php のオーナーは root だから SuexecUserGroupで指定した www-data になります.

cgi の動作原理について

cgiApacheが起動する
ttp://takuya.eample.jp/sample.php
は handler が起動して
ttp://takuya.eample.jp/cgi-bin/php.exe?/home/takuya/public_html/sample.php

のように cgi-bin は引数にファイル名を取ることが出来る.内部的に自動変換がされている


cgi ファイルは shebang を利用している。実行可能
shebang を起動するときに SuEXEC は setuid する

  1. cgi ファイルはActionを決めることが出来る
  2. 該当拡張子が来たらファイル実行する代わりにActionに渡す。
  3. SuEXECはAction指定先を確認する。←ここがActionでSuEXECが効かない原因と思われる

php のオーナーが www-data です

ではどうするのか

ユーザー単位で、CGIディレクトリを作る

調べてみたら、ロリポップはユーザー毎に phpcgi-bin に置いてあるみたい
なるほどね。そうすれば、ユーザー毎にphp.ini使えるもんね

How and why Anchor runs PHP as CGI with suexec on their shared hosting servers - Web and dedicated hosting tutorials by Anchor

Many apache configurations will already have something like this. ScriptAlias is used to map the /cgi-bin/ path to a specific directory, and mark the contents as executable.

ScriptAlias  /cgi-bin/  /var/www/users/username/cgi-bin/

そもそも何でsuexecでPHPを動かすのか

ファイルのオーナー問題

ファイルパーミッションの問題が頻発する。

mod_phpだと、PHPが作るファイルがwww-data(apache)になる
これが問題.
session.save_path=/tmp
だったりしたら、悲惨.www-data でセッション共有してるから、別ユーザーのPHPセッションを平気で見られる
同様に,ファイルのオーナーが同じって事は、読込書き込み削除がPHP側から自由自在。他人のファイルも書き換えれる

リソース制限

  • RLimitCPU - Limit the CPU consumption of a process to N seconds
  • RLimitMEM - Limit the memory consumption of a process to N bytes
  • RLimitNPROC - Limit the number of processes that can be launched to N

suexecならリソース制限がかけやすい(らしい

ちなみに cgi-binにリンクを置くとどうなるのか

drwxr-xr-x 2 takuya takuya 4096 2011-09-03 08:43 .
lrwxrwxrwx 1 takuya takuya   20 2011-09-03 08:43 php -> /usr/lib/cgi-bin/php
drwxr-xr-x 6 takuya takuya 4096 2011-09-03 08:43 ..

結果

Forbidden

You don't have permission to access /~takuya/cgi-bin/php/~takuya/info.php on this server.

Additionally, a 403 Forbidden error was encountered while trying to use an ErrorDocument to handle the request.

なぜか


        AllowOverride FileInfo AuthConfig Limit Indexes
        Options MultiViews Indexes SymLinksIfOwnerMatch Includes ExecCGI

SymLinksIfOwnerMatch に引っかかって実行出来ない

2014-05-22 追記

Actionディレクティブが動かなかった。

Invalid command 'Action', perhaps misspelled or defined by a module not included in the server configuration

原因は、a2enmod actionsを忘れてた。

*1:書いたヤツは何も考えてない