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

Commit 37bf7d5

Browse files
committed
Delay loading the dev cert #2422
1 parent 70ce044 commit 37bf7d5

File tree

6 files changed

+73
-46
lines changed

6 files changed

+73
-46
lines changed

src/Kestrel.Core/Internal/KestrelServerOptionsSetup.cs

Lines changed: 0 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,6 @@
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

44
using System;
5-
using System.Linq;
6-
using System.Security.Cryptography.X509Certificates;
7-
using Microsoft.AspNetCore.Certificates.Generation;
8-
using Microsoft.AspNetCore.Server.Kestrel.Internal;
9-
using Microsoft.Extensions.DependencyInjection;
10-
using Microsoft.Extensions.Logging;
115
using Microsoft.Extensions.Options;
126

137
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
@@ -24,33 +18,6 @@ public KestrelServerOptionsSetup(IServiceProvider services)
2418
public void Configure(KestrelServerOptions options)
2519
{
2620
options.ApplicationServices = _services;
27-
UseDefaultDeveloperCertificate(options);
28-
}
29-
30-
private void UseDefaultDeveloperCertificate(KestrelServerOptions options)
31-
{
32-
var logger = options.ApplicationServices.GetRequiredService<ILogger<KestrelServer>>();
33-
X509Certificate2 certificate = null;
34-
try
35-
{
36-
var certificateManager = new CertificateManager();
37-
certificate = certificateManager.ListCertificates(CertificatePurpose.HTTPS, StoreName.My, StoreLocation.CurrentUser, isValid: true)
38-
.FirstOrDefault();
39-
}
40-
catch
41-
{
42-
logger.UnableToLocateDevelopmentCertificate();
43-
}
44-
45-
if (certificate != null)
46-
{
47-
logger.LocatedDevelopmentCertificate(certificate);
48-
options.DefaultCertificate = certificate;
49-
}
50-
else
51-
{
52-
logger.UnableToLocateDevelopmentCertificate();
53-
}
5421
}
5522
}
5623
}

src/Kestrel.Core/KestrelConfigurationLoader.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,9 @@ public void Load()
225225
// Specified
226226
httpsOptions.ServerCertificate = LoadCertificate(endpoint.Certificate, endpoint.Name)
227227
?? httpsOptions.ServerCertificate;
228+
229+
// Fallback
230+
Options.ApplyDefaultCert(httpsOptions);
228231
}
229232

230233
if (EndpointConfigurations.TryGetValue(endpoint.Name, out var configureEndpoint))

src/Kestrel.Core/KestrelServerOptions.cs

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,17 @@
33

44
using System;
55
using System.Collections.Generic;
6+
using System.Linq;
67
using System.Net;
78
using System.Security.Cryptography.X509Certificates;
9+
using Microsoft.AspNetCore.Certificates.Generation;
810
using Microsoft.AspNetCore.Http;
911
using Microsoft.AspNetCore.Server.Kestrel.Https;
12+
using Microsoft.AspNetCore.Server.Kestrel.Internal;
1013
using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal;
1114
using Microsoft.Extensions.Configuration;
15+
using Microsoft.Extensions.DependencyInjection;
16+
using Microsoft.Extensions.Logging;
1217

1318
namespace Microsoft.AspNetCore.Server.Kestrel.Core
1419
{
@@ -75,10 +80,15 @@ public class KestrelServerOptions
7580
private Action<HttpsConnectionAdapterOptions> HttpsDefaults { get; set; } = _ => { };
7681

7782
/// <summary>
78-
/// The default server certificate for https endpoints. This is applied before HttpsDefaults.
83+
/// The default server certificate for https endpoints. This is applied lazily after HttpsDefaults and user options.
7984
/// </summary>
8085
internal X509Certificate2 DefaultCertificate { get; set; }
8186

87+
/// <summary>
88+
/// Has the default dev certificate load been attempted?
89+
/// </summary>
90+
internal bool IsDevCertLoaded { get; set; }
91+
8292
/// <summary>
8393
/// Specifies a configuration Action to run for each newly created endpoint. Calling this again will replace
8494
/// the prior action.
@@ -105,10 +115,50 @@ public void ConfigureHttpsDefaults(Action<HttpsConnectionAdapterOptions> configu
105115

106116
internal void ApplyHttpsDefaults(HttpsConnectionAdapterOptions httpsOptions)
107117
{
108-
httpsOptions.ServerCertificate = DefaultCertificate;
109118
HttpsDefaults(httpsOptions);
110119
}
111120

121+
internal void ApplyDefaultCert(HttpsConnectionAdapterOptions httpsOptions)
122+
{
123+
// TODO SNI: Also check for the server cert selector
124+
if (httpsOptions.ServerCertificate != null)
125+
{
126+
return;
127+
}
128+
129+
EnsureDefaultCert();
130+
131+
httpsOptions.ServerCertificate = DefaultCertificate;
132+
}
133+
134+
private void EnsureDefaultCert()
135+
{
136+
if (DefaultCertificate == null && !IsDevCertLoaded)
137+
{
138+
IsDevCertLoaded = true; // Only try once
139+
var logger = ApplicationServices.GetRequiredService<ILogger<KestrelServer>>();
140+
try
141+
{
142+
var certificateManager = new CertificateManager();
143+
DefaultCertificate = certificateManager.ListCertificates(CertificatePurpose.HTTPS, StoreName.My, StoreLocation.CurrentUser, isValid: true)
144+
.FirstOrDefault();
145+
146+
if (DefaultCertificate != null)
147+
{
148+
logger.LocatedDevelopmentCertificate(DefaultCertificate);
149+
}
150+
else
151+
{
152+
logger.UnableToLocateDevelopmentCertificate();
153+
}
154+
}
155+
catch
156+
{
157+
logger.UnableToLocateDevelopmentCertificate();
158+
}
159+
}
160+
}
161+
112162
/// <summary>
113163
/// Creates a configuration loader for setting up Kestrel.
114164
/// </summary>

src/Kestrel.Core/ListenOptionsHttpsExtensions.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,7 @@ public static ListenOptions UseHttps(this ListenOptions listenOptions, Action<Ht
178178
var options = new HttpsConnectionAdapterOptions();
179179
listenOptions.KestrelServerOptions.ApplyHttpsDefaults(options);
180180
configureOptions(options);
181+
listenOptions.KestrelServerOptions.ApplyDefaultCert(options);
181182

182183
if (options.ServerCertificate == null)
183184
{
@@ -191,6 +192,7 @@ internal static bool TryUseHttps(this ListenOptions listenOptions)
191192
{
192193
var options = new HttpsConnectionAdapterOptions();
193194
listenOptions.KestrelServerOptions.ApplyHttpsDefaults(options);
195+
listenOptions.KestrelServerOptions.ApplyDefaultCert(options);
194196

195197
if (options.ServerCertificate == null)
196198
{

test/Kestrel.Core.Tests/KestrelServerTests.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,9 @@ public void StartWithHttpsAddressConfiguresHttpsEndpoints()
6565
[Fact]
6666
public void KestrelServerThrowsUsefulExceptionIfDefaultHttpsProviderNotAdded()
6767
{
68-
using (var server = CreateServer(CreateServerOptions(), throwOnCriticalErrors: false))
68+
var options = CreateServerOptions();
69+
options.IsDevCertLoaded = true; // Prevent the system default from being loaded
70+
using (var server = CreateServer(options, throwOnCriticalErrors: false))
6971
{
7072
server.Features.Get<IServerAddressesFeature>().Addresses.Add("https://127.0.0.1:0");
7173

test/Kestrel.FunctionalTests/HttpsTests.cs

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -47,38 +47,41 @@ public void UseHttpsDefaultsToDefaultCert()
4747
options.UseHttps();
4848
});
4949

50+
Assert.False(serverOptions.IsDevCertLoaded);
51+
5052
serverOptions.ListenLocalhost(5001, options =>
5153
{
5254
options.UseHttps(opt =>
5355
{
54-
Assert.Equal(defaultCert, opt.ServerCertificate);
56+
// The default cert is applied after UseHttps.
57+
Assert.Null(opt.ServerCertificate);
5558
});
5659
});
60+
Assert.False(serverOptions.IsDevCertLoaded);
5761
}
5862

5963
[Fact]
60-
public void ConfigureHttpsDefaultsOverridesDefaultCert()
64+
public void ConfigureHttpsDefaultsNeverLoadsDefaultCert()
6165
{
6266
var serverOptions = CreateServerOptions();
63-
var defaultCert = new X509Certificate2(TestResources.TestCertificatePath, "testPassword");
64-
serverOptions.DefaultCertificate = defaultCert;
67+
var testCert = new X509Certificate2(TestResources.TestCertificatePath, "testPassword");
6568
serverOptions.ConfigureHttpsDefaults(options =>
6669
{
67-
Assert.Equal(defaultCert, options.ServerCertificate);
68-
options.ServerCertificate = null;
70+
Assert.Null(options.ServerCertificate);
71+
options.ServerCertificate = testCert;
6972
options.ClientCertificateMode = ClientCertificateMode.RequireCertificate;
7073
});
7174
serverOptions.ListenLocalhost(5000, options =>
7275
{
7376
options.UseHttps(opt =>
7477
{
75-
Assert.Null(opt.ServerCertificate);
78+
Assert.Equal(testCert, opt.ServerCertificate);
7679
Assert.Equal(ClientCertificateMode.RequireCertificate, opt.ClientCertificateMode);
77-
78-
// So UseHttps won't throw
79-
opt.ServerCertificate = defaultCert;
8080
});
8181
});
82+
// Never lazy loaded
83+
Assert.False(serverOptions.IsDevCertLoaded);
84+
Assert.Null(serverOptions.DefaultCertificate);
8285
}
8386

8487
[Fact]

0 commit comments

Comments
 (0)