From c2eb245b32870b6360079ff9a4b063a7cd84d585 Mon Sep 17 00:00:00 2001 From: Tiago Natel de Moura Date: Wed, 9 Sep 2020 19:28:44 +0100 Subject: [PATCH] PHP: fixed "rootfs" isolation dependency on system mounts. --- auto/modules/php | 30 +----- src/nxt_php_sapi.c | 186 ++++++++++++++++++++++------------ test/test_php_isolation.py | 51 ---------- test/test_python_isolation.py | 30 ++++++ 4 files changed, 150 insertions(+), 147 deletions(-) diff --git a/auto/modules/php b/auto/modules/php index 848fc1bc..41eeb1c3 100644 --- a/auto/modules/php +++ b/auto/modules/php @@ -59,12 +59,6 @@ NXT_PHP_MODULE=${NXT_PHP_MODULE=${NXT_PHP##*/}} NXT_PHP_LIB_PATH=${NXT_PHP_LIB_PATH=} NXT_PHP_LIB_STATIC=${NXT_PHP_LIB_STATIC=no} NXT_PHP_ADDITIONAL_FLAGS= -NXT_PHP_REALPATH=realpath - - -if [ -z `which $NXT_PHP_REALPATH` ]; then - NXT_PHP_REALPATH="readlink -e" -fi $echo "configuring PHP module" @@ -81,12 +75,6 @@ if /bin/sh -c "${NXT_PHP_CONFIG} --version" >> $NXT_AUTOCONF_ERR 2>&1; then NXT_PHP_VERSION="`${NXT_PHP_CONFIG} --version`" NXT_PHP_EXT_DIR="`${NXT_PHP_CONFIG} --extension-dir`" - NXT_PHP_LIBC_DIR="`${CC} --print-file-name=libc.so`" - NXT_PHP_LIBC_DIR="`$NXT_PHP_REALPATH $NXT_PHP_LIBC_DIR`" - NXT_PHP_LIBC_DIR="`dirname $NXT_PHP_LIBC_DIR`" - NXT_PHP_SYSLIB_DIR="`${CC} --print-file-name=libtinfo.so`" - NXT_PHP_SYSLIB_DIR="`$NXT_PHP_REALPATH $NXT_PHP_SYSLIB_DIR`" - NXT_PHP_SYSLIB_DIR="`dirname $NXT_PHP_SYSLIB_DIR`" $echo " + PHP SAPI: [`${NXT_PHP_CONFIG} --php-sapis`]" @@ -228,21 +216,6 @@ if grep ^$NXT_PHP_MODULE: $NXT_MAKEFILE 2>&1 > /dev/null; then fi -NXT_PHP_MOUNTS_HEADER=nxt_${NXT_PHP_MODULE}_mounts.h - -cat << END > $NXT_BUILD_DIR/$NXT_PHP_MOUNTS_HEADER -static const nxt_fs_mount_t nxt_php_mounts[] = { - {(u_char *) "$NXT_PHP_EXT_DIR", (u_char *) "$NXT_PHP_EXT_DIR", - (u_char *) "bind", NXT_MS_BIND | NXT_MS_REC, NULL, 1}, - {(u_char *) "$NXT_PHP_LIBC_DIR", (u_char *) "$NXT_PHP_LIBC_DIR", - (u_char *) "bind", NXT_MS_BIND | NXT_MS_REC, NULL, 1}, - {(u_char *) "$NXT_PHP_SYSLIB_DIR", (u_char *) "$NXT_PHP_SYSLIB_DIR", - (u_char *) "bind", NXT_MS_BIND | NXT_MS_REC, NULL, 1}, -}; - -END - - $echo " + PHP module: ${NXT_PHP_MODULE}.unit.so" . auto/cc/deps @@ -268,8 +241,7 @@ for nxt_src in $NXT_PHP_MODULE_SRCS; do cat << END >> $NXT_MAKEFILE $NXT_BUILD_DIR/$nxt_obj: $nxt_src $NXT_VERSION_H - \$(CC) -c \$(CFLAGS) -DNXT_PHP_MOUNTS_H=\"$NXT_PHP_MOUNTS_HEADER\" \\ - $NXT_PHP_ADDITIONAL_FLAGS \$(NXT_INCS) \\ + \$(CC) -c \$(CFLAGS) $NXT_PHP_ADDITIONAL_FLAGS \$(NXT_INCS) \\ $NXT_PHP_INCLUDE -DNXT_ZEND_SIGNAL_STARTUP=$NXT_ZEND_SIGNAL_STARTUP \\ $nxt_dep_flags \\ -o $NXT_BUILD_DIR/$nxt_obj $nxt_src diff --git a/src/nxt_php_sapi.c b/src/nxt_php_sapi.c index d7e5b476..de329ad7 100644 --- a/src/nxt_php_sapi.c +++ b/src/nxt_php_sapi.c @@ -14,8 +14,6 @@ #include #include -#include NXT_PHP_MOUNTS_H - #if PHP_VERSION_ID >= 50400 #define NXT_HAVE_PHP_IGNORE_CWD 1 @@ -79,9 +77,13 @@ typedef void (*zif_handler)(INTERNAL_FUNCTION_PARAMETERS); #endif +static nxt_int_t nxt_php_setup(nxt_task_t *task, nxt_process_t *process, + nxt_common_app_conf_t *conf); static nxt_int_t nxt_php_start(nxt_task_t *task, nxt_process_data_t *data); static nxt_int_t nxt_php_set_target(nxt_task_t *task, nxt_php_target_t *target, nxt_conf_value_t *conf); +static nxt_int_t nxt_php_set_ini_path(nxt_task_t *task, nxt_str_t *path, + char *workdir); static void nxt_php_set_options(nxt_task_t *task, nxt_conf_value_t *options, int type); static nxt_int_t nxt_php_alter_option(nxt_str_t *name, nxt_str_t *value, @@ -252,9 +254,9 @@ NXT_EXPORT nxt_app_module_t nxt_app_module = { compat, nxt_string("php"), PHP_VERSION, - nxt_php_mounts, - nxt_nitems(nxt_php_mounts), NULL, + 0, + nxt_php_setup, nxt_php_start, }; @@ -268,13 +270,82 @@ static void ***tsrm_ls; #endif +static nxt_int_t +nxt_php_setup(nxt_task_t *task, nxt_process_t *process, + nxt_common_app_conf_t *conf) +{ + nxt_str_t ini_path; + nxt_int_t ret; + nxt_conf_value_t *value; + nxt_php_app_conf_t *c; + + static nxt_str_t file_str = nxt_string("file"); + static nxt_str_t user_str = nxt_string("user"); + static nxt_str_t admin_str = nxt_string("admin"); + + c = &conf->u.php; + +#ifdef ZTS + +#if PHP_VERSION_ID >= 70400 + php_tsrm_startup(); +#else + tsrm_startup(1, 1, 0, NULL); + tsrm_ls = ts_resource(0); +#endif + +#endif + +#if defined(NXT_PHP7) && defined(ZEND_SIGNALS) + +#if (NXT_ZEND_SIGNAL_STARTUP) + zend_signal_startup(); +#elif defined(ZTS) +#error PHP is built with thread safety and broken signals. +#endif + +#endif + + sapi_startup(&nxt_php_sapi_module); + + if (c->options != NULL) { + value = nxt_conf_get_object_member(c->options, &file_str, NULL); + + if (value != NULL) { + nxt_conf_get_string(value, &ini_path); + + ret = nxt_php_set_ini_path(task, &ini_path, + conf->working_directory); + + if (nxt_slow_path(ret != NXT_OK)) { + return NXT_ERROR; + } + } + } + + if (nxt_slow_path(nxt_php_startup(&nxt_php_sapi_module) == FAILURE)) { + nxt_alert(task, "failed to initialize SAPI module and extension"); + return NXT_ERROR; + } + + if (c->options != NULL) { + value = nxt_conf_get_object_member(c->options, &admin_str, NULL); + nxt_php_set_options(task, value, ZEND_INI_SYSTEM); + + value = nxt_conf_get_object_member(c->options, &user_str, NULL); + nxt_php_set_options(task, value, ZEND_INI_USER); + } + + return NXT_OK; +} + + static nxt_int_t nxt_php_start(nxt_task_t *task, nxt_process_data_t *data) { - u_char *p; uint32_t next; - nxt_str_t ini_path, name; nxt_int_t ret; + nxt_str_t name; nxt_uint_t n; nxt_unit_ctx_t *unit_ctx; nxt_unit_init_t php_init; @@ -282,10 +353,6 @@ nxt_php_start(nxt_task_t *task, nxt_process_data_t *data) nxt_php_app_conf_t *c; nxt_common_app_conf_t *conf; - static nxt_str_t file_str = nxt_string("file"); - static nxt_str_t user_str = nxt_string("user"); - static nxt_str_t admin_str = nxt_string("admin"); - conf = data->app; c = &conf->u.php; @@ -318,60 +385,6 @@ nxt_php_start(nxt_task_t *task, nxt_process_data_t *data) } } -#ifdef ZTS - -#if PHP_VERSION_ID >= 70400 - php_tsrm_startup(); -#else - tsrm_startup(1, 1, 0, NULL); - tsrm_ls = ts_resource(0); -#endif - -#endif - -#if defined(NXT_PHP7) && defined(ZEND_SIGNALS) - -#if (NXT_ZEND_SIGNAL_STARTUP) - zend_signal_startup(); -#elif defined(ZTS) -#error PHP is built with thread safety and broken signals. -#endif - -#endif - - sapi_startup(&nxt_php_sapi_module); - - if (c->options != NULL) { - value = nxt_conf_get_object_member(c->options, &file_str, NULL); - - if (value != NULL) { - nxt_conf_get_string(value, &ini_path); - - p = nxt_malloc(ini_path.length + 1); - if (nxt_slow_path(p == NULL)) { - return NXT_ERROR; - } - - nxt_php_sapi_module.php_ini_path_override = (char *) p; - - p = nxt_cpymem(p, ini_path.start, ini_path.length); - *p = '\0'; - } - } - - if (nxt_slow_path(nxt_php_startup(&nxt_php_sapi_module) == FAILURE)) { - nxt_alert(task, "failed to initialize SAPI module and extension"); - return NXT_ERROR; - } - - if (c->options != NULL) { - value = nxt_conf_get_object_member(c->options, &admin_str, NULL); - nxt_php_set_options(task, value, ZEND_INI_SYSTEM); - - value = nxt_conf_get_object_member(c->options, &user_str, NULL); - nxt_php_set_options(task, value, ZEND_INI_USER); - } - ret = nxt_unit_default_init(task, &php_init); if (nxt_slow_path(ret != NXT_OK)) { nxt_alert(task, "nxt_unit_default_init() failed"); @@ -388,9 +401,8 @@ nxt_php_start(nxt_task_t *task, nxt_process_data_t *data) nxt_php_unit_ctx = unit_ctx; - nxt_unit_run(unit_ctx); - - nxt_unit_done(unit_ctx); + nxt_unit_run(nxt_php_unit_ctx); + nxt_unit_done(nxt_php_unit_ctx); exit(0); @@ -512,6 +524,46 @@ nxt_php_set_target(nxt_task_t *task, nxt_php_target_t *target, } +static nxt_int_t +nxt_php_set_ini_path(nxt_task_t *task, nxt_str_t *ini_path, char *workdir) +{ + size_t wdlen; + u_char *p, *start; + + if (ini_path->start[0] == '/' || workdir == NULL) { + p = nxt_malloc(ini_path->length + 1); + if (nxt_slow_path(p == NULL)) { + return NXT_ERROR; + } + + start = p; + + } else { + wdlen = nxt_strlen(workdir); + + p = nxt_malloc(wdlen + ini_path->length + 2); + if (nxt_slow_path(p == NULL)) { + return NXT_ERROR; + } + + start = p; + + p = nxt_cpymem(p, workdir, wdlen); + + if (workdir[wdlen - 1] != '/') { + *p++ = '/'; + } + } + + p = nxt_cpymem(p, ini_path->start, ini_path->length); + *p = '\0'; + + nxt_php_sapi_module.php_ini_path_override = (char *) start; + + return NXT_OK; +} + + static void nxt_php_set_options(nxt_task_t *task, nxt_conf_value_t *options, int type) { diff --git a/test/test_php_isolation.py b/test/test_php_isolation.py index f4170f1b..556bd387 100644 --- a/test/test_php_isolation.py +++ b/test/test_php_isolation.py @@ -85,54 +85,3 @@ class TestPHPIsolation(TestApplicationPHP): assert 'json' in extensions, 'json in extensions list' assert 'unit' in extensions, 'unit in extensions list' - - def test_php_isolation_rootfs_no_language_libs(self, is_su): - isolation_features = self.available['features']['isolation'].keys() - - if not is_su: - if 'user' not in isolation_features: - pytest.skip('requires unprivileged userns or root') - - if not 'unprivileged_userns_clone' in isolation_features: - pytest.skip('requires unprivileged userns or root') - - if 'mnt' not in isolation_features: - pytest.skip('requires mnt ns') - - isolation = { - 'rootfs': option.test_dir, - 'automount': {'language_deps': False}, - 'namespaces': {'credential': not is_su, 'mount': not is_su}, - } - - self.load('list-extensions', isolation=isolation) - - assert 'success' in self.conf( - '"/php/list-extensions"', 'applications/list-extensions/root' - ) - - assert 'success' in self.conf( - {'file': '/php/list-extensions/php.ini'}, - 'applications/list-extensions/options', - ) - - assert 'success' in self.conf( - '"/php/list-extensions"', - 'applications/list-extensions/working_directory', - ) - - extensions = self.getjson()['body'] - - assert 'unit' in extensions, 'unit in extensions list' - assert 'json' not in extensions, 'json not in extensions list' - - assert 'success' in self.conf( - {'language_deps': True}, - 'applications/list-extensions/isolation/automount', - ) - - extensions = self.getjson()['body'] - - assert 'unit' in extensions, 'unit in extensions list 2' - assert 'json' in extensions, 'json in extensions list 2' - diff --git a/test/test_python_isolation.py b/test/test_python_isolation.py index 564ec79c..59ac670a 100644 --- a/test/test_python_isolation.py +++ b/test/test_python_isolation.py @@ -65,3 +65,33 @@ class TestPythonIsolation(TestApplicationPython): assert ( ret['body']['FileExists'] == True ), 'application exists in rootfs' + + def test_python_isolation_rootfs_no_language_deps(self, is_su): + isolation_features = self.available['features']['isolation'].keys() + + if 'mnt' not in isolation_features: + pytest.skip('requires mnt ns') + + if not is_su: + if 'user' not in isolation_features: + pytest.skip('requires unprivileged userns or root') + + if not 'unprivileged_userns_clone' in isolation_features: + pytest.skip('requires unprivileged userns or root') + + isolation = { + 'namespaces': {'credential': not is_su, 'mount': True}, + 'rootfs': self.temp_dir, + 'automount': {'language_deps': False} + } + + self.load('empty', isolation=isolation) + + assert (self.get()['status'] != 200), 'disabled language_deps' + + isolation['automount']['language_deps'] = True + + self.load('empty', isolation=isolation) + + assert (self.get()['status'] == 200), 'enabled language_deps' +