<p>At the end of 2017, I resigned from my PostDoc position at University of
Cambridge (in the <a href="https://www.cl.cam.ac.uk/~pes20/rems/">rems</a> project). Early
December 2017 I organised the <a href="https://mirage.io/blog/2017-winter-hackathon-roundup">4th MirageOS hack
retreat</a>, with which I'm
very satisfied. In March 2018 the <a href="http://retreat.mirage.io">5th retreat</a> will
happen (please sign up!).</p>
<p>In 2018 I moved to Berlin and started to work for the (non-profit) <a href="https://techcultivation.org">Center for
the cultivation of technology</a> with our
<a href="http://robur.io">robur.io</a> project "At robur, we build performant bespoke
minimal operating systems for high-assurance services". robur is only possible
by generous donations in autumn 2017, enthusiastic collaborateurs, supportive
friends, and a motivated community, thanks to all. We will receive funding from
the <a href="https://prototypefund.de/project/robur-io/">prototypefund</a> to work on a
<a href="https://robur.io/Our%20Work/Projects#CalDAV-Server">CalDAV server</a> implementation in OCaml
targeting MirageOS. We're still looking for donations and further funding,
please get in touch. Apart from CalDAV, I want to start the year by finishing
several projects which I discovered on my hard drive. This includes DNS, <a href="/Posts/Conex">opam
signing</a>, TCP, ... . My personal goal for 2018 is to develop a
flexible <code>mirage deploy</code>, because after configuring and building a unikernel, I
want to get it smoothly up and running (spoiler: I already use
<a href="/Posts/VMM">albatross</a> in production).</p>
<p>To kick off (3% of 2018 is already used) this year, I'll talk in more detail
about <a href="https://github.com/roburio/udns">µDNS</a>, an opinionated from-scratch
re-engineered DNS library, which I've been using since Christmas 2017 in production for
<a href="https://github.com/hannesm/ns.nqsb.io">ns.nqsb.io</a> and
<a href="https://git.robur.io/?p=ns.robur.io.git;a=summary">ns.robur.io</a>. The
development started in March 2017, and continued over several evenings and long
weekends. My initial motivation was to implement a recursive resolver to run on
my laptop. I had a working prototype in use on my laptop over 4 months in the
summer 2017, but that code was not in a good shape, so I went down the rabbit
hole and (re)wrote a server (and learned more about GADT). A configurable
resolver needs a server, as local overlay, usually anyways. Furthermore,
dynamic updates are standardised and thus a configuration interface exists
inside the protocol, even with hmac-signatures for authentication!
Coincidentally, I started to solve another issue, namely automated management of let's
encrypt certificates (see <a href="https://github.com/hannesm/ocaml-letsencrypt/tree/nsupdate">this
branch</a> for an
initial hack). On my journey, I also reported a cache poisoning vulnerability,
which was fixed in <a href="https://docs.docker.com/docker-for-windows/release-notes/#docker-community-edition-17090-ce-win32-2017-10-02-stable">Docker for
Windows</a>.</p>
<p>But let's get started with some content. Please keep in mind that while the
code is publicly available, it is not yet released (mainly since the test
coverage is not high enough, and the lack of documentation). I appreciate early
adopters, please let me know if you find any issues or find a use case which is
not straightforward to solve. This won't be the last article about DNS this
year - persistent storage, resolver, let's encrypt support are still missing.</p>
| Soa, Soa -> Eq | Soa, _ -> Lt | _, Soa -> Gt
| A, A -> Eq
end
type 'a key = 'a Key.t
(* our OCaml Map with an encapsulated constructor as key *)
type k = K : 'a key -> k
module M = Map.Make(struct
type t = k
(* the price I pay for not using int as three-state value *)
let compare (K a) (K b) = match Key.compare a b with
| Order.Lt -> -1
| Order.Eq -> 0
| Order.Gt -> 1
end)
(* v contains a key and value pair, wrapped by a single constructor *)
type v = V : 'a key * 'a -> v
(* t is the main type of a Dns_map, used by clients *)
type t = v M.t
(* retrieve a typed value out of the store *)
let get : type a. a Key.t -> t -> a = fun k t ->
match M.find (K k) t with
| V (k', v) ->
(* this comparison is superfluous, just for the types *)
match Key.compare k k' with
| Order.Eq -> v
| _ -> assert false
</code></pre>
<p>This helps me to programmaticaly retrieve tightly typed values from the cache,
important when code depends on concrete values (i.e. when there are domain
names, look these up as well and add as additional records). Look into <a href="https://github.com/roburio/udns/blob/master/server/dns_server.ml">server/dns_server.ml</a></p>
<p>To deploy servers without much persistent data, an authentication schema is
hardcoded in the dns-server: shared secrets are also stored as DNS entries
(DNSKEY), and <code>_transfer.zone</code>, <code>_update.zone</code>, and <code>_key-management.zone</code> names
are introduced to encode the permissions. A <code>_transfer</code> key also needs to
encode the IP address of the primary (to know where to request zone transfers)
and secondary IP (to know where to send notifications).</p>
<p>Please have a look at
<a href="https://git.robur.io/?p=ns.robur.io.git;a=summary">ns.robur.io</a> and the <a href="https://github.com/roburio/udns/blob/master/mirage/examples">examples</a> for more details. The shared secrets are provided as boot parameter of the unikernel.</p>