Add bind9-api post
This commit is contained in:
parent
2e4008aff7
commit
9083331238
132
content/post/bind9-api.md
Normal file
132
content/post/bind9-api.md
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
+++
|
||||||
|
date = "2018-07-15T17:00:00+02:00"
|
||||||
|
publishdate = "2018-07-15T17:00:00+02:00"
|
||||||
|
title = "BIND9 API"
|
||||||
|
description = ""
|
||||||
|
draft = true
|
||||||
|
categories = ["rust", "letsencrypt", "dns"]
|
||||||
|
tags = ["rust", "actix-web"]
|
||||||
|
|
||||||
|
+++
|
||||||
|
|
||||||
|
I manage most of my domains using my own nameservers, running BIND9 on
|
||||||
|
two Debian VPS located in Italy (master) and France (slave). Until
|
||||||
|
now, I've been changing the DNS records by SSHing into the machine and
|
||||||
|
editing the zonefile by hand. This worked fine since I rarely needed
|
||||||
|
to change any DNS records. Then earlier this year, [LetsEncrypt][0]
|
||||||
|
put the ACME v2 endpoint into production which allows users to issue
|
||||||
|
wildcard certificates using the DNS challenge. This put me into a
|
||||||
|
situation where I needed to create, update and delete DNS records
|
||||||
|
automatically.
|
||||||
|
|
||||||
|
<!-- more -->
|
||||||
|
|
||||||
|
While the LetsEncrypt HTTP challenge requires the user to make the
|
||||||
|
challenge flag available via HTTP under
|
||||||
|
`http://www.example.com/.well-known/acme-challenge`. This way, the
|
||||||
|
ACME endpoint can only verify ownership over a specific subdomain
|
||||||
|
(`www.example.com` in this case). The DNS challenge looks for the flag
|
||||||
|
in the TXT record `_acme-challenge.example.com`. This allows the ACME
|
||||||
|
endpoint to validate ownership over the whole domain and it is
|
||||||
|
possible to issue a wildcard certificate for `*.example.com`.
|
||||||
|
|
||||||
|
Since DNS setups vary depending on the domain provider or used DNS
|
||||||
|
server, certbot can use manual auth and cleanup hooks, that receive
|
||||||
|
the domain name and challenge flag via the environment variables
|
||||||
|
`$CERTBOT_DOMAIN` and `$CERTBOT_VALIDATION` respectively.
|
||||||
|
|
||||||
|
Once the challenge mechanism was understood, I needed a way to
|
||||||
|
programmatically create and delete records on my BIND9 server. I
|
||||||
|
decided to implement I REST-like webservice to run on the same machine
|
||||||
|
as BIND9 and modify records using the `nsupdate` command.
|
||||||
|
|
||||||
|
The REST API offers two methods:
|
||||||
|
|
||||||
|
```
|
||||||
|
POST /record
|
||||||
|
X-Api-Token: <api-token>
|
||||||
|
|
||||||
|
{
|
||||||
|
"name": "_acme-challenge.example.com",
|
||||||
|
"value": "<challenge flag>",
|
||||||
|
"record": "TXT",
|
||||||
|
"ttl": 1337
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
DELETE /record
|
||||||
|
X-Api-Token: <api-token>
|
||||||
|
|
||||||
|
{
|
||||||
|
"name": "_acme-challenge.example.com",
|
||||||
|
"record": "TXT"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The `X-Api-Token` header contains the SHA256-HMAC over the request
|
||||||
|
body using a pre-shared secret to prevent unauthenticated use of the
|
||||||
|
API but this still does not protect against replay attacks. If an
|
||||||
|
attacker managed to intercept an request to the API, (s)he would be
|
||||||
|
able to resend the same request to the server and re-execute the
|
||||||
|
command. To prevent this, the API server has to be placed behind a
|
||||||
|
reverse proxy like nginx to encrypt the requests using TLS or as I am
|
||||||
|
doing it, make the server listen on a private IP address inside an
|
||||||
|
encrypted VLAN ([tinc][1] in my case).
|
||||||
|
|
||||||
|
For the implementation of the API and the client, I chose to use Rust
|
||||||
|
with the [actix-web][2] framework for the server and [reqwest][3] to
|
||||||
|
make HTTP requests on the client side. While I have already worked
|
||||||
|
with other Rust web frameworks, namely [Rocket][4] for my Bachelor
|
||||||
|
thesis but it depends on the nightly branch of the compiler and is a
|
||||||
|
pain to maintain over a longer period of time. Also actix-web is
|
||||||
|
_really_ fast[^actix-performance].
|
||||||
|
|
||||||
|
The client itself is independent of the way, certbot works and the
|
||||||
|
integration into the workflow is archived by bash scripts inspired by
|
||||||
|
[these INWX certbot hooks][5].
|
||||||
|
|
||||||
|
For the server to work, a DNS key has to be generated as described in
|
||||||
|
[the repository][6] to be able to modify the records using `nsupdate`.
|
||||||
|
I start the API server using a systemd service:
|
||||||
|
|
||||||
|
```
|
||||||
|
[Unit]
|
||||||
|
Description=BIND9 API
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=onshot
|
||||||
|
ExecStart=/usr/local/bin/bind9-api -k /etc/bind/dnskey -h 10.0.1.101 -t <api secret>
|
||||||
|
ExecStop=pkill bind9-api
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
```
|
||||||
|
|
||||||
|
The client is configured using the configuration file
|
||||||
|
`/etc/bind9apiclient.toml` that contains the API URL and secret.
|
||||||
|
|
||||||
|
After placing the client somewhere in `$PATH` and putting the certbot
|
||||||
|
hooks on the machine that should issue the certificates, I can invoke
|
||||||
|
certbot like followed:
|
||||||
|
|
||||||
|
```
|
||||||
|
certbot certonly -n --agree-tos --server \
|
||||||
|
https://acme-v02.api.letsencrypt.org/directory --preferred-challenges=dns-01 \
|
||||||
|
--manual --manual-auth-hook /usr/lib/letsencrypt-bind9/certbot-bind9-auth \
|
||||||
|
--manual-cleanup-hook /usr/lib/letsencrypt-bind9/certbot-bind9-cleanup \
|
||||||
|
--manual-public-ip-logging-ok -d example.com -d '*.example.com'
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
[0]: https://letsencrypt.org/
|
||||||
|
[1]: https://www.tinc-vpn.org/
|
||||||
|
[2]: https://github.com/actix/actix-web/
|
||||||
|
[3]: https://github.com/seanmonstar/reqwest/
|
||||||
|
[4]: https://github.com/SergioBenitez/Rocket/
|
||||||
|
[5]: https://github.com/kegato/letsencrypt-inwx/
|
||||||
|
[6]: https://github.com/vbrandl/bind9-api#server
|
||||||
|
|
||||||
|
[^actix-performance]: https://www.techempower.com/benchmarks/#section=data-r16&hw=ph&test=plaintext
|
||||||
|
|
||||||
|
> vim: set filetype=markdown ts=4 sw=4 tw=70 noet :
|
Loading…
Reference in New Issue
Block a user