前言
假設(shè)gRPC服務(wù)端的主機(jī)名為qw.er.com
,需要為gRPC服務(wù)端和客戶端之間的通信配置tls雙向認(rèn)證加密。
生成證書
- 生成ca根證書。生成過程會(huì)要求填寫密碼、CN、ON、OU等信息,記住密碼。
openssl req -x509 -newkey rsa:4096 -keyout ca.key -out ca.crt -subj "/CN=qw.er.com" -days 365
- 新建并編輯文件
openssl.cnf
文件。req_distinguished_name中內(nèi)容按需填寫,DNS.1要替換成實(shí)際域名。
[req]
req_extensions = v3_req
distinguished_name = req_distinguished_name
prompt = no
[req_distinguished_name]
countryName = CN
stateOrProvinceName = Anhui
localityName = Hefei
organizationName = zhangsan
commonName = qw.er.com
[v3_req]
subjectAltName = @alt_names
[alt_names]
DNS.1 = qw.er.com
- 生成服務(wù)端證書
openssl req -newkey rsa:2048 -nodes -keyout server.key -out server.csr -subj "/CN=qw.er.com" -config openssl.cnf
# 提示輸入ca私鑰的密碼
openssl x509 -req -in server.csr -out server.crt -CA ca.crt -CAkey ca.key -CAcreateserial -days 365 -extensions v3_req -extfile openssl.cnf
- 生成客戶端證書
openssl req -newkey rsa:2048 -nodes -keyout client.key -out client.csr -subj "/CN=qw.er.com" -config openssl.cnf
# 提示輸入ca私鑰的密碼
openssl x509 -req -in client.csr -out client.crt -CA ca.crt -CAkey ca.key -CAcreateserial -days 365 -extensions v3_req -extfile openssl.cnf
proto示例
用的還是入門級(jí)的helloworld
syntax = "proto3"; // protocol buffers版本
option go_package = "./;proto"; // 生成的Go代碼將被放在當(dāng)前目錄,并使用proto作為包名稱
// 定義grpc服務(wù)的接口。服務(wù)就是一組可被遠(yuǎn)程調(diào)用的方法
service Greeter {
// 定義遠(yuǎn)程調(diào)用方法
rpc SayHello (HelloRequest) returns (HelloReply);
}
// 定義消息格式和消息類型
message HelloRequest {
string name = 1; // 1 是二進(jìn)制格式中的字段編號(hào), 應(yīng)該唯一
}
message HelloReply {
string message = 1;
}
生成go代碼:
protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative hello.proto
服務(wù)端代碼示例
如果需要客戶端和服務(wù)端直接通信,可以參考以下示例代碼。
package main
import (
pb "grpcs/proto"
"context"
"crypto/tls"
"crypto/x509"
"flag"
"fmt"
"log"
"net"
"os"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
)
var (
port = flag.Int("port", 8010, "the server port")
crtFile = flag.String("crt", "server.crt", "the server crt file")
keyFile = flag.String("key", "server.key", "the server key file")
caFile = flag.String("ca", "ca.crt", "the server ca file")
)
type server struct{
pb.UnimplementedGreeterServer
}
func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
log.Printf("Received: %v", in.GetName())
return &pb.HelloReply{Message: "Hello " + in.GetName()}, nil
}
func main() {
flag.Parse()
// 通過服務(wù)端的證書和密鑰直接創(chuàng)建X.509密鑰對(duì)
certificate, err := tls.LoadX509KeyPair(*crtFile, *keyFile)
if err != nil {
log.Fatalf("Failed to load key pair: %v", err)
}
// 通過CA創(chuàng)建證書池
certPool := x509.NewCertPool()
ca, err := os.ReadFile(*caFile)
if err != nil {
log.Fatalf("Failed to read ca: %v", err)
}
// 將來自CA的客戶端證書附加到證書池
if ok := certPool.AppendCertsFromPEM(ca); !ok {
log.Fatalf("Failed to append ca certificate")
}
opts := []grpc.ServerOption{
grpc.Creds( // 為所有傳入的連接啟用TLS
credentials.NewTLS(&tls.Config{
ClientAuth: tls.RequireAndVerifyClientCert,
Certificates: []tls.Certificate{certificate},
ClientCAs: certPool,
},
)),
}
listen, err := net.Listen("tcp", fmt.Sprintf("0.0.0.0:%d", *port))
if err != nil {
log.Fatalf("failed to listen %d port", *port)
}
// 通過傳入的TLS服務(wù)器憑證創(chuàng)建新的gRPC服務(wù)實(shí)例
s := grpc.NewServer(opts...)
pb.RegisterGreeterServer(s, &server{})
log.Printf("server listening at %v", listen.Addr())
if err := s.Serve(listen); err != nil {
log.Fatalf("Failed to serve: %v", err)
}
}
運(yùn)行:
go build -o server.bin
./server.bin -ca ca.crt -crt server.crt -key server.key -port 8010
客戶端代碼示例
package main
import (
pb "grpcc/proto"
"context"
"crypto/tls"
"crypto/x509"
"flag"
"log"
"os"
"time"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
)
var (
addr = flag.String("addr", "qw.er.com:8010", "server address")
hostname = flag.String("host", "qw.er.com", "server hostname")
crtFile = flag.String("crt", "client.crt", "client crt file")
keyFile = flag.String("key", "client.key", "client key file")
caFile = flag.String("ca", "ca.crt", "ca file")
name = flag.String("n", "zhangsan", "name")
)
func main() {
flag.Parse()
certificate, err := tls.LoadX509KeyPair(*crtFile, *keyFile)
if err != nil {
log.Fatalf("Failed to load client key pair, %v", err)
}
certPool := x509.NewCertPool()
ca, err := os.ReadFile(*caFile)
if err != nil {
log.Fatalf("Failed to read %s, error: %v", *caFile, err)
}
if ok := certPool.AppendCertsFromPEM(ca); !ok {
log.Fatalf("Failed to append ca certs")
}
opts := []grpc.DialOption{
grpc.WithTransportCredentials(credentials.NewTLS(
&tls.Config{
ServerName: *hostname,
Certificates: []tls.Certificate{certificate},
RootCAs: certPool,
})),
}
// conn, err := grpc.Dial(*addr, grpc.WithTransportCredentials(insecure.NewCredentials()))
conn, err := grpc.Dial(*addr, opts...)
if err != nil {
log.Fatalf("Connect to %s failed", *addr)
}
defer conn.Close()
client := pb.NewGreeterClient(conn)
// 創(chuàng)建帶有超時(shí)時(shí)間的上下文, cancel可以取消上下文
ctx, cancel := context.WithTimeout(context.Background(), time.Second*3)
defer cancel()
// 業(yè)務(wù)代碼處理部分 ...
r, err := client.SayHello(ctx, &pb.HelloRequest{Name: *name})
if err != nil {
log.Printf("Failed to greet, error: %v", err)
} else {
log.Printf("Greeting: %v",r.GetMessage())
}
}
運(yùn)行:
go build -o client.bin
./client.bin -addr qw.er.com:8010 -host qw.er.com -ca ca.crt -crt client.crt -key client.key -name 'lisi'
nginx代理
某些場景下服務(wù)端和客戶端無法直接通信,需要在中間加個(gè)nginx反向代理服務(wù)端。目前個(gè)人方案是客戶端與nginx之間為https雙向加密通信,nginx與服務(wù)端之間為http普通通信。
客戶端代碼無需改動(dòng),服務(wù)端就是去掉tls相關(guān)配置,示例:文章來源:http://www.zghlxwxcb.cn/news/detail-622562.html
package main
import (
"context"
"flag"
"fmt"
"log"
"net"
pb "grpcs/proto"
"google.golang.org/grpc"
)
var (
port = flag.Int("port", 8010, "The server port")
)
// server is used to implement hello.GreeterServer.
type server struct {
pb.UnimplementedGreeterServer
}
// SayHello 實(shí)現(xiàn) proto 中的 service Greeter
func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
log.Printf("Received: %v", in.GetName())
return &pb.HelloReply{Message: "Hello " + in.GetName()}, nil
}
func main() {
flag.Parse()
lis, err := net.Listen("tcp", fmt.Sprintf(":%d", *port))
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
s := grpc.NewServer()
pb.RegisterGreeterServer(s, &server{})
log.Printf("server listening at %v", lis.Addr())
if err := s.Serve(lis); err != nil {
log.Fatalf("failed to serve: %v", err)
}
}
tls證書配到nginx:文章來源地址http://www.zghlxwxcb.cn/news/detail-622562.html
server {
listen 80 ssl http2;
server_name qw.er.com;
# 證書文件路徑
ssl_certificate /home/admin/apps/openresty/nginx/certs/qwer/server.crt;
ssl_certificate_key /home/admin/apps/openresty/nginx/certs/qwer/server.key;
# 驗(yàn)證客戶端證書
ssl_verify_client on;
ssl_client_certificate /home/admin/apps/openresty/nginx/certs/qwer/ca.crt;
# 反向代理服務(wù)端
location / {
grpc_pass grpc://192.168.1.111:8010;
}
}
到了這里,關(guān)于[grpc]雙向tls加密認(rèn)證的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!