Introduction to DNSSEC with ZKT

About ZKT

ZKT is a simple tool to make key management and administration of DNSSEC for your domains easier. This is an introduction on how ZKT can be used to sign an example zone.

ZKT is a project written and managed by Holger Zulegerand. The project website is here: http://www.hznet.de/dns/zkt/ and the github repo here:  https://github.com/hzuleger/ZKT

Preparations

note: this guide was prepared using Ubuntu 14.04

Install ISC BIND, the name server software. Version 9.9 or greater is recommended.

$ sudo apt-get install bind9 dnsutils

 

Recommendation: do not mix the authoritative nameserver function and caching resolver in the same instance of BIND. Ensure that ”recursion no” is set in your named.conf.options.


options {
recursion no;
}

Add a user for the zkt application:

$ sudo useradd -m -d /home/zkt -s /bin/bash -c "zkt user" -G bind -U zkt

Create the directory structure that is going to be used for the zone files and its keys. Below is an example of a structure that works well and is used in this guide.

Configuration File:

/etc/zkt/dnssec.conf

$ sudo mkdir --mode=0755 /etc/zkt

Zone files and keys
/var/dnssec/zkt
/var/dnssec/keysets


$ sudo mkdir --mode=755 -p /var/dnssec/zkt
$ sudo chown zkt:zkt /var/dnssec/zkt
$ sudo mkdir --mode=775 /var/dnssec/keysets
$ sudo chown root:zkt /var/dnssec/keysets

/usr/local/bin is the standard installation directory for ZKT and contains the programs:

  • zkt-signer to sign a zone and manage the lifetime and rollover of the zone signing keys
  • zkt-conf for managing the config file
  • zkt-ls to list DNSSEC zone keys
  • zkt-keyman to manage DNSSEC zone keys manually (seldom used)
  • zkt-soaserial

Compile and install ZKT

Install git so you can get the latest source code from the git repo

$ sudo apt-get install git

Get the source code for ZKT from the git repo:
$ git clone https://github.com/hzuleger/ZKT.git

Change to the ZKT directory you have cloned and build ZKT

$ cd ZKT
ZKT$ ./configure
ZKT$ make
ZKT$ sudo make install

Configure ZKT with a DNSSEC policy

Start by creating a dnssec.conf file in /etc/zkt/


$ sudo touch /etc/zkt/dnssec.conf
$ sudo chmod 640 /etc/zkt/dnssec.conf
$ sudo chown root:zkt /etc/zkt/dnssec.conf

with the following content:  (use your editor of choice) note: these are decent values for signing intervals, key lengths, but please update all according to your DNSSEC policy or to the needs of your lab.

#
# @(#) dnssec.conf vT0.90 (c) Feb 2005 - Dec 2006 Holger Zuleger hznet.de
#

# dnssec-zkt options
Zonedir: "/var/dnssec/zkt"
Recursive: True
PrintTime: False
PrintAge: True
LeftJustify: False

# zone specific timing values
ResignInterval: 1d # (86400 seconds)
Sigvalidity: 10d # (864000 seconds)
Max_TTL: 8h # (28800 seconds)
Key_TTL: 1h
Propagation: 5m # (300 seconds)
Serialformat: Unixtime

# signing key parameters
KSK_lifetime: 10y
KSK_algo: RSASHA256 # (Algorithm ID 8)
KSK_bits: 2048
KSK_randfile: "/dev/urandom"
ZSK_lifetime: 30d # (2592000 seconds)
ZSK_algo: RSASHA256 # (Algorithm ID 8)
ZSK_bits: 2048
ZSK_randfile: "/dev/urandom"

# dnssec-signer options
Keyfile: "dnskey.db"
Zonefile: "zone.db"
Keysetdir: "/var/dnssec/keysets"
DLV_Domain: ""
Sig_Pseudorand: True

Configure an example zone ( unsigned so far )

edit the named.conf.local file to add example.se to your bind configuration


zone "example.se" {
type master;
file "/var/dnssec/zkt/example.se./zone.db";
};

Create the zone file

$ sudo mkdir -p /var/dnssec/zkt/example.se.
$ sudo touch /var/dnssec/zkt/example.se./zone.db
$ sudo chown zkt:zkt /var/dnssec/zkt/example.se.
$ sudo chown zkt:zkt /var/dnssec/zkt/example.se./zone.db

Edit your example.se zone file /var/dnssec/zkt/example.se./zone.db

$ORIGIN example.se.
$TTL 60
@ IN SOA ns1.example.se. hostmaster.example.se. (
1 ; serial
360 ; refresh (6 minutes)
360 ; retry (6 minutes)
1800 ; expire (30 minutes)
60 ; minimum (1 minute)
)

;NS
IN NS ns1.example.se.
IN NS ns2.example.se.

ns1.example.se. IN A 127.0.0.1
ns2.example.se. IN A 127.0.0.1


*note: if you are using Ubuntu and apparmor you need to adjust your apparmor configuration to allow bind to read /var/dnssec/zkt by adding   /var/dnssec/zkt/** r, to /etc/apparmor.d/usr.sbin.named and restarting apparmor

run rndc reconfig bind and test your configuration

*note: when you make configuration changes you need to run rndc reconfig, if you are just updating an already configured zone then it is enough with a rndc reload.

$ sudo rndc reconfig
$ dig @localhost example.se soa
; <<>> DiG 9.9.5-3ubuntu0.14-Ubuntu <<>> @localhost example.se soa
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 40853
;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 2, ADDITIONAL: 3
;; WARNING: recursion requested but not available
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;example.se.            IN  SOA
;; ANSWER SECTION:
example.se.     60  IN  SOA ns1.example.se. hostmaster.example.se. 1 360 360 1800 60
;; AUTHORITY SECTION:
example.se.     60  IN  NS  ns1.example.se.
example.se.     60  IN  NS  ns2.example.se.
;; ADDITIONAL SECTION:
ns1.example.se.     60  IN  A   127.0.0.1
ns2.example.se.     60  IN  A   127.0.0.1
;; Query time: 4 msec
;; SERVER: 127.0.0.1#53(127.0.0.1)
;; WHEN: Wed Jun 07 13:16:18 UTC 2017
;; MSG SIZE  rcvd: 154

Signing the zone files

The zonefile zone.db must contain the line “$INCLUDE dnskey.db”, which includes the KSK and the ZSK for the zone or else the zone is not going to be signed.

Edit  your example.se zone file /var/dnssec/zkt/example.se./zone.db again and add the include statement


$ORIGIN example.se.
$TTL 60
@ IN SOA ns1.example.se. hostmaster.example.se. (
1 ; serial
360 ; refresh (6 minutes)
360 ; retry (6 minutes)
1800 ; expire (30 minutes)
60 ; minimum (1 minute)
)

;NS
IN NS ns1.example.se.
IN NS ns2.example.se.

ns1.example.se. IN A 127.0.0.1
ns2.example.se. IN A 127.0.0.1

$INCLUDE dnskey.db


Initially the file zone.db.signed is an empty file. The file works as an indicator for ZKT that the zone is to be signed. When the zone has been signed it is stored in zone.db.signed. In the original file, zone.db, only the serial number changes.

Create the zone.db.signed file


$ sudo touch /var/dnssec/zkt/example.se./zone.db.signed
$ sudo chown zkt:zkt /var/dnssec/zkt/example.se./zone.db.signed
$ sudo chmod 664 /var/dnssec/zkt/example.se./zone.db.signed

Sign your example zone.

If this is the first time that the signer is run for a domain all keys are created, both KSK and ZSK. If everything went fine there should be a couple of new files in the zonefiles directories. The file zone.db.signed should be populated by DNSSEC signatures and the current keys.


$ sudo -u zkt zkt-signer -c /etc/zkt/dnssec.conf -v -v
parsing zone "example.se." in dir "/var/dnssec/zkt/example.se."
Check RFC5011 status
->not a rfc5011 zone, looking for a regular ksk rollover
Check KSK status
No active KSK found: generate new one
Check ZSK status
No active ZSK found: generate new one
Re-signing necessary: Modified zone key set
Writing key file "/var/dnssec/zkt/example.se./dnskey.db"
Signing zone "example.se."
Run cmd "cd /var/dnssec/zkt/example.se.; /usr/sbin/dnssec-signzone -C -g -p -d /var/dnssec/keysets -o example.se. -e +864000 -N unixtime zone.db K*.private 2>&1"
Cmd dnssec-signzone returns with exitcode=0: "zone.db.signed"
Signing completed after 0s.

Now edit your bind configuration to use the signed zone.

zone "example.se" {
type master;
file "/var/dnssec/zkt/example.se./zone.db.signed";
};

run rndc reconfig and test your updated configuration

$ sudo rndc reconfig
$ dig @localhost example.se soa +dnssec

; <<>> DiG 9.9.5-3ubuntu0.14-Ubuntu <<>> @localhost example.se soa +dnssec
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 51513
;; flags: qr aa rd; QUERY: 1, ANSWER: 2, AUTHORITY: 3, ADDITIONAL: 5
;; WARNING: recursion requested but not available

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags: do; udp: 4096
;; QUESTION SECTION:
;example.se. IN SOA

;; ANSWER SECTION:
example.se. 60 IN SOA ns1.example.se. hostmaster.example.se. 1496843290 360 360 1800 60
example.se. 60 IN RRSIG SOA 8 2 60 20170617124810 20170607124810 18798 example.se. ucAudk6EF2ln+JFTtXSlKl/Tnn1AJilmdD81oNXX/ZUgFLgSQcIzB+Mq iKuFyrPua5Mck33Nl3D+S+EXnqL2izmLb5m75qXnYBh7KH3QpHSOfkWp 59CXZpcyRLfXLeiBUvwgZdXgBAHUkC9RteWgQFqm5Z3zdK0/MijbTRcD VTbSoMjBRFAPJbIy/TVblzU9/YZ1o0881lups9lJEADSfsICOGFuLqPu UiUzV3cTFe4N/GEIslJgolQErw6r/bHUGl4Rko00rozSMWMHpwtbuKLi urXfd/nnBi+M0BpttsoHc8mrvmLg0+OU8urcEJ7GTHhdag99xOXunesN wqya9Q==

;; AUTHORITY SECTION:
example.se. 60 IN NS ns2.example.se.
example.se. 60 IN NS ns1.example.se.
example.se. 60 IN RRSIG NS 8 2 60 20170617124810 20170607124810 18798 example.se. VkGLysqYWp3FgTJht8598c17wyDHJH6fzbjkwFA//ucKeMi+zuunY1me /fLZBhmCGgEGOPoMKKkzZrQrDghaj06PddSQn2R2W7bBvxN6A1tYvCkm CoSBs8tWeX8Sl3jrKND16wqa4RfD/Rem5+34Vopi1BsDKXWfnfRDiTvt hdPzcUTU/t7vfv0wsMGcpN/Xw4XULwmZh5tPjTe9KYQTVZehSPKojnfE R4ViXY9OTuRSuxYVDTUznXCEqfBQd1Mkd0xsA8jxILezkUnZhLvqWrip bol+m1nhmyjKZP0HuNzuYvRgzN0/fDxXPBh2JDSU075L59VFiEVCa11g nvQFIA==

;; ADDITIONAL SECTION:
ns1.example.se. 60 IN A 127.0.0.1
ns2.example.se. 60 IN A 127.0.0.1
ns1.example.se. 60 IN RRSIG A 8 3 60 20170617124810 20170607124810 18798 example.se. BwGmc1fkj2zPAyoeprHbdaiVlMjixkgSQAmyR8CtQwPxwr23DNT7sK+Y ig662N3OTt0E1u/DW8tHLGrsBovoIMWQltQCpcuXjxBNzYySZTYqKDbZ 1GCc9A4oe/CALgS6jPLskqgSxYM0lq5+g+Mo7n3Bjopg5Hq1YzzxnqaV MDY05OxTvIMZG8BVKtLu6Z8iTF9W36HqYJ99G4jpn5wtvWtw5R4PAWr3 nkbOBp634CW39vCvc+jyOEC5yfCT7MbIRR1mOux5vcrHYdlAKn4g5Wdm tafs2kNWXZUWejVCo9elvwLFsfpt7m9qIQDKFDfkt319/6Rnm6CuYYQs 7JwYKQ==
ns2.example.se. 60 IN RRSIG A 8 3 60 20170617124810 20170607124810 18798 example.se. r5HnG2aNv88LgI9z5ZYqU6OyTvbYU5RBceuaemscr2sR+0SXShEMFO2l 0Dq0CbHALFVM0uEEttWFSk0IsT30/fMXECYyQwRNM7T0H9E09rR96MLe ROnNHZJxGc8k+K1fXIzFrGlXfHBg37NHNriO8LL5ca9zpL/0F2d9MRId mNPrRTnw5BQvKflIBoEWxWgLsC0W9qT5QUF1w7B/UDz3+XuYE9L9orMW sa9ebh2pb4EBQ1AHDtXhrZzPKVoQJ+bfDJ6bY6bUnRb9C99VOUYLJaLJ jtK3kzN6e5Cw0DcK+ZETVafmzfCas5rlIGqkL3cW1DS/jAtkdunmFA8T Mtsovg==

;; Query time: 4 msec
;; SERVER: 127.0.0.1#53(127.0.0.1)
;; WHEN: Thu Jun 08 08:13:56 UTC 2017
;; MSG SIZE rcvd: 1346


Regular scheduled signing

The signature needs to be renewed periodically. In order to do this you need to create a cron job in the user zkt crontab which might look like this:

*/5 * * * * /usr/local/bin/zkt-signer -c /etc/zkt/dnssec.conf -v -v; /usr/sbin/rndc reload

It is important to remember that  you need to reload bind after you resign the zone to keep your signatures up to date.

 

To make changes to the content of the zone you edit the zone.db file and then resign the zone, either manually or let your cron job do it for you.

Test editing your sample zone and add a AAAA record for ns1.example.se


$ORIGIN example.se.
$TTL 60
@ IN SOA ns1.example.se. hostmaster.example.se. (
1 ; serial
360 ; refresh (6 minutes)
360 ; retry (6 minutes)
1800 ; expire (30 minutes)
60 ; minimum (1 minute)
)

;NS
IN NS ns1.example.se.
IN NS ns2.example.se.

ns1.example.se. IN A 127.0.0.1
ns1.example.se. IN AAAA ::1
ns2.example.se. IN A 127.0.0.1

$INCLUDE dnskey.db


Check the results after your cron job has signed the zone and reloaded bind

dig @localhost example.se ns +dnssec

; <<>> DiG 9.9.5-3ubuntu0.14-Ubuntu <<>> @localhost example.se ns +dnssec
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 62841
;; flags: qr aa rd; QUERY: 1, ANSWER: 3, AUTHORITY: 0, ADDITIONAL: 7
;; WARNING: recursion requested but not available

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags: do; udp: 4096
;; QUESTION SECTION:
;example.se. IN NS

;; ANSWER SECTION:
example.se. 60 IN NS ns2.example.se.
example.se. 60 IN NS ns1.example.se.
example.se. 60 IN RRSIG NS 8 2 60 20170609124501 20170608124501 27954 example.se. ME8Qh7H9vKIDK7FK1kstlCzryh2pJlAAdW8PUsZhkLSiSEtzz4r+7uk5 HV6YSadehXLIgU4uQ2wd+ZU5oANalC1JSiyO9jbk4VTgkkhjTExIzHjt e5ZIlkGZoEVeOo92ZsyUVzr7crMGAwtQHW2qIOHRnJMAKcmZayKPFK2d +Lip1M1z4Qkr+D0wnV5mELiuG7hStrRUDKh5Za1seEs3IAYZVYVbl5g7 8ds+OTD8HQGRf9FA2xSekr/30Y9KQC6HyB+tlmXXc9Y31hYhgDpYjgRf jHClqbJQNVry7VWcvdf9GtI/JwItN2lWpqk+C20NZLmgk0w+fTjE8ecE VofJTw==

;; ADDITIONAL SECTION:
ns1.example.se. 60 IN A 127.0.0.1
ns1.example.se. 60 IN AAAA ::1
ns2.example.se. 60 IN A 127.0.0.1
ns1.example.se. 60 IN RRSIG A 8 3 60 20170609124501 20170608124501 27954 example.se. uzb/A8q/dKV/98lewDu59oVvIEGbGzfm72lHyJEWr7AoQB95xhGnKfWN BXiPsITMwuMf60BiaVYvmSUKU8XVHDqPpTJUukut47iFi4tVYOd76Nv4 M1FwqRg6DQgJcmJTKh1CSYuOB6yQX//L9EBNSShedte5B7cFMkr4m6XR z3DXupXoaX2eFMRQT3Ueq3QO1Qq62MbgAIoP2dt1t6UIXydmQPUSpBgf hIkt3FMJe6WHQDg1/ob0AasrWempMQfoXCEKCUA2TZbKRCXBxqvmA9SL SjX5sAaFkWO8lXgn6GoA0sM2YDWiBKxufe92Z473ZiZ6L8Zy3zmNYUiL SIRTKg==
ns1.example.se. 60 IN RRSIG AAAA 8 3 60 20170609124501 20170608124501 27954 example.se. nb5cgTfynDg6htRmjzX9BlAwyoWUkS8vY83wFwWL9xSUirWNfmj9Vuri slr/DOLqlKtlpqG/SCLnYB4J8rCAla4AEDWtQgQ4fLroDMhnR44bd4rT NfaUc/g1iWo5/2qlqZuLxjpIK2AfuGzBxPEciP9WRXvO3adkuG0kPX6i xBjpS+F+G/hhI6yu6t+OUSecYyv6Dn2J+0jZeSppCIuaOk9ftKHyBZWs ES48ZOnSD7hA684Wm4rOv3Wrr0PO9botbXKxIgnLuIblippaOI3FJDcc B7K0ulZ2MODG4hnNhvDuJwJA+RsKQvOEtc1bNTotwADMPaUoUwt3AupK ZdDXuw==
ns2.example.se. 60 IN RRSIG A 8 3 60 20170609124501 20170608124501 27954 example.se. B3Y/+3pgTonroUvJeS00bFFS6sHjmM1xWsGMZxcoBUkQNEfawpKNdfy6 Be3NNxHdkkCT47Q8UYO3lOwEQl6qUwbm5qomrWs5Xs0tAwtspf3WMENH c1xMZpkF14d1Imkwg/1eruMxmAqO34gKCOuV69d7P5jHzdcnrN0YH9zF SCY0k6tLagaBWIiUZneRI0qerLfhKwogXgrb5WNLfiJUFWLcOg7hjvz/ CC9tLBaAVOAl8B65Jntl+36ob2qfZ23s/y/fOT6WJ95atKjlpKFLYFZ9 2ACdQa90ZalONMCHOji2SqIJPZiQrUfEsefpPMC0HrEo8hXprcxylwzQ bTElvQ==

;; Query time: 4 msec
;; SERVER: 127.0.0.1#53(127.0.0.1)
;; WHEN: Thu Jun 08 13:45:56 UTC 2017
;; MSG SIZE rcvd: 1327

ZSK Key Rollover:

ZSK Key Rollover is maintained automatically by the ZKT software based on the values set in your dnssec.conf file

KSK Key Rollover: Double Signature

Create new KSK

$ sudo -u zkt zkt-keyman -c /etc/zkt/dnssec.conf -k -C example.se

You can see that the new KSK you created in our example has a key tag of 13411 and a Status of standby, you will use this key tag.

$ sudo -u zkt zkt-ls -c /etc/zkt/dnssec.conf -L
Keyname Tag Typ Sta Algorit Age
example.se. 61936 KSK act RSASHA2 6d 42m 4s
example.se. 13411 KSK sta RSASHA2 8s
example.se. 27954 ZSK dep RSASHA2 3h 4m48s
example.se. 44184 ZSK act RSASHA2 3h 4m48s

Activate new KSK
$ sudo -u zkt zkt-keyman --active=13411 -c /etc/zkt/dnssec.conf

You can see that the new KSK is now active

$ sudo -u zkt zkt-ls -c /etc/zkt/dnssec.conf -L
example.se. 61936 KSK act RSASHA2 6d 45m58s
example.se. 13411 KSK act RSASHA2 4m 2s
example.se. 27954 ZSK dep RSASHA2 3h 8m42s
example.se. 44184 ZSK act RSASHA2 3h 8m42s

Start signing DNSKEY RRset with both KSKs

$ sudo -u zkt zkt-signer -c /etc/zkt/dnssec.conf -v -v -f
parsing zone "example.se." in dir "/var/dnssec/zkt/example.se."
Check RFC5011 status
->not a rfc5011 zone, looking for a regular ksk rollover
Check KSK status
Check ZSK status
Re-signing necessary: Option -f
Writing key file "/var/dnssec/zkt/example.se./dnskey.db"
Signing zone "example.se."
Run cmd "cd /var/dnssec/zkt/example.se.; /usr/sbin/dnssec-signzone -C -g -p -d /var/dnssec/keysets -o example.se. -e +86400 -N unixtime zone.db K*.private 2>&1"
Cmd dnssec-signzone returns with exitcode=0: "zone.db.signed"
Signing completed after 0s.

Reload Bind
$ sudo rndc reload

Send DS to parent zone and wait until propagated and TTL of the old DS record has passed,  The DS records can be found in the ”Keysetdir” (as configured in the dnssec.conf file ), which in our lab set up you will find it in /var/dnssec/keysets/ named dsset-example.se.

Depreciate old KSK and stop signing with it.


$ sudo -u zkt zkt-keyman --depreciated=61936 -c /etc/zkt/dnssec.conf


$ sudo -u zkt zkt-ls -c /etc/zkt/dnssec.conf -L
Keyname Tag Typ Sta Algorit Age
example.se. 61936 KSK dep RSASHA2 6d 2h42m42s
example.se. 13411 KSK act RSASHA2 2h 46s
example.se. 27954 ZSK dep RSASHA2 5h 5m26s
example.se. 44184 ZSK act RSASHA2 5h 5m26s

Re-sign DNSKEY RRset with only the new key

$ sudo -u zkt zkt-signer -c /etc/zkt/dnssec.conf -v -v -f
parsing zone "example.se." in dir "/var/dnssec/zkt/example.se."
Check RFC5011 status
->not a rfc5011 zone, looking for a regular ksk rollover
Check KSK status
Check ZSK status
Re-signing necessary: Option -f
Writing key file "/var/dnssec/zkt/example.se./dnskey.db"
Signing zone "example.se."
Run cmd "cd /var/dnssec/zkt/example.se.; /usr/sbin/dnssec-signzone -C -g -p -d /var/dnssec/keysets -o example.se. -e +86400 -N unixtime zone.db K*.private 2>&1"
Cmd dnssec-signzone returns with exitcode=0: "zone.db.signed"
Signing completed after 0s.


Reload Bind
$ sudo rndc reload

You can now remove the DS for the depreciated KSK from the parent and destroy the old KSK
$ sudo -u zkt zkt-keyman --destroy=61936 -c /etc/zkt/dnssec.conf