core: Add support for PrivateUsers=identity
This configures an indentity mapping similar to systemd-nspawn --private-users=identity.
This commit is contained in:
+53
-4
@@ -60,6 +60,7 @@ static BUS_DEFINE_PROPERTY_GET2(property_get_ioprio_class, "i", ExecContext, exe
|
||||
static BUS_DEFINE_PROPERTY_GET2(property_get_ioprio_priority, "i", ExecContext, exec_context_get_effective_ioprio, ioprio_prio_data);
|
||||
static BUS_DEFINE_PROPERTY_GET_GLOBAL(property_get_empty_string, "s", NULL);
|
||||
static BUS_DEFINE_PROPERTY_GET_REF(property_get_private_tmp_ex, "s", PrivateTmp, private_tmp_to_string);
|
||||
static BUS_DEFINE_PROPERTY_GET_REF(property_get_private_users_ex, "s", PrivateUsers, private_users_to_string);
|
||||
static BUS_DEFINE_PROPERTY_GET_REF(property_get_syslog_level, "i", int, LOG_PRI);
|
||||
static BUS_DEFINE_PROPERTY_GET_REF(property_get_syslog_facility, "i", int, LOG_FAC);
|
||||
static BUS_DEFINE_PROPERTY_GET(property_get_cpu_affinity_from_numa, "b", ExecContext, exec_context_get_cpu_affinity_from_numa);
|
||||
@@ -1027,6 +1028,21 @@ static int property_get_private_tmp(
|
||||
return sd_bus_message_append_basic(reply, 'b', &b);
|
||||
}
|
||||
|
||||
static int property_get_private_users(
|
||||
sd_bus *bus,
|
||||
const char *path,
|
||||
const char *interface,
|
||||
const char *property,
|
||||
sd_bus_message *reply,
|
||||
void *userdata,
|
||||
sd_bus_error *error) {
|
||||
|
||||
PrivateUsers *p = ASSERT_PTR(userdata);
|
||||
int b = *p != PRIVATE_USERS_OFF;
|
||||
|
||||
return sd_bus_message_append_basic(reply, 'b', &b);
|
||||
}
|
||||
|
||||
const sd_bus_vtable bus_exec_vtable[] = {
|
||||
SD_BUS_VTABLE_START(0),
|
||||
SD_BUS_PROPERTY("Environment", "as", NULL, offsetof(ExecContext, environment), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
@@ -1149,7 +1165,8 @@ const sd_bus_vtable bus_exec_vtable[] = {
|
||||
SD_BUS_PROPERTY("ProtectKernelLogs", "b", bus_property_get_bool, offsetof(ExecContext, protect_kernel_logs), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("ProtectControlGroups", "b", bus_property_get_bool, offsetof(ExecContext, protect_control_groups), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("PrivateNetwork", "b", bus_property_get_bool, offsetof(ExecContext, private_network), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("PrivateUsers", "b", bus_property_get_bool, offsetof(ExecContext, private_users), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("PrivateUsers", "b", property_get_private_users, offsetof(ExecContext, private_users), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("PrivateUsersEx", "s", property_get_private_users_ex, offsetof(ExecContext, private_users), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("PrivateMounts", "b", bus_property_get_tristate, offsetof(ExecContext, private_mounts), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("PrivateIPC", "b", bus_property_get_bool, offsetof(ExecContext, private_ipc), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("ProtectHome", "s", property_get_protect_home, offsetof(ExecContext, protect_home), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
@@ -1857,6 +1874,41 @@ int bus_exec_context_set_transient_property(
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (streq(name, "PrivateUsers")) {
|
||||
int v;
|
||||
|
||||
r = sd_bus_message_read(message, "b", &v);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
|
||||
c->private_users = v ? PRIVATE_USERS_SELF : PRIVATE_USERS_OFF;
|
||||
(void) unit_write_settingf(u, flags, name, "%s=%s", name, yes_no(v));
|
||||
}
|
||||
|
||||
return 1;
|
||||
|
||||
} else if (streq(name, "PrivateUsersEx")) {
|
||||
const char *s;
|
||||
PrivateUsers t;
|
||||
|
||||
r = sd_bus_message_read(message, "s", &s);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
t = private_users_from_string(s);
|
||||
if (t < 0)
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid %s setting: %s", name, s);
|
||||
|
||||
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
|
||||
c->private_users = t;
|
||||
(void) unit_write_settingf(u, flags, name, "PrivateUsers=%s",
|
||||
private_users_to_string(c->private_users));
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (streq(name, "PrivateDevices"))
|
||||
return bus_set_transient_bool(u, name, &c->private_devices, message, flags, error);
|
||||
|
||||
@@ -1875,9 +1927,6 @@ int bus_exec_context_set_transient_property(
|
||||
if (streq(name, "PrivateIPC"))
|
||||
return bus_set_transient_bool(u, name, &c->private_ipc, message, flags, error);
|
||||
|
||||
if (streq(name, "PrivateUsers"))
|
||||
return bus_set_transient_bool(u, name, &c->private_users, message, flags, error);
|
||||
|
||||
if (streq(name, "NoNewPrivileges"))
|
||||
return bus_set_transient_bool(u, name, &c->no_new_privileges, message, flags, error);
|
||||
|
||||
|
||||
+38
-16
@@ -2077,7 +2077,7 @@ static int build_pass_environment(const ExecContext *c, char ***ret) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int setup_private_users(uid_t ouid, gid_t ogid, uid_t uid, gid_t gid) {
|
||||
static int setup_private_users(PrivateUsers private_users, uid_t ouid, gid_t ogid, uid_t uid, gid_t gid) {
|
||||
_cleanup_free_ char *uid_map = NULL, *gid_map = NULL;
|
||||
_cleanup_close_pair_ int errno_pipe[2] = EBADF_PAIR;
|
||||
_cleanup_close_ int unshare_ready_fd = -EBADF;
|
||||
@@ -2096,31 +2096,48 @@ static int setup_private_users(uid_t ouid, gid_t ogid, uid_t uid, gid_t gid) {
|
||||
* For unprivileged users (i.e. without capabilities), the root to root mapping is excluded. As such, it
|
||||
* does not need CAP_SETUID to write the single line mapping to itself. */
|
||||
|
||||
if (private_users == PRIVATE_USERS_OFF)
|
||||
return 0;
|
||||
|
||||
if (private_users == PRIVATE_USERS_IDENTITY) {
|
||||
uid_map = strdup("0 0 65536\n");
|
||||
if (!uid_map)
|
||||
return -ENOMEM;
|
||||
/* Can only set up multiple mappings with CAP_SETUID. */
|
||||
if (have_effective_cap(CAP_SETUID) > 0 && uid != ouid && uid_is_valid(uid))
|
||||
} else if (have_effective_cap(CAP_SETUID) > 0 && uid != ouid && uid_is_valid(uid)) {
|
||||
r = asprintf(&uid_map,
|
||||
UID_FMT " " UID_FMT " 1\n" /* Map $OUID → $OUID */
|
||||
UID_FMT " " UID_FMT " 1\n", /* Map $UID → $UID */
|
||||
ouid, ouid, uid, uid);
|
||||
else
|
||||
if (r < 0)
|
||||
return -ENOMEM;
|
||||
} else {
|
||||
r = asprintf(&uid_map,
|
||||
UID_FMT " " UID_FMT " 1\n", /* Map $OUID → $OUID */
|
||||
ouid, ouid);
|
||||
if (r < 0)
|
||||
return -ENOMEM;
|
||||
if (r < 0)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (private_users == PRIVATE_USERS_IDENTITY) {
|
||||
gid_map = strdup("0 0 65536\n");
|
||||
if (!gid_map)
|
||||
return -ENOMEM;
|
||||
/* Can only set up multiple mappings with CAP_SETGID. */
|
||||
if (have_effective_cap(CAP_SETGID) > 0 && gid != ogid && gid_is_valid(gid))
|
||||
} else if (have_effective_cap(CAP_SETGID) > 0 && gid != ogid && gid_is_valid(gid)) {
|
||||
r = asprintf(&gid_map,
|
||||
GID_FMT " " GID_FMT " 1\n" /* Map $OGID → $OGID */
|
||||
GID_FMT " " GID_FMT " 1\n", /* Map $GID → $GID */
|
||||
ogid, ogid, gid, gid);
|
||||
else
|
||||
if (r < 0)
|
||||
return -ENOMEM;
|
||||
} else {
|
||||
r = asprintf(&gid_map,
|
||||
GID_FMT " " GID_FMT " 1\n", /* Map $OGID -> $OGID */
|
||||
ogid, ogid);
|
||||
if (r < 0)
|
||||
return -ENOMEM;
|
||||
if (r < 0)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* Create a communication channel so that the parent can tell the child when it finished creating the user
|
||||
* namespace. */
|
||||
@@ -2231,7 +2248,7 @@ static int setup_private_users(uid_t ouid, gid_t ogid, uid_t uid, gid_t gid) {
|
||||
if (r != EXIT_SUCCESS) /* If something strange happened with the child, let's consider this fatal, too */
|
||||
return -EIO;
|
||||
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int create_many_symlinks(const char *root, const char *source, char **symlinks) {
|
||||
@@ -3834,7 +3851,7 @@ static bool exec_context_need_unprivileged_private_users(
|
||||
if (params->runtime_scope != RUNTIME_SCOPE_USER)
|
||||
return false;
|
||||
|
||||
return context->private_users ||
|
||||
return context->private_users != PRIVATE_USERS_OFF ||
|
||||
context->private_tmp != PRIVATE_TMP_OFF ||
|
||||
context->private_devices ||
|
||||
context->private_network ||
|
||||
@@ -4743,18 +4760,23 @@ int exec_invoke(
|
||||
/* 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 all of the other namespaces (i.e. network, mount, UTS) without a user namespace. */
|
||||
PrivateUsers pu = context->private_users;
|
||||
if (pu == PRIVATE_USERS_OFF)
|
||||
pu = PRIVATE_USERS_SELF;
|
||||
|
||||
r = setup_private_users(saved_uid, saved_gid, uid, gid);
|
||||
r = setup_private_users(pu, saved_uid, saved_gid, uid, gid);
|
||||
/* 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) {
|
||||
if (r < 0 && context->private_users != PRIVATE_USERS_OFF) {
|
||||
*exit_status = EXIT_USER;
|
||||
return log_exec_error_errno(context, params, r, "Failed to set up user namespacing for unprivileged user: %m");
|
||||
}
|
||||
if (r < 0)
|
||||
log_exec_info_errno(context, params, r, "Failed to set up user namespacing for unprivileged user, ignoring: %m");
|
||||
else
|
||||
else {
|
||||
assert(r > 0);
|
||||
userns_set_up = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (exec_needs_network_namespace(context) && runtime && runtime->shared && runtime->shared->netns_storage_socket[0] >= 0) {
|
||||
@@ -4867,8 +4889,8 @@ int exec_invoke(
|
||||
* case of mount namespaces being less privileged when the mount point list is copied from a
|
||||
* different user namespace). */
|
||||
|
||||
if (needs_sandboxing && context->private_users && !userns_set_up) {
|
||||
r = setup_private_users(saved_uid, saved_gid, uid, gid);
|
||||
if (needs_sandboxing && !userns_set_up) {
|
||||
r = setup_private_users(context->private_users, saved_uid, saved_gid, uid, gid);
|
||||
if (r < 0) {
|
||||
*exit_status = EXIT_USER;
|
||||
return log_exec_error_errno(context, params, r, "Failed to set up user namespacing: %m");
|
||||
|
||||
@@ -1894,7 +1894,7 @@ static int exec_context_serialize(const ExecContext *c, FILE *f) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = serialize_bool_elide(f, "exec-context-private-users", c->private_users);
|
||||
r = serialize_item(f, "exec-context-private-users", private_users_to_string(c->private_users));
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@@ -2778,10 +2778,9 @@ static int exec_context_deserialize(ExecContext *c, FILE *f) {
|
||||
return r;
|
||||
c->private_network = r;
|
||||
} else if ((val = startswith(l, "exec-context-private-users="))) {
|
||||
r = parse_boolean(val);
|
||||
if (r < 0)
|
||||
return r;
|
||||
c->private_users = r;
|
||||
c->private_users = private_users_from_string(val);
|
||||
if (c->private_users < 0)
|
||||
return -EINVAL;
|
||||
} else if ((val = startswith(l, "exec-context-private-ipc="))) {
|
||||
r = parse_boolean(val);
|
||||
if (r < 0)
|
||||
|
||||
+1
-1
@@ -1002,7 +1002,7 @@ void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) {
|
||||
prefix, yes_no(c->protect_clock),
|
||||
prefix, yes_no(c->protect_control_groups),
|
||||
prefix, yes_no(c->private_network),
|
||||
prefix, yes_no(c->private_users),
|
||||
prefix, private_users_to_string(c->private_users),
|
||||
prefix, protect_home_to_string(c->protect_home),
|
||||
prefix, protect_system_to_string(c->protect_system),
|
||||
prefix, yes_no(exec_context_get_effective_mount_apivfs(c)),
|
||||
|
||||
+1
-1
@@ -318,7 +318,7 @@ struct ExecContext {
|
||||
PrivateTmp private_tmp;
|
||||
bool private_network;
|
||||
bool private_devices;
|
||||
bool private_users;
|
||||
PrivateUsers private_users;
|
||||
bool private_ipc;
|
||||
bool protect_kernel_tunables;
|
||||
bool protect_kernel_modules;
|
||||
|
||||
@@ -130,7 +130,7 @@
|
||||
{{type}}.IPCNamespacePath, config_parse_unit_path_printf, 0, offsetof({{type}}, exec_context.ipc_namespace_path)
|
||||
{{type}}.LogNamespace, config_parse_log_namespace, 0, offsetof({{type}}, exec_context)
|
||||
{{type}}.PrivateNetwork, config_parse_bool, 0, offsetof({{type}}, exec_context.private_network)
|
||||
{{type}}.PrivateUsers, config_parse_bool, 0, offsetof({{type}}, exec_context.private_users)
|
||||
{{type}}.PrivateUsers, config_parse_private_users, 0, offsetof({{type}}, exec_context.private_users)
|
||||
{{type}}.PrivateMounts, config_parse_tristate, 0, offsetof({{type}}, exec_context.private_mounts)
|
||||
{{type}}.PrivateIPC, config_parse_bool, 0, offsetof({{type}}, exec_context.private_ipc)
|
||||
{{type}}.ProtectSystem, config_parse_protect_system, 0, offsetof({{type}}, exec_context.protect_system)
|
||||
|
||||
@@ -134,6 +134,7 @@ DEFINE_CONFIG_PARSE_ENUM(config_parse_exec_keyring_mode, exec_keyring_mode, Exec
|
||||
DEFINE_CONFIG_PARSE_ENUM(config_parse_protect_proc, protect_proc, ProtectProc);
|
||||
DEFINE_CONFIG_PARSE_ENUM(config_parse_proc_subset, proc_subset, ProcSubset);
|
||||
DEFINE_CONFIG_PARSE_ENUM(config_parse_private_tmp, private_tmp, PrivateTmp);
|
||||
DEFINE_CONFIG_PARSE_ENUM(config_parse_private_users, private_users, PrivateUsers);
|
||||
DEFINE_CONFIG_PARSE_ENUM(config_parse_exec_utmp_mode, exec_utmp_mode, ExecUtmpMode);
|
||||
DEFINE_CONFIG_PARSE_ENUM(config_parse_job_mode, job_mode, JobMode);
|
||||
DEFINE_CONFIG_PARSE_ENUM(config_parse_notify_access, notify_access, NotifyAccess);
|
||||
|
||||
@@ -112,6 +112,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_set_status);
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_namespace_path_strv);
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_temporary_filesystems);
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_private_tmp);
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_private_users);
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_cpu_quota);
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_allowed_cpuset);
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_protect_home);
|
||||
|
||||
@@ -3227,3 +3227,11 @@ static const char* const private_tmp_table[_PRIVATE_TMP_MAX] = {
|
||||
};
|
||||
|
||||
DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(private_tmp, PrivateTmp, PRIVATE_TMP_CONNECTED);
|
||||
|
||||
static const char* const private_users_table[_PRIVATE_USERS_MAX] = {
|
||||
[PRIVATE_USERS_OFF] = "off",
|
||||
[PRIVATE_USERS_SELF] = "self",
|
||||
[PRIVATE_USERS_IDENTITY] = "identity",
|
||||
};
|
||||
|
||||
DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(private_users, PrivateUsers, PRIVATE_USERS_SELF);
|
||||
|
||||
@@ -61,6 +61,14 @@ typedef enum PrivateTmp {
|
||||
_PRIVATE_TMP_INVALID = -EINVAL,
|
||||
} PrivateTmp;
|
||||
|
||||
typedef enum PrivateUsers {
|
||||
PRIVATE_USERS_OFF,
|
||||
PRIVATE_USERS_SELF,
|
||||
PRIVATE_USERS_IDENTITY,
|
||||
_PRIVATE_USERS_MAX,
|
||||
_PRIVATE_USERS_INVALID = -EINVAL,
|
||||
} PrivateUsers;
|
||||
|
||||
struct BindMount {
|
||||
char *source;
|
||||
char *destination;
|
||||
@@ -199,6 +207,9 @@ ProcSubset proc_subset_from_string(const char *s) _pure_;
|
||||
const char* private_tmp_to_string(PrivateTmp i) _const_;
|
||||
PrivateTmp private_tmp_from_string(const char *s) _pure_;
|
||||
|
||||
const char* private_users_to_string(PrivateUsers i) _const_;
|
||||
PrivateUsers private_users_from_string(const char *s) _pure_;
|
||||
|
||||
void bind_mount_free_many(BindMount *b, size_t n);
|
||||
int bind_mount_add(BindMount **b, size_t *n, const BindMount *item);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user