From: Lennart Poettering Date: Mon, 7 May 2018 17:35:48 +0000 (+0200) Subject: nspawn: add a new --no-new-privileges= cmdline option to nspawn X-Git-Tag: v239~243^2~9 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=66edd96310515e8236f5b3da62f0a1f5143bcd83;p=thirdparty%2Fsystemd.git nspawn: add a new --no-new-privileges= cmdline option to nspawn This simply controls the PR_SET_NO_NEW_PRIVS flag for the container. This too is primarily relevant to provide OCI runtime compaitiblity, but might have other uses too, in particular as it nicely complements the existing --capability= and --drop-capability= flags. --- diff --git a/man/systemd-nspawn.xml b/man/systemd-nspawn.xml index 16d65d2b29c..7f2b755eabe 100644 --- a/man/systemd-nspawn.xml +++ b/man/systemd-nspawn.xml @@ -733,6 +733,17 @@ above). + + + + Takes a boolean argument. Specifies the value of the PR_SET_NO_NEW_PRIVS + flag for the container payload. Defaults to off. When turned on the payload code of the container cannot + acquire new privileges, i.e. the "setuid" file bit as well as file system capabilities will not have an effect + anymore. See prctl2 for details + about this flag. + + diff --git a/man/systemd.nspawn.xml b/man/systemd.nspawn.xml index 3f8c325fd9c..436f8cd0e99 100644 --- a/man/systemd.nspawn.xml +++ b/man/systemd.nspawn.xml @@ -222,6 +222,17 @@ all cases. + + NoNewPrivileges= + + Takes a boolean argument that controls the PR_SET_NO_NEW_PRIVS flag for + the container payload. This is equivalent to the + command line switch. See + systemd-nspawn1 for + details. + + + KillSignal= diff --git a/src/nspawn/nspawn-gperf.gperf b/src/nspawn/nspawn-gperf.gperf index f7d2e1a5155..a3759674ef9 100644 --- a/src/nspawn/nspawn-gperf.gperf +++ b/src/nspawn/nspawn-gperf.gperf @@ -50,6 +50,7 @@ Exec.LimitNICE, config_parse_rlimit, RLIMIT_NICE, of Exec.LimitRTPRIO, config_parse_rlimit, RLIMIT_RTPRIO, offsetof(Settings, rlimit) Exec.LimitRTTIME, config_parse_rlimit, RLIMIT_RTTIME, offsetof(Settings, rlimit) Exec.Hostname, config_parse_hostname, 0, offsetof(Settings, hostname) +Exec.NoNewPrivileges, config_parse_tristate, 0, offsetof(Settings, no_new_privileges) Files.ReadOnly, config_parse_tristate, 0, offsetof(Settings, read_only) Files.Volatile, config_parse_volatile_mode, 0, offsetof(Settings, volatile_mode) Files.Bind, config_parse_bind, 0, 0 diff --git a/src/nspawn/nspawn-settings.c b/src/nspawn/nspawn-settings.c index 3757380e282..11a2f41b961 100644 --- a/src/nspawn/nspawn-settings.c +++ b/src/nspawn/nspawn-settings.c @@ -36,6 +36,7 @@ int settings_load(FILE *f, const char *path, Settings **ret) { s->userns_mode = _USER_NAMESPACE_MODE_INVALID; s->uid_shift = UID_INVALID; s->uid_range = UID_INVALID; + s->no_new_privileges = -1; s->read_only = -1; s->volatile_mode = _VOLATILE_MODE_INVALID; diff --git a/src/nspawn/nspawn-settings.h b/src/nspawn/nspawn-settings.h index 3d3ee4c28cd..130331ee18f 100644 --- a/src/nspawn/nspawn-settings.h +++ b/src/nspawn/nspawn-settings.h @@ -50,9 +50,10 @@ typedef enum SettingsMask { SETTING_PIVOT_ROOT = UINT64_C(1) << 15, SETTING_SYSCALL_FILTER = UINT64_C(1) << 16, SETTING_HOSTNAME = UINT64_C(1) << 17, - SETTING_RLIMIT_FIRST = UINT64_C(1) << 18, /* we define one bit per resource limit here */ - SETTING_RLIMIT_LAST = UINT64_C(1) << (18 + _RLIMIT_MAX - 1), - _SETTINGS_MASK_ALL = (UINT64_C(1) << (18 + _RLIMIT_MAX)) - 1 + SETTING_NO_NEW_PRIVILEGES = UINT64_C(1) << 18, + SETTING_RLIMIT_FIRST = UINT64_C(1) << 19, /* we define one bit per resource limit here */ + SETTING_RLIMIT_LAST = UINT64_C(1) << (19 + _RLIMIT_MAX - 1), + _SETTINGS_MASK_ALL = (UINT64_C(1) << (19 + _RLIMIT_MAX)) - 1 } SettingsMask; typedef struct Settings { @@ -76,6 +77,7 @@ typedef struct Settings { char **syscall_blacklist; struct rlimit *rlimit[_RLIMIT_MAX]; char *hostname; + int no_new_privileges; /* [Image] */ int read_only; diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index 7357576a2ad..52c6ba1c0cc 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -203,6 +203,7 @@ static size_t arg_root_hash_size = 0; static char **arg_syscall_whitelist = NULL; static char **arg_syscall_blacklist = NULL; static struct rlimit *arg_rlimit[_RLIMIT_MAX] = {}; +static bool arg_no_new_privileges = false; static void help(void) { printf("%s [OPTIONS...] [PATH] [ARGUMENTS...]\n\n" @@ -446,6 +447,7 @@ static int parse_argv(int argc, char *argv[]) { ARG_SYSTEM_CALL_FILTER, ARG_RLIMIT, ARG_HOSTNAME, + ARG_NO_NEW_PRIVILEGES, }; static const struct option options[] = { @@ -462,6 +464,7 @@ static int parse_argv(int argc, char *argv[]) { { "read-only", no_argument, NULL, ARG_READ_ONLY }, { "capability", required_argument, NULL, ARG_CAPABILITY }, { "drop-capability", required_argument, NULL, ARG_DROP_CAPABILITY }, + { "no-new-privileges", required_argument, NULL, ARG_NO_NEW_PRIVILEGES }, { "link-journal", required_argument, NULL, ARG_LINK_JOURNAL }, { "bind", required_argument, NULL, ARG_BIND }, { "bind-ro", required_argument, NULL, ARG_BIND_RO }, @@ -773,6 +776,15 @@ static int parse_argv(int argc, char *argv[]) { break; } + case ARG_NO_NEW_PRIVILEGES: + r = parse_boolean(optarg); + if (r < 0) + return log_error_errno(r, "Failed to parse --no-new-privileges= argument: %s", optarg); + + arg_no_new_privileges = r; + arg_settings_mask |= SETTING_NO_NEW_PRIVILEGES; + break; + case 'j': arg_link_journal = LINK_GUEST; arg_link_journal_try = true; @@ -2463,6 +2475,10 @@ static int inner_child( if (r < 0) return r; + if (arg_no_new_privileges) + if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) < 0) + return log_error_errno(errno, "Failed to disable new privileges: %m"); + /* LXC sets container=lxc, so follow the scheme here */ envp[n_env++] = strjoina("container=", arg_container_service_name); @@ -3339,6 +3355,10 @@ static int load_settings(void) { settings->hostname) free_and_replace(arg_hostname, settings->hostname); + if ((arg_settings_mask & SETTING_NO_NEW_PRIVILEGES) == 0 && + settings->no_new_privileges >= 0) + arg_no_new_privileges = settings->no_new_privileges; + return 0; } @@ -4229,7 +4249,7 @@ int main(int argc, char *argv[]) { assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD, SIGWINCH, SIGTERM, SIGINT, -1) >= 0); - if (prctl(PR_SET_CHILD_SUBREAPER, 1) < 0) { + if (prctl(PR_SET_CHILD_SUBREAPER, 1, 0, 0, 0) < 0) { r = log_error_errno(errno, "Failed to become subreaper: %m"); goto finish; }