It's my first time trying gRPC with MTLS, and I ran into some issues.
I have created a service for creating thumbnails in Go, and I want to connect it through gRPC with my Django API (Python). The service is in the services/thumbnailer/ directory, the API is in skynotes/ and certificates are in certs/. I host everything on Docker and that's what I’m getting:
celery.utils.serialization.UnpickleableExceptionWrapper: <_InactiveRpcError of RPC that terminated with:
2024-01-10 19:28:46 status = StatusCode.UNAVAILABLE
2024-01-10 19:28:46 details = "failed to connect to all addresses; last error: UNKNOWN: ipv4:172.18.0.2:50051: Peer name thumbnailer is not in peer certificate"
2024-01-10 19:28:46 debug_error_string = "UNKNOWN:Error received from peer {created_time:"2024-01-10T18:28:46.5870333+00:00", grpc_status:14, grpc_message:"failed to connect to all addresses; last error: UNKNOWN: ipv4:172.18.0.2:50051: Peer name thumbnailer is not in peer certificate"}"
Here is my code:
Certificates configurations
File cfssl.json
{
"signing": {
"default": {
"expiry": "87600h"
},
"profiles": {
"rootca": {
"usages": [
"signing",
"digital signature",
"key encipherment",
"cert sign",
"crl sign",
"server auth",
"client auth"
],
"ca_constraint": {
"is_ca": true
},
"expiry": "87600h"
},
"server": {
"usages": [
"signing",
"digital signing",
"key encipherment",
"server auth"
],
"expiry": "87600h"
},
"client": {
"usages": [
"signing",
"digital signature",
"key encipherment",
"client auth"
],
"expiry": "87600h"
}
}
}
}
File csr.json
{
"hosts": [
"localhost",
"127.0.0.1"
],
"key": {
"algo": "ecdsa",
"size": 256
},
"CN": "localhost",
"names": []
}
File services/thumbnailer/main.go
func main() {
secureAddress := "0.0.0.0:50051"
tlsConfig, err := LoadTlSConfig("certs/server.pem", "certs/server-key.pem", "certs/root.pem")
if err != nil {
panic(err)
}
server := grpc.NewServer(
grpc.Creds(tlsConfig),
grpc.UnaryInterceptor(MiddlewareHandler),
)
api.RegisterThumbnailServiceServer(server, new(api.Server))
basectx, casncel := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
defer casncel()
listener, err := net.Listen("tcp", secureAddress)
if err != nil {
panic(err)
}
go func() {
<-basectx.Done()
server.GracefulStop()
log.Println("bye")
}()
log.Println("listen and serving...")
if err := server.Serve(listener); err != nil {
panic(err)
}
}
File skynotes/services/client.py
import grpc
from thumbnailer import thumbnailer_pb2, thumbnailer_pb2_grpc
class Certs:
root = None
cert = None
key = None
def __init__(self, root, cert, key):
self.root = open(root, "rb").read()
self.cert = open(cert, "rb").read()
self.key = open(key, "rb").read()
class Client:
thumbnailer = None
def __init__(self, addr: str, crt: Certs):
creds = grpc.ssl_channel_credentials(crt.root, crt.key, crt.cert)
channel = grpc.secure_channel(addr, creds)
self.thumbnailer = thumbnailer_pb2_grpc.ThumbnailServiceStub(channel)
def generate_thumbnail(self, content: bytes) -> bytes:
return self.thumbnailer.GenerateThumbnail(
thumbnailer_pb2.ThumbnailRequest(content=content)
).thumbnail
^ This is the class for connecting to RPC, and the issue seems to be called in the grpc.ssl_channel_credentials line.
I tried multiple combinations of using PEM files, but none of these worked.