-
Notifications
You must be signed in to change notification settings - Fork 10.3k
Support certificate auto-rotation in Kestrel #32351
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
Comments
This is a feature we're considering, above and beyond kubernetes. We're not at the design stage yet, but it's on the radar |
We've moved this issue to the Backlog milestone. This means that it is not going to be worked on for the coming release. We will reassess the backlog following the current release and consider this item at that time. To learn more about our issue management process and to have better expectation regarding different types of issues you can read our Triage Process. |
@blowdart this is something I think we should support natively. We did work in .NET 5 to make Kestrel respect configuration reload but that doesn't work well for things in configuration that change without configuration itself changing. This is going to affect YARP as well @Tratcher. @aelij Are you using certmgr? |
@davidfowl No, we're planning on using the new Secrets Store CSI Driver once it's out of preview, which natively supports auto-rotation. |
Thanks for contacting us. We're moving this issue to the |
@blowdart Removing this from 6. Please move it back if you think it should get done. |
We've moved this issue to the Backlog milestone. This means that it is not going to be worked on for the coming release. We will reassess the backlog following the current release and consider this item at that time. To learn more about our issue management process and to have better expectation regarding different types of issues you can read our Triage Process. |
Certs can be configured either in code or via @aelij @craigktreasure Is |
|
@aelij Tell me more about the multiple certs? I assume you mean different certs for different endpoints? Yes, we're talking about the cert file changing in-place without a corresponding change to the path or (e.g.) appsettings.json. |
Yes |
In my case, it was single cert and the path was specified on disk via |
Excellent. If the |
That's at the System.IO layer, but we're working at the Microsoft.Extensions.FileProviders layer. We don't want to dig down in case the backing implementation isn't a disk.
No, that's what I expected. Seeing the redundant event message (which indicates that the new and old mtimes were the same) made me think there might be some other criteria.
Sorry, it's a long thread, but there are some notes above. The file layout is described here and the watch is on (in that example), (Note that @aelij, who has been exceptionally helpful, is on European time and is, hopefully, enjoying the weekend.) |
What we need is watching files that are actually sym linked to trigger the change token. It needs to work like a normal file change. I'm not sure we need to know if a file is a sym link or not, that might be useful in general but for this, it should just work if I watch a file and that file changes and it happens to be a link. If that behavior needs to be opt-in, it should be opt-in, but the complexity should be hidden in the watch implementation. |
That could be dotnet/runtime#55951 if it is executing fast enough, i.e: in milliseconds. But if this was tested manually then is unlikely.
|
Why does the reload work for normal AppSettings files mounted into a container in the same manner, but not in this case? |
@pinkfloydx33 that's what I'm trying to understand too. |
Figured this out over the weekend: the change event is for the target of the link and the timestamp check is on the link itself. (If there's some way to pull the path out of the event, I haven't found it, so I'm associating a path with each change token manually.) I'd like to update the mtime check to consider the target-of-link mtime as well, but (AFAICT) |
Where do those files sit in the directory hierarchy you listed above? Is there an appsettings.json next to each tls.key? |
In that particular example no, it's just the key. But generally that's exactly how I mount AppSettings files in a container (at /app/settings but same concepts apply). If I terminal into the pod and edit one of those files and look at the logs I can see my yarp gateway refresh within seconds. |
If the app is finding its appsettings.json using a path that doesn't require symlink resolution, I'd expect it to just work. Alternatively, have you enabled polling file watching? |
This thread is getting fairly long, so I'll attempt to summarize our current status. ProblemThe appsettings.json file for an aspnetcore app contains the path to a certificate file. We would like to reload endpoints using that certificate file if it changes on disk, even if there's no corresponding change to the appsettings file. The reload behavior will be the same as if the appsettings file had been updated with a new certificate path. File Layout
That is, The file change we expect to see in practice is an update of StatusAt present, aspnetcore adds a file watcher (either When using When using polling, the mtime of the resolved symlink target (i.e. As a result, aspnetcore does not reload the affected endpoints in either mode. Ways ForwardRegardless of which path we take, I'll need to update the code to stop instantiating Option: Do NothingIf we make no further changes (beyond the fix mentioned above), things won't work on k8s, which was the motivating scenario for this work. It will, however, result in a low-risk change with intelligible behavior. Option: Adjust the mtime checksWe could detect that the user has opted for polling file watching and skip the mtime check in that case. That would be enough to unblock the k8s scenario, but would likely increase CPU usage by making all file watching poll-based. Option: Remove the mtime checksIf we remove the mtime checks but allow and/or default to Option: Switch from mtime checks to SHAsObviously, this would use a lot more CPU. We can limit this by combining it with polling (i.e. so we hash at most every 4 seconds). Given that Draft: #50172 Option: Implement symlink support in kestrelWe have to (a) move watchers higher up the file hierarchy to detect changes to directories earlier in the path and write a bunch of abstraction-violating and error-prone code. It's doable but ugly and, as @davidfowl points out, at the wrong layer. Draft: #50074 Option: Implement symlink support in the runtimeAs complicated as the kestrel code is, it gets to make a bunch of simplifying assumptions. In the general case, we'd have to think about globbing and how to get a new The biggest risk here is that we're quite late in the 8.0 cycle, so there wouldn't be a lot of time to let this bake. With so many file systems to consider, there's quite a large test matrix and there's likely to be a bug tail as we discover special cases. ProposalPersonally, I think the most pragmatic solution would be to force polling for precisely certificate files (i.e. continuing to respect the user's environment variable for other files, including appsettings.json) and dropping the mtime check. In fairness, I should point out that @aelij suggested this long ago. As mentioned above, we probably want to start pulling from Thoughts? |
Thanks for the clear summary @amcasey. I support your proposal for .NET 8. Longer term, improving symlink support in the framework is likely the best option but it's too late in this release for that as you noted. I'd also be interested in whether removing the mtime checks actually works well, but would want us to understand all the debouncing logic fully if we want to go that route with confidence. |
There's little advantage to dropping the mtime check without also forcing polling since While we're talking about runtime improvements, it would be nice if the non-polling watcher just didn't send duplicate events. 😉 |
|
I made this change here. Unfortunately, I don't think we'll be able to take it for 8.0 because it will prevent us from using polling for specific files (the file watcher is shared by several consumers). Not having it is an abstraction violation, but fixing it doesn't fix the abstraction violation because we still pass the path directly to |
It's common for certificates to be rotated using symlinks and `FileSystemWatcher` doesn't resolve symlinks. Use polling, which does, but only for certificates since it has a CPU cost. Fixes dotnet#32351
It's common for certificates to be rotated using symlinks and `FileSystemWatcher` doesn't resolve symlinks. Use polling, which does, but only for certificates since it has a CPU cost. Fixes dotnet#32351
PR for polling and ignoring mtime: #50251 |
It took a bit of massaging, but I got @aelij's repro script working (he was naively taking for granted that I would know when and how to log in to azure 😆). With #50251, I'm seeing
🥳 |
I don't think there's "a usual". For example at my job by convention we put things into IOW mounting k8s certs within the content root is certainly possible but is no way guaranteed. The mount point may be dictated by devops, and/or those responsible for managing configuration and might not even be a developer concern. |
Yes, there should be no relation to the app root. |
It's in! @aelij I've already validated using your sample project (thanks again!), but it would be great if you could confirm that an upcoming nightly works for you. |
Not sure why merging #50251 didn't close this... |
I can confirm it's working. Thanks @amcasey! |
Is your feature request related to a problem? Please describe.
In Kubernetes, certificates are mounted as secret volumes, which can be configured to update automatically when the cert is rotated (e.g. from Key Vault). To achieve auto-rotation in Kestrel today, we need to hook up
ServerCertificateSelector
and listen to file changes (e.g. usingIFileProvider.Watch
).Describe the solution you'd like
Add a
HttpsConnectionAdapterOptions.ServerCertificatePath
property that would watch for file changes.The text was updated successfully, but these errors were encountered: