Writing an application that supports DNSSEC in RHEL and Fedora
DNS is a distributed database that is capable of storing different types of data, not only IP addresses, in which the domain owner can publish various domain specific data. Yet, plain DNS does not offer any type of security measures. This means that DNS data in the response can be spoofed by anybody at any time.
This is where DNSSEC comes in. DNSSEC stands for DNS SECurity Extensions and brings the data authentication and data integrity check into the DNS world. The whole solution is based on asymmetric cryptography.
Having the ability to check DNS data integrity and authenticity is great for new applications when you have to:
- Verify a remote server SSH fingerprint using SSHFP record in DNS (RFC 4255)
- Verify a TLS certificate offered by the remote server using DANE and TLSA record in DNS (RFC 6698)
- Get IPsec keys for a particular remote host automatically using IPSECKEY record in DNS (RFC 4025)
- Get X.509 or OpenPGP certificates using CERT record in DNS (RFC 4398)
- Verify that a specific Certification Authority is authorized to issue a certificate for a particular domain, using the CAA record in DNS (RFC 6844)
For the client software to be able to benefit from all of this, it needs to be sure that the data from DNS were in fact signed and also successfully verified using DNSSEC mechanisms. Currently there are two (in fact three) approaches on how to do this:
Implement the DNSSEC verification yourself
- Use an existing DNS resolver library with DNSSEC support.
- Have a trusted validating resolver running locally on the client device. Use it for all DNS queries and check the AD (Authenticated Data) flag in the DNS response.
I’m not going to discuss the first (1) option, since hopefully nobody wants to reinvent the wheel and introduce bugs that are already solved in existing implementations.
The second (2) option is the most portable one, since the application does not have to rely on any third party application or on the client system setup. However even though a library is used to do the name resolution and validation, some logic must always be duplicated within the application. This is mainly due to various challenges that the client application may need to address, especially if it’s targeting mobile client devices, which are often migrating between different networks and environments. Linking the required libraries and the portability of used libraries should be considered.
The third (3) option relies on a specific system configuration which may not be fulfilled on every system installation. The requirement for a local Validating Resolver is a must, since stub-resolvers often communicate with recursive-resolvers via an insecure channel. If you use an insecure channel the resultant DNS data and DNS header are not trustworthy. On the other hand, this setup offers a single point of configuration for the system administrator. As an added benefit, all the challenges present on mobile client devices can be solved in a single place, consistently, for all applications. Application “just” checks the AD flag in the DNS response header and acts upon it.
Let’s discuss the mentioned possibilities one (1) and two (2), together with challenges of DNSSEC-enabled application in more detail in the following sections.
Implementing DNSSEC enabled client application using a resolver library
To be able to do the DNSSEC validation, the client application needs to be able to obtain all the necessary keys and signatures from DNS and build the chain of trust from a preconfigured trust anchor (generally this is the public key used by the root zone “.”).
The natural way to go is to use some of the existing resolver libraries and prevent introducing implementation bugs of our own into the DNSSEC validation process. From the libraries that are included in Fedora and RHEL, I would like to mention ldns and libunbound, which provide a common API to make DNS queries and to do DNSSEC validation.
ldns is a low level DNS library written in C to ease the implementation of applications that are using DNS, possibly with DNSSEC. It also helps the application to conform to all of the different RFCs concerning DNS. It supports IPv4 and IPv6, signing DNS queries using TSIG (RFC 2845) and also the DNSSEC validation and signing. The library comes with a set of tools that are handy for DNS and DNSSEC debugging and also serve as an example on how to use the library.
libunbound provides higher level DNS resolver library API. It is also written in C and allows one to look up different types of data in DNS and perform DNSSEC validation of responses. The library covers a lot of the necessary tasks for the developer. The state between API calls is stored in a special context object. This allows the application to use multiple contexts with different settings.
Both libraries also come with Python bindings which enables rapid prototyping of DNSSEC-enabled applications.
Implementing DNSSEC aware client application using the AD flag
Both Fedora and RHEL-7 come with Dnssec-trigger and Unbound which offer client side DNSSEC validation solution. Unbound is a validating resolver implemented using libunbound and runs locally in an out-of-the-box setup. Dnssec-trigger is a daemon which communicates with NetworkManager and reconfigures unbound server dynamically on each network configuration change. On each network configuration change, NetworkManager’s dispatcher executes a script provided by Dnssec-trigger, which then fetches the current configuration from NetworkManager using Python bindings to libnm-glib.
When using Dnssec-trigger with Unbound, the /etc/resolv.conf points naturally to the Unbound resolver running locally on the address 127.0.0.1.
To check the AD flag in the DNS header, one can for example use the res_* libc API from resolv.h or any other stub-resolver library. Unfortunately, the libc stub-resolver currently doesn’t distinguish between an AD flag set by a trusted, locally-running resolver and an untrusted resolver provided by the network’s DHCP server. Luckily, there is an ongoing discussion on the libc mailinglist to modify libc to clear the AD flag if the DNS response didn’t come from the trusted resolver (e.g. local Unbound server). Thereby, assuring that the client application can trust the AD flag in the DNS response header.
Now we have at least some overview of possible approaches and libraries that are available. But what challenges can a client application face in the real world if it is DNSSEC-enabled?
Challenges of DNSSEC client applications
Mobile client devices are often migrating across various networks and environments. Some of the networks can be trusted, like corporate network, but some of them can not be trusted, like a free WiFi hotspot at an airport. Clients can also be connected to multiple networks at the same time. A network-provided DNS server may have an internal view of a specific DNS zone. These are a couple of the situations which a DNSSEC-enabled client application must handle if it is doing DNS resolution and DNSSEC validation by itself.
Using a solution with local validating resolver (e.g. Dnssec-trigger and Unbound) can solve all of these situations for the client application. All it needs to do is check the AD flag in the DNS response header.
When the client device connects to a network, the local DHCP server tells it which DNS resolvers to use. With plain DNS the device could use those without any problems. However, the client device would have no way to verify the authenticity of the DNS information.
But if we want to use DNSSEC, any resolver has to be tested before use. The reason is to make sure the resolver supports EDNS0 protocol, which is vital for DNSSEC, and it is able to provide all the necessary DNSSEC data for validation (signatures and keys). The testing can be done, for example, against top level domains, since the root zone is signed.
If the network provided DNS resolvers don’t support DNSSEC, the client application may need to use some public resolvers with DNSSEC support. But, the network can block all DNS requests on the standard DNS ports to the outside world. To work around this, the application can send the DNS requests to some DNS resolvers running on HTTP port or even on HTTPS port and use SSL. Of course this requires an existing external DNS infrastructure that listens on such ports. For example, Unbound is one of the servers that supports such setup, even with the SSL. There are existing public Fedora and NLnet Labs (Unbound upstream) Unbound server instances listening on HTTP port and also on HTTPS port with a known public key used for SSL. This infrastructure is, for example, used in case you use setup with Dnssec-trigger and local Unbound server on Fedora or RHEL-7.
In case the client device is connected to a local, possibly public network and also connected to a remote network via a VPN, the VPN server may provide another set of DNS resolvers. Such resolvers may have an internal to the VPN view of some DNS zones and the client application should use them in addition to the DNS resolvers from the local network. Without using them, the application would not be able to resolve names that are available only via the resolvers internal to the VPN network.
The catch is in knowing which DNS queries send to which DNS servers. One, and probably the most common solution, is to use the VPN-provided resolvers for all DNS queries. But this can be viewed as a private information leak if the VPN is configured to be used only for resources from within the VPN network. If the VPN server provides a list of search domains, the application can forward only queries for those domains to the VPN-provided resolvers. However, this requires the the application to trust the VPN server and the list of search domains provided by it. In theory, the VPN server could hijack DNS queries for specific domains by just listing them in the list of search domains. However, If the VPN-provided resolvers support DNSSEC, the application can at least make sure that it always got authenticated data for signed DNS zones. This would eliminate the risk of DNS spoofing attack for signed zones completely.
However, the situation is much less safe when the VPN-provided DNS resolvers don’t support DNSSEC. In order to resolve internal domain names using the VPN-provided resolvers, the application would need to disable DNSSEC validation when communicating with it. As a result, the VPN-provided resolvers could not only hijack the DNS queries, but also spoof the data in DNS.
In fact, the author of this post, and some others, are currently pursuing a patent around a resolution for using DNSSEC in a mixed environment with local DNS resolvers being NOT DNSSEC-enabled and public DNSSEC-enabled DNS resolvers. The aim is to use DNSSEC if the domain is signed, while still be able to resolve possibly internal domain names that only the local DNSSEC-not-enabled resolvers can resolve. Watch this blog for the next installment where we explain how this idea works.
A lot of public WiFi hotspots require a user to log in using a Captive portal, before connecting to the Internet. The login is done by redirecting the user to a specific login page. This is usually done by spoofing DNS responses, so that all domain names are resolved to the IP address of the Captive portal. With plain DNS this is not a big issue, but when using DNSSEC, such a situation is interpreted as a DNS spoofing attack and DNSSEC is a mechanism to prevent such attacks. Therefore the Captive Portal situation has to be detected and handled properly. To log in using a Captive Portal requires to disable DNSSEC validation for the necessary time. We have some better ideas like opening a sandboxed browser window to allow the user to log in, however no particularly good solutions to this presently exist, but we would love to see your ideas in the comments.
These were just a few common challenges when using DNSSEC on mobile client devices. There may be even more for specific corner cases.
DNSSEC opens new possibilities for client applications. For example, a SSH client can automatically verify the remote host fingerprint using DNS, a web browser can verify a TLS certificate automatically based on the data for the domain, a client application can establish a secure connection using IPsec if it is able to get the remote host IPsec keys from the DNS database and the remote host is also able to get the client’s IPsec keys. These are only some of the cool new features, developers can add to their applications leveraging DNSSEC. The bottom line is that DNSSEC enables getting trusted data associated with a particular domain name from DNS and verify the integrity and authenticity of that data. Presently, very few, if any, OS-provided, stub-resolvers support DNSSEC. This will hopefully change in the future.
In the meantime, this blog post outlined a few possibilities for developers to leverage libraries in RHEL and Fedora to give you DNSSEC support, right now, today. At the very least, this should help you to pursue using option two (2) above. We are actively working in Fedora to improve the default auto-configuration which will flow to RHEL and make option three (3) even more appealing on both platforms. We are encouraging you to try out the setup with Unbound server and Dnssec-trigger in Fedora and RHEL-7.
However, please, please, don’t choose option one (1), but do enable DNSSEC in your applications, thereby making a more secure and robust Internet for all of us.
Join the Red Hat Developer Program (it’s free) and get access to related cheat sheets, books, and product downloads.