DnsServer: update with more current unikernels and mirage

This commit is contained in:
Hannes Mehnert 2023-03-02 18:20:44 +01:00
parent 212884184b
commit 6cd06cdfd7

View file

@ -45,7 +45,7 @@ Let's create a fresh `switch` for the DNS journey:
```shell ```shell
$ opam init $ opam init
$ opam update $ opam update
$ opam switch create udns 4.09.0 $ opam switch create udns 4.14.1
# waiting a bit, a fresh OCaml compiler is getting bootstrapped # waiting a bit, a fresh OCaml compiler is getting bootstrapped
$ eval `opam env` #sets some environment variables $ eval `opam env` #sets some environment variables
``` ```
@ -71,29 +71,26 @@ FWIW, `ozone` accepts `--old <filename>` to check whether an update from the old
The next step is to compile the primary server and run it to serve the domain data. Since the git-via-ssh client is not yet released, we need to add a custom opam repository to this switch. The next step is to compile the primary server and run it to serve the domain data. Since the git-via-ssh client is not yet released, we need to add a custom opam repository to this switch.
```shell ```shell
# git via ssh is not yet released, but this opam repository contains the branch information
$ opam repo add git-ssh git+https://github.com/roburio/git-ssh-dns-mirage3-repo.git
# get the `mirage` application via opam # get the `mirage` application via opam
$ opam install lwt mirage $ opam install lwt mirage
# get the source code of the unikernels # get the source code of the unikernels
$ git clone -b future https://github.com/roburio/unikernels.git $ git clone https://github.com/roburio/dns-primary-git.git
$ cd unikernels/primary-git $ cd dns-primary-git
# let's build the server first as unix application # let's build the server first as unix application
$ mirage configure --prng fortuna #--no-depext if you have all system dependencies $ mirage configure #--no-depext if you have all system dependencies
$ make depend
$ make $ make
# run it # run it
$ ./primary_git $ dist/primary-git
# starts a unix process which clones https://github.com/roburio/udns.git # starts a unix process which clones https://github.com/roburio/udns.git
# attempts to parse the data as zone files, and fails on parse error # attempts to parse the data as zone files, and fails on parse error
$ ./primary-git --remote=https://my-public-git-repository $ dist/primary-git --remote=https://my-public-git-repository
# this should fail with ENOACCESS since the DNS server tries to listen on port 53 # this should fail with ENOACCESS since the DNS server tries to listen on port 53
# which requires a privileged user, i.e. su, sudo or doas # which requires a privileged user, i.e. su, sudo or doas
$ sudo ./primary-git --remote=https://my-public-git-repository $ sudo dist/primary-git --remote=https://my-public-git-repository
# leave it running, run the following programs in a different shell # leave it running, run the following programs in a different shell
# test it # test it
@ -112,24 +109,24 @@ Let's authenticate the access by using ssh, so we feel ready to push data there
```shell ```shell
# collect the RSA host key fingerprint # collect the RSA host key fingerprint
$ ssh-keyscan <git-server> > /tmp/git-server-public-keys $ ssh-keyscan <git-server> > /tmp/git-server-public-keys
$ ssh-keygen -l -E sha256 -f /tmp/git-server-public-keys | grep RSA $ ssh-keygen -l -E sha256 -f /tmp/git-server-public-keys | grep ED25519
2048 SHA256:a5kkkuo7MwTBkW+HDt4km0gGPUAX0y1bFcPMXKxBaD0 <git-server> (RSA) 256 SHA256:a5kkkuo7MwTBkW+HDt4km0gGPUAX0y1bFcPMXKxBaD0 <git-server> (ED25519)
# we're interested in the SHA256:yyy only # we're interested in the SHA256:yyy only
# generate a ssh keypair # generate a ssh keypair
$ awa_gen_key # installed by the make depend step above in ~/.opam/udns/bin $ awa_gen_key --keytype ed25510 # installed by the make step above in ~/.opam/udns/bin
seed is pIKflD07VT2W9XpDvqntcmEW3OKlwZL62ak1EZ0m private key: ed25519:nO7ervdJqzPfuvdM/J4qImipwVoI5gl53fpqgjZnv9w=
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC5b2cSSkZ5/MAu7pM6iJLOaX9tJsfA8DB1RI34Zygw6FA0y8iisbqGCv6Z94ZxreGATwSVvrpqGo5p0rsKs+6gQnMCU1+sOC4PRlxy6XKgj0YXvAZcQuxwmVQlBHshuq0CraMK9FASupGrSO8/dW30Kqy1wmd/IrqW9J1Cnw+qf0C/VEhIbo7btlpzlYpJLuZboTvEk1h67lx1ZRw9bSPuLjj665yO8d0caVIkPp6vDX20EsgITdg+cFjWzVtOciy4ETLFiKkDnuzHzoQ4EL8bUtjN02UpvX2qankONywXhzYYqu65+edSpogx2TuWFDJFPHgcyO/ZIMoluXGNgQlP awa@awa.local ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICyliWbwWWXBc1+DRIzReLQ4UFiVGXJ6Paw1Jts+XQte awa@awa.local
# please run your own awa_gen_key, don't use the numbers above # please run your own awa_gen_key, don't use the numbers above
``` ```
The public key needs is in standard OpenSSH format and needs to be added to the list of accepted keys on your server - the exact steps depend on your git server, if you're running your own with [gitosis](https://github.com/tv42/gitosis), add it as new public key file and grant that key access to the data repository. If you use gitlab or github, you may want to create a new user account and with the generated key. The public key needs is in standard OpenSSH format and needs to be added to the list of accepted keys on your server - the exact steps depend on your git server, if you're running your own with [gitosis](https://github.com/tv42/gitosis), add it as new public key file and grant that key access to the data repository. If you use gitlab or github, you may want to create a new user account and with the generated key.
The private key is not displayed, but only the seed required to re-generate it, when using the same random number generator, in our case [fortuna implemented by nocrypto](http://mirleft.github.io/ocaml-nocrypto/doc/Nocrypto.Rng.html) - used by both `awa_gen_key` and `primary_git`. The seed is provided as command-line argument while starting `primary_git`: The private key is not displayed, but only the seed required to re-generate it, when using the same random number generator, in our case [fortuna implemented by mirage-crypto](https://mirage.github.io/mirage-crypto/doc/mirage-crypto-rng/Mirage_crypto_rng/index.html) - used by both `awa_gen_key` and `primary-git`. The seed is provided as command-line argument while starting `primary-git`:
```shell ```shell
# execute with git over ssh, authenticator from ssh-keyscan, seed from awa_gen_key # execute with git over ssh, authenticator from ssh-keyscan, seed from awa_gen_key
$ ./primary_git --authenticator=SHA256:a5kkkuo7MwTBkW+HDt4km0gGPUAX0y1bFcPMXKxBaD0 --seed=pIKflD07VT2W9XpDvqntcmEW3OKlwZL62ak1EZ0m --remote=ssh://git@<git-server>/repo-name.git $ ./primary-git --authenticator=SHA256:a5kkkuo7MwTBkW+HDt4km0gGPUAX0y1bFcPMXKxBaD0 --ssh-key=ed25519:nO7ervdJqzPfuvdM/J4qImipwVoI5gl53fpqgjZnv9w= --remote=git@<git-server>:repo-name.git
# started up, you can try the host and dig commands from above if you like # started up, you can try the host and dig commands from above if you like
``` ```
@ -139,9 +136,9 @@ To wrap up, we now have a primary authoritative name server for our zone running
Our remote git repository is the source of truth, if you need to add a DNS entry to the zone, you git pull, edit the zone file, remember to increase the serial in the SOA line, run `ozone`, git commit and push to the repository. Our remote git repository is the source of truth, if you need to add a DNS entry to the zone, you git pull, edit the zone file, remember to increase the serial in the SOA line, run `ozone`, git commit and push to the repository.
So, the `primary_git` needs to be informed of git pushes. This requires a communication channel from the git server (or somewhere else, e.g. your laptop) to the DNS server. I prefer in-protocol solutions over adding yet another protocol stack, no way my DNS server will talk HTTP REST. So, the `primary-git` needs to be informed of git pushes. This requires a communication channel from the git server (or somewhere else, e.g. your laptop) to the DNS server. I prefer in-protocol solutions over adding yet another protocol stack, no way my DNS server will talk HTTP REST.
The DNS protocol has an extension for [notifications of zone changes](https://tools.ietf.org/html/rfc1996) (as a DNS packet), usually used between the primary and secondary servers. The `primary_git` accepts these notify requests (i.e. bends the standard slightly), and upon receival pulls the remote git repository, and serves the fresh zone files. Since a git pull may be rather excessive in terms of CPU cycles and network bandwidth, only authenticated notifications are accepted. The DNS protocol has an extension for [notifications of zone changes](https://tools.ietf.org/html/rfc1996) (as a DNS packet), usually used between the primary and secondary servers. The `primary-git` accepts these notify requests (i.e. bends the standard slightly), and upon receival pulls the remote git repository, and serves the fresh zone files. Since a git pull may be rather excessive in terms of CPU cycles and network bandwidth, only authenticated notifications are accepted.
The DNS protocol specifies in another extension [authentication (DNS TSIG)](https://tools.ietf.org/html/rfc2845) with transaction signatures on DNS packets including a timestamp and fudge to avoid replay attacks. As key material hmac secrets distribued to both the communication endpoints are used. The DNS protocol specifies in another extension [authentication (DNS TSIG)](https://tools.ietf.org/html/rfc2845) with transaction signatures on DNS packets including a timestamp and fudge to avoid replay attacks. As key material hmac secrets distribued to both the communication endpoints are used.
@ -163,7 +160,7 @@ git-repo> echo "personal._update.mirage. DNSKEY 0 3 163 kJJqipaQHQWqZL31Raar6uPn
git-repo> git add mirage._keys && git commit -m "add hmac secret" && git push git-repo> git add mirage._keys && git commit -m "add hmac secret" && git push
# now we need to restart the primary git to get the git repository with the key # now we need to restart the primary git to get the git repository with the key
$ ./primary_git --seed=... # arguments from above, remote git, host key fingerprint, private key seed $ ./primary-git --ssh-key=... # arguments from above, remote git, host key fingerprint, private key seed
# now test that a notify results in a git pull # now test that a notify results in a git pull
$ onotify 127.0.0.1 mirage --key=personal._update.mirage:SHA256:kJJqipaQHQWqZL31Raar6uPnepGFIdtpjkXot9rv2xg= $ onotify 127.0.0.1 mirage --key=personal._update.mirage:SHA256:kJJqipaQHQWqZL31Raar6uPnepGFIdtpjkXot9rv2xg=
@ -177,16 +174,22 @@ Ok, this onotify command line could be setup as a git post-commit hook, or run m
It's time to figure out how to integrate the secondary name server. An already existing bind or something else that accepts notifications and issues zone transfers with hmac-sha256 secrets should work out of the box. If you encounter interoperability issues, please get in touch with me. It's time to figure out how to integrate the secondary name server. An already existing bind or something else that accepts notifications and issues zone transfers with hmac-sha256 secrets should work out of the box. If you encounter interoperability issues, please get in touch with me.
The `secondary` subdirectory of the cloned `unikernels` repository is another unikernel that acts as secondary server. It's only command line argument is a list of hmac secrets used for authenticating that the received data originates from the primary server. Data is initially transferred by a [full zone transfer (AXFR)](https://tools.ietf.org/html/rfc5936), later updates (upon refresh timer or notify request sent by the primary) use [incremental (IXFR)](https://tools.ietf.org/html/rfc1995). Zone transfer requests and data are authenticated with transaction signatures again. The `secondary` unikernel is available from another git repository:
```shell
# get the secondary sources
$ git clone https://github.com/roburio/dns-secondary.git
$ cd dns-secondary
```
It's only command line argument is a list of hmac secrets used for authenticating that the received data originates from the primary server. Data is initially transferred by a [full zone transfer (AXFR)](https://tools.ietf.org/html/rfc5936), later updates (upon refresh timer or notify request sent by the primary) use [incremental (IXFR)](https://tools.ietf.org/html/rfc1995). Zone transfer requests and data are authenticated with transaction signatures again.
Convenience by OCaml DNS is that transfer key names matter, and are of the form <primary-ip>.<secondary-ip>._transfer.domain, i.e. `1.1.1.1.2.2.2.2._transfer.mirage` if the primary server is 1.1.1.1, and the secondary 2.2.2.2. Encoding the IP address in the name allows both parties to start the communication: the secondary starts by requesting a SOA for all domains for which keys are provided on command line, and if an authoritative SOA answer is received, the AXFR is triggered. The primary server emits notification requests on startup and then on every zone change (i.e. via git pull) to all secondary IP addresses of transfer keys present for the specific zone in addition to the notifications to the NS records in the zone. Convenience by OCaml DNS is that transfer key names matter, and are of the form <primary-ip>.<secondary-ip>._transfer.domain, i.e. `1.1.1.1.2.2.2.2._transfer.mirage` if the primary server is 1.1.1.1, and the secondary 2.2.2.2. Encoding the IP address in the name allows both parties to start the communication: the secondary starts by requesting a SOA for all domains for which keys are provided on command line, and if an authoritative SOA answer is received, the AXFR is triggered. The primary server emits notification requests on startup and then on every zone change (i.e. via git pull) to all secondary IP addresses of transfer keys present for the specific zone in addition to the notifications to the NS records in the zone.
```shell ```shell
$ cd ../secondary $ mirage configure
$ mirage configure --prng fortuna
# make depend should not be needed since all packages are already installed by the primary-git
$ make $ make
$ ./secondary $ ./dist/secondary
``` ```
### IP addresses and routing ### IP addresses and routing
@ -238,27 +241,26 @@ ns2 A 10.0.42.3
git-repo> cat mirage._keys git-repo> cat mirage._keys
personal._update.mirage. DNSKEY 0 3 163 kJJqipaQHQWqZL31Raar6uPnepGFIdtpjkXot9rv2xg= personal._update.mirage. DNSKEY 0 3 163 kJJqipaQHQWqZL31Raar6uPnepGFIdtpjkXot9rv2xg=
10.0.42.2.10.0.42.3._transfer.mirage. DNSKEY 0 3 163 cDK6sKyvlt8UBerZlmxuD84ih2KookJGDagJlLVNo20= 10.0.42.2.10.0.42.3._transfer.mirage. DNSKEY 0 3 163 cDK6sKyvlt8UBerZlmxuD84ih2KookJGDagJlLVNo20=
git-repo> git commit -m "udpates" . && git push git-repo> git commit -m "updates" . && git push
``` ```
Ok, the git repository is ready, now we need to compile the unikernels for the virtualisation target (see [other targets](https://mirage.io/wiki/hello-world#Building-for-Another-Backend) for further information). Ok, the git repository is ready, now we need to compile the unikernels for the virtualisation target (see [other targets](https://mirage.io/wiki/hello-world#Building-for-Another-Backend) for further information).
```shell ```shell
# back to primary # back to primary
$ cd ../primary-git $ cd ../dns-primary-git
$ mirage configure -t hvt --prng fortuna # or e.g. -t spt (and solo5-spt below) $ mirage configure -t hvt # or e.g. -t spt (and solo5-spt below)
# installs backend-specific opam packages, recompiles some # installs backend-specific opam packages, recompiles some
$ make depend
$ make $ make
[...] [...]
$ solo5-hvt --net:service=tap0 -- primary_git.hvt --ipv4=10.0.42.2/24 --ipv4-gateway=10.0.42.1 --seed=.. --authenticator=.. --remote=ssh+git://... $ solo5-hvt --net:service=tap0 -- primary_git.hvt --ipv4=10.0.42.2/24 --ipv4-gateway=10.0.42.1 --seed=.. --authenticator=.. --remote=...
# should now run as a virtual machine (kvm, bhyve), and clone the git repository # should now run as a virtual machine (kvm, bhyve), and clone the git repository
$ dig any mirage @10.0.42.2 $ dig any mirage @10.0.42.2
# should reply with the SOA and NS records, and also the name server address records in the additional section # should reply with the SOA and NS records, and also the name server address records in the additional section
# secondary # secondary
$ cd ../secondary $ cd ../dns-secondary
$ mirage configure -t hvt --prng fortuna $ mirage configure -t hvt
$ make $ make
$ solo5-hvt --net:service=tap1 -- secondary.hvt --ipv4=10.0.42.3/24 --keys=10.0.42.2.10.0.42.3._transfer.mirage:SHA256:cDK6sKyvlt8UBerZlmxuD84ih2KookJGDagJlLVNo20= $ solo5-hvt --net:service=tap1 -- secondary.hvt --ipv4=10.0.42.3/24 --keys=10.0.42.2.10.0.42.3._transfer.mirage:SHA256:cDK6sKyvlt8UBerZlmxuD84ih2KookJGDagJlLVNo20=
# an ipv4-gateway is not needed in this setup, but in real deployment later # an ipv4-gateway is not needed in this setup, but in real deployment later
@ -327,9 +329,10 @@ The let's encrypt unikernel does not serve anything, it is a reactive system whi
```shell ```shell
# getting let's encrypt up and running # getting let's encrypt up and running
$ cd ../lets-encrypt $ cd ..
$ mirage configure -t hvt --prng fortuna $ git clone https://github.com/roburio/dns-letsencrypt-secondary.git
$ make depend $ cd dns-letsencrypt-secondary
$ mirage configure -t hvt
$ make $ make
# run it # run it
@ -347,8 +350,8 @@ As fine print, while this tutorial was about the `mirage` zone, you can stick an
This tutorial presented how to use the OCaml DNS based unikernels to run authoritative name servers for your domain, using a git repository as the source of truth, dynamic authenticated updates, and let's encrypt certificate issuing. This tutorial presented how to use the OCaml DNS based unikernels to run authoritative name servers for your domain, using a git repository as the source of truth, dynamic authenticated updates, and let's encrypt certificate issuing.
There are further steps to take, such as monitoring -- have a look at the `monitoring` branch of the opam repository above, and the `future-robur` branch of the unikernels repository above, which use a second network interface for reporting syslog and metrics to telegraf / influx / grafana. Some DNS features are still missing, most prominently DNSSec. There are further steps to take, such as monitoring (`mirage configure --monitoring`), which use a second network interface for reporting syslog and metrics to telegraf / influx / grafana. Some DNS features are still missing, most prominently DNSSec.
I'd like to thank all people involved in this software stack, without other key components, including [git](https://github.com/mirage/ocaml-git), [irmin 2.0](https://irmin.io/), [nocrypto](https://github.com/mirleft/ocaml-nocrypto), [awa-ssh](https://github.com/haesbaert/awa-ssh), [cohttp](https://github.com/mirage/ocaml-cohttp), [solo5](https://github.com/solo5/sol5), [mirage](https://github.com/mirage/mirage), [ocaml-letsencrypt](https://github.com/mmaker/ocaml-letsencrypt), and more. I'd like to thank all people involved in this software stack, without other key components, including [git](https://github.com/mirage/ocaml-git), [mirage-crypto](https://github.com/mirage/mirage-crypto), [awa-ssh](https://github.com/mirage/awa-ssh), [solo5](https://github.com/solo5/sol5), [mirage](https://github.com/mirage/mirage), [ocaml-letsencrypt](https://github.com/mmaker/ocaml-letsencrypt), and more.
If you want to support our work on MirageOS unikernels, please [donate to robur](https://robur.coop/Donate). I'm interested in feedback, either via [twitter](https://twitter.com/h4nnes), [hannesm@mastodon.social](https://mastodon.social/@hannesm) or via eMail. If you want to support our work on MirageOS unikernels, please [donate to robur](https://robur.coop/Donate). I'm interested in feedback, either via [twitter](https://twitter.com/h4nnes), [hannesm@mastodon.social](https://mastodon.social/@hannesm) or via eMail.