I'm trying to connect a gRPC client written in Rust to a certain service, which is also written in Rust.
This service is running on Kubernetes, and its ingress is controlled by Nginx. The service is accessible via HTTPS (on the default port 443). I'm trying to configure the communication channel in various ways so that the client can connect to the service, but I keep getting the error as below:
Error: tonic::transport::Error(Transport, hyper::Error(Connect, Custom { kind: InvalidData, error: InvalidCertificate(UnknownIssuer) }))
I've tried pointing to various certificates that should be trusted, but it changes nothing. The dynamic certificate received by the ingress controller (based on Nginx) is issued by Let's Encrypt. It is a wildcard certificate, and the HTTPS connection is made based on it. I can connect to this service without any problem, for example, from code written in .NET; however, in Rust, this is a challenge (probably to make things not too simple or too normal as in other programming languages). Of course, there is no possibility to directly use configurations from rustls::ClientConfig, which would make things much easier (as in the code below):
// Add root certificates to the root store
let mut root_store = rustls::RootCertStore::empty();
root_store.extend(webpki_roots::TLS_SERVER_ROOTS.iter().cloned());
// Create a TLS config
let tls_config = rustls::ClientConfig::builder()
.with_root_certificates(root_store) // .with_native_roots()?
.with_no_client_auth();
Below is the code I've tried to use to force the client to connect to the service.
This can't be that difficult! I must be making a mistake somewhere.
When I dump the certificate data, it is as follows (retrieved using openssl
):
subject=CN = *.my-service.eu
issuer=C = US, O = Let's Encrypt, CN = R3
Does anyone know any working method to configure the client to connect to a service
running on TLS (HTTPS)?
let service_cert_str = r#"-----BEGIN CERTIFICATE---...ThanQ==-----END CERTIFICATE-----"#;
let lets_encrypt_cert_str = r#"-----BEGIN CERTIFICATE-----MIIF...oq7hHwg==-----END CERTIFICATE-----"#;
let isrg_root_cert_str = r#"-----BEGIN CERTIFICATE-----MIIFa...reGCc=-----END CERTIFICATE-----"#;
// let pem = pem::parse(service_cert_str)?;
let pem = pem::parse(lets_encrypt_cert_str)?;
// let pem = pem::parse(isrg_root_cert_str)?;
let ca_certificate = Certificate::from_pem(pem.contents());
let tls_config = ClientTlsConfig::new()
.ca_certificate(ca_certificate)
.domain_name("some.my-service.eu");
let channel = tonic::transport::channel::Channel::from_static("https://some.my-service.eu")
.tls_config(tls_config)?
//.tls_config(Default::default())?
.connect()
.await?;
let mut client = SomeMyServiceClient::with_interceptor(channel, move |mut req: Request<()>| {
req.metadata_mut().insert("authorization", token.clone());
Ok(req)
})
.send_compressed(CompressionEncoding::Gzip)
.accept_compressed(CompressionEncoding::Gzip);