55 lines
2.5 KiB
C
55 lines
2.5 KiB
C
#define _GNU_SOURCE
|
|||
|
|
#include <errno.h>
|
||
|
|
#include <linux/sched.h>
|
||
|
|
#include <seccomp.h>
|
||
|
|
#include <stdio.h>
|
||
|
|
#include <stdlib.h>
|
||
|
|
#include <sys/socket.h>
|
||
|
|
#include <unistd.h>
|
||
|
|
|
||
|
|
/* Layered on an empty mount tree, fresh PID/user/network namespaces, no caps,
|
||
|
|
* no inherited descriptors and an immutable runtime closure. Deny kernel and
|
||
|
|
* process escape surfaces; retain V8's ordinary memory/thread syscalls. */
|
||
|
|
static void checked(int result) {
|
||
|
|
if (result < 0) {
|
||
|
|
fprintf(stderr, "seccomp rule: %d\n", result);
|
||
|
|
exit(1);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
int main(void) {
|
||
|
|
scmp_filter_ctx filter = seccomp_init(SCMP_ACT_ALLOW);
|
||
|
|
if (!filter) return 1;
|
||
|
|
const char *denied[] = {
|
||
|
|
"fork", "vfork", "execveat", "ptrace", "process_vm_readv", "process_vm_writev",
|
||
|
|
"pidfd_getfd", "kcmp", "bpf", "perf_event_open", "userfaultfd", "io_uring_setup",
|
||
|
|
"mount", "umount2", "pivot_root", "chroot", "move_mount", "open_tree", "fsopen",
|
||
|
|
"fsconfig", "fsmount", "fspick", "mount_setattr", "open_by_handle_at",
|
||
|
|
"name_to_handle_at", "setns", "unshare", "keyctl", "add_key", "request_key",
|
||
|
|
"reboot", "kexec_load", "kexec_file_load", "init_module", "finit_module",
|
||
|
|
"delete_module", "swapon", "swapoff", "syslog", "acct", "sethostname",
|
||
|
|
"setdomainname", "iopl", "ioperm", "lookup_dcookie", NULL
|
||
|
|
};
|
||
|
|
for (size_t i = 0; denied[i]; i++) {
|
||
|
|
int syscall = seccomp_syscall_resolve_name(denied[i]);
|
||
|
|
if (syscall != __NR_SCMP_ERROR)
|
||
|
|
checked(seccomp_rule_add(filter, SCMP_ACT_ERRNO(EPERM), syscall, 0));
|
||
|
|
}
|
||
|
|
/* glibc falls back to clone when clone3 is unavailable. Only thread creation
|
||
|
|
* is admitted; subprocesses and new namespaces cannot be manufactured. */
|
||
|
|
checked(seccomp_rule_add(filter, SCMP_ACT_ERRNO(ENOSYS), SCMP_SYS(clone3), 0));
|
||
|
|
checked(seccomp_rule_add(filter, SCMP_ACT_ERRNO(EPERM), SCMP_SYS(clone), 1,
|
||
|
|
SCMP_A0(SCMP_CMP_MASKED_EQ, CLONE_THREAD, 0)));
|
||
|
|
const unsigned long namespaces[] = {CLONE_NEWUSER, CLONE_NEWNS, CLONE_NEWNET,
|
||
|
|
CLONE_NEWPID, CLONE_NEWIPC, CLONE_NEWUTS, CLONE_NEWCGROUP};
|
||
|
|
for (size_t i = 0; i < sizeof(namespaces) / sizeof(namespaces[0]); i++)
|
||
|
|
checked(seccomp_rule_add(filter, SCMP_ACT_ERRNO(EPERM), SCMP_SYS(clone), 1,
|
||
|
|
SCMP_A0(SCMP_CMP_MASKED_EQ, namespaces[i], namespaces[i])));
|
||
|
|
checked(seccomp_rule_add(filter, SCMP_ACT_ERRNO(EPERM), SCMP_SYS(socket), 1,
|
||
|
|
SCMP_A0(SCMP_CMP_NE, AF_UNIX)));
|
||
|
|
checked(seccomp_rule_add(filter, SCMP_ACT_ERRNO(EPERM), SCMP_SYS(ioctl), 1,
|
||
|
|
SCMP_A1(SCMP_CMP_EQ, 0x5412))); /* TIOCSTI */
|
||
|
|
checked(seccomp_export_bpf(filter, STDOUT_FILENO));
|
||
|
|
seccomp_release(filter);
|
||
|
|
return 0;
|
||
|
|
}
|