systemd-nspawn: Allow setting ambient capability set

The old code was only able to pass the value 0 for the inheritable
and ambient capability set when a non-root user was specified.

However, sometimes it is useful to run a program in its own container
with a user specification and some capabilities set. This is needed
when the capabilities cannot be provided by file capabilities (because
the file system is mounted with MS_NOSUID for additional security).

This commit introduces the option --ambient-capability and the config
file option AmbientCapability=. Both are used in a similar way to the
existing Capability= setting. It changes the inheritable and ambient
set (which is 0 by default). The code also checks that the settings
for the bounding set (as defined by Capability= and DropCapability=)
and the setting for the ambient set (as defined by AmbientCapability=)
are compatible. Otherwise, the operation would fail in any way.

Due to the current use of -1 to indicate no support for ambient
capability set the special value "all" cannot be supported.

Also, the setting of ambient capability is restricted to running a
single program in the container payload.
This commit is contained in:
Torsten Hilbrich 2020-12-04 11:27:12 +01:00 committed by Zbigniew Jędrzejewski-Szmek
parent bf20d93750
commit 88fc9c9bad
5 changed files with 100 additions and 9 deletions

View File

@ -1002,7 +1002,11 @@
If the special value <literal>all</literal> is passed, all capabilities are retained.</para>
<para>If the special value of <literal>help</literal> is passed, the program will print known
capability names and exit.</para></listitem>
capability names and exit.</para>
<para>This option sets the bounding set of capabilities which
also limits the ambient capabilities as given with the
<option>--ambient-capability=</option>.</para></listitem>
</varlistentry>
<varlistentry>
@ -1014,7 +1018,32 @@
above).</para>
<para>If the special value of <literal>help</literal> is passed, the program will print known
capability names and exit.</para></listitem>
capability names and exit.</para>
<para>This option sets the bounding set of capabilities which
also limits the ambient capabilities as given with the
<option>--ambient-capability=</option>.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--ambient-capability=</option></term>
<listitem><para>Specify one or more additional capabilities to
pass in the inheritable and ambient set to the program started
within the container. The value <literal>all</literal> is not
supported for this setting.</para>
<para>All capabilities specified here must be in the set
allowed with the <option>--capability=</option> and
<option>--drop-capability=</option> options. Otherwise, an
error message will be shown.</para>
<para>This option cannot be combined with the boot mode of the
container (as requested via <option>--boot</option>).</para>
<para>If the special value of <literal>help</literal> is
passed, the program will print known capability names and
exit.</para></listitem>
</varlistentry>
<varlistentry>

View File

@ -190,7 +190,34 @@
<filename>/run/system/nspawn/</filename> (see above). On the
other hand, <varname>DropCapability=</varname> takes effect in
all cases. If the special value <literal>all</literal> is passed, all
capabilities are retained (or dropped).</para></listitem>
capabilities are retained (or dropped).</para>
<para>These settings change the bounding set of capabilities which
also limits the ambient capabilities as given with the
<varname>AmbientCapability=</varname>.</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>AmbientCapability=</varname></term>
<listitem><para>Takes a space-separated list of Linux process
capabilities (see
<citerefentry project='man-pages'><refentrytitle>capabilities</refentrytitle><manvolnum>7</manvolnum></citerefentry>
for details). The <varname>AmbientCapability=</varname> setting
specifies capability which will be passed to to started program
in the inheritable and ambient capability sets. This will grant
these capabilities to this process. This setting correspond to
the <option>--ambient-capability=</option> command line switch.
</para>
<para>The value <literal>all</literal> is not supported for this
setting.</para>
<para>The setting of <varname>AmbientCapability=</varname> must
be covered by the bounding set settings which were established by
<varname>Capability=</varname> and <varname>DropCapability=</varname>.
</para>
<para>Note that <varname>AmbientCapability=</varname> is a privileged
setting (see above).</para></listitem>
</varlistentry>
<varlistentry>

View File

@ -25,6 +25,7 @@ Exec.Parameters, config_parse_strv, 0, of
Exec.Environment, config_parse_strv, 0, offsetof(Settings, environment)
Exec.User, config_parse_string, 0, offsetof(Settings, user)
Exec.Capability, config_parse_capability, 0, offsetof(Settings, capability)
Exec.AmbientCapability, config_parse_capability, 0, offsetof(Settings, ambient_capability)
Exec.DropCapability, config_parse_capability, 0, offsetof(Settings, drop_capability)
Exec.KillSignal, config_parse_signal, 0, offsetof(Settings, kill_signal)
Exec.Personality, config_parse_personality, 0, offsetof(Settings, personality)

View File

@ -157,6 +157,7 @@ typedef struct Settings {
char *user;
uint64_t capability;
uint64_t drop_capability;
uint64_t ambient_capability;
int kill_signal;
unsigned long personality;
sd_id128_t machine_id;

View File

@ -165,6 +165,7 @@ static uint64_t arg_caps_retain =
(1ULL << CAP_SYS_PTRACE) |
(1ULL << CAP_SYS_RESOURCE) |
(1ULL << CAP_SYS_TTY_CONFIG);
static uint64_t arg_caps_ambient = 0;
static CapabilityQuintet arg_full_capabilities = CAPABILITY_QUINTET_NULL;
static CustomMount *arg_custom_mounts = NULL;
static size_t arg_n_custom_mounts = 0;
@ -379,6 +380,9 @@ static int help(void) {
" --capability=CAP In addition to the default, retain specified\n"
" capability\n"
" --drop-capability=CAP Drop the specified capability from the default set\n"
" --ambient-capability=CAP\n"
" Sets the specified capability for the started\n"
" process. Not useful if booting a machine.\n"
" --no-new-privileges Set PR_SET_NO_NEW_PRIVS flag for container payload\n"
" --system-call-filter=LIST|~LIST\n"
" Permit/prohibit specific system calls\n"
@ -648,6 +652,7 @@ static int parse_argv(int argc, char *argv[]) {
ARG_UUID,
ARG_READ_ONLY,
ARG_CAPABILITY,
ARG_AMBIENT_CAPABILITY,
ARG_DROP_CAPABILITY,
ARG_LINK_JOURNAL,
ARG_BIND,
@ -709,6 +714,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "uuid", required_argument, NULL, ARG_UUID },
{ "read-only", no_argument, NULL, ARG_READ_ONLY },
{ "capability", required_argument, NULL, ARG_CAPABILITY },
{ "ambient-capability", required_argument, NULL, ARG_AMBIENT_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 },
@ -1018,6 +1024,15 @@ static int parse_argv(int argc, char *argv[]) {
arg_settings_mask |= SETTING_READ_ONLY;
break;
case ARG_AMBIENT_CAPABILITY: {
uint64_t m;
r = parse_capability_spec(optarg, &m);
if (r <= 0)
return r;
arg_caps_ambient |= m;
arg_settings_mask |= SETTING_CAPABILITY;
break;
}
case ARG_CAPABILITY:
case ARG_DROP_CAPABILITY: {
uint64_t m;
@ -1760,6 +1775,17 @@ static int verify_arguments(void) {
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "--port= is not supported, compiled without libiptc support.");
#endif
if (arg_caps_ambient) {
if (arg_caps_ambient == (uint64_t)-1)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "AmbientCapability= does not support the value all.");
if ((arg_caps_ambient & arg_caps_retain) != arg_caps_ambient)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "AmbientCapability= setting is not fully covered by Capability= setting.");
if (arg_start_mode == START_BOOT)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "AmbientCapability= setting is not useful for boot mode.");
}
r = custom_mount_check_all();
if (r < 0)
return r;
@ -2622,13 +2648,13 @@ static int drop_capabilities(uid_t uid) {
q.effective = uid == 0 ? q.bounding : 0;
if (q.inheritable == (uint64_t) -1)
q.inheritable = uid == 0 ? q.bounding : 0;
q.inheritable = uid == 0 ? q.bounding : arg_caps_ambient;
if (q.permitted == (uint64_t) -1)
q.permitted = uid == 0 ? q.bounding : 0;
q.permitted = uid == 0 ? q.bounding : arg_caps_ambient;
if (q.ambient == (uint64_t) -1 && ambient_capabilities_supported())
q.ambient = 0;
q.ambient = arg_caps_ambient;
if (capability_quintet_mangle(&q))
return log_error_errno(SYNTHETIC_ERRNO(EPERM), "Cannot set capabilities that are not in the current bounding set.");
@ -2637,9 +2663,9 @@ static int drop_capabilities(uid_t uid) {
q = (CapabilityQuintet) {
.bounding = arg_caps_retain,
.effective = uid == 0 ? arg_caps_retain : 0,
.inheritable = uid == 0 ? arg_caps_retain : 0,
.permitted = uid == 0 ? arg_caps_retain : 0,
.ambient = ambient_capabilities_supported() ? 0 : (uint64_t) -1,
.inheritable = uid == 0 ? arg_caps_retain : arg_caps_ambient,
.permitted = uid == 0 ? arg_caps_retain : arg_caps_ambient,
.ambient = ambient_capabilities_supported() ? arg_caps_ambient : (uint64_t) -1,
};
/* If we're not using OCI, proceed with mangled capabilities (so we don't error out)
@ -4070,6 +4096,7 @@ static int merge_settings(Settings *settings, const char *path) {
if ((arg_settings_mask & SETTING_CAPABILITY) == 0) {
uint64_t plus, minus;
uint64_t network_minus = 0;
uint64_t ambient;
/* Note that we copy both the simple plus/minus caps here, and the full quintet from the
* Settings structure */
@ -4101,6 +4128,12 @@ static int merge_settings(Settings *settings, const char *path) {
else
arg_full_capabilities = settings->full_capabilities;
}
ambient = settings->ambient_capability;
if (!arg_settings_trusted && ambient != 0)
log_warning("Ignoring AmbientCapability= setting, file %s is not trusted.", path);
else
arg_caps_ambient |= ambient;
}
if ((arg_settings_mask & SETTING_KILL_SIGNAL) == 0 &&