「go-zero 系列」zRPC 设置证书认证

1,011 阅读2分钟

💬

错觉。

💻

介绍完 grpc tls 证书认证,接下来继续介绍如何在 go-zero zrpc 中如何配置,关于 go-zero zrpc 的简单使用见 zrpc demo

服务端

AddOptions 即可,这里使用了 go1.16 的特性 embed 加载证书

import _ "embed"

var (
	configFile = flag.String("f", "etc/hello.yaml", "the config file")

	//go:embed tls/server.pem
	serverPem []byte

	//go:embed tls/server.key
	serverKey []byte

	//go:embed tls/ca.pem
	caPem []byte
)

//getCreds 添加凭证
func getCreds() credentials.TransportCredentials {
	// cert, err := tls.LoadX509KeyPair("../../tls/server.pem", "../../tls/server.key")

	cert, err := tls.X509KeyPair(serverPem, serverKey)
	if err != nil {
		log.Fatalf("tls.LoadX509KeyPair err: %v", err)
	}

	certPool := x509.NewCertPool()
	// ca, err := os.ReadFile("../../tls/ca.pem")
	// if err != nil {
	// 	log.Fatalf("ioutil.ReadFile err: %v", err)
	// }

	if ok := certPool.AppendCertsFromPEM(caPem); !ok {
		log.Fatalf("certPool.AppendCertsFromPEM err")
	}

	creds := credentials.NewTLS(&tls.Config{
		Certificates: []tls.Certificate{cert},
		ClientAuth:   tls.RequireAndVerifyClientCert,
		ClientCAs:    certPool,
	})

	return creds
}
func main() {
	flag.Parse()

	var c config.Config
	conf.MustLoad(*configFile, &c)
	ctx := svc.NewServiceContext(c)
	srv := server.NewHelloServer(ctx)

	s := zrpc.MustNewServer(c.RpcServerConf, func(grpcServer *grpc.Server) {
		hello.RegisterHelloServer(grpcServer, srv)
	})

	// 添加证书设置
	cred := getCreds()
	s.AddOptions(grpc.Creds(cred))

	defer s.Stop()

	fmt.Printf("Starting rpc server at %s...\n", c.ListenOn)
	s.Start()
}
ronething at ronething-dell-ubuntu in ~/Documents/grpc-sample/go-zero-tls (master●●) 
$ ./main 
Starting rpc server at 127.0.0.1:9999...
{"@timestamp":"2021-04-15T00:33:50.129+08","level":"stat","content":"(rpc) shedding_stat [1m], cpu: 254, total: 0, pass: 0, drop: 0"}
{"@timestamp":"2021-04-15T00:33:50.130+08","level":"stat","content":"CPU: 242m, MEMORY: Alloc=2.6Mi, TotalAlloc=6.0Mi, Sys=71.1Mi, NumGC=2"}
{"@timestamp":"2021-04-15T00:34:50.129+08","level":"stat","content":"(rpc) shedding_stat [1m], cpu: 143, total: 0, pass: 0, drop: 0"}

客户端

因为 zrpc newClient 写死了 dialoptions WithInsecure 如果直接使用原先的 test client 会报错

// https://github.com/tal-tech/go-zero/blob/5aded99df53c6f3eb0de896c213b65a4d6478b83/zrpc/internal/client.go#L67
func (c *client) buildDialOptions(opts ...ClientOption) []grpc.DialOption {
	var cliOpts ClientOptions
	for _, opt := range opts {
		opt(&cliOpts)
	}

	options := []grpc.DialOption{
		grpc.WithInsecure(), // 这里设置了 WithInsecure
		grpc.WithBlock(),
		WithUnaryClientInterceptors(
			clientinterceptors.TracingInterceptor,
			clientinterceptors.DurationInterceptor,
			clientinterceptors.BreakerInterceptor,
			clientinterceptors.PrometheusInterceptor,
			clientinterceptors.TimeoutInterceptor(cliOpts.Timeout),
		),
	}

	return append(options, cliOpts.DialOptions...)
}
ronething at ronething-dell-ubuntu in ~/Documents/grpc-sample/go-zero-tls/helloclient (master●●) 
$ go test -v -run TestPing 
=== RUN   TestPing
2021/04/15 00:36:19 rpc dial: discov://127.0.0.1:2379/hello.rpc, error: context deadline exceeded, make sure rpc service "hello.rpc" is alread started
exit status 1
FAIL	grpc-sample/go-zero-tls/helloclient	3.007s

两种解决方案:

1、不用 zrpc 的 client 使用原生 grpc client 进行连接

2、修改相关代码,其实我也已经提了 issue 不过官方暂时没有回复

临时修改一下看一下效果

首先将 go-zero zrpc client 连接选项 WithInsecure 注释

image-20210415004316881.png

package helloclient

import (
	"context"
	"crypto/tls"
	"crypto/x509"
	"log"
	"os"
	"testing"

	"github.com/tal-tech/go-zero/core/discov"
	"github.com/tal-tech/go-zero/zrpc"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
)

func TestPing(t *testing.T) {

	// 添加证书设置
	cred := getCreds()
	client := zrpc.MustNewClient(
		zrpc.RpcClientConf{
			Etcd: discov.EtcdConf{
				Hosts: []string{"127.0.0.1:2379"},
				Key:   "hello.rpc",
			},
		},
		zrpc.WithDialOption(grpc.WithTransportCredentials(cred)), // 证书认证
	)

	h := NewHello(client)
	resp, err := h.Ping(context.TODO(), &Request{
		Ping: "ashing",
	})
	if err != nil {
		t.Error(err)
		return
	}

	t.Log(resp.GetPong())
}

//getCreds 添加凭证
func getCreds() credentials.TransportCredentials {
	cert, err := tls.LoadX509KeyPair("../tls/client.pem", "../tls/client.key")
	if err != nil {
		log.Fatalf("tls.LoadX509KeyPair err: %v", err)
	}

	certPool := x509.NewCertPool()
	ca, err := os.ReadFile("../tls/ca.pem")
	if err != nil {
		log.Fatalf("ioutil.ReadFile err: %v", err)
	}

	if ok := certPool.AppendCertsFromPEM(ca); !ok {
		log.Fatalf("certPool.AppendCertsFromPEM err")
	}

	creds := credentials.NewTLS(&tls.Config{
		Certificates: []tls.Certificate{cert},
		ServerName:   "localhost",
		RootCAs:      certPool,
	})

	return creds
}

ronething at ronething-dell-ubuntu in ~/Documents/grpc-sample/go-zero-tls/helloclient (master●●) 
$ go test -v -run TestPing      
=== RUN   TestPing
2021/04/15 01:01:16 {"@timestamp":"2021-04-15T01:01:16.963+08","level":"stat","content":"p2c - conn: 127.0.0.1:9999, load: 670, reqs: 1"}
    hello_test.go:40: hello: ashing
--- PASS: TestPing (0.01s)
PASS
ok  	grpc-sample/go-zero-tls/helloclient	0.013s

返回正常。完整代码见 go-zero-tls

🌞

嗯 早点睡。

写于 2021-04-14 夜晚