Unikernels + connected devices

10th Feb 2017

By Thomas Ryd

Do you want your connected device to be more secure, faster, more energy efficient, and easily managed? The promise of unikernels includes all of these. Are unikernels the next big thing for connected devices? At Mender, we are excited about the possibilities brought forward by unikernels and how it can make our increasingly connected world more secure and reliable.

What are unikernels?

Unikernels are part of the evolution of IT. If virtual machines (VMs) are a natural extension of physical machines, unikernels can be thought of as a natural extension of containers. Where machines share physical resources to create virtual machines and containers share physical or virtual operating systems to create virtual applications, unikernels do not share anything. This is the core of what makes them more secure, faster, and easier to manage.

Unikernels are self-contained binary images containing only the pieces of software you need to get your application going. Think of unikernels as a for-purpose system, similar to a for-purpose RTOS (real-time operating system). This is opposed to a general purpose technology commonly used today. While the Linux kernel alone consists of more than 20 million lines of code, a unikernel program can be as small as a few kilobytes.

Several unikernel projects exists. One is IncludeOS, offered by our friends in Oslo. IncludeOS is an includable, minimal unikernel operating system for C++ services.

A fast and energy efficient solution doing one thing and one thing only

Booting a physical machine can take minutes, while virtual machines can take less. Containers can fire up in microseconds. Unikernels can be even faster.

Unikernels can boot so fast you might not even need to have them running. The time it takes to boot a unikernel service and respond to a request versus just responding to a request can be be almost indistinguishable, as shown by Heikki Mahkonen at Ericsson in his Unikernels meet NFV article. To some extent, you can think of them as serverless functions. You call the service, it does its job, and then you don’t have to worry about it anymore. It goes dormant.

While impressive, the speed is not the most fascinating. If you are in industries such as high-frequency trading or big data crunching processing jobs, writing a whole stack using unikernel technologies can give you a speed advantage.

A related feature is CPU efficiency. A 2015 paper made by Bratterud, Walla et al. at Department of Computer Science at Oslo and Akershus University College of Applied Sciences showed that a service used less CPU cycles when they ran in IncludeOS than the same binary and workload run under Linux. That’s exciting news for battery powered devices.

Isolation reduces attack surface

Aside from human error, most vulnerabilities today are caused by bugs in shared libraries. With hardware virtualization, zero bytes of code is directly shared between host and service and between individual services, as each service (virtual machine) must communicate directly via hardware at the instruction level. The isolation makes it hard to leverage an exploit in one unikernel to access others. Since all code is compiled into one blob, it is hard for adversaries to even know what libraries are being used. An exploit in the host of a virtualization technology (for instance Docker), might allow access to all its hosting services.

The more bytes of physically unprotected data or code your service contains, the greater the attack surface. With unikernels all libraries are static which means that the bootable image will only contain what the service needs and nothing else. This leads to a reduced attack surface.

The concept of unikernels invite users to write puristic for-purpose applications. This in itself will lead to less bloated applications and implicit reduced attack surface.

There is no shell by default. A unikernel is a singular process that just happens to contain parts of an operating system. You don’t need a terminal application inside any more than you’d need a terminal inside your Apache web server process. All the classical attack vectors that include gaining root access to a device are immediately gone. Clearly, unwise developers can still build in back doors, but there is no universal access point to all the various subsystems like the one a terminal represents.

Atomicity makes it more manageable

One of the greatest challenges managing infrastructure today revolves around diversity. Diversity in operating systems, middleware, applications, architectures, teams, business units and multi-region datacenters. The more self-contained something is (with well-defined interfaces), the easier it will be to manage. The opposite of atomicity is dependency, and if you are in operation I am sure you can attest to the nightmare of dealing with dependencies, whether it relates to software packages, subordinates, or beloved managers.

At Mender we have developed what we want to be the world’s easiest over-the-air updater for connected devices. Our initial release offers a dual partition approach. Whenever you want to update a piece of software running on your device, you will compile a new image of the whole stack. Mender will deploy the new version to your devices and reboot the device into the new partition. If something fails, Mender will detect it and reboot into the previously running image. This is a nice, atomic, but expensive way to update devices. It is currently the preferred way of updating devices over-the-air today.

The downside of this approach is that you would need two partitions on your device (more storage), the amount of data traffic is substantial and there will be down-time on the device during boot. Enter unikernels. If your stack has been written using unikernels, we can use the same approach, but with reduced drawbacks:

The amount of data needed to be transferred is minimal Minimal amount of data means reduced storage cost There is no downtime during boot (it can take place in memory)

Pretty cool, right? But what about the downside?

What might prohibit the promise of unikernels?

Ecosystem proliferation. Inherently in its design lies the biggest challenge of unikernels. Everything needs to be built over. A major reason for the success of many technologies lies in its ecosystem and out-of-the box solutions. Think how easy it is to find a library to solve a use case in javascript, or find a suitable Docker image. With unikernels, almost everything needs to be written from scratch. Serious effort and time will be required to build a satisfactory out-of-the box solutions.

In the case of includeOS for instance, while a POSIX interface is in the works which would allow for other language runtimes, currently everything needs to be written in C++. The people behind includeOS work hard to provide the basic building blocks (TCP/IP stack, HTTP, TLS, etc.) to get you started, but for the most part, whatever it is you will need you probably have to develop it yourself in the beginning. To make it more complicated the various unikernel projects unfortunately have their own favorite language. Where includeOS loves C++, MirageOS another popular project (owned by Docker), has found their love in OCaml.

Summary

I hope this post made you curious about unikernels. As the health and security for all us will depend on the reliability of more and more machines that instruct our lives, I hope we converge towards better, more reliable and predictable engineering practices and technologies. It is our duty to always seek ways to improve and my hope is that you by now have entered “unikernel” into your google search field.

Thanks to Alfred Bratterud over at includeOS for valuable input.