mirror of
https://github.com/systemd/systemd.git
synced 2025-01-06 17:18:12 +03:00
machined: expose "UID shift" concept for containers
UID/GID mapping with userns can be arbitrarily complex. Let's break this down to a single admin-friendly parameter: let's expose the UID/GID shift of a container via a new bus call for each container, and let's show this as part of "machinectl status" if it is not 0. This should work for pretty much all real-life full OS container setups (i.e. the stuff machined is suppose to be useful for). For everything else we generate a clean error, clarifying that we can't expose the mapping.
This commit is contained in:
parent
f73e6ee687
commit
3401419bb8
@ -1276,6 +1276,32 @@ int bus_machine_method_open_root_directory(sd_bus_message *message, void *userda
|
||||
return sd_bus_reply_method_return(message, "h", fd);
|
||||
}
|
||||
|
||||
int bus_machine_method_get_uid_shift(sd_bus_message *message, void *userdata, sd_bus_error *error) {
|
||||
Machine *m = userdata;
|
||||
uid_t shift = 0;
|
||||
int r;
|
||||
|
||||
assert(message);
|
||||
assert(m);
|
||||
|
||||
/* You wonder why this is a method and not a property? Well, properties are not supposed to return errors, but
|
||||
* we kinda have to for this. */
|
||||
|
||||
if (m->class == MACHINE_HOST)
|
||||
return sd_bus_reply_method_return(message, "u", UINT32_C(0));
|
||||
|
||||
if (m->class != MACHINE_CONTAINER)
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "UID/GID shift may only be determined for container machines.");
|
||||
|
||||
r = machine_get_uid_shift(m, &shift);
|
||||
if (r == -ENXIO)
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Machine %s uses a complex UID/GID mapping, cannot determine shift", m->name);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return sd_bus_reply_method_return(message, "u", (uint32_t) shift);
|
||||
}
|
||||
|
||||
const sd_bus_vtable machine_vtable[] = {
|
||||
SD_BUS_VTABLE_START(0),
|
||||
SD_BUS_PROPERTY("Name", "s", NULL, offsetof(Machine, name), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
@ -1293,6 +1319,7 @@ const sd_bus_vtable machine_vtable[] = {
|
||||
SD_BUS_METHOD("Kill", "si", NULL, bus_machine_method_kill, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_METHOD("GetAddresses", NULL, "a(iay)", bus_machine_method_get_addresses, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_METHOD("GetOSRelease", NULL, "a{ss}", bus_machine_method_get_os_release, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_METHOD("GetUIDShift", NULL, "u", bus_machine_method_get_uid_shift, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_METHOD("OpenPTY", NULL, "hs", bus_machine_method_open_pty, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_METHOD("OpenLogin", NULL, "hs", bus_machine_method_open_login, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_METHOD("OpenShell", "ssasas", "hs", bus_machine_method_open_shell, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
|
@ -39,6 +39,7 @@ int bus_machine_method_open_shell(sd_bus_message *message, void *userdata, sd_bu
|
||||
int bus_machine_method_bind_mount(sd_bus_message *message, void *userdata, sd_bus_error *error);
|
||||
int bus_machine_method_copy(sd_bus_message *message, void *userdata, sd_bus_error *error);
|
||||
int bus_machine_method_open_root_directory(sd_bus_message *message, void *userdata, sd_bus_error *error);
|
||||
int bus_machine_method_get_uid_shift(sd_bus_message *message, void *userdata, sd_bus_error *error);
|
||||
|
||||
int machine_send_signal(Machine *m, bool new_machine);
|
||||
int machine_send_create_reply(Machine *m, sd_bus_error *error);
|
||||
|
@ -38,6 +38,7 @@
|
||||
#include "parse-util.h"
|
||||
#include "process-util.h"
|
||||
#include "special.h"
|
||||
#include "stdio-util.h"
|
||||
#include "string-table.h"
|
||||
#include "terminal-util.h"
|
||||
#include "unit-name.h"
|
||||
@ -604,6 +605,96 @@ void machine_release_unit(Machine *m) {
|
||||
m->unit = mfree(m->unit);
|
||||
}
|
||||
|
||||
int machine_get_uid_shift(Machine *m, uid_t *ret) {
|
||||
char p[strlen("/proc//uid_map") + DECIMAL_STR_MAX(pid_t) + 1];
|
||||
uid_t uid_base, uid_shift, uid_range;
|
||||
gid_t gid_base, gid_shift, gid_range;
|
||||
_cleanup_fclose_ FILE *f = NULL;
|
||||
int k;
|
||||
|
||||
assert(m);
|
||||
assert(ret);
|
||||
|
||||
/* Return the base UID/GID of the specified machine. Note that this only works for containers with simple
|
||||
* mappings. In most cases setups should be simple like this, and administrators should only care about the
|
||||
* basic offset a container has relative to the host. This is what this function exposes.
|
||||
*
|
||||
* If we encounter any more complex mappings we politely refuse this with ENXIO. */
|
||||
|
||||
if (m->class == MACHINE_HOST) {
|
||||
*ret = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (m->class != MACHINE_CONTAINER)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
xsprintf(p, "/proc/" PID_FMT "/uid_map", m->leader);
|
||||
f = fopen(p, "re");
|
||||
if (!f) {
|
||||
if (errno == ENOENT) {
|
||||
/* If the file doesn't exist, user namespacing is off in the kernel, return a zero mapping hence. */
|
||||
*ret = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -errno;
|
||||
}
|
||||
|
||||
/* Read the first line. There's at least one. */
|
||||
errno = 0;
|
||||
k = fscanf(f, UID_FMT " " UID_FMT " " UID_FMT "\n", &uid_base, &uid_shift, &uid_range);
|
||||
if (k != 3) {
|
||||
if (ferror(f))
|
||||
return -errno;
|
||||
|
||||
return -EBADMSG;
|
||||
}
|
||||
|
||||
/* Not a mapping starting at 0? Then it's a complex mapping we can't expose here. */
|
||||
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)
|
||||
return -ENXIO;
|
||||
|
||||
/* If there's more than one line, then we don't support this mapping. */
|
||||
if (fgetc(f) != EOF)
|
||||
return -ENXIO;
|
||||
|
||||
fclose(f);
|
||||
|
||||
xsprintf(p, "/proc/" PID_FMT "/gid_map", m->leader);
|
||||
f = fopen(p, "re");
|
||||
if (!f)
|
||||
return -errno;
|
||||
|
||||
/* Read the first line. There's at least one. */
|
||||
errno = 0;
|
||||
k = fscanf(f, GID_FMT " " GID_FMT " " GID_FMT "\n", &gid_base, &gid_shift, &gid_range);
|
||||
if (k != 3) {
|
||||
if (ferror(f))
|
||||
return -errno;
|
||||
|
||||
return -EBADMSG;
|
||||
}
|
||||
|
||||
/* If there's more than one line, then we don't support this file. */
|
||||
if (fgetc(f) != EOF)
|
||||
return -ENXIO;
|
||||
|
||||
/* If the UID and GID mapping doesn't match, we don't support this mapping. */
|
||||
if (uid_base != (uid_t) gid_base)
|
||||
return -ENXIO;
|
||||
if (uid_shift != (uid_t) gid_shift)
|
||||
return -ENXIO;
|
||||
if (uid_range != (uid_t) gid_range)
|
||||
return -ENXIO;
|
||||
|
||||
*ret = uid_shift;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char* const machine_class_table[_MACHINE_CLASS_MAX] = {
|
||||
[MACHINE_CONTAINER] = "container",
|
||||
[MACHINE_VM] = "vm",
|
||||
|
@ -108,3 +108,5 @@ KillWho kill_who_from_string(const char *s) _pure_;
|
||||
|
||||
int machine_openpt(Machine *m, int flags);
|
||||
int machine_open_terminal(Machine *m, const char *path, int mode);
|
||||
|
||||
int machine_get_uid_shift(Machine *m, uid_t *ret);
|
||||
|
@ -611,6 +611,37 @@ static int print_os_release(sd_bus *bus, const char *method, const char *name, c
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int print_uid_shift(sd_bus *bus, const char *name) {
|
||||
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
|
||||
uint32_t shift;
|
||||
int r;
|
||||
|
||||
assert(bus);
|
||||
assert(name);
|
||||
|
||||
r = sd_bus_call_method(bus,
|
||||
"org.freedesktop.machine1",
|
||||
"/org/freedesktop/machine1",
|
||||
"org.freedesktop.machine1.Manager",
|
||||
"GetMachineUIDShift",
|
||||
&error,
|
||||
&reply,
|
||||
"s", name);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to query UID/GID shift: %s", bus_error_message(&error, r));
|
||||
|
||||
r = sd_bus_message_read(reply, "u", &shift);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (shift == 0) /* Don't show trivial mappings */
|
||||
return 0;
|
||||
|
||||
printf(" UID Shift: %" PRIu32 "\n", shift);
|
||||
return 0;
|
||||
}
|
||||
|
||||
typedef struct MachineStatusInfo {
|
||||
char *name;
|
||||
sd_id128_t id;
|
||||
@ -714,6 +745,8 @@ static void print_machine_status_info(sd_bus *bus, MachineStatusInfo *i) {
|
||||
|
||||
print_os_release(bus, "GetMachineOSRelease", i->name, "\t OS: ");
|
||||
|
||||
print_uid_shift(bus, i->name);
|
||||
|
||||
if (i->unit) {
|
||||
printf("\t Unit: %s\n", i->unit);
|
||||
show_unit_cgroup(bus, i->unit, i->leader);
|
||||
|
@ -729,6 +729,26 @@ static int method_open_machine_root_directory(sd_bus_message *message, void *use
|
||||
return bus_machine_method_open_root_directory(message, machine, error);
|
||||
}
|
||||
|
||||
static int method_get_machine_uid_shift(sd_bus_message *message, void *userdata, sd_bus_error *error) {
|
||||
Manager *m = userdata;
|
||||
Machine *machine;
|
||||
const char *name;
|
||||
int r;
|
||||
|
||||
assert(message);
|
||||
assert(m);
|
||||
|
||||
r = sd_bus_message_read(message, "s", &name);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
machine = hashmap_get(m->machines, name);
|
||||
if (!machine)
|
||||
return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name);
|
||||
|
||||
return bus_machine_method_get_uid_shift(message, machine, error);
|
||||
}
|
||||
|
||||
static int method_remove_image(sd_bus_message *message, void *userdata, sd_bus_error *error) {
|
||||
_cleanup_(image_unrefp) Image* i = NULL;
|
||||
const char *name;
|
||||
@ -1416,6 +1436,7 @@ const sd_bus_vtable manager_vtable[] = {
|
||||
SD_BUS_METHOD("CopyFromMachine", "sss", NULL, method_copy_machine, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_METHOD("CopyToMachine", "sss", NULL, method_copy_machine, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_METHOD("OpenMachineRootDirectory", "s", "h", method_open_machine_root_directory, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_METHOD("GetMachineUIDShift", "s", "u", method_get_machine_uid_shift, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_METHOD("RemoveImage", "s", NULL, method_remove_image, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_METHOD("RenameImage", "ss", NULL, method_rename_image, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_METHOD("CloneImage", "ssb", NULL, method_clone_image, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
|
@ -64,6 +64,10 @@
|
||||
send_interface="org.freedesktop.machine1.Manager"
|
||||
send_member="GetMachineOSRelease"/>
|
||||
|
||||
<allow send_destination="org.freedesktop.machine1"
|
||||
send_interface="org.freedesktop.machine1.Manager"
|
||||
send_member="GetMachineUIDShift"/>
|
||||
|
||||
<allow send_destination="org.freedesktop.machine1"
|
||||
send_interface="org.freedesktop.machine1.Manager"
|
||||
send_member="OpenMachineLogin"/>
|
||||
@ -148,6 +152,10 @@
|
||||
send_interface="org.freedesktop.machine1.Machine"
|
||||
send_member="GetOSRelease"/>
|
||||
|
||||
<allow send_destination="org.freedesktop.machine1"
|
||||
send_interface="org.freedesktop.machine1.Machine"
|
||||
send_member="GetUIDShift"/>
|
||||
|
||||
<allow send_destination="org.freedesktop.machine1"
|
||||
send_interface="org.freedesktop.machine1.Machine"
|
||||
send_member="OpenLogin"/>
|
||||
|
Loading…
Reference in New Issue
Block a user