mattintosh note

どこかのエンジニアモドキの備忘録

EC-CUBE 4.1のhtmlpurifierキャッシュのパーミッション調整

EC-CUBE 4.1 から execise/htmlpurifier-bundle パッケージが追加されたようです。

フロント入力項目のサニタイズ · EC-CUBE/ec-cube@871ba01 · GitHub

2021 年 6 月だから最近というわけでも無いのですが、これによってキャッシュ削除処理でパーミッション問題が出るようになったっぽいです。

Failed to remove file "/var/www/eccube/var/cache/dev/htmlpurifier/URI/4.14.0,3478238e680361cd87bf880f5b3cc50a1e7abc6c,1.ser": unlink(/var/www/eccube/var/cache/dev/htmlpurifier/URI/4.14.0,3478238e680361cd87bf880f5b3cc50a1e7abc6c,1.ser): Permission denied

execise/htmlpurifier-bundle と一緒に ezyang/htmlpurifier パッケージがインストールされますが、こいついがパーミッション設定を独自に持っていてプロセス(Apache やら PHP-FPM など)の Umask を無視して勝手にパーミッション設定をしてしまうことにより下記のディレクトリや配下のファイルのパーミッション755 または 644 になりグループ権限がリードだけになってしまいます。

  • var/cache/ENV/htmlpurifier/CSS
  • var/cache/ENV/htmlpurifier/HTML
  • var/cache/ENV/htmlpurifier/URI

で、これらのキャッシュが Web サーバのプロセスによって生成された場合、作業用ユーザーとは異なるので console c:c などしたときにこいつらが消せなくなります。(かといって sudoconsole c:c するとキャッシュのオーナーが root になるのでダメ)

管理画面から削除すればいいんですがプロキシやらスキーマの更新のときにいちいちそんなことはしたくないですよね。

ディレクトリとファイルを生成している Serializer.php を見てみると mkdir() の引数にパーミッションを与えている部分と chmod()パーミッションを変更している部分が見つかります。

vendor/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCache/Serializer.php

    private function _prepareDir($config)
    {
        $directory = $this->generateDirectoryPath($config);
        $chmod = $config->get('Cache.SerializerPermissions');
        if ($chmod === null) {
            if (!@mkdir($directory) && !is_dir($directory)) {
                trigger_error(
                    'Could not create directory ' . $directory . '',
                    E_USER_WARNING
                );
                return false;
            }
            return true;
        }
        if (!is_dir($directory)) {
            $base = $this->generateBaseDirectoryPath($config);
            if (!is_dir($base)) {
                trigger_error(
                    'Base directory ' . $base . ' does not exist,
                    please create or change using %Cache.SerializerPath',
                    E_USER_WARNING
                );
                return false;
            } elseif (!$this->_testPermissions($base, $chmod)) {
                return false;
            }
            if (!@mkdir($directory, $chmod) && !is_dir($directory)) {
                trigger_error(
                    'Could not create directory ' . $directory . '',
                    E_USER_WARNING
                );
                return false;
            }
            if (!$this->_testPermissions($directory, $chmod)) {
                return false;
            }
        } elseif (!$this->_testPermissions($directory, $chmod)) {
            return false;
        }
        return true;
    }
    private function _write($file, $data, $config)
    {
        $result = file_put_contents($file, $data);
        if ($result !== false) {
            // set permissions of the new file (no execute)
            $chmod = $config->get('Cache.SerializerPermissions');
            if ($chmod !== null) {
                chmod($file, $chmod & 0666);
            }
        }
        return $result;
    }

いずれも $config->get('Cache.SerializerPermissions')Cache.SerializerPermissions の値を拾っているようですが、これは YAML などには記述されていません。恐らく ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Cache.SerializerPermissions.txt の中に書いてある DEFAULT の値を見ているような気がします。

ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Cache.SerializerPermissions.txt

Cache.SerializerPermissions
TYPE: int/null
VERSION: 4.3.0
DEFAULT: 0755
--DESCRIPTION--

<p>
    Directory permissions of the files and directories created inside
    the DefinitionCache/Serializer or other custom serializer path.
</p>
<p>
    In HTML Purifier 4.8.0, this also supports <code>NULL</code>,
    which means that no chmod'ing or directory creation shall
    occur.
</p>
--# vim: et sw=4 sts=4

しかし、この値を変更しても変化はなく、恐らく変更したあとに ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema.serシリアライズしなければならないのではないかと思ってます。詳しくは調べてません。vendor 配下を変更したくないので。

似たような値に Cache.SerializerPath というものがあり、こちらは README によれば exercise/htmlpurifier-bundle から default_cache_serializer_path によって変更できるようになっているようです。

## Configuration in Symfony 4 and up

If you do not explicitly configure this bundle, an HTMLPurifier service will be
defined as `exercise_html_purifier.default`. This behavior is the same as if you
had specified the following configuration:

``yaml
# config/packages/exercise_html_purifier.yaml

exercise_html_purifier:
    default_cache_serializer_path: '%kernel.cache_dir%/htmlpurifier'
``

しかし、今回やりたいことは Cache.SerializerPermissions で、これは exercize_html_purifier.default_cache_serializer_permissions のような形で渡せるようにはなっていないようです。

明確な答えはどこにも書いてなかったのですが、README の各例から推測して Core.Encoding などの値同様に exercize_html_purifier.html_profiles.default.config.Cache.SerializerPermissions として渡すことができるようです。

app/config/eccube/packages/exercise_html_purifier.yaml

exercise_html_purifier:
    html_profiles:
        default:
            config:
                Cache.SerializerPermissions: 0777

これでディレクトリは 777、ファイルは 666 となります。Cache.SerializerPermissions: 0775 とすればディレクトリは 775、ファイルは 664 です。

軽く検索したところ現時点では日本語の情報は特に出てこなかったのだけど Web サーバのプロセスと SSH でユーザーが違うときに同じ問題に遭遇してる人はまだいないのだろうか。(深追いしすぎてたまに余計なことしてることもあるからそれほど悩む問題でもないのかもしれない)

参考: Permissions issue when clearing cache · Issue #22 · Exercise/HTMLPurifierBundle · GitHub