Skip navigation
Yellow Corn Kernels with One Red Kernel.png

Risk Analysis: Unikernels vs. Containers, et al.

Unikernels boast limited attack surface and fast execution, but they can be inflexible and challenging to maintain.

The unikernel model places just enough code (libraries and executables) into an autonomous package to run applications. Some unikernel packages are poised as virtual machines or container packages, and where sufficient hardware drivers are present, can be launched onto bare metal or IoT packaging.

A unikernel is often a monolithic executable file that creates an instance of an application. The application lives in a total vacuum until ports are opened by the unikernel’s code to communicate to the outside world. Code executes in memory, unless accompanying hardware drivers (or hypervisor support infrastructure) exist to store data.

Unikernel apps live under the control of hypervisors like QEMU, KVM and VMware in many constructs. With appropriate hardware drivers, unikernel applications can live on bare metal without need for an underlying operating system. Only the code that’s needed to run the application set is present in an executable unikernel package. The benefits are that the attack surface of the unikernel package and its apps and libraries are reduced dramatically, compared to the use of minimal stand-alone operating system distributions or standard distributions of operating systems.

With little to load, click-to-availability times can be far faster than with standard operating systems and--in cases with all but the most minimal possible packaging--containers. The upside is, therefore, reduced attack surface, fast execution and comparatively tiny storage displacement of the unikernel package.

The downsides are notable, however. It's unlikely that the libraries and applications packaged into the unikernel can be easily updated, if at all. If a library or the application code can be pillaged through external manipulation, the unikernel package is entirely susceptible to vulnerabilities. For example, if a library or application is, or becomes, vulnerable under certain conditions to a buffer overflow (packed text or stream to a port), then the package becomes a serious security risk. So long as the foundational code is immune to vulnerabilities, the security risk is highly reduced. But entropy eventually affects all code, and unikernels typically must be replaced after rebuild, rather than updated or upgraded in place.

Building unikernel prototypes is comparatively simple. Numerous platforms exist that bind applications, libraries and perhaps various packaging sauces into recipes for unikernels. Adaptation of hypervisor-focused unikernels into stand-alone, bare metal or IoT-focused unikernels is more complicated. The complication in running bare metal or IoT unikernels comes from the need to have the correct recipes in terms of driver combinations to match target hardware over the proposed life of the hardware.

There are kits that are similar to unikernels that perform the same single-address-space app execution model, folding in extra components for increased functionality. One such kit, the rump kernel, contains just the bottom hardware abstraction layer/HAL driver components that interface hardware or hypervisor paravirtualization links to a package containing another kernel (perhaps a slimmed-down Linux, BSD, Android or other kernel) along with program/app components that comprise the functionality of the unikernel unit. The rump kernel has been melded with example kernel and app pieces that contain web services, SQLite and proof-of-concept unikernels, not unlike the shared functionality provided by the packaging that Docker, rkt and other app container methods promote.

Other rump kernels have been melded with microservices and node.js applications into what some call nanokernels. NanoVMs uses a packaging method that mimes container functionality, but with no user context so user context exploits are impossible.

Examples of varying unikernels are easy to find, but their construction techniques vary. Some unikernels are designed to be programming language-specific, while others can accommodate programs that aren’t specifically designed for unikernel use, providing resources or resource stubs to applications needing them. Some use Posix-compliant infrastructure as a basis to make apps “feel comfortable.”

Downstream support for the unikernel architecture chosen may or may not be available over a long term, as unikernel projects are sometimes abandoned. With that said, unikernels aren’t designed for long-term maintenance--just long-term functionality replacement on the chosen destination hardware, hypervisor or other platform.

While monolithic in purpose, unikernels are application-running lumps of code that are far tinier than the often bloated substrates used for applications deployment. The removal of the bloat comes at the cost of maintainability that other application substrates offer. As IoT piloting platforms, unikernels replace more expensive firmware platforms, while sometimes missing more evolved firmware platform capabilities in terms of maintenance. A quick and (not necessarily) dirty unikernel takes reasonable programming skills, and the knowledge that unikernel coding platforms are shifting and evolving rapidly.

The simplicity of a unikernel can sometimes be bliss. Long term, evolving unikernel construction platform providers will make unikernels an even wiser choice.

Hide comments


  • Allowed HTML tags: <em> <strong> <blockquote> <br> <p>

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.