Generic support for unknown DNS RR types (RFC 3597)

Overview

RFC 3597 describes mechanism necessary for supporting unknown/new DNS RR types in DNS servers. BIND 9 supports this so we only need to expose this functionality too.

Use Cases

This is a future-proof workaround for cases when someone needs a new type which is not standardized yet or is not supported in installed version of BIND.

Keep in mind that support for new RR types might take a long time to get into conservative distros like CentOS etc. Also, this proposal enables users to mix older and newer versions of BIND in the same LDAP cluster and still use all possible record types.

Design

New LDAP schema

  • 1 new attribute:

    ( <OID> NAME 'UnknownRecord' DESC 'unknown DNS record, RFC 3597' EQUALITY
    caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
    

The attribute should be added to existing idnsRecord object class as MAY.

This new attribute will contain data encoded according to RFC 3597 section 5:

The RDATA section of an RR of unknown type is represented as a
   sequence of white space separated words as follows:

      The special token \# (a backslash immediately followed by a hash
      sign), which identifies the RDATA as having the generic encoding
      defined herein rather than a traditional type-specific encoding.

      An unsigned decimal integer specifying the RDATA length in octets.

      Zero or more words of hexadecimal data encoding the actual RDATA
      field, each containing an even number of hexadecimal digits.

   If the RDATA is of zero length, the text representation contains only
   the \# token and the single zero representing the length.

Examples from RFC:
      a.example.   CLASS32     TYPE731         \# 6 abcd (
                                               ef 01 23 45 )
      b.example.   HS          TYPE62347       \# 0
      e.example.   IN          A               \# 4 0A000001
      e.example.   CLASS1      TYPE1           10.0.0.2

Attribute usage

Every DNS RR type has an IANA-assigned number which is used on wire.

RR types which are unknown to the server cannot be named by their mnemonic/type name because server would not be able to do name->number conversion and to generate DNS wire format.

As a result, we have to encode the RR type number somehow. RR type is represented as LDAP sub-type of UnknownRecord named using standard TYPE12345 convention defined in RFC 3597 section 5.

E.g. a record with type 65280 and hex value 0A000001 will be represented as:

UnknownRecord;TYPE65280: \# 4 0A000001

String stored in attribute value includes whole right-hand side including \# and length as decimal number, e.g.

\# 4 0A000001

Usage of LDAP sub-types allows usage of standard LDAP filtering and access control mechanisms on RR type-level.

Caveat

Unlimited write access to UnknownRecord attribute allows user to create arbitrary RR with arbitrary data. It is highly desirable to use LDAP access controls to allow access only to subset of sub-types and thus to allow to create only subset of all possible RR types. E.g. it might be undesirable to allow lower-level users to create TLSA records for arbitrary hosts etc.

Implementation

bind-dyndb-ldap converts RR type number to string like AAAA and tries to store the data into the <RRType>Record attribute first. If this operation fails with LDAP object class violation, then bind-dyndb-ldap converts the data into generic format and stores them into UnknownRecord;TYPE12345 attribute.

It is an implementation detail if driver understands the schema or of it is dumb and just tries to write both variants.

Upgrade

Records stored in UnknownRecord attribute will not be available on older servers which do not understand this record type.

Generic problem with multiple versions of BIND backed with shared LDAP database can be mitigated using UnknownRecord attribute:

Imagine you have a cluster consisting of BIND 9.8 and 9.9 installations running on the same LDAP database.

E.g. BIND 9.8 does not know OPENPGPKEY record and BIND 9.9 knows it. Naturally, BIND 9.8 cannot understand the textual representation of OPENPGPKEY records so we have to resort to intersection of sets of record types supported by all BIND versions in the cluster.

If BIND 9.8 receives update request to add OPENPGPKEY record it transforms RR type to TYPE61 and uses generic encoding for data written to LDAP because it is the only format the old BIND understands.

If BIND 9.9 receives update request to add OPENPGPKEY record it has two options:

  • Use the type-specific mnemonic and text format
  • Use the generic formats.

At this point, we need to ensure that BIND 9.9 does the right thing and uses the generic format otherwise the older BIND 9.8 in the same cluster would not understand the data.

This can be done by not adding OPENPGPKEYRecord attribute to the schema until all BIND 9.8 servers in the cluster are upgraded to understand the new record type.

How to Test

Generally, try to add a new record which is not known to BIND. E.g.

$ nsupdate -g
update add newrec.zone.test. 10 IN TYPE65280 \# 4 0A000001
send

$ dig newrec.zone.test. TYPE65280

dig command has to display the new record and it has to survive BIND restart.

Test Plan

TODO Test scenarios that will be transformed to test cases for FreeIPA Continuous Integration during implementation or review phase. This can be also link to source in git with the test, if appropriate.

Author

pspacek@…