Saturday, March 23, 2024

How to have multiple TLS certificates on the same IP ?

UPDATE: It turns out that Cloudflare actually allows you 10 Origin Rules which allow you to rewrite the destination port to whatever you want! So you can host a service on your web server on port (say) 8081. Now, if you tried to connect to Cloudflare proxy on port 8081, your traffic would just get dropped. But, if you created a custom rule that said that all traffic destined for a certain hostname should have the destination port redirected to port 8081, then you can connect to the Cloudflare proxy on any proxied port and it will rewrite the destination port to whatever you set it to! Pretty cool, right?

 

 

 

UPDATE: Apparently having x (repeated 3 times) dot com in your blog post automatically marks it as an adult blog post by blogger. Pretty interesting. I didn't know that. Changed it to aaa.com, now seems fine.

[This blog post is written for myself only]

So here is my problem:

  1. I want to host multiple domains (e.g. aaa.com and bbb.com)
  2. I want to host them on the same IP address. (IP addresses are very limited, so it's really really important for servers to be able to serve multiple domains from one IP address)
  3. I want to serve them over TLS.
  4. I want to use one TLS certificate for some domains, and another TLS certificate for other domains (yes, I do have one TLS certificate that is valid for some of my domains, but I want to use another TLS certificate for some of my other domains).
  5. I want to proxy my traffic through Cloudflare.

 Anyway, as far as I know there are only 2 solutions to this problem:

  1. Use SNI
  2. Use different ports

If you're proxying your traffic through Cloudflare (the cloud icon on the DNS page in Cloudflare) then ALL traffic will first go thru Cloudflare proxy server before ending up at your server.

This means that if you're hosting a service on a non-proxied port, like port 8081, then try to access that port through your domain, your traffic will simply get dropped by Cloudflare - the packets simply won't arrive at your server!

Unfortunately, the number of ports proxied by Cloudflare is quite small -- only a dozen or so -- and only like 2 or 3 are actually cached - port 80 and port 443 and I think 8080 (haven't tried).

So if you want Cloudflare proxying, you can only choose one out of a dozen or so ports. And if you want Cloudflare caching then your options are basically limited to port 80 or 443.

But let's take a step back. Why are we limited to these 2 options? Why can't we just build a reverse proxy like we can with plain old HTTP traffic?

The reason you can't reverse proxy TLS traffic the same way you reverse proxy plain old HTTP traffic is because during the initial TLS handshake (prior to SNI), the server has to send over the certificate before the client indicates which domain it's trying to connect to. When the server has multiple certificates, it doesn't know which certificate to send over. If it sends over the wrong certificate then the handshake simply fails.

But now there is this cool TLS extension called SNI - Server Name Indication (it's badly named - it should really be called DNI - Domain Name Indication, because the domain name is what is being indicated).

Without SNI, you couldn't have a TLS reverse proxy. Why? Because you want your TLS reverse proxy to direct packets to the service based on the domain name. But the initial TLS handshake packets don't contain the domain name, so you don't know which service to direct the packets to. All you can see is just the IP and port, which are the same regardless of which domain the client is requesting.

So without SNI, it would be impossible to do even something as simple as hosting multiple domains on the same IP over TLS on the same port - something that is trivial to do with HTTP, because HTTP is not encrypted so the reverse proxy can see which domain the client is requesting and just direct the traffic to the appropriate service. You can't do that with TLS. If SNI didn't exist, this blog post would be titled "Why TLS Is Annoying". 



Anyway, using different ports to serve different websites is clearly not a very scalable solution (since Cloudflare only proxies a dozen or so ports), but it also lacks caching, and just generally feels pretty hacky.

So I think SNI is the right way to go here.

EDIT: Found this link about writing a reverse proxy that does SNI in Go: https://www.agwa.name/blog/post/writing_an_sni_proxy_in_go

See also: https://www.gilesthomas.com/2013/07/sni-based-reverse-proxying-with-golang



I guess a further question to ask is whether or not the reverse proxy should decrypt the TLS traffic.

I think it should not, because it would be simpler to have each separate service managing its own TLS certificates.



No comments:

Post a Comment