/// <summary> /// Configure Kestrel to use HTTPS. This does not use default certificates or other defaults specified via config or /// <see cref="KestrelServerOptions.ConfigureHttpsDefaults(Action{HttpsConnectionAdapterOptions})"/>. /// </summary> /// <param name="listenOptions">The <see cref="ListenOptions"/> to configure.</param> /// <param name="callbackOptions">Options for a per connection callback.</param> /// <returns>The <see cref="ListenOptions"/>.</returns> public static ListenOptions UseHttps(this ListenOptions listenOptions, TlsHandshakeCallbackOptions callbackOptions) { if (callbackOptions is null) { throw new ArgumentNullException(nameof(callbackOptions)); } if (callbackOptions.OnConnection is null) { throw new ArgumentException($"{nameof(TlsHandshakeCallbackOptions.OnConnection)} must not be null."); } var loggerFactory = listenOptions.KestrelServerOptions?.ApplicationServices.GetRequiredService <ILoggerFactory>() ?? NullLoggerFactory.Instance; listenOptions.IsTls = true; listenOptions.HttpsCallbackOptions = callbackOptions; listenOptions.Use(next => { // Set the list of protocols from listen options. // Set it inside Use delegate so Protocols and UseHttps can be called out of order. callbackOptions.HttpProtocols = listenOptions.Protocols; var middleware = new HttpsConnectionMiddleware(next, callbackOptions, loggerFactory); return(middleware.OnConnectionAsync); }); return(listenOptions); }
/// <summary> /// Configure Kestrel to use HTTPS. This does not use default certificates or other defaults specified via config or /// <see cref="KestrelServerOptions.ConfigureHttpsDefaults(Action{HttpsConnectionAdapterOptions})"/>. /// </summary> /// <param name="listenOptions">The <see cref="ListenOptions"/> to configure.</param> /// <param name="callbackOptions">Options for a per connection callback.</param> /// <returns>The <see cref="ListenOptions"/>.</returns> public static ListenOptions UseHttps(this ListenOptions listenOptions, TlsHandshakeCallbackOptions callbackOptions) { if (callbackOptions is null) { throw new ArgumentNullException(nameof(callbackOptions)); } if (callbackOptions.OnConnection is null) { throw new ArgumentException($"{nameof(TlsHandshakeCallbackOptions.OnConnection)} must not be null."); } if (listenOptions.Protocols.HasFlag(HttpProtocols.Http3)) { throw new NotSupportedException($"{nameof(UseHttps)} with {nameof(TlsHandshakeCallbackOptions)} is not supported with HTTP/3."); } var loggerFactory = listenOptions.KestrelServerOptions?.ApplicationServices.GetRequiredService <ILoggerFactory>() ?? NullLoggerFactory.Instance; listenOptions.IsTls = true; listenOptions.Use(next => { // Set the list of protocols from listen options callbackOptions.HttpProtocols = listenOptions.Protocols; var middleware = new HttpsConnectionMiddleware(next, callbackOptions, loggerFactory); return(middleware.OnConnectionAsync); }); return(listenOptions); }
/// <summary> /// Configure Kestrel to use HTTPS. /// </summary> /// <param name="listenOptions">The <see cref="ListenOptions"/> to configure.</param> /// <param name="httpsOptionsCallback">Callback to configure HTTPS options.</param> /// <param name="state">State for the <paramref name="httpsOptionsCallback"/>.</param> /// <param name="handshakeTimeout">Specifies the maximum amount of time allowed for the TLS/SSL handshake. This must be positive and finite.</param> /// <returns>The <see cref="ListenOptions"/>.</returns> internal static ListenOptions UseHttps(this ListenOptions listenOptions, HttpsOptionsCallback httpsOptionsCallback, object state, TimeSpan handshakeTimeout) { var loggerFactory = listenOptions.KestrelServerOptions?.ApplicationServices.GetRequiredService <ILoggerFactory>() ?? NullLoggerFactory.Instance; listenOptions.IsTls = true; listenOptions.Use(next => { var middleware = new HttpsConnectionMiddleware(next, httpsOptionsCallback, state, handshakeTimeout, loggerFactory); return(middleware.OnConnectionAsync); }); return(listenOptions); }
/// <summary> /// Configure Kestrel to use HTTPS. /// </summary> /// <param name="listenOptions">The <see cref="ListenOptions"/> to configure.</param> /// <param name="httpsOptions">Options to configure HTTPS.</param> /// <returns>The <see cref="ListenOptions"/>.</returns> public static ListenOptions UseHttps(this ListenOptions listenOptions, HttpsConnectionAdapterOptions httpsOptions) { var loggerFactory = listenOptions.KestrelServerOptions?.ApplicationServices.GetRequiredService <ILoggerFactory>() ?? NullLoggerFactory.Instance; listenOptions.IsTls = true; listenOptions.Use(next => { // Set the list of protocols from listen options httpsOptions.HttpProtocols = listenOptions.Protocols; var middleware = new HttpsConnectionMiddleware(next, httpsOptions, loggerFactory); return(middleware.OnConnectionAsync); }); return(listenOptions); }
public async Task <EndPoint> BindAsync(EndPoint endPoint, MultiplexedConnectionDelegate multiplexedConnectionDelegate, ListenOptions listenOptions, CancellationToken cancellationToken) { if (_multiplexedTransportFactory is null) { throw new InvalidOperationException($"Cannot bind with {nameof(MultiplexedConnectionDelegate)} no {nameof(IMultiplexedConnectionListenerFactory)} is registered."); } var features = new FeatureCollection(); // HttpsOptions or HttpsCallbackOptions should always be set in production, but it's not set for InMemory tests. // The QUIC transport will check if TlsConnectionCallbackOptions is missing. if (listenOptions.HttpsOptions != null) { var sslServerAuthenticationOptions = HttpsConnectionMiddleware.CreateHttp3Options(listenOptions.HttpsOptions); features.Set(new TlsConnectionCallbackOptions { ApplicationProtocols = sslServerAuthenticationOptions.ApplicationProtocols ?? new List <SslApplicationProtocol> { SslApplicationProtocol.Http3 }, OnConnection = (context, cancellationToken) => ValueTask.FromResult(sslServerAuthenticationOptions), OnConnectionState = null, }); } else if (listenOptions.HttpsCallbackOptions != null) { features.Set(new TlsConnectionCallbackOptions { ApplicationProtocols = new List <SslApplicationProtocol> { SslApplicationProtocol.Http3 }, OnConnection = (context, cancellationToken) => { return(listenOptions.HttpsCallbackOptions.OnConnection(new TlsHandshakeCallbackContext { ClientHelloInfo = context.ClientHelloInfo, CancellationToken = cancellationToken, State = context.State, Connection = new ConnectionContextAdapter(context.Connection), })); }, OnConnectionState = listenOptions.HttpsCallbackOptions.OnConnectionState, }); } var transport = await _multiplexedTransportFactory.BindAsync(endPoint, features, cancellationToken).ConfigureAwait(false); StartAcceptLoop(new GenericMultiplexedConnectionListener(transport), c => multiplexedConnectionDelegate(c), listenOptions.EndpointConfig); return(transport.EndPoint); }
public async Task <EndPoint> BindAsync(EndPoint endPoint, MultiplexedConnectionDelegate multiplexedConnectionDelegate, ListenOptions listenOptions, CancellationToken cancellationToken) { if (_multiplexedTransportFactory is null) { throw new InvalidOperationException($"Cannot bind with {nameof(MultiplexedConnectionDelegate)} no {nameof(IMultiplexedConnectionListenerFactory)} is registered."); } var features = new FeatureCollection(); // This should always be set in production, but it's not set for InMemory tests. // The transport will check if the feature is missing. if (listenOptions.HttpsOptions != null) { features.Set(HttpsConnectionMiddleware.CreateHttp3Options(listenOptions.HttpsOptions)); } var transport = await _multiplexedTransportFactory.BindAsync(endPoint, features, cancellationToken).ConfigureAwait(false); StartAcceptLoop(new GenericMultiplexedConnectionListener(transport), c => multiplexedConnectionDelegate(c), listenOptions.EndpointConfig); return(transport.EndPoint); }
public SniOptionsSelector( string endpointName, Dictionary <string, SniConfig> sniDictionary, ICertificateConfigLoader certifcateConfigLoader, HttpsConnectionAdapterOptions fallbackHttpsOptions, HttpProtocols fallbackHttpProtocols, ILogger <HttpsConnectionMiddleware> logger) { _endpointName = endpointName; _fallbackServerCertificateSelector = fallbackHttpsOptions.ServerCertificateSelector; _onAuthenticateCallback = fallbackHttpsOptions.OnAuthenticate; foreach (var(name, sniConfig) in sniDictionary) { var sslOptions = new SslServerAuthenticationOptions { ServerCertificate = certifcateConfigLoader.LoadCertificate(sniConfig.Certificate, $"{endpointName}:Sni:{name}"), EnabledSslProtocols = sniConfig.SslProtocols ?? fallbackHttpsOptions.SslProtocols, CertificateRevocationCheckMode = fallbackHttpsOptions.CheckCertificateRevocation ? X509RevocationMode.Online : X509RevocationMode.NoCheck, }; if (sslOptions.ServerCertificate is null) { if (fallbackHttpsOptions.ServerCertificate is null && _fallbackServerCertificateSelector is null) { throw new InvalidOperationException(CoreStrings.NoCertSpecifiedNoDevelopmentCertificateFound); } if (_fallbackServerCertificateSelector is null) { // Cache the fallback ServerCertificate since there's no fallback ServerCertificateSelector taking precedence. sslOptions.ServerCertificate = fallbackHttpsOptions.ServerCertificate; } } if (sslOptions.ServerCertificate != null) { // This might be do blocking IO but it'll resolve the certificate chain up front before any connections are // made to the server sslOptions.ServerCertificateContext = SslStreamCertificateContext.Create((X509Certificate2)sslOptions.ServerCertificate, additionalCertificates: null); } if (!certifcateConfigLoader.IsTestMock && sslOptions.ServerCertificate is X509Certificate2 cert2) { HttpsConnectionMiddleware.EnsureCertificateIsAllowedForServerAuth(cert2); } var clientCertificateMode = sniConfig.ClientCertificateMode ?? fallbackHttpsOptions.ClientCertificateMode; if (clientCertificateMode != ClientCertificateMode.NoCertificate) { sslOptions.ClientCertificateRequired = clientCertificateMode == ClientCertificateMode.AllowCertificate || clientCertificateMode == ClientCertificateMode.RequireCertificate; sslOptions.RemoteCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => HttpsConnectionMiddleware.RemoteCertificateValidationCallback( clientCertificateMode, fallbackHttpsOptions.ClientCertificateValidation, certificate, chain, sslPolicyErrors); } var httpProtocols = sniConfig.Protocols ?? fallbackHttpProtocols; httpProtocols = HttpsConnectionMiddleware.ValidateAndNormalizeHttpProtocols(httpProtocols, logger); HttpsConnectionMiddleware.ConfigureAlpn(sslOptions, httpProtocols); var sniOptions = new SniOptions(sslOptions, httpProtocols, clientCertificateMode); if (name.Equals(WildcardHost, StringComparison.Ordinal)) { _wildcardOptions = sniOptions; } else if (name.StartsWith(WildcardPrefix, StringComparison.Ordinal)) { // Only slice off 1 character, the `*`. We want to match the leading `.` also. _wildcardPrefixOptions.Add(name.Substring(1), sniOptions); } else { _exactNameOptions.Add(name, sniOptions); } } }
public (SslServerAuthenticationOptions, ClientCertificateMode) GetOptions(ConnectionContext connection, string serverName) { SniOptions?sniOptions = null; if (!string.IsNullOrEmpty(serverName) && !_exactNameOptions.TryGetValue(serverName, out sniOptions)) { foreach (var(suffix, options) in _wildcardPrefixOptions) { if (serverName.EndsWith(suffix, StringComparison.OrdinalIgnoreCase)) { sniOptions = options; break; } } } // Fully wildcarded ("*") options can be used even when given an empty server name. sniOptions ??= _wildcardOptions; if (sniOptions is null) { if (serverName is null) { // There was no ALPN throw new AuthenticationException(CoreStrings.FormatSniNotConfiguredToAllowNoServerName(_endpointName)); } else { throw new AuthenticationException(CoreStrings.FormatSniNotConfiguredForServerName(serverName, _endpointName)); } } connection.Features.Set(new HttpProtocolsFeature(sniOptions.HttpProtocols)); var sslOptions = sniOptions.SslOptions; if (sslOptions.ServerCertificate is null) { Debug.Assert(_fallbackServerCertificateSelector != null, "The cached SniOptions ServerCertificate can only be null if there's a fallback certificate selector."); // If a ServerCertificateSelector doesn't return a cert, HttpsConnectionMiddleware doesn't fallback to the ServerCertificate. sslOptions = CloneSslOptions(sslOptions); var fallbackCertificate = _fallbackServerCertificateSelector(connection, serverName); if (fallbackCertificate != null) { HttpsConnectionMiddleware.EnsureCertificateIsAllowedForServerAuth(fallbackCertificate); } sslOptions.ServerCertificate = fallbackCertificate; } if (_onAuthenticateCallback != null) { // From doc comments: "This is called after all of the other settings have already been applied." sslOptions = CloneSslOptions(sslOptions); _onAuthenticateCallback(connection, sslOptions); } return(sslOptions, sniOptions.ClientCertificateMode); }