Matthieu Cormier
Matthieu Cormier

Reputation: 2185

Detecting Windows proxy settings in .Net Core

One of our test servers has proxy settings and I was able to detect what proxy to use by using a 1 line powershell command.

 (new-object System.Net.WebClient).Proxy.GetProxy("https://www.google.com")

However, if I instantiate a WebClient object in .Net Core the proxy is null. Our current solution is to simply set the proxy for all outgoing connections as part of the environment configuration. This worked fine for some of our simpler services that only contacted one endpoint that needed to be proxied, however I am now working on a fix where one endpoint needs to be proxied and another not.

My options are

  1. Add an exclusion list for endpoints not to be proxied
  2. Get the proxy settings from the operating system somehow

This question is asking for help for number 2. I can implement option 1 with no issue.

My best guess is that the proxy information is not available due to changes in .Net architecture. I found the following illuminating quote. (https://github.com/dotnet/runtime/issues/24756) We are using dot net core 3.

As of .NET Core 2.1, the HTTP protocol stack used in .NET Core was changed. It no longer uses native Windows WinHTTP nor Linux 'libcurl'. Those are considered now the 'legacy' stacks. We have a new HTTP protocol stack called "SocketsHttpHandler". It used by default.

So basically I think I am looking for some magic code to get proxy information from the old Windows network stack if running on Windows.

bool isWindows = System.Runtime.InteropServices.RuntimeInformation
                                               .IsOSPlatform(OSPlatform.Windows);

if (isWindows) {
   // Do magic get proxy settings secret sauce.
}

Upvotes: 1

Views: 7447

Answers (1)

Matthieu Cormier
Matthieu Cormier

Reputation: 2185

Eventually I determined that the "system" configuration of the powershell script was coming from an old config file in the Framework install.

C:\Windows\Microsoft.NET\Framework\v4.0.30319\Config\machine.config

<system.net>  
    <defaultProxy>
        <proxy
            usesystemdefault="false"
            proxyaddress="http://proxy.internal.local"
            bypassonlocal="true"
        />
        <bypasslist>
            <add address="someServer.com" />
            <add address="someotherserver.com" />
        </bypasslist>
    </defaultProxy>
</system.net>

Initial Solution

Here is the initial solution we used in .Net core. We later discovered that the .Net Core code supports HTTPS_PROXY and NO_PROXY variables, scroll down to see the final solution.

First we added the following settings to appsettings.json

"proxy": {
  "address": "http://proxy.internal.local",
  "bypasslist": [
    "someServer.com",
    "someotherserver.com"
  ]
}  

Then we used the following C# code to load the settings.

    private static void ConfigureProxy()
    {

        IConfigurationSection proxySection = configuration.GetSection("proxy");

        if ( ! proxySection.Exists() ) {
            return;  // A proxy is not configured.  Do nothing.
        }

        string proxyUrl = proxySection["address"];

        if ( proxyUrl == null )
        {
            Logger.LogWarning("Detected a proxy configuration section without a proxy address.  Review your configuration.");
            return;
        }

        string[] bypasslist = proxySection.GetSection("bypasslist")
                                    .GetChildren()
                                    .Select(x => x.Value)
                                    .ToArray();

        WebProxy proxyObject;
        
        if ( bypasslist.Length > 0 ) {
            proxyObject = new WebProxy(proxyUrl,true, bypasslist);
        } else {
            Logger.LogWarning("Creating proxy with no bypass list");
            proxyObject = new WebProxy(proxyUrl,true);
        }
                   
        WebRequest.DefaultWebProxy = proxyObject;
        Logger.LogInformation($"WebRequest.DefaultWebProxy set to {proxyUrl}");
    }

The benefit of this solution is that you see your proxy settings in appsettings.json if that's what floats your boat.

Final Solution

So that is a solution if you want to support proxy information in appsettings.json. However there is another way. You can simply use the HTTPS_PROXY and NO_PROXY environment variables and Microsoft code will configure the proxies for you.

$env:HTTPS_PROXY = 'http://proxy.internal.local'
$env:NO_PROXY = '.someServer.com,.someotherserver.com'
RunYourApplication

Take note of the NO_PROXY format. It is a comma separated list and the domain names must be prefixed with a dot.

Upvotes: 5

Related Questions