grpc: Python gRPC with TLS no longer seems to find the correct credentials by default

What version of gRPC and what language are you using?

Python gRPC 1.32.0

What operating system (Linux, Windows,…) and version?

macOS 11.6 (Big Sur)

What runtime / compiler are you using (e.g. python version or version of gcc)

Python 3.8.3

What did you do?

Attempt to establish a secure channel with a remote host using valid SSL certificate.

What did you expect to see?

A secure channel established. Example:

import grpc

host = "hostname"
port = 443

credentials = grpc.ssl_channel_credentials()
channel = grpc.secure_channel('{}:{}'.format(host, port), credentials)

try:
    grpc.channel_ready_future(channel).result(timeout=5)
    print(f"Successfully established secure channel to {host}")
except:
    print(f"Timeout. Failed to establish secure channel to {host}")

Should return:

Successfully established secure channel to hostname

What did you see instead?

E1014 14:19:37.997415000 123145637335040 ssl_transport_security.cc:1446] Handshake failed with fatal error SSL_ERROR_SSL: error:1000007d:SSL routines:OPENSSL_internal:CERTIFICATE_VERIFY_FAILED.

This has previously worked for everyone at my current organization using the gRPC Python client. Instead, on all hosts (macOS, centos) and Python versions (>=3.6) we suddenly need to explicitly pass the credentials location. The following works on macOs:

import grpc

host = "hostname"
port = 443

with open('/etc/ssl/cert.pem', 'rb') as f:
    certificate_chain = f.read()

credentials = grpc.ssl_channel_credentials(root_certificates=certificate_chain)
channel = grpc.secure_channel('{}:{}'.format(host, port), credentials)

try:
    grpc.channel_ready_future(channel).result(timeout=5)
    print(f"Successfully established secure channel to {host}")
except:
    print(f"Timeout. Failed to establish secure channel to {host}")

Can verify curl finds the proper SSL credentials.

Anything else we should know about your project / environment?

The SSL cert is valid, here’s is some information from curl -v hostname on the host we’re trying to reach.

* ALPN, server accepted to use http/1.1
* Server certificate:
*  subject: CN=hostname
*  start date: Sep  1 20:41:14 2021 GMT
*  expire date: Nov 30 20:41:13 2021 GMT
*  subjectAltName: host "hostname" matched cert's "hostname"
*  issuer: C=US; O=Let's Encrypt; CN=R3
*  SSL certificate verify ok.

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Comments: 21 (5 by maintainers)

Most upvoted comments

Using certifi, this works fine.

>>> cert = Path(certifi.where()).read_bytes()
>>> channel = grpc.secure_channel("foo.bar:443", grpc.ssl_channel_credentials(root_certificates=cert))
>>> grpc.channel_ready_future(channel)
<grpc._utilities._ChannelReadyFuture object at 0x7ff8086d5150>

However, without it, LetsEncrypt certificates are broken even in the Python 3.10 Docker image, so it’s not just a OS X thing.

Edit: Looks like this will be fixed in the next release. #27539 was merged in.

It looks like v1.41.0 came out 18 days ago and #27619 was merged 9 days ago, so there’s no current release that has this fix, if I’m not mistaken.

@gitpushdashf it works! thanks so much for the help everyone.

Yes, by default grpc/etc/roots.pem is used in gRPC python to verify the peer’s certificate, and due to the recent Let’s Encrypt issue, we need to explicitly remove the DST Root CA X3 cert from the roots.pem. Please see this comment for more details. #27539 has been cherrypicked into v.1.41.x (#27619).

Here might be a less ideal way to workaround the issue:

export GRPC_DEFAULT_SSL_ROOTS_FILE_PATH="/etc/ssl/cert.pem'"

I’m not sure what is causing the issue, @yihuazhang if you got a second, can you help to take a look at this issue?

@kylegallatin Can you upgrade to the latest gRPC release v1.41.0 and see if the problem persists?