DNSSEC
Policies
If dnssec
is enabled in bind__features
, the
/etc/bind/named.conf
file will contain a number of DNSSEC policies,
which control the kind of keys to use for signing a given zone, including
details such as the validity of the keys and various timeouts.
The default policies are (see bind__default_configuration
):
csk
csk-rollover
kskzsk
kskzsk-rollover
In general, keys in DNSSEC are used for one or both of the following roles: as a Zone Signing Key (ZSK), used to protect all zone data; or as a Key Signing Key (KSK), used to protect the ZSKs. The KSKs are typically longer-lived than the ZSKs, as they need to be published in the parent zone, which may require manual intervention and/or interaction with a registrar or registry (e.g. the organization that you bought the domain from).
A key that is used for both roles is referred to as a Combined Signing Key (CSK). Like the KSK, a CSK needs to be published in the parent zone.
Enabling DNSSEC
DNSSEC has to be enabled on a per-zone basis by specifying a suitable policy
for the given zone in its options
parameter, for example like this (see
Zones and Views for more details and complete examples):
bind__zones:
- name: 'example.com'
comment: 'My main domain'
options:
- name: 'dnssec-policy'
value: '"kskzsk-rollover"'
Key Rollover
The *-rollover
versions of the DNSSEC policies will create a CSK or KSK
which will automatically expire at certain intervals (default: one year) for
security reasons (for details, see e.g. RFC 6781 and RFC 7583).
Sometime before the key expiry, BIND will generate a new CSK/KSK, which needs
to be published in the parent zone, and some time after the expiry, the old
CSK/KSK also needs to be removed from the parent zone.
Automatic Key Rollover
BIND includes a relatively new feature which can, in theory, automate this,
by publishing suitable CDS and CDNSKEY (RFC 8078) resource records.
These resource records can be used by the parent zone to automatically update
its DS
records.
Often, these records can also be used to enable/disable DNSSEC validation.
Whether they can do so, and under which circumstances is registry/registrar
specific (one common policy to enable DNSSEC for a previously unsigned zone
is that the CDS
/CDNSKEY
entries need to remain stable for 72 hours
and have to be identical for all name servers for a given zone).
Publishing the addition/removal of keys in the parent zone is the first half of the automation puzzle. The second half is making BIND aware that the (un)publication has been performed in the parent zone.
For this, the parental-agents configuration option can be used.
For each zone which uses CDS
/CDNSKEY
updates, a list of parent
zone server IP addresses needs to be provided. A list of IP addresses can
be obtained using dig for the NS
records of the parent zone.
Assuming the BIND role is configured to be authoritative for example.com
,
the parent zone would be com.
:
# dig com. NS
...
;; ANSWER SECTION:
com. 142479 IN NS a.gtld-servers.net.
com. 142479 IN NS b.gtld-servers.net.
...
;; ADDITIONAL SECTION:
a.gtld-servers.net. 142479 IN A <first IP addr>
b.gtld-servers.net. 142479 IN A <second IP addr>
...
a.gtld-servers.net. 142479 IN AAAA <first IPv6 addr>
b.gtld-servers.net. 142479 IN AAAA <second IPv6 addr>
...
Note
If dig provides no ADDITIONAL
section, you'll have to
query the IP addresses separately using dig <fqdn> A
and/or
dig <fqdn> AAAA
.
All of, or some, of these IP addresses can then be added to a
parental-agents
block, either directly in the relevant zone(s):
bind__zones:
- name: 'example.com'
options:
- name: 'parental-agents'
options:
- name: 'parent-1'
raw: '<first IP addr>;'
- name: 'parent-2'
raw: '<second IP addr>;'
Or as a separate top-level option which can be reused in several zones:
bind__configuration:
- name: 'parental-agents dot-com-agents'
options:
- name: 'parent-1'
raw: '<first IP addr>;'
- name: 'parent-2'
raw: '<second IP addr>;'
bind__zones:
- name: 'example.com'
options:
- name: 'parental-agents'
options:
- name: 'parent-1'
raw: '"dot-com-agents";'
The parental-agents
for a given zone will be queried periodically by
BIND and if all the parental agents have the correct DS
entries, the
key will automatically be considered (un)published once the time intervals
configured in the dnssec-policy
have lapsed. This will also be reflected
in the logs produced by named:
# journalctl -u named --since 12:00 | grep checkds
Aug 15 18:34:41 test named[1102873]: zone example.com/IN/external-view: checkds: set 6 parentals
Aug 15 18:34:42 test named[1102873]: keymgr: checkds DS for key example.com/ECDSAP256SHA256/12349 seen published at Mon Aug 15 18:34:42 2022
In theory, the parental-agents
feature could also be used in conjunction
with the Rollover Script to not have to manually notify
BIND when the requested actions have been performed.
The parental-agents
feature will hopefully be further developed and
automated in future BIND releases.
Manual Key Rollover
Unfortunately, many registries/registrars do not support automated key updates
via CDS
/CDNSKEY
records yet (a list which may or may not be accurate
is provided here). As an alternative, this role includes a script which
will be executed periodically to check for keys which need to be (un)published
in the parent zone.
Rollover Script
The rollover script can be enabled/disabled via the
bind__dnssec_script_enabled
variable.
The script will run periodically (via debops.cron), and check for keys which are to be published in, or removed from, the parent zone.
The configuration for the script is defined in the
bind__dnssec_script_*_configuration
variables, which will be merged and
used to create a configuration file in
/etc/bind/debops-bind-rollkey.json
.
Note
If bind__dnssec_script_domains
has not been configured, the script
will attempt to automatically determine all zones which need to be
monitored. This currently requires dumping the complete zone database to
a file and parsing the file. This can be both slow (depending on zone size)
and error-prone, so a manual definition of the zones to monitor may be
worthwhile.
Depending on how bind__dnssec_script_method
has been configured,
the script will perform different actions when it detects that a key change
needs to be performed:
log
Key updates will simply be logged to a file, default
/var/log/debops-bind-rollkey.log
. The administrator will have to perform the requested actions manually and will also have to notify BIND manually once the requested actions have been completed. This can be done using rndc on the BIND host. This can be done by executing (on the BIND host):# rndc dnssec -checkds -key <key-id> -alg <key-alg> (published | withdrawn) zone [class [view]]
email
Key updates will be requested via email. Like for
log
, the administrator will have to notify BIND once the requested actions have been performed.external
An external script will be executed when keys need to be updated. Many registrars provide APIs which allow DNSSEC keys to be added/removed in a programmatic manner without user intervention (once suitable scripts have been initialized). The script will be called with six arguments:
<action> <key-id> <key-alg> <zone> <class> <view>
Where:
action
Describes the action which needs to be performed. Currently defined actions are:
withdraw
andpublish
.key-id
The numeric ID of the key on which the
action
needs to be performed.key-alg
The numeric algorithm of the key on which the
action
needs to be performed.zone
The zone which the key belongs to.
class
The class of the
zone
. It is exceedingly unlikely that this will be anything else thanIN
.view
The view which the
zone
belongs to. If no views are configured, the view will be_default
.
If the script exits with a status of
0
, the script will assume that the requested action was performed and will automatically notify BIND that the key(s) have been published/withdrawn.A suitable script will be expected to be located on the Ansible controller, in the
files_path
override directory defined in thedebops.cfg
file (see Configuration).For example, if
.debops.cfg
reads:... [override_paths] files_path = ansible/overrides/files ...
Then the custom script should be placed in
project-dir/ansible/overrides/files/usr/local/sbin/debops-bind-rollkey-action
NSEC vs NSEC3
DNSSEC provides two mechanisms for when the server has to sign replies
indicating that a particular resource record doesn't exist, NSEC
and
NSEC3
.
NSEC
is computationally less intensive than NSEC3
, but allows a
zone to be walked (i.e. for an attacker to determine all records in the
zone). NSEC
also has no opt-out, so for a sparsely signed zone (e.g.
the .com
TLD), the resulting zone files are much larger.
NSEC3
makes zone walking more difficult, at the cost of some
additional computational burden for the server. It also gives the server
the possibility to exclude some records (insecure delegations).
Whether DNSSEC signed zones should, by default, use NSEC3
instead of
NSEC
is controlled by the bind__dnssec_use_nsec3
variable,
see the BIND DNSSEC Guide for more details.
CSYNC
Another interesting possibility for automation which builds on DNSSEC is
CSYNC
(defined in RFC 7477), which allows a child zone to automatically
update NS
and, where applicable/supported, A
and AAAA
("glue")
records in the parent zone without manual intervention.
This can be done by publishing a suitable CSYNC
record in a DNSSEC
signed zone. For example:
example.com. 3600 IN CSYNC 66 3 NS A AAAA
Which would tell the parent zone that NS
records (and the A
/AAAA
records for the nameservers) for the child zone should be automatically updated
by the parent zone, but only if the SOA
serial number is at least 66
.
Or, alternatively:
example.com. 3600 IN CSYNC 0 1 NS A AAAA
Which would tell the parent zone that the same records should be updated from
the child zone no matter what the SOA
serial number is.
See RFC 7477 for further details and valid values for the CSYNC
record.
Similarly to the CDS
/CDNSKEY
situation, most registries/registrars do
not yet support automated record updates in the parent zone via CSYNC
.
Testing DNSSEC
The DNSSEC status of a domain can be tested using the dig tool.
First, make sure that DS
and DNSKEY
entries exist for the domain:
# dig DS example.com
...
;; ANSWER SECTION
example.com. 3600 IN DS ...
...
$ dig DNSKEY example.com
...
;; ANSWER SECTION
example.com. 3600 IN DNSKEY ...
Next, try looking up a record using DNSSEC (using the +dnssec
option):
# dig example.com A +dnssec
...
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 5638
;; flags: qr rd ra ad; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags: do; udp: 1232
;; QUESTION SECTION:
;example.com. IN A
;; ANSWER SECTION:
example.com. 3600 IN A <some IP address>
example.com. 3660 IN RRSIG A ...
...
The important parts are: the status: NOERROR
, that the flags:
include
ad
(authentic data), and that the answer includes an RRSIG
.
For more details, see the dig(1) man page.
Useful online tools for testing your DNSSEC configuration include: