{
public const string Realm = "LINUX.CONTOSO.COM";
public const string NegotiateAuthWebServer = "http://apacheweb.linux.contoso.com/auth/kerberos/";
+ public const string NegotiateAuthWebServerNotDefaultPort = "http://apacheweb.linux.contoso.com:8081/auth/kerberos/";
public const string AlternativeService = "http://altweb.linux.contoso.com:8080/auth/kerberos/";
public const string NtlmAuthWebServer = "http://apacheweb.linux.contoso.com:8080/auth/ntlm/";
public const string DigestAuthWebServer = "http://apacheweb.linux.contoso.com/auth/digest/";
</IfDefine>
<IfDefine !ALTPORT>
Listen 80
+Listen 8081
</IfDefine>
#
# e-mailed. This address appears on some server-generated pages, such
# as error documents. e.g. admin@your-domain.com
#
-ServerAdmin you@example.com
+ServerAdmin webmaster@contoso.com
#
# ServerName gives the name and port that the server uses to identify itself.
SSLRandomSeed connect builtin
</IfModule>
+<IfDefine ALTPORT>
<VirtualHost *:8080>
- ServerAdmin webmaster@contoso.com
DocumentRoot "/setup/altdocs"
ServerName altservice.contoso.com:8080
</VirtualHost>
+</IfDefine>
+
+<IfDefine !ALTSPN>
+<VirtualHost *:8081>
+ DocumentRoot "/setup/htdocs"
+</VirtualHost>
+</IfDefine>
<IFDefine NTLM>
fi
if [ "$1" == "-DNTLM" ]; then
+ # NTLM/Winbind is aggressive and eats Negotiate so it cannot be combined with Kerberos
./setup-pdc.sh
/usr/sbin/apache2 -DALTPORT "$@"
shift
hostname: altweb
domainname: linux.contoso.com
dns_search: linux.contoso.com
- command: -DALTPORT
+ command: "-DALTPORT -DALTSPN"
volumes:
- shared-volume:/SHARED
networks:
{
internal static partial class AuthenticationHelper
{
+ private const string UsePortInSpnCtxSwitch = "System.Net.Http.UsePortInSpn";
+ private const string UsePortInSpnEnvironmentVariable = "DOTNET_SYSTEM_NET_HTTP_USEPORTINSPN";
+
+ private static volatile int s_usePortInSpn = -1;
+
+ private static bool UsePortInSpn
+ {
+ get
+ {
+ int usePortInSpn = s_usePortInSpn;
+ if (usePortInSpn != -1)
+ {
+ return usePortInSpn != 0;
+ }
+
+ // First check for the AppContext switch, giving it priority over the environment variable.
+ if (AppContext.TryGetSwitch(UsePortInSpnCtxSwitch, out bool value))
+ {
+ s_usePortInSpn = value ? 1 : 0;
+ }
+ else
+ {
+ // AppContext switch wasn't used. Check the environment variable.
+ s_usePortInSpn =
+ Environment.GetEnvironmentVariable(UsePortInSpnEnvironmentVariable) is string envVar &&
+ (envVar == "1" || envVar.Equals("true", StringComparison.OrdinalIgnoreCase)) ? 1 : 0;
+ }
+
+ return s_usePortInSpn != 0;
+ }
+ }
+
private static Task<HttpResponseMessage> InnerSendAsync(HttpRequestMessage request, bool async, bool isProxyAuth, HttpConnectionPool pool, HttpConnection connection, CancellationToken cancellationToken)
{
return isProxyAuth ?
hostName = result.HostName;
}
- if (!isProxyAuth && !authUri.IsDefaultPort)
+ if (!isProxyAuth && !authUri.IsDefaultPort && UsePortInSpn)
{
hostName = string.Create(null, stackalloc char[128], $"{hostName}:{authUri.Port}");
}
using System.Net.Test.Common;
using System.Threading.Tasks;
-
+using Microsoft.DotNet.RemoteExecutor;
using Xunit;
namespace System.Net.Http.Enterprise.Tests
[ConditionalClass(typeof(EnterpriseTestConfiguration), nameof(EnterpriseTestConfiguration.Enabled))]
public class HttpClientAuthenticationTest
{
+ private const string AppContextSettingName = "System.Net.Http.UsePortInSpn";
+
[Theory]
[InlineData(EnterpriseTestConfiguration.NegotiateAuthWebServer, false)]
- [InlineData(EnterpriseTestConfiguration.AlternativeService, false)]
+ [InlineData(EnterpriseTestConfiguration.NegotiateAuthWebServerNotDefaultPort, false)]
+ [InlineData(EnterpriseTestConfiguration.AlternativeService, false, true)]
[InlineData(EnterpriseTestConfiguration.DigestAuthWebServer, true)]
[InlineData(EnterpriseTestConfiguration.DigestAuthWebServer, false)]
[InlineData(EnterpriseTestConfiguration.NtlmAuthWebServer, true)]
- public async Task HttpClient_ValidAuthentication_Success(string url, bool useDomain)
+ public void HttpClient_ValidAuthentication_Success(string url, bool useDomain, bool useAltPort = false)
{
- using var handler = new HttpClientHandler();
- handler.Credentials = useDomain ? EnterpriseTestConfiguration.ValidDomainNetworkCredentials : EnterpriseTestConfiguration.ValidNetworkCredentials;
- using var client = new HttpClient(handler);
+ RemoteExecutor.Invoke((url, useAltPort, useDomain) =>
+ {
+ // This is safe as we have no parallel tests
+ if (!string.IsNullOrEmpty(useAltPort))
+ {
+ AppContext.SetSwitch(AppContextSettingName, true);
+ }
+ using var handler = new HttpClientHandler();
+ handler.Credentials = string.IsNullOrEmpty(useDomain) ? EnterpriseTestConfiguration.ValidNetworkCredentials : EnterpriseTestConfiguration.ValidDomainNetworkCredentials;
+ using var client = new HttpClient(handler);
- using HttpResponseMessage response = await client.GetAsync(url);
- Assert.Equal(HttpStatusCode.OK, response.StatusCode);
+ using HttpResponseMessage response = client.GetAsync(url).GetAwaiter().GetResult();
+ Assert.Equal(HttpStatusCode.OK, response.StatusCode);
+ }, url, useAltPort ? "true" : "" , useDomain ? "true" : "").Dispose();
}
[ActiveIssue("https://github.com/dotnet/runtime/issues/416")]
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>$(NetCoreAppCurrent)-Unix;$(NetCoreAppCurrent)-Browser</TargetFrameworks>
+ <IncludeRemoteExecutor>true</IncludeRemoteExecutor>
</PropertyGroup>
<ItemGroup>
<Compile Include="HttpClientAuthenticationTest.cs" />
<Compile Include="$(CommonTestPath)System\Net\EnterpriseTests\EnterpriseTestConfiguration.cs"
Link="Common\System\Net\EnterpriseTests\EnterpriseTestConfiguration.cs" />
</ItemGroup>
-</Project>
\ No newline at end of file
+</Project>