BewareMyPower commented on issue #184:
URL: 
https://github.com/apache/pulsar-client-cpp/issues/184#issuecomment-1420509146

   The root cause is that the OAuth2 client credential flow does not configure 
any CA cert for verification.
   
   TL; DR, just see the following workaround section.
   
   ### Workaround
   
   Just take Ubuntu 20.04 for example, whose default CA file is 
`/etc/ssl/certs/ca-certificates.crt`.
   
   C++ client: pass the CA cert path to `ClientConfiguration`:
   
   ```c++
   ClientConfiguration config;
   config.setTlsTrustCertsFilePath("/etc/ssl/certs/ca-certificates.crt")
   Client client(serviceUrl, config);
   ```
   
   Python and Node.js clients: though they also have the related configs to set 
the CA cert path, it does not work actually. (See the following section for 
detailed explanation). You have to copy the CA cert file to the specific path, 
which is determined by your system.
   
   ```bash
   # All Python wheels for Linux use /etc/pki/tls/certs/ca-bundle.crt as the 
path of the CA cert
   sudo mkdir -p /etc/pki/tls/certs
   sudo cp /etc/ssl/certs/ca-certificates.crt /etc/pki/tls/certs/ca-bundle.crt
   ```
   
   ### Analysis
   
   From https://curl.se/docs/sslcerts.html we can see
   
   > If the remote server uses a self-signed certificate, if you do not install 
a CA cert store, if the server uses a certificate signed by a CA that is not 
included in the store you use or if the remote host is an impostor 
impersonating your favorite site, and you want to transfer files from this 
server, do one of the following:
   > 1. Tell libcurl to not verify the peer. With libcurl you disable this with 
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, FALSE);
   
   Before Pulsar C++ client 3.0.0, the OAuth2 client credential flow didn't 
verify the peer, which leads to the 
[CVE-2022-33684](https://lists.apache.org/thread/ky1ssskvkj00y36k7nys9b5gm5jjrzwv).
 Then https://github.com/apache/pulsar/pull/16064 fixes it by enabling the 
option to verify the peer.
   
   However, the libcurl dependency is **built from source** and linked 
statically by:
   - Pre-built C++ libraries, e.g. the deb package
   - Python wheels
   - Node.js C++ Add-ons
   
   When it's built from source, the default path of the CA cert is detected 
automatically and determined by the OS. e.g. the Python wheels for Linux are 
built on `manylinux2014` images, the bundled path is 
`/etc/pki/tls/certs/ca-bundle.crt`. You can see the path from any workflow 
[here](https://github.com/apache/pulsar-client-python/actions/runs/4070977203/jobs/7014442726):
   
   
![20230207132723](https://user-images.githubusercontent.com/18204803/217209563-fcea5cc8-0157-473c-b8d6-4b91b71ec532.jpg)
   
   The only workaround for Python and Node.js clients on Linux is copying your 
CA cert file into that path. The C++ client can work by configuring 
`tlsTrustCertsFilePath` because the deb package is compiled on Debian, rpm 
package is compiled on CentOS, the default path of the CA cert file is the same 
with the path on Debian-based distros and RedHat-based distros. However, the 
Python client for Linux is built on `manylinux` image, which is based on a 
RedHat-based distro, so the path is different with the Debian-based distros.
   
   ### How to reproduce
   
   Create an account on StreamNative cloud to get the client id and client 
secret. You can switch to your own vendor if you have an Pulsar service with 
OAuth2 authentication enabled.
   
   TlsOauth2Example.cc
   
   ```c++
   #include <pulsar/Client.h>
   using namespace pulsar;
   
   int main(int argc, char *argv[]) {
       std::string params = R"({
       "issuer_url": "https://auth.streamnative.cloud/";, // St
       "client_id": "<your-client-id>",
       "client_secret": "<your-client-secret>",
       "audience": "<your-audience>"})";
       ClientConfiguration config;
       config.setAuth(AuthOauth2::create(params));
       // NOTE: here we use a customized CA cert path
       config.setTlsTrustCertsFilePath("/app/ca-certificates.crt");
       Client client("pulsar+ssl://<your-host>:<your-port>", config);
   
       Producer producer;
       auto result = 
client.createProducer("persistent://public/default/my-topic", producer);
       if (result != ResultOk) {
           std::cerr << "Failed to create producer: " << result << std::endl;
           return 1;
       }
   
       client.close();
       return 0;
   }
   ```
   
   Start a docker container:
   
   ```bash
   docker run -v $PWD:/app -it ubuntu:20.04 /bin/bash
   ```
   
   Run the following commands inside the container:
   
   ```bash
   cd /app
   apt update -y
   apt install -y curl g++
   curl -O -L 
https://archive.apache.org/dist/pulsar/pulsar-client-cpp-3.1.1/deb-x86_64/apache-pulsar-client-dev.deb
   curl -O -L 
https://archive.apache.org/dist/pulsar/pulsar-client-cpp-3.1.1/deb-x86_64/apache-pulsar-client.deb
   apt install ./apache-pulsar-client*.deb
   # NOTE: move the CA file from the default path to /app
   mv /etc/ssl/certs/ca-certificates.crt .
   g++ TlsOauth2Example.cc -std=c++11 -lpulsar
   ./a.out
   ```
   
   You will see the following output:
   
   ```
   2023-02-07 10:05:33.619 ERROR [139656646591168] AuthOauth2:224 | Response 
failed for getting the well-known configuration 
https://auth.streamnative.cloud/. Error Code 77: error setting certificate 
verify locations:  CAfile: /etc/ssl/certs/ca-certificates.crt CApath: none
   ...
   2023-02-07 10:05:37.566 ERROR [139656646563584] ClientImpl:184 | Error 
Checking/Getting Partition Metadata while creating producer on 
persistent://public/default/my-topic -- AuthenticationError
   Failed to create producer: 2023-02-07 10:05:37.566 INFO  [139656646563584] 
ClientConnection:267 | [172.17.0.2:55690 -> 3.219.62.61:6651] Destroyed 
connection
   AuthenticationError
   ```
   
   ### Solution
   
   We should use the `tlsTrustCertsFilePath` for HTTP requests in OAuth2's 
client credential flow, just like `HTTPLookupService` does: 
https://github.com/apache/pulsar-client-cpp/blob/0631e366708fefff399a6d224674428a048e8d37/lib/HTTPLookupService.cc#L260-L262
   
   I will push a PR soon.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to