]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
nspawn: support foreign mappings also when nspawn doing the mapping itself
authorLennart Poettering <lennart@poettering.net>
Wed, 13 Nov 2024 11:13:30 +0000 (12:13 +0100)
committerLennart Poettering <lennart@poettering.net>
Thu, 23 Jan 2025 20:48:02 +0000 (21:48 +0100)
This adds a new "foreign" value to --private-users-ownership= which is a
lot like "map", but maps from the host's foreign UID range rather than from the
host's 0.

(This has nothing much to do with making unprivileged directory-based
containers work, it's just very handy that we can run privileged
contains with such a mapping too, with an easy switch)

src/nspawn/nspawn-settings.c
src/nspawn/nspawn-settings.h
src/nspawn/nspawn.c
src/shared/mount-util.c
src/shared/mount-util.h

index 7842d93c34a9ecea997a99cef22fd81f594bc2ea..2d883e21968e37d687c4aaf5baf279a2564daf32 100644 (file)
@@ -930,10 +930,11 @@ DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(timezone_mode, TimezoneMode, TIMEZONE_AU
 DEFINE_CONFIG_PARSE_ENUM(config_parse_userns_ownership, user_namespace_ownership, UserNamespaceOwnership);
 
 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",
+        [USER_NAMESPACE_OWNERSHIP_OFF]     = "off",
+        [USER_NAMESPACE_OWNERSHIP_CHOWN]   = "chown",
+        [USER_NAMESPACE_OWNERSHIP_MAP]     = "map",
+        [USER_NAMESPACE_OWNERSHIP_FOREIGN] = "foreign",
+        [USER_NAMESPACE_OWNERSHIP_AUTO]    = "auto",
 };
 
 /* Note: while "yes" maps to "auto" here, we don't really document that, in order to make things clearer and less confusing to users. */
index 135b3dbb0a6365c60fee568b72a7184dbf7189c4..767057eeb40aa93b0f5394125848d70c85f8aa35 100644 (file)
@@ -34,9 +34,10 @@ typedef enum UserNamespaceMode {
 } UserNamespaceMode;
 
 typedef enum UserNamespaceOwnership {
-        USER_NAMESPACE_OWNERSHIP_OFF,
-        USER_NAMESPACE_OWNERSHIP_CHOWN,
-        USER_NAMESPACE_OWNERSHIP_MAP,
+        USER_NAMESPACE_OWNERSHIP_OFF,     /* do not change ownership */
+        USER_NAMESPACE_OWNERSHIP_CHOWN,   /* chown to target range */
+        USER_NAMESPACE_OWNERSHIP_MAP,     /* map from 0x00000000…0x0000FFFF range to target range */
+        USER_NAMESPACE_OWNERSHIP_FOREIGN, /* map from 0x7FFE0000…0x7FFEFFFF range to target range */
         USER_NAMESPACE_OWNERSHIP_AUTO,
         _USER_NAMESPACE_OWNERSHIP_MAX,
         _USER_NAMESPACE_OWNERSHIP_INVALID = -1,
index b7622b8db43ecc07471a4bfe11981b7e0fcadf25..929342c22852af0f60b6079eb9917194d3a58dda 100644 (file)
 #include "sysctl-util.h"
 #include "terminal-util.h"
 #include "tmpfile-util.h"
+#include "uid-classification.h"
 #include "umask-util.h"
 #include "unit-name.h"
 #include "user-util.h"
@@ -4137,9 +4138,39 @@ static int outer_child(
                 return r;
 
         if (arg_userns_mode != USER_NAMESPACE_NO &&
-            IN_SET(arg_userns_ownership, USER_NAMESPACE_OWNERSHIP_MAP, USER_NAMESPACE_OWNERSHIP_AUTO) &&
+            IN_SET(arg_userns_ownership, USER_NAMESPACE_OWNERSHIP_MAP, USER_NAMESPACE_OWNERSHIP_FOREIGN, USER_NAMESPACE_OWNERSHIP_AUTO) &&
             arg_uid_shift != 0) {
                 _cleanup_strv_free_ char **dirs = NULL;
+                RemountIdmapping mapping;
+
+                switch (arg_userns_ownership) {
+                case USER_NAMESPACE_OWNERSHIP_MAP:
+                        mapping = REMOUNT_IDMAPPING_HOST_ROOT;
+                        break;
+
+                case USER_NAMESPACE_OWNERSHIP_FOREIGN:
+                        mapping = REMOUNT_IDMAPPING_FOREIGN_WITH_HOST_ROOT;
+                        break;
+
+                case USER_NAMESPACE_OWNERSHIP_AUTO: {
+                        struct stat st;
+
+                        if (lstat(directory, &st) < 0)
+                                return log_error_errno(errno, "Failed to stat() container root directory '%s': %m", directory);
+
+                        r = stat_verify_directory(&st);
+                        if (r < 0)
+                                return log_error_errno(r, "Container root directory '%s' is not a directory: %m", directory);
+
+                        mapping = uid_is_foreign(st.st_uid) ?
+                                REMOUNT_IDMAPPING_FOREIGN_WITH_HOST_ROOT :
+                                REMOUNT_IDMAPPING_HOST_ROOT;
+                        break;
+                }
+
+                default:
+                        assert_not_reached();
+                }
 
                 if (arg_volatile_mode != VOLATILE_YES) {
                         r = strv_extend(&dirs, directory);
@@ -4158,7 +4189,13 @@ static int outer_child(
                                 return log_oom();
                 }
 
-                r = remount_idmap(dirs, arg_uid_shift, arg_uid_range, UID_INVALID, UID_INVALID, REMOUNT_IDMAPPING_HOST_ROOT);
+                r = remount_idmap(
+                                dirs,
+                                arg_uid_shift,
+                                arg_uid_range,
+                                /* host_owner= */ UID_INVALID,
+                                /* dest_owner= */ UID_INVALID,
+                                mapping);
                 if (r == -EINVAL || ERRNO_IS_NEG_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
index c7a9984528c5a818f5f14b4cd7492d08eccbe415..9e2d70b30aa5b8c24b3fb1828893fb24ae983d68 100644 (file)
@@ -1359,9 +1359,15 @@ int fd_make_mount_point(int fd) {
         return 1;
 }
 
-int make_userns(uid_t uid_shift, uid_t uid_range, uid_t source_owner, uid_t dest_owner, RemountIdmapping idmapping) {
+int make_userns(uid_t uid_shift,
+                uid_t uid_range,
+                uid_t source_owner,
+                uid_t dest_owner,
+                RemountIdmapping idmapping) {
+
         _cleanup_close_ int userns_fd = -EBADF;
         _cleanup_free_ char *line = NULL;
+        uid_t source_base = 0;
 
         /* Allocates a userns file descriptor with the mapping we need. For this we'll fork off a child
          * process whose only purpose is to give us a new user namespace. It's killed when we got it. */
@@ -1369,8 +1375,18 @@ int make_userns(uid_t uid_shift, uid_t uid_range, uid_t source_owner, uid_t dest
         if (!userns_shift_range_valid(uid_shift, uid_range))
                 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid UID range for user namespace.");
 
-        if (IN_SET(idmapping, REMOUNT_IDMAPPING_NONE, REMOUNT_IDMAPPING_HOST_ROOT)) {
-                if (asprintf(&line, UID_FMT " " UID_FMT " " UID_FMT "\n", 0u, uid_shift, uid_range) < 0)
+        switch (idmapping) {
+
+        case REMOUNT_IDMAPPING_FOREIGN_WITH_HOST_ROOT:
+                source_base = FOREIGN_UID_BASE;
+                _fallthrough_;
+
+        case REMOUNT_IDMAPPING_NONE:
+        case REMOUNT_IDMAPPING_HOST_ROOT:
+
+                if (asprintf(&line,
+                             UID_FMT " " UID_FMT " " UID_FMT "\n",
+                             source_base, uid_shift, uid_range) < 0)
                         return log_oom_debug();
 
                 /* If requested we'll include an entry in the mapping so that the host root user can make
@@ -1387,28 +1403,34 @@ int make_userns(uid_t uid_shift, uid_t uid_range, uid_t source_owner, uid_t dest
                 if (idmapping == REMOUNT_IDMAPPING_HOST_ROOT)
                         if (strextendf(&line,
                                        UID_FMT " " UID_FMT " " UID_FMT "\n",
-                                       UID_MAPPED_ROOT, 0u, 1u) < 0)
+                                       UID_MAPPED_ROOT, (uid_t) 0u, (uid_t) 1u) < 0)
                                 return log_oom_debug();
-        }
 
-        if (idmapping == REMOUNT_IDMAPPING_HOST_OWNER) {
+                break;
+
+        case REMOUNT_IDMAPPING_HOST_OWNER:
                 /* Remap the owner of the bind mounted directory to the root user within the container. This
                  * way every file written by root within the container to the bind-mounted directory will
                  * be owned by the original user from the host. All other users will remain unmapped. */
-                if (asprintf(&line, UID_FMT " " UID_FMT " " UID_FMT "\n", source_owner, uid_shift, 1u) < 0)
+                if (asprintf(&line,
+                             UID_FMT " " UID_FMT " " UID_FMT "\n",
+                             source_owner, uid_shift, (uid_t) 1u) < 0)
                         return log_oom_debug();
-        }
+                break;
 
-        if (idmapping == REMOUNT_IDMAPPING_HOST_OWNER_TO_TARGET_OWNER) {
+        case REMOUNT_IDMAPPING_HOST_OWNER_TO_TARGET_OWNER:
                 /* Remap the owner of the bind mounted directory to the owner of the target directory
                  * within the container. This way every file written by target directory owner within the
                  * container to the bind-mounted directory will be owned by the original host user.
                  * All other users will remain unmapped. */
-                if (asprintf(
-                             &line,
+                if (asprintf(&line,
                              UID_FMT " " UID_FMT " " UID_FMT "\n",
-                             source_owner, dest_owner, 1u) < 0)
+                             source_owner, dest_owner, (uid_t) 1u) < 0)
                         return log_oom_debug();
+                break;
+
+        default:
+                assert_not_reached();
         }
 
         /* We always assign the same UID and GID ranges */
index 3235a3cc81f35688733e9e5fd9cc15bdf1d4e50a..895fe2b2cdece26b21e3e7d273b0e124aa5d7cd5 100644 (file)
@@ -156,6 +156,9 @@ typedef enum RemountIdmapping {
          * to add inodes to file systems mapped this way should set this flag, but given it comes with
          * certain security implications defaults to off, and requires explicit opt-in. */
         REMOUNT_IDMAPPING_HOST_ROOT,
+        /* Much like REMOUNT_IDMAPPING_HOST_ROOT, but the source mapping is not from 0…65535 but from the
+         * foreign UID range. */
+        REMOUNT_IDMAPPING_FOREIGN_WITH_HOST_ROOT,
         /* Define a mapping from root user within the container to the owner of the bind mounted directory.
          * This ensures no root-owned files will be written in a bind-mounted directory owned by a different
          * user. No other users are mapped. */