commit 3499ce5a9ac180a805d8e507207accf8ea352f48 from: Omar Polo date: Sun Sep 19 17:08:12 2021 UTC landlock the server process Trying to implement some landlock policies (rules?) where possible. The server process is, of course, the most dangerous process so start with that. The following should be equivalent to the unveil(2) call on OpenBSD: allows only to read files and directories inside the vhost roots. I'm assuming seccomp is enabled so I'm not trying to disallow actions such as LANDLOCK_ACCESS_FS_EXECUTE or LANDLOCK_ACCESS_FS_REMOVE_FILE which require syscalls that are already disallowed. I'm only trying to limit the damage that the currently allowed system calls can do. e.g. since write(2) is allowed, gmid could modify *any* file it has access to; this is now forbidden by landlock. There are still too many #ifdefs for my tastes, but it's still better than the seccomp code. commit - d85aa60208bc38ff99fb170559188d5ec9545e04 commit + 3499ce5a9ac180a805d8e507207accf8ea352f48 blob - 8cb94881488733f7238b83939a1e63820cbd7224 blob + a5f6c63cd32c4d92c51eb0376594370f41291d62 --- sandbox.c +++ sandbox.c @@ -83,6 +83,10 @@ sandbox_logger_process(void) #include #include #include + +#if HAVE_LANDLOCK +# include "landlock_shim.h" +#endif /* uncomment to enable debugging. ONLY FOR DEVELOPMENT */ /* #define SC_DEBUG */ @@ -415,10 +419,89 @@ sandbox_seccomp_catch_sigsys(void) __func__, strerror(errno)); } #endif /* SC_DEBUG */ + +#if HAVE_LANDLOCK +static int +server_landlock(void) +{ + int fd, err; + struct vhost *h; + struct location *l; + /* + * These are all the actions that we want to either allow or + * disallow. Things like LANDLOCK_ACCESS_FS_EXECUTE are + * omitted because are already handled by seccomp. + */ + struct landlock_ruleset_attr ruleset_attr = { + .handled_access_fs = LANDLOCK_ACCESS_FS_WRITE_FILE | + LANDLOCK_ACCESS_FS_READ_FILE | + LANDLOCK_ACCESS_FS_READ_DIR | + LANDLOCK_ACCESS_FS_MAKE_CHAR | + LANDLOCK_ACCESS_FS_MAKE_DIR | + LANDLOCK_ACCESS_FS_MAKE_REG | + LANDLOCK_ACCESS_FS_MAKE_SOCK | + LANDLOCK_ACCESS_FS_MAKE_FIFO | + LANDLOCK_ACCESS_FS_MAKE_BLOCK | + LANDLOCK_ACCESS_FS_MAKE_SYM, + }; + + /* + * These are all the actions allowed for the root directories + * of the vhosts. All the other rules mentioned in + * ruleset_attr and omitted here are implicitly disallowed. + */ + struct landlock_path_beneath_attr path_beneath = { + .allowed_access = LANDLOCK_ACCESS_FS_READ_FILE | + LANDLOCK_ACCESS_FS_READ_DIR, + }; + + fd = landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); + if (fd == -1) { + switch (errno) { + case ENOSYS: + fatal("%s: failed to create ruleset. " + "Landlock doesn't seem to be supported by the " + "current kernel.", __func__); + case EOPNOTSUPP: + log_warn(NULL, "%s: failed to create ruleset. " + "Landlock seems to be currently disabled; " + "continuing without it.", __func__); + return -1; + default: + fatal("%s: failed to create ruleset: %s", + __func__, strerror(errno)); + } + } + + TAILQ_FOREACH(h, &hosts, vhosts) { + TAILQ_FOREACH(l, &h->locations, locations) { + if (l->dir == NULL) + continue; + + path_beneath.parent_fd = open(l->dir, O_PATH); + if (path_beneath.parent_fd == -1) + fatal("%s: can't open %s for landlock: %s", + __func__, l->dir, strerror(errno)); + + err = landlock_add_rule(fd, LANDLOCK_RULE_PATH_BENEATH, + &path_beneath, 0); + if (err) + fatal("%s: landlock_add_rule(%s) failed: %s", + __func__, l->dir, strerror(errno)); + + close(path_beneath.parent_fd); + } + } + + return fd; +} +#endif + void sandbox_server_process(void) { + int fd; struct sock_fprog prog = { .len = (unsigned short) (sizeof(filter) / sizeof(filter[0])), .filter = filter, @@ -428,10 +511,26 @@ sandbox_server_process(void) sandbox_seccomp_catch_sigsys(); #endif +#if HAVE_LANDLOCK + log_warn(NULL, "loading landlock..."); + fd = server_landlock(); +#else + (void)fd; /* avoid unused var warning */ +#endif + if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == -1) fatal("%s: prctl(PR_SET_NO_NEW_PRIVS): %s", __func__, strerror(errno)); +#if HAVE_LANDLOCK + if (fd != -1) { + if (landlock_restrict_self(fd, 0)) + fatal("%s: landlock_restrict_self: %s", + __func__, strerror(errno)); + close(fd); + } +#endif + if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog) == -1) fatal("%s: prctl(PR_SET_SECCOMP): %s\n", __func__, strerror(errno));