OpenDNSSEC 2 BIND Key States

We have to transform OpenDNSSEC version 1.x key states into key states understood by BIND. Versions used:

Limitations / missing features

Currently we do not have support for:

  • Automating DNSSEC Delegation Trust Maintenance (CDS/CDNSKEY mechanism, RFC 7344)
  • Automated Updates of DNSSEC Trust Anchors (RFC 5011)

For this reason some options are ignored. This should be fixed in future.

BIND

Key states

BIND is using set of timestamps to determine use of a particular key. Information about timing options from manual page dnssec-settime(8) follow:

  • -P date/offset

    Sets the date on which a key is to be published to the zone. After that date, the key will be included in the zone but will not be used to sign it.

  • -P sync date/offset

    Sets the date on which CDS and CDNSKEY records that match this key are to be published to the zone.

  • -A date/offset

    Sets the date on which the key is to be activated. After that date, the key will be included in the zone and used to sign it.

  • -R date/offset

    Sets the date on which the key is to be revoked. After that date, the key will be flagged as revoked. It will be included in the zone and will be used to sign it.

  • -I date/offset:

    Sets the date on which the key is to be retired. After that date, the key will still be included in the zone, but it will not be used to sign it.

  • -D date/offset

    Sets the date on which the key is to be deleted. After that date, the key will no longer be included in the zone. (It may remain in the key repository, however.)

  • -D sync date/offset

    Sets the date on which the CDS and CDNSKEY records that match this key are to be deleted.

Implementation

Private key file format generated by dnssec-* utilities looks like this:

Created: 20151220115444
Publish: 20151220135927
Activate: 20151220145927
Revoke: 20151221083330
Inactive: 20151220155927
Delete: 20151220175927
SyncPublish: 20151220125927
SyncDelete: 20151220165927
  • Timestamps are in UTC.
  • Created timestamp is set to current time when dnssec-* utility is called.
  • Currently we do not have support for CDS/CDNSKEY mechanism (RFC 7344) so we will ignore SyncPublish (-P sync) and SyncDelete (-D sync) options.
  • We will also ignore field Revoke (-R) because RFC 5011 is not supported.

OpenDNSSEC

Key states

According to wiki page Key States, OpenDNSSEC is internally using following key states:

  • Generate: Keys in the generate state have been created and stored but not used yet.
  • Publish: Keys in the publish state have been published in the zone, but are not yet considered safe to use. (i.e. They have not been in the zone long enough to have propagated through the system.)
  • Ready: Keys in the ready state have been published long enough that we could safely start to use them.
  • Active: Keys in the active state are those that are in use.
  • Retired: Keys in the retire state have been in use but have been replaced by a successor, they are post-published while signatures generated with them might still be in the system.
  • Dead: Keys in the dead state have been retired long enough for them to be safely removed from the zone.
  • Standby KSK states: For standby KSKs there are some extra states which replace Published and Ready. This is because the standby keys are not introduced into the zone until they are needed. Instead their DS record is submitted to the parent - see Uploading a Trust Anchor. (The idea is that if the key is needed in an emergency the shortest timescale that it can be used in is the publication through the child system.)
    • DSSUB: The DS has possibly been submitted (if it happened automatically) but in any case we are waiting for the ds-seen command.
    • DSPUBLISH: The ds-seen command has been given, and we are now waiting for the various propagation delays and safety margins to pass.
    • DSREADY: The DS record is now considered safe to use, so the standby key is ready.
    • KEYPUBLISH: We have been asked to use the standby key so we have published it in the zone. Once the key has propagated through the system it will move into the active state.

Implementation

Enforcer is using database table dnsseckeys to store key state and timestamps for particular key. These timestamps often refer to future, to a planned state transitions. Beware: dnsseckeys table contains timestamps in local time zone!

Internally a key is represented as KsmKey structure filled by opendnssec//enforcer/ksm/ksm_key.c:KsmKey() function.

Enforcer normally generates configuration files for signerd and signerd then signs the zone using specifed keys. Relevant parts of signerd configuration format are described in opendnssec/conf/signconf.rnc:

element Key {
    # sign all the DNSKEY RRsets with this key?
    element KSK { empty }?,

    # sign all non-DNSKEY RRsets with this key?
    element ZSK { empty }?,

    # include this key in the zonefile?
    element Publish { empty }?,

    # deactivate this key (i.e. do not recycle any signatures)
    element Deactivate { empty }?,

    # Ignore DS and use RFC5011 to maintain chain of trust.
    element RFC5011 { empty }?
}

Element Deactivate is completely unused so we can ignore it. Currently we do not have server-side support for RFC 5011 so we will ignore RFC5011, too.

Configuration for signerd is generated by opendnssec/enforcer/enforcerd/enforcer.c:commGenSignConf() function.

Most importantly, it iterates over all keys returned by enforcer/ksm/ksm_request.c:KsmRequestIssueKeys():

/*+
 * KsmRequestIssueKeys - Issue Keys
 *
 * Description:
 *      Done as the last step in the "REQUEST KEYS" operation, this actually
 *      issues the keys that should be in the current zone file.  All keys in
 *      the "publish", "ready", "active" and "retire" states are included.
 *
 * Arguments:
 *      int keytype
 *          Type of keys required.
 *
 *      KSM_REQUEST_CALLBACK callback
 *          Callback function called for every key that will be issued.
 * ...
 */

The code actually selects all keys in states (KSM_STATE_PUBLISH, KSM_STATE_READY, KSM_STATE_ACTIVE, KSM_STATE_RETIRE, KSM_STATE_KEYPUBLISH).

Keys returned by KsmRequestIssueKeys() are further processed by callback function opendnssec/enforcer/enforcerd/enforcer.c:commKeyConfig() which transforms key state into signerd configuration:

if (key_data->keytype == KSM_TYPE_KSK) {
    if (!(key_data->rfc5011 && key_data->state == KSM_STATE_PUBLISH))
        fprintf(file, "\t\t\t\t<KSK />\n");
}
if (key_data->keytype == KSM_TYPE_ZSK && key_data->state == KSM_STATE_ACTIVE)
{
    fprintf(file, "\t\t\t\t<ZSK />\n");
}
if ((key_data->state > KSM_STATE_GENERATE && key_data->state < KSM_STATE_DEAD) || key_data->state == KSM_STATE_KEYPUBLISH)
{
    fprintf(file, "\t\t\t\t<Publish />\n");
}

OpenDNSSEC’s key state constants are defined in opendnssec/enforcer/ksm/include/ksm/ksm.h:

#define KSM_STATE_GENERATE          1
#define KSM_STATE_PUBLISH           2
#define KSM_STATE_READY             3
#define KSM_STATE_ACTIVE            4
#define KSM_STATE_RETIRE            5
#define KSM_STATE_DEAD              6
#define KSM_STATE_DSSUB             7
#define KSM_STATE_DSPUBLISH         8
#define KSM_STATE_DSREADY           9
#define KSM_STATE_KEYPUBLISH        10

Key state transformation

We need to make sure that current state in OpenDNSSEC matches key state in LDAP and in BIND.

Wrong approach (naive)

We cannot unconditionaly copy timestamps of planned state changes from dnsseckeys table to LDAP and to BIND private key files.

The reason is that planned key state transition may not happen for some reason. These reasons include not running OpenDNSSEC key daemon, or a bug in export/import scripts which will prevent upload of new keys and metadata to LDAP. In these situations BIND would actually do the transition as planned which may leave the DNS zone with only inactive keys, which would break chain of trust because parent zone would still contain DS record. Example of what would happen because of unconditional copying of timestamp:

  1. Add a zone and let OpenDNSSEC to generate keys.
  2. Wait until keys are in state “published” and next state is “inactive”.
  3. Shutdown enforcerd or break replication from DNSSEC key master.
  4. See that keys on DNS replicas will transition to state “inactive” even though it should not happen because OpenDNSSEC is not available (i.e. new keys may not be available).
  5. End result is that affected zone will not be signed anymore, even though it should stay signed with the old keys.

Proper solution

Solution is to closely map meaning of key states in enforcerd and signerd to meaning of key states expressed by BIND timestamps. Added restriction is that BIND should not see timestamps for planned key state transitions because these may not happen as planned.

Please note that this is not 1:1 mapping because OpenDNSSEC has more states than BIND. E.g. READY key state does not have its direct equivalent in BIND. Also, the logic is different for ZSK and KSK (see commKeyConfig()).

Assumptions

For current and all preceding key states, timestamps in dnsseckeys SQL table used by enforcerd always refer to the past. E.g. if key state is PUBLISH key then publish timestamp is <= current time.

State transformation

KsmRequestIssueKeys() limits set of key states which can be visible in DNS zones to (KSM_STATE_PUBLISH, KSM_STATE_READY, KSM_STATE_ACTIVE, KSM_STATE_RETIRE, KSM_STATE_KEYPUBLISH). Keys in all other states are not included in the zone, which means that current time has to be before Publish or past Dead timestamps in BIND key file, or that no timestamp except Created can be present in the BIND key file.

  • Created: BIND timestamp does nothing functionaly-wise so we may always copy it.
  • Publish: According to commKeyConfig() all keys in states (KSM_STATE_PUBLISH, KSM_STATE_READY, KSM_STATE_ACTIVE, KSM_STATE_RETIRE, KSM_STATE_KEYPUBLISH) should be always published. Given the assumption above, we may simply copy publish timestamp to LDAP for all keys in states.
  • Delete: This state directly maps to dead state.

Transformation of other states depends on key type conditions in commKeyConfig():

KSK
  • Activate: Considering that we do not support RFC 5011 yet, commKeyConfig() will always use all KSK keys in states (KSM_STATE_PUBLISH, KSM_STATE_READY, KSM_STATE_ACTIVE, KSM_STATE_RETIRE, KSM_STATE_KEYPUBLISH) for signing. Using our assumption, we can always put publish timestamp from SQL database into Activate field in LDAP and key file.
  • Inactive: State retired is effectively ignored for KSK so this field will be always empty.
ZSK
  • Activate: Equal to active state.
  • Inactive: Equal to retired state.