Tuesday, March 13, 2012

Going DNSSEC, Unbound and PowerDNS

stalkr.net is now signed with DNSSEC! In this post I will quickly describe what is DNSSEC and why I chose to deploy it, then my choice of Unbound as a resolver and PowerDNS as a server and finally give a few resources about this topic.

Very quickly, what is DNSSEC?

A hierarchical system to authenticate (+integrity) DNS to avoid forgery using public key cryptography.
You create a set of private/public keys, use private to sign your zone and publish public to your top-level domain via your registrar (who has to support it, see ICANN's list) or publish it to ISC DLV.
When asked for a domain, DNSSEC-enabled resolvers check the hierarchy of signatures. If a domain said it was using DNSSEC but signature is bad, no result.

Why DNSSEC?

I acknowledge it is far from perfect:
However, it is increasingly used on the Internet (unlike DNSCurve), provides some security and I wanted to have it to play with it.

DNS resolver: Unbound

I was using ISC BIND. It supports DNSSEC with:
dnssec-enable yes;
dnssec-validation auto;
// ISC DLV https://dlv.isc.org/about/using
dnssec-lookaside . trust-anchor dlv.isc.org.;
But problem if you also want to resolve alternative TLDs such as .42 or .ovh, they will not resolve because insecure and BIND does not have an option to mark domains as insecure.
Solution: change software. I wanted to switch to Unbound for some time and the fact that it has the option (domain-insecure) to the previous problem made me switch.

Related fragments of my configuration:
server:
  # The following line will configure unbound to perform cryptographic
  # DNSSEC validation using the root trust anchor.
  auto-trust-anchor-file: "root.key"

  # File with DLV trusted keys. Same format as trust-anchor-file.
  # There can be only one DLV configured, it is trusted from root down.
  # http://ftp.isc.org/www/dlv/dlv.isc.org.key
  dlv-anchor-file: "dlv.isc.org.key"

  domain-insecure: 42
  domain-insecure: ovh

# http://wiki.42registry.org/page/Resolve
stub-zone:
  name: "42"
  stub-addr: 81.93.248.68
  stub-addr: 91.194.60.196
  stub-addr: 193.17.192.53

stub-zone:
  name: "ovh"
  stub-addr: 213.251.128.133
  stub-addr: 213.251.188.133
Rest of the configuration is simple.

DNS server: PowerDNS

DNSSEC is complex to set up and maintain with BIND. There are plenty docs, tools and scripts on the subject, but I wanted something simple.

So I turned to PowerDNS by Bert Hubert: it has DNSSEC support since version 3.0 (July 2011) and it's easy (quote: "minimal administrative overhead"). Indeed, PowerDNS handles all the key rotation and resigning maintenance. I acknowledge it is a security trade-off but good enough for me.

Since I like having my zones in BIND text format (easy to edit), I did not switch to MySQL/PostgreSQL/SQLite backends but used the convenient BIND backend.
Fragment from my pdns.conf:
master=yes
launch=bind
bind-config=named.conf
bind-dnssec-db=bind-dnssec.db
This bind-dnssec-db is very new. For older 3.0 versions and according to the doc, you need to use an extra sqlite3 backend and insert a new record for your domain (but that's all, PowerDNS takes care of the rest).

Then to enable DNSSEC on your domain:
$ pdnssec secure-zone example.com
$ pdnssec set-nsec3 example.com
$ pdnssec rectify-zone example.com # not needed if BIND-only mode
$ invoke-rc.d pdns restart
What if you update the zone file? Rectify + reload:
$ pdnssec rectify-zone example.com # not needed if BIND-only mode
$ invoke-rc.d pdns reload

Next, extract public info from your zone: DNSKEY (public key) and DS (hashes of public key) records.
$ pdnssec show-zone example.com
and publish it to your registrar (who will send it to your top-level domain) and/or to ISC DNSSEC Look-aside Validation (DLV) Registry.

Finally, check your setup with DNSSEC Analyzer and DNSViz.

Secondary DNS

I was using two free secondary DNS services: BuddyNS and PUCK. Unfortunately BuddyNS does not support DNSSEC, so I switched to PUCK only. Just note that it can be a little slow to update (1+ hour).

Cool stuff in DNS which can benefit from DNSSEC

SSHFP (RFC 4255) records, fingerprint of your SSH server:
$ dig +short SSHFP stalkr.net
1 1 D353AA5D599D37647682566ECB8553AE851998EF
2 1 76B50825241C1A85BCFD6954C3DCE2E65D4B71A0
Then use VerifyHostKeyDNS yes.

PGP keys in DNS. First, PKA (no RFC):
$ dig +short TXT stalkr._pka.stalkr.net
"v=pka1\;fpr=7D6740F72080593D0F71E9863D1D15C9AFCE54F8\;
 uri=http://stalkr.net/pgp.asc"
Second, CERT (RFC 2538, 4398):
$ dig +tcp +short CERT stalkr.stalkr.net
PGP 0 0 mQGiBEnIIn0RBADwVyT0DtawUJNxHdaKM [...] full cert

DANE (RFC draft), fingerprint of web server SSL/TLS certificate. Using dane tool from sshfp version 1.2:
$ dane --rfc stalkr.net
_443._tcp.stalkr.net. IN TLSA 1 1 9454E68DBD3A9C0F460
  9D033E291D53563A27CAB2C5AF4B482D66024BB841670
The fingerprint is indeed the one from my webserver:
$ echo | openssl s_client -connect stalkr.net:443 2>/dev/null | openssl x509 | \
  python -c 'import sys, hashlib;
    cert="".join(sys.stdin.read().strip().split("\n")[1:-1]);
    print hashlib.sha256(cert.decode("base64")).hexdigest()'
9454e68dbd3a9c0f4609d033e291d53563a27cab2c5af4b482d66024bb841670
Warning though, it's still an RFC draft and is changing, but when it settles it should be good!

DNS(SEC) resources

8 comments:

  1. Nice writeup, thanks!
    Bert - PowerDNS

    ReplyDelete
  2. Good write up. However, your TLSA record isn't correct:
    ----
    $ ./swede verify stalkr.net
    Received the following record for name _443._tcp.stalkr.net.:
    Usage: 1 (End-Entity Constraint + chain to CA)
    Selector: 1 (SubjectPublicKeyInfo)
    Matching Type: 247 (INVALID)
    Certificate for Association: 8e7813af03043dc0f42d05e3ad3d0f4df7136f750f9df9eb7036ec2001d82e40178078f360faeb4db8041f38d7aef4
    Error: The TLSA record is invalid.
    Matching Type: invalid (247 is not one of 0, 1 or 2)
    ----

    It appears to be based on an older draft (the dane tool is not up-to-date):

    ----
    $ dig +short TYPE65468 _443._tcp.stalkr.net
    \# 50 0101F78E7813AF03043DC0F42D05E3AD3D0F4DF7136F750F9DF9EB70 36EC2001D82E40178078F360FAEB4DB8041F38D7AEF4
    ----

    Create a valid one using swede (I'm biased, I made swede): https://github.com/pieterlexis/swede

    ReplyDelete
  3. Good catch but yes, I know ;) I'm using not-up-to-date-with-the-rfc-draft-18 versions of sslh and PowerDNS, hence the comment about the fact it's a changing RFC draft. I will bother to fix this when it's stabilized as RFC, for now it was just to play with it.

    Thanks anyway, especially for the hint to your tool! Very good, I updated the post to link it.

    ReplyDelete
  4. I use PowerDNS with MySQL backend and PowerAdmin for Zones.
    They work faultlessly .... Easy to use ..

    ReplyDelete
  5. @Josh : How did you create both keys and update them ?

    ReplyDelete
  6. Something went wrong...
    Warning! No TLSA records for _443._tcp.stalkr.net. were found.
    PKIX validation without DANE will be performed.
    91.121.51.205 did not dane-validate, because: Could not PKIX validate

    ReplyDelete
  7. As the output says, the error is because I removed TLSA while it was still a draft. But thanks for your comment, now it's an RFC so I put it back.

    I used swede to create the TLSA record, with patched m2crypto to use SNI because my web server uses it:
    $ swede create -s 1 -o rfc stalkr.net
    No certificate specified on the commandline, attempting to retrieve it from the server stalkr.net.
    Attempting to get certificate from 91.121.51.205
    Got a certificate with Subject: /CN=stalkr.net
    _443._tcp.stalkr.net. IN TLSA 1 1 1 a0e5e59adc9932a4d9977fe37c50da32d12119fe1478ffca89620bfa3206b750

    Verify works:
    $ swede verify stalkr.net
    Received the following record for name _443._tcp.stalkr.net.:
    Usage: 1 (End-Entity Constraint + chain to CA)
    Selector: 1 (SubjectPublicKeyInfo)
    Matching Type: 1 (SHA-256)
    Certificate for Association: a0e5e59adc9932a4d9977fe37c50da32d12119fe1478ffca89620bfa3206b750
    This record is valid (well-formed).
    Attempting to verify the record with the TLS service...
    Unable to resolve stalkr.net.: Unsuccesful lookup or no data returned for rrtype 28.
    Got the following IP: 91.121.51.205
    SUCCESS (Usage 1): Certificate offered by the server matches the one mentioned in the TLSA record and chains to a valid CA certificate
    The matched certificate has Subject: /CN=stalkr.net

    I presume you were using http://check.sidnlabs.nl/dane/?
    It fails to validate: "91.121.51.205 did not dane-validate, because: The TLSA record(s) did not match with the server certificate (chain)", but I think it's because they don't include CAcert.
    Testing locally http://www.nlnetlabs.nl/projects/ldns/ works:
    $ ./ldns-dane verify -f /etc/ssl/certs/ca-certificates.crt stalkr.net 443
    91.121.51.205 dane-validated successfully

    ReplyDelete
  8. PUCK gives only one server
    PUCK do support Notify Slaves, so propagation can be fast.

    I suggest you also take a look at Roller Network (http://rollernet.us) because it support all three that's important for you : DNSSEC (for security), Notify Slaves (for quick propagation of updated record) and gives two servers (despite both being in the same town, Reno, Nevada).

    ReplyDelete