gohttp-proxyntlmdefaultnetworkcredentials

Does Go have some way of using DefaultNetworkCredentials with a proxy when making an HTTP request?


I'm writing a simple Go utility that makes some POST requests to the internet, but I need to go through a proxy and use NTLM without knowing the user's credentials. I've seen CSharp code like below:

wc = new CookieWebClient();
wc.UseDefaultCredentials = true;
wc.Proxy = WebRequest.DefaultWebProxy;
wc.Proxy.Credentials = CredentialCache.DefaultNetworkCredentials;

In Go I've managed to determine the system proxy with what feels like a hack:

import (
    "net/http"
    "net/url"
    "strings"

    "golang.org/x/sys/windows/registry"
)

func getSystemProxy() func(req *http.Request) (*url.URL, error) {
    var e error
    var k registry.Key
    var proxy *url.URL
    var v string

    // Open the "Internet Settings" registry key
    k, e = registry.OpenKey(
        registry.CURRENT_USER,
        strings.Join(
            []string{
                "Software",
                "Microsoft",
                "Windows",
                "CurrentVersion",
                "Internet Settings",
            },
            "\\",
        ),
        registry.QUERY_VALUE,
    )
    if e != nil {
        return http.ProxyFromEnvironment
    }
    defer k.Close()

    // Read the "ProxyServer" value
    v, _, e = k.GetStringValue("ProxyServer")
    if (e != nil) || (v == "") {
        return http.ProxyFromEnvironment
    }

    // Get the http= portion and fix it up
    v = strings.Split(v, ";")[0]
    v = strings.Replace(v, "=", "://", 1)

    // Parse url
    if proxy, e = url.Parse(v); e != nil {
        return http.ProxyFromEnvironment
    }

    return http.ProxyURL(proxy)
}

I've been unable to find a Go equivalent to CSharp's https://learn.microsoft.com/en-us/dotnet/api/system.net.credentialcache.defaultnetworkcredentials?view=netcore-3.1. I have found https://github.com/Azure/go-ntlmssp but that requires me to hard-code the user's credentials, which I do not have, and I would need to compile a new binary for each user, which I do not want to do. I'm looking for a 0 config solution. I would prefer this to be as dynamic as possible. If CSharp can do it, why can't Go?

Any help would be greatly appreciated!


Solution

  • I've been told I can use WinHTTP or WinINet to achieve this. I will play with the golang.org/x/sys/windows package and call the procedures from the DLLs. Alternatively, I've been told I could use github.com/go-ole/go-ole and use WinHTTP via COM objects like in this other SO question: Windows system credentials in Go HTTP NTLM requests.

    Update: I ultimately decided to settle on WinINet and I created my own module (github.com/mjwhitta/win/wininet) which contains an http client implementation which can be used to make GET and POST requests. See the README for an example of how to use it. I will probably expand on it in the future should I have the need. Pull requests are welcome.

    Update (2024-04-17): My original module (github.com/mjwhitta/win) contained WinHTTP and WinINet implementations. I've now created a new module (github.com/mjwhitta/inet) which contains a minimal, cross-platform HTTP client interface so you no longer need to worry about the OS the code is running on. On Windows it defaults to WinINet but can be configured to use WinHTTP or net/http. On other OS, it only uses net/http. See the README for examples.