前言

假设gRPC服务端的主机名为qw.er.com,需要为gRPC服务端和客户端之间的通信配置tls双向认证加密。

生成证书

  1. 生成ca根证书。生成过程会要求填写密码、CN、ON、OU等信息,记住密码。
openssl req -x509 -newkey rsa:4096 -keyout ca.key -out ca.crt -subj "/CN=qw.er.com" -days 365
  1. 新建并编辑文件openssl.cnf文件。req_distinguished_name中内容按需填写,DNS.1要替换成实际域名。
[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
  1. 生成服务端证书
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
  1. 生成客户端证书
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示例

用的还是入门级的helloworld

syntax = "proto3";   // protocol buffers版本
option go_package = "./;proto"; // 生成的Go代码将被放在当前目录,并使用proto作为包名称 // 定义grpc服务的接口。服务就是一组可被远程调用的方法
service Greeter {
// 定义远程调用方法
rpc SayHello (HelloRequest) returns (HelloReply);
} // 定义消息格式和消息类型
message HelloRequest {
string name = 1; // 1 是二进制格式中的字段编号, 应该唯一
} 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

服务端代码示例

如果需要客户端和服务端直接通信,可以参考以下示例代码。

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() // 通过服务端的证书和密钥直接创建X.509密钥对
certificate, err := tls.LoadX509KeyPair(*crtFile, *keyFile)
if err != nil {
log.Fatalf("Failed to load key pair: %v", err)
} // 通过CA创建证书池
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服务器凭证创建新的gRPC服务实例
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)
}
}

运行:

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)
// 创建带有超时时间的上下文, cancel可以取消上下文
ctx, cancel := context.WithTimeout(context.Background(), time.Second*3)
defer cancel() // 业务代码处理部分 ...
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())
}
}

运行:

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代理

某些场景下服务端和客户端无法直接通信,需要在中间加个nginx反向代理服务端。目前个人方案是客户端与nginx之间为https双向加密通信,nginx与服务端之间为http普通通信。

客户端代码无需改动,服务端就是去掉tls相关配置,示例:

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 实现 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:

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;
# 验证客户端证书
ssl_verify_client on;
ssl_client_certificate /home/admin/apps/openresty/nginx/certs/qwer/ca.crt; # 反向代理服务端
location / {
grpc_pass grpc://192.168.1.111:8010;
}
}

[grpc]双向tls加密认证的更多相关文章

  1. elk 系列:Elasticsearch 7.2 集群部署+TLS 加密+认证登陆

    背景 2019年5月21日,Elastic官方发布消息: Elastic Stack 新版本6.8.0 和7.1.0的核心安全功能现免费提供. 这意味着用户现在能够对网络流量进行加密.创建和管理用户. ...

  2. Go gRPC进阶-TLS认证+自定义方法认证(七)

    前言 前面篇章的gRPC都是明文传输的,容易被篡改数据.本章将介绍如何为gRPC添加安全机制,包括TLS证书认证和Token认证. TLS证书认证 什么是TLS TLS(Transport Layer ...

  3. idou老师教你学Istio 17 : 通过HTTPS进行双向TLS传输

    众所周知,HTTPS是用来解决 HTTP 明文协议的缺陷,在 HTTP 的基础上加入 SSL/TLS 协议,依靠 SSL 证书来验证服务器的身份,为客户端和服务器端之间建立“SSL”通道,确保数据运输 ...

  4. 《LDAP服务器和客户端的加密认证》RHEL6——第二篇 运维工程师必考

    服务端的配置: (基于原先配好的ldap服务器)打开加密认证: Iptables –F  setenforce 0 1.编辑ldap的配置文件:slapd.conf 2.启动ldap服务器: 3.切换 ...

  5. Docker实战 | 第四篇:Docker启用TLS加密解决暴露2375端口引发的安全漏洞,被黑掉三台云主机的教训总结

    一. 前言 在之前的文章中 IDEA集成Docker插件实现一键自动打包部署微服务项目,其中开放了服务器2375端口监听,此做法却引发出来一个安全问题,在上篇文章评论也有好心的童鞋提示,但自己心存侥幸 ...

  6. mosquitto ---SSL/TLS 单向认证+双向认证

    生成证书 # * Redistributions in binary form must reproduce the above copyright #   notice, this list of ...

  7. 基于mosquitto的MQTT服务器---SSL/TLS 单向认证+双向认证

    基于mosquitto的MQTT服务器---SSL/TLS 单向认证+双向认证 摘自:https://blog.csdn.net/ty1121466568/article/details/811184 ...

  8. MQTT TLS 加密传输

    MQTT TLS 加密传输 Mosquitto原生支持了TLS加密,TLS(传输层安全)是SSL(安全套接层)的新名称,生成证书后再配置一下MQTT代理,本文主要介绍Mqtt如何实现双向认证和单向认证 ...

  9. Linux的加密认证功能以及openssl详解

    一.详细介绍加密.解密技术 现在的加密/解密技术主要有三种:对称加密,非对称加密,和单向加密 这三种加密解密技术的组合就是现在电子商务的基础,它们三个有各自最适合的领域,而且所要完成的功能也是不同的, ...

  10. SSL/TLS 加密新纪元 - Let's Encrypt

    转自: https://linux.cn/article-6565-1.html SSL/TLS 加密新纪元 - Let's Encrypt 根据 Let's Encrypt 官方博客消息,Let's ...

随机推荐

  1. Hardhat 开发框架 - Solidity开发教程连载

    Decert.me 要连载教程了, <Solidity 开发教程> 力求系统.深入的介绍 Solidity 开发, 同时这是一套交互式教程,你可以实时的修改教程里的合约代码并运行. 本教程 ...

  2. 源端为备库的场景下Duplicate失败问题

    环境: Oracle 11.2.0.3 + OEL 7.9 A -> B -> C 级联ADG环境:db11g -> db11gadg -> db11gcas 之前测试提到,从 ...

  3. ERROR: Failed to install the following Android SDK packages as some licences have not been accepted.

    android studio 配置sdk时提示如下错误 麻麻蛋~ 根据accepted 了解到是安装android-26时未被允许:于是执行如下步骤 1.cd 到sdk目录 D:\develop\An ...

  4. docker升级gitlab

    昨天在家部署了gitlab,版本居然是15.10,公司版本却是14.6,升级一波. 官方文档: https://docs.gitlab.com/ee/update/#upgrading-without ...

  5. odoo开发教程十五:仪表板

    仪表盘可以通过外部ID引用其他视图文件的内容,整合到一个界面进行显示. 一:建立仪表盘视图文件 views/session_board.xml: 通过外部id引入要展示的视图文件--定义仪表板form ...

  6. P3498 [POI2010]KOR-Beads 题解

    前言: 最近在做哈希的题,发现了这道好题,看题解里很多大佬的方法都很巧妙,自己就发一个较为朴素的方法吧. 题意: 题目传送门 给你一个序列,需要求出数 k,使划分的子串长度为 k 时,不同的子串数量最 ...

  7. .Net8罕见的技术:MSIL的机器码简析

    前言 一般的只有最终的汇编代码才有机器码表示,然一个偶然的机会发现,MSIL(Microsoft intermediate language)作为一个中间语言表示,居然也有机器码,其实这也难怪,计算机 ...

  8. Atcoder-AGC033C

    看到这道题,是个博弈论,没见过树上的,于是想到在数列里的博弈论,又联想到树的特殊形式----链. 于是我们来讨论一下链的情况(对于没有硬币的点,我们就视为它被删掉了): 讨论链的情况 发现若是选择两端 ...

  9. 使用Git进行版本控制和协作:代码共享、协作和版本管理

    目录 引言 Git 是一款开源的分布式版本控制系统,它已经成为了现代软件开发中必不可少的工具之一.在这篇文章中,我们将介绍如何使用 Git 进行版本控制和协作,以实现代码共享.协作和版本管理.Git ...

  10. FPGA加速技术在人机交互界面中的应用及优化

    目录 引言 随着人工智能.云计算.大数据等技术的发展,人机交互界面的重要性也越来越凸显.作为用户与计算机之间的桥梁,人机交互界面的性能和效率直接影响用户的体验和使用效果.为了优化人机交互界面的性能,我 ...