Build Xen-enabled Mirage unikernels on this VM.
Related work for HaLVM, the unikernel system in Haskell.
In all other cases it should return 401 UNAUTHORIZED.
Store data in some key/value store: MirageOS provides kv_ro which is an abstract key/value store which can be backed in code or in a FAT-formatted disk image.
The server code resides in a unikernel.ml file. I am not at all familiar with OCaml so my code is probably not really idiomatic. Learning OCaml is one of the reasons I became interested in MirageOS in the first place !
We first define the function that will check the database for matching username/password pair, given an access request. The main thing to note here is that reading from the store happens within the context of so-called light-weight threads or Lwt which is the basic Mirage cooperative thread execution model. Potentially blocking operations expect a continuation that will be passed the result of the operation.
Then comes the meat of the server, the http_callback function that will be called by cohttp upon everyrequest. We extract interesting parts of the request’s URI then check whether or not the username/password pair matches, and finally return a response.
The single most important thing to note here is that this code is pretty much system-agnostic: There is no dependency on a particular low-level runtime infrastructure and the same code can be run either as a native binary on Unix or packaged as unikernel on Xen (or QEMU). Implementation details are hidden behind the signatures, e.g. interfaces, that parameterize the Main module and whose instances are passed to the start function.
The second important file one has to write for a MirageOS application is the config.ml file which is used by mirage tool to generate the needed boilerplate to run the actual code.
let auth_data = generic_kv_ro "data"
Our second component is the HTTP server. We can see here our configuration depends on the underlying platform. Note that the generic_stackv4 provides IPv4 implementation for both Unix and Xen and strictly speaking we could get rid of the if_impl construct. It is also possible to use a direct_stackv4_with_default_ipv4 implementation on Xen: This will assign a static IP to the kernel which can be changed when configuring mirage.
The handler function defines the general structure of our service and adds some libraries and packages to be imported by mirage. Then we can finally register our auth_service passing it the result of invoking handler with actual implementation of the modules it requires.
We can first try to build and run our service on Unix, e.g. locally. We first need to populate a data/ directory with some files representing usernames and add a password inside each file. Then we can configure Mirage, build the executable using the generated Makefile and run it.
Configure bridge, dnsmasq and IP forwarding in the VM to be used by Xen domU kernels.
The part I had most difficulties with is of course the networking part. We want the domU virtual machines running on top of Xen hypervisor inside the VirtualBox VM to be visible from our host. The following picture tries to render my understanding of the network topology and configuration we want to achieve (actual IPs/MACs may of course differ).
The VirtualBox engine exposes the host-only interface to the host as vboxnet4 with a pre-configured IP (e.g. IP is set in Vagrantfile as part of definition of host-only network). This interface is linked to the eth1 “physical interface” inside the VM and part of the same ethernet network. We create a br0 interface which is a bridge: An interface that connects two or more interfaces by routing the packets to/from each of the bridged interfaces. In this case it connects the host-only interface (eth1) and the the virtual interfaces created for each domU kernel, in this case vif1.1. The latter is pair of a pair of virtual interfaces created by underlying hypervisor when the domU kernel boots up, the other member of the pair being the tap0 interface on the domU side. Each packet going through either interface is made available to the other interface.
Another important is configuring and activating dnsmasq to ensure domU kernels will be able to get an IP address using DHCP. Here we configure it to be attached to br0 interface - our bridge - and to serve IPs from 192.168.77.3 to 192.168.77.200.
# Generated by auth_service (Wed, 20 Apr 2016 13:53:27 GMT).
The -v option asks xl to be verbose and the -c attaches a console to the launched kernel.
There is a slight error in the make-fat_block1-image.sh: The block size used is made equal to the total size of the files to include in the image, which does not seem to make much sense. I set it to be 32Kb.
ARP responding to: who-has 192.168.77.157?
We have been running successfuly our first home-made unikernel on an hypervisor!
A top-down and language-centric approach exemplified by Mirage and HaLVM where system-level components are actually written in the language thus providing all the benefits of tight integration and focus, and packed with a custom minimalistic OS.
Obviously, the former approach definitely requires less effort and allows one to produce unikernels in a very short time span by simply repackaging existing applications and services. The latter approach is more demanding as it requires expertise in a specific language and building all the needed components, sometimes from scratch, to provide low-level services otherwise provided by standard system libraries. I don’t have much rational arguments, beyond those provided in Mirage’s own documentation, to back my personal preference for the latter: I might be biased by my interest in functional programming languages, especially strongly typed ones, and the somewhat childish desire to master every single part of the system stack.
Experiment with deployment of unikernels on cloud providers. There is some documentation and script already available to deploy to AWS which I would like to try.