diff options
author | Omar Polo <op@omarpolo.com> | 2021-09-19 17:08:12 +0000 |
---|---|---|
committer | Omar Polo <op@omarpolo.com> | 2021-09-19 17:08:12 +0000 |
commit | 3499ce5a9ac180a805d8e507207accf8ea352f48 (patch) | |
tree | f420f9dd34ec65af43ec12bba967d59f0afed211 /sandbox.c | |
parent | d85aa60208bc38ff99fb170559188d5ec9545e04 (diff) |
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.
Diffstat (limited to 'sandbox.c')
-rw-r--r-- | sandbox.c | 99 |
1 files changed, 99 insertions, 0 deletions
@@ -84,6 +84,10 @@ sandbox_logger_process(void) #include <stdio.h> #include <string.h> +#if HAVE_LANDLOCK +# include "landlock_shim.h" +#endif + /* uncomment to enable debugging. ONLY FOR DEVELOPMENT */ /* #define SC_DEBUG */ @@ -416,9 +420,88 @@ sandbox_seccomp_catch_sigsys(void) } #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)); |