root「ないです。apacheになって見てやんよ」
su apache 「ないです」
結論
Systemd入門(5) - PrivateTmpの実装を見る - めもめも
にすべてが書いてある。
おわり!
これは、あくまでsystemdから起動するプロセスに対して設定するべき内容です。普通に上記の操作をすると、システム上のすべてのプロセスに対して、ファイルシステムの状態が変更されてしまいます。ここで登場するのが、「ファイルシステムのnamespace」(以降は単に「namespace」と表記)の機能です。これを使うと、プロセスごとにファイルシステムの構成状態(マウント状態)を分離することができます。systemdでは、この機能によって、起動プロセスから見えるファイルシステムの構成状態(マウント状態)のみを変更しています。
,
PrivateTmpの機能も同様に、namespaceを利用しています。「/tmp/systemd-private-XXXXXX」(XXXXXXはランダム)という空のディレクトリを用意して、該当プロセスに対しては、/tmpをここにバインドマウントします。これで、このプロセスに対して、/tmpの中身は、/tmp/systemd-private-XXXXXXになります。/var/tmpも同様に別の空ディレクトリにバインドマウントします。
,
また、このシェルからは、次のように/tmpがバインドマウントされているエントリが確認できますが、別の端末からログインした方では、このエントリは見えません。これからもファイルシステムのnamespaceが分離されていることが分かります。
,
プログラム内で、具体的にnamespaceの分離を行う部分をコメントに記してあります。(*1)でnamespaceを分離した後に、(*2)で/以下に「MS_SLAVE」というマークをつけています。これは、このnamespaceで行った変更(追加のバインドマウントなど)を元のnamespaceには反映しないという指定です。これによって、(*3)のバインドマウントは、このプロセスのnamespaceのみで実施されて、他のプロセスからは見えなくなります。
,
上記で説明したように、PrivateTmpを利用してプロセスを起動すると、そのプロセスが追加のバインドマウントを行っても、それは他のプロセスからは見えません
/usr/lib/systemd/system/httpd.service
のPrivateTmp=true
をPrivateTmp=false
にすると問題が発生しない。
他の一部機能がtrue
な場合も同様の状態が発生すると思われる。
PrivateTmp
の仕様というか、副作用により、ブラウザ(Apache)を経由して実行されるmount
はそのプロセスからしか参照できない。
プロセス内でmountすることでリードオンリーにしたり、/tmp
に/tmp/systemd-private-XXXXXX
をmountして、/tmp
への書き込みにprivateTmpを実現しているのだが、このとき、PHPでもあるようなnamespaceを使うことで、他プロセスとはmount状況を独立させることができる。
この影響はプロセス内で残り続けるので、PHPでexec('mount hogehoge')
としても、そのmountは同じnamespaceのプロセス内でしかmountされていることにはならない。
したがって、端末のrootやapacheユーザーであろうと、mount状況は見えない。
環境
- CentOS7 64bit
- Apache/2.4.6 (CentOS)
- systemd 219
問題
書いていたコード
ブラウザから入力された内容でmountを行い、ブラウザでファイル一覧を確認したり、バッチ処理でデータの読み書きを行う。
CentOS7移行前まで稼働実績あり
起こったこと
バッチでのmount領域内の処理が失敗する。
というか、mount
コマンドでmountされているかの確認を行うが、されていないと出るので処理が行われない。
sshで入り確認するも、手動のmount
やdf
ではやはり確認出来ないが、
ブラウザから確認すると、同様のmountチェックコードでも正常にmountされていることが確認できてしまう。
Apacheでしかmount
できないわけではなく、Apacheでのmount状態がApache以外では確認できないような状態だった。
(Apacheと端末で同じ領域に別々にmountしたりできてしまった)
確認した方法
最小構成のPHPを公開領域に置き、ブラウザでアクセスした場合と手動で実行した場合で問題が再現したので、Apacheの問題とした。
<?php
exec('/usr/bin/sudo /bin/mount -t nfs 192.168.1.123:/nas /nas');
echo shell_exec('/usr/bin/sudo /bin/mount');
PrivateTmp
はデバッグ時のファイル書き出しで引っかかって覚えていたのですが、デバッグ以外では/tmp
に書き込まないからと放置していた結果の落とし穴でした。
PrivateTmp
の無効方法およびその他の落とし穴は
お前らもさっさとハマって泣くべきCentOS7の落とし穴4つ - Qiita
をどうぞ。
/tmp
問題はこっちを先に見ていたのに忘れてハマって泣いたんですけど。