If you want to prevent fork/exec, that's super easy to do in a conventional Linux application:
#include <linux/seccomp.h>
#include <linux/filter.h>
#include <linux/audit.h>
#include <sys/prctl.h>
#include <asm/unistd.h>
#include <stddef.h>
prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
struct sock_filter filter[] = {
BPF_STMT(BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, arch)),
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, AUDIT_ARCH_X86_64, 1, 0),
BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_KILL),
BPF_STMT(BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, nr)),
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_fork, 4, 0),
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_vfork, 3, 0),
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_clone, 2, 0),
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_execve, 1, 0),
BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW),
BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_KILL),
};
struct sock_fprog fprog = {
.len = sizeof(filter) / sizeof(filter[0]),
.filter = filter,
};
prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &fprog, 0, 0);
or, if you're using systemd, simply SystemCallFilter=~fork,vfork,clone,execve
Again, the hard part here is architecting your application not to need fork/exec for its own purposes anyway, which (as far as I'm aware) is a strict prerequisite of porting it to the unikernel model. If you can do that, forswearing fork/exec for security purposes is straightforward and doesn't require a unikernel. (Also, if you take the prctl approach, you can do that after you've forked a few helper processes, which is probably a much easier port!)Or in other words, the argument isn't "unikernels are secure," it is "programming in an environment without fork/exec is secure," and unikernels are a needlessly complicated way to provide that environment.