Auto-configuration of MirageOS unikernels
2025-09-16TL;DR: less secrets on developers machines, less manual configuration, less data in shell scripts -- instead using a DHCP server.
We are working on streamlining the deployment of MirageOS unikernels, using Albatross and Mollymawk. As a first step, we reuse our recent development of DNSvizor to distribute IP addresses.
First Step: Dynamic Configuration Of IP Addresses
Up until now, we configured MirageOS unikernels manually with IP addresses and other configuration information via command line arguments (--ipv4=192.168.0.23/24 --ipv4-gateway=192.168.0.1
). By deploying a DHCP server on the same network, we remove the need for these command line arguments. You may know about DHCP already, it is likely that you've used DHCP today when your computer connected to the network ;).
The burden to select IP addresses is removed from the unikernel operator, and now the DHCP server operator is responsible for that. For unikernels that do client work, so they do not need to be accessible by other services, this is very fine.
For unikernels that provide services, such as a web server, it is crucial that other computers are able to find these. One way is to use static entries in the DHCP configuration: the unikernel asking for an IP address (using its MAC address) will get a statically configured IP address. We can do this, because Albatross generates the MAC address based on the bridge name and the unikernal name as follows:
let mac name bridge =
(* deterministic mac address computation: VEB Kombinat Robotron prefix
vielen dank, liebe genossen! *)
let prefix = "\x00\x80\x41"
and ours = Digest.string (bridge ^ to_string name)
in
Macaddr.of_octets_exn (prefix ^ String.sub ours 0 3)
A second option is that after the IP address is assigned, the authoritative DNS service is updated (using DNS update) to point to the new address. If the unikernel itself would need to do this, we wouldn't get a lot of advantage, since now the unikernel would need to know about the authoritative DNS service and get credentials (e.g. a shared secret and TSIG) to update that service. Fortunately, since around 20 years the client FQDN extension for DHCP is standardised and supported by various DHCP clients and servers.
MirageOS brief interlude
Since its inception, MirageOS unikernels had the possibility to use DHCP or static addresses. This decision was done at configuration (compile) time, we delayed this to the runtime by leveraging the DHCP client implementation to eventually use static IP addresses.
Updating DNS
Let's consider we have a web service unikernel that should serve "blog.robur.coop". It will provide a DHCP client FQDN extension with the hostname "blog.robur.coop" where the the "S" flag (indicating the DHCP server should do the update) is set.
Now, the DHCP server, after validation that the DHCP client is legible to carry the hostname "blog.robur.coop", reaches out to the authoritative DNS server to set the A(ddress) record to the IP address it will hand out. If successful, it will report so in the DHCP reply to the unikernel.
What did we gain until now? The operator does not need to deal with static assignment of IP addresses anymore. The DHCP server takes care of that IP address assignment, and in addition, the DHCP server will update the authoritative DNS server. An enrollment of a new service - e.g. "next-blog.robur.coop" - just got simpler: the authoritative DNS is automatically updated (by DHCP), and no manual intervention is needed.
TLS Reverse Proxy
There is a scarcity of IPv4 addresses, and also Let's encrypt provides wildcard certificates. This means that not every of our services needs to have a public IPv4 address - but various web services are behind a TLS reverse proxy (we use tlstunnel, there is as well contruno). In this auto-configuration setup we obviously also want to support to update the TLS reverse proxy instead of the authoritative DNS server - which has a wildcard entry, so every subdomain of robur.coop resolves to our TLS reverse proxy.
We implemented this the same way as the DNS update. A unikernel sends a DHCP request with a client FQDN extension on the private service interface. The DHCP server will see this request and update the TLS reverse proxy instead of the authoritative DNS server.
Concrete Work Done
- charrua: support client FQDN
- DNSvizor: support "domain" (fix1, fix2)
- charrua: provide data when a lease is handed out
- DNSvizor: update authoritative DNS
- DNSvizor: update TLStunnel
- charrua: allow runtime selection of DHCP or not
- traceroute: defer DHCP usage or not to runtime
Conclusion
In this article we described the first steps towards auto-configuration of our unikernels. We dived into depth what auto-configuration entails for us. To conclude, when deploying a service, instead of (a1) manual configuration of DNS, or (a2) manual configuration of our TLS reverse proxy - both entailing the distribution of shared secrets onto our developer computers; and (b) manual selection of IP addresses and (c) providing these via command line (done in shell scripts to deploy these unikernels), we now use DHCP and our DNSvizor unikernel to leverage the deployment.
We're keen to hear if you're interested in such a setup, or have questions about it. Please reach out via eMail (team@robur.coop) or opening an issue at the respective repository.
Our work is only partially funded, we cross-fund our work by commercial contracts and public (EU) funding. We are part of a non-profit company, you can make a (in the EU tax-deductible) donation (select "DONATION robur" in the dropdown menu), or sponsor us via the GitHub sponsor button.