user units: implicitly enable PrivateUsers= when sandboxing options are set
Enabling these options when not running as root requires a user namespace, so implicitly enable PrivateUsers=. This has a side effect as it changes which users are visible to the unit. However until now these options did not work at all for user units, and in practice just a handful of user units in Fedora, Debian and Ubuntu mistakenly used them (and they have been all fixed since). This fixes the long-standing confusing issue that the user and system units take the same options but the behaviour is wildly (and sometimes silently) different depending on which is which, with user units requiring manually specifiying PrivateUsers= in order for sandboxing options to actually work and not be silently ignored.
This commit is contained in:
committed by
Luca Boccassi
parent
ce963a747f
commit
6ef721cbc7
+46
-3
@@ -4400,6 +4400,44 @@ static void log_command_line(Unit *unit, const char *msg, const char *executable
|
||||
LOG_UNIT_INVOCATION_ID(unit));
|
||||
}
|
||||
|
||||
static bool exec_context_need_unprivileged_private_users(const ExecContext *context, const Manager *manager) {
|
||||
assert(context);
|
||||
assert(manager);
|
||||
|
||||
/* These options require PrivateUsers= when used in user units, as we need to be in a user namespace
|
||||
* to have permission to enable them when not running as root. If we have effective CAP_SYS_ADMIN
|
||||
* (system manager) then we have privileges and don't need this. */
|
||||
if (MANAGER_IS_SYSTEM(manager))
|
||||
return false;
|
||||
|
||||
return context->private_users ||
|
||||
context->private_tmp ||
|
||||
context->private_devices ||
|
||||
context->private_network ||
|
||||
context->network_namespace_path ||
|
||||
context->private_ipc ||
|
||||
context->ipc_namespace_path ||
|
||||
context->private_mounts ||
|
||||
context->mount_apivfs ||
|
||||
context->n_bind_mounts > 0 ||
|
||||
context->n_temporary_filesystems > 0 ||
|
||||
context->root_directory ||
|
||||
!strv_isempty(context->extension_directories) ||
|
||||
context->protect_system != PROTECT_SYSTEM_NO ||
|
||||
context->protect_home != PROTECT_HOME_NO ||
|
||||
context->protect_kernel_tunables ||
|
||||
context->protect_kernel_modules ||
|
||||
context->protect_kernel_logs ||
|
||||
context->protect_control_groups ||
|
||||
context->protect_clock ||
|
||||
context->protect_hostname ||
|
||||
!strv_isempty(context->read_write_paths) ||
|
||||
!strv_isempty(context->read_only_paths) ||
|
||||
!strv_isempty(context->inaccessible_paths) ||
|
||||
!strv_isempty(context->exec_paths) ||
|
||||
!strv_isempty(context->no_exec_paths);
|
||||
}
|
||||
|
||||
static int exec_child(
|
||||
Unit *unit,
|
||||
const ExecCommand *command,
|
||||
@@ -5032,17 +5070,22 @@ static int exec_child(
|
||||
}
|
||||
}
|
||||
|
||||
if (needs_sandboxing && context->private_users && have_effective_cap(CAP_SYS_ADMIN) <= 0) {
|
||||
if (needs_sandboxing && exec_context_need_unprivileged_private_users(context, unit->manager)) {
|
||||
/* If we're unprivileged, set up the user namespace first to enable use of the other namespaces.
|
||||
* Users with CAP_SYS_ADMIN can set up user namespaces last because they will be able to
|
||||
* set up the all of the other namespaces (i.e. network, mount, UTS) without a user namespace. */
|
||||
|
||||
userns_set_up = true;
|
||||
r = setup_private_users(saved_uid, saved_gid, uid, gid);
|
||||
if (r < 0) {
|
||||
/* If it was requested explicitly and we can't set it up, fail early. Otherwise, continue and let
|
||||
* the actual requested operations fail (or silently continue). */
|
||||
if (r < 0 && context->private_users) {
|
||||
*exit_status = EXIT_USER;
|
||||
return log_unit_error_errno(unit, r, "Failed to set up user namespacing for unprivileged user: %m");
|
||||
}
|
||||
if (r < 0)
|
||||
log_unit_info_errno(unit, r, "Failed to set up user namespacing for unprivileged user, ignoring: %m");
|
||||
else
|
||||
userns_set_up = true;
|
||||
}
|
||||
|
||||
if (exec_needs_network_namespace(context) && runtime && runtime->shared && runtime->shared->netns_storage_socket[0] >= 0) {
|
||||
|
||||
Reference in New Issue
Block a user