 All right, thank you, everyone. Last talk of the day, so thank you for bearing with us. I'm going to talk with you about using Envoy's new filter chain matcher API for advanced TLS routing. Essentially, what we're doing here at a high level is we managed to figure out a way to route traffic to different upstreams based on the TLS cifers that were provided during the client hello. So yeah, and my name is Ashish Banerjee. I work at solow.io. We provide API gateway solutions, service mesh solutions, obviously built on top of Envoy. So what motivated this application? This essentially came from one of our happy users. They said everything's going great with Envoy. We'd like to migrate one of our services from our existing API gateway onto Envoy, but they identified a problem during the migration process. They identified the fact that some of these services still explicitly had enabled support for certain TLS cifers that are not natively supported within boring SSL. They've been deprecated. They've been removed. And honestly, they're not the best TLS cifers in the world. But when we're talking about migrating users from an existing product to a new one, regrettably just cutting these users off because we're using a new product is not really feasible. So we had to come up with a way to support them. And so since this was a migration effort, we essentially decided upon a solution. There are many possible solutions to this problem, but the one that we specifically chose was that we wanted to route all traffic through Envoy. But we wanted to pass through TLS traffic that could not be decrypted to a separate upstream for this other upstream to handle. So how does that work at a high level? Now, typically on the normal request path, if we just pay attention to the lower path here, we essentially have TLS client hello. We do our host name matching for SNI purposes. And then we go through the TLS handshake process. And if the TLS handshake is successful, then we perform termination. We decrypts the connection. And we go through our standard HTTP filters and send it to our HTTP upstream. We cannot make the connection work because there's a lack of overlap between TLS cifers. Then we just send a handshake failure back to the clients and the connection is closed from there. Now what we're essentially doing here is we're adding a third path, this upper path here, highlighted in red, where if we determine that native TLS termination cannot occur, but one of the very explicitly selected TLS cifers for this particular service have been enabled and is present in the TLS clients cifers in the client hello, then we say, OK, let's select the second filter chain to a TCP proxy to a separate upstream over L4. And then it's separate upstream's problem that that service can do whatever it wants with that traffic. And obviously, we're not limited to just three possible routes here. You could add as many as you want, but at least for this particular application. We only needed three, which was handled by two filter chains. Yeah, so how does this work in Envoy itself? So it's not as simple as just enabling the filter chain matcher to perform this operation. We had to do a couple earlier steps in order to make this work. The key limitation was the fact that the TLS cifers were not explicitly provided to the filter chain matcher for us to be able to make this decision. So we had to write a couple of custom extensions and filters to make this work. So the first was a listener filter, the TLS cipher inspector. So if you're doing SNI in Envoy, you need to enable the TLS inspector. This is what provides a host of important information to the filter chain matcher, filter chain matcher object in order to select a particular upstream. It parses out important information, like the SNI host name, for example. But it also provides other useful things like ALPN, and TLS version, and all the other helpful useful stuff. But very critically, it does not provide the TLS cifers that the client provides. So we wrote a small extension to do this for us. It basically just does the same thing as the TLS inspector. But instead of parsing out all the other information I told you about, it parses out the TLS client provided cifers. We're not done there. Just because we've parsed it, we also have to figure out a way to send it to the filter chain matcher. And to the best of my knowledge, there's no specific way to do this. But we just do it using the same generic way we would do everything when we have to pass information between filters. And that's essentially to instantiate a filter state objects or dynamic metadata either would work. But in this case, we chose a filter state because it just ended up being a little bit easier to use. And then finally, we step into the filter chain matcher. And I'll go into a little bit more detail on this in the next slide. But essentially, what it's doing, it's doing two things. First, it's performing SNI matching to select the particular client certificate. And then finally, we perform the matching on the client cifers that are provided. And we make a decision here. We look at the client cifers that have been provided by the clients. And we look at the cifers that have been configured on this particular service. And we make a decision. Do we go to the L7 filter chain with the HTTP connection manager and the HTTP filters and everything? Or do we just proxy it over? Or do we do the handshake failure? So what does this filter chain matcher look like? It's essentially a two-level tree. You know, this matching API is extremely flexible and extremely powerful. You can do a lot of different things with it. In our particular specific case, we did a top-level tree where, essentially, at the highest level, we have this server name input. So essentially, the server name is provided into the tree. And then we match on it. So you can see, I know the text is a little bit small. Envoy configuration is hard to get too many lines in there. But there's a number of entries in here. There's server1.com, where we have an unmatched fields, which I neglected to include. It's server3.com, which also I neglected to include. But you can see, these only have just one filter chain for each one, which uses the HTTP filter chain that we talked about previously. But then on specifically server2.com, we are actually enabling this advanced matching algorithm. So we're only doing this on server2.com, right? What's nice about this sort of interface is the fact that you don't have to enable this across the board. You can only do it very explicitly on these specific services that have this feature enabled. And as clients upgrade, and we see traffic has stopped coming in using these old siphers, we can slowly turn them off and disable them. So you can see the configuration here. Suppose we match server2.com, then we add a subtree in terms of matching. So let's take a look at what's going on in here. We have the siffer detection input. This is essentially the custom logic that we're using for matching and making our selection. And I'm sure you can imagine what the logic looks like in your head, right? You iterate through the client provided siphers, and then you say, OK, well, if there's one in the native terminated list, then return early. Just provide that as the output. If not, see if any of the previous siphers was in the pass-through list. And if so, then return that value. And if not, we just go back to the original filter chain 2 list down here. So this filter chain list down here, filter chain 2, is the one where we do either the TLS termination or the TLS handshake failure. And this node over here, which is switching on the output from the node that is analyzing the client siphers, that's the one that's actually getting a value and evaluating it within the Envoy ML configuration. And it says, OK, well, I've got a pass-through filter chain as the result. That was returned here by our custom logic, my apologies. And so what do we do? Well, then we go to filter chain 3, which is the pass-through filter chain over here. And another thing you'll notice is that the TLS siphers here are provided using their UN16 values. This is necessary because boring SSL doesn't actually know what the names of these TLS siphers are. I could specify them by name, AES128, whatever, ECDHE, whatever. But AES128 doesn't actually exist in boring SSL. We can't use it. So we just specified them by their UN16 values. OK? So as a quick demonstration, basically, there are three requests that you see here. And I apologize. They're a little bit long just because specifying SNI and Curl is a little bit weird. But essentially, all of these requests are exactly the same, except in terms of the TLS siphers that are being provided. So the top two cases are the ones that we're typically familiar with. This ECDHE, AESGCM siphers here is the 49,200 that we had in the terminating siphers list down here. So that's 49,200. And what happens when we hit that, when we sense that TLS siphers, Envoy says, OK, I can perform TLS termination. It performs TLS termination successfully. I just implemented a direct response action that says, hello from Envoy. But we wouldn't be able to do that if Envoy were not actually terminating the TLS and decrypting the request. The next one is Cha Cha 20 TLS siphers. Now this one is actually natively supported and boring SSL. But we did not mention it in the list of TLS siphers that we provided in the previous slide. So Envoy doesn't use it. And it simply returns a TLS handshake failure. And then finally, at the bottom, we have an example of one of our pass-through siphers. This was 60 from the previous slide, AES 128 Cha 256. And so what happens here is Envoy says, OK, I'll use the other filter chain. And what does that other filter chain do? It just creates an L4 proxy to tcbbin.com. tcbbin.com decrypts the request for us. And it's a TCP Echo server, so it just echoes the contents of the request right back. That's exactly what a HTTP request looks like. Not a response, but OK. So that's basically all I wanted to demonstrate for you. This filter chain match API is incredibly flexible and powerful just as we've come to expect from this incredible project. Everything is extensible, right? So we demonstrated basically how you could incorporate new sources of information into the matching tree. Basically write your own parser, put it in a filter state or a dynamic metadata object, and then parse it from the tree. We can add our complex logic in there for the routing decisions. So I mean, I told you, the algorithm itself to write is fairly simple and straightforward, but imagine doing something like that in YAML configuration, right? It would be much more complicated to do. So we just write our custom C++ logic into this extension to make the decision for us. And we have the inputs. We have the outputs, and we can switch on that. And yeah, supporting this use case can promote greater adoption of Envoy, because this is unfortunately one of the shortcomings of Envoy, right? When we were talking about this with this customer, he compared it. He's like, all these other API gateway services use these TLS sifers. And when we hear that, we want to promote Envoy. We want it to be adopted everywhere we can. So this is just our take on how we overcame this limitation. OK, that's all I wanted to share with all of you. Thank you so much for your attention. And yeah, have a wonderful EnvoyCon. Thanks.