-
Notifications
You must be signed in to change notification settings - Fork 523
Conversation
What's the behavior for legacy clients that don't support SNI? I'm assuming since we fallback to using the ServerCertificate if specified or close the connection? |
/cc @Drawaes |
To be fair it's not "legacy clients" SNI isn't required and if you go via an IP there is no SNI to compare. The problem you have of course is if you are multi-homing you can't just use "certificate x" it might be the wrong website. So I think a default should be allowed but not required. It's a MAY include extension. As an aside I wonder if the possible cipher list could be supplied if you are reading the client hello anyway. That way say a smaller and more secure ECDSA cert could be supplied and an RSA supplied if the client doesn't support it. |
@Drawaes you'll have to ask on dotnet/corefx#28278 about exposing additional parameters. Right now they only give us a name. @shirhatti The current SslStream PR will invoke the callback with a null name if SNI was not supplied by the client. The developer is free to return a default certificate to continue, or return null or throw to abort. ServerCertificate is always ignored if a selector is provided (SslStream makes them mutually exclusive). |
var cert = selector(name); | ||
if (cert != null) | ||
{ | ||
EnsureCertificateIsAllowedForServerAuth(cert); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@blowdart it's potentially quite expensive to validate EKUs on every new connection. Can we exclude that for the advanced SNI scenario?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cache the result based on cert thumbprint?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@davidfowl what are the odds you'd allow registering and injecting a MemoryCache here? 😁
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Kestrel.Core shouldn't depend on the memory cache.
#else | ||
{ | ||
get => null; | ||
set => throw new PlatformNotSupportedException("This API requires targeting netcoreapp2.1"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not a huge fan of this throwing. If we made another callback API that let you control the cert per connection what would that look like?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated. Now a callback will be allowed even for 2.0. The difference is that for 2.0 we'll always invoke it with a null name before the handshake. The developer won't be able to distinguish between the client and server failing to provide SNI.
If server name could not be extracted or is not supported the callback will pass null string |
var serverCert = _serverCertificate; | ||
if (_serverCertificateSelector != null) | ||
{ | ||
serverCert = _serverCertificateSelector(null, null); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you think we should null check serverCert to get better errors when ServerCertificateSelector is not null but its return value is?
// If a selector is provided then ignore the cert, it may be a default cert. | ||
if (selector != null) | ||
{ | ||
// SslStream doesn't allow both. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm thinking it should be up to the caller of the HttpsConnectionAdapter ctor to ensure either ServerCertificateSelector or ServerCertificate is set but not both. I think it's better to throw if both are set instead of committing to this behavior of preferring the selector.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In UseHttps, is ServerCertificate already set by the time the config callback is called?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The default certs mess that up. See #2422
{ | ||
throw new ArgumentException(CoreStrings.ServiceCertificateRequired, nameof(options)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lol the good 'ol service certificate.
/// If the server certificate has an Extended Key Usage extension, the usages must include Server Authentication (OID 1.3.6.1.5.5.7.3.1). | ||
/// </para> | ||
/// </summary> | ||
public Func<string, X509Certificate2> ServerCertificateSelector { get; set; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider passing through a state object, either a Kestrel specific object or the SslStream itself. That's what SslStream passes to their callback (as an Object).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So more of a "sender" than a state object? I don't think a state object is necessary since any closures would be allocated only once for the lifetime of the server.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, a sender. Is there any connection specific state we think would be interesting to expose to this callback?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not just send the SslStream any properties are likely to be exposed on that in the future and means you won't have to change the kestrel side for every change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SslStream is largely uninitialized at this stage in the handshake. See dotnet/corefx#28278 (comment)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree that is the way it is today. But as we have client hello parsing started it's just a backdoor to other parts being parsed and populated ;) (don't tell anyone) and if these features are populated then there will be no lightup required in kestrel.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe the ConnectionContext would be useful.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated to pass through the sender. Trying to pass though per-connection state requires allocating a per-connection closure. We'll follow up in RC to see if that makes sense.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not that we have to do this now, but a per-connection closure allocation isn't a big deal in the context of initializing an HTTPS connection in Kestrel right now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I realized that looking at the client cert validation code. I added it back for now.
f4ad8ad
to
9c8095b
Compare
These aren't flaky tests, these look like regressions:
|
@pakrym this is the stuff you're updating right? Is this a matter of mismatched dependencies? |
The update is finished in |
samples/SampleApp/Startup.cs
Outdated
var localhostCert = CertificateLoader.LoadFromStoreCert("localhost", "My", StoreLocation.CurrentUser, allowInvalid: true); | ||
httpsOptions.ServerCertificateSelector = (features, name) => | ||
{ | ||
// TODO: Name check, multiple certs, null names. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this something we actually want to do in this sample?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No plans to do this in the sample, only showing where to do it. I'll remove the TODO.
@@ -29,14 +30,25 @@ public HttpsConnectionAdapterOptions() | |||
|
|||
/// <summary> | |||
/// <para> | |||
/// Specifies the server certificate used to authenticate HTTPS connections. | |||
/// Specifies the server certificate used to authenticate HTTPS connections. This is ignored if ServerCertificateSelector is set. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wish we didn't have to set an order of precedence. Are there any alternatives?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are several layers that can assign default certs, so the best option for now is to ignore it if a selector is provided. See #2422
listenOptions.UseHttps(httpsOptions => | ||
{ | ||
var localhostCert = CertificateLoader.LoadFromStoreCert("localhost", "My", StoreLocation.CurrentUser, allowInvalid: true); | ||
httpsOptions.ServerCertificateSelector = (features, name) => |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure I like this just being a settable property. What about adding a RegisterCertificateSelectorCallback() method that takes a state object as well?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Offline note: this is per endpoint, not per connection. No state is required to reduce allocations.
#2357 Here's what Kestrel's SNI support might look like.
This is based on the initial CoreFx design in dotnet/corefx#28278.