Delay loading the dev cert #2422
This commit is contained in:
parent
953496a970
commit
2ee0d6e337
|
|
@ -2,12 +2,6 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using Microsoft.AspNetCore.Certificates.Generation;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Internal;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
|
||||
|
|
@ -24,33 +18,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
|
|||
public void Configure(KestrelServerOptions options)
|
||||
{
|
||||
options.ApplicationServices = _services;
|
||||
UseDefaultDeveloperCertificate(options);
|
||||
}
|
||||
|
||||
private void UseDefaultDeveloperCertificate(KestrelServerOptions options)
|
||||
{
|
||||
var logger = options.ApplicationServices.GetRequiredService<ILogger<KestrelServer>>();
|
||||
X509Certificate2 certificate = null;
|
||||
try
|
||||
{
|
||||
var certificateManager = new CertificateManager();
|
||||
certificate = certificateManager.ListCertificates(CertificatePurpose.HTTPS, StoreName.My, StoreLocation.CurrentUser, isValid: true)
|
||||
.FirstOrDefault();
|
||||
}
|
||||
catch
|
||||
{
|
||||
logger.UnableToLocateDevelopmentCertificate();
|
||||
}
|
||||
|
||||
if (certificate != null)
|
||||
{
|
||||
logger.LocatedDevelopmentCertificate(certificate);
|
||||
options.DefaultCertificate = certificate;
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.UnableToLocateDevelopmentCertificate();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -225,6 +225,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel
|
|||
// Specified
|
||||
httpsOptions.ServerCertificate = LoadCertificate(endpoint.Certificate, endpoint.Name)
|
||||
?? httpsOptions.ServerCertificate;
|
||||
|
||||
// Fallback
|
||||
Options.ApplyDefaultCert(httpsOptions);
|
||||
}
|
||||
|
||||
if (EndpointConfigurations.TryGetValue(endpoint.Name, out var configureEndpoint))
|
||||
|
|
|
|||
|
|
@ -3,12 +3,17 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using Microsoft.AspNetCore.Certificates.Generation;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Https;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Internal;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Microsoft.AspNetCore.Server.Kestrel.Core
|
||||
{
|
||||
|
|
@ -75,10 +80,15 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
|
|||
private Action<HttpsConnectionAdapterOptions> HttpsDefaults { get; set; } = _ => { };
|
||||
|
||||
/// <summary>
|
||||
/// The default server certificate for https endpoints. This is applied before HttpsDefaults.
|
||||
/// The default server certificate for https endpoints. This is applied lazily after HttpsDefaults and user options.
|
||||
/// </summary>
|
||||
internal X509Certificate2 DefaultCertificate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Has the default dev certificate load been attempted?
|
||||
/// </summary>
|
||||
internal bool IsDevCertLoaded { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Specifies a configuration Action to run for each newly created endpoint. Calling this again will replace
|
||||
/// the prior action.
|
||||
|
|
@ -105,10 +115,49 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
|
|||
|
||||
internal void ApplyHttpsDefaults(HttpsConnectionAdapterOptions httpsOptions)
|
||||
{
|
||||
httpsOptions.ServerCertificate = DefaultCertificate;
|
||||
HttpsDefaults(httpsOptions);
|
||||
}
|
||||
|
||||
internal void ApplyDefaultCert(HttpsConnectionAdapterOptions httpsOptions)
|
||||
{
|
||||
if (httpsOptions.ServerCertificate != null || httpsOptions.ServerCertificateSelector != null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
EnsureDefaultCert();
|
||||
|
||||
httpsOptions.ServerCertificate = DefaultCertificate;
|
||||
}
|
||||
|
||||
private void EnsureDefaultCert()
|
||||
{
|
||||
if (DefaultCertificate == null && !IsDevCertLoaded)
|
||||
{
|
||||
IsDevCertLoaded = true; // Only try once
|
||||
var logger = ApplicationServices.GetRequiredService<ILogger<KestrelServer>>();
|
||||
try
|
||||
{
|
||||
var certificateManager = new CertificateManager();
|
||||
DefaultCertificate = certificateManager.ListCertificates(CertificatePurpose.HTTPS, StoreName.My, StoreLocation.CurrentUser, isValid: true)
|
||||
.FirstOrDefault();
|
||||
|
||||
if (DefaultCertificate != null)
|
||||
{
|
||||
logger.LocatedDevelopmentCertificate(DefaultCertificate);
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.UnableToLocateDevelopmentCertificate();
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
logger.UnableToLocateDevelopmentCertificate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a configuration loader for setting up Kestrel.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -178,6 +178,7 @@ namespace Microsoft.AspNetCore.Hosting
|
|||
var options = new HttpsConnectionAdapterOptions();
|
||||
listenOptions.KestrelServerOptions.ApplyHttpsDefaults(options);
|
||||
configureOptions(options);
|
||||
listenOptions.KestrelServerOptions.ApplyDefaultCert(options);
|
||||
|
||||
if (options.ServerCertificate == null && options.ServerCertificateSelector == null)
|
||||
{
|
||||
|
|
@ -191,6 +192,7 @@ namespace Microsoft.AspNetCore.Hosting
|
|||
{
|
||||
var options = new HttpsConnectionAdapterOptions();
|
||||
listenOptions.KestrelServerOptions.ApplyHttpsDefaults(options);
|
||||
listenOptions.KestrelServerOptions.ApplyDefaultCert(options);
|
||||
|
||||
if (options.ServerCertificate == null && options.ServerCertificateSelector == null)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -65,7 +65,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
|||
[Fact]
|
||||
public void KestrelServerThrowsUsefulExceptionIfDefaultHttpsProviderNotAdded()
|
||||
{
|
||||
using (var server = CreateServer(CreateServerOptions(), throwOnCriticalErrors: false))
|
||||
var options = CreateServerOptions();
|
||||
options.IsDevCertLoaded = true; // Prevent the system default from being loaded
|
||||
using (var server = CreateServer(options, throwOnCriticalErrors: false))
|
||||
{
|
||||
server.Features.Get<IServerAddressesFeature>().Addresses.Add("https://127.0.0.1:0");
|
||||
|
||||
|
|
|
|||
|
|
@ -47,25 +47,56 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
|
|||
options.UseHttps();
|
||||
});
|
||||
|
||||
Assert.False(serverOptions.IsDevCertLoaded);
|
||||
|
||||
serverOptions.ListenLocalhost(5001, options =>
|
||||
{
|
||||
options.UseHttps(opt =>
|
||||
{
|
||||
Assert.Equal(defaultCert, opt.ServerCertificate);
|
||||
// The default cert is applied after UseHttps.
|
||||
Assert.Null(opt.ServerCertificate);
|
||||
});
|
||||
});
|
||||
Assert.False(serverOptions.IsDevCertLoaded);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ConfigureHttpsDefaultsOverridesDefaultCert()
|
||||
public void ConfigureHttpsDefaultsNeverLoadsDefaultCert()
|
||||
{
|
||||
var serverOptions = CreateServerOptions();
|
||||
var defaultCert = new X509Certificate2(TestResources.TestCertificatePath, "testPassword");
|
||||
serverOptions.DefaultCertificate = defaultCert;
|
||||
var testCert = new X509Certificate2(TestResources.TestCertificatePath, "testPassword");
|
||||
serverOptions.ConfigureHttpsDefaults(options =>
|
||||
{
|
||||
Assert.Equal(defaultCert, options.ServerCertificate);
|
||||
options.ServerCertificate = null;
|
||||
Assert.Null(options.ServerCertificate);
|
||||
options.ServerCertificate = testCert;
|
||||
options.ClientCertificateMode = ClientCertificateMode.RequireCertificate;
|
||||
});
|
||||
serverOptions.ListenLocalhost(5000, options =>
|
||||
{
|
||||
options.UseHttps(opt =>
|
||||
{
|
||||
Assert.Equal(testCert, opt.ServerCertificate);
|
||||
Assert.Equal(ClientCertificateMode.RequireCertificate, opt.ClientCertificateMode);
|
||||
});
|
||||
});
|
||||
// Never lazy loaded
|
||||
Assert.False(serverOptions.IsDevCertLoaded);
|
||||
Assert.Null(serverOptions.DefaultCertificate);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ConfigureCertSelectorNeverLoadsDefaultCert()
|
||||
{
|
||||
var serverOptions = CreateServerOptions();
|
||||
var testCert = new X509Certificate2(TestResources.TestCertificatePath, "testPassword");
|
||||
serverOptions.ConfigureHttpsDefaults(options =>
|
||||
{
|
||||
Assert.Null(options.ServerCertificate);
|
||||
Assert.Null(options.ServerCertificateSelector);
|
||||
options.ServerCertificateSelector = (features, name) =>
|
||||
{
|
||||
return testCert;
|
||||
};
|
||||
options.ClientCertificateMode = ClientCertificateMode.RequireCertificate;
|
||||
});
|
||||
serverOptions.ListenLocalhost(5000, options =>
|
||||
|
|
@ -73,12 +104,13 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
|
|||
options.UseHttps(opt =>
|
||||
{
|
||||
Assert.Null(opt.ServerCertificate);
|
||||
Assert.NotNull(opt.ServerCertificateSelector);
|
||||
Assert.Equal(ClientCertificateMode.RequireCertificate, opt.ClientCertificateMode);
|
||||
|
||||
// So UseHttps won't throw
|
||||
opt.ServerCertificate = defaultCert;
|
||||
});
|
||||
});
|
||||
// Never lazy loaded
|
||||
Assert.False(serverOptions.IsDevCertLoaded);
|
||||
Assert.Null(serverOptions.DefaultCertificate);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
|
|||
Loading…
Reference in New Issue