nsresourced: Add support for delegated ranges

We want to support the scenario where we bind mount the nsresourced
varlink socket into a container to allow nested containers where the
outer container runs in its own transient range from nsresourced but
can still allocate transient ranges for its own nested containers.

To support this use case let's add support for delegation. Delegated
ranges are allocated when allocating the primary range and are propagated
1:1 to the user namespace. We track delegated ranges in ".delegate" files
in the userns registry so that they can't be used for other range allocations.

We make one exception for delegated ranges though, if we get a request from
a user namespace that is a child of the user namespace that owns the delegated
ranges, we allow allocating from the delegated range. The parent userns already
has full ownership over the child userns, so it doesn't matter that the parent
userns and the child userns share the same range. This allows making use of
delegated ranges without having to run another copy of nsresourced inside the
parent userns to hand out from the delegated range.

To support recursive delegations, we keep track of the previous owners of the
delegated range and restore ownership to the last previous owner when the current
owner is freed.
This commit is contained in:
Daan De Meyer
2026-02-24 18:29:37 +01:00
committed by Daan De Meyer
parent 6c2d437585
commit 025439faaa
9 changed files with 1404 additions and 104 deletions
+18 -3
View File
@@ -19,7 +19,10 @@ int uid_range_add_internal(UIDRange **range, uid_t start, uid_t nr, bool coalesc
static inline int uid_range_add(UIDRange **range, uid_t start, uid_t nr) {
return uid_range_add_internal(range, start, nr, true);
}
int uid_range_add_str(UIDRange **range, const char *s);
int uid_range_add_str_full(UIDRange **range, const char *s, bool coalesce);
static inline int uid_range_add_str(UIDRange **range, const char *s) {
return uid_range_add_str_full(range, s, true);
}
int uid_range_next_lower(const UIDRange *range, uid_t *uid);
@@ -48,11 +51,23 @@ typedef enum UIDRangeUsernsMode {
_UID_RANGE_USERNS_MODE_INVALID = -EINVAL,
} UIDRangeUsernsMode;
int uid_range_load_userns(const char *path, UIDRangeUsernsMode mode, UIDRange **ret);
int uid_range_load_userns_by_fd(int userns_fd, UIDRangeUsernsMode mode, UIDRange **ret);
int uid_range_load_userns_full(const char *path, UIDRangeUsernsMode mode, bool coalesce, UIDRange **ret);
static inline int uid_range_load_userns(const char *path, UIDRangeUsernsMode mode, UIDRange **ret) {
return uid_range_load_userns_full(path, mode, true, ret);
}
int uid_range_load_userns_by_fd_full(int userns_fd, UIDRangeUsernsMode mode, bool coalesce, UIDRange **ret);
static inline int uid_range_load_userns_by_fd(int userns_fd, UIDRangeUsernsMode mode, UIDRange **ret) {
return uid_range_load_userns_by_fd_full(userns_fd, mode, true, ret);
}
bool uid_range_overlaps(const UIDRange *range, uid_t start, uid_t nr);
int uid_range_clip(UIDRange *range, uid_t min, uid_t max);
int uid_range_partition(UIDRange *range, uid_t size);
int uid_range_copy(const UIDRange *range, UIDRange **ret);
int uid_range_remove(UIDRange *range, uid_t start, uid_t size);
int uid_range_translate(const UIDRange *outside, const UIDRange *inside, uid_t uid, uid_t *ret);
int uid_map_search_root(pid_t pid, UIDRangeUsernsMode mode, uid_t *ret);
uid_t uid_range_base(const UIDRange *range);