From: Christian Goeschel Ndjomouo Date: Mon, 20 Apr 2026 04:16:11 +0000 (-0400) Subject: unshare: add whitelist-env command line option X-Git-Url: http://git.ipfire.org/gitweb/?a=commitdiff_plain;h=1a2151eac5f1138da47f671b175f8a0a9388f340;p=thirdparty%2Futil-linux.git unshare: add whitelist-env command line option This new option allows controlled inheritance of environment variables from the parent process by the unshared process. This option by default clears all environment variables except for those provided in the option argument, specified as a comma-separated list. Signed-off-by: Christian Goeschel Ndjomouo --- diff --git a/bash-completion/unshare b/bash-completion/unshare index 8fc9c340e..23b10166c 100644 --- a/bash-completion/unshare +++ b/bash-completion/unshare @@ -6,7 +6,7 @@ _unshare_module() OPTARGOPTS="--mount=|--uts=|--ipc=|--net=|--pid=|--user=|--cgroup=|--time=|--kill-child=|--mount-proc=|--mount-binfmt=" - REQARGOPTS="--owner|--propagation|--setgroups|--root|--wd|--load-interp|--map-group|--map-groups|--map-user|--map-users|--monotonic|--boottime|--setuid|--setgid" + REQARGOPTS="--owner|--propagation|--setgroups|--root|--wd|--load-interp|--map-group|--map-groups|--map-user|--map-users|--monotonic|--boottime|--setuid|--setgid|--whitelist-env" REQARGOPTS_SHORT="-l|-R|-w|-S|-G" diff --git a/sys-utils/unshare.1.adoc b/sys-utils/unshare.1.adoc index d9806975c..f104c33b6 100644 --- a/sys-utils/unshare.1.adoc +++ b/sys-utils/unshare.1.adoc @@ -170,6 +170,9 @@ Set the offset of *CLOCK_BOOTTIME* which will be used in the entered time namesp *--clear-env*:: Do not pass the environment variables of the calling process to the unshared process. +*--whitelist-env* _list_:: +Clear all environment variables except for those specified in the comma-separated _list_. + include::man-common/help-version.adoc[] == ENVIRONMENT diff --git a/sys-utils/unshare.c b/sys-utils/unshare.c index 216bfa785..beecc69d4 100644 --- a/sys-utils/unshare.c +++ b/sys-utils/unshare.c @@ -48,6 +48,7 @@ #include "signames.h" #include "strutils.h" #include "pwdutils.h" +#include "env.h" #ifndef HAVE_ENVIRON_DECL extern char **environ; @@ -743,6 +744,15 @@ static void load_interp(const char *binfmt_mnt, const char *interp) close(dirfd); } +static void set_whitelist_envs(struct ul_env_list **el) +{ + if (env_list_setenv(*el, 0) != 0) + err(EXIT_FAILURE, _("failed to set whitelisted environment variables")); + + env_list_free(*el); + *el = NULL; +} + static void __attribute__((__noreturn__)) usage(void) { FILE *out = stdout; @@ -797,6 +807,7 @@ static void __attribute__((__noreturn__)) usage(void) fputs(_(" --monotonic set clock monotonic offset (seconds) in time namespaces\n"), out); fputs(_(" --boottime set clock boottime offset (seconds) in time namespaces\n"), out); fputs(_(" --clear-env do not inherit environment variables from the calling process\n"), out); + fputs(_(" --whitelist-env clear environment except for specified variables\n"), out); fputs(USAGE_SEPARATOR, out); fprintf(out, USAGE_HELP_OPTIONS(27)); @@ -825,6 +836,7 @@ int main(int argc, char *argv[]) OPT_OWNER, OPT_FORWARD_SIGNALS, OPT_CLEAR_ENV, + OPT_WHITELIST_ENV, }; static const struct option longopts[] = { { "help", no_argument, NULL, 'h' }, @@ -864,6 +876,7 @@ int main(int argc, char *argv[]) { "boottime", required_argument, NULL, OPT_BOOTTIME }, { "load-interp", required_argument, NULL, 'l' }, { "clear-env", no_argument, NULL, OPT_CLEAR_ENV }, + { "whitelist-env", required_argument, NULL, OPT_WHITELIST_ENV }, { NULL, 0, NULL, 0 } }; @@ -874,6 +887,7 @@ int main(int argc, char *argv[]) gid_t mapgroup = -1, ownergroup = -1; struct map_range *usermap = NULL; struct map_range *groupmap = NULL; + struct ul_env_list *env_whitelist = NULL; /* environment whitelist */ int kill_child_signo = 0; /* 0 means --kill-child was not used */ const char *procmnt = NULL; const char *binfmt_mnt = NULL; @@ -1080,6 +1094,9 @@ int main(int argc, char *argv[]) case OPT_CLEAR_ENV: clear_env = 1; break; + case OPT_WHITELIST_ENV: + env_whitelist = env_list_add_getenvs(env_whitelist, optarg); + break; case 'h': usage(); case 'V': @@ -1333,13 +1350,16 @@ int main(int argc, char *argv[]) if (keepcaps && (unshare_flags & CLONE_NEWUSER)) cap_permitted_to_ambient(); - if (clear_env) + if (clear_env || env_whitelist) #ifdef HAVE_CLEARENV clearenv(); #else environ = NULL; #endif + if (env_whitelist) + set_whitelist_envs(&env_whitelist); + if (optind < argc) { execvp(argv[optind], argv + optind); errexec(argv[optind]); diff --git a/tests/expected/unshare/env-clear-env b/tests/expected/unshare/env-clear-env new file mode 100644 index 000000000..929843a12 --- /dev/null +++ b/tests/expected/unshare/env-clear-env @@ -0,0 +1 @@ +CLEARED diff --git a/tests/expected/unshare/env-whitelist-env b/tests/expected/unshare/env-whitelist-env new file mode 100644 index 000000000..257cc5642 --- /dev/null +++ b/tests/expected/unshare/env-whitelist-env @@ -0,0 +1 @@ +foo diff --git a/tests/ts/unshare/clear-env b/tests/ts/unshare/env similarity index 57% rename from tests/ts/unshare/clear-env rename to tests/ts/unshare/env index 5064a7be2..f6f2f2d3e 100755 --- a/tests/ts/unshare/clear-env +++ b/tests/ts/unshare/env @@ -23,16 +23,28 @@ ts_init "$*" ts_check_test_command "$TS_CMD_UNSHARE" ts_check_test_command "$TS_HELPER_STRERROR" + +ts_init_subtest "clear-env" export UL_TEST_ENV=foo -res="$("$TS_CMD_UNSHARE" --clear-env /bin/bash -c 'echo $UL_TEST_ENV' 2>&1)" +"$TS_CMD_UNSHARE" --clear-env /bin/bash -c 'echo "${UL_TEST_ENV:-CLEARED}"' >>"$TS_OUTPUT" 2>>"$TS_ERRLOG" unset -v UL_TEST_ENV -if echo "$res" | grep -q "$($TS_HELPER_STRERROR EPERM)"; then - ts_skip "missing permissions" +if grep -q "$($TS_HELPER_STRERROR EPERM)" "$TS_OUTPUT" "$TS_ERRLOG"; then + ts_skip_subtest "missing permissions" +else + ts_finalize_subtest fi -if [[ -n "$res" ]]; then - ts_failed "environment not cleared correctly: $res" + +ts_init_subtest "whitelist-env" +export UL_TEST_ENV1=foo +export UL_TEST_ENV2=bar +"$TS_CMD_UNSHARE" --whitelist-env UL_TEST_ENV1 /bin/bash -c 'echo "${UL_TEST_ENV1}${UL_TEST_ENV2}"' >>"$TS_OUTPUT" 2>>"$TS_ERRLOG" + +if grep -q "$($TS_HELPER_STRERROR EPERM)" "$TS_OUTPUT" "$TS_ERRLOG"; then + ts_skip_subtest "missing permissions" +else + ts_finalize_subtest fi ts_finalize