]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
nspawn: introduce --private-users-ownership=map|auto
authorLennart Poettering <lennart@poettering.net>
Tue, 27 Apr 2021 16:03:31 +0000 (18:03 +0200)
committerLennart Poettering <lennart@poettering.net>
Fri, 7 May 2021 20:44:13 +0000 (22:44 +0200)
This adds a two new values to --private-users-ownership=: "map" and
"auto".

"map" exposes the kernel 5.12 idmap feature pretty much 1:1. It fails if
the kernel or used file system doesn't support ID mapping.

"auto" is a bit smarter: if we can make ID mapping work, we'll use it,
otherwise revert back to classic chown()ing. We'll also use chown()ing
if we detect that an image is already ID shifted, both to increase
compatibility with the status quo ante, and to simplify our codepaths,
since the mappings become a lot simpler if we only have to map from zero
to something else, instead of from anything to anything else.

The short -U switch, and --private-users=pick will now imply
--private-users-ownership=auto instead of
--private-users-ownership=chown, since the new logic should be the much
better choice.

src/nspawn/nspawn-settings.c
src/nspawn/nspawn-settings.h
src/nspawn/nspawn.c

index d5a0a675efd97c62423e4bb2901a26372b0fe192..55b8c4375f78a2885e1713324eef2fdbc37b9bab 100644 (file)
@@ -872,6 +872,8 @@ DEFINE_CONFIG_PARSE_ENUM(config_parse_userns_ownership, user_namespace_ownership
 static const char *const user_namespace_ownership_table[_USER_NAMESPACE_OWNERSHIP_MAX] = {
         [USER_NAMESPACE_OWNERSHIP_OFF] = "off",
         [USER_NAMESPACE_OWNERSHIP_CHOWN] = "chown",
+        [USER_NAMESPACE_OWNERSHIP_MAP] = "map",
+        [USER_NAMESPACE_OWNERSHIP_AUTO] = "auto",
 };
 
 DEFINE_STRING_TABLE_LOOKUP(user_namespace_ownership, UserNamespaceOwnership);
index 2b453f1ee01f656c9474444d54855b17e7131440..c0ad0741ab774ab551497b00973473364a5f9df4 100644 (file)
@@ -39,6 +39,8 @@ typedef enum UserNamespaceMode {
 typedef enum UserNamespaceOwnership {
         USER_NAMESPACE_OWNERSHIP_OFF,
         USER_NAMESPACE_OWNERSHIP_CHOWN,
+        USER_NAMESPACE_OWNERSHIP_MAP,
+        USER_NAMESPACE_OWNERSHIP_AUTO,
         _USER_NAMESPACE_OWNERSHIP_MAX,
         _USER_NAMESPACE_OWNERSHIP_INVALID = -1,
 } UserNamespaceOwnership;
index 72886a070b44895eb0929e024e455d693265252a..4ef95bfa5a95c816991d99ed6fb533dc74ccd857 100644 (file)
@@ -1733,7 +1733,7 @@ static int verify_arguments(void) {
 
         if (arg_userns_ownership < 0)
                 arg_userns_ownership =
-                        arg_userns_mode == USER_NAMESPACE_PICK ? USER_NAMESPACE_OWNERSHIP_CHOWN :
+                        arg_userns_mode == USER_NAMESPACE_PICK ? USER_NAMESPACE_OWNERSHIP_AUTO :
                                                                  USER_NAMESPACE_OWNERSHIP_OFF;
 
         if (arg_start_mode == START_BOOT && arg_kill_signal <= 0)
@@ -3065,6 +3065,18 @@ static int determine_uid_shift(const char *directory) {
                                                "UID and GID base of %s don't match.", directory);
 
                 arg_uid_range = UINT32_C(0x10000);
+
+                if (arg_uid_shift != 0) {
+                        /* If the image is shifted already, then we'll fall back to classic chowning, for
+                         * compatibility (and simplicity), or refuse if mapping is explicitly requested.  */
+
+                        if (arg_userns_ownership == USER_NAMESPACE_OWNERSHIP_AUTO) {
+                                log_debug("UID base of %s is non-zero, not using UID mapping.", directory);
+                                arg_userns_ownership = USER_NAMESPACE_OWNERSHIP_CHOWN;
+                        } else if (arg_userns_ownership == USER_NAMESPACE_OWNERSHIP_MAP)
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                                       "UID base of %s is not zero, UID mapping not supported.", directory);
+                }
         }
 
         if (!userns_shift_range_valid(arg_uid_shift, arg_uid_range))
@@ -3558,6 +3570,7 @@ static int outer_child(
 
         _cleanup_strv_free_ char **os_release_pairs = NULL;
         _cleanup_close_ int fd = -1;
+        bool idmap = false;
         const char *p;
         pid_t pid;
         ssize_t l;
@@ -3661,6 +3674,32 @@ static int outer_child(
                 directory = "/run/systemd/nspawn-root";
         }
 
+        if (arg_userns_mode != USER_NAMESPACE_NO &&
+            IN_SET(arg_userns_ownership, USER_NAMESPACE_OWNERSHIP_MAP, USER_NAMESPACE_OWNERSHIP_AUTO) &&
+            arg_uid_shift != 0) {
+                r = make_mount_point(directory);
+                if (r < 0)
+                        return r;
+
+                r = remount_idmap(directory, arg_uid_shift, arg_uid_range);
+                if (r == -EINVAL || ERRNO_IS_NOT_SUPPORTED(r)) {
+                        /* This might fail because the kernel or file system doesn't support idmapping. We
+                         * can't really distinguish this nicely, nor do we have any guarantees about the
+                         * error codes we see, could be EOPNOTSUPP or EINVAL. */
+                        if (arg_userns_ownership != USER_NAMESPACE_OWNERSHIP_AUTO)
+                                return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
+                                                       "ID mapped mounts are apparently not available, sorry.");
+
+                        log_debug("ID mapped mounts are apparently not available on this kernel or for the selected file system, reverting to recursive chown()ing.");
+                        arg_userns_ownership = USER_NAMESPACE_OWNERSHIP_CHOWN;
+                } else if (r < 0)
+                        return log_error_errno(r, "Failed to set up ID mapped mounts: %m");
+                else {
+                        log_debug("ID mapped mounts available, making use of them.");
+                        idmap = true;
+                }
+        }
+
         r = setup_pivot_root(
                         directory,
                         arg_pivot_root_new,
@@ -3701,7 +3740,8 @@ static int outer_child(
                                 DISSECT_IMAGE_MOUNT_NON_ROOT_ONLY|
                                 DISSECT_IMAGE_DISCARD_ON_LOOP|
                                 DISSECT_IMAGE_USR_NO_ROOT|
-                                (arg_read_only ? DISSECT_IMAGE_READ_ONLY : DISSECT_IMAGE_FSCK|DISSECT_IMAGE_GROWFS));
+                                (arg_read_only ? DISSECT_IMAGE_READ_ONLY : DISSECT_IMAGE_FSCK|DISSECT_IMAGE_GROWFS)|
+                                (idmap ? DISSECT_IMAGE_MOUNT_IDMAPPED : 0));
                 if (r == -EUCLEAN)
                         return log_error_errno(r, "File system check for image failed: %m");
                 if (r < 0)