9

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)
}
3
  • 1
    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. Commented May 19, 2022 at 3:56
  • @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)? Commented May 19, 2022 at 9:58
  • 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. Commented May 19, 2022 at 11:14

4 Answers 4

7

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/

Sign up to request clarification or add additional context in comments.

4 Comments

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.
If I specify a port, is that part of ORBIUM_ADDR?
@JohnZabroski: The port is part of the ADDR string as host:port like example.com:443.
@MartinUeding Thanks, I figured out my challenge. I was given a certificate from a vendor that was not a root in my own certificate store - it was an intermediary certificate. I had to do the mapping to resolve it to something the Windows Certificate Store could recognize.
4
+50

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.

Comments

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

Comments

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

1 Comment

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)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.