PHP: fixed "rootfs" isolation dependency on system mounts.

This commit is contained in:
Tiago Natel de Moura
2020-09-09 19:28:44 +01:00
parent d491527555
commit c2eb245b32
4 changed files with 150 additions and 147 deletions

View File

@@ -59,12 +59,6 @@ NXT_PHP_MODULE=${NXT_PHP_MODULE=${NXT_PHP##*/}}
NXT_PHP_LIB_PATH=${NXT_PHP_LIB_PATH=} NXT_PHP_LIB_PATH=${NXT_PHP_LIB_PATH=}
NXT_PHP_LIB_STATIC=${NXT_PHP_LIB_STATIC=no} NXT_PHP_LIB_STATIC=${NXT_PHP_LIB_STATIC=no}
NXT_PHP_ADDITIONAL_FLAGS= 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" $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_VERSION="`${NXT_PHP_CONFIG} --version`"
NXT_PHP_EXT_DIR="`${NXT_PHP_CONFIG} --extension-dir`" 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`]" $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 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" $echo " + PHP module: ${NXT_PHP_MODULE}.unit.so"
. auto/cc/deps . auto/cc/deps
@@ -268,8 +241,7 @@ for nxt_src in $NXT_PHP_MODULE_SRCS; do
cat << END >> $NXT_MAKEFILE cat << END >> $NXT_MAKEFILE
$NXT_BUILD_DIR/$nxt_obj: $nxt_src $NXT_VERSION_H $NXT_BUILD_DIR/$nxt_obj: $nxt_src $NXT_VERSION_H
\$(CC) -c \$(CFLAGS) -DNXT_PHP_MOUNTS_H=\"$NXT_PHP_MOUNTS_HEADER\" \\ \$(CC) -c \$(CFLAGS) $NXT_PHP_ADDITIONAL_FLAGS \$(NXT_INCS) \\
$NXT_PHP_ADDITIONAL_FLAGS \$(NXT_INCS) \\
$NXT_PHP_INCLUDE -DNXT_ZEND_SIGNAL_STARTUP=$NXT_ZEND_SIGNAL_STARTUP \\ $NXT_PHP_INCLUDE -DNXT_ZEND_SIGNAL_STARTUP=$NXT_ZEND_SIGNAL_STARTUP \\
$nxt_dep_flags \\ $nxt_dep_flags \\
-o $NXT_BUILD_DIR/$nxt_obj $nxt_src -o $NXT_BUILD_DIR/$nxt_obj $nxt_src

View File

@@ -14,8 +14,6 @@
#include <nxt_unit.h> #include <nxt_unit.h>
#include <nxt_unit_request.h> #include <nxt_unit_request.h>
#include NXT_PHP_MOUNTS_H
#if PHP_VERSION_ID >= 50400 #if PHP_VERSION_ID >= 50400
#define NXT_HAVE_PHP_IGNORE_CWD 1 #define NXT_HAVE_PHP_IGNORE_CWD 1
@@ -79,9 +77,13 @@ typedef void (*zif_handler)(INTERNAL_FUNCTION_PARAMETERS);
#endif #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_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, static nxt_int_t nxt_php_set_target(nxt_task_t *task, nxt_php_target_t *target,
nxt_conf_value_t *conf); 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, static void nxt_php_set_options(nxt_task_t *task, nxt_conf_value_t *options,
int type); int type);
static nxt_int_t nxt_php_alter_option(nxt_str_t *name, nxt_str_t *value, 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, compat,
nxt_string("php"), nxt_string("php"),
PHP_VERSION, PHP_VERSION,
nxt_php_mounts,
nxt_nitems(nxt_php_mounts),
NULL, NULL,
0,
nxt_php_setup,
nxt_php_start, nxt_php_start,
}; };
@@ -268,13 +270,82 @@ static void ***tsrm_ls;
#endif #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 static nxt_int_t
nxt_php_start(nxt_task_t *task, nxt_process_data_t *data) nxt_php_start(nxt_task_t *task, nxt_process_data_t *data)
{ {
u_char *p;
uint32_t next; uint32_t next;
nxt_str_t ini_path, name;
nxt_int_t ret; nxt_int_t ret;
nxt_str_t name;
nxt_uint_t n; nxt_uint_t n;
nxt_unit_ctx_t *unit_ctx; nxt_unit_ctx_t *unit_ctx;
nxt_unit_init_t php_init; 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_php_app_conf_t *c;
nxt_common_app_conf_t *conf; 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; conf = data->app;
c = &conf->u.php; 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); ret = nxt_unit_default_init(task, &php_init);
if (nxt_slow_path(ret != NXT_OK)) { if (nxt_slow_path(ret != NXT_OK)) {
nxt_alert(task, "nxt_unit_default_init() failed"); 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_php_unit_ctx = unit_ctx;
nxt_unit_run(unit_ctx); nxt_unit_run(nxt_php_unit_ctx);
nxt_unit_done(nxt_php_unit_ctx);
nxt_unit_done(unit_ctx);
exit(0); 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 static void
nxt_php_set_options(nxt_task_t *task, nxt_conf_value_t *options, int type) nxt_php_set_options(nxt_task_t *task, nxt_conf_value_t *options, int type)
{ {

View File

@@ -85,54 +85,3 @@ class TestPHPIsolation(TestApplicationPHP):
assert 'json' in extensions, 'json in extensions list' assert 'json' in extensions, 'json in extensions list'
assert 'unit' in extensions, 'unit 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'

View File

@@ -65,3 +65,33 @@ class TestPythonIsolation(TestApplicationPython):
assert ( assert (
ret['body']['FileExists'] == True ret['body']['FileExists'] == True
), 'application exists in rootfs' ), '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'