Unikernels Will Create More Security Problems Than They Solve
Unikernels, the most recent overhyped technology in search of a problem to solve, have a number of claimed attributes that make them a “better choice.” One most often claimed is that they are “more secure.” This is the first in a series of articles bringing some light to the reality of unikernels so that you can think about them properly, employ them for what they are good for, and avoid the hype.
Here’s the punch line: With unikernels, your application is running in the same space as the kernel (ring 0). Any buffer overflow could mean giving remote access to the system, and that’s probably a bad day for everyone.
First, let me say that unikernels probably have a place. Most likely around building embedded systems, which is really what spawned them in the first place. The problem is that people want unikernels to be something they are not: a better hypervisor or better application container. One way in which unikernels are “better” are the purported security benefits.
Security is a complex topic. Anyone who has worked in the space for a long time can be quite jaded about it. Folks who have not spent significant amounts of time working in the information security (INFOSEC) space get confused because they look at security one piece at a time, when, of all the IT disciplines, it is the most dependent on systems thinking.
When it comes to security for unikernels they are claimed to be more secure by a single dimension: there is less code and ergo less “attack surface.” Nominally this makes the system more secure because there is less software to attack. In reality, the unikernel architecture may make the system less secure overall.
Understanding Attack Surface
Attack surface is a security term for the aggregate of all of the “attack vectors” or “attack avenues” that are possible against a particular system. So, for example, if you have a web server that faces the Internet and provides access to HTTP/HTTPS, that is part of the attack surface or the area of the system that is attackable. Similarly, the operating system (OS) and any software loaded onto the server(s) would be considered part of the attack surface of a system, because if an attacker gained access to an OS they could attack all of the software there in an attempt to escalate privileges or to otherwise further their penetration of the system.
At its most extreme, the attack surface of a system could really — from the point of view of the attacker — be considered the entire system itself, which makes the notion useless. Instead, most security folks will build a “threat model” around the system, assessing the entry points (attack vectors), the types of attackers, attacker sophistication, and so on. This threat model effectively maps the effective and realistic attack surface of a system.
The important thing to understand here is that attack surface is a holistic measure of what can be attacked and the results of those potential attacks, meaning how deeply penetrated a system can be. Accessing the webserver’s log files over the Internet is not nearly as serious as a remote root exploit where full access to an individual server is achieved, and that system can be used to bootstrap attacks further into the system.
In other words, attack surface must be measured not just in terms of how many attack vectors there are, but also what the result of an attack may be. In other words, the level of penetration that an attacker can achieve along any particular vector must be factored in. There is, therefore, an escalating set of vectors combined with the depth of penetration. Here is a simplified example:
|Vector||Result of Exploit||Relative Surface Area|
|Web server data leakage (logs)||Access to web server logs||x1|
|Webserver remote exploit||Access to server remotely as web server user (e.g. user “apache” or “httpd”)||x5|
|OS privilege escalation bug||Allows escalating from normal user to root – requires remote access||x10|
|Remote access to root (ring 0) level process||Access to server remote as full Administrator||x100|
Where you think the “relative surface area” numbers come down, my point is that while the leaking of web server logs is a problem (and here my baseline), something like remote user access is a much bigger problem (likely a multiple if you are performing threat modeling). And full Administrative access is the beginning of the end in most cases.
Which brings us to the huge elephant in the room: Unikernels may actually be less secure than a traditional general purpose operating system because any exploit is A) remote and B) Administrative (the worst of the worst).
In this way, unikernels increase attack surface. There is nothing special about removing software and running a minimalist system. We have been using this as a standard hardening technique for years, and it can be applied to hypervisors, containers, and even bare metal.
How does this happen? Read on.
Unikernels Run Your App as Administrator/Root
Unfortunately, the core premise of a unikernel is that they flatten the kernel, the operating system, and the application together into a monolithic entity. The best way to think of them is as custom kernels that integrate your application as part of the kernel itself. Just enough kernel is created to provide access to the hardware components (disk, network, RAM, CPU) as necessary for your particular application. No other userland or operating system (OS) components are provided. This all adds up to making the system less secure, even with a smaller attack surface.
For a long time now, traditional operating systems use protection rings that allow escalating levels of access to the system. Ring 0 is where the kernel runs. Device drivers run in ring 1 and ring 2, and applications or “userland” runs in ring 3. These protection rings are what keep one application from being able to read another application’s memory. This is also what stops a simple remote access exploit, like a buffer overflow, against a web server from being able to access other parts of memory (outside of the web server application).
So here is the problem, an attacker has to find a “0-day” exploit in your application’s code to have full and complete access to your unikernel-based system. Your application is running in ring 0, so any buffer overflow can result in remote access to the system and the ability to run arbitrary code on the server, again providing a place for an attacker to bootstrap an attack against the entire system.
Think this is hard or impossible? The attacker doesn’t have the application’s code? Doesn’t know where to strike? Yes, she does.
Attackers are extremely sophisticated, and access to the application’s code is irrelevant. In a presentation at BlackHat/DefCon, Michael Lynn demonstrated a full remote exploit against Cisco routers. This is mainstream bread and butter of both professional attackers and researchers.
I just want to point out that not all of EMC thinks Unikernel’s are a thing. This article misrepresents this. https://t.co/5kq5IjKfkt
— Randy Bias (@randybias) June 21, 2016
So, now you can see how unikernels probably increase the attack surface of your system because everything is running as an already privileged user. You’re quite intentionally removing all of the relatively sophisticated security mechanisms that exist in Linux/Windows, not to mention all of the additional security measures you can add from the default OS installation to increase security and to make it difficult to escalate privileges in an undetected manner.
And all the while you’re doing this, you’re being defrauded with the hype that “unikernels are more secure.”
Linux Can Be Trimmed Down Easily
Want to reduce the attack surface of your application in a secure manner? Run it in a container as an unprivileged user.
— The New Stack (@thenewstack) June 21, 2016
Inside the container should only be the statically linked libraries that your application uses to run (libc, etc.). A container built in this manner is hundreds of kilobytes to tens of megabytes (i.e. just as small as most any unikernel system). Sure, the host running the container has more software, but an attacker doesn’t have immediate access to that software. An attacker has to penetrate your application and then attack the host operating system. Plus, since it’s a general purpose Linux operating systems, many security mechanisms (SELinux, Tripwire) can be running at the host level to keep the app running in the container isolated and to detect changes to the running application that are unauthorized.
Voila! A reduced attack surface that delivers what unikernels pretend to deliver.
Linux and Windows Have Well Understood Security Mechanisms
Unikernels have a long way to go before they recreate the huge number of built-in security mechanisms that general purpose operating systems have. These operating systems inherently include:
- privilege separation
- protection rings
- protected memory spaces
- fine-grained access control (e.g. SELinux)
- application execution enforcement (e.g. AppArmor)
- network filtering
- audit capabilities
- and so much more.
Not only this, but the security of general purpose operating systems is well understood. Security personnel know how they fail, know how to troubleshoot them, know how to harden them, and know how to add additional security mechanisms above and beyond what is possible with the default OS.
Unikernels have none of this and in fact, because of their architecture, it isn’t clear if the same level of security rigor can be applied. Thus, one might reasonably ask why we’d expend scarce engineering resources to fix unikernel security problems when the other benefits of the technology are already available in more proven compute models.
Security Matters and Unikernels are NOT a Secure Model
Before you join the Flavor of the Month crowd and start falling in love with unikernels, consider that a key claim — that they are more secure — appears to be inherently false and possibly misleading. Unikernels are probably less secure than a general purpose OS that is properly configured and are certainly significantly less secure than a VM or container-based approach that is properly configured.
Next, I’ll address unikernels, and their performance benefits followed up by some speculation on what exactly unikernels might be good for and why for the enterprise it probably won’t matter.
Feature image via Pixabay.