Secure DNS Enhanced Bootstrap
This Document is Public
Authors: bemasc@chromium.org
April 2021
Currently, Chrome’s Secure DNS functionality only works if the network’s DNS resolver is correctly functioning and able to resolve the domain. This design outlines steps to reduce Secure DNS’s reliance on the network’s resolver. This should improve the reliability of Secure DNS, especially if the network’s resolver is experiencing an outage.
This change has no impact on resolver selection. Captive portal detection and users of ISP DNS filtering are not expected to be affected.
Mac, Windows, Linux, Chrome OS, Android
net-dev@chromium.org
Network stack
Chrome’s Secure DNS functionality is based on DNS over HTTPS (DoH), in which a DNS server is identified by a URI Template, typically including a domain name as the hostname. This creates a circular dependency: we cannot use the DNS server until we know its IP addresses, and we cannot look up the IP addresses until we can reach the DNS server.
Currently, Chrome solves this problem by resolving the DNS server’s domain name using the insecure network resolver, as part of the standard HTTPS connection setup process. This works, but it means that Secure DNS will fail to start if the network’s resolver is absent or malfunctioning. This is unfortunate because Secure DNS would be especially valuable in these situations, as a way to increase resilience and restore internet access.
Treating the DoH connection as a standard HTTPS link also periodically delays page loads: when the DoH server’s DNS entries are not in the stub cache (e.g. fresh cache or after expiration), all DNS activity is blocked until the DoH server’s name can be resolved, via the insecure resolver. In some common configurations, this is expected to result in a one-second pause every two hours (on average).
In many cases, these DNS lookups are unnecessary. Chrome already has the IP addresses of many Secure DNS resolvers built in, as part of its auto-upgrade functionality. However, Secure DNS server operators might use different IPs for secure and insecure DNS, or apply DNS-based load-balancing to the server IPs, so we cannot simply use these addresses unconditionally.
In addition to the built-in servers, Chrome also supports arbitrary custom Secure DNS URI templates. To support network debugging and advanced users, we should make this functionality available in that case as well.
We will add a flag to net::DohProviderEntry indicating whether the auto-upgrade IPs are also usable for DoH. For debugging and advanced users, we will support entering a JSON blob (instead of a URI template) in the “Custom provider” text field, with the following format:
{
“uriTemplate”: ”https://dns.google/dns-query{?dns}”,
“dnsRecords”: {
“A“: [“8.8.8.8”, ”8.8.4.4”],
“AAAA”: [”2001:4860:4860::8888”]
}
}
For enterprise administrators, this functionality will be available via the DnsOverHttpsTemplates group policy.
When Secure DNS is enabled, Chrome will insert the pre-populated DNS data related to the Secure DNS service into the secure net::HostCache with TTL zero, making it immediately stale. We will define a new HostCache::Entry::Source, SOURCE_PREFILL, to identify these entries for debugging purposes. These entries will also carry a new “bootstrap” flag, to ensure that stale entries that are used to connect to the current Secure DNS resolver are never evicted. Any record in the secure HostCache that is added or used by Secure DNS bootstrap will have this flag set.
We will replace the “disable_secure_dns” flag on net::URLRequest, HTTPRequestInfo, etc., with a SecureDNSTag enum containing the values ALLOW, DISABLE, and BOOTSTRAP. SecureDNSTag::BOOTSTRAP will be set only by the resolver, on the URLRequests it issues for Secure DNS. When processing a URLRequest with this tag, the async resolver will check the secure cache and allow the use of stale entries. If a required entry is not found in the secure cache, it uses the network’s resolver and the insecure path, whose behavior is not altered.
After returning the answer, if the answer was not secure and fresh, the resolver will initiate an async secure lookup to refresh the secure cache. If this lookup fails, any stale “bootstrap” cache entries are evicted, and the prefill data (if any) is re-inserted. Similarly, all bootstrap entries are cleared and reinitialized when a network change is detected.
This behavior results in the following properties:
Problems of this kind are rare enough that they are unlikely to be detectable in metrics, and users on broken networks can’t report metrics so A/B comparison is not possible. However, it’s possible that metrics for page-load-on-launch could show a measurable improvement if we can isolate the cases when Secure DNS is enabled.
We may temporarily add some detailed metrics to verify that the rollout is working and bootstrap IPs are performing well.
We have various performance and reliability metrics for DoH. We’ll keep an eye on them for regressions during the Finch experiment rollout, which should provide a clean A/B comparison.
This behavior is well-suited to a Finch-based rollout.
Experiment-controlled rollout, A/B testing bootstrap behavior for users who are already using Google Public DNS. Once the functionality is confirmed working, it will be made available to all providers.
This rollout is not expected to impact the behavior of captive portal detection or login. Users of their ISP’s DNS filtering service should also not be affected. Please reach out to net-dev@chromium.org if you have identified a potential issue or concern.
This change should improve page load speed when Secure DNS is enabled and the IP address of the Secure DNS server is not in cache. This is most relevant when launching Chrome for immediate navigation, because the Secure DNS bootstrap DNS resolution is on the critical path of a user interaction. This is especially acute on Android, where background processes are frequently terminated and cross-app page loads are common.
This change also enables better load-balancing and geotargeting of Secure DNS connections, by allowing the Secure DNS server to return IP addresses for itself. For some servers, this will result in lower average DNS latency.
Risk: If the HostCache contains multiple bad “bootstrap” addresses (e.g. stale across a major server config change), the stub’s fallback logic could take a long time (several seconds) to try them all. This issue seems unlikely in practice, as IP addresses are typically removed from service infrequently, and not deactivated immediately after the DNS record expires.
This change is not primarily a security enhancement. However, by reducing the reliance on insecure DNS, this change should improve security against certain complex attacks (e.g. an attacker who can poison the insecure DNS resolver cache in order to trigger a downgrade or abuse a mis-issued certificate).
This code will be part of //net/dns, which runs in the network (desktop) or browser (mobile) process.
This change should improve the reliability of Secure DNS, especially on networks with malfunctioning local DNS.
Usage: This change is designed to improve user experience invisibly, so there are no changes to UI or UX. (JSON configuration blobs, for debugging and advanced use cases, are implemented without exposing any complexity to ordinary users.)
Implementation: The proposed implementation is largely contained within //net/dns, and does not change the API exposed to the rest of //net, or Chrome generally.
The binary size and memory impact are expected to be minimal.
This change does not alter the amount or type of information shared with anyone. All relevant state is reset on network changes to avoid linking successive user IPs.
This feature is contained entirely within //net, and will be tested by unit testing consistent with the surrounding functionality.
Once the code is fully rolled out, we will probably want a Chromium wiki page describing the JSON format for advanced use cases. We will also need to extend the DoH provider review process to ask each provider whether their upgrade-trigger IPs are suitable for bootstrap use.