in go, grpc, language

I couldn’t find a single article explaining the different ways of connecting two Go applications using gRPC. After many tries, I came to the final 3 most useful ways to go:

Basic connection, no security

First, and easiest, is to not use TLS at all and not require any certificate. This mode is most useful if you have no certificate or if you don’t care about security, as transport is just HTTP, everything is in clear :-)

On the server side:

func (s *GroundServer) Serve(addr string) {

	lis, err := net.Listen("tcp", addr)
	if err != nil {
		return fmt.Errorf("could not list on %s: %s", addr, err)
	}

	srv := grpc.NewServer()
	pb.RegisterFooServer(srv, s)

	if err := srv.Serve(lis); err != nil {
		return fmt.Errorf("grpc serve error: %s", err)
	}
}

Here, we didn’t specify any argument to grpc.NewServer(), so no TLS!

On the client side:

func Run(addr string) error {

	creds := credentials.NewTLS(&tls.Config{InsecureSkipVerify: true})

	// conn, err := grpc.Dial(addr, grpc.WithInsecure())
	// Create a connection with the TLS credentials
	conn, err := grpc.Dial(addr, grpc.WithTransportCredentials(creds))
	if err != nil {
		return fmt.Errorf("could not dial %s: %s", addr, err)
	}

	defer conn.Close()

	// Initialize the client and make the request
	client := pb.NewFooClient(conn)
	pong, err := client.Feature(context.Background(), &pb.UniqID{Uniqid: "aaabc23232aaa", Timestamp: 1234})
	if err != nil {
		return fmt.Errorf("could not ping %s: %s", addr, err)
	}

	log.Printf("%s\n", pong.String())
	return nil
}

Secure connection, using TLS

Now, we establish a connection using TLS, which requires a certificate on the server side. To generate a self-signed certificate, you can use openssl like so:

openssl req -x509 -newkey rsa:4096 -keyout server.key \
     -out server.crt -nodes -days 365 \
     -subj '/CN=mynicedomainname.com'

Server side would be:

func (s *GroundServer) Serve(addr string) {
	lis, err := net.Listen("tcp", addr)
	if err != nil {
		return fmt.Errorf("could not list on %s: %s", addr, err)
	}

	creds, err := credentials.NewServerTLSFromFile(crt, key)
	if err != nil {
		panic(fmt.Errorf("could not load TLS keys: %s", err))
	}

	srv := grpc.NewServer(grpc.Creds(creds))
	pb.RegisterFooServer(srv, s)

	if err := srv.Serve(lis); err != nil {
		return fmt.Errorf("grpc serve error: %s", err)
	}
}

Client side would be :

func Run(addr string) {

	creds := credentials.NewTLS(&tls.Config{InsecureSkipVerify: true})

	conn, err := grpc.Dial(addr, grpc.WithTransportCredentials(creds))
	if err != nil {
		return fmt.Errorf("could not dial %s: %s", addr, err)
	}

	defer conn.Close()

	// Initialize the client and make the request
	client := pb.NewFooClient(conn)
	pong, err := client.Feature(context.Background(), &pb.UniqID{Uniqid: "aaabc23232aaa", Timestamp: 1234})
	if err != nil {
		return fmt.Errorf("could not ping %s: %s", addr, err)
	}

	log.Printf("%s\n", pong.String())
}

Even more secure connection, using TLS and server cert

We establish a connection using TLS, which requires a certificate on the server side.

Server side would be:

func (s *GroundServer) Serve(addr string) {
	lis, err := net.Listen("tcp", addr)
	if err != nil {
		return fmt.Errorf("could not list on %s: %s", addr, err)
	}

	creds, err := credentials.NewServerTLSFromFile(crt, key)
	if err != nil {
		panic(fmt.Errorf("could not load TLS keys: %s", err))
	}

	srv := grpc.NewServer(grpc.Creds(creds))
	pb.RegisterFooServer(srv, s)

	if err := srv.Serve(lis); err != nil {
		return fmt.Errorf("grpc serve error: %s", err)
	}
}

And on the client side:

func Run(addr string) error {
	creds, err := credentials.NewClientTLSFromFile(cert, "")
	if err != nil {
		return fmt.Errorf("could not load tls cert: %s", err)
	}

	conn, err := grpc.Dial(addr, grpc.WithTransportCredentials(creds))
	if err != nil {
		return fmt.Errorf("could not dial %s: %s", addr, err)
	}

	defer conn.Close()

	client := pb.NewDistributerClient(conn)
	pong, err := client.Feature(context.Background(), &pb.UniqID{Uniqid: "aaabc23232aaa", Timestamp: 1234})
	if err != nil {
		return fmt.Errorf("could not ping %s: %s", addr, err)
	}

	log.Printf("%s\n", pong.String())
	return nil
}

Gophers images provided by René French, Brian Ketelsen and Steve Francia, https://github.com/ashleymcnamara/gophers