]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
nspawn: add a new --no-new-privileges= cmdline option to nspawn
authorLennart Poettering <lennart@poettering.net>
Mon, 7 May 2018 17:35:48 +0000 (19:35 +0200)
committerLennart Poettering <lennart@poettering.net>
Thu, 17 May 2018 18:47:20 +0000 (20:47 +0200)
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.

man/systemd-nspawn.xml
man/systemd.nspawn.xml
src/nspawn/nspawn-gperf.gperf
src/nspawn/nspawn-settings.c
src/nspawn/nspawn-settings.h
src/nspawn/nspawn.c

index 16d65d2b29c58465732e1afedd5dd23d5e7fa39c..7f2b755eabe5c7b94dc5e6be20eed8b7ddee4b3a 100644 (file)
         above).</para></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><option>--no-new-privileges=</option></term>
+
+        <listitem><para>Takes a boolean argument. Specifies the value of the <constant>PR_SET_NO_NEW_PRIVS</constant>
+        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 <citerefentry
+        project='man-pages'><refentrytitle>prctl</refentrytitle><manvolnum>2</manvolnum></citerefentry> for details
+        about this flag. </para></listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><option>--system-call-filter=</option></term>
 
index 3f8c325fd9cbeb4688c06cd881f15b88b431d76e..436f8cd0e99b116f6449e022d8a38dba3c11cca0 100644 (file)
         all cases.</para></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><varname>NoNewPrivileges=</varname></term>
+
+        <listitem><para>Takes a boolean argument that controls the <constant>PR_SET_NO_NEW_PRIVS</constant> flag for
+        the container payload. This is equivalent to the
+        <option>--no-new-privileges=</option> command line switch. See
+        <citerefentry><refentrytitle>systemd-nspawn</refentrytitle><manvolnum>1</manvolnum></citerefentry> for
+        details.</para>
+        </listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><varname>KillSignal=</varname></term>
 
index f7d2e1a5155b5f71b5e3dde02e2e9f90d1dd4dab..a3759674ef91294eabf8dd055b4e32f6d393b747 100644 (file)
@@ -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
index 3757380e282639504d9664cd43c74bf3f6973ee9..11a2f41b961b510d39a2de8adf74d2064fc3e2d9 100644 (file)
@@ -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;
index 3d3ee4c28cd5aed7860449371947c8e4e948a992..130331ee18fbe5610b448376b9216690450d8e48 100644 (file)
@@ -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;
index 7357576a2ad5e87474ccaa01b56e5fcd448230b8..52c6ba1c0cc1d7835511cc96f9c4d873c3b5c883 100644 (file)
@@ -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;
         }