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):

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]