Automatic Key Rolling

I recently moved my test domains onto a separate DNS master so that I could more freely tinker with these domains without risk to my regular stable domains.

I use catalog zones (maybe this is for another post!) to distribute the zones to save myself the bother of having to configure all the slaves, particularly since I’ve recently started spinning up an experimental anycast network of virtual machines, and so I added a second catalog to my slave servers, and away we went.

I’m a keen user of debian, and so I built the test master on ‘sid’ so I could run bleeding edge.

The benefit, primarily, was that whereas Debian 10 gets BIND 9.11.5, sid gets 9.16.8 (at the time of writing) as well as a newer version of openssl, meaning I could sign a zone with algorithm 16, ED448. DS digest algorithm 4 (SHA-384) is also supported.

The biggy for me, though, and the main driver for having the newer version of BIND, was dnssec-policy; getting BIND to automatically roll your keys.

I’m not aware of an ability in this version to support either a hook to run a script to interact with your registrar to update a DS record when your KSK rolls, nor an ability to automate CDS or CDNSKEY, but they’ll be coming at some point in the future.

I decided to test this by auto-rolling my ZSK, so, I added the following to /etc/bind/named.conf.options :

dnssec-policy normal {
	dnskey-ttl PT1H;
	keys {
		ksk lifetime unlimited algorithm ecdsa384;
		zsk lifetime 90D algorithm ecdsa384;
	max-zone-ttl P1D;
	parent-ds-ttl P1D;
	parent-propagation-delay PT1H;
	parent-registration-delay P1D;
	publish-safety PT1H;
	retire-safety PT1H;
	signatures-refresh P5D;
	signatures-validity P2W;
	signatures-validity-dnskey P2W;
	zone-propagation-delay PT5M;

You don’t need to create initial keys or anything; BIND will do all the key juggling automatically, and will store them wherever you set key-directory to in your options section.

It doesn’t matter how you add your domains; I use rndc addzone as I have scripts that automate this and adding the zone to the catalog (again, that’ll be in another post at some point), the key thing being you just specify the policy in the zone. I’m also using inline-signing; whether you do will depend on your setup. Here’s a sample:

zone "" {
    type master;
    file "/path/to/";
    dnssec-policy "normal";
    inline-signing yes;

…and as if by magic, rndc reconfig, and the keyfiles are created, and the zone signed.


More DNS Anycast

Also known as “how to do BGP with Vultr using ExaBGP”.

Having previously written about locally anycasting services within my home network, I recently decided to run an experiment anycasting a prefix on the internet.

I’ve used ExaBGP before, and so it was a no brainer to use it again. For anycasted services, it offers a couple of benefits; it’s small and lightweight, it’s in many linux distros, and it can easily spawn a watchdog process that you can use to control your prefix advertisements.

I’ve been using Vultr for my authoritative DNS servers for a while, and so it was also a bit of a no brainer to use their services for this. I’m familiar with their UI, I already have an account, and even on the cheapest virtual machines, you can do BGP; you just need to send them a letter of authority (LOA) proving you own the address space you plan on advertising. I have my own IPv6 PI address space from RIPE, courtesy of a friendly sponsoring LIR, as well as my own ASN, so I was all set.

The other nice thing about Vultr is that the BGP session is the same peer IP at the Vultr end regardless of which of their datacentres you choose to spin things up in which means your automation to configure things is easier.

Vultr insist on a BGP session password, and the first problem I ran into turns out to be related to this, and so part of the reason for writing this is to help out anyone that also runs into this problem.

I went down a bit of a rabbit hole thinking the problem was to do with multihop BGP (Vultr’s sessions are multihop) and wondered if I needed to be setting the TTL on the outbound packets. This turned out not to be the case, but I left the settings in place anyway.

I installed BIRD and used one of Vultr’s canned configs for the virtual machine in question, and this worked like a charm, so this steered me in the direction of the problem being in my configuration of ExaBGP.

BIRD would have done the job, but I’d have had to write a new watchdog, and the one I have for ExaBGP is tried and tested, tweaked to my needs, and works well, so I was keen to get ExaBGP working. It’s a complete re-write from the one I talk about in the earlier blog post, so maybe I’ll write a post on that soon…

Either in a template or neighbor configuration, depending on the complexity of your needs, you just need outgoing-ttl 2; and incoming-ttl 2;

By default, ExaBGP expects md5-password to be a base64 encoded string, and so if what you’ve specified is just the plain text string for the session, it won’t work. If you want to specify the plain text password in this parameter, you need to set md5-base64 to false.

I’m using ansible to automate configuration, and so the template for my exabgp.conf looks like this:

process monitor {
	run /usr/local/bin/exabgp-healthcheck anycast;
	encoder text;

neighbor 2001:19f0:ffff::1 {
	local-address {{ ansible_default_ipv6.address }};
	router-id {{ router_id }};
	local-as {{ as_number }};
	peer-as 64515;
	hold-time 10;
	group-updates true;
	md5-password {{ md5_password }};
	md5-base64 false;
	outgoing-ttl 2;
	incoming-ttl 2;

	capability {
		graceful-restart 10;

	family {
		ipv6 unicast;

	api service {
		processes [ monitor ];

I don’t have any IPv4 prefixes to advertise, so you’d need to add the relevant bits to the above if you do.

I had upgraded ExaBGP to version 4 as part of a distro upgrade on my internal resolvers, and rather than update the watchdog script, I opted for reverting ExaBGP’s setting instead, and so in exabgp.env I altered the ack setting to false in the [exabgp.api] section.