Hacker News new | past | comments | ask | show | jobs | submit login
Unikernels are secure (unikernel.org)
193 points by ingve on July 10, 2017 | hide | past | favorite | 140 comments



> Good luck guessing that address. Our own unikernel, IncludeOS, randomizes addresses at each build, so even with access to source code you still don’t know the memory layout.

"There is one mortal sin in computer security (or by the way, in any kind of security) that is feeling safe. It’s just a variation of pride to be true, but it’s very deadly. Blindly trusting a protection technology is an extreme error."

Read up on DEP. Randomizing function addresses at each build is useless when it comes to attack mitigation. You need to randomize addresses at each execution.


Pardon me, but this seems pretty different from the use cases of DEP. Perhaps you'd like to expound further on this.

Imagine a standard webapp deployed via CI. Each time any feature is shipped the entire layout is randomized and then deployed. While a redundant system will have multiple copies with an identical layout, that layout will rotate. If required, a CI process could rebuild the system into N parallel groups with N different layouts (your degree of paranoia is the salt to taste here).

Given that these systems are frequently rebuilt with each deploy it seems nearly identical to me in practice to a relink-on-run.


lots of binaries are distributed as compiled, thats one of the attractions of a unikernel you can just hand people a VM image of whatever the hell you want.

In regards to what could be rebuilt, computer security is plagued with "coulds". What matters is what is done.


This is a tricky argument you make because it's so asynmetrical. You can essentially cherry pick the worst opsec and the most dedicated attacker and then use it to discount an entire technique.

I will not play this "Use OpenBSD because it has a new technique that is fashionable" game that I know is being played.

Custom baremetal custon built VMs is not really in the business of distribution appliance images. That's Docker. Different use case. So what IS done is a workflow to rebuild these images when they change.

And that's often. Their HTTP routing table is literally baked into the image. They're not "reusable" for the most part.


DEP isn't relevant. If you're compiling your own kernel this is a completely viable security technique.

Where it fails is in distros. AFAIK most linux distros do something similar (based on Grsecurity patches) but it's useless because... they then distribute the binary, with everything necessary to get addresses.

But if you're distributing it internally/ it's a secret, yeah, you're going to benefit from that.


is linux randomizing is per exec ?

ps: BSD have started to randomize at boot time, fun times


> is linux randomizing is per exec ?

Not entirely sure what you're meaning to ask, but yes, Linux does KASLR and tries very hard not to leak pointer addresses from the running kernel to unprivileged userspace:

https://lwn.net/Articles/569635/

It's been enabled by default since the upstream 4.12 kernel, and usually well before that in distros.


KALSR's not the same as ASLR (although ideologically related). ALSR has been around for a good number of years in userspace via randomize_va_space sysctl

(edit I presume that's what the poster above mentioned re: per exec)


yes ALSR, sorry, acronym fail on my part. So ALSR does randomize bindings on each execution ?


Yes. If I run e.g. `ldd /bin/bash` multiple times, it shows different memory addresses each time. That's ASLR.


cat /proc/self/maps is a better way to see that. If it's built with PIE, the executable base is randomized. Some distributions use full ASLR across the board (Alpine, Android, ChromeOS, Hardened Gentoo) while others don't yet enable PIE globally or have only recently started and it isn't all pushed out yet (OpenSUSE, Fedora, Debian).


Also worth mentioning that `ldd` only demonstrates how shared libraries are mmap'ed at randomized locations. Stack and heap allocations are also randomized at execution time.


I never bothered to check, what a m... I am. Thanks a lot :)


Linux randomises userspace executables on per-exec (check out /proc/self/maps).

BSD also does the same for userspace, but their new feature is that they randomise the kernel per-boot (by re-linking the objects). This is a far stronger form of kASLR (though I think they're calling it something else because it requires relinking the kernel binary).

Even Windows does this well.

Bragging about boot-time ASLR seems quite odd for an article like this, given that traditional operating systems have had similar (and in many ways superior) features like this for more than a decade (PaX introduced ASLR in Linux in 2001, OpenBSD had it by-default in 2003, and Linux shipped it by-default in 2005).


I would hesitate to call something Secure unless some major pen test effort were engaged to try to break it.

This is a corollary to "If it isn't tested it is broken". (Another corollary is "Even if it is tested, it may still be broken.") This is more so with security.

Just looking at some of the assumptions, e.g., that the lack of a shell significantly increases the difficulty of an attack, leaves me with doubts about the claims.


My thoughts exactly. If they wrote "more secure", this would make more sense. In the current form it's just boasting.


> I would hesitate to call something Secure unless some major pen test effort were engaged to try to break it.

Precisely. On another domain: "My crypto algorithm has never been broken, it is secure". No it is not, it's just too irrelevant for anyone to be interested in auditing it.


Yes, exactly. The article would be much stronger if it also covered ways in which unikernels either don't improve security, or are actually more vulnerable.


Hi author here. I agree wholeheartedly. I wasn't thinking in absolutes, but I absolutely can understand how it could read that way. I could have titled the post "Unikernels are resonably secure", but then nobody would have read it.

I was doubtful about mentioning the lack of shell. Technically it doesn't really do much in terms of security, but it does make a compromise harder. And currently, where we stand, a lot of the security comes from things being really, really hard to compromise. Guessing adresses, not having a shell, potentially no filesystem, etc.


With "Unikernels are reasonably secure", but then nobody would have read it., you might also take into account whether the audience, having read it, are happy that they did. The title sounded pretty absolute.

So you do have a noble goal. When I am asked to assess software, to gain a first order approximation, I often look at the culture of the organization that produced it. On the high end, that often translates to some sort of formal process involving un-fun letters like 'ISO' and 'SOC'.

For smaller organizations, or OSS software, I try to get a feel for how the developers seem to be thinking about security. Some of the cultural things I see in your writeup are encouraging--Smaller Codebase, Removing hardware emulation, Cutting off access to ring 0--all bode well for how you are thinking about producing secure software.

But there are some things that are missing from your description. What is your development process? In particular how and when do you attack your assumptions about how the code actually works? What flaws have you found in your testing? Zero findings could mean 1) it wasn't tested 2) it wasn't tested very hard 3) the testers were not very good at security testing.

And as OpenBSD's history has illustrated, some things are totally secure until they aren't.


To be fair, I enjoyed reading the article. My conclusion though is that unikernels (an interesting concept in itself) offer only a part of the functionality of a standard OS. As such, it might actually be easier to limit the functionality of a general OS in such a way that it does only what needs to be done and nothing more - and there already some relatively mature solutions for that.


I'm interested that they didn't list the "old" reason people thought unikernels could be more secure. That is that they could be small enough to be completely written in higher languages. With something like https://github.com/GaloisInc/HaLVM you could bring to bear formal methods in more straight forward ways.

This of course left the host still needing to be secured using traditional approaches but that surface area was smaller.

I'd buy that you can't list that in an article about why all unikernels are already secure, but that seemed to be the heart of the pitch to me when I first started hearing it.


well, you also have to keep in mind that this article is written by the CEO of a company producing a particular unikernel implementation -- one that isn't written in a higher language.


So to summarize, unikernels stop a couple of attack vectors (syscalls and running other programs (notably shells) that are loadable from well-known paths). Address randomization is not unique to unikernels, and the other benefits are theoretical for the time being until hypervisors provide more unikernel support (though the mention of paravirtualization makes me think performance (particularly network) in this theroetical hyper-restricted mode will be poor).

While I certainly sympathize with the idea that the typical kitchen-sink deployment of a full suite of administrative/troubleshooting/build tools on an application server is a security risk, it seems the actual improvements enumerated here are relatively small potatoes.

Apologies for not knowing a lot about the state of the art here, but I'm curious about how these unikernels address VM-internal security (eg, network, filesystem, inter-process level stuff). Do you farm all of that out to the hypervisor? Design around the lack of it? _Is_ there even "inter-process stuff"?


So, basically, we're going to run all your code at ring0, so a single bug is now either a total DoS (kernel shuts down) or an exploit vector straight to ring0. Although they talk hypothetically about a defense for that involving the MMU, this is not implemented in IncludeOS.

The build should not be relied on to be secure either; certain classes of memory disclosure bugs could leak out enough memory to let you work out e.g. a ROP payload, which wouldn't require injecting any machine code necessarily. Plus, a long-lived machine, or a single instance deployed across many machines, would present insufficient diversity to protect against a determined attacker. The gist here appears to be the notion that their build diversity gives them most of their security, while they've eliminated most of the traditional defense layers (ASLR, DEP/NX, privilege separation, API isolation, etc.)

If the attacker gets her machine code running in the unikernel environment, it's game over no matter what. The machine code can just directly implement a subset of needed functionality without having to call out to unikernel functions.

In short - this post has a hell of a click bait title but really insufficient evidence to suggest that these are truly "secure".


Keep in mind that it's ring 0 inside a VM that contains almost nothing. Really a unikernel is just a process that uses hypercalls instead of system calls. So exploiting a unikernel is no worse than exploiting a user process, and the article explains a few reasons it could be more secure.

But they should definitely add ASLR.


Just like an operating-system executes processes, a VMM executes unikernels. An operating system is responsible for randomizing the layout of processes in memory. Shouldn't the actual question here be "Does my hypervisor implement some mechanism for randomizing the layout of my unikernel?"


I don't think it matters whether it's implemented by the hypervisor or the unikernel since it's all open source. But for compatibility with existing hypervisors/clouds one could imagine a boot loader that loads the main unikernel at a random address, sets up page tables with NX, and then makes a one-way transition to ring 3 so that the page tables cannot be modified.


> one could imagine a boot loader that loads the main unikernel at a random address, sets up page tables with NX, and then makes a one-way transition to ring 3

True - I suppose I'm trying to hold onto the "kernel:hypervisor :: process:unikernel" analogy here for no good reason. Following it suggested that it might be the hypervisor's responsibility.


IncludeOS has ASLR. Or as close to ASLR as you can get without dynamic linking. We randomize the layout when we link. And since we typically re-link the image on each configuration change in a network of 1000s VMs each one will have a different memory layout.


In a unikernel, how would you even detect you've been hacked ?


> In short - this post has a hell of a click bait title but really insufficient evidence to suggest that these are truly "secure".

From the original pull request drafting this post:

> While I agree that making hyperbolical claims isn't typically good I'm not sure if it is bad here. There seem to be a tradition in unikernel land with outlandish claims in various blogposts ("Unikernels will kill containers in five years", "Unikernels are unfit for production", etc) so I wrote it with that tone in mind.

https://github.com/Unikernel-Systems/unikernel.org/pull/45


If the unikernel people keep up with the hyperbole the potential audience will learn to just ignore most posts about unikernels. Please don't use hyperbole.


> So, basically, we're going to run all your code at ring0

From the article: 'However, if the hypervisor can set up the VM before it is booted and provide paravirtualized interfaces to the hardware, we don’t need access to ring 0.'

Which would be pretty sweet, no?


Ring count doesn't matter when everything is the same ring.


The argument that something IS secure is somehow supported by a whole lot of hypotheticals of the form "we could do $X and that would be secure if someone also did $Y".

So say it how it is: unikernels COULD be secure if all this work that needs to be done and we haven't done was done...maybe


That's par for the course. Unikernel people talk about VMs that use megabytes of RAM while cloud providers only provide gigabytes. Unikernel people talk about spawning VMs in tens of milliseconds while clouds take tens of seconds.


Hypothetically, if there were a buffer overflow in a unikernel process, wouldn't it potentially give the exploit full-system access, whereas a normal operating system would detect the out-of-bounds memory access and kill the process? I'm sorry if this is an ignorant question.


First, standard page fault mechanisms would still be in place, that's a processor/architecture feature, so out of bounds memory access would be detected just as in a normal operating system. Of course, a normal operating system does not detect many/most buffer overflows.

In general, you could have a unikernel where it's impossible to get executable buffer overflow because there are no memory pages that are both writable and executable - as the article describes, that'd need some help from the hypervisor for the initialization.

Return oriented programming exploits could work anyway, though.


I could be wrong, but I think this is what Memory Management Units are for, on processor chips. It's one major differentiator between application processors and more embedded ones like Cortex-M or ATMega MCUs, which typically can't run complex modern operating systems. A lot of those chips have a simpler Memory Protection Unit that does basic enforcement of permissions like read+execute, read+write, etc, but they lack the ability to partition memory virtually between a large number of potential 'owners.'

I think - I'm still learning about this embedded stuff.


The point is that once you're doing that, and implementing communication between all your separate owners (which map pretty precisely to a reasonable definition of "processes")... you've just implemented another operating system, not a unikernel.


I'm not super familiar with this, but it seems like the question is what is "full-system" in this case if each application is sandboxed with its own embedded kernel? I mean there's no shell, ostensibly all other applications and system functions are bundled similarly, right?


It'd make exploits more difficult, but you'd still be able to upload your own code to do whatever you want.

The trouble is that instead of uploading "/bin/sh" you'd have to upload the whole shell which you'd want to run; instead of making a syscall for something that a normal kernel would do but this one doesn't, you'd have to compile and upload appropriate code (including device drivers) to get that done.

It'd be a "bring your own machine code" party.


Not necessarily. The kernel can run the code in non ring 0, ie in userspace. I can't speak to what is typical, but i imagine you'd get most of your gains by stripping most vulnerable code out of the system outright.


> if there were a buffer overflow in a unikernel process, wouldn't it potentially give the exploit full-system access

Yes

> whereas a normal operating system would detect the out-of-bounds memory access and kill the process?

No. A buffer overflow in a normal operating system usually results in the the attacker gaining control over the process that the buffer overflow occurred in. This is not as bad as an attacker gaining full system access, but is generally plenty for an attacker to accomplish their goals.


Best i can tell, unikernels are a reaction to the shift from VMs to containers in cloud services. This by pairing down the content of a VM to the bare minimum.

Meaning that if you have a buffer overflow or similar, all you could access was what was in the VM (unless you also happen to pack a VM escape).

That said, i can't help think "DOS in a can" whenever i read about unikernels.


If it wasn't for the negative connotations people have with DOS I'd say it a lot more. Implementing a unikernel chain loader and a simple shell would be quite simple. That would be quite DOS-like.

IncludeOS actually came out of a research need to stress-test hypervisors and run 10000 VMs on a single host. Since it is a clean slate system it has some different characteristics and those can be exploited in certain use cases and so people have kept on working on it.


In addition to the blog post, there's an interesting discussion on Github that happened before the blog post was published: https://github.com/Unikernel-Systems/unikernel.org/pull/45


Excellent discussion. I'm not knowledgeable enough in Linux internals to know whether the ring0 versus ring3 criticism is warranted. Is it just a matter of if/when an attacker achieves escalated privileges they will have far more attack surface on ring0?


There is quite a difference.

Ring 3 is userspace, you can't interact with hardware or the operating system or anything not in Ring 3 directly.

Ring 0 is everything. There are no restrictions and nothing stops you from writing "Ahahah You didn't say the magic word!" over your entire memory until the CPU crashes.

Having root on a linux kernel is heavily restricted compared to this and still runs in Ring 3 like all other userspace code.

As root, you still have to run the kernel. As Ring 0, you can replace the kernel. Or run your own OS.


Access to ring 0 on a traditional OS is indeed usually "game over".

In the case of a unikernel deployed on a hypervisor this is not the case, since there is not much else in ring 0 that you wouldn't already have access to from ring 3. Conceptually you can think of the hypervisor as "kernel space" and anything inside the unikernel as "userspace".

There are advantages to running the unikernel solely in ring 3 (eg. immutable page tables) however this is not a requirement for security.


I still see it as worse than a normal application being compromised.

When Ring 0 is compromised, there is no alert or anything to protect the app from compromise. If there is an exploit, it's game over.

However, in Ring 3 and a normal kernel, you get various protections that allow the kernel to recognize some attacks and shutdown the application immediately or even shutdown the kernel.

This prevents a compromised app from running to some extend.

A unikernel cannot do this. If the app is compromised and I don't notice and don't restart it...

Even worse, the attacker could use it as leverage to infect other unikernel based instances of the app to gain some permanence against restarts by simply reinfecting when an instance goes down.

The unikernel is not userspace, not even conceptually. The hpyervisor will not shutdown the app unless it executes illegal instructions. The kernel will shutdown misbehaving programs more easily.


> A unikernel cannot do this. If the app is compromised and I don't notice and don't restart it...

I disagree. There's no reason such mitigations (not sure what exactly you're referring to) can't be implemented by the monitor process (ukvm in the Solo5/ukvm model).

I'd also argue that a normal kernel does not do any integrity checks on the code running in a user process, so the model is exactly the same.

> Even worse, the attacker could use it as leverage to infect other unikernel based instances of the app to gain some permanence against restarts by simply reinfecting when an instance goes down.

For that they'd need to break out of the virtual machine and into the hypervisor / monitor. Which is by no means impossible, but with careful design of unikernel-specific monitors can be much reduced. Of course, I'm by no means suggesting you should back your unikernels with a monitor along the lines of QEMU :-)


1) You are in Ring 0. There is no defense unless you reimplement a normal Kernel to run a process in Ring 3 along with the monitoring process and capabilities management... etc

2) No, the attacker is most likely there because of some bug in the app, once in the network, it becomes harder to stop the attacker infecting other instances.

3) Hypervisors are not perfect. There are known instances of people infecting the host through the hypervisor.


1) Virtualized Ring 0 != Ring 0. See section 24.6 "VM-execution control fields" of the Intel SDM for details of the controls a hypervisor can use to modify guest behaviour.

2) The same applies to any application, not just unikernels.

3) I completely agree.


1) You are still on a Ring 0. On a normal operating system, an exploited app has a limited action range, depending on the system settings. A lot of exploits simply do not work because the operating system kills the process. On Ring 0, even virtualized, all these protections do not work. You have full control within the VM and you can't have some process within the VM to check this as it is equally vulnerable.

2) Yes but Unikernels do not provide special protection against this either.


> On a normal operating system, an exploited app has a limited action range

Minor point, but this seems to be a bit lost in the discussion: Generally 1 unikernel == 1 VM (or, virtualization-backed sandbox, the use of "virtual machine" brings too much baggage with it) == 1 application.

So, the attack scope for the class of attacks we're debating is equally limited to a single application, just like on a normal operating system.


Not quite.

When you write an exploit for a normal operating system application, you can't, for example, just write your payload into data memory and start executing it. You can't jump to the address of an array and have the CPU execute it's contents.

On a unikernel this sort of thing becomes trivial since everything is Ring 0 and all protections can be trivially disabled.

You can just write your payload into any arbitrary data field and your exploit only needs to jump to it, even with address randomization this can be exploited (ASLR and similar techniques do not prevent exploits, only make them harder)

The exploiting just becomes a whole lot easier.

It's not even remotely more secure than Ring 3 code running on a kernel that has strict capability enforcement.


> On a unikernel this sort of thing becomes trivial since everything is Ring 0 and all protections can be trivially disabled.

If the hypervisor sets the relevant VMCS control fields for the unikernel to cause a VMEXIT on (for example) a load of CR3 and any EPT violations and sets up the guest with a static set of read-only and executable (or in fact, with EPT you could even do execute-only) pages then there is no way the unikernel can disable these protections.

Having said that, I'm not arguing that running unikernels in Ring 0 is the best approach for security, just that it's not impossibly insecure.

With ukvm we're also looking into running all of the unikernel in (virtualized) Ring 3 with nothing running in Ring 0. However, this needs more experimentation since with x86 you can't (for example) easily handle legitimate exceptions (e.g. divide by zero) from Ring 3.


Unikernels are not that new. CMS running on VM has existed since the 1960s, with CMS being the unikernel (actually, a unitasking OS about as complex as MS-DOS) and VM being what's now called a hypervisor: Something which multiplexes hardware, but provides no APIs, such that VM guests think they're running alone on bare hardware.

(VM can even run VM as a guest, recursively, which is useful for developing the newer version of VM on a machine other people are using for other work.)

Therefore, we can evaluate these claims by asking what we know about security breaches on VM/CMS systems.


Something which multiplexes hardware, but provides no APIs, such that VM guests think they're running alone on bare hardware.

There is what amounts to an "API". The virtualized software thinks it's talking directly to hardware, so the API is just an emulation of that hardware. Since controlling hardware is almost invariably messier than doing system calls, the API is actually more complex.

Therefore, we can evaluate these claims by asking what we know about security breaches on VM/CMS systems.

Here's an HN post I wrote a few years ago, about VM exploits: https://news.ycombinator.com/item?id=9241807

Plenty of exploits to be found, many of which were related to emulation of "I/O channel programs", which are, essentially, System/370 I/O "hardware".


> There is what amounts to an "API". The virtualized software thinks it's talking directly to hardware, so the API is just an emulation of that hardware. Since controlling hardware is almost invariably messier than doing system calls, the API is actually more complex.

Nobody said a hypervisor was completely trivial, but just to be clear, a pure hypervisor wouldn't "emulate" any hardware except what was physically present on the system. The line between "virtual machine" and "hypervisor" and "emulator" are blurry enough as it is, and I'd like to be clear about what the subject of this little thread is.

> Here's an HN post I wrote a few years ago, about VM exploits: https://news.ycombinator.com/item?id=9241807

> Plenty of exploits to be found, many of which were related to emulation of "I/O channel programs", which are, essentially, System/370 I/O "hardware".

Very interesting. Thank you.


In addition one would have to address hardware virtualization features that would increase security, no?


"Unikernels have no shells. Most attacks I’ve seen invoke /bin/sh to modify the system they are attacking. Without a shell the attacker doesn’t have this opportunity. This forces the attacker to use machine code to subvert the system, decreasing the likelihood of succeeding with the attack."

Timmy the software developer and his many years of forensic analysis have led him to believe that you need a shell to root something. Literally the entire page's takeaway about security is "It's hard to use, so it's secure!"


One thing that I never understand when I read articles like this (especially this article) is that the "security improvements" that are mentioned are entirely related to there being no userspace (you don't need a shell or syscalls if you don't have a userspace).

But the whole point of userspace is to provide privilege separation between the kernel and userspace. In unikernels everything is in the kernel, and you can freely mutate in-kernel state without any segmentation violations. In other words, of course there are no syscalls when there is no separation between user code and the kernel ("no syscalls" is the description of a unikernel). So, what is the tangible security benefit?

What I want to know is how do you protect against in-kernel ROP? Or hell, just plain old-fashioned stack overflows? The ASLR mentioned is not really useful because it's only on-recompile (Linux's ASLR is on-execution). The only experience I've had with writing shell code was messing around with https://microcorruption.com/ for a few evenings, and even I know that putting everything into the same address space is just asking for trouble (of course you can mitigate it, but praising the premise as a feat of security seems to be missing the point to me).

Maybe I'm just massively misinformed, but I simply don't see how someone can proclaim that "unikernels are secure" by just re-stating the premise as the justification for their security (and then following up with a bunch of hypotheticals).


A unikernel is running a single process in a single address space. So yes, if you compromise the app you compromise the whole system but the whole system is the app.


That's not entirely true in practice. I'm currently playing with rump kernels deployed on top of seL4's hypervisor to give my platform the security posture of a unikernel inside the security posture of seL4 VM isolation.

You could potentially compromise the rump kernel, but you still wouldn't be able to break out of the VM's isolation context.


The app is still compromised.

On top of that, you didn't get to reap the benefits of any of the protections you get from a proper Kernel like Ring 3 execution, NX bits on pages, guard pages on the stack, etc.


I don't really like the argument that something is 'secure' because it is not vulnerable in the same ways that an alternative is. I think this is why I like talking about encryption so much. It's possible to mathematically prove the security of encryption algorithms, and all that's really left to pick apart is the implementation, politics and impact.


Ehh... you can often prove that X algorithm is not susceptible to A, B or C attacks, but you cannot usually prove that the algorithm is "secure" in a fundamental sense.


https://en.wikipedia.org/wiki/Provable_security

You can prove that it's secure in that the algorithm itself does not leak information.


With some exceptions, those proofs make assumptions about their primitives like having block ciphers be "unpredictable permutations" or having hash functions be "random oracles". These proofs also make assumptions that information doesn't leak in other ways (like BEAST, heartbleed, timing attacks, poor entropy sources, etc).

In other words, those proofs don't have as much real-world significance as you'd like.


Yeah, but this is not what most people think of when they think secure, is it? That is, this proves that people have to actually break an algorithm to know what it is protecting. This does nothing to show that an algorithm is unbreakable.

(As an example of my understanding, rot13 does nothing to randomize distribution of characters, so it "leaks" information about what it has modified. This sort of leakage can be proven as absent from your algorithm. Anything else is a bit tougher.)


Unless you're dealing with OTPs, hashing, or lattice-based schemes, there are almost no information theoretical guarantees in encryption. For a field that uses math so heavily, it's surprising how rare traditional proofs are in the cryptology literature. Most encryption schemes are specifically designed to be hard to analyze.


This isn't for lack of trying on the part of cryptographers - unconditional proofs of security for most modern cryptosystems would imply that P and NP are separate. For example, a direct proof that SHA-256 is collision-resistant would imply that one-way functions exist unconditionally.


Even crypto algorithms rely on unproven assumptions for their security, even ignoring e.g. side-channel attacks.


Not true. Look up "resilience to information leakage".


I was talking more about cryptographic hardness assumptions such as the existence of one-way functions. Nevertheless, even in http://www.ccs.neu.edu/home/wichs/thesis.pdf, which was the first search result for that term, one of the first phrases is 'subject only to the constraint that the rate of leakage is bounded'.


This is not true at all for most forms of cryptography. Hash functions and symmetric cryptography are just shown to be resistant against all known attacks. For public-key cryptography there are sometimes security reductions to computational problems that are thought to be hard, but even schemes like RSA do not have such a security proof.


> Unikernels have no shells. Most attacks I’ve seen invoke /bin/sh to modify the system they are attacking. Without a shell the attacker doesn’t have this opportunity. This forces the attacker to use machine code to subvert the system, decreasing the likelihood of succeeding with the attack.

This argument is completely incoherent.


And, while this is a more minor rebuttal, it's still worth saying: it's totally incorrect. Modern exploits (for definitions of "modern" meaning "written after 1999") do not care if you have a shell on your system.

The author has presumably confused the concept of a POC, which is an exploit reduced and simplified for the consumption and understanding of laypeople, with that of a real exploit.


As one the early users[1] guilty of using the "there is no shell" argument for unikernel security, I agree with what you say. I would rephrase the argument as "there is no fork()/exec()", meaning that a unikernel cannot spawn a new process.

A unikernel application which only executes native code[2], combined with a hypervisor / hardware that enforces immutable page tables and NX, AFAICT leaves ROP/JOP as the sole remaining attack vector for RCE. Further, each deployment of the application should be done via a new build (or at least re-link), which can employ the build-time ASLR Per mentions to mitigate ROP/JOP attacks.

I'd be interested in your thoughts on what other relevant attack vectors I've not thought of that would allow for a persistent compromise of a unikernel. Feel free to discuss here or on the devel.unikernel.org forum thread[3].

[1] Page 4 in this whitepaper from 2015: https://wiki.xenproject.org/images/3/34/XenProject_Unikernel...

[2] Does not contain a JIT or interpreter able to "eval()" new code.

[3] https://devel.unikernel.org/t/unikernels-are-secure-here-is-...


What difference does it make if there's no fork/exec? Back in 1999 we had MOSDEF and CORE IMPACT, both of which used staged loaders to boot up a language runtime in a remote process after compromising it with a stack overflow. In a broader sense this is stuff that viruses had been doing for about 8 years prior to that.

Only executing native code is no defense at all. You're not even stopping attackers from the '90s like that.

ROP is only a hardship until exploit code can find a way to allocate executable memory or hook the program's own dynamic execution (note: this is not necessarily a "JIT" or "eval") capabilities. ASLR only matters when exploits can't find infoleaks.

Really, what you're describing here is the browser security model. When staffed by the largest, best security teams in the world, the browser security model almost works.


> Back in 1999 we had MOSDEF and CORE IMPACT (...)

I'm not familiar with these. Googling "MOSDEF attack" suggests Massive Attack songs and "CORE IMPACT" various products by Core Security, I presume you're referring to neither? :-)

> Only executing native code is no defense at all. You're not even stopping attackers from the '90s like that.

> ROP is only a hardship until exploit code can find a way to allocate executable memory or hook the program's own dynamic execution (...)

Only executing native code loaded at deployment time, i.e. presumably from a trusted source. What I meant by "immutable page tables" is that after loading the unikernel image the set of executable pages is fixed, and all of those are read only. This is not something current unikernels do, but that's more due to lack of development time rather than any inherent implementation complexity.

If there is no way for the exploit code to ask the hypervisor for new executable pages or overwrite existing ones then the amount of damage it can do and its ability to persist in the running system is greatly reduced.

> Really, what you're describing here is the browser security model. When staffed by the largest, best security teams in the world, the browser security model almost works.

It's similar but not the same. The browser security model breaks down in a large part due to exposing way too many APIs, most of which are too complex to sensibly audit. Linux system calls have a similar problem, which is why you have to hand-craft things like seccomp profiles to match your particular application.

Unikernels (as used to implement network services) lend themselves extremely well to running on top of a well defined API / sandbox boundary, designed with security in mind. Again, we're not there yet, but the steps we need to explore in this direction are fairly clear.



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.


> If you want to prevent fork/exec, that's super easy to do in a conventional Linux application:

And mmap(), mprotect(), ptrace(), ..., $syscall_du_jour(). It's not about fork/exec, it's about limiting the APIs available to the unikernel by default and by design.

Yes, you can do this for conventional Linux applications. But I wouldn't call it super easy, at least not for the vast majority of developers out there.


I assume it's referring to shell injection attacks. No shell means no shell injections. Of course, it also means that whatever functionality you were calling in a child process now must be in the same address space as the rest of your system. (E.g. rather than call Imagemagick to convert some incoming images from the client, you now must have a library with equivalent functionality in your unikernel.) Whether that's a net security improvement is questionable.


If the argument is supposed to mean "Unikernel applications cannot call system("echo " + user_provided_input)", well, it's pretty easy to do that in conventional applications: just don't call system. If you want to be sure instead of relying on static analysis/code review, rm /bin/sh in your production containers, or something.

Changing all your code to respect this standard is strictly less hard than porting it to a unikernel, because it's one step of porting it to a unikernel.


"No system calls"

Yeah, they're direct function calls now. How exactly is that more secure? The author's right that many traditional exploit paths are gone, but only because they've been replaced by even easier ones.


Because you don't know how to call the functions, as explained in the article. To call a function, you need to know its address among other things. And you don't, since the address was decided at random at build time (or even boot time in some systems).


There are other ways to find functions besides knowing their addresses a priori.


How is this done?

I think kernels try not to leak the address of system calls at run time, and if they are scattered around in a 64bit address space they are tough to search for. Educate me!


I have no idea, but couldn't you use some part of the software you just compromised that makes syscalls?


There are no syscalls as the fine article explains.


Ah, wait, I mis-read your comment and it's too late to edit mine. Yes, that sounds plausible, and particularly if you have the source code, which you do for many common servers.


They assume that 99% of libc was stripped out at build time. The full network stack is probably there but there might not be much filesystem code.


The real problem for unikernels is that they are effectively irrelevant until the major clouds provide millisecond boot times along with appropriate pricing models that charge for execution on a millisecond level basis.

Developers love to write code, but unless unikernel developers start speaking up instead of writing code, the major clouds won't get on board and provide the needed fast boot times plus pricing, which makes all this unikernel code not a lot more than interesting personal / academic projects.

Questions of unikernel security are certainly academic if the cloud infrastructure doesn't run them in the most effective manner.


Why are millisecond bit times necessary? Linux doesn't boot in a millisecond...


Because you can then build single function unikernels that do one thing very fast then disappear from RAM.

The point being that docker is actually a really clunky thing that is reinventing the virtual server ecosystem within the OS which is pointless.

Serverless systems are better implemented as unikernels than docker images.

Unikernels make it possible to boot an entire OS in milliseconds, service an inbound web request, then disappear.


It's more efficient to let the unikernel start a HTTP server and keep running for a few minutes. With AWS Lambda you're paying for walltime even if the CPU is idle because it makes a long running network request. Imagine you're making 10 HTTP requests per second and each has a very long latency of 5 seconds. With 10 AWS Lambda "instances" running concurrently you're billed for 50 seconds. With a single EC2 instance you're billed for 5 seconds.


I understand the use case; I just don't understand why operating as an HTTP server isn't sufficient?


What's the use case for booting an entire OS to service a request?


when the OS is just some library functions statically compiled in and the entire binary is a few megabytes that you can load directly into memory, why not?


Serverless applications


Hard to take claims of security seriously for untyped languages which are not provably correct and lack a denotational semantics. Let's start by provably not compiling buffer overflows and memory overreads considering those are the vast majority of RCE and privilege escalation.


C++ is strongly typed. I believe there are frameworks that will give you denotational semantics in C++.

It shouldn't surprise anyone that feature X is available in C++. But I acknowledge there are tons of shotguns strewn around everywhere in C++ so you can blow your feet off.


C++ is not "strongly typed" when compared to almost any other statically-typed language, e.g. Ada, OCaml, Haskell or Rust. This is a big part of why it is so insecure.


> There are two aspects of unikernel security. There are two aspects of unikernel security. One is the vector into the VM and the other is the vector from the VM into the hypervisor.

The "vector into the VM" might not be as obvious. For example, in a network security contest in college I compromised the source repository of the server to give my team an advantage and won. That was before git it was just a folder on the server. Others argued it wasn't fair but the instructor sided with me since that's a plausible scenario in real life.

There also tempest-like attacks for extracting private keys and such, so it's there at also at least the 3rd vector - from VM to the hardware as well. And forth from VM to the network (but I guess these can be subsumed in the "to the hypervisor" case).


So now the hypervisor is ring0, the cloud provider is the OS, and the OS is the app. IP is the IPC and the Internet is the IPC bus.

I suppose the hypervisor is more minimal and secure. Tenenbaum, thou art avenged.


The intro video is somewhat confusing - it indicates that they're secure but if that they're "harder to attack" and says if they are compromised they can be restored. Also, he said "in the future, I believe unikernels will be more or less unbreakable"

I think you could describe that as "more secure" or perhaps more generically "safer" rather than "secure."


don't unikernels run everything in ring0. if you run everything in ring0 you can't claim not having system calls is a security feature :/


And if you run a Linux kernel with a C app as one, for example, it's probably possible to run a shellcode proxy in memory, even if you can't modify the app code, you could ROP if a big enough buffer overflow is possible on the stack, etc.?


None of the claims in this have anything to do with the essence of security, they all address the window-dressing of security.

What does it mean for something to be secure? As I see it, for something to be secure, it means that every security invariant holds under all attacker models.

Let's break this down. What's a security invariant, and what's an attacker model?

A security invariant is a statement like "information may only be read by principals authorized to do so" or something similar. The blog post talks about "oh well every exploit I've seen pops a shell with /bin/sh and we don't have /bin/sh so you can't hack it." This attacks the symptom and not the disease. If the attacker is in a position to run /bin/sh you are already at a disadvantage, the attacker was previously going to break your security invariant by spawning a shell and then copying out your secrets. They could instead just use their code execution to read the secrets directly, ala heartbleed, if they are in the same address space.

An attacker model describes the attacker, their motivations/goals, and capabilities. For example, what if the attacker wanted to take your unikernel enabled app and repurpose it into an e-mail spam bot? Well, there isn't a writeable FS and there isn't the ability to exec stuff, so the old model of "copy some files up and run them" won't work. However, is there executable memory in general? Can the application allocate existing executable memory? If the application is, or involves, for example a modern JavaScript interpreter, then it has to be able to do this in some capacity. So now the attacker modifies their methods so that their goal can be achieved in the constraints of this new system.

There is an argument you can make about sandboxing and principle of least privilege, but that argument transcends unikernels really. Look at the sandboxing and separation strategies used by programs like chrome, vsftpd, and qmail. They (in conjunction with enforcement technologies like seccomp-bpf or selinux) can make the same guarantees about the lack of system calls and least privilege.

I'm pretty sure that the title of this post really should be "unikernels are obscure" because that's the layer of protection you're really getting here.


> IncludeOS is a clean-slate unikernel written in C++ with performance and security in mind.

One of those things doesn't belong there. Guess which?


Yes it reads like satire. MirageOS, having been written in OCaml, would have a better claim to being written with security in mind.


C++ (who uses that when perfectly good C is available?)

:)


The article states:

>"Unikernels on the other hand don’t have system calls. They only have function calls."

If my program is compiled against glibc functions which wrap actual system calls how does my program work when there is no userland?

Is there a compiler that compiles my program to a unikernel target? Can someone explain how this works?


"Secure" and yet they aren't even using a memory safe language such as Rust.


Looks to be unikernel is just a RTOS, why the name unikernel then ? Whats the difference with RTOS?


A unikernel could be designed as an RTOS, but not all RTOS are unikernels (in fact, I'm not aware that any are). Indeed, QNX, a RTOS, is not implemented using a single address space, nor is your application linked against the kernel and supporting libraries into a unikernel. While it's certainly possible that you could implement an application/unikernel with real-time guarantees, I'm not aware of a specific unikernel implementation that targets that space specifically.


Thanks or your kind reply, I use to find difficult to classify between RTOS and unikernels technically.


A RTOS means a specific thing regarding timing (and sometimes fairness) guarantees. A unikernel isn't that.


> What we need is this:

> * packet interface for the network

> * a block interface for some storage

> * a serial port to output console data

Some applications need additional devices, such as for example hardware RNG, atomic clocks and/or GPGPU.


Why am I reading this "Unicorns"? :D


It's secure if and only if there's a formal proof.


"Secure" sounds like a binary attribute but it's actually not. A formal proof doesn't save you from errors in the specification. At some point you need trust, the amount of trust needed is inversely proportional to the security.


Without a formal proof there is no measure of correctness at all.


Lots of passing tests mean nothing to you?


When an exploit only needs one edge case not handled right? No, lots of unit tests means nothing. Maybe less than nothing due to the false sense of security they seem to give you.


So you trust a software that has had zero testing equally to a software like sqlite that has extensive testing?


lets not go too far! tests can provide a security advantage over regressions or bugs introduced in the future. They are necessary but not sufficient to prove a particular security level.


That's like saying that the world was flat before they could prove that it is round.


No, that's like saying that we don't know what shape the world is before we could prove that it is round.


No, it's like saying it is round "if and only if" we can prove that it is round.


Which the scientifical method tells you to verify experimentally, i.e. with tests.


No, the point is: The sphere doesn't care if you can prove if it is a sphere or not, it stays a sphere. If you CAN TELL if it is (or not) is a different matter entirely, so it doesn't work that you say "if and only if it is proven" when you make claims about something having a certain property.

i.e. Just because I can't tell if you have two hands to type this doesn't mean you don't have them.

"You have two hands if and only if there's a formal proof that you have two hands"


Who proves the proof though? :)


Generalisations are generalising.


Claiming something is secure is more like claiming an over unity perpetual machine




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: