Skip to content
This repository has been archived by the owner on Jul 9, 2023. It is now read-only.

Support NTLM/Kerberos upstream proxy authentication #944

Open
SamuelePilleri opened this issue Jan 4, 2023 · 1 comment
Open

Support NTLM/Kerberos upstream proxy authentication #944

SamuelePilleri opened this issue Jan 4, 2023 · 1 comment

Comments

@SamuelePilleri
Copy link

SamuelePilleri commented Jan 4, 2023

I'm trying to create a local proxy service based on the WindowsService example to overcome limitations in my company.

Every PC is set up with a PAC URL that contains what proxy to use for each destination. This is what a correct request to the outside world looks like:

$ curl --proxy {upstreamproxy}:8080 --proxy-ntlm --proxy-user ":" --verbose https://ipinfo.io
* Trying {omitted}:8080...
* Connected to {upstreamproxy} ({omitted}) port 8080 (#0)
* allocate connect buffer
> CONNECT ipinfo.io:443 HTTP/1.1
> Host: ipinfo.io:443
> Proxy-Authorization: NTLM {omitted}
> User-Agent: curl/7.83.1
> Proxy-Connection: Keep-Alive
>
< HTTP/1.1 407 authenticationrequired
< Date: Wed, 04 Jan 2023 10:36:50 GMT
< Content-Type: text/html
< Cache-Control: no-cache
< Content-Length: 0
< X-Frame-Options: deny
< Proxy-Connection: Keep-Alive
< Proxy-Authenticate: NTLM {omitted}
<
> CONNECT ipinfo.io:443 HTTP/1.1
> Host: ipinfo.io:443
> Proxy-Authorization: NTLM {omitted}
> User-Agent: curl/7.83.1
> Proxy-Connection: Keep-Alive
>
< HTTP/1.0 200 Connection established
<
> GET / HTTP/1.1
> Host: ipinfo.io
> User-Agent: curl/7.83.1
> Accept: */*
>
< HTTP/1.1 200 OK
< {ipinfo.io response headers, omitted}

Based on the example provided, this is what I've written:

protected override void OnStart(string[] args)
{
	proxyServerInstance = new(userTrustRootCertificate: false) {
		CheckCertificateRevocation = X509RevocationMode.NoCheck,
		ConnectionTimeOutSeconds = 30,
		Enable100ContinueBehaviour = true,
		EnableConnectionPool = true,
		EnableTcpServerConnectionPrefetch = true,
		EnableWinAuth = true,
		ForwardToUpstreamGateway = true,
		MaxCachedConnections = 2,
		ReuseSocket = true,
		TcpTimeWaitSeconds = 30,
		EnableHttp2 = true,
		NoDelay = true,
		ThreadPoolWorkerThread = Environment.ProcessorCount,
		ExceptionFunc = ProxyException,
	};
	
	proxyServerInstance.CertificateManager.SaveFakeCertificates = false;

	var explicitEndPointV4 = new ExplicitProxyEndPoint(IPAddress.Loopback, ListeningPort, false);
	proxyServerInstance.AddEndPoint(explicitEndPointV4);

	var explicitEndPointV6 = new ExplicitProxyEndPoint(IPAddress.IPv6Loopback, ListeningPort, false);
	proxyServerInstance.AddEndPoint(explicitEndPointV6);

	proxyServerInstance.Start();

	#if DEBUG
	Console.WriteLine($"Service Listening on port {ListeningPort}");
	#else
	ProxyServiceEventLog.WriteEntry($"Service Listening on port {ListeningPort}", EventLogEntryType.Information);
	#endif
}

However the exception Upstream proxy failed to create a secure tunnel is raised.

throw new Exception("Upstream proxy failed to create a secure tunnel");

Looking at the code it may depend on how Windows authentication is handled.

// check for windows authentication
if (args.EnableWinAuth)
{
if (response.StatusCode == (int)HttpStatusCode.Unauthorized)
await Handle401UnAuthorized(args);
else
WinAuthEndPoint.AuthenticatedResponse(args.HttpClient.Data);
}

I guess the problem here is the upstream proxy replying with 407 rather then 401, but I have limited debugging capabilities (work PC) and it's hard to investigate further.

Can someone please help me troubleshoot this? I'm using .NET Core 6 (can't install full Visual Studio) and Titanium 3.2.0.

@SamuelePilleri
Copy link
Author

Upon further inspection it seems to me that the library does not handle the case when the upstream proxy server supports NTLM/Kerberos authentication.

I'm willing to implement it, I just need some guidance on what may already be in place to support it in this huge code base.

On the protocol side, it should no be very difficult:

$ curl --proxy {upstreamproxy}:8080 --verbose https://ipinfo.io
* Trying {omitted}:8080...
* Connected to {upstreamproxy} ({omitted}) port 8080 (#0)
* allocate connect buffer
* Establish HTTP proxy tunnel to ipinfo.io:443
> CONNECT ipinfo.io:443 HTTP/1.1
> Host: ipinfo.io:443
> User-Agent: curl/7.83.1
> Proxy-Connection: Keep-Alive
>
< HTTP/1.1 407 authenticationrequired
< Date: Thu, 05 Jan 2023 17:59:47 GMT
< Content-Type: text/html
< Cache-Control: no-cache
< Content-Length: 3741
< X-Frame-Options: deny
< Proxy-Connection: Keep-Alive
< Proxy-Authenticate: NTLM
< Proxy-Authenticate: Basic realm="{omitted}"
<
* Closing connection 0

As you can see the proxy server itself replies with supported authentication mechanisms and I've seen some RetryLogic in the code base, but it would be much easier to implement with a few hints on how to do it.

@SamuelePilleri SamuelePilleri changed the title EnableWinAuth 407 ProxyAuthenticationRequired Support NTLM/Kerberos upstream proxy authentication Jan 5, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant