1. Regarding bugs: There is a lot of code in your monolithic OS kernel. There have been, and will be, a lot of vulnerabilities in that code. Various sandboxing mechanisms notwithstanding, the vast majority of processes running on your system can potentially use all of that code as an attack vector. Unikernels let you switch off access to most of that "host" code, and, through the use of a library OS, contain only the minimal set of libraries needed to run your application in the "guest". Regarding updates: Valid point, but no different from any modern application of substantial complexity, except that now you also have to update (e.g.) the library providing its TCP stack.
2. Unikernels don't make that problem any worse. In fact, if you run on something like Muen (https://muen.sk/), it'll mitigate a bunch of these attacks by giving you a less precise RDTSC at the subject ("VM") level.
3. I don't follow. Implement what?
The paper suggests using system-call filtering via seccomp or similar to block off this attack surface, but if you trust seccomp to have zero bugs, you might as well do traditional process sandboxing a la Google Chrome.
The problem unikernels solve is that traditionally, a vulnerability in C code means "game over" and the box is completely pwned with all memory and storage directly accessible, while with a unikernel, a vulnerability means only that an attacker can access other functionality within the app, and can't rely on eg. a shell or a debugger being available to arbitrarily move bytes around.
Correct. Conceptually the same applies to all Type 2 hypervisors. Type 1 less so, but you could still potentially exploit Xen and you have all of the dom0 to play around in.
> The paper suggests using system-call filtering via seccomp or similar to block off this attack surface, but if you trust seccomp to have zero bugs, you might as well do traditional process sandboxing a la Google Chrome.
If you do that, you will have to do a line-by-line analysis of the code you want to sandbox in order to determine exactly which syscalls it's using. With the approach presented in our paper the developer does not need to care about this. For example, she can develop her MirageOS unikernel as a normal UNIX process and switch to a guaranteed-to-work-minimal-seccomp sandbox with a simple change of target (build-time configuration option).
But yes, you are now trusting seccomp instead of KVM. I believe in giving people the ability to easily make that choice.
> The problem unikernels solve is that traditionally [...] can't rely on eg. a shell or a debugger being available to arbitrarily move bytes around.
That part does not change with the sandboxing mechanism changing. The stuff that's inside is still only your unikernel.
Which do you trust more?
For the 80% case, on x86_64, I consider them more or less equivalent. KVM is used daily in anger to provide isolation (e.g. GCE, and now ChromeOS) and has been around much longer but you need to trust hardware virtualization which is a large attack surface on the CPU itself. Given what we've learned about CPU vulnerabilities over the last year, I wouldn't be surprised to find some lurking in the VT-x/SVM implementations.
Seccomp OTOH is difficult to use correctly for arbitrary/existing applications but exposes less of the kernel (depending on your metric, see our paper) and does not need hardware virtualization.
For the 20% case, where the stakes are higher (e.g. High Assurance), I would use something like Muen or SeL4 and run a disaggregated system on top of that.
I'm aware it's not an entirely accurate metaphor, but it might turn out to be a more accessible one for opening a conversation.
This code is usually there for a reason and a unikernel will have to implement large swaths of this code anyway.
Networking isn't trivial and I don't think anyone will be reimplementing the TCP/IP, UDP/IP, ARP, DHCP and more stacks without breaking in a few bugs themselves.
Example, modern TCP congestion control requires a very precise packet pacing and timing.
Another example, TCP retransmission will be required at some point, the code doing this will have to run side-by-side with the app. This will also be some amount of code.
And all this just piles up and up.
The only real advantage I see for unikernels is that because all the hard work is done by the hypervisor they don't have to bother implementing device drivers and task scheduling but end up either replicating or using the hypervisor's own networking stack and a bunch of other subsystems.
The largest part of a monolithic kernel today is devices drivers, by far.
> Networking isn't trivial and I don't think anyone will be reimplementing the TCP/IP, UDP/IP, ARP, DHCP and more stacks without breaking in a few bugs themselves.
Sure. And then more people will use those stacks, and they will get better. The more the merrier, we have too much of a software monoculture anyway.
> The only real advantage I see for unikernels is that because all the hard work is done by the hypervisor they don't have to bother implementing device drivers
This. People continually underestimate the amount of work required to support the hardware ecosystem. This is also why rump kernels (note, not the same term as the unikernel known as Rumprun) are such an achievement, also very much underappreciated.
> The more the merrier, we have too much of a software monoculture anyway.
Linux and some other kernels allow userspace apps to have their own network stack in userspace, latest kernels allow even larger sections of the networking subsystems to be entirely in userspace.
I think this approach should be favored over a unikernel since it uses the natural x86 privilege seperation between userspace and kernel.
*EDIT: Unikernels are neat as replacements for VMs since the abstraction layer can be much less costly/higher performance. They cannot be used as regular applications running on the OS due to how security is implemented.
1) (Putting on my black hat) Attackers don't care about bugs or exploits. They care about running their code on your system. Whether that is as simple as mysqldump or wget'ng a monero cryptominer to run on there it all is based on the premise that the monolithic operating system (whose design is ~50 years old and linux is > 27 years old) is explicitly designed to run multiple programs by multiple users. Keep in mind this design pre-dates commercialized virtualization (eg: vmware) and pre-date "cloud" (eg: aws). If we assume that you are already utilizing VMs (and you are if you are on any public cloud and you are in most private on-prem deployments) the VM then becomes your isolation model. Can you still attack the underlying infrastructure? Sure - but if you can root GCE or AWS I'd say we all have some serious thinking to do on the current state of cloud infrastructure. Contrast and compare that to all the ridiculous headlines you see every single day and the fact that every single RCE that is worth doing entails forking/execve a new process. It's one thing to have the instruction pointer - it's quite another to launch a shell that doesn't exist, link your program to libraries that don't exist, as a user that doesn't exist, download new code when you can't.... etc.
2) Not to belabor this point but side-channel attacks affect everyone and Intel has been taking the hard (in terms of market) approach of simply disabling hyper-threading on some of their hardware.
Security is the number one selling point for unikernels imo.
There has been exactly 1 remotely exploitable bug and exactly 1 information leak bug in openssh in more than a decade (I believe the exploit is actually more than a decade old, need to check though), it is by far the most secure daemon ever. It is hard to overstate just how much of an accomplishment that is. NOTHING matches it in security. This is even more impressive given how widespread it's usage it.
> We need authority if we are sharing peripherals.
I think you'll find that large websites ... just don't do this. The only real purpose of multiple processes on such webservers is administration.
> f we are not going to save memory and implement that as a static library ...
The reason you want to do this is that the slowest thing in modern processors is context-switching. Going from user-space to kernel space. If you have, say, a networking stack in user space you can avoid such context switching entirely. The speed gains are enormous, especially for webservers.
Ridiculous performance in networking also enables many more applications that just won't work without. What do you think about cluster-wide disks, for instance ? SQLite like databases that run against Petabyte-sized files, concurrently, safely. You're just not going to do that efficiently by having it done in the OS.
Re: 2 - this is managed by the hypervisor. SEL4 can be run as a hypervisor and has been formally verified to be correct [1]. Trusting a proven system like SEL4 is a far-cry from trusting Linux's isolation primitives because we can make hard-guarantees about the behavior.
Unikernels have the advantage that they are basically backwards-compatible (you can run any VM on the infrastructure you develop, even if that infrastructure is tuned for unikernel-native applications). With Unikernels, you can achieve VMs that are lighter than containers [2] thereby increasing your customer-per-server ratio.
[1] https://sel4.systems/Info/FAQ/proof.pml [2] http://cnp.neclab.eu/projects/lightvm/lightvm.pdf
Here [0] is a good discussion of unikernels (TheRaven knows what he's talking about). ctrl-f for "You absolutely can not do the same thing with a conventional *NIX OS"
> Explain this to me then: What's the difference, conceptually, between (A) a VM host running a unikernel capable of solving one particular kind of problem, and (B) an OS running on bare metal running a process capable of solving one particular kind of problem?
They are very similar. The main difference is the shape of the OS APIs. In the former scenario, the host provides things that look like CPUs, NICs and things that look like block devices, with no high-level abstractions (e.g. threads, sockets, and filesystems). If these are needed, then the unikernel will link in device libraries that provide them. In contrast, in the later situation the OS will be providing these even when they are not needed and they will add overhead to those code paths.
Additionally, the amount of process state owned by the OS is far greater in the latter case. Things like file descriptors, thread priorities and so on make it far harder to snapshot or migrate an OS process. Hypervisor interfaces tend to be as close to stateless as possible.
To add to this, with the approach we're taking with Solo5 [1], hardware virtualization is only used as one possible sandbox/isolation mechanism. If you look into the code, you'll find that the abstractions presented to the unikernel by the hvt tender are much thinner than those presented to a guest OS by a traditional VMM (e.g. KVM/QEMU).
I agree that if the advantages boil down to saving a hundred MB of RAM of two, and enabling additional compiler optimisations, it won't be compelling to that many folks.
Taken to the extreme though, we can start spinning up instances 'just in time' to deal with incoming requests. Doing this, you could run (and hopefully pay for) your instances for only a tiny fraction of the time your service is in a working/available state.
This has apparently already been demonstrated [0], but their demo has been broken for as long as I can remember. I presume it worked at one point. I don't know if any commercial Xen-based offerings can spin up an instance as quickly as their configuration did, but the idea is there.
2nd. In a pure-unikernel approach, applications can’t steal from each other because there is only one application. Isolation is left to the VM. The goal is to get rid of the redundant isolation of running a multiplexing OS over a multiplexing VM. In the as-processes model, the Linux kernel still provides sandboxing. The goal there is to be a bridge between traditional and unikernel development.
3rd. The goal is to boil down to just virtualization and only the libraries specifically needed by a single specific application.
See, for example: https://www.kernel.org/doc/html/v4.11/gpu/i915.html#batchbuf...
See r600_packet0_check, r600_packet3_check: https://github.com/torvalds/linux/blob/master/drivers/gpu/dr...
NICs are an unfair comparison because the commands for NICs are all generated by the kernel, which is trusted. For GPUs, commands are generated by an untrusted user space driver that translates OpenGL or Vulkan commands into command packets for the GPU. Those command packets contain memory addresses, so in order to achieve isolation between different user space processes in the face of a potentially malicious user space driver, the kernel used to have to validate those addresses in the absence of GPU page tables.
Anyway, this kind of validation really hasn't been necessary for a long time now, despite pcwalton's outdated information, because GPU page tables were phased in around 2010. (The first AMD chip to have them was Cayman in 2010. Since we're citing kernel drivers here, take a look at https://github.com/torvalds/linux/blob/44786880df196a4200c17... and note how the function skips any parsing when virtual memory is used.)
That said, we're not moving in the direction of unikernels, but it has a clear application in my mind. It's a fair bit of work to migrate, and I've yet to see a practical comparison, so it's unclear what the actual benefits over conceptual benefits would be.
https://github.com/rumpkernel/rumprun-packages/tree/master/e...
http://www.erlang-factory.com/static/upload/media/1474729921...
I'm not sure if there's any news on it since then.
> Correct me if I'm wrong.
OpenSSH is probably the single most secure piece of C network software. It isn't exactly a good example for "wide-spread vulnerabilities in middleware".
Once developers get into the workflow of writing applixations that can have fuller control of its environment, rarely one would return back to the world of opaque infrastructure and framework.
1st. I don't see that many bugs in the TCP/IP, filesystems, etc. specially those exploitable by black hat hackers, specially remote exploits. We usually see wide-spread vulnerabilities in the middleware like OpenSSH, which is in the userspace, putting everything in the app is not going to solve that, quite the contrary, we need to wait for the library maintainer to fix the bug, for the applications to integrate with the newer version of the library, and then, update all the applications.
2nd. Side-channel attacks. I'm not really confident in letting applications have that low level of hardware access. I mean, it's really easy for one application to steal the resources from another. We need authority if we are sharing peripherals. Well, we have one peripheral already that is shared by processes, that's memory and look at the mess that is a modern MMU and all the attacks on those that we have seen throughout the decades.
3rd. If we are going to implement that as a shared library, the result is exactly the same as today's micro and hybrid kernels, if we are not going to save memory and implement that as a static library, it's exactly the save as virtualization.
Correct me if I'm wrong.