]> git.ipfire.org Git - thirdparty/lxc.git/commitdiff
start: Add Landlock restrictions to monitor
authorStéphane Graber <stgraber@stgraber.org>
Wed, 27 Aug 2025 04:22:55 +0000 (00:22 -0400)
committerStéphane Graber <stgraber@stgraber.org>
Sun, 31 Aug 2025 21:50:05 +0000 (17:50 -0400)
Signed-off-by: Stéphane Graber <stgraber@stgraber.org>
src/lxc/start.c

index b5e27eed35673899f25636d8828f427acd5fdd89..d566dd5ddbf95e2b35b82cf4387787e0c4a31c0b 100644 (file)
@@ -6,6 +6,7 @@
 #include <errno.h>
 #include <fcntl.h>
 #include <grp.h>
+#include <linux/landlock.h>
 #include <poll.h>
 #include <pthread.h>
 #include <signal.h>
 #include "strlcpy.h"
 #endif
 
+#if HAVE_LANDLOCK_MONITOR
+#ifndef landlock_create_ruleset
+static inline int
+landlock_create_ruleset(const struct landlock_ruleset_attr *const attr,
+                       const size_t size, const __u32 flags)
+{
+       return syscall(__NR_landlock_create_ruleset, attr, size, flags);
+}
+#endif
+
+#ifndef landlock_restrict_self
+static inline int landlock_restrict_self(const int ruleset_fd,
+                                        const __u32 flags)
+{
+       return syscall(__NR_landlock_restrict_self, ruleset_fd, flags);
+}
+#endif
+
+#ifndef landlock_add_rule
+static inline int landlock_add_rule(const int ruleset_fd,
+                                   const enum landlock_rule_type rule_type,
+                                   const void *const rule_attr,
+                                   const __u32 flags)
+{
+       return syscall(__NR_landlock_add_rule, ruleset_fd, rule_type, rule_attr,
+                      flags);
+}
+#endif
+#endif /* HAVE_LANDLOCK_MONITOR */
+
 lxc_log_define(start, lxc);
 
 extern void mod_all_rdeps(struct lxc_container *c, bool inc);
@@ -572,11 +603,123 @@ int lxc_set_state(const char *name, struct lxc_handler *handler,
        return 0;
 }
 
+#if HAVE_LANDLOCK_MONITOR
+static int lxc_mainloop_protect(void)
+{
+       int err;
+       int ruleset_fd;
+
+       /* Detect the supported Landlock ABI. */
+       int abi;
+       abi = landlock_create_ruleset(NULL, 0, LANDLOCK_CREATE_RULESET_VERSION);
+       if (abi < 0) {
+               /* Landlock not supported. */
+               SYSERROR("Landlock not supported on system");
+               return -1;
+       }
+
+       struct landlock_ruleset_attr ruleset_attr = {
+               .handled_access_fs =
+                       LANDLOCK_ACCESS_FS_WRITE_FILE |
+                       LANDLOCK_ACCESS_FS_READ_FILE,
+       };
+
+       /* Define a new ruleset. */
+       ruleset_fd = landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
+       if (ruleset_fd < 0) {
+               /* Failed to create ruleset. */
+               SYSERROR("Failed to create landlock ruleset");
+               return -1;
+       }
+
+       /* Allow /sys/fs/cgroup write access. */
+       struct landlock_path_beneath_attr path_beneath = {
+               .allowed_access =
+                   LANDLOCK_ACCESS_FS_READ_FILE |
+                   LANDLOCK_ACCESS_FS_WRITE_FILE,
+       };
+
+       path_beneath.parent_fd = open("/sys/fs/cgroup", O_PATH | O_CLOEXEC);
+       if (path_beneath.parent_fd < 0) {
+               SYSERROR("Failed to open /sys/fs/cgroup");
+               close(ruleset_fd);
+               return -1;
+       }
+
+       err = landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, &path_beneath, 0);
+       close(path_beneath.parent_fd);
+       if (err) {
+               SYSERROR("Failed to update ruleset");
+               close(ruleset_fd);
+               return -1;
+       }
+
+       /* Allow /dev/ptmx write access. */
+       path_beneath.parent_fd = open("/dev/ptmx", O_PATH | O_CLOEXEC);
+       if (path_beneath.parent_fd < 0) {
+               SYSERROR("Failed to open /dev/ptmx");
+               close(ruleset_fd);
+               return -1;
+       }
+
+       err = landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, &path_beneath, 0);
+       close(path_beneath.parent_fd);
+       if (err) {
+               SYSERROR("Failed to update ruleset");
+               close(ruleset_fd);
+               return -1;
+       }
+
+       /* Allow /dev/pts write access. */
+       path_beneath.parent_fd = open("/dev/pts", O_PATH | O_CLOEXEC);
+       if (path_beneath.parent_fd < 0) {
+               SYSERROR("Failed to open /dev/pts");
+               close(ruleset_fd);
+               return -1;
+       }
+
+       err = landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, &path_beneath, 0);
+       close(path_beneath.parent_fd);
+       if (err) {
+               SYSERROR("Failed to update ruleset");
+               close(ruleset_fd);
+               return -1;
+       }
+
+       /* Prevent getting more privileges. */
+       if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {
+               SYSERROR("Failed to restrict monitor privileges");
+               close(ruleset_fd);
+               return -1;
+       }
+
+       /* Apply the Landlock restrictions. */
+       if (landlock_restrict_self(ruleset_fd, 0)) {
+               SYSERROR("Failed to enforce Landlock ruleset");
+               close(ruleset_fd);
+               return -1;
+       }
+
+       close(ruleset_fd);
+
+       return 0;
+}
+
 void *lxc_handler_mainloop_thread_fn(void *arg)
 {
+       int ret;
        struct lxc_async_descr *descr = arg;
 
-       return INT_TO_PTR(lxc_mainloop(descr, -1));
+       ret = lxc_mainloop_protect();
+       if (ret < 0) {
+               ERROR("Failed to protect the monitor process");
+               goto out;
+       }
+
+       ret = lxc_mainloop(descr, -1);
+
+out:
+       return INT_TO_PTR(ret);
 }
 
 int lxc_handler_mainloop(struct lxc_async_descr *descr, struct lxc_handler *handler)
@@ -585,6 +728,17 @@ int lxc_handler_mainloop(struct lxc_async_descr *descr, struct lxc_handler *hand
        int ret;
        pthread_t thread;
 
+       /* Skip protection if a seccomp proxy is setup. */
+       if (!handler || !handler->conf || handler->conf->seccomp.notifier.proxy_fd > 0) {
+               /* Landlock not supported when seccomp notify is in use. */
+               SYSERROR("Skipping Landlock due to seccomp notify");
+
+               /* We don't need to use thread then */
+               return lxc_mainloop(descr, -1);
+       }
+
+       INFO("Spawning monitor as a thread for Landlock confinement");
+
        ret = pthread_create(&thread, NULL, lxc_handler_mainloop_thread_fn, (void *)descr);
        if (ret) {
                return log_error_errno(-1, ret,
@@ -603,6 +757,7 @@ int lxc_handler_mainloop(struct lxc_async_descr *descr, struct lxc_handler *hand
 
        return PTR_TO_INT(exit_code);
 }
+#endif
 
 int lxc_poll(const char *name, struct lxc_handler *handler)
 {
@@ -658,7 +813,11 @@ int lxc_poll(const char *name, struct lxc_handler *handler)
 
        TRACE("Mainloop is ready");
 
+#if HAVE_LANDLOCK_MONITOR
        ret = lxc_handler_mainloop(&descr, handler);
+#else
+       ret = lxc_mainloop(&descr, -1);
+#endif
        if (descr.type == LXC_MAINLOOP_EPOLL)
                close_prot_errno_disarm(descr.epfd);
        if (ret < 0 || !handler->init_died)