nspawn: Add support for delegated UID ranges
We expose this via --private-users-delegate= which takes the number of ranges to delegate. On top of delegating the ranges, we also mount in the nsresourced socket and the mountfsd socket so that nested containers can use nsresourced to allocate from the delegated ranges and mountfsd to mount images. Finally, we also create /run/systemd/dissect-root with systemd-tmpfiles to make sure it is always available as unpriv users won't be able to create it themselves.
This commit is contained in:
@@ -34,6 +34,7 @@ Exec.MachineID, config_parse_id128, 0,
|
||||
Exec.WorkingDirectory, config_parse_path, 0, offsetof(Settings, working_directory)
|
||||
Exec.PivotRoot, config_parse_pivot_root, 0, 0
|
||||
Exec.PrivateUsers, config_parse_private_users, 0, 0
|
||||
Exec.PrivateUsersDelegate, config_parse_unsigned, 0, offsetof(Settings, delegate_container_ranges)
|
||||
Exec.NotifyReady, config_parse_tristate, 0, offsetof(Settings, notify_ready)
|
||||
Exec.SystemCallFilter, config_parse_syscall_filter, 0, 0
|
||||
Exec.LimitCPU, config_parse_rlimit, RLIMIT_CPU, offsetof(Settings, rlimit)
|
||||
|
||||
@@ -175,6 +175,7 @@ typedef struct Settings {
|
||||
char *pivot_root_old;
|
||||
UserNamespaceMode userns_mode;
|
||||
uid_t uid_shift, uid_range;
|
||||
unsigned delegate_container_ranges;
|
||||
int notify_ready;
|
||||
char **syscall_allow_list;
|
||||
char **syscall_deny_list;
|
||||
|
||||
+67
-2
@@ -214,6 +214,7 @@ static char **arg_property = NULL;
|
||||
static sd_bus_message *arg_property_message = NULL;
|
||||
static UserNamespaceMode arg_userns_mode; /* initialized depending on arg_privileged in run() */
|
||||
static uid_t arg_uid_shift = UID_INVALID, arg_uid_range = 0x10000U;
|
||||
static unsigned arg_delegate_container_ranges = 0;
|
||||
static UserNamespaceOwnership arg_userns_ownership = _USER_NAMESPACE_OWNERSHIP_INVALID;
|
||||
static int arg_kill_signal = 0;
|
||||
static SettingsMask arg_settings_mask = 0;
|
||||
@@ -430,6 +431,10 @@ static int help(void) {
|
||||
" --private-users-ownership=MODE\n"
|
||||
" Adjust ('chown') or map ('map') OS tree ownership\n"
|
||||
" to private UID/GID range\n"
|
||||
" --private-users-delegate=N\n"
|
||||
" Delegate N additional 64K UID/GID ranges for use\n"
|
||||
" by nested containers (requires managed user\n"
|
||||
" namespaces)\n"
|
||||
" -U Equivalent to --private-users=pick and\n"
|
||||
" --private-users-ownership=auto\n"
|
||||
"\n%3$sNetworking:%4$s\n"
|
||||
@@ -710,6 +715,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
ARG_TEMPLATE,
|
||||
ARG_PROPERTY,
|
||||
ARG_PRIVATE_USERS,
|
||||
ARG_PRIVATE_USERS_DELEGATE,
|
||||
ARG_KILL_SIGNAL,
|
||||
ARG_SETTINGS,
|
||||
ARG_CHDIR,
|
||||
@@ -794,6 +800,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
{ "private-users", optional_argument, NULL, ARG_PRIVATE_USERS },
|
||||
{ "private-users-chown", optional_argument, NULL, ARG_PRIVATE_USERS_CHOWN }, /* obsolete */
|
||||
{ "private-users-ownership",required_argument, NULL, ARG_PRIVATE_USERS_OWNERSHIP},
|
||||
{ "private-users-delegate", required_argument, NULL, ARG_PRIVATE_USERS_DELEGATE },
|
||||
{ "kill-signal", required_argument, NULL, ARG_KILL_SIGNAL },
|
||||
{ "settings", required_argument, NULL, ARG_SETTINGS },
|
||||
{ "chdir", required_argument, NULL, ARG_CHDIR },
|
||||
@@ -1249,6 +1256,14 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
arg_settings_mask |= SETTING_USERNS;
|
||||
break;
|
||||
|
||||
case ARG_PRIVATE_USERS_DELEGATE:
|
||||
r = safe_atou(optarg, &arg_delegate_container_ranges);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse --private-users-delegate= parameter: %s", optarg);
|
||||
|
||||
arg_settings_mask |= SETTING_USERNS;
|
||||
break;
|
||||
|
||||
case ARG_KILL_SIGNAL:
|
||||
if (streq(optarg, "help"))
|
||||
return DUMP_STRING_TABLE(signal, int, _NSIG);
|
||||
@@ -1648,6 +1663,9 @@ static int verify_arguments(void) {
|
||||
if (arg_userns_mode == USER_NAMESPACE_MANAGED && !arg_private_network)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Managed user namespace operation requires private networking, as otherwise /sys/ may not be mounted.");
|
||||
|
||||
if (arg_delegate_container_ranges > 0 && arg_userns_mode != USER_NAMESPACE_MANAGED)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--private-users-delegate= requires --private-users=managed.");
|
||||
|
||||
if (!(arg_clone_ns_flags & CLONE_NEWPID) ||
|
||||
!(arg_clone_ns_flags & CLONE_NEWUTS)) {
|
||||
arg_register = false;
|
||||
@@ -2881,6 +2899,43 @@ static int setup_machine_id(const char *directory) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int setup_varlink_socket(const char *directory, const char *name) {
|
||||
int r;
|
||||
|
||||
assert(directory);
|
||||
|
||||
if (arg_delegate_container_ranges == 0)
|
||||
return 0;
|
||||
|
||||
r = make_run_host(directory);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
_cleanup_free_ char *src = path_join("/run/systemd", name);
|
||||
if (!src)
|
||||
return log_oom();
|
||||
|
||||
_cleanup_free_ char *dest = path_join(directory, "/run/host", name);
|
||||
if (!dest)
|
||||
return log_oom();
|
||||
|
||||
r = touch(dest);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to create %s: %m", dest);
|
||||
|
||||
r = userns_lchown(dest, 0, 0);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to chown %s: %m", dest);
|
||||
|
||||
return mount_nofollow_verbose(
|
||||
LOG_ERR,
|
||||
src,
|
||||
dest,
|
||||
/* fstype= */ NULL,
|
||||
MS_BIND|MS_RDONLY,
|
||||
/* options= */ NULL);
|
||||
}
|
||||
|
||||
static int recursive_chown(const char *directory, uid_t shift, uid_t range) {
|
||||
int r;
|
||||
|
||||
@@ -4371,6 +4426,14 @@ static int outer_child(
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = setup_varlink_socket(directory, "io.systemd.NamespaceResource");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = setup_varlink_socket(directory, "io.systemd.MountFileSystem");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* The same stuff as the $container env var, but nicely readable for the entire payload */
|
||||
free(p);
|
||||
p = path_join(directory, "/run/host/container-manager");
|
||||
@@ -4885,6 +4948,7 @@ static int merge_settings(Settings *settings, const char *path) {
|
||||
arg_uid_shift = settings->uid_shift;
|
||||
arg_uid_range = settings->uid_range;
|
||||
arg_userns_ownership = settings->userns_ownership;
|
||||
arg_delegate_container_ranges = settings->delegate_container_ranges;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6158,10 +6222,11 @@ static int run(int argc, char *argv[]) {
|
||||
goto finish;
|
||||
}
|
||||
|
||||
userns_fd = nsresource_allocate_userns(
|
||||
userns_fd = nsresource_allocate_userns_full(
|
||||
nsresource_link,
|
||||
userns_name,
|
||||
NSRESOURCE_UIDS_64K); /* allocate 64K UIDs */
|
||||
NSRESOURCE_UIDS_64K,
|
||||
arg_delegate_container_ranges);
|
||||
if (userns_fd < 0) {
|
||||
r = log_error_errno(userns_fd, "Failed to allocate user namespace with 64K users: %m");
|
||||
goto finish;
|
||||
|
||||
Reference in New Issue
Block a user