DNSSEC goodness without losing vi access

For years I had been Wes’ dnssec-signing tools, even though rollerd just never worked right. After an update to my devuan DNS host, the libssl 1.0 was gone, and with it, MD5 support, and the dnssec-signing tools needed it. While I could have hacked the source code, that set of tools is really really dead, and it was time to move on.

Bind9 (what happened to bind10?) versions 9.16 upwards have lots of goodness. I’m running bind 9.19.2-0… debian11.

I have a configuration where all zone files live in /etc/domain, in subdirectories, with Makefiles to update the serial number in place. I edit zone files with vi, or more often now, remotely with Emacs and Tramp mode. (M-x compile is smart enough to invoke make on the remote system…) Some group permissions mean that nothing is root owned, and rndc reload has group permissions to read the key. My login is in the “bind” group.

I use etckeeper for audit logs.

At first i thought I could just use “update-policy local”, and “dnssec-policy default” and magic would happen. Yes, it was magical as bind9 followed my instructions, and obsoleted my type 5 (RSASHA1) keys, generated type 13 (ECDSAP256SHA256) and expected them to be used.

Oops. Of course, it’s not obvious at first when your zone goes invalid, but at least 8.8.8.8 validates DNSSEC, and that’s good, and you should be testing that way. Lots of places do not validate DNSSEC, so they don’t care. I did a mix or scrambling to put my keys back in place for some zones, and for other zones, I went to bash my registrar with the new DS records.

There is a way to write a better policy that won’t screw you up, but I don’t know how to do that, and it was mostly a forcing function for me. Also, permissions become a problem, because bind wants the keys private, the directories accessible and it wants to rewrite your zone files.

For many zones, I backed out of the dnssec-policy default and update-policy local, and went back to what I knew: edit zone files with vi, sign them, and load signed zones. Make sure to do this weekly, at least monthly.

My /etc/domain/sandelman.ca/Makefile looks like this:

FLAGS=-algorithm ECDSAP256SHA256
all:	db.sandelman.ca.signed db.sandelman.ottawa.on.ca.signed     \
        db.16_28.249.87.209.in-addr.arpa.signed db.acme.sandelman.ca.signed
   rndc reload

db.sandelman.ca.signed: db.sandelman.ca hosts.onsite hosts.sandelman ../stamp acme.sandelman
    dnssec-signzone -S -z -N date -o sandelman.ca db.sandelman.ca

db.16_28.249.87.209.in-addr.arpa.signed: db.16_28.249.87.209.in-addr.arpa ../stamp
    dnssec-signzone -S -z -N date -o 16_28.249.87.209.in-addr.arpa \
           db.16_28.249.87.209.in-addr.arpa

This produces files like:

-rw-rw-r-- 1 bind bind   2244 Feb 12 17:32 db.sandelman.ca
-rw-r--r-- 1 mcr  bind 150455 Feb 13 17:48 db.sandelman.ca.signed

(there are many $INCLUDES in that file, btw, in case the size difference is surprising)

and you just load the signed files:

zone "sandelman.ca" {
    zone-statistics yes; type master;
    file "/etc/domain/sandelman.ca/db.sandelman.ca.signed";
    //update-policy local;
    //key-directory "/etc/domain/sandelman.ca";
};

Really, this is exactly what I did before, but I’ll note that the -S -z -N date flags to dnssec-signzone do a fair bit of other thinking about the keys.

-N date uses the date for the SOA serialnumber.
-S is the smart signing option
-z is about the KSK, and probably should get turned off.

When you start, and you aren’t sure what’s upstream, and when things should rotate, probably leave off the -S flag.

As an example of a zone which I allow bind9 to completely maintain. For this zone, “dasblinkenled.org”, everything in the zone is dynamically updated via nsupdate. I never edit thie file with vi.

zone "dasblinkenled.org" {
   type master;
   file "/etc/domain/dasblinkenled.org/db.dasblinkenled.org";

   key-directory "/etc/domain/dasblinkenled.org";
   inline-signing yes;
   #	auto-dnssec maintain;
   dnssec-policy default;

   update-policy {
      grant highway. subdomain r.dasblinkenled.org. ANY;
      ...
   	   };

};

I haven’t turned on auto-dnssec maintain yet, because I don’t think that I have any connection to my registrar to update things, and I don’t think the dot.org people do CDS yet.

But, what about in between? How do that The answer is inline DNSSEC signing.

In theory, that means having a stealth primary DNS server with your zone unsigned in editable form as you have done since 1987. Then another DNS server acts as a secondary, sucks the zone over, signs it, and then serves it.

In practice, you can do this all with view.

I started by putting an extra IPv6 on my loopback… but you could use 127.1.1.1 or whatever:

1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default inet6 2607:f0b0:f::babe:311/128 scope global valid_lft forever preferred_lft forever

inet6 2607:f0b0:f::babe:f00d/128 scope global
   valid_lft forever preferred_lft forever

2607:f0b0:f::babe:f00d is the IPv6 that I use to answer queries, and I have a /128 route for it. 2607:f0b0:f::babe:311 is my new address.

I edited my /etc/bind/named.conf to move everything I used to have into a view “authoritative” and I created a new view “unsigned”:

include "/etc/bind/named.conf.options";

 view "authoritative" {
    // This should match our internal networks.
    match-destinations { 2607:f0b0:f:3::190/128; 2607:f0b0:f::babe:f00d/128; 209.87.249.18/32; 172.30.3.190/32; };

    include "/etc/bind/named.conf.local";
    include "/etc/bind/named.conf.default-zones";
 };

 view "unsigned" {
    // This is a special IP which serves unsigned zones
    match-destinations { 2607:f0b0:f::babe:311e/128; };
    match-clients { 2607:f0b0:f::/56; };

    notify-source-v6   2607:f0b0:f::babe:311e;
    transfer-source-v6 2607:f0b0:f::babe:311e;
    also-notify { 2607:f0b0:f:3::190; };

    include "/etc/bind/named.conf.unsigned";

};

I included match-clients to allow my local machines to be able to do diagnostics, but probably not necessary. 2607:f0b0:f:3::190; is the public IPv6 of the machine.

In my named.conf.local, I configure brski.org as a secondary

zone "brski.org" {
 zone-statistics yes;
 file "/etc/domain/brski.org/signed/db.brski.org";
 type secondary; masters { 2607:f0b0:f::babe:311e; };
 key-directory "/etc/domain/brski.org/signed";
 dnssec-policy default;
};

I use an explicit key-directory because I need to turn the directory over to bind. It does stuff with the directory. I have several ones in that directory.

% ls -ld /etc/domain/brski.org/signed
drwxr-sr-x 2 bind bind 4096 Feb 14 00:21 /etc/domain/brski.org/signed/

tilapia-[/etc/bind] mcr 10027 %ls -l /etc/domain/brski.org/signed
total 200
-rw-r--r-- 1 bind bind  399 Jul  6  2022 Kbrski.org.+013+25798.key
-rw------- 1 bind bind  215 Jul  6  2022 Kbrski.org.+013+25798.private
-rw-r--r-- 1 bind bind  651 Jul  6  2022 Kbrski.org.+013+25798.state
-rw-r--r-- 1 bind bind  505 Jul  6  2022 Kbrski.org.+013+54683.key
-rw------- 1 bind bind  263 Jul  6  2022 Kbrski.org.+013+54683.private
-rw-r--r-- 1 bind bind  736 Jul  6  2022 Kbrski.org.+013+54683.state
-rw-r--r-- 1 bind bind  403 May 17  2022 Krfc8990.org.+013+36433.key
-rw------- 1 bind bind  215 May 17  2022 Krfc8990.org.+013+36433.private
-rw-r--r-- 1 bind bind  653 May 17  2022 Krfc8990.org.+013+36433.state
-rw-r--r-- 1 bind bind  403 May 17  2022 Krfc8994.org.+013+55582.key
-rw------- 1 bind bind  215 May 17  2022 Krfc8994.org.+013+55582.private
-rw-r--r-- 1 bind bind  653 May 17  2022 Krfc8994.org.+013+55582.state
-rw-r--r-- 1 bind bind  402 May 17  2022 Krfc8995.org.+013+07016.key
-rw------- 1 bind bind  215 May 17  2022 Krfc8995.org.+013+07016.private
-rw-r--r-- 1 bind bind  652 May 17  2022 Krfc8995.org.+013+07016.state
-rw-r--r-- 1 bind bind 4282 Jul  5  2022 db-5OdbL0tB
-rw-r--r-- 1 bind bind 4334 May 17  2022 db-83c4tcE9
-rw-r--r-- 1 bind bind 4315 May 17  2022 db-b9O02zWO
-rw-r--r-- 1 bind bind 4334 May 17  2022 db-y37K1Ddm
-rw-r--r-- 1 bind bind  635 Aug 18 20:21 db.brski.org
-rw-r--r-- 1 bind bind  684 Aug 18 20:21 db.brski.org.jnl
-rw-r--r-- 1 bind bind 3494 Feb 10 06:44 db.brski.org.signed
-rw-r--r-- 1 bind bind 1944 Feb 10 06:44 db.brski.org.signed.jnl
-rw-r--r-- 1 bind bind  704 Aug 18 18:16 db.rfc8990.org
-rw-r--r-- 1 bind bind  512 Jul  6  2022 db.rfc8990.org.jbk
-rw-r--r-- 1 bind bind  692 Aug 18 18:16 db.rfc8990.org.jnl
-rw-r--r-- 1 bind bind 3789 Feb 11 18:36 db.rfc8990.org.signed
-rw-r--r-- 1 bind bind 3472 Feb 11 18:36 db.rfc8990.org.signed.jnl
-rw-r--r-- 1 bind bind  659 Aug 18 17:09 db.rfc8994.org
-rw-r--r-- 1 bind bind  512 Jul  6  2022 db.rfc8994.org.jbk
-rw-r--r-- 1 bind bind  692 Aug 18 17:09 db.rfc8994.org.jnl
-rw-r--r-- 1 bind bind 3610 Feb 13 06:49 db.rfc8994.org.signed
-rw-r--r-- 1 bind bind 5856 Feb 13 06:38 db.rfc8994.org.signed.jnl
-rw-r--r-- 1 bind bind  659 Aug 18 17:19 db.rfc8995.org
-rw-r--r-- 1 bind bind  512 Jul  6  2022 db.rfc8995.org.jbk
-rw-r--r-- 1 bind bind  692 Aug 18 17:19 db.rfc8995.org.jnl
-rw-r--r-- 1 bind bind 3610 Feb 14 00:21 db.rfc8995.org.signed
-rw-r--r-- 1 bind bind 4700 Feb 14 00:07 db.rfc8995.org.signed.jnl
-rw-r--r-- 1 bind bind 5612 May 17  2022 jn-ESV2pai7
-rw-r--r-- 1 bind bind 2904 May 17  2022 jn-J2BO2X8Z
-rw-r--r-- 1 bind bind 2904 May 17  2022 jn-f3fDZzat
-rw-r--r-- 1 bind bind 5444 Jul  5  2022 jn-oQ4OX5Wi

Now, in the newly created /etc/bind/named.conf.unsigned, I have:

zone "brski.org" {
 zone-statistics yes; type master;
 file "/etc/domain/brski.org/db.brski.org";
};

Just totally ordinary. Edit with vi/tramp, have a Makefile update the serial, rndc reload. It’s a bit weird to have the bind9 process secondary from itself, but it works. Some people would turn to containers or the like to make it all work, and that might be simpler for some people.