Home United States USA — software Writing SSL Proxy, Part I: Routing

Writing SSL Proxy, Part I: Routing


We take a look at how one developer implemented secure routing for his database using SSL and TLS in order to secure traffic to and from his database.
RavenDB 4.0 uses x509 client certificates for authentication. That is good because it means that we get both encryption and authentication on both ends, but it does make it more complex to handle some deployment scenarios. It turns out that there is quite a big demand for doing things to the data that goes to and from RavenDB.
We’ll start with the simpler case, of having dynamic deployment on Docker, with nodes that may be moved from location to location. Instead of exposing the nastiness of the internal network to the outside world with URLs such as (https://129-123-312-1.rvn-srv.local:59421) we want to have nice and clean URLs such as https://orders.rvn.cluster. The problem is that in order to do that, we need to put a proxy in place.
That is pretty easy when you deal with HTTP or plain TCP, but much harder when you deal with HTTPS and TLS because you also need to handle the encrypted stream. We looked at various options, such as Ngnix and Traefik as well as a peek at Squid but we rule them out for various reasons, mostly related to the deployment pattern (Ngnix doesn’t handle dynamic routing), feature set (Traefik doesn’t handle client certificates properly) and use case (Squid seems to be much more focused on being a cache). All of them didn’t support the proper networking model we wanted (1:1 connection matches from client to server, which we would really like to preserve because it simplifies authentication costs significantly).
The answer, as it turned out, is that this is not a new problem. One of the ways to do that is to just intercept the traffic. We can do that because, in this deployment model, we control both the proxy and the server, so we can put the certificate from “orders.rvn.cluster” in the proxy, decrypt the traffic and then forward it to the right location. That works, but it means that we have a man in the middle. Is there another option?
Anyway, the key here is that this is possible, so let’s make this happen. The solution is almost literally pulled from the StreamExtended readme page.
We get a TCP stream from a client, and we peek into it to read the TLS header, at which point we can pull the server name out. At this point, you’ll note, we haven’t touched SSL and we can forward the stream toward its destination without needing to inspect any other content, just carrying the raw bytes.
This is great because it means that things like client authentication can just work and authenticate against the final server without any complexity. But it can be a problem if we actually need to do something with the traffic. I’ll discuss how to handle this properly in the next post.

Continue reading...