読者です 読者をやめる 読者になる 読者になる

Thanks Driven Life

日々是感謝

PHP 5.4.28 以降で session.save_path を Vagrant(Virtualbox) の synced_folder 内に指定した時に悲しまないように

php vagrant

タイトル長い上にそんな限られた状況の人居ないとは思いますが

まずは結論

synced_folder の owner オプション には、Web サーバ(Apacheとか)を起動するユーザ(例えば apache ユーザ)と同じにすべし。でないとセッションの保存に失敗します。

経緯

  • PHP アプリケーションの開発環境を CentOS6 on Vagrant with Virtualbox で構築している
  • PHP のバージョンは PHP 5.4.35 (パッケージリポジトリremi を利用)
  • アプリケーションのソースコードはホスト側に置いており、 synced_folder でゲスト側にマウントしてそいつを Apache に認識させている

という感じに加えて

session.save_path を synced_folder でマウントしたディレクトリ以下を指定

している。何故そんなことをしているのかというは割愛させていただきます。

どうなるか確認してみよう

こんな Vagrantfile を用意してみました。

# -*- mode: ruby -*-

VAGRANTFILE_API_VERSION = "2"

$script = <<SCRIPT
curl -O http://rpms.famillecollet.com/enterprise/remi-release-7.rpm

sudo yum install -y epel-release
sudo rpm -ivh remi-release-7.rpm
sudo sudo yum install -y --enablerepo=remi php

sudo cat <<"PHP" > /var/www/html/foo.php
<?php
session_save_path('/session');
session_start();

if (!isset($_SESSION['count'])) {
  $_SESSION['count'] = 0;
} else {
  $_SESSION['count']++;
}

var_dump($_SESSION['count']);
PHP

sudo systemctl start httpd
sudo systemctl enable httpd
SCRIPT


Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
  config.vm.box = "chef/centos-7.0"
  config.vm.network :forwarded_port, guest: 80, host: 8080
  config.vm.provision "shell", inline: $script

  config.vm.synced_folder './', '/session',
                          mount_options: ['fmode=777', 'dmode=777']
end

こいつをてきとーな場所に配置して

$ vagrant up

すれば一通り検証環境ができあがります。終わったら http://localhost:8080/foo.php にアクセスしてみてください。

f:id:gongoZ:20141217202637p:plain

こういう画面になると思います。

Vagrantfile にも書いた通り、リロードすれば数値がインクリメントされていくはずなんですが、 現時点ではおそらく int(0) のままだと思います。

ここで、ゲストOSにログインしてエラーログを確認してみます。

$ vagrant ssh
[vagrant@localhost ~]$ sudo tail -n 3 /var/log/httpd/error_log
[Wed Dec 17 11:32:51.388156 2014] [:error] [pid 2282] [client 10.0.2.2:58918] PHP Warning:  Unknown: Failed to write session data (files). Please verify that the current setting of session.save_path is correct (/session) in Unknown on line 0
[Wed Dec 17 11:32:51.540342 2014] [:error] [pid 2282] [client 10.0.2.2:58918] PHP Warning:  Unknown: Failed to write session data (files). Please verify that the current setting of session.save_path is correct (/session) in Unknown on line 0
[Wed Dec 17 11:32:51.702463 2014] [:error] [pid 2282] [client 10.0.2.2:58918] PHP Warning:  Unknown: Failed to write session data (files). Please verify that the current setting of session.save_path is correct (/session) in Unknown on line 0

session.save_path が何かおかしい? /session の中身を見てみます。

[vagrant@localhost ~]$ ls -la /session/
合計 8
drwxrwxrwx.  1 vagrant vagrant  170 1217 11:32 .
drwxr-xr-x. 19 root    root    4096 1217 11:31 ..
drwxrwxrwx.  1 vagrant vagrant  102 1217 10:44 .vagrant
-rwxrwxrwx.  1 vagrant vagrant  860 1217 11:30 Vagrantfile
-rwxrwxrwx.  1 vagrant vagrant    0 1217 11:32 sess_mjrp1g7fk861nq2gpbivh35ju1

セッションファイルらしくものはできている。この時の Cookie の値を見てみると

f:id:gongoZ:20141217203528p:plain

となっているため、おそらくこのファイルで間違いないと思われます。

/session およびその中に作られるファイルは、synced_folder のマウントオプションによりファイルもディレクトリもパーミッション 777 になっています。なのにファイルサイズは0。つまり

  1. session_start()
  2. セッションファイルを /session 以下に作る。
    • /session は 777 なので apache ユーザでも作れる
  3. /session/sess_xxxx が作成されるが、この瞬間に synced_folder の効果によりファイル owner が vagrant に変化する
  4. /session/sess_xxxx の owner が apache ではなくなったため、書き込みに失敗

ちなみに PHP 5.3 までは上記 Vagrantfile の状態でもしっかりカウントアップされます。PHP 5.4 にすると駄目。

原因

PHP 5.4、正確には 5.4.28 から セッションファイルの owner は root もしくは Webサーバのユーザに限る という制限が付きました。

上記リンクを見ていただければわかるとおり、ユーザセッションファイルをしっかりチェックしていこうという流れの中で、 セッションファイルの owner に関しても制限を付けました、というものです。

この影響で、/session 内に空ファイル事態は作れたけど、その後の内容に関しては上のチェックにひっかかって失敗した、ということでした。

解決策

今回使った環境では Web サーバのユーザは apache なので、Vagrantfile を下記のように変更してみます。

@@ -34,5 +34,6 @@
   config.vm.provision "shell", inline: $script
 
   config.vm.synced_folder './', '/session',
+                          owner: 'apache',
                           mount_options: ['fmode=777', 'dmode=777']
 end

変更後 vagrant reload して再度 http://localhost:8080/foo.php にアクセスしてみましょう。リロードするだけカウントアップできるようになっていると思います。

/session の状態を見てみましょう。

[vagrant@localhost ~]$ ls -la /session
合計 12
drwxrwxrwx.  1 apache vagrant  170 1217 11:46 .
drwxr-xr-x. 19 root   root    4096 1217 11:46 ..
drwxrwxrwx.  1 apache vagrant  102 1217 10:44 .vagrant
-rwxrwxrwx.  1 apache vagrant  903 1217 11:45 Vagrantfile
-rwxrwxrwx.  1 apache vagrant   10 1217 11:46 sess_mjrp1g7fk861nq2gpbivh35ju1

書き込めてます!

まとめ

session.save_path は弄らずに平和に普段通り過ごそう