aws/aws-sdk-cpp

Support for setting CURLSSLOPT_NATIVE_CA on Windows

pr0g opened this issue · 5 comments

Describe the feature

(moved from discussion #2724)

Pass CURLSSLOPT_NATIVE_CA to aws-sdk-cpp

Use Case

This might be an issue but it's also very likely this is possible and I just don't know how to do it.

I am using aws-sdk-cpp on Windows and have a custom build of OpenSSL and Curl. When I try to make an https request using Curl directly, I get the error:

 SSL peer certificate or SSH remote key was not OK

I can fix this by either setting a certificate with:

curl_easy_setopt(curl, CURLOPT_CAINFO, "cacert.pem");

(Where "cacert.pem" is one I grabbed from here).

Or I can use:

curl_easy_setopt(curl, CURLOPT_SSL_OPTIONS, CURLSSLOPT_NATIVE_CA);

Where CURLSSLOPT_NATIVE_CA states "Tell libcurl to use the operating system's native CA store for certificate verification" (see here for more info).

The problem with this option is I don't see a way to pass that through to AWS SDK. I can get things to work if I use:

Aws::Client::ClientConfiguration client_config;
client_config.region = "<my-region>";
client_config.caFile = "cacert.pem";

But it would be nice if I could just use the operating system's native CA store.

Is there a way to do this that I'm not seeing? I initialize Curl outside of aws-sdk-cpp and I've tried setting the property right after curl_global_init(CURL_GLOBAL_ALL);.

Something like...

curl_global_init(CURL_GLOBAL_ALL);

if (CURL* curl = curl_easy_init()) {
  curl_easy_setopt(curl, CURLOPT_SSL_OPTIONS, CURLSSLOPT_NATIVE_CA);
}

Aws::SDKOptions options;
options.httpOptions.initAndCleanupCurl = false;
Aws::InitAPI(options);

...

Aws::ShutdownAPI(options);
curl_global_cleanup();

But that unfortunately does not seem to work. If there's a way to do this I'd be really curious to know. Thank you!

Proposed Solution

Pass Curl option to aws-sdk-cpp at initialization time to use the operating system's native CA store.

Other Information

No response

Acknowledgements

  • I may be able to implement this feature request
  • This feature might incur a breaking change

Hi @pr0g ,

There is a an abstract method on the Curl HTTP Client wrapper:

virtual void OverrideOptionsOnConnectionHandle(CURL*) const {}

You should be able to inherit the built-in Curl HTTP Client and then provide your implementation in this method. Please also refer to "Override the default HTTP client factory" in our documentation in order to make SDK to use your new customized client.

Best regards,
Sergey

Hi @SergeyRyabinin,

Thank you very much for getting back to me about this. This sounds like exactly what I was after! 🥳 Apologies I didn't spot this before.

I'll test this out soon and report back with how it went.

Thanks again very much for your help!

Closing for now, will reopen if needed, thanks again 👍

⚠️COMMENT VISIBILITY WARNING⚠️

Comments on closed issues are hard for our team to see.
If you need more assistance, please either tag a team member or open a new issue that references this one.
If you wish to keep having a conversation with other community members under this issue feel free to do so.

Just for completeness in case anyone else stumbles across this trying to do the same thing, this is what I used:

class CustomCurlHttpClient : public Aws::Http::CurlHttpClient
{
public:
  CustomCurlHttpClient(const Aws::Client::ClientConfiguration& client_config)
    : Aws::Http::CurlHttpClient(client_config) {}
private:
  void OverrideOptionsOnConnectionHandle(CURL* curl) const override {
    curl_easy_setopt(curl, CURLOPT_SSL_OPTIONS, CURLSSLOPT_NATIVE_CA);
  }
};

class CustomHttpClientFactory : public Aws::Http::HttpClientFactory
{
public:
  std::shared_ptr<Aws::Http::HttpClient> CreateHttpClient(
    const Aws::Client::ClientConfiguration& client_configuration) const override {
    return std::make_shared<CustomCurlHttpClient>(client_configuration);
  }
  virtual std::shared_ptr<Aws::Http::HttpRequest> CreateHttpRequest(
    const Aws::String& uri, Aws::Http::HttpMethod method,
    const Aws::IOStreamFactory& streamFactory) const override {
    return CreateHttpRequest(Aws::Http::URI(uri), method, streamFactory);
  }
  virtual std::shared_ptr<Aws::Http::HttpRequest> CreateHttpRequest(
    const Aws::Http::URI& uri, Aws::Http::HttpMethod method,
    const Aws::IOStreamFactory& streamFactory) const override {
    auto request =
      std::make_shared<Aws::Http::Standard::StandardHttpRequest>(uri, method);
    request->SetResponseStreamFactory(streamFactory);
    return request;
  }
};

...

// init time
Aws::SDKOptions options;
options.httpOptions.initAndCleanupCurl = false; // I call curl_global_init/curl_global_cleanup
options.httpOptions.httpClientFactory_create_fn = []() {
  return std::make_shared<CustomHttpClientFactory>();
};