I’ve been doing a lot of hacking on Eon to get it to a state where I can use it to provision certificates in production.
I’ve written a capnproto schema file that exposes a capability to a domain.
When we run a nameserver it outputs a capability for each domain for which it is authoritative.
$ cap -z cl.freumh.org --capnp-secret-key-file /var/lib/eon/capnp-secret.pem --capnp-listen-address tcp:cl.freumh.org:7000 --state-dir /var/lib/eon
$ sudo ls /var/lib/eon/caps/
cl.freumh.org.cap zone.cap
This capability can then be provided to a client.
$ capc get-name cl.freumh.org.cap
cl.freumh.org
The client can create a new capability for a subdomain, which could be passed to a service. NB this is persisted to disk so it can be referenced across reboots.
$ capc delegate cl.freumh.org.cap test
Wrote capability to test.cl.freumh.org.cap
$ capc get-name test.cl.freumh.org.cap
test.cl.freumh.org
We expose a DNS UPDATE semantic-compatible interface over capnptoto (which not shown here can support arbitrarily complex pre-requisites).
$ capc update test.cl.freumh.org.cap -u add:test.cl.freumh.org:A:128.232.113.136:3600
$ dig test.cl.freumh.org +short
128.232.113.136
A nice effect of the capability interface is that I can create a service to manage dynamic runtime records that can’t be statically configured, such as mailserver DKIM records, using a capability file and unix group permissions. Another runtime thing that would be nice to manage for Eilean is the DNS SOA serial No., though perhaps not important if we don’t have secondaries.
And finally, we also support provisioning certificates with the ACME DNS-01 challenge client embedded in the nameserver, modifying the trie in-memory. A schema compatible capnproto server could also do this via DNS UPDATES to another DNS provider.
$ capc cert test.cl.freumh.org.cap ryan@test.cl.freumh.org -d test.cl.freumh.org
Updated certificate for test.cl.freumh.org
Renewals are supported via forking a fiber, sleeping to the expiration date minus 30 days, and providing the new certificate to the client via a callback. I’ve written a NixOS module that will create a systemd service running the client for each certificate declared which will allow me to use this for my personal machines as well as Eilean.
This is great because we don’t need to manage DNS UPDATE credentials, exposing our whole domain just to get a cert. Instead we an simply share a callback that provides a service with a cert only. It moves all the complexity to a server that can manage the timings of provisioning and renewals. E.g. see the complexity different between NixOS ACME support and Eon ACME support. It allows provisioning certificates for services that aren’t publicly accessible and supports renewals (which Tailscale doesn’t).
Going back to:
We’re only talking about restarting the webserver as it looks like every HTTP service (matrix, mastodon, etc) is expected to run behind a reverse proxy which handles things like TLS. (With SVCB records specifying a port, maybe this is unnecessary.)
We’re basically trying to solve how to demultiplex multiple HTTP services on one machine. (NB though some services like transmission don’t even support TLS).
The options I see here:
- Currents solution: provide a HTTPS reverse proxy the TLS certs, terminate TLS there, and demultiplex based on the HTTP host field.
- Proxy TLS streams based on the SNI field. This might be complicated by encrypted SNI / encrypted client hello, but we provide that key in the DNS so should be able to decrypt it. E.g. https://github.com/spacemonkeygo/tlshowdy
- Run services on separate ports advertised in SVCB records.
- An issue: it looks like SVCB support is not there yet, e.g. chrome doesn’t respect the SVCB port field www.netmeister.org/blog/https-rrs.html#1 src.
- Support IPv6-only services with each service bound to a different
IP, since we have 2^64 addresses to play with in the host identifier
portion of an address. Is anyone else doing this? I know Cloudflare are
basically doing the opposite
which they say is not IPv4 specific.
- This would be great for Eon, as the client could provision the DNS record that the service would bind to. A ‘DHCP for IPv6 services’. As with TLS certificate paths there is a configuration challenge here – every service is configured differently. Maybe there’s some network namespacing that could automagically bind a service to a particular IP…
Note this problem doesn’t apply to machines running a single service, or non-HTTP services like SMTP/IMAP, MQTT.
Some issues
- cmdliner
--help
seems to be broken - OSCP stapling for cert revocation. I don’t understand this yet.