Go HTTPS Servers with TLS
eli.thegreenplace.net
eli.thegreenplace.net
I appreciate it isn't central to the article's purpose, but this is wrong, and as background noise it contributes to a misunderstanding that causes other problems every time it occurs.
I will describe what's done in TLS 1.3 because that's clearer, but you should know this isn't how your TLS 1.2 server actually works in real use today either.
TLS encryption doesn't care about these certificates. (Elliptic curve) Diffie-Hellman is used to agree random keys, and the random keys encrypt everything. In TLS 1.3 nobody even says anything about certificates before that step happens - the certificates are sent encrypted.
So why have certificates at all? Because without them you're having a secure conversation... but you don't know who you are having a conversation with. So now we're definitely communicating securely with someone let's check it's who we expected. In TLS 1.3 the server (and optionally client) can send a certificate, claiming some identity, and then they sign the transcript (a record of everything said so far) with the corresponding private key, in effect saying "This is proof you are having this conversation with me".
It is also, factually, a description of how RSA key agreement works.
More importantly, nobody still does this. If you're writing new HTTPS Servers, in Go, in 2021, your only reason to even implement RSA kex is that it's technically still Mandatory To Implement for TLS 1.2 and so it's a compliance consideration. You definitely shouldn't have clients that still think RSA kex is a good idea.
The reason I care though isn't about being technically correct, it's because people get the idea that this sort of 1990s hybrid crypto is really how things work, and then they're confused because of course that's not how anything they actually use works. At best this means TLS gets a reputation for being more mysterious or complicated than it really is, and at worst we get cargo cult solutions that make "sense" based on the misunderstanding but alas aren't actually secure in practice.
Also your comment seems like an odd nitpick given the author states in the first paragraph:
>"I won't be covering how the protocol itself works in detail here ..."
And in the sentence before that they also linked to previous articles they've published on both DH and RSA which go heavily into the details of how each works. It's seems to me like they actually were being quite careful to avoid the very thing you are being critical of even though it was obvious that it wasn't the point in the article.
I personally have rarely found the need to handle TLS in my application code; even in a cloud-native environment where you want to secure traffic inside your network, I prefer to do TLS termination in another process (in Kubernetes land this would be a sidecar container in a pod), because {nginx,envoy,caddy,etc} have great TLS support and there are strong standard conventions for how to configure them with the key. It's often much easier for someone to come along and figure figure out how you're configuring a standard HTTP reverse proxy than to figure out some bespoke application configuration when a TLS key needs to be rotated.
This is probably a little naive and I suspect that some highly latency-sensitive applications benefit from the overhead of that additional hop (even if it's over localhost), but I think offloading the TLS workload as its own concern has many benefits.
One thing is your compute environment; if there's a really solid way to assure that the connection between your proxy and your service implementation remains secure then that's one thing - however, outside of K8s (or similar) that may not be easy or may be bespoke, and there's a configuration overhead of assuring that proxies run the right place, bind the right ports, expose them only to the right places and so on.
Even assuming a good orchestrator, there is that extra degree of assurance in knowing that TLS is actually being terminated in your process.
Public CA PKI vs. enterprise PKI also makes a difference; it's easy enough to integrate with LetsEncrypt or whatever using Nginx but your enterprise PKI might be more complex and having SDKs for direct integration into service code might actually be easier. In a large environment, or one where certificates are short-lived, the operational aspect dominates.
Level of cloud buy-in is also a factor; for example, if you're deep in AWS then you're probably already hosting behind an AWS load balancer of some sort that knows how to terminate TLS and also knows how to integrate with ACM for automated renewals.
Finally, if you need client authentication certificates then you might find your proxy implementation just passes them on to you as headers (or perhaps just does chain validation), which leaves you holding the bag for the rest of that in your service anyway.
Likely in a small toy system this is all just binaries running inside a VM like the K8s setup you describe - and you could reason that any adversary who is running inside your VM has won already, but on bigger systems this design increasingly makes it likely production will end up with the unencrypted bits moving over at least some sort of internal network.
I've built things that worked that way, but I've also built things that deliberately did their own TLS, and I know for sure which I'd have confidence in to protect valuable data.
I want to just run a server somewhere (my laptop, kubernetes, some virtual machine) and have any domain which points to that VM auto-create a TLS certificate.
I challenge anyone to propose a way to register and set up "mydomain.com" (https serving some static files) with opensource software without having to type "mydomain" at least 3 times!
You always have to paste in 100 lines of boilerplate into some hidden config file.
A few lines to install the debian repo (with systemd service): https://caddyserver.com/docs/install#debian-ubuntu-raspbian
Then your /etc/caddy/Caddyfile just looks like this (assuming you have an app listening on port 8080 that you want to proxy to):
example.com
reverse_proxy localhost:8080
Yer done. Automatic HTTPS for "example.com", all you need to do is point your DNS to your server, make sure ports 80 and 443 are accessible, and Caddy does the rest.But you won't be able to solve the general problem without some other element. The current iteration of the Ten Blessed Methods makes it very easy to have an HTTP or HTTPS server that arranges for its own certificates with ACME, because they assume that if you get to run web servers (on port 80 and/or 443) on some.name.example that's because you control some.name.example or you've got permission from whoever does control it to do whatever you want.
It's trickier for a mail server. You may genuinely run the MX for some.name.example but if you don't control DNS for that name, nor run the web servers, you can't pass ACME challenges. In the case of a mail server you could automate a hypothetical ACME challenge that relies on say 3.2.2.4.4 Constructed Email to Domain Contact but I don't think anybody actually offers that today.
It's completely impossible (today, on your own) for some other random TLS service. If you run the TLS-enabled IRC server on some.name.example you can't get certificates without help from the person who really controls the some.name.example FQDN. They need to either let you run an HTTP server, control their DNS records or just hand out working certificates to you somehow.
Beyond that, though, there really isn't a solid "turn on TLS" option because security is hard and TLS is not a panacea. It ostensibly encourages that a client on the Internet will only send data to a domain name that currently has a matching TLS certificate (practically relying on an intricate web of trust between DNS and PKI), but that isn't even a guarantee. Web servers often still listen on port 80, clients can be forced to ignore certificate errors, and TLS does zero client-side authentication or authorization by default.