machined: make registration of unpriv user's VMs/containers work (#37855)
This adds missing glue to reasonably allow unpriv users VMs/containers to register with the system machined. This primarily adds two things: 1. machined can now properly track VMs/containers residing in subcgroups of units, because that's effectively what happens for per-user VMs/containers: they are placed below the system unit `user@….service` in some user unit. 2. machines registered with machined now have an owning UID: users can operate on their own machines withour re-authentication, but not on others. Note that this is only a first step regarding machined's hookup of nspawn/vmspawn in the long run for unpriv operation. I think eventually we should make it so that there's both a per-user and a per-system machined instance (so far, and even with this PR there's still one per-system instance), and per-user containers/VMs would registering with *both*. Having two instances makes sense I think, because it would mean we can make machined reasonably manage the per-user image discovery, and also do the per-system network/hostname handling.
This commit is contained in:
+108
-58
@@ -12,7 +12,6 @@
|
||||
#include <sys/prctl.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
#include "constants.h"
|
||||
|
||||
#if HAVE_SELINUX
|
||||
#include <selinux/selinux.h>
|
||||
@@ -38,8 +37,10 @@
|
||||
#include "cgroup-util.h"
|
||||
#include "chase.h"
|
||||
#include "common-signal.h"
|
||||
#include "constants.h"
|
||||
#include "copy.h"
|
||||
#include "cpu-set-util.h"
|
||||
#include "daemon-util.h"
|
||||
#include "dev-setup.h"
|
||||
#include "devnum-util.h"
|
||||
#include "discover-image.h"
|
||||
@@ -1667,11 +1668,6 @@ static int verify_arguments(void) {
|
||||
if (has_custom_root_mount(arg_custom_mounts, arg_n_custom_mounts))
|
||||
arg_read_only = true;
|
||||
|
||||
if (arg_keep_unit && arg_register && cg_pid_get_owner_uid(0, NULL) >= 0)
|
||||
/* Save the user from accidentally registering either user-$SESSION.scope or user@.service.
|
||||
* The latter is not technically a user session, but we don't need to labour the point. */
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--keep-unit --register=yes may not be used when invoked from a user session.");
|
||||
|
||||
if (arg_directory && arg_image)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--directory= and --image= may not be combined.");
|
||||
|
||||
@@ -2960,13 +2956,19 @@ static int wait_for_container(PidRef *pid, ContainerStatus *container) {
|
||||
static int on_orderly_shutdown(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
|
||||
PidRef *pid = ASSERT_PTR(userdata);
|
||||
|
||||
assert(si);
|
||||
|
||||
if (pidref_is_set(pid))
|
||||
if (pidref_kill(pid, arg_kill_signal) >= 0) {
|
||||
log_info("Trying to halt container. Send SIGTERM again to trigger immediate termination.");
|
||||
log_info("Trying to halt container by sending %s to container PID 1. Send SIGTERM again to trigger immediate termination.",
|
||||
signal_to_string(si->ssi_signo));
|
||||
sd_event_source_set_userdata(s, NULL);
|
||||
sd_notify(/* unset_environment= */ false, NOTIFY_STOPPING_MESSAGE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
log_debug("Got %s, exiting.", signal_to_string(si->ssi_signo));
|
||||
|
||||
sd_event_exit(sd_event_source_get_event(s), 0);
|
||||
return 0;
|
||||
}
|
||||
@@ -4617,15 +4619,18 @@ static int nspawn_dispatch_notify_fd(sd_event_source *source, int fd, uint32_t r
|
||||
log_debug("Got sd_notify() message: %s", strnull(joined));
|
||||
}
|
||||
|
||||
char *status = strv_find_startswith(tags, "STATUS=");
|
||||
if (status)
|
||||
(void) sd_notifyf(/* unset_environment= */ false, "STATUS=Container running: %s", status);
|
||||
|
||||
if (strv_contains(tags, "READY=1")) {
|
||||
r = sd_notify(false, "READY=1\n");
|
||||
r = sd_notify(/* unset_environment= */ false, "READY=1\n");
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Failed to send readiness notification, ignoring: %m");
|
||||
}
|
||||
|
||||
char *p = strv_find_startswith(tags, "STATUS=");
|
||||
if (p)
|
||||
(void) sd_notifyf(false, "STATUS=Container running: %s", p);
|
||||
if (!status)
|
||||
(void) sd_notifyf(/* unset_environment= */ false, "STATUS=Container running.");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -5136,7 +5141,6 @@ static int run_container(
|
||||
_cleanup_(sd_event_unrefp) sd_event *event = NULL;
|
||||
_cleanup_(pty_forward_freep) PTYForward *forward = NULL;
|
||||
_cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
|
||||
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
|
||||
_cleanup_free_ uid_t *bind_user_uid = NULL;
|
||||
size_t n_bind_user_uid = 0;
|
||||
ContainerStatus container_status = 0;
|
||||
@@ -5459,19 +5463,35 @@ static int run_container(
|
||||
return r;
|
||||
}
|
||||
|
||||
if (arg_register || !arg_keep_unit) {
|
||||
if (arg_privileged || arg_register)
|
||||
r = sd_bus_default_system(&bus);
|
||||
else
|
||||
r = sd_bus_default_user(&bus);
|
||||
/* Registration always happens on the system bus */
|
||||
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *system_bus = NULL;
|
||||
if (arg_register || arg_privileged) {
|
||||
r = sd_bus_default_system(&system_bus);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to open bus: %m");
|
||||
return log_error_errno(r, "Failed to open system bus: %m");
|
||||
|
||||
r = sd_bus_set_close_on_exit(bus, false);
|
||||
r = sd_bus_set_close_on_exit(system_bus, false);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to disable close-on-exit behaviour: %m");
|
||||
|
||||
(void) sd_bus_set_allow_interactive_authorization(bus, arg_ask_password);
|
||||
(void) sd_bus_set_allow_interactive_authorization(system_bus, arg_ask_password);
|
||||
}
|
||||
|
||||
/* Scope allocation happens on the user bus if we are unpriv, otherwise system bus. */
|
||||
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *user_bus = NULL;
|
||||
_cleanup_(sd_bus_unrefp) sd_bus *runtime_bus = NULL;
|
||||
if (arg_privileged)
|
||||
runtime_bus = sd_bus_ref(system_bus);
|
||||
else {
|
||||
r = sd_bus_default_user(&user_bus);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to open user bus: %m");
|
||||
|
||||
r = sd_bus_set_close_on_exit(user_bus, false);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to disable close-on-exit behaviour: %m");
|
||||
|
||||
runtime_bus = sd_bus_ref(user_bus);
|
||||
}
|
||||
|
||||
if (!arg_keep_unit) {
|
||||
@@ -5480,7 +5500,7 @@ static int run_container(
|
||||
* scope. Let's hook into that, and cleanly shut down the container, and print a friendly message. */
|
||||
|
||||
r = sd_bus_match_signal_async(
|
||||
bus,
|
||||
runtime_bus,
|
||||
/* ret= */ NULL,
|
||||
"org.freedesktop.systemd1",
|
||||
/* path= */ NULL,
|
||||
@@ -5493,11 +5513,46 @@ static int run_container(
|
||||
return log_error_errno(r, "Failed to request RequestStop match: %m");
|
||||
}
|
||||
|
||||
if (arg_keep_unit) {
|
||||
/* If we are not supposed to allocate a unit, then let's move the process now, so that we can
|
||||
* register things while being in the right cgroup location already. Otherwise, let's move
|
||||
* the process later, once we have unit and hence cgroup. */
|
||||
r = create_subcgroup(
|
||||
pid,
|
||||
arg_keep_unit,
|
||||
arg_uid_shift,
|
||||
userns_fd,
|
||||
arg_userns_mode);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
bool scope_allocated = false;
|
||||
if (!arg_keep_unit && (!arg_register || !arg_privileged)) {
|
||||
AllocateScopeFlags flags = ALLOCATE_SCOPE_ALLOW_PIDFD;
|
||||
r = allocate_scope(
|
||||
runtime_bus,
|
||||
arg_machine,
|
||||
pid,
|
||||
arg_slice,
|
||||
arg_custom_mounts, arg_n_custom_mounts,
|
||||
arg_kill_signal,
|
||||
arg_property,
|
||||
arg_property_message,
|
||||
arg_start_mode,
|
||||
flags);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
scope_allocated = true;
|
||||
}
|
||||
|
||||
bool registered = false;
|
||||
if (arg_register) {
|
||||
RegisterMachineFlags flags = 0;
|
||||
SET_FLAG(flags, REGISTER_MACHINE_KEEP_UNIT, arg_keep_unit);
|
||||
SET_FLAG(flags, REGISTER_MACHINE_KEEP_UNIT, arg_keep_unit || !arg_privileged);
|
||||
r = register_machine(
|
||||
bus,
|
||||
system_bus,
|
||||
arg_machine,
|
||||
pid,
|
||||
arg_directory,
|
||||
@@ -5514,33 +5569,22 @@ static int run_container(
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
} else if (!arg_keep_unit) {
|
||||
AllocateScopeFlags flags = ALLOCATE_SCOPE_ALLOW_PIDFD;
|
||||
r = allocate_scope(
|
||||
bus,
|
||||
arg_machine,
|
||||
pid,
|
||||
arg_slice,
|
||||
arg_custom_mounts, arg_n_custom_mounts,
|
||||
arg_kill_signal,
|
||||
arg_property,
|
||||
arg_property_message,
|
||||
arg_start_mode,
|
||||
flags);
|
||||
if (r < 0)
|
||||
return r;
|
||||
registered = true;
|
||||
}
|
||||
|
||||
} else if (arg_slice || arg_property)
|
||||
if (arg_keep_unit && (arg_slice || arg_property))
|
||||
log_notice("Machine and scope registration turned off, --slice= and --property= settings will have no effect.");
|
||||
|
||||
r = create_subcgroup(
|
||||
pid,
|
||||
arg_keep_unit,
|
||||
arg_uid_shift,
|
||||
userns_fd,
|
||||
arg_userns_mode);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (!arg_keep_unit) {
|
||||
r = create_subcgroup(
|
||||
pid,
|
||||
arg_keep_unit,
|
||||
arg_uid_shift,
|
||||
userns_fd,
|
||||
arg_userns_mode);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Notify the child that the parent is ready with all its setup (including cgroup-ification), and
|
||||
* that the child can now hand over control to the code to run inside the container. */
|
||||
@@ -5561,10 +5605,16 @@ static int run_container(
|
||||
|
||||
(void) sd_event_set_watchdog(event, true);
|
||||
|
||||
if (bus) {
|
||||
r = sd_bus_attach_event(bus, event, 0);
|
||||
if (system_bus) {
|
||||
r = sd_bus_attach_event(system_bus, event, 0);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to attach bus to event loop: %m");
|
||||
return log_error_errno(r, "Failed to attach system bus to event loop: %m");
|
||||
}
|
||||
|
||||
if (user_bus) {
|
||||
r = sd_bus_attach_event(user_bus, event, 0);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to attach user bus to event loop: %m");
|
||||
}
|
||||
|
||||
r = setup_notify_parent(event, notify_socket, pid, ¬ify_event_source);
|
||||
@@ -5592,11 +5642,11 @@ static int run_container(
|
||||
* will make them appear in getpwuid(), thus we can release the /etc/passwd lock. */
|
||||
etc_passwd_lock = safe_close(etc_passwd_lock);
|
||||
|
||||
(void) sd_notifyf(false,
|
||||
"STATUS=Container running.\n"
|
||||
(void) sd_notifyf(/* unset_environment= */ false,
|
||||
"STATUS=Container started.\n"
|
||||
"X_NSPAWN_LEADER_PID=" PID_FMT, pid->pid);
|
||||
if (!arg_notify_ready) {
|
||||
r = sd_notify(false, "READY=1\n");
|
||||
r = sd_notify(/* unset_environment= */ false, "READY=1\n");
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Failed to send readiness notification, ignoring: %m");
|
||||
}
|
||||
@@ -5710,8 +5760,8 @@ static int run_container(
|
||||
return log_error_errno(r, "Failed to run event loop: %m");
|
||||
|
||||
/* Kill if it is not dead yet anyway */
|
||||
if (!arg_register && !arg_keep_unit && bus)
|
||||
terminate_scope(bus, arg_machine);
|
||||
if (scope_allocated)
|
||||
terminate_scope(runtime_bus, arg_machine);
|
||||
|
||||
/* Normally redundant, but better safe than sorry */
|
||||
(void) pidref_kill(pid, SIGKILL);
|
||||
@@ -5731,8 +5781,8 @@ static int run_container(
|
||||
r = wait_for_container(pid, &container_status);
|
||||
|
||||
/* Tell machined that we are gone. */
|
||||
if (arg_register && bus)
|
||||
(void) unregister_machine(bus, arg_machine);
|
||||
if (registered)
|
||||
(void) unregister_machine(system_bus, arg_machine);
|
||||
|
||||
if (r < 0)
|
||||
/* We failed to wait for the container, or the container exited abnormally. */
|
||||
|
||||
Reference in New Issue
Block a user