Georg Lukas, 2015-10-16 17:55

.IM top-level domain Domain Name System Security Extensions Look-aside Validation DNS-based Authentication of Named Entities Extensible Messaging and Presence Protocol TLSA ("TLSA" does not stand for anything; it is just the name of the RRtype) resource record.

Okay, seriously: this post is about securing an XMPP server running on an .IM domain with DNSSEC, using yax.im as a real-life example. In the world of HTTP there is HPKP, and browsers come with a long list of pre-pinned site certificates for the who's'who of the modern web. For XMPP, DNSSEC is the only viable way to extend the broken Root CA trust model with a slightly-less-broken hierarchical trust model from DNS (there is also TACK, which is impossible to deploy because it modifies the TLS protocol, and also unmaintained).

Because the .IM TLD is not DNSSEC-signed yet, we will need to use DLV (DNSSEC Look-aside Validation), an additional DNSSEC trust root operated by the ISC (until the end of 2016). Furthermore, we will need to set up the correct entries for yax.im (the XMPP service domain), chat.yax.im (the conference domain) and xmpp.yaxim.org (the actual server running the service).

This post has been sitting in the drafts folder for a while, but now that DANE-SRV has been promoted to Proposed Standard, it was a good time to finalize the article.

Introduction

Our (real-life) scenario is as follows: the yax.im XMPP service is run on a server named xmpp.yaxim.org (for historical reasons, the yax.im host is a web server forwarding to yaxim.org, not the actual XMPP server). The service furthermore hosts the chat.yax.im conference service, which needs to be accessible from other XMPP servers as well.

In the following, we will create SRV DNS records to advertise the server name, obtain a TLS certificate, configure DNSSEC on both domains and create (signed) DANE records that define which certificate a client can expect when connecting.

Once this is deployed, state-level attackers will not be able to MitM users of the service simply by issuing rogue certificates, they would also have to compromise the DNSSEC chain of trust (in our case one of the following: ICANN/VeriSign, DLV, PIR or the registrar/NS hosting our domains, essentially limiting the number of states able to pull this off to one).

Creating SRV Records for XMPP

The service / server separation is made possible with the SRV record in DNS, which is a more generic variant of records like MX (e-mail server) or NS (domain name server) and defines which server is responsible for a given service on a given domain.

For XMPP, we create the following three SRV records to allow clients (_xmpp-client._tcp), servers (_xmpp-server._tcp) and conference participants (_xmpp-server._tcp on chat.yax.im) to connect to the right server:

_xmpp-client._tcp.yax.im       IN SRV 5 1 5222 xmpp.yaxim.org.
_xmpp-server._tcp.yax.im       IN SRV 5 1 5269 xmpp.yaxim.org.
_xmpp-server._tcp.chat.yax.im  IN SRV 5 1 5269 xmpp.yaxim.org.

The record syntax is: priority (5), weight (1), port (5222 for clients, 5269 for servers) and host (xmpp.yaxim.org). Priority and weight are used for load-balancing multiple servers, which we are not using.

Attention: some clients (or their respective DNS resolvers, often hidden in outdated, cheap, plastic junk routers provided by your "broadband" ISP) fail to resolve SRV records, and thus fall back to the A record. If you set up a new XMPP server, you will slightly improve your availability by ensuring that the A record (yax.im in our case) points to the XMPP server as well. However, DNSSEC will be even more of a challenge for them, so lets write them off for now.

Obtaining a TLS Certificate for XMPP

While DANE allows rolling out self-signed certificates, our goal is to stay compatible with clients and servers that do not deploy DNSSEC yet. Therefore, we need a certificate issued by a trustworthy member of the Certificate Extorion ring. Currently, StartSSL and WoSign offer free certificates, and Let's Encrypt is about to launch.

Both StartSSL and WoSign offer a convenient function to generate your keypair. DO NOT USE THAT! Create your own keypair! This "feature" will allow the CA to decrypt your traffic (unless all your clients deploy PFS, which they don't) and only makes sense if the CA is operated by an Intelligence Agency.

What You Ask For...

The certificate we are about to obtain must be somehow tied to our XMPP service. We have three different names (yax.im, chat.yax.im and xmpp.yaxim.org) and the obvious question is: which one should be entered into the certificate request.

Fortunately, this is easy to find out, as it is well-defined in the XMPP Core specification, section 13.7:

In a PKIX certificate to be presented by an XMPP server (i.e., a "server certificate"), the certificate SHOULD include one or more XMPP addresses (i.e., domainparts) associated with XMPP services hosted at the server. The rules and guidelines defined in [TLS‑CERTS] apply to XMPP server certificates, with the following XMPP-specific considerations:

  • Support for the DNS-ID identifier type [PKIX] is REQUIRED in XMPP client and server software implementations. Certification authorities that issue XMPP-specific certificates MUST support the DNS-ID identifier type. XMPP service providers SHOULD include the DNS-ID identifier type in certificate requests.

  • Support for the SRV-ID identifier type [PKIX‑SRV] is REQUIRED for XMPP client and server software implementations (for verification purposes XMPP client implementations need to support only the "_xmpp-client" service type, whereas XMPP server implementations need to support both the "_xmpp-client" and "_xmpp-server" service types). Certification authorities that issue XMPP-specific certificates SHOULD support the SRV-ID identifier type. XMPP service providers SHOULD include the SRV-ID identifier type in certificate requests.

  • [...]

Translated into English, our certificate SHOULD contain yax.im and chat.yax.im according to [TLS-CERTS], which is "Representation and Verification of Domain-Based Application Service Identity within Internet Public Key Infrastructure Using X.509 (PKIX) Certificates in the Context of Transport Layer Security (TLS)", or for short RFC 6125. There, section 2.1 defines that there is the CN-ID (Common Name, which used to be the only entry identifying a certificate), one or more DNS-IDs (baseline entries usable for any services) and one or more SRV-IDs (service-specific entries, e.g. for XMPP). DNS-IDs and SRV-IDs are stored in the certificate as subject alternative names (SAN).

Following the above XMPP Core quote, a CA must add support adding a DNS-ID and should add an SRV-ID field to the certificate. Clients and servers must support both field types. The SRV-ID is constructed according to RFC 4985, section 2, where it is called SRVName:

The SRVName, if present, MUST contain a service name and a domain name in the following form:

_Service.Name

For our XMPP scenario, we would need three SRV-IDs (_xmpp-client.yax.im for clients, _xmpp-server.yax.im for servers, and _xmpp-server.chat.yax.im for the conference service; all without the _tcp. part we had in the SRV record). In addition, the two DNS-IDs yax.im and chat.yax.im are required recommended by the specification, allowing the certificate to be (ab)used for HTTPS as well.

Update: The quoted specifications allow to create an XMPP-only certificate based on SRV-IDs, that contains no DNS-IDs (and has a non-hostname CN). Such a certificate could be used to delegate XMPP operations to a third party, or to limit the impact of leaked private keys. However, you will have a hard time convincing a public CA to issue one, and once you get it, it will be refused by most clients due to lack of SRV-ID implementation.

And then there is one more thing. RFC 7673 proposes also checking the certificate for the SRV destination (xmpp.yaxim.org in our case) if the SRV record was properly validated, there is no associated TLSA record, and the application user was born under the Virgo zodiac sign.

Summarizing the different possible entries in our certificate, we get the following picture:

Name(s) Field Type Meaning
yax.im or chat.yax.im Common Name (CN) Legacy name for really old clients and servers.
yax.im
chat.yax.im
DNS-IDs (SAN) Required entry telling us that the host serves anything on the two domain names.
_xmpp-client.yax.im
_xmpp-server.yax.im
SRV-IDs (SAN) Optional entry telling us that the host serves XMPP to clients and servers.
_xmpp-server.chat.yax.im SRV-ID (SAN) Optional entry telling us that the host serves XMPP to servers for chat.yax.im.
xmpp.yaxim.org DNS-ID or CN Optional entry if you can configure a DNSSEC-signed SRV record but not a TLSA record.

...and What You Actually Get

Most CAs have no way to define special field types. You provide a list of service/host names, the first one is set as the CN, and all of them are stored as DNS-ID SANs. However, StartSSL offers "XMPP Certificates", which look like they might do what we want above. Let's request one from them for yax.im and chat.yax.im and see what we got:

openssl x509 -noout -text -in yaxim.crt
[...]
Subject: description=mjp74P5w0cpIUITY, C=DE, CN=chat.yax.im/emailAddress=hostmaster@yax.im
X509v3 Subject Alternative Name:
    DNS:chat.yax.im, DNS:yax.im, othername:<unsupported>,
    othername:<unsupported>, othername:<unsupported>, othername:<unsupported>

So it's othername:<unsupported>, then? Thank you OpenSSL, for your openness! From RFC 4985 we know that "othername" is the basic type of the SRV-ID SAN, so it looks like we got something more or less correct. Using this script (highlighted source, thanks Zash), we can further analyze what we've got:

Extensions:
  X509v3 Subject Alternative Name:
    sRVName: chat.yax.im, yax.im
    xmppAddr: chat.yax.im, yax.im
    dNSName: chat.yax.im, yax.im

Alright, the two service names we submitted turned out under three different field types:

  • SRV-ID (it's mising the _xmpp-client. / _xmpp-server. part and is thus invalid)
  • xmppAddr (this was the correct entry type in the deprecated RFC 3920 XMPP specification, but is now only allowed in client certificates)
  • DNS-ID (wow, these ones happen to be correct!)

While this is not quite what we wanted, it is sufficient to allow a correctly implemented client to connect to our server, without raising certificate errors.

Configuring DNSSEC for Your Domain(s)

In the next step, the domain (in our case both yax.im and yaxim.org, but the following examples will only list yax.im) needs to be signed with DNSSEC. Because I'm a lazy guy, I'm using BIND 9.9, which does inline-signing (all I need to do is create some keys and enable the feature).

Key Creation with BIND 9.9

For each domain, a zone signing key (ZSK) is needed to sign the individual records. Furthermore, a key signing key (KSK) should be created to sign the ZSK. This allows you to rotate the ZSK as often as you wish.

# create key directory
mkdir /etc/bind/keys
cd /etc/bind/keys
# create key signing key
dnssec-keygen -f KSK -3 -a RSASHA256 -b 2048 yax.im
# create zone signing key
dnssec-keygen -3 -a RSASHA256 -b 2048 yax.im
# make all keys readable by BIND
chown -R bind.bind .

To enable it, you need to configure the key directory, inline signing and automatic re-signing:

zone "yax.im" {
    ...
    key-directory "/etc/bind/keys";
    inline-signing yes;
    auto-dnssec maintain;
};

After reloading the config, the keys need to be enabled in BIND:

# load keys and check if they are enabled
$ rndc loadkeys yax.im
$ rndc signing -list yax.im
Done signing with key 17389/RSASHA256
Done signing with key 24870/RSASHA256

The above steps need to be performed for yaxim.org as well.

NSEC3 Against Zone Walking

Finally, we also want to enable NSEC3 to prevent curious people from "walking the zone", i.e. retrieving a full list of all host names under our domains. To accomplish that, we need to specify some parameters for hashing names. These parameters will be published in an NSEC3PARAMS record, which resolvers can use to apply the same hashing mechanism as we do.

First, the hash function to be used. RFC 5155, section 4.1 tells us that...

"The acceptable values are the same as the corresponding field in the NSEC3 RR."

NSEC3 is also defined in RFC 5155, albeit in section 3.1.1. There, we learn that...

"The values for this field are defined in the NSEC3 hash algorithm registry defined in Section 11."

It's right there... at the end of the section:

Finally, this document creates a new IANA registry for NSEC3 hash algorithms. This registry is named "DNSSEC NSEC3 Hash Algorithms". The initial contents of this registry are:

0 is Reserved.

1 is SHA-1.

2-255 Available for assignment.

Let's pick 1 from this plethora of choices, then.

The second parameter is "Flags", which is also defined in Section 11, and must be 0 for now (other values have to be defined yet).

The third parameter is the number of iterations for the hash function. For a 2048 bit key, it MUST NOT exceed 500. Bind defaults to 10, Strotman references 330 from RFC 4641bis, but it seems that number was removed since then. We take this number anyway.

The last parameter is a salt for the hash function (a random hexadecimal string, we use 8 bytes). You should not copy the value from another domain to prevent rainbow attacks, but there is no need to make this very secret.

$ rndc signing -nsec3param 1 0 330 $(head -c 8 /dev/random|hexdump -e '"%02x"') yaxim.org
$ rndc signing -nsec3param 1 0 330 $(head -c 8 /dev/random|hexdump -e '"%02x"') yax.im

Whenever you update the NSEC3PARAM value, your zone will be re-signed and re-published. That means you can change the iteration count and salt value later on, if the need should arise.

Configuring the DS (Delegation Signer) Record for yaxim.org

If your domain is on an already-signed TLD (like yaxim.org on .org), you need to establish a trust link from the .org zone to your domain's signature keys (the KSK, to be precise). For this purpose, the delegation signer (DS) record type has been created.

A DS record is a signed record in the parent domain (.org) that identifies a valid key for a given sub-domain (yaxim.org). Multiple DS records can coexist to allow key rollover. If you are running an important service, you should create a second KSK, store it in a safe place, and add its DS in addition to the currently used one. Should your primary name server go up in flames, you can recover without waiting for the domain registrar to update your records.

Exporting the DS Record

To obtain the DS record, BIND comes with the dnssec-dsfromkey tool. Just pipe all your keys into it, and it will output DS records for the KSKs. We do not want SHA-1 records any more, so we pass -2 as well to get the SHA-256 record:

$ dig @127.0.0.1 DNSKEY yaxim.org | dnssec-dsfromkey -f - -2 yaxim.org
yaxim.org. IN DS 42199 8 2 35E4E171FC21C6637A39EBAF0B2E6C0A3FE92E3D2C983281649D9F4AE3A42533

This line is what you need to submit to your domain registrar (using their web interface or by means of a support ticket). The information contained is:

  • key tag: 42199 (this is just a numeric ID for the key, useful for key rollovers)
  • signature algorithm: 8 (RSA / SHA-256)
  • DS digest type: 2 (SHA-256)
  • hash value: 35E4E171...E3A42533

However, some registrars insist on creating the DS record themselves, and require you to send in your DNSKEY. We only need to give them the KSK (type 257), so we filter the output accordingly:

$ dig @127.0.0.1 DNSKEY yaxim.org | grep 257
yaxim.org.              86400   IN      DNSKEY  257 3 8
    AwEAAcDCzsLhZT849AaG6gbFzFidUyudYyq6NHHbScMl+PPfudz5pCBt
    G2AnDoqaW88TiI9c92x5f+u9Yx0fCiHYveN8XE2ed/IQB3nBW9VHiGQC
    CliM0yDxCPyuffSN6uJNVHPEtpbI4Kk+DTcweTI/+mtTD+sC+w/CST/V
    NFc5hV805bJiZy26iJtchuA9Bx9GzB2gkrdWFKxbjwKLF+er2Yr5wHhS
    Ttmvntyokio+cVgD1UaNKcewnaLS1jDouJ9Gy2OJFAHJoKvOl6zaIJuX
    mthCvmohlsR46Sp371oS79zrXF3LWc2zN67T0fc65uaMPkeIsoYhbsfS
    /aijJhguS/s=

Validation of the Trust Chain

As soon as the record is updated, you can check the trustworthiness of your domain. Unfortunately, all of the available command-line tools suck. One of the least-sucking ones is drill from ldns. It still needs a root.key file that contains the officially trusted DNSSEC key for the . (root) domain. In Debian, the dns-root-data package places it under /usr/share/dns/root.key. Let's drill our domain name with DNSSEC (-D), tracing from the root zone (-T), quietly (-Q):

$ drill -DTQ -k /usr/share/dns/root.key yaxim.org
;; Number of trusted keys: 1
;; Domain: .
[T] . 172800 IN DNSKEY 256 3 8 ;{id = 48613 (zsk), size = 1024b}
. 172800 IN DNSKEY 257 3 8 ;{id = 19036 (ksk), size = 2048b}
[T] org. 86400 IN DS 21366 7 1 e6c1716cfb6bdc84e84ce1ab5510dac69173b5b2 
org. 86400 IN DS 21366 7 2 96eeb2ffd9b00cd4694e78278b5efdab0a80446567b69f634da078f0d90f01ba 
;; Domain: org.
[T] org. 900 IN DNSKEY 257 3 7 ;{id = 9795 (ksk), size = 2048b}
org. 900 IN DNSKEY 256 3 7 ;{id = 56198 (zsk), size = 1024b}
org. 900 IN DNSKEY 256 3 7 ;{id = 34023 (zsk), size = 1024b}
org. 900 IN DNSKEY 257 3 7 ;{id = 21366 (ksk), size = 2048b}
[T] yaxim.org. 86400 IN DS 42199 8 2 35e4e171fc21c6637a39ebaf0b2e6c0a3fe92e3d2c983281649d9f4ae3a42533 
;; Domain: yaxim.org.
[T] yaxim.org. 86400 IN DNSKEY 257 3 8 ;{id = 42199 (ksk), size = 2048b}
yaxim.org. 86400 IN DNSKEY 256 3 8 ;{id = 6384 (zsk), size = 2048b}
[T] yaxim.org.  3600    IN  A   83.223.75.29
;;[S] self sig OK; [B] bogus; [T] trusted

The above output traces from the initially trusted . key to org, then to yaxim.org and determines that yaxim.org is properly DNSSEC-signed and therefore trusted ([T]). This is already a big step, but the tool lacks some color, and it does not allow to explicitly query the domain's name servers (unless they are open resolvers), so you can't test your config prior to going live.

To get a better view of our DNSSEC situation, we can query some online services:

Ironically, neither DNSViz nor Verisign support encrypted connections via HTTPS, and Lutz' livetest is using an untrusted root.

Enabling DNSSEC Look-aside Validation for yax.im

Unfortunately, we can not do the same with our short and shiny yax.im domain. If we try to drill it, we get the following:

$ drill -DTQ -k /usr/share/dns/root.key yax.im
;; Number of trusted keys: 1
;; Domain: .
[T] . 172800 IN DNSKEY 256 3 8 ;{id = 48613 (zsk), size = 1024b}
. 172800 IN DNSKEY 257 3 8 ;{id = 19036 (ksk), size = 2048b}
[T] Existence denied: im. DS
;; Domain: im.
;; No DNSKEY record found for im.
;; No DS for yax.im.;; Domain: yax.im.
[S] yax.im. 86400 IN DNSKEY 257 3 8 ;{id = 17389 (ksk), size = 2048b}
yax.im. 86400 IN DNSKEY 256 3 8 ;{id = 24870 (zsk), size = 2048b}
[S] yax.im. 3600    IN  A   83.223.75.29
;;[S] self sig OK; [B] bogus; [T] trusted

There are two pieces of relevant information here:

  • [T] Existence denied: im. DS - the top-level zone assures that .IM is not DNSSEC-signed (it has no DS record).
  • [S] yax.im. 3600 IN A 83.223.75.29 - yax.im is self-signed, providing no way to check its authenticity.

The .IM top-level domain for Isle of Man is operated by Domicilium. A friendly support request reveals the following:

Unfortunately there is no ETA for DNSSEC support at this time.

That means there is no way to create a chain of trust from the root zone to yax.im.

Fortunately, the desingers of DNSSEC anticipated this problem. To accelerate adoption of DNSSEC by second-level domains, the concept of look-aside validation was introduced in 2006. It allows to use an alternative trust root if the hierarchical chaining is not possible. The ISC is even operating such an alternative trust root. All we need to do is to register our domain with them, and add them to our resolvers (because they aren't added by default).

After registering with DLV, we are asked to add our domain with its respective KSK domain key entry. To prove domain and key ownership, we must further create a signed TXT record under dlv.yax.im with a specific value:

dlv.yax.im. IN TXT "DLV:1:fcvnnskwirut"

Afterwards, we request DLV to check our domain. It queries all of the domains' DNS servers for the relevant information and compares the results. Unfortunately, our domain fails the check:

FAILURE 69.36.225.255 has extra: yax.im. 86400 IN DNSKEY 256 3 RSASHA256 ( AwEAAepYQ66j42jjNHN50gUldFSZEfShF...
FAILURE 69.36.225.255 has extra: yax.im. 86400 IN DNSKEY 257 3 RSASHA256 ( AwEAAcB7Fx3T/byAWrKVzmivuH1bpP5Jx...
FAILURE 69.36.225.255 missing: YAX.IM. 86400 IN DNSKEY 256 3 RSASHA256 ( AwEAAepYQ66j42jjNHN50gUldFSZEfShF...
FAILURE 69.36.225.255 missing: YAX.IM. 86400 IN DNSKEY 257 3 RSASHA256 ( AwEAAcB7Fx3T/byAWrKVzmivuH1bpP5Jx...
FAILURE This means your DNS servers are out of sync. Either wait until the DNSKEY data is the same, or fix your server's contents.

This looks like a combination of two different issues:

  1. A part of our name servers is returning YAX.IM when asked for yax.im.
  2. The DLV script is case-sensitive when it comes to domains.

Problem #1 is officially not a problem. DNS is case-insensitive, and therefore all clients that fail to accept YAX.IM answers to yax.im requests are broken. In practice, this hits not only the DLV resolver (problem #2), but also the resolver code in Erlang, which is used in the widely-deployed ejabberd XMPP server.

While we can't fix all the broken servers out there, #2 has been reported and fixed, and hopefully the fix has been rolled out to production already. Still, issue #1 needs to be solved.

It turns out that it is caused by case insensitive response compression. You can't make this stuff up! Fortunately, BIND 9.9.6 added the no-case-compress ACL, so "all you need to do" is to upgrade BIND and enable that shiny new feature.

After checking and re-checking the dlv.yax.im TXT record with DLV, there is finally progress:

SUCCESS DNSKEY signatures validated.
...
SUCCESS COOKIE: Good signature on TXT response from <NS IP>
SUCCESS <NS IP> has authentication cookie DLV:1:fcvnnskwirut
...
FINAL_SUCCESS Success.

After your domain got validated, it will receive its look-aside validation records under dlv.isc.org:

$ dig +noall +answer DLV yax.im.dlv.isc.org
yax.im.dlv.isc.org. 3451    IN  DLV 17389 8 2 C41AFEB57D71C5DB157BBA5CB7212807AB2CEE562356E9F4EF4EACC2 C4E69578
yax.im.dlv.isc.org. 3451    IN  DLV 17389 8 1 8BA3751D202EF8EE9CE2005FAF159031C5CAB68A

This looks like a real success. Except that nobody is using DLV in their resolvers by default, and DLV will stop operations in 2017.

Until then, you can enable look-aside validation in your BIND and Unbound resolvers.

Lutz' livetest service supports checking DLV-backed domains as well, so let's verify our configuration:

Creating TLSA Records for HTTP and SRV

Now that we have created keys, signed our zones and established trust into them from the root (more or less), we can put more sensitive information into DNS, and our users can verify that it was actually added by us (or one of at most two or three governments: the US, the TLD holder, and where your nameservers are hosted).

This allows us to add a second, independent, trust root to the TLS certificates we use for our web server (yaxim.org) as well as for our XMPP server, by means of TLSA records.

These record types are defined in RFC 6698 and consist of the following pieces of information:

  • domain name (i.e. www.yaxim.org)
  • certificate usage (is it a CA or a server certificate, is it signed by a "trusted" Root CA?)
  • selector + matching type + certificate association data (the actual certificate reference, encoded in one of multiple possible forms)

Domain Name

The domain name is the hostname in the case of HTTPS, but it's slightly more complicated for the XMPP SRV record, because there we have the service domain (yax.im), the conference domain (chat.yax.im) and the actual server domain name (xmpp.yaxim.org).

The behavior for SRV TLSA handling is defined in RFC 7673, published as Proposed Standard in October 2015. First, the client must validate that the SRV response for the service domain is properly DNSSEC-signed. Only then the client can trust that the server named in the SRV record is actually responsible for the service.

In the next step, the client must ensure that the address response (A for IPv4 and AAAA for IPv6) is DNSSEC-signed as well, or fall back to the next SRV record.

If both the SRV and the A/AAAA records are properly signed, the client must do a TLSA lookup for the SRV target (which is _5222._tcp.xmpp.yaxim.org for our client users, or _5269._tcp.xmpp.yaxim.org for other XMPP servers connecting to us).

Certificate Usage

The certificate usage field can take one of four possible values. Translated into English, the possibilities are:

  1. "trusted" CA - the provided cert is a CA cert that is trusted by the client, and the server certificate must be signed by this CA. We could use this to indicate that our server only will use StartSSL-issued certificates.
  2. "trusted" server certificate - the provided cert corresponds to the certificate returned over TLS and must be signed by a trusted Root CA. We will use this to deliver our server certificate.
  3. "untrusted" CA - the provided CA certificate must be the one used to sign the server's certificate. We could roll out a private CA and use this type, but it would cause issues with non-DNSSEC clients.
  4. "untrusted" server certificate - the provided certificate must be the same as returned by the server, and no Root CA trust checks shall be performed.

The Actual Certificate Association

Now that we know the server name for which the certificate is valid and the type of certificate and trust checks to perform, we need to store the actual certificate reference. Three fields are used to encode the certificate reference.

The selector defines whether the full certificate (0) or only the SubjectPublicKeyInfo field (1) is referenced. The latter allows to get the server key re-signed by a different CA without changing the TLSA records. The former could be theoretically used to put the full certificate into DNS (a rather bad idea for TLS, but might be interesting for S/MIME certs).

The matching type field defines how the "selected" data (certificate or SubjectPublicKeyInfo) is stored:

  1. exact match of the whole "selected" data
  2. SHA-256 hash of the "selected" data
  3. SHA-512 hash of the "selected" data

Finally, the certificate association data is the certificate/SubjectPublicKeyInfo data or hash, as described by the previous fields.

Putting it all Together

A good configuration for our service is a record based on a CA-issued server certificate (certificate usage 1), with the full certificate (selector 0) hashed via SHA-256 (matching type 1). We can obtain the required association data using OpenSSL command line tools:

openssl x509 -in yaxim.org-2014.crt -outform DER | openssl sha256
(stdin)= bbcc3ca09abfc28beb4288c41f4703a74a8f375a6621b55712600335257b09a9

Taken together, this results in the following entries for HTTPS on yaxim.org and www.yaxim.org:

_443._tcp.yaxim.org     IN TLSA 1 0 1 bbcc3ca09abfc28beb4288c41f4703a74a8f375a6621b55712600335257b09a9
_443._tcp.www.yaxim.org IN TLSA 1 0 1 bbcc3ca09abfc28beb4288c41f4703a74a8f375a6621b55712600335257b09a9

This is also the SHA-256 fingerprint you can see in your web browser.

For the XMPP part, we need to add TLSA records for the SRV targets (_5222._tcp.xmpp.yaxim.org for clients and _5269._tcp.xmpp.yaxim.org for servers). There should be no need to make TLSA records for the service domain (_5222._tcp.yax.im), because a modern client will always try to resolve SRV records, and no DNSSEC validation will be possible if that fails.

Here, we take the SHA-256 sum of the yax.im certificate we obtained from StartSSL, and create two records with the same type and format as above:

_5222._tcp.xmpp.yaxim.org IN TLSA 1 0 1 cef7f6418b7d6c8e71a2413f302f92fc97e57ec18b36f97a4493964564c84836
_5269._tcp.xmpp.yaxim.org IN TLSA 1 0 1 cef7f6418b7d6c8e71a2413f302f92fc97e57ec18b36f97a4493964564c84836

These fields will be used by DNSSEC-enabled clients to verify the TLS certificate presented by our XMPP service.

Replacing the Server Certificate

Now that the TLSA records are in place, it is not as easy to replace your server certificate as it was before, because the old one is now anchored in DNS.

You need to perform the following steps in order to ensure that all clients will be able to connect at any time:

  1. Obtain the new certificate
  2. Create a second set of TLSA records, for the new certificate (keep the old one in place)
  3. Wait for the configured DNS time-to-live to ensure that all users have received both sets of TLSA records
  4. Replace the old certificate on the server with the new one
  5. Remove the old TLSA records

If you fail to add the TLSA records and wait the DNS TTL, some clients will have cached a copy of only the old TLSA records, so they will reject your new server certificate.

Conclusion

DANE for XMPP is a chicken-and-egg problem. As long as there are no servers, it will not be implemented in the clients, and vice versa. However, the (currently unavailable) xmpp.net XMPP security analyzer is checking the DANE validation status, and GSoC 2015 brought us DNSSEC support in minidns, which soon will be leveraged in Smack-based XMPP clients.

With this (rather long) post covering all the steps of a successful DNSSEC implementation, including the special challenges of .IM domains, I hope to pave the way for more XMPP server operators to follow.

Enabling DNSSEC and DANE provides an improvement over the rather broken Root CA trust model, however it is not without controversy. tptacek makes strong arguments against DNSSEC, because it is using outdated crypto and because it doesn't completely solve the government-level MitM problem. Unfortunately, his proposal to "do nothing" will not improve the situation, and the only positive contribution ("use TACK!") has expired in 2013.

Finally, one last technical issue not covered by this post is periodic key rollover; this will be covered by a separate post eventually.

Comments on HN