This is an attempt to implement an API to create, update or delete DNS records on a BIND9 DNS server.
To compile the code, you first need to install Rust. Then you can run
cargo build --release
in the project root. The server and client binary will be located in
./target/release/bind9-api
and ./target/release/bind9-api-client
respectively.
The server will wait for incoming requests and uses the nsupdate
command to
perform operations on the BIND9 nameserver. For the server to work, a DNS key is
needed to perform the updates.
$ dnssec-keygen -r /dev/urandom -a HMAC-SHA256 -b 256 -n HOST dnskey
Copy the Key
section of the resulting Kdnskey*.private
file into a file that
looks like this:
key "dns-key" {
algorithm hmac-sha256;
secret "<your secret>";
}
And extend the zone section of the zones you'd like to modify in your
named.conf.local
zone "example.com" {
type master;
file "/var/lib/bind/db.example.com";
...
allow-update { key "dns-key"; };
...
}
Now you can start the server:
$ ./bind9-api -k <path to dnskey> -t <your api token>
By default, the server will bind to 0.0.0.0:8000
. The host and port to bind
to, can be changed using the -h
and -p
flags respectively. For production
use, you should bind to a private IP address (LAN or VLAN) or to 127.0.0.1
and
put the server behind a reverse proxy that offers TLS.
The client is used to perform changes to the DNS zone from any server. My use
case is to perform LetsEncrypt DNS challenges. The client will look for a
configuration file in /etc/bind9apiclient.toml
which looks like this:
# API server host
host = "http://127.0.0.1:8080"
# API secret
secret = "topsecret"
The client can perform two operations: Creating/updating and deleting DNS records. The client is invoked like this
$ ./bind9-api-client -d foo.example.com -r TXT update -v foobar
$ ./bind9-api-client -d foo.example.com -r TXT delete
POST /record
X-Api-Token: <api-token>
{
"name": "foo.example.com",
"value": "127.0.0.1",
"record": "A",
"ttl": 1337
}
DELETE /record
X-Api-Token: <api-token>
{
"name": "foo.example.com",
"record": "A"
}
The API token is a SHA256 HMAC over the request body using a pre-shared secret.
The current API design does not migrate replay attacks. An attacker that is able to intercept a request to the API can resend the same request again to execute the same operation. To prevent these kinds of attacks, you should use a reverse proxy and encrypt the connections using TLS. Future versions of the server might provide TLS functionality by itself.
In letsencrypt/
, two example scripts can be found to use the client as a
certbot hook for DNS challenges. It assumes that the client is located somewhere
in $PATH
and that the configurations file exists.
To obtain a new certificate, certbot can be invoked as 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'
This project is licensed under either of
- Apache License, Version 2.0, (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)
at your option.