-
Notifications
You must be signed in to change notification settings - Fork 4.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Selecting SSL certificate dynamically #25958
Comments
I'm sure you can do whatever you need if you write a custom handshaker. I'm not sure if this can be done out of the box. Your description is hard to understand so perhaps you could make it more clear on what exactly you are trying to achieve. cc @ggreenway @lizan |
Basically, for a multi tenancy model, I would like to present each tenant with a different certificate. The tenancy information would be resolved in a listener filter, prior to the TLS handshake, so I would like to choose the matching certificate per tenant dynamically. |
Yeah I think you will likely need to create a custom handshaker but it should be possible. |
My concern is that custom handshaker would somehow mess the handshake and compromise security (even though I read it's possible to take the normal handshaker as base and modify the minimal parts needed). |
You can currently specify a number of certificates and have the correct one chosen based on the SNI presented in the handshake, but there's not support for looking on disk for them when the TLS handshake begins; all certificates are loaded when the config is loaded (or fetched via SDS). |
Freel free to propose a change and we can take a look. If filter state is possible to get to in the handshaker this seems like something we could potentially support. |
@ggreenway I'm afraid the SNI lookup would not be sufficient because two tenants might request the same SNI, although I would want to present each one with a different certificate |
Sure, we could add some way for a listener filter similar to tls_inspector to indicate which certificate should be used. But it would only work if the certificate(s) were already loaded; reading them from disk at this point would be very difficult and would introduce performance issues as the disk read could potentially block in the kernel for an arbitrary amount of time. |
Yeah I agree, I didn't mean to directly access the disk, but use the SDS as a broker, the mechanism for certificates loading would be the same, only the name of the secret would be configured dynamically per-connection (if specified by filter state) |
That sounds reasonable to me. Want to put together a really rough version of a PR so we can make sure we're on the same page? |
Absolutely, I'll get to it in next few days, hopefully tomorrow |
@ggreenway I had a thought about adding the matcher API to CommonTlsContext / SdsSecretConfig an option of "oneof" between "name" and "name_matcher", i.e:
The "oneof" should make this a non-breaking for those who use "name" already and enable the usage of Matching API for deciding upon the name of the secret to fetch, this way it could be applicable to both downstream and upstream TLS context. WDYT? I still intend to start with the filter state override, I don't have enough capacity to add the matcher API right away, but I think it could be a more cleaner way to decide upon secrets name. |
You can't wrap existing fields in But I like the idea of using the matcher API for extending certificate selection. |
How do you suggest to integrate this into the api config? |
Thinking about this, I'm not sure it applies to upstream connections. The current SNI matching is only on the downstream side. I think you could just add some config to |
I'm confused a little, how is SNI impacting name of secret to fetch using SDS? |
All of the secrets are fetched from SDS when the config is loaded; the name of the secrets to fetch is not dynamic. Then, from the loaded certificates, we match the SNI of the incoming TLS handshake to one of those fetched certificates. |
Have I completely misunderstood how SDS works then? When we discussed earlier in this thread about overriding the name by filter state, what was the meaning as you understood it? Because if all certificates are loaded by SDS at bootstrap, then the name in the config must be static, isn't it? |
I understood that as the name of the certificate to use. But I assumed that the cert was already loaded. |
The field "name" in SdsSecretConfig configures the name of the certificate to use (which was already loaded at bootstrap), or configures the name of the certificate that SDS should load at bootstrap? |
I believe that field is sent to the sds server, so that it knows which secret to send back. |
At which time is this field sent to the server? On-demand (when TLS handshake requested) or at bootstrap? |
When the configuration is loaded. There is not a way at this time to load additional secrets during the TLS handshake. |
Although the SDS server can push a new version of named secrets to Envoy at any time. But I believe it can only push 1 certificate for each named request. I don't think there's currently a way for Envoy to ask SDS for "give me everything you have". But it may be possible to implement what you're asking for. I haven't looked closely enough to know whether it's feasible. |
So that means that if I have two listeners and each has downstream TLS context, one with name "x1" and the second "x2", then I'll end up with only two certificates in Envoy, for these names? If that's true then I don't believe adding option to override used cert by filtet state is meaningful, because it could still be either "x1" or "x2". Another question here is what happens in case LDS is configured? As I understand it, in LDS case the listeners are not static and can change throughout the lifetime of the Envoy process. In that case, each new listener might introduce a new requested secret that was previously unknown, so how the SDS handles this case? I expected Envoy to fetch all the certificates the SDS wants to provide at bootstrap, and refresh the inventory from time to time, then the field name would be for selecting a certain certificate out of all of the certificates that the SDS has provided. As I understand, step 1 for supporting this dynamic selection would be adding an option to fetch all certificates provided by SDS without requesting for specific names at fetch time? |
Yes, I think your understanding is correct. If you cannot enumerate the SDS name of each certificate in the envoy configuration right now, there isn't a way to fetch all the certificates. One possible workaround is to use LDS instead of SDS, and configure your listener with all the certs needed, and do a listener update whenever that list changes. It's not a great solution, but it would work. |
@ggreenway so that means that if a new listener is loaded through LDS with a new secret, then it would request that delta secret from SDS accordingly? |
Kind of. It depends on how you think about it. For actually using a TLS certificate, it is ALWYS loaded specifically for the context in question as a BoringSSL If multiple parts of the envoy configuration want to use the same secrets (ie the request to the SDS server is identical), those get de-duped and only fetched once.
listenerA would issue a request that fetches all secrets, and listenerB would issue a request to fetch a single secret
There would be a filter-state with a fixed name. If it has been set, it will contain the name of the secret to use. This name will need to match the name that the SDS server associated with the secret.
No, LDS/CDS can only be from one mgmt server, because there is a single list of listeners and a single list of clusters in envoy. But TLS-using-SDS does support different mgmt servers for each TLS context; we should not discard that support here. |
Ok, I guess that gives me good idea about why not using dynamic_resources config, thanks.
Could we instead make current
Still not sure I get this part. Assuming Envoy has two listeners that both enabled Additional question, if |
What I'm saying is that there's no need to make the filter-state key configurable. The key should be fixed, and if it is set, then it will be used. If it is not set, the current behavior will be used. Without something new to envoy (new listener filter, etc) to set it, it will never be present and so the current behavior will be used. Filter-state is used for filters to pass information to each other. How this works is setup in code, not configuration. That's just how filter-state is used in Envoy. But if you want to use a match-tree, that will definitely need configuration. But one thing to keep in mind is that either a match-tree or filter-state is not directly related to SDS; both would work with other methods of adding certificates (such as pointing to a list of certs on the filesystem). The process for loading certificates (which happens at config-load time) and selecting a certificate for a connection (done at TLS handshake time) are completely separate processes. There are different ways to provide certs (SDS, filesystem) and ways to select a cert (built-in SNI-based selection, custom handshaker). These two concerns are not tightly coupled.
No, envoy would send one SDS request, receive the response, and update both TLS contexts from the same response.
I don't know yet. I want input from @adisuissa on how the SDS protocol should be setup for this use case. We just need some way to signal to the SDS server that this is the intent. |
My motivation was having a more user-friendly interface, but I guess I can give this up.
Yes, I think this is a cool feature to include and I can find some useful use cases with this. Would supporting this require making
Got it, so as long as there's at least one occasion that enabled |
No; the name field is part of the SDS config. The matcher is selecting which certificate to select for a specific TLS handshake. These are unrelated.
Actually, I misnamed it in the snippet above. It should actually be named something like |
If that's the case, would it still be true that Envoy sends a single fetch request, for all secrets? It seems like now the |
Envoy will send a single SDS request for each unique request. Any identical requests will be combined. So if you put the same |
So practically, to achieve dynamic secret selection from an unknown set of secrets, I would: set |
Yes, I think that's correct. |
Some more follow up questions then:
As a next step, I would like to support on-the-fly fetching of secret from SDS, meaning that if the secret isn't available at Envoy at the time of TLS handshake, then Envoy would fetch it from SDS and pause TLS handshake. Does this API model support this? |
Then those are different requests, so you'll end up with 2 SDS server requests, one for each mode.
No, listener name is not related to SDS requests.
Each TLS context has 1 or more certificates. When they are loaded, we figure out which SANs each one has. When a connection comes in, the correct one is chosen based on SNI. I'm not sure what you mean exactly by "buckets".
That's a completely different feature than what we've been discussing here. The closest thing I can think of is |
I thought
I meant different
Forget about buckets, I'm trying to understand the practice behind this API design. Envoy would request secrets from SDS, and let's say that the returned collection is relevant to both listenerA and listenerB, but not to listenerC. What would make those secrets available to be chosen only by listenerA/B but not C?
Referring to above comment about the full requirements I'm looking from this API change: #25958 (comment) |
That's an interesting point. I'm not sure if it ends up being a different request or not. If not, I don't think we even need an option; we could just allow multiple secrets anytime the SDS server sends them in a response (for TLS certificates; other secret types would need to be evaluated for whether that makes sense).
Yes, the
The SDS configuration in the TLS context specifies which secrets that context gets. If A and B should get the same secrets and C a different set, then A and B should use the same name in the SDS config, and C should use a different name. Then it's up to the SDS server to use those name values to respond with the correct set of secrets. |
Right, but that is a different feature than either "fetch all" or "dynamically select a cert". That's a 3rd feature that we haven't discussed how to do yet. |
I meant regarding internal implementation of Envoy - what enforces this so listenerA/B can't access C's secrets?
How is it different than 2nd feature from that comment? "If it is not, then an on-demand request to the SDS will take place, asking for that secret, and the TLS handshake will be paused until the SDS responds with the required cert.". |
They're stored in the objects created for the TLS context. They just can't be found in the wrong place, because they're not there.
This API change doesn't help or prevent fetching secrets on-demand. They're not really related. |
There would be reuse of memory though? Or will each TLS context have its own set of secrets? I mean, considering SDS responded with 1000 secrets that are relevant to both listenerA and B, would it require x2 memory? |
That's a very long thread and thank you @ggreenway for shepherding this. I just wanted to mention something that may be relevant to this discussion from the xDS protocol perspective. |
Yes, this is the case, but this will always be the case (for any of the proposed solutions in this thread). My rough guess is that each TLS cert will cost around 10kb or so of memory per TLS context it is used in. If you're worried about memory use, I'd start by measuring the usage, which can be done without any code changes by configuring a bunch of certs using existing mechanisms. |
@adisuissa I agree that glob collections look like a reasonable way to specify this. Are there any compatibility issues we need to worry about? Or would it be ok to modify envoy to always accept multiple TLS certs in any SDS response for TLS certs? |
Why would this be the case if we were to use the approach of having a single global collection (as in LDS/CDS)?
Seems like it's an API proposal, is this pattern currently in use with Envoy? Anyways, to use this pattern, we would have to somehow signal Envoy that a global collection is required, so there would be a requirement to add some boolean config to tls_context? |
Read the code for the tls transport socket, and see how the secrets are used. There's not really a way to share them between contexts. |
So no matter how certificates end up in Envoy, whether they are fetched by SDS, or statically referenced from file, etc.. each TLS context will always load all secrets? Is this something that can theoretically be changed or implemented in another way? |
In theory, if ALL parameters of the tls context were identical, you may be able to share those between TLS contexts, but it would make the implementation more complicated, and there may be complicated lifetime issues to solve. If ANY parameter (explicit or implicit) of the TLS context is different, then I don't think they can be shared. |
Bringing up again our discussion about scoped secrets with a small question. I can see that I'm trying to understand the difference when trying to apply the same to dynamic resources (which this discussion previously ruled out), would it be different simply because the secrets need to be assigned to a specific TLS context when they are loaded? |
This issue has been automatically marked as stale because it has not had activity in the last 30 days. It will be closed in the next 7 days unless it is tagged "help wanted" or "no stalebot" or other activity occurs. Thank you for your contributions. |
This issue has been automatically closed because it has not had activity in the last 37 days. If this issue is still valid, please ping a maintainer and ask them to label it as "help wanted" or "no stalebot". Thank you for your contributions. |
Assuming I am using SDS and I have on my disk a lot of certificates where the name of the certificate is a variable "x.cer".
I have a listener filter that would dynamically, per connection, resolve the value of x.
Would it be possible to apply downstream TLS context and dynamically select the presented certificate, based on the resolved value? Thanks
The text was updated successfully, but these errors were encountered: