Runtime: Documentation: Update docs on supported libcurl features on Linux and OSX.

Created on 19 Jul 2016  路  21Comments  路  Source: dotnet/runtime

This is a cross post from dotnet/KestrelHttpServer and WCF. I've closed that issue as it is not Kestrel related, but I apologize for the cross-posting, I'm still learning about the different repositories and what functions are where.

I have some functionality that is working on windows, but fails on all of the unix droplets I've spun up. I'm seeing one type of failure message on ubuntu 14.04 and debian 8.5 and another type on fedora 23 and centos 7 when using BasicHttpsbinding and the connection factory. I've tried using the svcutil to generate the interface and I've also used the new WCF Connected Services utility and I've even crafted the entire message by hand and used SendAsync and PostAsync. All scenarios exhibit the same behavior.

Here's the code:
```c#
private string invokeSsoSoapRequest(string xmlRequest)
{
ChannelFactory factory = null;
SingleSignOnSoap serviceProxy = null;
var binding = new BasicHttpsBinding();
binding.Security.Mode = BasicHttpsSecurityMode.Transport;
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Certificate;

    var baseAddress = new Uri(mySettings.ClientUrl);
    var endpointAddress = new EndpointAddress(baseAddress);

    X509Certificate2Collection collection = new X509Certificate2Collection();

    if (RuntimeEnvironment.OperatingSystemPlatform == Platform.Windows)
    {
        //windows file location
        collection.Import(mySettings.ClientPrivateKeyWindowsPath, mySettings.ClientPfxPass, X509KeyStorageFlags.PersistKeySet);
    }else
    {
        collection.Import(mySettings.ClientPrivateKeyUnixPath, mySettings.ClientPfxPass, X509KeyStorageFlags.PersistKeySet);
    }

    //parse pfx for client auth key
    factory = new ChannelFactory<SingleSignOnSoap>(binding, new EndpointAddress(baseAddress));
    foreach (X509Certificate2 cert in collection)
    {
        if (cert.HasPrivateKey)
        {
            factory.Credentials.ClientCertificate.Certificate = cert;
        }
    }
    serviceProxy = factory.CreateChannel();
    RequestTicketRequest request = new RequestTicketRequest();
    RequestTicketRequestBody body = new RequestTicketRequestBody();
    request.Body = body;
    request.Body.sRequestXML = xmlRequest;
    return serviceProxy.RequestTicket(request).Body.RequestTicketResult;
}

}
```

`
Here are the errors I'm getting on Ubuntu and Debian:

TimeoutException: The HTTP request to 'https://services.**SingleSignOn.asmx' has exceeded the allotted timeout of 00:01:00. The time allotted to this operation may have been a portion of a longer timeout.

The above dotnet error claims the service timed out while waiting for a response (00:01:00), but a tshark trace shows the SOAP response negotiation failed when the client sent [RST, ACK] back the the remote webservice. See image below. The same trace in windows environment shows no errors and the transaction works and as you can see a few lines above, the server hello, certificate, and key exchanges appear to be successful, so I don't think I'm dealing with a cert issue.

image

I've also tried this in docker just to rule out any host config issues with the following docker config and see the same error:

FROM microsoft/dotnet:1.0.0-preview2-sdk
RUN mkdir -p /dotnetapp
WORKDIR /dotnetapp
EXPOSE 5000
COPY . /dotnetapp
RUN dotnet restore
ENTRYPOINT ["dotnet", "run"]`

I've tried this on Fedora and Centos, but get a different error related to the curl version that ships with those distros. I'm not entirely sure how to fix it, but I suspect I'd have the same issue if I got past it.

Here is the error I received on Fedora and Centos:

System.PlatformNotSupportedException: The libcurl library in use (7.43.0) and its SSL backend ("NSS/3.24 Basic ECC") do not support custom handling of certificates. A libcurl built with OpenSSL is required.

*As a note, I compiled 7.50 curl against openssl and still received the above error. I'm a little bit of a linux noob, so I'm not sure if there's a way to force dotnet core to use the 7.5 installation, but as of now, dotnet core is still using 7.4.3.

Here is my project.json
{ "dependencies": { "Microsoft.NETCore.App": { "version": "1.0.0", "type": "platform" }, "Microsoft.AspNetCore.Mvc": "1.0.0", "Microsoft.AspNetCore.Server.Kestrel": "1.0.0", "Microsoft.Extensions.Configuration.EnvironmentVariables": "1.0.0", "Microsoft.Extensions.Configuration.FileExtensions": "1.0.0", "Microsoft.Extensions.Configuration.Json": "1.0.0", "Microsoft.Extensions.Logging": "1.0.0", "Microsoft.Extensions.Logging.Console": "1.0.0", "Microsoft.Extensions.Logging.Debug": "1.0.0", "Microsoft.Extensions.Options.ConfigurationExtensions": "1.0.0", "System.ServiceModel.Http": "4.1.0", "System.Xml.XmlSerializer": "4.0.11", "Microsoft.Extensions.Options": "1.0.0", "System.Runtime.Extensions": "4.1.0", "System.Text.Encoding": "4.0.11", "System.Xml.XmlDocument": "4.0.1", "System.Xml.XDocument": "4.0.11", "System.Security.Cryptography.X509Certificates": "4.1.0", "System.Security.Cryptography.Csp": "4.0.0", "system.xml.xpath.xmldocument": "4.0.0", "JsonWebTokens": "1.2.0", "Microsoft.AspNetCore.Diagnostics": "1.0.0", "Microsoft.AspNetCore.Server.IISIntegration": "1.0.0" }, "tools": { }, "frameworks": { "netcoreapp1.0": { "imports": [ "dotnet5.6" ] } }, "buildOptions": { "emitEntryPoint": true, "preserveCompilationContext": true, "debugType": "portable" }, "runtimeOptions": { "configProperties": { "System.GC.Server": true } }, "publishOptions": { "include": [ "wwwroot", "Views", "Areas/**/Views", "appsettings.json", "web.config", "Dockerfile" ] }, "scripts": { "postpublish": [ "dotnet publish-iis --publish-folder %publish:OutputPath% --framework %publish:FullTargetFramework%" ] } }

As I said on my other post, I would appreciate any help and know that it's more than plausible I've done something wrong here. This is the only piece of this API stack that we haven't been able to port.

Lastly, if I can provide any other information, please let me know!
Thank you!

area-System.Net.Http documentation os-linux

Most helpful comment

Sorry but dotnet/runtime#17723 was closed sending us here for docs that I can't find. What is the fix for 9728 on OS X? Thanks

All 21 comments

@mwdavisii, I'm sorry for the slow response to this issue. Thank you for reporting it! Before I dig in further, have you made any progress on this from your end since reporting this?

No worries! No, I haven't been able to get past it. I ended up writing a class wrapper to call a bash script I wrote that calls curl directly. It feels nasty, but I commented the function heavily and added a link to this issue with the hope of making it work the right way one day.

```c#
if (RuntimeEnvironment.OperatingSystemPlatform == Platform.Windows)
{
//Invoke SSO Service
response = invokeSSO(xmlRequest);
}
else
{

            //open up RunShell to execute bash script
            ShellWrapper runShell = new ShellWrapper(this.mySettings, this.myLogger, this.myHostingEnv);

            response = runShell.invokeViaCurlCommandLine(customerIdentification.clientID, employeeID);

        }

```

I suspect it's not a very common problem as we're consuming a vendor's old dotnet soap service (looks like maybe 1.1), which also appears to be poorly defined and designed - the input parameters expect xml as string (CDATA) and the WSDL points to http when the service lives at https, and all of the responses are CDATA XML as well.

@Priya91 can you please triage it?

This issue was moved to dotnet/wcf#1792

Copying comment from dotnet/wcf#1792

On unix, HttpClient uses libcurl for communication. libcurl uses openssl for the certificate handling. My understanding is if you give HttpClient an ephemeral certificate which isn't in the local certificate store then libcurl is unable to use it. If you use X509Store to add it to your store, this places it in a special .net folder in your home directory and libcurl still won't find it. You need to install the client cert in your local machine certificate store before HttpClient/libcurl will use it. This is all from memory from over a year ago so @karelz will need to confirm this is still the behavior of HttpClient.

@mconnew I don't understand how that explains the original issue, can you please provide a smaller repro with HttpClient to enable further investigation.

When the user specifies a client certificate with HttpClientHandler.ClientCertificates.Add(cert), it gets added to ssl object used by libcurl, by setting the client certificate callback function here. More docs about client cert callback is here. This functionality doesn't work on OSX, due to underlying platform difference as explained here

cc @bartonjs

Yep, ephemeral certificates should work just fine in on Linux with libcurl+openssl. libcurl+nss (or any other provider) won't work; and client authentication is not supported via the Http classes on macOS. (libcurl+darwinssl doesn't support the callback, and libcurl+openssl doesn't speak the right pointer language since our certs are backed by Security.framework on macOS now)

Why lack of understanding and request for repro makes it not a bug any more ? :)
As it is been indicated in related ticket (https://github.com/PowerShell/PowerShell/issues/2511) this functionality is very much needed. Please at least provide some workaround for this issue before proper solution is implemented.

Why lack of understanding and request for repro makes it not a bug any more ? :)

@ryangurazov We need more information on which os distro and libcurl config used to verify if it's a bug or by design. From @mconnew summary, it looks like the behavior is by design. The workaround is to use libcurl built with openssl, while using .NET Core.

Yes, removing bug without providing the information must have been alarming, replacing it with question, until verified as bug :)

.NET Core depends on system-wide components, incl. libcurl.
We can document which version we advise customers to install, would that help?
We could update the exception text to link to such documentation ... would that make the e2e experience better?

Short of those documentation changes, I don't think there is anything we could/should do here. Bundling of libcurl (which is the ultimate solution) has been turned down for 2.0 at least.

Thanks for all the details @Priya91 and @karelz
@karelz : given current plans for libcurl bundling, those two options you listed will help big time.

OK, switching to documentation bug.
@ryanbrandenburg do you have any recommendation which docs to change? (We can find some, but it would be great to confirm from user perspective we got the right places)
Also if you have wording suggestions, that would be great.

@karelz, is there corresponding feature for libcurl bundling in the roadmap?

@ryangurazov not at this moment. We explored the option for 2.0, but it was turned down.

There are other long-term plans in the area which may mitigate the problem. We should be able to talk about them publicly in next few weeks ...

FYI @karelz you are @ 'ing me instead of @ryangurazov.

@ryanbrandenburg after 3rd character most of the time it is alright to assume the match, but it looks like here it goes all the way to 5th :)

Thanks for details @karelz

Ooops, sorry - GitHub is not helpful in this :( ... earlier today someone (actually it was earlier on this thread 馃槅) used 'karlz' instead of 'karelz' (I updated it earlier and also fixed my mistake) ... sorry!

@karelz We should document the dependencies here, and we can reference this in the upcoming issues. Do you know who should be contacted to make progress on this issue further?

@richlander @leecow @gotheap are you the right people to help with prereq docs?

@Priya91 we have other places as well - e.g. Supported OS versions and CoreFX/CoreCLR engineering docs.

Moving to Post-ZBB.

Sorry but dotnet/runtime#17723 was closed sending us here for docs that I can't find. What is the fix for 9728 on OS X? Thanks

This issue has changed over the course of discussion from a problem with Curl to a documentation issue for Linux HTTP stack (CurlHandler).

Starting with .NET Core 2.1 (available now as Preview 2), we have a new HTTP stack, SocketsHttpHandler, that works on both Windows and Linux. In terms of the some of the client certificate problems discussed on this GitHub issue, you may find the problem working for your scenarios.

See this link for info:
https://blogs.msdn.microsoft.com/dotnet/2018/04/11/announcing-net-core-2-1-preview-2/

Due to the long and changing discussion on this issue, we will close it. Please try out .NET Core 2.1 Preview 2. If you have problems, please open a new issue. Thanks!

Was this page helpful?
0 / 5 - 0 ratings