parent
7a9707eb98
commit
8b7972064f
|
|
@ -5,6 +5,7 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Security.Authentication;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Https;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
|
||||
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
|
||||
|
|
@ -18,6 +19,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
|
|||
private const string EndpointDefaultsKey = "EndpointDefaults";
|
||||
private const string EndpointsKey = "Endpoints";
|
||||
private const string UrlKey = "Url";
|
||||
private const string ClientCertificateModeKey = "ClientCertificateMode";
|
||||
|
||||
private readonly IConfiguration _configuration;
|
||||
|
||||
|
|
@ -50,6 +52,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
|
|||
// "EndpointDefaults": {
|
||||
// "Protocols": "Http1AndHttp2",
|
||||
// "SslProtocols": [ "Tls11", "Tls12", "Tls13"],
|
||||
// "ClientCertificateMode" : "NoCertificate"
|
||||
// }
|
||||
private EndpointDefaults ReadEndpointDefaults()
|
||||
{
|
||||
|
|
@ -57,7 +60,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
|
|||
return new EndpointDefaults
|
||||
{
|
||||
Protocols = ParseProtocols(configSection[ProtocolsKey]),
|
||||
SslProtocols = ParseSslProcotols(configSection.GetSection(SslProtocolsKey))
|
||||
SslProtocols = ParseSslProcotols(configSection.GetSection(SslProtocolsKey)),
|
||||
ClientCertificateMode = ParseClientCertificateMode(configSection[ClientCertificateModeKey])
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -75,7 +79,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
|
|||
// "Certificate": {
|
||||
// "Path": "testCert.pfx",
|
||||
// "Password": "testPassword"
|
||||
// }
|
||||
// },
|
||||
// "ClientCertificateMode" : "NoCertificate"
|
||||
// }
|
||||
|
||||
var url = endpointConfig[UrlKey];
|
||||
|
|
@ -91,7 +96,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
|
|||
Protocols = ParseProtocols(endpointConfig[ProtocolsKey]),
|
||||
ConfigSection = endpointConfig,
|
||||
Certificate = new CertificateConfig(endpointConfig.GetSection(CertificateKey)),
|
||||
SslProtocols = ParseSslProcotols(endpointConfig.GetSection(SslProtocolsKey))
|
||||
SslProtocols = ParseSslProcotols(endpointConfig.GetSection(SslProtocolsKey)),
|
||||
ClientCertificateMode = ParseClientCertificateMode(endpointConfig[ClientCertificateModeKey])
|
||||
};
|
||||
|
||||
endpoints.Add(endpoint);
|
||||
|
|
@ -100,6 +106,16 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
|
|||
return endpoints;
|
||||
}
|
||||
|
||||
private ClientCertificateMode? ParseClientCertificateMode(string clientCertificateMode)
|
||||
{
|
||||
if (Enum.TryParse<ClientCertificateMode>(clientCertificateMode, ignoreCase: true, out var result))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static HttpProtocols? ParseProtocols(string protocols)
|
||||
{
|
||||
if (Enum.TryParse<HttpProtocols>(protocols, ignoreCase: true, out var result))
|
||||
|
|
@ -129,11 +145,13 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
|
|||
// "EndpointDefaults": {
|
||||
// "Protocols": "Http1AndHttp2",
|
||||
// "SslProtocols": [ "Tls11", "Tls12", "Tls13"],
|
||||
// "ClientCertificateMode" : "NoCertificate"
|
||||
// }
|
||||
internal class EndpointDefaults
|
||||
{
|
||||
public HttpProtocols? Protocols { get; set; }
|
||||
public SslProtocols? SslProtocols { get; set; }
|
||||
public ClientCertificateMode? ClientCertificateMode { get; set; }
|
||||
}
|
||||
|
||||
// "EndpointName": {
|
||||
|
|
@ -143,7 +161,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
|
|||
// "Certificate": {
|
||||
// "Path": "testCert.pfx",
|
||||
// "Password": "testPassword"
|
||||
// }
|
||||
// },
|
||||
// "ClientCertificateMode" : "NoCertificate"
|
||||
// }
|
||||
internal class EndpointConfig
|
||||
{
|
||||
|
|
@ -155,6 +174,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
|
|||
public HttpProtocols? Protocols { get; set; }
|
||||
public SslProtocols? SslProtocols { get; set; }
|
||||
public CertificateConfig Certificate { get; set; }
|
||||
public ClientCertificateMode? ClientCertificateMode { get; set; }
|
||||
|
||||
// Compare config sections because it's accessible to app developers via an Action<EndpointConfiguration> callback.
|
||||
// We cannot rely entirely on comparing config sections for equality, because KestrelConfigurationLoader.Reload() sets
|
||||
|
|
|
|||
|
|
@ -280,6 +280,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel
|
|||
if (https)
|
||||
{
|
||||
httpsOptions.SslProtocols = ConfigurationReader.EndpointDefaults.SslProtocols ?? SslProtocols.None;
|
||||
httpsOptions.ClientCertificateMode = ConfigurationReader.EndpointDefaults.ClientCertificateMode ?? ClientCertificateMode.NoCertificate;
|
||||
|
||||
// Defaults
|
||||
Options.ApplyHttpsDefaults(httpsOptions);
|
||||
|
|
@ -289,6 +290,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel
|
|||
httpsOptions.SslProtocols = endpoint.SslProtocols.Value;
|
||||
}
|
||||
|
||||
if (endpoint.ClientCertificateMode.HasValue)
|
||||
{
|
||||
httpsOptions.ClientCertificateMode = endpoint.ClientCertificateMode.Value;
|
||||
}
|
||||
|
||||
// Specified
|
||||
httpsOptions.ServerCertificate = LoadCertificate(endpoint.Certificate, endpoint.Name)
|
||||
?? httpsOptions.ServerCertificate;
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ using System.Linq;
|
|||
using System.Security.Authentication;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Https;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Xunit;
|
||||
|
||||
|
|
@ -92,7 +93,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Tests
|
|||
[Fact]
|
||||
public void ReadCertificatesSection_ThrowsOnCaseInsensitiveDuplicate()
|
||||
{
|
||||
var exception = Assert.Throws<ArgumentException>(() =>
|
||||
var exception = Assert.Throws<ArgumentException>(() =>
|
||||
new ConfigurationBuilder().AddInMemoryCollection(new[]
|
||||
{
|
||||
new KeyValuePair<string, string>("Certificates:filecert:Password", "certpassword"),
|
||||
|
|
@ -154,10 +155,13 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Tests
|
|||
{
|
||||
new KeyValuePair<string, string>("Endpoints:End1:Url", "http://*:5001"),
|
||||
new KeyValuePair<string, string>("Endpoints:End2:Url", "https://*:5002"),
|
||||
new KeyValuePair<string, string>("Endpoints:End2:ClientCertificateMode", "AllowCertificate"),
|
||||
new KeyValuePair<string, string>("Endpoints:End3:Url", "https://*:5003"),
|
||||
new KeyValuePair<string, string>("Endpoints:End3:ClientCertificateMode", "RequireCertificate"),
|
||||
new KeyValuePair<string, string>("Endpoints:End3:Certificate:Path", "/path/cert.pfx"),
|
||||
new KeyValuePair<string, string>("Endpoints:End3:Certificate:Password", "certpassword"),
|
||||
new KeyValuePair<string, string>("Endpoints:End4:Url", "https://*:5004"),
|
||||
new KeyValuePair<string, string>("Endpoints:End4:ClientCertificateMode", "NoCertificate"),
|
||||
new KeyValuePair<string, string>("Endpoints:End4:Certificate:Subject", "certsubject"),
|
||||
new KeyValuePair<string, string>("Endpoints:End4:Certificate:Store", "certstore"),
|
||||
new KeyValuePair<string, string>("Endpoints:End4:Certificate:Location", "cetlocation"),
|
||||
|
|
@ -171,6 +175,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Tests
|
|||
var end1 = endpoints.First();
|
||||
Assert.Equal("End1", end1.Name);
|
||||
Assert.Equal("http://*:5001", end1.Url);
|
||||
Assert.Null(end1.ClientCertificateMode);
|
||||
Assert.NotNull(end1.ConfigSection);
|
||||
Assert.NotNull(end1.Certificate);
|
||||
Assert.False(end1.Certificate.ConfigSection.Exists());
|
||||
|
|
@ -178,6 +183,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Tests
|
|||
var end2 = endpoints.Skip(1).First();
|
||||
Assert.Equal("End2", end2.Name);
|
||||
Assert.Equal("https://*:5002", end2.Url);
|
||||
Assert.Equal(ClientCertificateMode.AllowCertificate, end2.ClientCertificateMode);
|
||||
Assert.NotNull(end2.ConfigSection);
|
||||
Assert.NotNull(end2.Certificate);
|
||||
Assert.False(end2.Certificate.ConfigSection.Exists());
|
||||
|
|
@ -185,6 +191,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Tests
|
|||
var end3 = endpoints.Skip(2).First();
|
||||
Assert.Equal("End3", end3.Name);
|
||||
Assert.Equal("https://*:5003", end3.Url);
|
||||
Assert.Equal(ClientCertificateMode.RequireCertificate, end3.ClientCertificateMode);
|
||||
Assert.NotNull(end3.ConfigSection);
|
||||
Assert.NotNull(end3.Certificate);
|
||||
Assert.True(end3.Certificate.ConfigSection.Exists());
|
||||
|
|
@ -197,6 +204,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Tests
|
|||
var end4 = endpoints.Skip(3).First();
|
||||
Assert.Equal("End4", end4.Name);
|
||||
Assert.Equal("https://*:5004", end4.Url);
|
||||
Assert.Equal(ClientCertificateMode.NoCertificate, end4.ClientCertificateMode);
|
||||
Assert.NotNull(end4.ConfigSection);
|
||||
Assert.NotNull(end4.Certificate);
|
||||
Assert.True(end4.Certificate.ConfigSection.Exists());
|
||||
|
|
@ -235,7 +243,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Tests
|
|||
var reader = new ConfigurationReader(config);
|
||||
|
||||
var endpoint = reader.Endpoints.First();
|
||||
Assert.Equal(SslProtocols.Tls11|SslProtocols.Tls12, endpoint.SslProtocols);
|
||||
Assert.Equal(SslProtocols.Tls11 | SslProtocols.Tls12, endpoint.SslProtocols);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -287,5 +295,41 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Tests
|
|||
var endpoint = reader.EndpointDefaults;
|
||||
Assert.Null(endpoint.SslProtocols);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadEndpointWithNoClientCertificateModeSettings_ReturnsNull()
|
||||
{
|
||||
var config = new ConfigurationBuilder().AddInMemoryCollection(new[]
|
||||
{
|
||||
new KeyValuePair<string, string>("Endpoints:End1:Url", "http://*:5001"),
|
||||
}).Build();
|
||||
var reader = new ConfigurationReader(config);
|
||||
|
||||
var endpoint = reader.Endpoints.First();
|
||||
Assert.Null(endpoint.ClientCertificateMode);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadEndpointDefaultsWithClientCertificateModeSet_ReturnsCorrectValue()
|
||||
{
|
||||
var config = new ConfigurationBuilder().AddInMemoryCollection(new[]
|
||||
{
|
||||
new KeyValuePair<string, string>("EndpointDefaults:ClientCertificateMode", "AllowCertificate"),
|
||||
}).Build();
|
||||
var reader = new ConfigurationReader(config);
|
||||
|
||||
var endpoint = reader.EndpointDefaults;
|
||||
Assert.Equal(ClientCertificateMode.AllowCertificate, endpoint.ClientCertificateMode);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadEndpointDefaultsWithNoAllowCertificateSettings_ReturnsCorrectValue()
|
||||
{
|
||||
var config = new ConfigurationBuilder().Build();
|
||||
var reader = new ConfigurationReader(config);
|
||||
|
||||
var endpoint = reader.EndpointDefaults;
|
||||
Assert.Null(endpoint.ClientCertificateMode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -704,6 +704,136 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Tests
|
|||
Assert.True(ran1);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EndpointConfigureSection_CanSetClientCertificateMode()
|
||||
{
|
||||
var serverOptions = CreateServerOptions();
|
||||
var ranDefault = false;
|
||||
|
||||
serverOptions.ConfigureHttpsDefaults(opt =>
|
||||
{
|
||||
opt.ServerCertificate = TestResources.GetTestCertificate();
|
||||
|
||||
// Kestrel default
|
||||
Assert.Equal(ClientCertificateMode.NoCertificate, opt.ClientCertificateMode);
|
||||
ranDefault = true;
|
||||
});
|
||||
|
||||
var ran1 = false;
|
||||
var ran2 = false;
|
||||
var config = new ConfigurationBuilder().AddInMemoryCollection(new[]
|
||||
{
|
||||
new KeyValuePair<string, string>("Endpoints:End1:ClientCertificateMode", "AllowCertificate"),
|
||||
new KeyValuePair<string, string>("Endpoints:End1:Url", "https://*:5001"),
|
||||
}).Build();
|
||||
serverOptions.Configure(config)
|
||||
.Endpoint("End1", opt =>
|
||||
{
|
||||
Assert.Equal(ClientCertificateMode.AllowCertificate, opt.HttpsOptions.ClientCertificateMode);
|
||||
ran1 = true;
|
||||
})
|
||||
.Load();
|
||||
serverOptions.ListenAnyIP(0, opt =>
|
||||
{
|
||||
opt.UseHttps(httpsOptions =>
|
||||
{
|
||||
// Kestrel default.
|
||||
Assert.Equal(ClientCertificateMode.NoCertificate, httpsOptions.ClientCertificateMode);
|
||||
ran2 = true;
|
||||
});
|
||||
});
|
||||
|
||||
Assert.True(ranDefault);
|
||||
Assert.True(ran1);
|
||||
Assert.True(ran2);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EndpointConfigureSection_CanOverrideClientCertificateModeFromConfigureHttpsDefaults()
|
||||
{
|
||||
var serverOptions = CreateServerOptions();
|
||||
|
||||
serverOptions.ConfigureHttpsDefaults(opt =>
|
||||
{
|
||||
opt.ServerCertificate = TestResources.GetTestCertificate();
|
||||
opt.ClientCertificateMode = ClientCertificateMode.RequireCertificate;
|
||||
});
|
||||
|
||||
var ran1 = false;
|
||||
var config = new ConfigurationBuilder().AddInMemoryCollection(new[]
|
||||
{
|
||||
new KeyValuePair<string, string>("Endpoints:End1:ClientCertificateMode", "AllowCertificate"),
|
||||
new KeyValuePair<string, string>("Endpoints:End1:Url", "https://*:5001"),
|
||||
}).Build();
|
||||
serverOptions.Configure(config)
|
||||
.Endpoint("End1", opt =>
|
||||
{
|
||||
Assert.Equal(ClientCertificateMode.AllowCertificate, opt.HttpsOptions.ClientCertificateMode);
|
||||
ran1 = true;
|
||||
})
|
||||
.Load();
|
||||
|
||||
Assert.True(ran1);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DefaultEndpointConfigureSection_CanSetClientCertificateMode()
|
||||
{
|
||||
var serverOptions = CreateServerOptions();
|
||||
|
||||
serverOptions.ConfigureHttpsDefaults(opt =>
|
||||
{
|
||||
opt.ServerCertificate = TestResources.GetTestCertificate();
|
||||
});
|
||||
|
||||
var ran1 = false;
|
||||
var config = new ConfigurationBuilder().AddInMemoryCollection(new[]
|
||||
{
|
||||
new KeyValuePair<string, string>("EndpointDefaults:ClientCertificateMode", "AllowCertificate"),
|
||||
new KeyValuePair<string, string>("Endpoints:End1:Url", "https://*:5001"),
|
||||
}).Build();
|
||||
serverOptions.Configure(config)
|
||||
.Endpoint("End1", opt =>
|
||||
{
|
||||
Assert.Equal(ClientCertificateMode.AllowCertificate, opt.HttpsOptions.ClientCertificateMode);
|
||||
ran1 = true;
|
||||
})
|
||||
.Load();
|
||||
|
||||
Assert.True(ran1);
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public void DefaultEndpointConfigureSection_ConfigureHttpsDefaultsCanOverrideClientCertificateMode()
|
||||
{
|
||||
var serverOptions = CreateServerOptions();
|
||||
|
||||
serverOptions.ConfigureHttpsDefaults(opt =>
|
||||
{
|
||||
opt.ServerCertificate = TestResources.GetTestCertificate();
|
||||
|
||||
Assert.Equal(ClientCertificateMode.AllowCertificate, opt.ClientCertificateMode);
|
||||
opt.ClientCertificateMode = ClientCertificateMode.RequireCertificate;
|
||||
});
|
||||
|
||||
var ran1 = false;
|
||||
var config = new ConfigurationBuilder().AddInMemoryCollection(new[]
|
||||
{
|
||||
new KeyValuePair<string, string>("EndpointDefaults:ClientCertificateMode", "AllowCertificate"),
|
||||
new KeyValuePair<string, string>("Endpoints:End1:Url", "https://*:5001"),
|
||||
}).Build();
|
||||
serverOptions.Configure(config)
|
||||
.Endpoint("End1", opt =>
|
||||
{
|
||||
Assert.Equal(ClientCertificateMode.RequireCertificate, opt.HttpsOptions.ClientCertificateMode);
|
||||
ran1 = true;
|
||||
})
|
||||
.Load();
|
||||
|
||||
Assert.True(ran1);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Reload_IdentifiesEndpointsToStartAndStop()
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in New Issue