From 87d5e4f2866889eaeacc47daec75aab41eeed54d Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sat, 2 Dec 2017 12:48:31 +0100 Subject: [PATCH 01/12] build-sys: make the dynamic UID range, and the container UID range configurable Also, export these ranges in our pkg-config files. --- meson.build | 24 +++++++++++++++++++++--- meson_options.txt | 12 ++++++++++++ src/basic/user-util.h | 7 ------- src/core/systemd.pc.in | 4 ++++ src/nspawn/nspawn-def.h | 6 ------ src/nspawn/nspawn.c | 4 ++-- 6 files changed, 39 insertions(+), 18 deletions(-) diff --git a/meson.build b/meson.build index edfa02afbb..ea13dff44a 100644 --- a/meson.build +++ b/meson.build @@ -614,9 +614,6 @@ conf.set('SYSTEM_UID_MAX', system_uid_max) substs.set('systemuidmax', system_uid_max) message('maximum system UID is @0@'.format(system_uid_max)) -conf.set_quoted('NOBODY_USER_NAME', get_option('nobody-user')) -conf.set_quoted('NOBODY_GROUP_NAME', get_option('nobody-group')) - system_gid_max = get_option('system-gid-max') if system_gid_max == '' system_gid_max = run_command( @@ -629,6 +626,23 @@ conf.set('SYSTEM_GID_MAX', system_gid_max) substs.set('systemgidmax', system_gid_max) message('maximum system GID is @0@'.format(system_gid_max)) +dynamic_uid_min = get_option('dynamic-uid-min').to_int() +dynamic_uid_max = get_option('dynamic-uid-max').to_int() +conf.set('DYNAMIC_UID_MIN', dynamic_uid_min) +conf.set('DYNAMIC_UID_MAX', dynamic_uid_max) +substs.set('dynamicuidmin', dynamic_uid_min) +substs.set('dynamicuidmax', dynamic_uid_max) + +container_uid_base_min = get_option('container-uid-base-min').to_int() +container_uid_base_max = get_option('container-uid-base-max').to_int() +conf.set('CONTAINER_UID_BASE_MIN', container_uid_base_min) +conf.set('CONTAINER_UID_BASE_MAX', container_uid_base_max) +substs.set('containeruidbasemin', container_uid_base_min) +substs.set('containeruidbasemax', container_uid_base_max) + +conf.set_quoted('NOBODY_USER_NAME', get_option('nobody-user')) +conf.set_quoted('NOBODY_GROUP_NAME', get_option('nobody-group')) + tty_gid = get_option('tty-gid') conf.set('TTY_GID', tty_gid) substs.set('TTY_GID', tty_gid) @@ -2506,6 +2520,10 @@ status = [ 'users GID: @0@'.format(users_gid), 'maximum system UID: @0@'.format(system_uid_max), 'maximum system GID: @0@'.format(system_gid_max), + 'minimum dynamic UID: @0@'.format(dynamic_uid_min), + 'maximum dynamic UID: @0@'.format(dynamic_uid_max), + 'minimum container UID base: @0@'.format(container_uid_base_min), + 'maximum container UID base: @0@'.format(container_uid_base_max), '/dev/kvm access mode: @0@'.format(get_option('dev-kvm-mode')), 'render group access mode: @0@'.format(get_option('group-render-mode')), 'certificate root directory: @0@'.format(get_option('certificate-root')), diff --git a/meson_options.txt b/meson_options.txt index 8c03b0c6de..f0c0506ff1 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -147,6 +147,18 @@ option('system-uid-max', type : 'string', description : 'maximum system UID') option('system-gid-max', type : 'string', description : 'maximum system GID') +option('dynamic-uid-min', type : 'string', + description : 'minimum dynamic UID', + value : '61184') # That's → 0x0000EF00 in hex +option('dynamic-uid-max', type : 'string', + description : 'maximum dynamic UID', + value : '65519') # That's → 0x0000FFEF in hex +option('container-uid-base-min', type : 'string', + description : 'minimum container UID base', + value : '524288') # That's → 0x00080000 in hex +option('container-uid-base-max', type : 'string', + description : 'maximum container UID base', + value : '1878982656') # That's → 0x6FFF0000 in hex option('tty-gid', type : 'string', description : 'the numeric GID of the "tty" group', value : '5') diff --git a/src/basic/user-util.h b/src/basic/user-util.h index f13367893e..855813cc76 100644 --- a/src/basic/user-util.h +++ b/src/basic/user-util.h @@ -60,13 +60,6 @@ int take_etc_passwd_lock(const char *root); #define UID_INVALID ((uid_t) -1) #define GID_INVALID ((gid_t) -1) -/* Let's pick a UIDs within the 16bit range, so that we are compatible with containers using 16bit - * user namespacing. At least on Fedora normal users are allocated until UID 60000, hence do not - * allocate from below this. Also stay away from the upper end of the range as that is often used - * for overflow/nobody users. */ -#define DYNAMIC_UID_MIN ((uid_t) UINT32_C(0x0000EF00)) -#define DYNAMIC_UID_MAX ((uid_t) UINT32_C(0x0000FFEF)) - static inline bool uid_is_dynamic(uid_t uid) { return DYNAMIC_UID_MIN <= uid && uid <= DYNAMIC_UID_MAX; } diff --git a/src/core/systemd.pc.in b/src/core/systemd.pc.in index 22e8cbda8b..655773ea8a 100644 --- a/src/core/systemd.pc.in +++ b/src/core/systemd.pc.in @@ -29,6 +29,10 @@ modulesloaddir=@modulesloaddir@ catalogdir=@catalogdir@ systemuidmax=@systemuidmax@ systemgidmax=@systemgidmax@ +dynamicuidmin=@dynamicuidmin@ +dynamicuidmax=@dynamicuidmax@ +containeruidbasemin=@containeruidbasemin@ +containeruidbasemax=@containeruidbasemax@ Name: systemd Description: systemd System and Service Manager diff --git a/src/nspawn/nspawn-def.h b/src/nspawn/nspawn-def.h index fc3c94064c..43a19d84f5 100644 --- a/src/nspawn/nspawn-def.h +++ b/src/nspawn/nspawn-def.h @@ -21,12 +21,6 @@ #include -/* Note that devpts's gid= parameter parses GIDs as signed values, hence we stay away from the upper half of the 32bit - * UID range here. We leave a bit of room at the lower end and a lot of room at the upper end, so that other subsystems - * may have their own allocation ranges too. */ -#define UID_SHIFT_PICK_MIN ((uid_t) UINT32_C(0x00080000)) -#define UID_SHIFT_PICK_MAX ((uid_t) UINT32_C(0x6FFF0000)) - /* While we are chmod()ing a directory tree, we set the top-level UID base to this "busy" base, so that we can always * recognize trees we are were chmod()ing recursively and got interrupted in */ #define UID_BUSY_BASE ((uid_t) UINT32_C(0xFFFE0000)) diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index ccf5418ceb..4b82345d08 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -2862,7 +2862,7 @@ static int uid_shift_pick(uid_t *shift, LockFile *ret_lock_file) { if (--n_tries <= 0) return -EBUSY; - if (candidate < UID_SHIFT_PICK_MIN || candidate > UID_SHIFT_PICK_MAX) + if (candidate < CONTAINER_UID_BASE_MIN || candidate > CONTAINER_UID_BASE_MAX) goto next; if ((candidate & UINT32_C(0xFFFF)) != 0) goto next; @@ -2904,7 +2904,7 @@ static int uid_shift_pick(uid_t *shift, LockFile *ret_lock_file) { } else random_bytes(&candidate, sizeof(candidate)); - candidate = (candidate % (UID_SHIFT_PICK_MAX - UID_SHIFT_PICK_MIN)) + UID_SHIFT_PICK_MIN; + candidate = (candidate % (CONTAINER_UID_BASE_MAX - CONTAINER_UID_BASE_MIN)) + CONTAINER_UID_BASE_MIN; candidate &= (uid_t) UINT32_C(0xFFFF0000); } } From 399725532df4aedc30119f920551bdbcbb01f13a Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sat, 2 Dec 2017 12:49:22 +0100 Subject: [PATCH 02/12] doc: add a document briefly explaining UID/GID range assumptions we make --- UIDS-GIDS.md | 242 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 242 insertions(+) create mode 100644 UIDS-GIDS.md diff --git a/UIDS-GIDS.md b/UIDS-GIDS.md new file mode 100644 index 0000000000..d44d93d144 --- /dev/null +++ b/UIDS-GIDS.md @@ -0,0 +1,242 @@ +# Users, Groups, UIDs and GIDs on `systemd` systems + +Here's a summary of the requirements `systemd` (and Linux) make on UID/GID +assignments and their ranges. + +Note that while in theory UIDs and GIDs are orthogonal concepts they really +aren't IRL. With that in mind, when we discuss UIDs below it should be assumed +that whatever we say about UIDs applies to GIDs in mostly the same way, and all +the special assignments and ranges for UIDs always have mostly the same +validity for GIDs too. + +## Special Linux UIDs + +In theory, the range of the C type `uid_t` is 32bit wide on Linux, +i.e. 0…4294967295. However, four UIDs are special on Linux: + +1. 0 → The `root` super-user + +2. 65534 → The `nobody` UID, also called the "overflow" UID or similar. It's + where various subsystems map unmappable users to, for example NFS or user + namespacing. (The latter can be changed with a sysctl during runtime, but + that's not supported on `systemd`. If you do change it you void your + warranty.) Because Fedora is a bit confused the `nobody` user is called + `nfsnobody` there (and they have a different `nobody` user at UID 99). I + hope this will be corrected eventually though. (Also, some distributions + call the `nobody` group `nogroup`. I wish they didn't.) + +3. 4294967295, aka "32bit `(uid_t) -1`" → This UID is not a valid user ID, as + setresuid(), chown() and friends treat -1 as a special request to not change + the UID of the process/file. This UID is hence not available for assignment + to users in the user database. + +4. 65535, aka "16bit `(uid_t) -1`" → Once upon a time `uid_t` used to be 16bit, and + programs compiled for that would hence assume that `(uid_t) -1` is 65535. This + UID is hence not usable either. + +The `nss-systemd` glibc NSS module will synthesize user database records for +the UIDs 0 and 65534 if the system user database doesn't list them. This means +that any system where this module is enabled works to some minimal level +without `/etc/passwd`. + +## Special Distribution UID ranges + +Distributions generally split the available UID range in two: + +1. 1…999 → System users. These are users that do not map to actual "human" + users, but are used as security identities for system daemons, to implement + privilege separation and run system daemons with minimal privileges. + +2. 1000…65533 and 65536…4294967294 → Everything else, i.e. regular (human) users. + +Note that most distributions allow changing the boundary between system and +regular users, even during runtime as user configuration. Moreover, some older +systems placed the boundary at 499/500, or even 99/100. In `systemd`, the +boundary is configurable only during compilation time, as this should be a +decision for distribution builders, not for users. Moreover, we strongly +discourage downstreams to change the boundary from the upstream default of +999/1000. + +Also note that programs such as `adduser` tend to allocate from a subset of the +available regular user range only, usually 1000..60000. And it's also usually +user-configurable, too. + +Note that systemd requires that system users and groups are resolvable without +networking available — a requirement that is not made for regular users. This +means regular users may be stored in remote LDAP or NIS databases, but system +users may not (except when there's a consistent local cache kept, that is +available during earliest boot, including in the initial RAM disk). + +## Special `systemd` GIDs + +`systemd` defines no special UIDs beyond what Linux already defines (see +above). However, it does define some special group/GID assignments, which are +primarily used for `systemd-udevd`'s device management. The precise list of the +currently defined groups is found in this `sysusers.d` snippet: +[basic.conf](https://raw.githubusercontent.com/systemd/systemd/master/sysusers.d/basic.conf.in) + +It's strongly recommended that downstream distributions include these groups in +their default group databases. + +Note that the actual GID numbers assigned to these groups do not have to be +constant beyond a specific system. There's one exception however: the `tty` +group must have the GID 5. That's because it must be encoded in the `devpts` +mount parameters during earliest boot, at a time where NSS lookups are not +possible. (Note that the actual GID can be changed during `systemd` build time, +but downstreams are strongly advised against doing that.) + +## Special `systemd` UID ranges + +`systemd` defines a number of special UID ranges: + +1. 61184…65519 → UIDs for dynamic users are allocated from this range (see the + `DynamicUser=` documentation in + [`systemd.exec(5)`](https://www.freedesktop.org/software/systemd/man/systemd.exec.html)). This + range has been chosen so that it is below the 16bit boundary (i.e. below + 65535), in order to provide compatibility with container environments that + assign a 64K range of UIDs to containers using user namespacing. This range + is above the 60000 boundary, so that its allocations are unlikely to be + affected by `adduser` allocations (see above). And we leave some room + upwards for other purposes. (And if you wonder why precisely these numbers: + if you write them in hexadecimal, they might make more sense: 0xEF00 and + 0xFFEF). The `nss-systemd` module will synthesize user records implicitly + for all currently allocated dynamic users from this range. Thus, NSS-based + user record resolving works correctly without those users being in + `/etc/passwd`. + +2. 524288…1879048191 → UID range for `systemd-nspawn`'s automatic allocation of + per-container UID ranges. When the `--private-users=pick` switch is used (or + `-U`) then it will automatically find a so far unused 16bit subrange of this + range and assign it to the container. The range is picked so that the upper + 16bit of the 32bit UIDs are constant for all users of the container, while + the lower 16bit directly encode the 65536 UIDs assigned to the + container. This mode of allocation means that the upper 16bit of any UID + assigned to a container are kind of a "container ID", while the lower 16bit + directly expose the container's own UID numbers. If you wonder why precisely + these numbers, consider them in hexadecimal: 0x00080000…0x6FFFFFFF. This + range is above the 16bit boundary. Moreover it's below the 31bit boundary, + as some broken code (specifically: the kernel's `devpts` file system) + erroneously considers UIDs signed integers, and hence can't deal with values + above 2^31. The `nss-mymachines` glibc NSS module will synthesize user + database records for all UIDs assigned to a running container from this + range. + +Note for both allocation ranges: when an UID allocation takes place NSS is +checked for collisions first, and a different UID is picked if an entry is +found. Thus, the user database is used as synchronization mechanism to ensure +exclusive ownership of UIDs and UID ranges. To ensure compatibility with other +subsystems allocating from the same ranges it is hence essential that they +ensure that whatever they pick shows up in the user/group databases, either by +providing an NSS module, or by adding entries directly to `/etc/passwd` and +`/etc/group`. For performance reasons, do note that `systemd-nspawn` will only +do an NSS check for the first UID of the range it allocates, not all 65536 of +them. Also note that while the allocation logic is operating, the glibc +`lckpwdf()` user database lock is taken, in order to make this logic race-free. + +## Figuring out the system's UID boundaries + +The most important boundaries of the local system may be queried with +`pkg-config`: + +``` +$ pkg-config --variable=systemuidmax systemd +999 +$ pkg-config --variable=dynamicuidmin systemd +61184 +$ pkg-config --variable=dynamicuidmax systemd +65519 +$ pkg-config --variable=containeruidbasemin systemd +524288 +$ pkg-config --variable=containeruidbasemax systemd +1878982656 +``` + +(Note that the latter encodes the maximum UID *base* `systemd-nspawn` might +pick — given that 64K UIDs are assigned to each container according to this +allocation logic, the maximum UID used for this range is hence +1878982656+65535=1879048191.) + +Note that systemd does not make any of these values runtime-configurable. All +these boundaries are chosen during build time. That said, the system UID/GID +boundary is traditionally configured in /etc/login.defs, though systemd won't +look there during runtime. + +## Considerations for container managers + +If you hack on a container manager, and wonder how and how many UIDs best to +assign to your containers, here are a few recommendations: + +1. Definitely, don't assign less than 65536 UIDs/GIDs. After all the `nobody` +user has magic properties, and hence should be available in your container, and +given that it's assigned the UID 65534, you should really cover the full 16bit +range in your container. Note that systemd will — as mentioned — synthesize +user records for the `nobody` user, and assumes its availability in various +other parts of its codebase, too, hence assigning fewer users means you lose +compatibility with running systemd code inside your container. And most likely +other packages make similar restrictions. + +2. While it's fine to assign more than 65536 UIDs/GIDs to a container, there's +most likely not much value in doing so, as Linux distributions won't use the +higher ranges by default (as mentioned neither `adduser` nor `systemd`'s +dynamic user concept allocate from above the 16bit range). Unless you actively +care for nested containers, it's hence probably a good idea to allocate exactly +65536 UIDs per container, and neither less nor more. A pretty side-effect is +that by doing so, you expose the same number of UIDs per container as Linux 2.2 +supported for the whole system, back in the days. + +3. Consider allocating UID ranges for containers so that the first UID you +assign has the lower 16bits all set to zero. That way, the upper 16bits become +a container ID of some kind, while the lower 16bits directly encode the +internal container UID. This is the way `systemd-nspawn` allocates UID ranges +(see above). Following this allocation logic ensures best compability with +`systemd-nspawn` and all other container managers following the scheme, as it +is sufficient then to check NSS for the first UID you pick regarding conflicts, +as that's what they do, too. Moreover, it makes `chown()`ing container file +system trees nicely robust to interruptions: as the external UID encodes the +internal UID in a fixed way, it's very easy to adjust the container's base UID +without the need to know the original base UID: to change the container base, +just mask away the upper 16bit, and insert the upper 16bit of the new container +base instead. Here are the easy conversions to derive the internal UID, the +external UID, and the container base UID from each other: + + ``` + INTERNAL_UID = EXTERNAL_UID & 0x0000FFFF + CONTAINER_BASE_UID = EXTERNAL_UID & 0xFFFF0000 + EXTERNAL_UID = INTERNAL_UID | CONTAINER_BASE_UID + ``` + +4. When picking a UID range for containers, make sure to check NSS first, with +a simple `getpwuid()` call: if there's already a user record for the first UID +you want to pick, then it's already in use: pick a different one. Wrap that +call in a `lckpwdf()` + `ulckpwdf()` pair, to make allocation +race-free. Provide an NSS module that makes all UIDs you end up taking show up +in the user database, and make sure that the NSS module returns up-to-date +information before you release the lock, so that other system components can +safely use the NSS user database as allocation check, too. Note that if you +follow this scheme no changes to `/etc/passwd` need to be made, thus minimizing +the artifacts the container manager persistently leaves in the system. + +## Summary + +| UID/GID | Purpose | Defined By | Listed in | +|-----------------------|-----------------------|---------------|-------------------------------| +| 0 | `root` user | Linux | `/etc/passwd` + `nss-systemd` | +| 1…4 | System users | Distributions | `/etc/passwd` | +| 5 | `tty` group | `systemd` | `/etc/passwd` | +| 6…999 | System users | Distributions | `/etc/passwd` | +| 1000…60000 | Regular users | Distributions | `/etc/passwd` + LDAP/NIS/… | +| 60001…61183 | Unused | | | +| 61184…65519 | Dynamic service users | `systemd` | `nss-systemd` | +| 65520…65533 | Unused | | | +| 65534 | `nobody` user | Linux | `/etc/passwd` + `nss-systemd` | +| 65535 | 16bit `(uid_t) -1` | Linux | | +| 65536…524287 | Unused | | | +| 524288…1879048191 | Container UID ranges | `systemd` | `nss-mymachines` | +| 1879048192…4294967294 | Unused | | | +| 4294967295 | 32bit `(uid_t) -1` | Linux | | + +Note that "Unused" in the table above doesn't meant that these ranges are +really unused. It just means that these ranges have no well-established +pre-defined purposes between Linux, generic low-level distributions and +`systemd`. There might very well be other packages that allocate from these +ranges. From ece877d434d3ef8ff096a6a130cfd28e6f643cd2 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sat, 2 Dec 2017 12:59:21 +0100 Subject: [PATCH 03/12] user-util: add new uid_is_system() helper This adds uid_is_system() and gid_is_system(), similar in style to uid_is_dynamic(). That a helper like this is useful is illustrated by the fact that test-condition.c didn't get the check right so far, which this patch fixes. --- src/basic/user-util.h | 8 ++++++++ src/coredump/coredump.c | 2 +- src/journal/journald-server.c | 4 ++-- src/login/logind-user.c | 2 +- src/nss-systemd/nss-systemd.c | 4 ++-- src/shared/condition.c | 2 +- src/test/test-condition.c | 2 +- 7 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/basic/user-util.h b/src/basic/user-util.h index 855813cc76..6de68e2d2c 100644 --- a/src/basic/user-util.h +++ b/src/basic/user-util.h @@ -64,6 +64,14 @@ static inline bool uid_is_dynamic(uid_t uid) { return DYNAMIC_UID_MIN <= uid && uid <= DYNAMIC_UID_MAX; } +static inline bool uid_is_system(uid_t uid) { + return uid <= SYSTEM_UID_MAX; +} + +static inline bool gid_is_system(gid_t gid) { + return gid <= SYSTEM_GID_MAX; +} + /* The following macros add 1 when converting things, since UID 0 is a valid UID, while the pointer * NULL is special */ #define PTR_TO_UID(p) ((uid_t) (((uintptr_t) (p))-1)) diff --git a/src/coredump/coredump.c b/src/coredump/coredump.c index d3533790a1..eda7d78be2 100644 --- a/src/coredump/coredump.c +++ b/src/coredump/coredump.c @@ -165,7 +165,7 @@ static int fix_acl(int fd, uid_t uid) { assert(fd >= 0); - if (uid <= SYSTEM_UID_MAX) + if (uid_is_system(uid)) return 0; /* Make sure normal users can read (but not write or delete) diff --git a/src/journal/journald-server.c b/src/journal/journald-server.c index 46bf2eb310..cc45591c09 100644 --- a/src/journal/journald-server.c +++ b/src/journal/journald-server.c @@ -248,7 +248,7 @@ static void server_add_acls(JournalFile *f, uid_t uid) { assert(f); #if HAVE_ACL - if (uid <= SYSTEM_UID_MAX) + if (uid_is_system(uid)) return; r = add_acls_for_user(f->fd, uid); @@ -406,7 +406,7 @@ static JournalFile* find_journal(Server *s, uid_t uid) { if (s->runtime_journal) return s->runtime_journal; - if (uid <= SYSTEM_UID_MAX || uid_is_dynamic(uid)) + if (uid_is_system(uid) || uid_is_dynamic(uid)) return s->system_journal; r = sd_id128_get_machine(&machine); diff --git a/src/login/logind-user.c b/src/login/logind-user.c index 43a9f58adc..01469438b1 100644 --- a/src/login/logind-user.c +++ b/src/login/logind-user.c @@ -617,7 +617,7 @@ int user_finalize(User *u) { * cases, as we shouldn't accidentally remove a system service's IPC objects while it is running, just because * a cronjob running as the same user just finished. Hence: exclude system users generally from IPC clean-up, * and do it only for normal users. */ - if (u->manager->remove_ipc && u->uid > SYSTEM_UID_MAX) { + if (u->manager->remove_ipc && !uid_is_system(u->uid)) { k = clean_ipc_by_uid(u->uid); if (k < 0) r = k; diff --git a/src/nss-systemd/nss-systemd.c b/src/nss-systemd/nss-systemd.c index d6de0a9878..d1a369672e 100644 --- a/src/nss-systemd/nss-systemd.c +++ b/src/nss-systemd/nss-systemd.c @@ -251,7 +251,7 @@ enum nss_status _nss_systemd_getpwuid_r( } } - if (uid <= SYSTEM_UID_MAX) + if (uid_is_system(uid)) goto not_found; if (getenv_bool_secure("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0) @@ -463,7 +463,7 @@ enum nss_status _nss_systemd_getgrgid_r( } } - if (gid <= SYSTEM_GID_MAX) + if (gid_is_system(gid)) goto not_found; if (getenv_bool_secure("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0) diff --git a/src/shared/condition.c b/src/shared/condition.c index f1e914cb2d..3f32dfb7b6 100644 --- a/src/shared/condition.c +++ b/src/shared/condition.c @@ -157,7 +157,7 @@ static int condition_test_user(Condition *c) { return id == getuid() || id == geteuid(); if (streq("@system", c->parameter)) - return getuid() <= SYSTEM_UID_MAX || geteuid() <= SYSTEM_UID_MAX; + return uid_is_system(getuid()) || uid_is_system(geteuid()); username = getusername_malloc(); if (!username) diff --git a/src/test/test-condition.c b/src/test/test-condition.c index 31e08b2318..d43db3a7cd 100644 --- a/src/test/test-condition.c +++ b/src/test/test-condition.c @@ -391,7 +391,7 @@ static void test_condition_test_user(void) { assert_se(condition); r = condition_test(condition); log_info("ConditionUser=@system → %i", r); - if (getuid() < SYSTEM_UID_MAX || geteuid() < SYSTEM_UID_MAX) + if (uid_is_system(getuid()) || uid_is_system(geteuid())) assert_se(r > 0); else assert_se(r == 0); From 4e72397b00ab23607635ce6725573554f235101a Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sat, 2 Dec 2017 13:02:44 +0100 Subject: [PATCH 04/12] coredump,journal: do not do ACL magic for processes of dynamic UIDs Dynamic UIDs should be treated like system users in this regard. --- src/coredump/coredump.c | 2 +- src/journal/journald-server.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/coredump/coredump.c b/src/coredump/coredump.c index eda7d78be2..cf56a8ad26 100644 --- a/src/coredump/coredump.c +++ b/src/coredump/coredump.c @@ -165,7 +165,7 @@ static int fix_acl(int fd, uid_t uid) { assert(fd >= 0); - if (uid_is_system(uid)) + if (uid_is_system(uid) || uid_is_dynamic(uid)) return 0; /* Make sure normal users can read (but not write or delete) diff --git a/src/journal/journald-server.c b/src/journal/journald-server.c index cc45591c09..06c64a46e2 100644 --- a/src/journal/journald-server.c +++ b/src/journal/journald-server.c @@ -248,7 +248,7 @@ static void server_add_acls(JournalFile *f, uid_t uid) { assert(f); #if HAVE_ACL - if (uid_is_system(uid)) + if (uid_is_system(uid) || uid_is_dynamic(uid)) return; r = add_acls_for_user(f->fd, uid); From 834382779304c6f8b1994dcafaa748d798f533b6 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sat, 2 Dec 2017 13:07:18 +0100 Subject: [PATCH 05/12] nss-systemd: tweak checks when we consult PID 1 for dynamic UID/GID lookups Instead of contacting PID 1 for dynamic UID/GID lookups for all UIDs/GIDs that do not qualify as "system" do the more precise check instead: check if they actually qualify for the "dynamic" range. --- src/basic/user-util.h | 4 ++++ src/nss-systemd/nss-systemd.c | 5 ++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/basic/user-util.h b/src/basic/user-util.h index 6de68e2d2c..d148d5819e 100644 --- a/src/basic/user-util.h +++ b/src/basic/user-util.h @@ -64,6 +64,10 @@ static inline bool uid_is_dynamic(uid_t uid) { return DYNAMIC_UID_MIN <= uid && uid <= DYNAMIC_UID_MAX; } +static inline bool gid_is_dynamic(gid_t gid) { + return uid_is_dynamic((uid_t) gid); +} + static inline bool uid_is_system(uid_t uid) { return uid <= SYSTEM_UID_MAX; } diff --git a/src/nss-systemd/nss-systemd.c b/src/nss-systemd/nss-systemd.c index d1a369672e..1852cf41cb 100644 --- a/src/nss-systemd/nss-systemd.c +++ b/src/nss-systemd/nss-systemd.c @@ -251,7 +251,7 @@ enum nss_status _nss_systemd_getpwuid_r( } } - if (uid_is_system(uid)) + if (!uid_is_dynamic(uid)) goto not_found; if (getenv_bool_secure("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0) @@ -463,7 +463,7 @@ enum nss_status _nss_systemd_getgrgid_r( } } - if (gid_is_system(gid)) + if (!gid_is_dynamic(gid)) goto not_found; if (getenv_bool_secure("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0) @@ -500,7 +500,6 @@ enum nss_status _nss_systemd_getgrgid_r( direct_lookup: if (bypass > 0) { - r = direct_lookup_uid(gid, &direct); if (r == -ENOENT) goto not_found; From 3a664727fa95e9683da33770e88aaaa1a3183cc6 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 4 Dec 2017 17:06:56 +0100 Subject: [PATCH 06/12] user-util: add UID_NOBODY defines that resolve to (uid_t) 65534 We use it all over the place, let's add a #define for it. Makes things easier greppable, and more explanatory I think. --- src/basic/user-util.h | 3 +++ src/machine/machine.c | 3 ++- src/nss-mymachines/nss-mymachines.c | 4 ++-- src/nss-systemd/nss-systemd.c | 6 +++--- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/basic/user-util.h b/src/basic/user-util.h index d148d5819e..79adf91ee9 100644 --- a/src/basic/user-util.h +++ b/src/basic/user-util.h @@ -60,6 +60,9 @@ int take_etc_passwd_lock(const char *root); #define UID_INVALID ((uid_t) -1) #define GID_INVALID ((gid_t) -1) +#define UID_NOBODY ((uid_t) 65534U) +#define GID_NOBODY ((gid_t) 65534U) + static inline bool uid_is_dynamic(uid_t uid) { return DYNAMIC_UID_MIN <= uid && uid <= DYNAMIC_UID_MAX; } diff --git a/src/machine/machine.c b/src/machine/machine.c index 10e379238c..3d3c7cb6b8 100644 --- a/src/machine/machine.c +++ b/src/machine/machine.c @@ -43,6 +43,7 @@ #include "string-table.h" #include "terminal-util.h" #include "unit-name.h" +#include "user-util.h" #include "util.h" Machine* machine_new(Manager *manager, MachineClass class, const char *name) { @@ -656,7 +657,7 @@ int machine_get_uid_shift(Machine *m, uid_t *ret) { if (uid_base != 0) return -ENXIO; /* Insist that at least the nobody user is mapped, everything else is weird, and hence complex, and we don't support it */ - if (uid_range < (uid_t) 65534U) + if (uid_range < UID_NOBODY) return -ENXIO; /* If there's more than one line, then we don't support this mapping. */ diff --git a/src/nss-mymachines/nss-mymachines.c b/src/nss-mymachines/nss-mymachines.c index ddad109eee..b2f46e3db2 100644 --- a/src/nss-mymachines/nss-mymachines.c +++ b/src/nss-mymachines/nss-mymachines.c @@ -480,7 +480,7 @@ enum nss_status _nss_mymachines_getpwnam_r( pwd->pw_name = buffer; pwd->pw_uid = mapped; - pwd->pw_gid = 65534; /* nobody */ + pwd->pw_gid = GID_NOBODY; pwd->pw_gecos = buffer; pwd->pw_passwd = (char*) "*"; /* locked */ pwd->pw_dir = (char*) "/"; @@ -557,7 +557,7 @@ enum nss_status _nss_mymachines_getpwuid_r( pwd->pw_name = buffer; pwd->pw_uid = uid; - pwd->pw_gid = 65534; /* nobody */ + pwd->pw_gid = GID_NOBODY; pwd->pw_gecos = buffer; pwd->pw_passwd = (char*) "*"; /* locked */ pwd->pw_dir = (char*) "/"; diff --git a/src/nss-systemd/nss-systemd.c b/src/nss-systemd/nss-systemd.c index 1852cf41cb..cd6479573b 100644 --- a/src/nss-systemd/nss-systemd.c +++ b/src/nss-systemd/nss-systemd.c @@ -47,8 +47,8 @@ static const struct passwd root_passwd = { static const struct passwd nobody_passwd = { .pw_name = (char*) NOBODY_USER_NAME, .pw_passwd = (char*) "*", /* locked */ - .pw_uid = 65534, - .pw_gid = 65534, + .pw_uid = UID_NOBODY, + .pw_gid = GID_NOBODY, .pw_gecos = (char*) "User Nobody", .pw_dir = (char*) "/", .pw_shell = (char*) "/sbin/nologin", @@ -63,7 +63,7 @@ static const struct group root_group = { static const struct group nobody_group = { .gr_name = (char*) NOBODY_GROUP_NAME, - .gr_gid = 65534, + .gr_gid = GID_NOBODY, .gr_passwd = (char*) "*", /* locked */ .gr_mem = (char*[]) { NULL }, }; From 7e61fd02b0a9b2c4455974eb795da08b39352927 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 4 Dec 2017 17:07:48 +0100 Subject: [PATCH 07/12] user-util: synthesize user records for "nobody" the same way as for "root" We already synthesize records for both "root" and "nobody" in nss-systemd. Let's do the same in our own NSS wrappers that are supposed to bypass NSS if possible. Previously this was done for "root" only, but let's clean this up, and do the same for "nobody" too, so that we synthesize records the same way everywhere, regardless whether in NSS or internally. --- src/basic/user-util.c | 59 ++++++++++++++++++++++++++++++++++----- src/test/test-user-util.c | 35 +++++++++++++++++++++++ 2 files changed, 87 insertions(+), 7 deletions(-) diff --git a/src/basic/user-util.c b/src/basic/user-util.c index ed54578a66..abb0b76866 100644 --- a/src/basic/user-util.c +++ b/src/basic/user-util.c @@ -117,15 +117,14 @@ int get_user_creds( assert(username); assert(*username); - /* We enforce some special rules for uid=0: in order to avoid - * NSS lookups for root we hardcode its data. */ + /* We enforce some special rules for uid=0 and uid=65534: in order to avoid NSS lookups for root we hardcode + * their user record data. */ - if (streq(*username, "root") || streq(*username, "0")) { + if (STR_IN_SET(*username, "root", "0")) { *username = "root"; if (uid) *uid = 0; - if (gid) *gid = 0; @@ -138,6 +137,23 @@ int get_user_creds( return 0; } + if (STR_IN_SET(*username, NOBODY_USER_NAME, "65534")) { + *username = NOBODY_USER_NAME; + + if (uid) + *uid = UID_NOBODY; + if (gid) + *gid = GID_NOBODY; + + if (home) + *home = "/"; + + if (shell) + *shell = "/sbin/nologin"; + + return 0; + } + if (parse_uid(*username, &u) >= 0) { errno = 0; p = getpwuid(u); @@ -218,7 +234,7 @@ int get_group_creds(const char **groupname, gid_t *gid) { /* We enforce some special rules for gid=0: in order to avoid * NSS lookups for root we hardcode its data. */ - if (streq(*groupname, "root") || streq(*groupname, "0")) { + if (STR_IN_SET(*groupname, "root", "0")) { *groupname = "root"; if (gid) @@ -227,6 +243,15 @@ int get_group_creds(const char **groupname, gid_t *gid) { return 0; } + if (STR_IN_SET(*groupname, NOBODY_GROUP_NAME, "65534")) { + *groupname = NOBODY_GROUP_NAME; + + if (gid) + *gid = GID_NOBODY; + + return 0; + } + if (parse_gid(*groupname, &id) >= 0) { errno = 0; g = getgrgid(id); @@ -258,6 +283,8 @@ char* uid_to_name(uid_t uid) { /* Shortcut things to avoid NSS lookups */ if (uid == 0) return strdup("root"); + if (uid == UID_NOBODY) + return strdup(NOBODY_USER_NAME); if (uid_is_valid(uid)) { long bufsize; @@ -296,6 +323,8 @@ char* gid_to_name(gid_t gid) { if (gid == 0) return strdup("root"); + if (gid == GID_NOBODY) + return strdup(NOBODY_GROUP_NAME); if (gid_is_valid(gid)) { long bufsize; @@ -387,7 +416,7 @@ int get_home_dir(char **_h) { return 0; } - /* Hardcode home directory for root to avoid NSS */ + /* Hardcode home directory for root and nobody to avoid NSS */ u = getuid(); if (u == 0) { h = strdup("/root"); @@ -397,6 +426,14 @@ int get_home_dir(char **_h) { *_h = h; return 0; } + if (u == UID_NOBODY) { + h = strdup("/"); + if (!h) + return -ENOMEM; + + *_h = h; + return 0; + } /* Check the database... */ errno = 0; @@ -434,7 +471,7 @@ int get_shell(char **_s) { return 0; } - /* Hardcode home directory for root to avoid NSS */ + /* Hardcode shell for root and nobody to avoid NSS */ u = getuid(); if (u == 0) { s = strdup("/bin/sh"); @@ -444,6 +481,14 @@ int get_shell(char **_s) { *_s = s; return 0; } + if (u == UID_NOBODY) { + s = strdup("/sbin/nologin"); + if (!s) + return -ENOMEM; + + *_s = s; + return 0; + } /* Check the database... */ errno = 0; diff --git a/src/test/test-user-util.c b/src/test/test-user-util.c index 639804f34a..17a8520741 100644 --- a/src/test/test-user-util.c +++ b/src/test/test-user-util.c @@ -23,6 +23,7 @@ #include "string-util.h" #include "user-util.h" #include "util.h" +#include "path-util.h" static void test_uid_to_name_one(uid_t uid, const char *name) { _cleanup_free_ char *t = NULL; @@ -144,17 +145,51 @@ static void test_valid_home(void) { assert_se(valid_home("/home/foo")); } +static void test_get_user_creds_one(const char *id, const char *name, uid_t uid, gid_t gid, const char *home, const char *shell) { + const char *rhome; + const char *rshell; + uid_t ruid; + gid_t rgid; + + assert_se(get_user_creds(&id, &ruid, &rgid, &rhome, &rshell) >= 0); + assert_se(streq_ptr(id, name)); + assert_se(ruid == uid); + assert_se(rgid == gid); + assert_se(path_equal(rhome, home)); + assert_se(path_equal(rshell, shell)); +} + +static void test_get_group_creds_one(const char *id, const char *name, gid_t gid) { + gid_t rgid; + + assert_se(get_group_creds(&id, &rgid) >= 0); + assert_se(streq_ptr(id, name)); + assert_se(rgid == gid); +} + int main(int argc, char*argv[]) { test_uid_to_name_one(0, "root"); + test_uid_to_name_one(UID_NOBODY, NOBODY_USER_NAME); test_uid_to_name_one(0xFFFF, "65535"); test_uid_to_name_one(0xFFFFFFFF, "4294967295"); test_gid_to_name_one(0, "root"); + test_gid_to_name_one(GID_NOBODY, NOBODY_GROUP_NAME); test_gid_to_name_one(TTY_GID, "tty"); test_gid_to_name_one(0xFFFF, "65535"); test_gid_to_name_one(0xFFFFFFFF, "4294967295"); + test_get_user_creds_one("root", "root", 0, 0, "/root", "/bin/sh"); + test_get_user_creds_one("0", "root", 0, 0, "/root", "/bin/sh"); + test_get_user_creds_one(NOBODY_USER_NAME, NOBODY_USER_NAME, UID_NOBODY, GID_NOBODY, "/", "/sbin/nologin"); + test_get_user_creds_one("65534", NOBODY_USER_NAME, UID_NOBODY, GID_NOBODY, "/", "/sbin/nologin"); + + test_get_group_creds_one("root", "root", 0); + test_get_group_creds_one("0", "root", 0); + test_get_group_creds_one(NOBODY_GROUP_NAME, NOBODY_GROUP_NAME, GID_NOBODY); + test_get_group_creds_one("65534", NOBODY_GROUP_NAME, GID_NOBODY); + test_parse_uid(); test_uid_ptr(); From 05fd2156b7bdb31eec1731b289e80dca56acd13c Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 4 Dec 2017 17:09:27 +0100 Subject: [PATCH 08/12] journal,coredump: do not do ACL magic for "nobody" user either The "nobody" user might possibly be seen by the journal or coredumping code if unmapped userns-using processes are somehow visible to them. Let's make sure we don't do the ACL magic for this user either, since this is a special system user that might be backed by different real users in different contexts. --- src/coredump/coredump.c | 2 +- src/journal/journald-server.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/coredump/coredump.c b/src/coredump/coredump.c index cf56a8ad26..ead04d4ee1 100644 --- a/src/coredump/coredump.c +++ b/src/coredump/coredump.c @@ -165,7 +165,7 @@ static int fix_acl(int fd, uid_t uid) { assert(fd >= 0); - if (uid_is_system(uid) || uid_is_dynamic(uid)) + if (uid_is_system(uid) || uid_is_dynamic(uid) || uid == UID_NOBODY) return 0; /* Make sure normal users can read (but not write or delete) diff --git a/src/journal/journald-server.c b/src/journal/journald-server.c index 06c64a46e2..3da3dad230 100644 --- a/src/journal/journald-server.c +++ b/src/journal/journald-server.c @@ -248,7 +248,7 @@ static void server_add_acls(JournalFile *f, uid_t uid) { assert(f); #if HAVE_ACL - if (uid_is_system(uid) || uid_is_dynamic(uid)) + if (uid_is_system(uid) || uid_is_dynamic(uid) || uid == UID_NOBODY) return; r = add_acls_for_user(f->fd, uid); @@ -406,7 +406,7 @@ static JournalFile* find_journal(Server *s, uid_t uid) { if (s->runtime_journal) return s->runtime_journal; - if (uid_is_system(uid) || uid_is_dynamic(uid)) + if (uid_is_system(uid) || uid_is_dynamic(uid) || uid == UID_NOBODY) return s->system_journal; r = sd_id128_get_machine(&machine); From 66d73cc7284b7a9b2c54d2cc1977d0c28237a20c Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 5 Dec 2017 10:59:44 +0100 Subject: [PATCH 09/12] test-execute: use the "nogroup" group if it exists for testing We currently look for "nobody" and "nfsnobody" when testing groups, both of which do not exist on Ubuntu, our main testing environment. Let's extend the tests slightly to also use "nogroup" if it exists. --- src/test/test-execute.c | 5 +++-- test/meson.build | 1 + test/test-execute/exec-group-nogroup.service | 7 +++++++ 3 files changed, 11 insertions(+), 2 deletions(-) create mode 100644 test/test-execute/exec-group-nogroup.service diff --git a/src/test/test-execute.c b/src/test/test-execute.c index 2bff00a9f7..fba798e22b 100644 --- a/src/test/test-execute.c +++ b/src/test/test-execute.c @@ -324,7 +324,6 @@ static void test_exec_systemcallfilter_system(Manager *m) { log_notice("Seccomp not available, skipping %s", __func__); return; } - if (getpwnam("nobody")) test(m, "exec-systemcallfilter-system-user.service", 0, CLD_EXITED); else if (getpwnam("nfsnobody")) @@ -348,8 +347,10 @@ static void test_exec_group(Manager *m) { test(m, "exec-group.service", 0, CLD_EXITED); else if (getgrnam("nfsnobody")) test(m, "exec-group-nfsnobody.service", 0, CLD_EXITED); + else if (getgrnam("nogroup")) + test(m, "exec-group-nogroup.service", 0, CLD_EXITED); else - log_error_errno(errno, "Skipping %s, could not find nobody/nfsnobody group: %m", __func__); + log_error_errno(errno, "Skipping %s, could not find nobody/nfsnobody/nogroup group: %m", __func__); } static void test_exec_supplementarygroups(Manager *m) { diff --git a/test/meson.build b/test/meson.build index 750f5c0379..5c533f4833 100644 --- a/test/meson.build +++ b/test/meson.build @@ -68,6 +68,7 @@ test_data_files = ''' test-execute/exec-environment.service test-execute/exec-environmentfile.service test-execute/exec-group-nfsnobody.service + test-execute/exec-group-nogroup.service test-execute/exec-group.service test-execute/exec-ignoresigpipe-no.service test-execute/exec-ignoresigpipe-yes.service diff --git a/test/test-execute/exec-group-nogroup.service b/test/test-execute/exec-group-nogroup.service new file mode 100644 index 0000000000..cf0773229e --- /dev/null +++ b/test/test-execute/exec-group-nogroup.service @@ -0,0 +1,7 @@ +[Unit] +Description=Test for Group + +[Service] +ExecStart=/bin/sh -x -c 'test "$$(id -n -g)" = "nogroup"' +Type=oneshot +Group=nogroup From afde45740ffd993b37aa71239cdb2a6c4938c33b Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 5 Dec 2017 11:00:24 +0100 Subject: [PATCH 10/12] meson: print warnings if the "nobody" user/group name is not compatible with the local system At least on Fedora and Debian systems this not obvious to get right, hence warn. --- meson.build | 49 +++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 45 insertions(+), 4 deletions(-) diff --git a/meson.build b/meson.build index ea13dff44a..eda74c4765 100644 --- a/meson.build +++ b/meson.build @@ -640,8 +640,49 @@ conf.set('CONTAINER_UID_BASE_MAX', container_uid_base_max) substs.set('containeruidbasemin', container_uid_base_min) substs.set('containeruidbasemax', container_uid_base_max) -conf.set_quoted('NOBODY_USER_NAME', get_option('nobody-user')) -conf.set_quoted('NOBODY_GROUP_NAME', get_option('nobody-group')) +nobody_user = get_option('nobody-user') +nobody_group = get_option('nobody-group') + +getent_result = run_command('getent', 'passwd', '65534') +if getent_result.returncode() == 0 + name = getent_result.stdout().split(':')[0] + if name != nobody_user + message('WARNING:\n' + + ' The local user with the UID 65534 does not match the configured user name "@0@" of the nobody user (its name is @1@).\n'.format(nobody_user, name) + + ' Your build will result in an user table setup that is incompatible with the local system.') + endif +endif +id_result = run_command('id', '-u', nobody_user) +if id_result.returncode() == 0 + id = id_result.stdout().to_int() + if id != 65534 + message('WARNING:\n' + + ' The local user with the configured user name "@0@" of the nobody user does not have UID 65534 (it has @1@).\n'.format(nobody_user, id) + + ' Your build will result in an user table setup that is incompatible with the local system.') + endif +endif + +getent_result = run_command('getent', 'group', '65534') +if getent_result.returncode() == 0 + name = getent_result.stdout().split(':')[0] + if name != nobody_group + message('WARNING:\n' + + ' The local group with the GID 65534 does not match the configured group name "@0@" of the nobody group (its name is @1@).\n'.format(nobody_group, name) + + ' Your build will result in an group table setup that is incompatible with the local system.') + endif +endif +id_result = run_command('id', '-g', nobody_group) +if id_result.returncode() == 0 + id = id_result.stdout().to_int() + if id != 65534 + message('WARNING:\n' + + ' The local group with the configured group name "@0@" of the nobody group does not have UID 65534 (it has @1@).\n'.format(nobody_group, id) + + ' Your build will result in an group table setup that is incompatible with the local system.') + endif +endif + +conf.set_quoted('NOBODY_USER_NAME', nobody_user) +conf.set_quoted('NOBODY_GROUP_NAME', nobody_group) tty_gid = get_option('tty-gid') conf.set('TTY_GID', tty_gid) @@ -2528,8 +2569,8 @@ status = [ 'render group access mode: @0@'.format(get_option('group-render-mode')), 'certificate root directory: @0@'.format(get_option('certificate-root')), 'support URL: @0@'.format(support_url), - 'nobody user name: @0@'.format(get_option('nobody-user')), - 'nobody group name: @0@'.format(get_option('nobody-group')), + 'nobody user name: @0@'.format(nobody_user), + 'nobody group name: @0@'.format(nobody_group), 'fallback hostname: @0@'.format(get_option('fallback-hostname')), 'symbolic gateway hostnames: @0@'.format(', '.join(gateway_hostnames)), From fa75683700ffd714cb53de302e6629e4c56adab6 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sat, 2 Dec 2017 13:11:25 +0100 Subject: [PATCH 11/12] update TODO --- TODO | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/TODO b/TODO index 2ca2a8d50f..0f9b28ab49 100644 --- a/TODO +++ b/TODO @@ -33,7 +33,8 @@ Features: systemd-journald writes to /var/log/journal, which could be useful when we doing disk usage calculations and so on. -* taint systemd if the overflowuid/overflowgid is not 65534 +* taint systemd if the overflowuid/overflowgid is not 65534, and if there are + fewer than 65536 users assigned to the system. * deprecate PermissionsStartOnly= and RootDirectoryStartOnly= in favour of the ExecStart= prefix chars @@ -77,16 +78,9 @@ Features: * beef up pam_systemd to take unit file settings such as cgroups properties as parameters -* export UID ranges nspawns's --private-user and DynamicUser= uses in - the systemd.pc pkg-config file, the same way we already expose the system - user boundary there - * a new "systemd-analyze security" tool outputting a checklist of security features a service does and does not implement -* Whenever we check a UID against the system UID range, also check for the - dynamic UID range - * maybe hook of xfs/ext4 quotactl() with services? i.e. automatically manage the quota of a the user indicated in User= via unit file settings, like the other resource management concepts. Would mix nicely with DynamicUser=1. Or From c82ce4f215f868382098dee38ffd5b54965c7bcb Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 6 Dec 2017 13:53:39 +0100 Subject: [PATCH 12/12] mkosi.build: add code to determine the right nobody user/group name --- mkosi.build | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/mkosi.build b/mkosi.build index c2693dedcf..0781f0d9c4 100755 --- a/mkosi.build +++ b/mkosi.build @@ -28,7 +28,41 @@ export LC_CTYPE=en_US.UTF-8 sysvinit_path=`realpath /etc/init.d` -[ -f "$BUILDDIR"/build.ninja ] || meson "$BUILDDIR" -D "sysvinit-path=$sysvinit_path" -D default-hierarchy=unified -D man=false +nobody_user=`id -u -n 65534 2> /dev/null` +if [ "$nobody_user" != "" ] ; then + # Validate that we can translate forth and back + if [ "`id -u $nobody_user`" != 65534 ] ; then + nobody_user="" + fi +fi +if [ "$nobody_user" = "" ] ; then + if id -u nobody 2> /dev/null ; then + # The "nobody" user is defined already for something else, pick the Fedora name + nobody_user=nfsnobody + else + # The "nobody" user name is free, use it + nobody_user=nobody + fi +fi + +nobody_group=`id -g -n 65534 2> /dev/null` +if [ "$nobody_group" != "" ] ; then + # Validate that we can translate forth and back + if [ "`id -g $nobody_group`" != 65534 ] ; then + nobody_group="" + fi +fi +if [ "$nobody_group" = "" ] ; then + if id -u nobody 2> /dev/null ; then + # The "nobody" group is defined already for something else, pick the Fedora name + nobody_group=nfsnobody + else + # The "nobody" group name is free, use it + nobody_group=nobody + fi +fi + +[ -f "$BUILDDIR"/build.ninja ] || meson "$BUILDDIR" -D "sysvinit-path=$sysvinit_path" -D default-hierarchy=unified -D man=false -D "nobody-user=$nobody_user" -D "nobody-group=$nobody_group" ninja -C "$BUILDDIR" all [ "$WITH_TESTS" = 0 ] || ninja -C "$BUILDDIR" test || ( RET="$?" ; cat "$BUILDDIR"/meson-logs/testlog.txt ; exit "$RET" ) ninja -C "$BUILDDIR" install