mirror of
https://github.com/systemd/systemd.git
synced 2025-03-24 14:50:17 +03:00
nspawn: mount most of the cgroup tree read-only in nspawn containers except for the container's own subtree in the name=systemd hierarchy
More specifically mount all other hierarchies in their entirety and the name=systemd above the container's subtree read-only.
This commit is contained in:
parent
714e2e1d56
commit
b12afc8c5c
@ -44,6 +44,7 @@
|
||||
#include "efivars.h"
|
||||
#include "smack-util.h"
|
||||
#include "def.h"
|
||||
#include "cgroup-util.h"
|
||||
|
||||
typedef enum MountMode {
|
||||
MNT_NONE = 0,
|
||||
@ -227,49 +228,17 @@ int mount_setup_early(void) {
|
||||
|
||||
int mount_cgroup_controllers(char ***join_controllers) {
|
||||
_cleanup_set_free_free_ Set *controllers = NULL;
|
||||
_cleanup_fclose_ FILE *f;
|
||||
char buf[LINE_MAX];
|
||||
int r;
|
||||
|
||||
/* Mount all available cgroup controllers that are built into the kernel. */
|
||||
|
||||
f = fopen("/proc/cgroups", "re");
|
||||
if (!f) {
|
||||
log_error_errno(errno, "Failed to enumerate cgroup controllers: %m");
|
||||
return 0;
|
||||
}
|
||||
|
||||
controllers = set_new(&string_hash_ops);
|
||||
if (!controllers)
|
||||
return log_oom();
|
||||
|
||||
/* Ignore the header line */
|
||||
(void) fgets(buf, sizeof(buf), f);
|
||||
|
||||
for (;;) {
|
||||
char *controller;
|
||||
int enabled = 0;
|
||||
|
||||
if (fscanf(f, "%ms %*i %*i %i", &controller, &enabled) != 2) {
|
||||
|
||||
if (feof(f))
|
||||
break;
|
||||
|
||||
log_error("Failed to parse /proc/cgroups.");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (!enabled) {
|
||||
free(controller);
|
||||
continue;
|
||||
}
|
||||
|
||||
r = set_consume(controllers, controller);
|
||||
if (r < 0) {
|
||||
log_error("Failed to add controller to set.");
|
||||
return r;
|
||||
}
|
||||
}
|
||||
r = cg_kernel_controllers(controllers);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to enumerate cgroup controllers: %m");
|
||||
|
||||
for (;;) {
|
||||
_cleanup_free_ char *options = NULL, *controller = NULL, *where = NULL;
|
||||
@ -348,7 +317,7 @@ int mount_cgroup_controllers(char ***join_controllers) {
|
||||
|
||||
/* Now that we mounted everything, let's make the tmpfs the
|
||||
* cgroup file systems are mounted into read-only. */
|
||||
mount("tmpfs", "/sys/fs/cgroup", "tmpfs", MS_REMOUNT|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME|MS_RDONLY, "mode=755");
|
||||
(void) mount("tmpfs", "/sys/fs/cgroup", "tmpfs", MS_REMOUNT|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME|MS_RDONLY, "mode=755");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -869,6 +869,112 @@ static int mount_binds(const char *dest, char **l, bool ro) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mount_cgroup_hierarchy(const char *dest, const char *controller, const char *hierarchy, bool read_only) {
|
||||
char *to;
|
||||
int r;
|
||||
|
||||
to = strappenda(dest, "/sys/fs/cgroup/", hierarchy);
|
||||
|
||||
r = path_is_mount_point(to, false);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to determine if %s is mounted already: %m", to);
|
||||
if (r > 0)
|
||||
return 0;
|
||||
|
||||
mkdir_p(to, 0755);
|
||||
|
||||
if (mount("cgroup", to, "cgroup", MS_NOSUID|MS_NOEXEC|MS_NODEV|(read_only ? MS_RDONLY : 0), controller) < 0)
|
||||
return log_error_errno(errno, "Failed to mount to %s: %m", to);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int mount_cgroup(const char *dest) {
|
||||
_cleanup_set_free_free_ Set *controllers = NULL;
|
||||
_cleanup_free_ char *own_cgroup_path = NULL;
|
||||
const char *cgroup_root, *systemd_root, *systemd_own;
|
||||
int r;
|
||||
|
||||
controllers = set_new(&string_hash_ops);
|
||||
if (!controllers)
|
||||
return log_oom();
|
||||
|
||||
r = cg_kernel_controllers(controllers);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to determine cgroup controllers: %m");
|
||||
|
||||
r = cg_pid_get_path(NULL, 0, &own_cgroup_path);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to determine our own cgroup path: %m");
|
||||
|
||||
cgroup_root = strappenda(dest, "/sys/fs/cgroup");
|
||||
if (mount("tmpfs", cgroup_root, "tmpfs", MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME, "mode=755") < 0)
|
||||
return log_error_errno(errno, "Failed to mount tmpfs to /sys/fs/cgroup: %m");
|
||||
|
||||
for (;;) {
|
||||
_cleanup_free_ char *controller = NULL, *origin = NULL, *combined = NULL;
|
||||
|
||||
controller = set_steal_first(controllers);
|
||||
if (!controller)
|
||||
break;
|
||||
|
||||
origin = strappend("/sys/fs/cgroup/", controller);
|
||||
if (!origin)
|
||||
return log_oom();
|
||||
|
||||
r = readlink_malloc(origin, &combined);
|
||||
if (r == -EINVAL) {
|
||||
/* Not a symbolic link, but directly a single cgroup hierarchy */
|
||||
|
||||
r = mount_cgroup_hierarchy(dest, controller, controller, true);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
} else if (r < 0)
|
||||
return log_error_errno(r, "Failed to read link %s: %m", origin);
|
||||
else {
|
||||
_cleanup_free_ char *target = NULL;
|
||||
|
||||
target = strjoin(dest, "/sys/fs/cgroup/", controller, NULL);
|
||||
if (!target)
|
||||
return log_oom();
|
||||
|
||||
/* A symbolic link, a combination of controllers in one hierarchy */
|
||||
|
||||
if (!filename_is_valid(combined)) {
|
||||
log_warning("Ignoring invalid combined hierarchy %s.", combined);
|
||||
continue;
|
||||
}
|
||||
|
||||
r = mount_cgroup_hierarchy(dest, combined, combined, true);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (symlink(combined, target) < 0)
|
||||
return log_error_errno(errno, "Failed to create symlink for combined hiearchy: %m");
|
||||
}
|
||||
}
|
||||
|
||||
r = mount_cgroup_hierarchy(dest, "name=systemd", "systemd", false);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* Make our own cgroup a (writable) bind mount */
|
||||
systemd_own = strappenda(dest, "/sys/fs/cgroup/systemd", own_cgroup_path);
|
||||
if (mount(systemd_own, systemd_own, NULL, MS_BIND, NULL) < 0)
|
||||
return log_error_errno(errno, "Failed to turn %s into a bind mount: %m", own_cgroup_path);
|
||||
|
||||
/* And then remount the systemd cgroup root read-only */
|
||||
systemd_root = strappenda(dest, "/sys/fs/cgroup/systemd");
|
||||
if (mount(NULL, systemd_root, NULL, MS_BIND|MS_REMOUNT|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_RDONLY, NULL) < 0)
|
||||
return log_error_errno(errno, "Failed to mount cgroup root read-only: %m");
|
||||
|
||||
if (mount(NULL, cgroup_root, NULL, MS_REMOUNT|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME|MS_RDONLY, "mode=755") < 0)
|
||||
return log_error_errno(errno, "Failed to remount %s read-only: %m", cgroup_root);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mount_tmpfs(const char *dest) {
|
||||
char **i, **o;
|
||||
|
||||
@ -3309,6 +3415,11 @@ int main(int argc, char *argv[]) {
|
||||
|
||||
kmsg_socket_pair[1] = safe_close(kmsg_socket_pair[1]);
|
||||
|
||||
/* Tell the parent that we are ready, and that
|
||||
* it can cgroupify us to that we lack access
|
||||
* to certain devices and resources. */
|
||||
(void) barrier_place(&barrier);
|
||||
|
||||
if (setup_boot_id(arg_directory) < 0)
|
||||
_exit(EXIT_FAILURE);
|
||||
|
||||
@ -3330,10 +3441,12 @@ int main(int argc, char *argv[]) {
|
||||
if (mount_tmpfs(arg_directory) < 0)
|
||||
_exit(EXIT_FAILURE);
|
||||
|
||||
/* Tell the parent that we are ready, and that
|
||||
* it can cgroupify us to that we lack access
|
||||
* to certain devices and resources. */
|
||||
(void)barrier_place(&barrier);
|
||||
/* Wait until we are cgroup-ified, so that we
|
||||
* can mount the right cgroup path writable */
|
||||
(void) barrier_sync_next(&barrier);
|
||||
|
||||
if (mount_cgroup(arg_directory) < 0)
|
||||
_exit(EXIT_FAILURE);
|
||||
|
||||
if (chdir(arg_directory) < 0) {
|
||||
log_error_errno(errno, "chdir(%s) failed: %m", arg_directory);
|
||||
@ -3472,8 +3585,10 @@ int main(int argc, char *argv[]) {
|
||||
fdset_free(fds);
|
||||
fds = NULL;
|
||||
|
||||
/* wait for child-setup to be done */
|
||||
if (barrier_place_and_sync(&barrier)) {
|
||||
/* Wait for the most basic Child-setup to be done,
|
||||
* before we add hardware to it, and place it in a
|
||||
* cgroup. */
|
||||
if (barrier_sync_next(&barrier)) {
|
||||
_cleanup_event_unref_ sd_event *event = NULL;
|
||||
_cleanup_(pty_forward_freep) PTYForward *forward = NULL;
|
||||
char last_char = 0;
|
||||
@ -3515,6 +3630,9 @@ int main(int argc, char *argv[]) {
|
||||
* control to the code to run inside the container. */
|
||||
(void) barrier_place(&barrier);
|
||||
|
||||
/* And wait that the child is completely ready now. */
|
||||
(void) barrier_place_and_sync(&barrier);
|
||||
|
||||
sd_notify(false,
|
||||
"READY=1\n"
|
||||
"STATUS=Container running.");
|
||||
|
@ -502,14 +502,16 @@ int cg_get_path(const char *controller, const char *path, const char *suffix, ch
|
||||
}
|
||||
|
||||
static int check_hierarchy(const char *p) {
|
||||
char *cc;
|
||||
const char *cc;
|
||||
|
||||
assert(p);
|
||||
|
||||
if (!filename_is_valid(p))
|
||||
return 0;
|
||||
|
||||
/* Check if this controller actually really exists */
|
||||
cc = alloca(strlen("/sys/fs/cgroup/") + strlen(p) + 1);
|
||||
strcpy(stpcpy(cc, "/sys/fs/cgroup/"), p);
|
||||
if (access(cc, F_OK) < 0)
|
||||
cc = strappenda("/sys/fs/cgroup/", p);
|
||||
if (laccess(cc, F_OK) < 0)
|
||||
return -errno;
|
||||
|
||||
return 0;
|
||||
@ -1732,3 +1734,54 @@ CGroupControllerMask cg_mask_supported(void) {
|
||||
|
||||
return mask;
|
||||
}
|
||||
|
||||
int cg_kernel_controllers(Set *controllers) {
|
||||
_cleanup_fclose_ FILE *f = NULL;
|
||||
char buf[LINE_MAX];
|
||||
int r;
|
||||
|
||||
assert(controllers);
|
||||
|
||||
f = fopen("/proc/cgroups", "re");
|
||||
if (!f) {
|
||||
if (errno == ENOENT)
|
||||
return 0;
|
||||
return -errno;
|
||||
}
|
||||
|
||||
/* Ignore the header line */
|
||||
(void) fgets(buf, sizeof(buf), f);
|
||||
|
||||
for (;;) {
|
||||
char *controller;
|
||||
int enabled = 0;
|
||||
|
||||
errno = 0;
|
||||
if (fscanf(f, "%ms %*i %*i %i", &controller, &enabled) != 2) {
|
||||
|
||||
if (feof(f))
|
||||
break;
|
||||
|
||||
if (ferror(f) && errno)
|
||||
return -errno;
|
||||
|
||||
return -EBADMSG;
|
||||
}
|
||||
|
||||
if (!enabled) {
|
||||
free(controller);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!filename_is_valid(controller)) {
|
||||
free(controller);
|
||||
return -EBADMSG;
|
||||
}
|
||||
|
||||
r = set_consume(controllers, controller);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -132,3 +132,5 @@ int cg_migrate_everywhere(CGroupControllerMask supported, const char *from, cons
|
||||
int cg_trim_everywhere(CGroupControllerMask supported, const char *path, bool delete_root);
|
||||
|
||||
CGroupControllerMask cg_mask_supported(void);
|
||||
|
||||
int cg_kernel_controllers(Set *controllers);
|
||||
|
Loading…
x
Reference in New Issue
Block a user