--- /dev/null
+[[!meta title="Certificates for services on our LAN"]]
+
+# Let's Encrypt certs for services on our LAN
+
+We use [Let's Encrypt] to acquire and renew certificates for basically
+all services. However, some services are only exposed on the LAN, and
+so certificate acquisition becomes a bit trickier.
+
+[ACME], the protocol for interacting with [Let's Encrypt],
+supports [DNS-01] authorization, so we can use that to acquire certs
+without exposing services to the Internet.
+
+[Let's Encrypt]: https://letsencrypt.org/
+[DNS-01]: https://tools.ietf.org/html/draft-ietf-acme-acme-07#section-8.5
+[ACME]: https://tools.ietf.org/html/draft-ietf-acme-acme-07
+
+
+## Overview
+
+Let's say we need certificates for `metrics.mgmt.realraum.at`
+
+`metrics.mgmt` will send DNS updates to `gw`. It only needs TXT records for
+ `_acme-challenge.metrics.mgmt.realraum.at` and they will be authenticated using
+ HMAC-SHA256.
+
+
+## Bind9
+
+### Generating a TSIG key
+
+On the system running the services:
+
+- Install `bind9utils` to have the not-so-aptly named `dnssec-keygen` tool.
+- As `root`, generate an HMAC-SHA256 key and make it readable by `acme`:
+
+ # dnssec-keygen -K /etc/acme -a HMAC-SHA256 -b 256 \
+ -n USER metrics.mgmt.realraum.at.
+ Kmetrics.mgmt.realraum.at.+163+06888
+
+ # chown root:acme /etc/acme/K*
+ # chmod 0440 /etc/acme/K*
+
+- Lookup the key, as we will need to put it in the NS' configuration
+
+ # cat /etc/acme/Kmetrics.mgmt.realraum.at.+163+06888.private
+ Private-key-format: v1.3
+ Algorithm: 163 (HMAC_SHA256)
+ Key: FG4v6Eya7utyJ1GxXm019kYBawN+jvfEWCC/7lIgraQ=
+ Bits: AAA=
+ Created: 20171022235329
+ Publish: 20171022235329
+ Activate: 20171022235329
+
+
+_Note:_ I selected HMAC-SHA256 because `gw.realraum.at` is running an
+ obsolete version of Bind9 that only supports HMAC or RSA.
+ In principle, the setup should be similar for asymetric signatures.
+
+
+### Adding the keys
+
+On `gw.realraum.at`:
+
+- `/etc/bind/keys.conf` should exist and be accessible to `root` and `bind`:
+
+ # touch /etc/bind/keys.conf
+ # chown root:bind /etc/bind/keys.conf
+ # chmod 0640 /etc/bind/keys.conf
+
+- Check that `keys.conf` is included from `named.conf.local`:
+
+ # head /etc/bind/named.conf.local
+ include "/etc/bind/zones.rfc1918";
+ include "/etc/bind/keys.conf";
+ [...]
+
+- Add the key descriptor to `keys.conf`:
+
+ # cat >> /etc/bind/keys.conf
+ key metrics.mgmt.realraum.at. {
+ algorithm HMAC-SHA256;
+ secret "4QZWZsLagxXaoBCAxDqbSZmoSjN5qJvZviadrPXkmvU=";
+ }
+
+
+### Setting up DNS updates
+
+- Edit the zone description in `named.conf.local` to allow updates:
+
+ zone "realraum.at" {
+ type master;
+ file "/etc/bind/db.realraum.at";
+ [...]
+
+ update-policy {
+ grant metrics.mgmt.realraum.at. name _acme-challenge.metrics.mgmt.realraum.at. TXT;
+ };
+ };
+
+- The update journal for the zone should be writeable by `bind`:
+
+ # touch /etc/bind/db.realraum.at.jnl
+ # chown root:bind /etc/bind/db.realraum.at.jnl
+ # chmod 0660 /etc/bind/db.realraum.at.jnl
+
+- Restart `bind`
+
+
+## [acmetool]
+
+### Installation
+
+- `acmetool` is available from the official repos starting with Stretch.
+- For earlier releases, Christian [has a package](https://build.spreadspace.org/)
+
+Start with a working, [rootless acmetool setup].
+
+_Note:_ On Debian, _hooks_ are located in `/etc/acme/hooks`, instead of
+ `/usr/lib/acme/hooks` or `/usr/libexec/acme/hooks`.
+
+[acmetool]: https://hlandau.github.io/acme/
+[rootless acmetool setup]: https://hlandau.github.io/acme/userguide#annex-root-configured-non-root-operation
+
+
+### Setting up the hook
+
+An example hook using `nsupdate`
+[already ships](https://github.com/hlandau/acme/blob/master/_doc/dns.hook)
+with acmetool.
+
+- Install `dnsutils` (contains `nsupdate`)
+- Link the hook from the documentation:
+
+ # ln -s ../../../usr/share/doc/acmetool/examples/dns.hook /etc/acme/
+
+- Write the configuration for it:
+
+ # cat > /etc/default/acme-dns
+ NSUPDATE_ARGS="-k /etc/acme/Kmetrics.mgmt.realraum.at.+163+06888.key"
+
+ nsupdate_cmds() {
+ echo server 192.168.33.1
+ }
+
+- Test
+
+ # sudo -u acme /etc/acme/hooks/dns.hook challenge-dns-start \
+ foo.example.com "" "foobar"
+ # sudo -u acme /etc/acme/hooks/dns.hook challenge-dns-start \
+ foo.example.com "" "foobar"
+
+ If either of those commands fail with an error,
+ check the DNS traffic (`tcpdump -vvv port 53`)
+
+
+### Certificate acquisition
+
+Once everything is setup, getting a certificate from Let's Encrypt
+is quite easy:
+
+ # sudo -u acme acmetool want metrics.mgmt.realraum.at
+
+
+### Testing automated removal
+
+Last thing, you should check that automatic renewal is setup and works:
+
+- Is the cron job in place?
+
+ # crontab -u acme -l
+
+ 37 13 * * * /usr/bin/acmetool --batch reconcile
+
+- Is the default hook for reloading services in place?
+ If you delete the certificate and key, then run `acmetool`,
+ do your services use the new certificate?
+
+ # [check the service's certificate fingerprint, with openssl s_client]
+ # rm -rf /var/lib/acme/keys/*
+ # sudo -u acme acmetool --batch reconcile
+ # [check the service's certificate fingerprint, they should differ]