それマグで!

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

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

OSX に php-cgi モードで起動させた、さらにapache権限のファイルを作らないようにsuexec

php の 755ファイルが面倒

php って便利なんだけど、至る所に、apacheユーザでファイルを作りまくられては困る。 755のパーミッションで、apacheユーザのファイルがあるのは/var/ww以下なら良いんだけど 自分のpublic_html内部にapacheユーザーの権限ファイルがあるとあれこれ面倒が多いよね。

phpがはき出したファイルを触るのにsetid・・つまり 、adduser takuya _www; chmod -R g+w path/to/web; chmod a+s path/to/web; umask 0002 とかしておく必要があるのがいやだし・・・

だから、cgiモードでtakuyaユーザーのファイルを作ってほしい。

先に状況を確認

OSXcgi-binは以下のようにマッピングされている

takuya@rena:~/Sites/upload$ cat /etc/apache2/httpd.conf | grep cgi-bin
340:    ScriptAliasMatch ^/cgi-bin/((?!(?i:webobjects)).*$) "/Library/WebServer/CGI-Executables/$1"

OS X mavericks の phpは 5.4でした

takuya@rena:~/Sites/upload$ /usr/bin/php -v
PHP 5.4.24 (cli) (built: Jan 19 2014 21:32:15)
Copyright (c) 1997-2013 The PHP Group
Zend Engine v2.4.0, Copyright (c) 1998-2013 Zend Technologies

OS X mavericks には php-cgiはありません

takuya@rena:~/Sites/upload$ /usr/bin/php-cgi
-bash: /usr/bin/php-cgi: No such file or directory
takuya@rena:~/Sites/upload$ /usr/bin/php -i | grep cgi
6:Configure Command =>   中略 '--disable-cgi'  以下略

php-cgiが無いなーと思ったら、disabledされてました。乙

cgiモードを使うには、インストールが必要です

ここから分かる通り、osx の標準添付のphp ではcgi は動かない。なのでインストールが必要と分かります。

OS X に php5.5をインストール

brew の標準では*1 tap に入っているのでtap*2を作ってphp5.5を流し込みます。

brew tap homebrew/php
brew install php55

phpインストールを確認

takuya@rena:~/Sites/upload$ which /usr/local/bin/php
/usr/local/bin/php
takuya@rena:~/Sites/upload$ /usr/local/bin/php  -v
PHP 5.5.11 (cli) (built: Apr 13 2014 02:20:35)
Copyright (c) 1997-2014 The PHP Group
Zend Engine v2.5.0, Copyright (c) 1998-2014 Zend Technologies

php-cgicgi-binにコピーします。

http://127.0.0.1/cgi-bin/php-cgi 

にアクセスして、php-cgiが動くようにします

sudo cp $(which php-cgi) /Library/WebServer/CGI-Executables/

cgi が動くようにapache を設定

httpd.conf で

LoadModule cgi_module libexec/apache2/mod_cgi.so
LoadModule actions_module libexec/apache2/mod_actions.so

コメントアウトを削除して、Action と cgi が動くように設定してリスタート

sudo /usr/sbin/apachectl restart

cgiphpが動くように 設定します。

takuyaユーザーに全権を許可しました。

/etc/apache2/users/takuya.conf

この辺は適宜やっちゃえば良いんですけど。user.confで設定してから、全権を許可して.htaccessで制御しました

<Directory "/Users/takuya/Sites/">
    #Options Indexes MultiViews FollowSymLinks # commented out
    Options All
    AllowOverride All
    AllowOverride all
    Order allow,deny
    Allow from all
</Directory>

<IfModule mod_rewrite.c>
   RewriteEngine on
</IfModule>

これで、.htaccessから全部制御します。

/Users/takuya/Sites/path/to/myApp/.htaccess

DirectoryIndex  index.php index.cgi index.fcgi
Action php-script /cgi-bin/php-cgi
AddHandler php-script  .cgi .php

php_flag display_errors on
php_flag display_startup_errors on
php_value date.timezone Asia/Tokyo

timezoneは書いておかないと、warnningが出るのと、display_errors は、何が起きてるか分かるようにするために、必要。本番では外す。

動作チェック

cd /Users/takuya/Sites/path/to/myApp/
echo "<?php phpinfo();" > index.php

これで動いてることが確認できる。

Server API   CGI/FastCGI 

になっていれば、phpapache 2 handler のmod_phpから CGI/FastCGIに制御が変わったことがわかる。

次に、ユーザ権限で動くようにする。

起動した状態で、~/takuya/index.phpはどのユーザーで動いているか確認してみる。

  • /cgi-bin/php の場合はApache
  • ~takuya/cgi-bin/php の場合は takuya で動いていた

(2015-05-25確認

動作してるユーザーを確認してみる

echo '<?php echo "<pre>"; var_dump(posix_getpwuid(posix_getuid()));'  > index.php

動作結果は以下のようになった。

array(7) {
    ["name"]=>  string(4) "_www" 
    ["passwd"]=>  string(1) "*"
    ["uid"]=>  int(70)
    ["gid"]=>  int(70)
    ["gecos"]=>  string(21) "World Wide Web Server"
    ["dir"]=>  string(18) "/Library/WebServer"
    ["shell"]=>  string(14) "/usr/bin/false"
}

え、っと、、、apacheユーザーで動いてます。ふーむ。正しいと言えば正しいんだけど。apacheユーザーでファイルを作りまくられては困る。 これでは、目的を達せない。

cgiを動かすと言えばsuexec。

suexec をインストール

cgi をユーザーのパーミッションで動かすためにsuexecを出来るようにします。

brew install mod_suexec

/etc/apache2/httpd.conf

apacheに suexecをロードさせます。

LoadModule suexec_module /usr/local/Cellar/mod_suexec/2.2.22/libexec/mod_suexec.so

Apache再起動

sudo apachectl restart

suexecのインストールの確認はrubyperlcgiを作ればOK

suexecの動作確認

php-cgiがsuexecでなんかおかしかったので、ruby+sinatraをrack cgiでsuexecを確認。

~/Site/ruby.cgi

#!/usr/bin/ruby
require 'pp'
require 'etc'
require 'bundler'
Bundler.require

get "/" do
      uid= Process.uid
      Etc.getpwuid( uid )
end
set :run => false
Rack::Handler::CGI.run Sinatra::Application

これで、実行時ユーザーが自分になればOK

でもって、suexecのログを確認

tail -n 1 /var/log/apache2/suexec.log
[2014-04-xxx 12xxxxx]: uid: (50xxx/takuya) gid: (20/staff) cmd: ruby.cgi

よしsuexec動いた

php-cgiをsuexecで動かす。

index.cgiを作って。。。

#!/usr/local/bin/php-cgi
<?php 
echo "<pre>";
var_dump(posix_getpwuid(posix_getuid()));

実行するとinternal server errorになる。えっと。なんでエラーなんですか

error_logは次のようになっていて。

[Sun Apr 13 04:29:08 2014] [error] [client ::1] suexec failure: could not open log file
[Sun Apr 13 04:29:08 2014] [error] [client ::1] fopen: Permission denied
[Sun Apr 13 04:29:08 2014] [error] [client ::1] Premature end of script headers: index.cgi

suexecには実行ログが出ている

[2014-04-13 04:30:08]: uid: (501/takuya) gid: (20/staff) cmd: index.cgi
[2014-04-13 04:30:10]: uid: (501/takuya) gid: (20/staff) cmd: index.cgi

suexecで実行ログがあるのに500 internal server erro だとか、security 警告が出る。

どうやら、suexec+php-cgiは動かない模様。

解決策

いろいろ試したところ、ユーザ権限でphp-cgiを動かすとしたら

  1. ユーザーごとのcgi-binを作成
  2. shebang+suexec+forec_redirectチェックオフ

このいずれかが必要と分かった。

phpのマニュアル通りに以下をhttpd.confまたは.htaccessに記述すると shebang なしでもphpが実行されるが

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

この場合は実行ユーザーはapacheになってしまう。

このままで動くんだけどApacheユーザーのファイルは要らないんだ。

いや、たぶんユーザーごとにcgi-binを作れば良いんだろうけど。。。それってsuexecの意味が・・・ もしかしてphpでは、suexecが想定されてない??

解決策1 ユーザーごとのcgi-binを作ってみた

mkdir -p ~/Sites/cgi-bin
cp $(which php-cgi) ~/Sites/cgi-bin

echo "\
Options ExecCGI
SetHandler cgi-script
" > ~/Sites/cgi-bin/.htaccess

~/Sites/path/to/app/.htaccess

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

これで動いた。とりあえずsuexecも動いてる。。 suexec_log

[2014-04-13 04:51:42]: uid: (501/takuya) gid: (20/staff) cmd: php-cgi

でもさ、これってsuexecの意味ない・・・よね。。。

takuyaユーザーがownerのphp-cgiを作ってActionを設定するのが正しいと言えばそうなんだけどさ。

解決策2cgi.force_redirect=0 にした

結局cgi-bin にphp-cgiをいくつも作るのは面倒だし。.htaccessファイルが共通にならないので、php.iniのforce_redirect=0で動かすことに

  1. .htaccesscgi-script有効にするためAddHandler cgi-script .cgiを書く
  2. php.iniで cgi.force_redirect=0にして
  3. index.cgishebang で #!/usr/local/bin/php-cgiを書く

.htaccess

AddHandler cgi-script .cgi

php.ini

cgi.force_redirect=0

権限チェックするphp

#!/usr/local/bin/php-cgi
<?php
echo "<pre>";
var_dump(posix_getpwuid(posix_getuid()));

suexec_log

[2014-04-13 04:56:54]: uid: (501/takuya) gid: (20/staff) cmd: index.cgi

これで、takuyaユーザーでphp-cgiが実行されていることが確認できた

念のためアクセスできないことを確認。

 http://127.0.0.1/cgi-bin/php-cgi/etc/passwd

にアクセスしておいた。

ふーむ。エラーになった。

結論

addhandler cgi-scriptとsuexec+shebang行では、force_redicrect=1に引っかかる。

apachephp-scriptと/cgi-binの組み合わせは必ずapacheユーザーで実行される

こういう当たり前のことを当たり前にキッチリ強制されてる感じでした。

面倒。。。なんか、きもい。きもいよphp。 いや、僕がsuexecを理解してない??そうかも。。。

Apache権限でファイルを作りまくる、それが一番怖いんだけど。

共用サーバーでapacheのファイルがいっぱいあって、それが全部wordpressだとしたらやばくねーかね。。。。。ファイル構造や場所やパーミッション丸見えじゃないかね。

2014-04-13 追記

そういえば、suexecにユーザーIDを指定するモードがあったことを忘れていた

解決策3

SuexecUserGroup takuya staff

これですね。ただし、SuexecUserGroupは VirtualHost単位でしか得使えないので注意が必要

参考資料

http://d.hatena.ne.jp/msakamoto-sf/20080802/1217662203

http://takuya-1st.hatenablog.jp/entry/20110903/1315008329

http://httpd.apache.org/docs/2.2/ja/howto/public_html.html

*1:phpは標準にあって競合するのからと思う

*2:追加機能はtap で入れる。cellar という用語から考えてtapは蛇口を作るってことだろうね