This repository has been archived by the owner on Nov 21, 2018. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 109
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Bind Kestrel options to config by default (#30).
- Loading branch information
Cesar Blum Silveira
committed
Apr 25, 2017
1 parent
98b1945
commit 60e4917
Showing
10 changed files
with
372 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
<Project Sdk="Microsoft.NET.Sdk.Web"> | ||
|
||
<Import Project="..\..\build\dependencies.props" /> | ||
|
||
<PropertyGroup> | ||
<TargetFramework>netcoreapp2.0</TargetFramework> | ||
<UserSecretsId>aspnetcore-MetaPackagesSampleApp-20170421155031</UserSecretsId> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<Folder Include="wwwroot\" /> | ||
<Content Include="testCert.pfx" CopyToOutputDirectory="PreserveNewest" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<ProjectReference Include="..\..\src\Microsoft.AspNetCore\Microsoft.AspNetCore.csproj" /> | ||
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="$(AspNetCoreVersion)" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<DotNetCliToolReference Include="Microsoft.Extensions.SecretManager.Tools" Version="$(AspNetCoreVersion)" /> | ||
</ItemGroup> | ||
|
||
</Project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
// Copyright (c) .NET Foundation. All rights reserved. | ||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | ||
|
||
using System; | ||
using Microsoft.AspNetCore; | ||
using Microsoft.AspNetCore.Http; | ||
|
||
namespace AppSettings | ||
{ | ||
public class Program | ||
{ | ||
public static void Main(string[] args) | ||
{ | ||
using (WebHost.Start(context => context.Response.WriteAsync("Hello, World!"))) | ||
{ | ||
Console.WriteLine("Running application: Press any key to shutdown..."); | ||
Console.ReadKey(); | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
{ | ||
"iisSettings": { | ||
"windowsAuthentication": false, | ||
"anonymousAuthentication": true, | ||
"iisExpress": { | ||
"applicationUrl": "http://localhost:53434/", | ||
"sslPort": 0 | ||
} | ||
}, | ||
"profiles": { | ||
"IIS Express": { | ||
"commandName": "IISExpress", | ||
"launchBrowser": true, | ||
"environmentVariables": { | ||
"ASPNETCORE_ENVIRONMENT": "Development" | ||
} | ||
}, | ||
"AppSettings": { | ||
"commandName": "Project", | ||
"launchBrowser": true, | ||
"environmentVariables": { | ||
"ASPNETCORE_ENVIRONMENT": "Development" | ||
}, | ||
"applicationUrl": "http://localhost:53435" | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
{ | ||
"Kestrel": { | ||
"EndPoints": { | ||
"Http": { | ||
"Address": "127.0.0.1", | ||
"Port": 8081 | ||
}, | ||
"HttpsInlineCertFile": { | ||
"Address": "127.0.0.1", | ||
"Port": 8082, | ||
"Certificate": { | ||
"Source": "File", | ||
"Path": "testCert.pfx", | ||
// TODO: remove when dotnet user-secrets is working again | ||
"Password": "testPassword" | ||
} | ||
}, | ||
// Add testCert.pfx to the current user's certificate store to enable this scenario. | ||
// "HttpsInlineCertStore": { | ||
// "Address": "127.0.0.1", | ||
// "Port": 8083, | ||
// "Certificate": { | ||
// "Source": "Store", | ||
// "Subject": "localhost", | ||
// "StoreName": "My", | ||
// "StoreLocation": "CurrentUser" | ||
// } | ||
// }, | ||
"HttpsCertFile": { | ||
"Address": "127.0.0.1", | ||
"Port": 8084, | ||
"Certificate": "TestCert" | ||
}, | ||
// Add testCert.pfx to the current user's certificate store to enable this scenario. | ||
// "HttpsCertStore": { | ||
// "Address": "127.0.0.1", | ||
// "Port": 8085, | ||
// "Certificate": "TestCertInStore" | ||
// } | ||
} | ||
}, | ||
"Certificates": { | ||
"TestCert": { | ||
"Source": "File", | ||
"Path": "testCert.pfx", | ||
// TODO: remove when dotnet user-secrets is working again | ||
"Password": "testPassword" | ||
}, | ||
// Add testCert.pfx to the current user's certificate store to enable this scenario. | ||
// "TestCertInStore": { | ||
// "Source": "Store", | ||
// "Subject": "localhost", | ||
// "StoreName": "My", | ||
// "StoreLocation": "CurrentUser" | ||
// } | ||
} | ||
} |
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
// Copyright (c) .NET Foundation. All rights reserved. | ||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | ||
|
||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Security.Cryptography.X509Certificates; | ||
using Microsoft.Extensions.Configuration; | ||
|
||
namespace Microsoft.AspNetCore | ||
{ | ||
/// <summary> | ||
/// A helper class to load certificates from files and certificate stores based on <seealso cref="IConfiguration"/> data. | ||
/// </summary> | ||
public static class CertificateLoader | ||
{ | ||
/// <summary> | ||
/// Loads one or more certificates from a single source. | ||
/// </summary> | ||
/// <param name="certificateConfiguration">An <seealso cref="IConfiguration"/> with information about a certificate source.</param> | ||
/// <param name="password">The certificate password, in case it's being loaded from a file.</param> | ||
/// <returns>The loaded certificates.</returns> | ||
public static X509Certificate2 Load(IConfiguration certificateConfiguration, string password = null) | ||
{ | ||
var sourceKind = certificateConfiguration.GetValue<string>("Source"); | ||
|
||
CertificateSource certificateSource; | ||
switch (sourceKind) | ||
{ | ||
case "File": | ||
certificateSource = new CertificateFileSource(password); | ||
break; | ||
case "Store": | ||
certificateSource = new CertificateStoreSource(); | ||
break; | ||
default: | ||
throw new InvalidOperationException($"Invalid certificate source kind: {sourceKind}"); | ||
} | ||
|
||
certificateConfiguration.Bind(certificateSource); | ||
return certificateSource.Load(); | ||
} | ||
|
||
/// <summary> | ||
/// Loads all certificates specified in an <seealso cref="IConfiguration"/>. | ||
/// </summary> | ||
/// <param name="configurationRoot">The root <seealso cref="IConfiguration"/>.</param> | ||
/// <returns> | ||
/// A dictionary mapping certificate names to loaded certificates. | ||
/// </returns> | ||
public static Dictionary<string, X509Certificate2> LoadAll(IConfiguration configurationRoot) | ||
{ | ||
var certificates = configurationRoot.GetSection("Certificates"); | ||
var loadedCertificates = new Dictionary<string, X509Certificate2>(); | ||
|
||
foreach (var certificateSource in certificates.GetChildren()) | ||
{ | ||
var name = certificateSource.Key; | ||
loadedCertificates[name] = Load(certificateSource, configurationRoot[$"Certificates:{name}:Password"]); | ||
} | ||
|
||
return loadedCertificates; | ||
} | ||
|
||
private abstract class CertificateSource | ||
{ | ||
public string Source { get; set; } | ||
|
||
public abstract X509Certificate2 Load(); | ||
} | ||
|
||
private class CertificateFileSource : CertificateSource | ||
{ | ||
private readonly string _password; | ||
|
||
public CertificateFileSource(string password) | ||
{ | ||
_password = password; | ||
} | ||
|
||
public string Path { get; set; } | ||
|
||
public override X509Certificate2 Load() | ||
{ | ||
var certificate = TryLoad(X509KeyStorageFlags.DefaultKeySet, out var error) | ||
?? TryLoad(X509KeyStorageFlags.UserKeySet, out error) | ||
#if NETCOREAPP2_0 | ||
?? TryLoad(X509KeyStorageFlags.EphemeralKeySet, out error) | ||
#endif | ||
; | ||
|
||
if (error != null) | ||
{ | ||
throw error; | ||
} | ||
|
||
return certificate; | ||
} | ||
|
||
private X509Certificate2 TryLoad(X509KeyStorageFlags flags, out Exception exception) | ||
{ | ||
try | ||
{ | ||
var loadedCertificate = new X509Certificate2(Path, _password); | ||
exception = null; | ||
return loadedCertificate; | ||
} | ||
catch (Exception e) | ||
{ | ||
exception = e; | ||
return null; | ||
} | ||
} | ||
} | ||
|
||
private class CertificateStoreSource : CertificateSource | ||
{ | ||
public string Subject { get; set; } | ||
public string StoreName { get; set; } | ||
public string StoreLocation { get; set; } | ||
|
||
public override X509Certificate2 Load() | ||
{ | ||
if (!Enum.TryParse(StoreLocation, true, out StoreLocation storeLocation)) | ||
{ | ||
throw new InvalidOperationException($"Invalid store location: {StoreLocation}"); | ||
} | ||
|
||
using (var store = new X509Store(StoreName, storeLocation)) | ||
{ | ||
store.Open(OpenFlags.ReadOnly); | ||
var foundCertificate = store.Certificates.Find(X509FindType.FindBySubjectName, Subject, validOnly: false) | ||
.OfType<X509Certificate2>() | ||
.OrderByDescending(certificate => certificate.NotAfter) | ||
.First(); | ||
|
||
#if NET46 | ||
store.Close(); | ||
#endif | ||
|
||
return foundCertificate; | ||
} | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
// Copyright (c) .NET Foundation. All rights reserved. | ||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | ||
|
||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Net; | ||
using System.Security.Cryptography.X509Certificates; | ||
using Microsoft.AspNetCore.Hosting; | ||
using Microsoft.AspNetCore.Server.Kestrel.Core; | ||
using Microsoft.AspNetCore.Server.Kestrel.Https; | ||
using Microsoft.Extensions.Configuration; | ||
using Microsoft.Extensions.DependencyInjection; | ||
using Microsoft.Extensions.Options; | ||
|
||
namespace Microsoft.AspNetCore | ||
{ | ||
/// <summary> | ||
/// Binds Kestrel configuration. | ||
/// </summary> | ||
public class KestrelServerOptionsSetup : IConfigureOptions<KestrelServerOptions> | ||
{ | ||
private IServiceProvider _services; | ||
|
||
/// <summary> | ||
/// Creates a new instance of <see cref="KestrelServerOptionsSetup"/>. | ||
/// </summary> | ||
/// <param name="services">An <seealso cref="IServiceProvider"/> instance.</param> | ||
public KestrelServerOptionsSetup(IServiceProvider services) | ||
{ | ||
_services = services; | ||
} | ||
|
||
/// <summary> | ||
/// Configures a <seealso cref="KestrelServerOptions"/> instance. | ||
/// </summary> | ||
/// <param name="options">The <seealso cref="KestrelServerOptions"/> to configure.</param> | ||
public void Configure(KestrelServerOptions options) | ||
{ | ||
options.ApplicationServices = _services; | ||
|
||
var configuration = _services.GetService<IConfiguration>(); | ||
BindConfiguration(options, configuration); | ||
} | ||
|
||
private static void BindConfiguration( | ||
KestrelServerOptions options, | ||
IConfiguration configurationRoot) | ||
{ | ||
var certificates = CertificateLoader.LoadAll(configurationRoot); | ||
var endPoints = configurationRoot.GetSection("Kestrel:EndPoints"); | ||
|
||
foreach (var endPoint in endPoints.GetChildren()) | ||
{ | ||
BindEndPoint(options, configurationRoot, endPoint, certificates); | ||
} | ||
} | ||
|
||
private static void BindEndPoint( | ||
KestrelServerOptions options, | ||
IConfiguration configurationRoot, | ||
IConfigurationSection endPoint, | ||
Dictionary<string, X509Certificate2> certificates) | ||
{ | ||
options.Listen(IPAddress.Parse(endPoint.GetValue<string>("Address")), endPoint.GetValue<int>("Port"), listenOptions => | ||
{ | ||
var certificateName = endPoint.GetValue<string>("Certificate"); | ||
|
||
X509Certificate2 endPointCertificate = null; | ||
if (certificateName != null) | ||
{ | ||
endPointCertificate = certificates[certificateName]; | ||
} | ||
else | ||
{ | ||
var certificate = endPoint.GetSection("Certificate"); | ||
|
||
if (certificate.GetChildren().Any()) | ||
{ | ||
var password = configurationRoot[$"Kestrel:EndPoints:{endPoint.Key}:Certificate:Password"]; | ||
endPointCertificate = CertificateLoader.Load(certificate, password); | ||
} | ||
} | ||
|
||
if (endPointCertificate != null) | ||
{ | ||
listenOptions.UseHttps(endPointCertificate); | ||
} | ||
}); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.