gRPC提供了内置的授权机制(Authorization),也提供接口用于扩展自定义授权验证。gRPC旨在和多种身份验证(Authentication)机制配合使用,可以轻松安全的使用gRPC同其他系统进行通信。

gRPC支持下面几种机制:

  • SSL/TLS:gRPC集成了SSL/TLS,并促进使用SSL/TLS对服务进行身份验证,并对客户端和服务端之间交互的所有数据进行加密
  • ALTS
  • Token-based authentication with Google

同时,也支持扩展自定义认证机制。

1. 前言

如下面一个最基本的Hello grpc程序中, 请求和响应都是明文传输,容易造成敏感信息、伪造篡改等问题。

server.go

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package main

import (
	"context"
	"log"
	"net"

	"google.golang.org/grpc"
	pb "xwxwgo.com/hello-grpc/lib/protos"
)

type Server struct {
	pb.UnimplementedHelloServiceServer
}

func (s *Server) SayHello(ctx context.Context, req *pb.HelloReq) (*pb.HelloRsp, error) {
	return &pb.HelloRsp{Reply: req.Greeting}, nil
}

func main() {
	lis, err := net.Listen("tcp", ":8080")
	if err != nil {
		log.Fatalf("failed to listen: %v", err)
	}

	server := grpc.NewServer()
	pb.RegisterHelloServiceServer(server, &Server{})
	server.Serve(lis)
}

client/main.go

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package main

import (
	"context"
	"log"

	"google.golang.org/grpc"
	pb "xwxwgo.com/hello-grpc/lib/protos"
)

func main() {
	conn, err := grpc.Dial(":8080", grpc.WithInsecure())
	if err != nil {
		log.Fatalf("dail field: %v", err)
	}
	defer conn.Close()

	client := pb.NewHelloServiceClient(conn)
	rsp, err := client.SayHello(context.Background(), &pb.HelloReq{Greeting: "Hello grpc"})
	if err != nil {
		log.Fatalf("call server failed: %v", err)
	}
	log.Printf("SayHello: %+v", rsp)
}

通过Wireshark本地gRPC抓包,可以看到请求具体信息:

image-20211019163046195

通过SSL/TLS对服务进行身份认证,可以客户端和服务端之间交互的所有数据进行加密。

2. SSL/TLS认证

证书制作

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
# 私钥
$ openssl ecparam -genkey -name secp384r1 -out server.key

# 自签公钥
$ openssl req -new -x509 -sha256 -key server.key -out server.pem -days 3650

You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:
State or Province Name (full name) [Some-State]:
Locality Name (eg, city) []:
Organization Name (eg, company) [Internet Widgits Pty Ltd]:
Organizational Unit Name (eg, section) []:
Common Name (e.g. server FQDN or YOUR name) []:
Email Address []:

Client:

credentials.NewClientTLSFromFile:通过服务端的自签公钥和服务名称,来构造TLS凭证

1
2
3
4
5
creds, _ := credentials.NewClientTLSFromFile(certFile, "hello-grpc")
conn, _ := grpc.Dial("localhost:50051", grpc.WithTransportCredentials(creds))
// error handling omitted
client := pb.NewHelloServiceClient(conn)
// ...

Server:

credentials.NewServerTLSFromFile:服务端证书文件和密钥构造 TLS 凭证

1
2
3
4
5
6
creds, _ := credentials.NewServerTLSFromFile(certFile, keyFile)
s := grpc.NewServer(grpc.Creds(creds))
pb.RegisterHelloServiceServer(s, &server.HelloServer{})
lis, _ := net.Listen("tcp", "localhost:50051")
// error handling omitted
s.Serve(lis)

再次进行抓包:

image-20211019164650896

附录:

  1. Analyzing gRPC messages using Wireshark
  2. Loopback capture