How to open a secure channel in python gRPC client without a client SSL certificate
Asked Answered
R

4

8

I have a grpc server (in Go) that has a valid TLS certificate and does not require client side TLS. For some reason I can not implement the client without mTLS in Python, even though I can do so in Golang.

In Python I have

os.environ["GRPC_VERBOSITY"] = "DEBUG"
# os.environ["GRPC_DEFAULT_SSL_ROOTS_FILE_PATH"] = "/etc/ssl/certs/ca-bundle.crt"

channel = grpc.secure_channel(ADDR, grpc.ssl_channel_credentials())
grpc.channel_ready_future(channel).result(timeout=10)

This gives me the following error

D0513 08:02:08.147319164   21092 security_handshaker.cc:181] Security handshake failed: {"created":"@1652446928.147311309","description":"Handshake failed","file":"src/core/lib/security/transport/security_handshaker.cc","file_line":377,"tsi_code":10,"tsi_error":"TSI_PROTOCOL_FAILURE"}

I can get this to work if I use SSL certificates by uncommenting the commented out line. I know for a fact that my server does not request, require or verify client certificates as The following Go code work perfectly

conn, err := grpc.DialContext(
    ctx,
    gRPCAddr,
    grpc.WithTransportCredentials(credentials.NewClientTLSFromCert(nil, "")),
)
dummyClient := dummy.NewDummyServiceClient(conn)
if _, err := dummyClient.Ping(context.Background(), &dummy.PingRequest{
    Ping: "go client ping",
}); err != nil {
    return fmt.Errorf("failed to ping: %w", err)
}
Ruthenium answered 13/5, 2022 at 13:10 Comment(3)
Is it possible one of the intermediate CAs is invalid? You should be able to test it using openssl's s_client, and trying to connect to the server ip/port.Riedel
@CarlMastrangelo I thought so too at first, but surely the intermediate CAs are valid because the go client is able to connect to the server with transport credentials (making the channel a secure one)?Ruthenium
If my memory serves the Go client handles TLS differently (pure Go reimplementation) than the BoringSSL/OpenSSL library does (Used by Python and wrapped languages). The difference in TLS libraries may be why there's a difference in verification. Hence, checking with openssl s_client will show for certain.Riedel
M
4

https://grpc.github.io/grpc/python/_modules/grpc.html#secure_channel has the docs for channel = grpc.secure_channel(ORBIUM_ADDR, grpc.ssl_channel_credentials()). This function relies on the class channel, see docs https://grpc.github.io/grpc/python/_modules/grpc/aio/_channel.html.

Basically, class Channel wraps C code to provide a secure channel. That wrapped C code expects the certificate. If you can implement in C, it might be easiest to just change the C code.

Mazurka answered 23/5, 2022 at 9:59 Comment(0)
W
6

If the certificate on the server-side is publicly signed, you can use:

grpc.secure_channel(ADDR, grpc.ssl_channel_credentials())

But that doesn't seem to work for you, so I guess the server certificate is signed by a root cert owned by you. You can pass in the root cert into the root_certificates field [1], and leave the other two fields empty. This use case is documented in our Authentication guide [2].

with open(os.environ["GRPC_DEFAULT_SSL_ROOTS_FILE_PATH"], 'rb') as f:
    creds = grpc.ssl_channel_credentials(f.read())

channel = grpc.secure_channel(ADDR, creds)

[1] https://grpc.github.io/grpc/python/grpc.html#grpc.ssl_channel_credentials

[2] https://grpc.io/docs/guides/auth/

Waylan answered 20/5, 2022 at 18:38 Comment(2)
Thank you for the answer, but as mentioned in the question, I have already considered this. I was looking for a way such that NO Certificate is required on the client side. This functionality is possible in the Go gRPC target. I was trying to emulate it in Python.Ruthenium
If I specify a port, is that part of ORBIUM_ADDR?Colleen
M
4

https://grpc.github.io/grpc/python/_modules/grpc.html#secure_channel has the docs for channel = grpc.secure_channel(ORBIUM_ADDR, grpc.ssl_channel_credentials()). This function relies on the class channel, see docs https://grpc.github.io/grpc/python/_modules/grpc/aio/_channel.html.

Basically, class Channel wraps C code to provide a secure channel. That wrapped C code expects the certificate. If you can implement in C, it might be easiest to just change the C code.

Mazurka answered 23/5, 2022 at 9:59 Comment(0)
R
1

The answer given by @former_Epsilon answered my question, however the solution I came up with for the problem was different and I ended up using a secure_channel so I wanted to post an answer for that as well.

import os
import grpc

# configure this dict for your systems
system_certs_map = {
    "Windows": "<Path to system cert>",
    "Darwin": "$REQUESTS_CA_BUNDLE",
    "Linux": "/etc/ssl/certs/ca-bundle.crt",
}

os.environ["GRPC_DEFAULT_SSL_ROOTS_FILE_PATH"] = system_certs_map[platform.system()]
channel_credentials = grpc.ssl_channel_credentials()
Ruthenium answered 8/8, 2022 at 10:34 Comment(0)
S
-4

My guess based on Python GRPC doc https://grpc.github.io/grpc/python/grpc.html

channel = grpc.insecure_channel(ORBIUM_ADDR)

instead of:

channel = grpc.secure_channel(ORBIUM_ADDR, grpc.ssl_channel_credentials())
Schnorkle answered 17/5, 2022 at 0:4 Comment(1)
The question specifies a secure channel. insecure_channel is insecure and has no TLS enabled. I still want TLS but I want to open a secure TLS enabled channel without any client side certs (because I only want server-side TLS)Ruthenium

© 2022 - 2024 — McMap. All rights reserved.