Wandering Thoughts archives

2019-05-08

Some general things and views on DNS over HTTPS

Over on Twitter I said something and then Brad Beyenhof asked me a sensible question related to DNS over HTTPS. Before I elaborate my Twitter answers to that specific question, I want to do an overview of some general views on DNS over HTTPS on the whole.

DNS over HTTPS (hereafter DoH) is what it sounds like; it's a protocol for making DNS queries over a HTTPS connection. There is also the older DNS over TLS, but my impression is that DoH has become more popular, perhaps partly because it's more likely to make it through middleware firewalls and so on. DoH and DoT are increasingly popular ideas for the straightforward reason that on the modern Internet, ISPs are one of your threats. A significant number of ISPs snoop on your DNS traffic to harvest privacy-invasive things about your Internet activities, and some of them actively tamper with DNS results. DoH (and DoT) mostly puts a stop to both DNS snooping and DNS interference.

(It's likely that DoH doesn't completely stop these because of the possibility of traffic analysis of the encrypted traffic, which has already been shown to reveal information about HTTPS browsing, but we probably won't know how serious an issue this is until DoH is enough used in the field to attract research attention.)

As far as I can tell, DoH (and DoT) are currently used and intended to be used between clients and resolving DNS servers. If there is work to protect queries between a resolving DNS server and authoritative servers, I think it's using a different protocol (Wikipedia mentions DNSCurve, but it's not widely adopted). This doesn't matter for typical users, who use someone else's DNS server, but it matters for people who run their own local resolving server that wants to query directly to authoritative servers instead of delegating to another resolving server.

DoH protects you from your ISP monitoring or tampering with your DNS queries, but it doesn't protect you from your DoH server of choice doing either. To protect against tampering, you'd need some form of signed DNS (but then see Against DNSSEC). It also doesn't currently protect you against your ISP knowing what HTTPS websites you go on to visit, because of SNI; until we move to ESNI, we're always sending the website name (but not the URL) in the clear as part of the early TLS negotiation, so your ISP can just capture it there. In fact, if your ISP wants to know what HTTPS sites you visit, harvesting the information from SNI is easier than getting your DNS queries and then correlating things.

(There is very little protection possible against your DoH server monitoring your activity; you either need to have a trustworthy DNS server provider or run your server yourself in a situation where its queries to authoritative servers will probably not be snooped on.)

More or less hard-coded use of DNS over HTTPS servers in client programs like browsers poses the same problems for sysadmins as basically any hard-coded use of an upstream resolver does, which is that it will bypass any attempts to use split-horizon DNS or other ways of providing internal DNS names and name bindings. Since these are obvious issues, I can at least optimistically hope that organizations like Mozilla are thinking about how to make this work without too much pain (see the current Mozilla wiki entry for some discussion of this; it appears that Mozilla's current setup will work for unresolvable names but not names that get a different result between internal and external DNS).

PS: There are a number of recursive DNS servers that can be configured to use DoH to an upstream recursive server; see eg this brief guide for Unbound. Unbound can also be configured to be a DoH server itself for clients, but this doesn't really help the split horizon case for reasons beyond the scope of this entry.

DNSOverHTTPSSomeViews written at 23:12:48; Add Comment

2019-05-05

TLS certificate rollover outside of the web is complex and tangled

On the web, renewing and rolling over TLS certificates is a well understood thing, with best practices that are exemplified by a Let's Encrypt based system. There is a chain of trust starting from the server's certificate and running up to a root certificate that browsers know, and everything except the root certificate is provided to the browser by the web site. Server certificates are rolled over regularly and automatically, and when this happens the website is also provided with the rest of the certificate chain, which it can and should simply serve to browsers. Intermediate certificates roll over periodically, but root certificates almost never do.

However, all of this relies on a crucial property, which is that web server certificates are ephemeral; they're used in the course of a single HTTPS connection and then they're forgotten. This means that clients visiting the website don't have to be updated when the web server certificate chain changes. Only the web server has to change, and the web PKI world has made a collective decision that we can force web servers to change on a regular basis.

(The one thing that requires browsers and other clients to change is changing Certificate Authority root certificates; how slow and hard it is to do that is part of the reason why CA root certificates are extremely long-lived.)

However, you don't always have ephemeral signatures and certificates, at least not naturally. One obvious case is the various forms of code signing, where you will get a signed blob once and then keep it for a long time, periodically re-verifying its signature and chain of trust. As Mozilla has demonstrated, rolling over any of the certificates involved in the signing chain is rather more challenging and being capable of doing it is going to have significant effects on the design of your system.

Very generally, if you want true certificate rollover (where the keys change), you need to have some way of re-signing existing artifacts and then (re-)distributing at least the new signatures and trust chain. If your signatures are detached signatures, stored separately from the blob being signed, you only need to propagate them; if the signatures are part of the blob, you need to re-distribute the whole blob. If you redistribute the whole blob, you need to take care that the only change is to the signature; people will get very unhappy if a new signature and chain causes other changes. You can also contrive more complex schemes where an integrated signature chain can be supplemented by later detached signatures, or signatures in some database maintained by the client program that re-verifies signatures.

(Since what you generally sign is actually a cryptographic hash of the blob, you don't have to keep full copies of every version of everything you've ever signed and still consider valid; it's sufficient to be able to re-sign the hashes. This does prevent you from changing the hash algorithm, though, and you may want to keep copies of the blobs anyway.)

For rolling over intermediate certificates in specific, I think that you only need access to the original end certificate to produce a version re-signed with your new intermediate, and you should be keeping a copy of these certificates anyway. You could then contrive a scheme where if a signature chain fails to verify purely because one of the certificates is out of its validity period, the client attempts to contact your service to get a new version of the end certificate and trust chain (and then saves the result, if it validates). But this scheme still requires the client to contact you periodically to download signature updates that are relevant to it.

(You could see this as sort of an extension of OCSP, or the inverse of a CRL.)

In Mozilla's case, they have the advantage that their program is more or less intrinsically used online on the Internet (with small exceptions) and is generally already set to check for updates to various things. Fetching some sort of signature updates for addons would not be a huge change to Firefox's operation in general, although it probably would be a significant change to how addons are verified and perhaps how they're updated.

All of this is an area of TLS certificates and certificate handling that I don't normally think about. Until Mozilla's problem made this quite visible to me, I hadn't even considered how things like code signatures have fundamental operational differences from TLS website certificates.

PS: Short-lived code signing certificates and other non-ephemeral signatures have the obvious and not entirely pleasant side effect that the signed objects only keep working for as long as you maintain your (re-)signing infrastructure. If you decide to stop doing so, existing signed code stops being accepted as soon as its certificates run out. The friendly way to shut down is probably to switch over to extremely long lived certificates before you decommission things.

CertificateRolloverComplex written at 21:26:21; Add Comment

What usually identifies an intermediate or root TLS certificate

The usual way of describing TLS certificates for things like websites is that they are a chain of trust, where your website's TLS certificate is signed by a Certificate Authority's current intermediate certificate and then that intermediate certificate is signed by the CA's root certificate. For example, you can read about Let's Encrypt's chain of certificates. Some CAs use a chain of multiple intermediate certificates.

(Modern TLS certificates often include a URL to fetch their parent certificate, as covered here.)

But this raises a question, namely how a TLS certificate specifies what its parent certificate is (the certificate that signed it). Until recently, if you had asked me and I had answered off the cuff, I would have guessed that it was based on something like the cryptographic hash of the parent certificate. As it turns out, this is not how it works; instead, as you can find out from actually looking at a certificate, the parent certificate's identity is given by providing its X.509 Subject Name attribute, which identifies both the organization and the intermediate or root certificate's Common Name attribute.

(Okay, there are also Key Identifiers, see here and here. It appears that Key Identifiers are normally exactly that, which is to say that they identify the public key used, not the certificate as such.)

There are some interesting consequences of this that I didn't fully realize or think about before recently. The first is what Let's Encrypt does, which is that it has two versions of its intermediate certificate, one signed by its own root certificate and one signed by IdenTrust. Since they have the same Subject (including Common Name), TLS certificate validation by browsers and other things will accept either. If you inspect the current two Let's Encrypt X3 certificates, you'll find that they also have slightly different key validity periods, presumably because the one signed by the LE root was signed later (about seven months, if we go from the not-before date).

The second is that you can take an existing intermediate certificate and re-issue and re-sign it with new validity dates (and perhaps other modifications to certificate metadata) but the same keypair, the same Subject and Common Name, and so on. This new version of the intermediate certificate can be transparently substituted for the old version because everything that identifies it matches. However, it absolutely must re-use the same public key, because that's the only way to have existing signatures verify correctly.

(This is similar to how in the old days of manual TLS certificate renewal, you could re-use your existing keypair in your new CSR if you wanted to, although it wasn't the recommended practice.)

(See also how TLS certificates specify the hosts they're for which covers server certificates and talks more about X.509 Subject Names.)

PS: Re-signing a certificate this way doesn't require access to its private key, so I believe that if you wanted to, you could re-sign someone else's intermediate certificate with your own CA. I can imagine some potential uses of this under sufficiently weird circumstances, since this would create a setup where with the right certificate chain a single TLS certificate would validate against both a public CA and your internal CA.

TLSCertificateIdentity written at 00:49:53; Add Comment

2019-05-01

One of my problems with YAML is its sheer complexity

YAML is unarguably a language, with both syntax (how you write and format it) and semantics (what it means when you write things in particular ways). This is not unusual for configuration files; in fact, you could say that it's absolutely required to represent a configuration in text in any way. However, most configuration file languages are very simple ones, with very little syntax and not much semantics.

YAML is not a simple language. YAML is a complex language, with both a lot of syntax to know and a lot of semantics attached to that syntax. For example, there are nine different ways to write multi-line strings (via), with subtle differences between each one. Like Markdown and even HTML, this complexity might be okay if everything used YAML, but of course this isn't the case. Even in our small Prometheus environment, Prometheus uses YAML but Grafana uses a .ini file (and sometimes JSON, if we were to use provisioning).

The problem with a complex configuration file language is that it is not the only thing in the picture. Specifying the configuration itself has its own syntax and semantics, often complex ones; it is just that they are not at the lexical level of 'what is a string' and 'what is a block' and 'how do you define names and attach values to them'. This is actually something that using YAML makes quite clear; your YAML describes a structure, but it doesn't say anything about what the structure means or how you write it so that it means the things you want. That's all up to the system reading the YAML, and it can be as complicated as the program involved wants to make it. Often this is pretty complicated, because the program needs to let you express a bunch of complicated concepts.

Since specifying the configuration is often intrinsically complex, a complex configuration file language on top of that is adding an extra layer of pain. Now instead of one complex thing to learn and become decently capable at, you have two. Dealing with two complex things slows you down, increases the chances of problems, and probably means that you will lose familiarity with the overall system faster once you stop actively working on it (since there is more you need to remember to do so).

Complexity also invites writing your YAML based partly on superstitions. If you don't fully understand YAML because it's so complex, your best approach is to copy what works and modify it only if you have to. This will probably not give you the right or the best YAML, but it will give you YAML that works, which is your real goal. Your goal is not to write perfect YAML, your goal is to configure your system; YAML (and the entire configuration file) is an obstacle in the way that you need to climb over.

(Do I write superstitious YAML? Yes, absolutely.)

Ultimately that is my biggest objection to the complexity of YAML; it puts a substantial obstacle in the way of getting to what you want, an obstacle that is generally unnecessary. Very few configuration languages need the complexity and generality in their configuration files that YAML provides, so almost all of them would be better served by a simpler configuration file language.

(This entry is informed by YAML: probably not so great after all, which covers a number of other problems and traps in YAML that don't concern me as much, partly because I don't think I'm likely to run into them. Via, itself via.)

YamlComplexityProblem written at 21:46:42; Add Comment

By day for May 2019: 1 5 8; before May; after May.

Page tools: See As Normal.
Search:
Login: Password:
Atom Syndication: Recent Pages, Recent Comments.

This dinky wiki is brought to you by the Insane Hackers Guild, Python sub-branch.