Skip to content
This repository was archived by the owner on Dec 18, 2018. It is now read-only.

Allow to replace the server cert at runtime #2103

Closed
iftahbe opened this issue Oct 8, 2017 · 34 comments
Closed

Allow to replace the server cert at runtime #2103

iftahbe opened this issue Oct 8, 2017 · 34 comments

Comments

@iftahbe
Copy link

iftahbe commented Oct 8, 2017

Right now, the SSL certificate that Kestrel will use is here:

https://github.com/aspnet/KestrelHttpServer/blob/dev/src/Kestrel.Https/Internal/HttpsConnectionAdapter.cs#L25-L26

There are certain scenarios that we want to support replacing the certificate while the server is still running. This can be done quite easily if we could set the _serverCertificate so all new connections will be able to use that.

Scenarios for that include using Let's Encrypt certificates and not having to restart every 3 months.

While making it public is one option, I think it would be better to not hold a reference to the _serverCertificate from the options but use the value in the options itself. That will mean that the caller could hold on to the options and change the certificate value used without messing with any internal state.

@Tratcher
Copy link
Member

Tratcher commented Oct 8, 2017

As an alternative, we hope to get SNI support at some point and one of the API proposals is for a callback where you can select your own cert for each connection. This would allow you to change the cert dynamically.

@Drawaes
Copy link
Contributor

Drawaes commented Oct 8, 2017

I would say at worst, this should wait until the "option bag" for SslStream is completed, then you could expose that or pass it into Kestrel. At best you will get the callback as mentioned above...

@Tratcher
Copy link
Member

Tratcher commented Oct 8, 2017

I doubt the new options bag will be exposed by kestrel APIs. For now it's Core only.

@davidfowl
Copy link
Member

The middleware could expose a callback today but before we do anything, somebody should hack together a lets encrypt connection adapter for kestrel so that we can see all of the touch points.

@ayende
Copy link

ayende commented Oct 9, 2017

I implemented a very rough draft here:
https://github.com/ayende/Kestrel.LetsEncrypt

No error handling and this is mostly to see that I could do it.
Pretty much the only thing that I actually needed to do was to change the way Kestrel is getting the certificate. But in order to do that I had to pretty much bring all of Kestrel.Https

@davidfowl
Copy link
Member

@ayende Sweet!

But in order to do that I had to pretty much bring all of Kestrel.Https

Yea, but that's not a big deal it's only a few files. This should also let us prove out if a callback is enough.

@ayende
Copy link

ayende commented Oct 10, 2017

A callback for the certificate would be enough, yes.

@ayende
Copy link

ayende commented Oct 10, 2017

The major issue here is that we need some way to prove to Let's Encrypt that we are the owners of the domain. We can do that in various ways, but the easiest from our perspective at this point is to use the http-01 challenge.

This basically means that we ask Let's Encrypt for a certificate, and we need to prove we own this domain. We do that by getting a token from Let's Encrypt and asking it to call back to us (over HTTP, using port 80) and thus proving how this can happen.

I thought about having a kestrel instance that is always on using port 80, but it seems easier to just open it to the few seconds we need to do the verification.

It would probably be easier to do this using DNS, but that require you to use some API and bind you to a specific DNS provider.

I couldn't find a way to make Kestrel route based on the listen source anyway, and I don't think it matters too much.

@Drawaes
Copy link
Contributor

Drawaes commented Oct 10, 2017

https://github.com/dotnet/corefx/issues/22510

Can't use protected data. Like secure string it either throws or just isn't (secure)

@Drawaes
Copy link
Contributor

Drawaes commented Oct 10, 2017

Let me qualify that. On anything other than windows

@ayende
Copy link

ayende commented Oct 10, 2017

The actual problem here is that we need to persist the cert to disk. We can do that with OS permissions so only the current user can touch it, or provide callbacks to the user to handle caching the certificate if they want to customize that (such as storing in a vault).

@Drawaes
Copy link
Contributor

Drawaes commented Oct 10, 2017

Why does it need to go to disk? Can't you just use the bytes [] ?

@ayende
Copy link

ayende commented Oct 10, 2017

Let's Encrypt encourages you to keep the same certificate around, rather than renew it on every start.
Otherwise, you might hit rate limits.

@Drawaes
Copy link
Contributor

Drawaes commented Oct 10, 2017

I guess that's a problem for people with crappy code that restart often of which I am sure yours is not ;). Maybe make an option to "persist certificates to disk"

@ayende
Copy link

ayende commented Oct 10, 2017

@Drawaes Consider the case of a user running in development mode.
They still want to use HTTPS, and while they can run with a self signed, it is much easier all around to be able to use a real one from Let's Encrypt. In that case, it is easy to have a debug / run cycle that will hit the limits.

For that matter, even CI that continuously deploy will have to deal with this, in production.
I think that a better option would indeed be to update the code there to use a callback, instead of making this decision.

@ayende
Copy link

ayende commented Oct 10, 2017

I updated the code, so this should be clearer now.

@Drawaes
Copy link
Contributor

Drawaes commented Oct 10, 2017

Yeah dev it's fine...

@Soarc
Copy link

Soarc commented Oct 10, 2017

@ayende for development mode user could use staging let's encrypt environment.

@Drawaes
Copy link
Contributor

Drawaes commented Oct 10, 2017

Actually the more I think about it, when does a Dev have a publicly facing DNS to get a cert in the first place?

@Soarc
Copy link

Soarc commented Oct 11, 2017

@Drawaes In my case when Dev is developing let's encrypt's automated certificate renewal :)

@benaadams
Copy link
Contributor

Let's Encrypt encourages you to keep the same certificate around, rather than renew it on every start.
Otherwise, you might hit rate limits.

They only issue domain validated certs and only for 90 days at a time. Rate is 5 times a week for same cert; 20 times for domain and all its sub-domains; which is quite low for a test server - so you'd want to be caching it.

@ayende's use ASP.NET's IDataProtectionProvider and IDataProtector?

@ayende
Copy link

ayende commented Oct 11, 2017

@Soarc Except that you want to hit your website in the browser (or from you manager's browser) and have it Just Work.

@Drawaes dynamic DNS would do quite well for these cases. Or CI deployments, etc.

@benaadams Yes, you have to cache that.
I couldn't find how IDataProtecter would work on Linux, and anyway, I just externalize all of that so user can handle it on their own.

@benaadams
Copy link
Contributor

It works via DI so is its externalized to the users choice of mechanism; though you'd probably need to change the LetsEncryptCertificateFetcher to a service to grab it?

@ayende
Copy link

ayende commented Oct 11, 2017

The problem is that at this point you'll need to provide Not just how to protect it but also where to protect it.
The fetcher is also going to start a whole new Kestrel instance to do the http-01 validation, so confusing with regards to whose service provider to use.

@benaadams
Copy link
Contributor

Apache has gone built-in for let's encrypt https://letsencrypt.org/2017/10/17/acme-support-in-apache-httpd.html

@muratg
Copy link
Contributor

muratg commented Nov 27, 2017

@joshfree To implement this, we'll need SNI on Core to support something like this in a future release. Heads-up.

cc @shirhatti

@joshfree
Copy link
Member

joshfree commented Dec 4, 2017

@saurabh500 @corivera

@saurabh500
Copy link

SNI uses the SSLStream from .Net Core to transmit data. I am not sure how SNI is relevant here.

@muratg
Copy link
Contributor

muratg commented Dec 4, 2017

@saurabh500 Proposal at this issue would possibly enable this. @Tratcher can share some more details.

@shirhatti @DamianEdwards Let's decide if we want this feature in 2.2 which would drive feature requirements from the CoreFX side.

@Tratcher
Copy link
Member

Tratcher commented Dec 4, 2017

We know this feature is not the intended purpose of SNI, however the the proposed SNI API with the delegate callback would also enable this scenario.

@saurabh500
Copy link

Ah OK. The SNI here is Server Name Indication and not the SNI that SQL server uses. SNI in Sql server is an acronym for SQL Network Interface. @joshfree We are not the right team here :)

@Tratcher
Copy link
Member

Tratcher commented Dec 4, 2017

@muratg
Copy link
Contributor

muratg commented Feb 13, 2018

Closing this one. We'll revisit the bigger scenario in the future.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

10 participants