Go gRPC 系列:

跟我一起学Go系列:gRPC 拦截器使用

跟我一起学Go系列:gRPC 入门必备

第一篇入门说过 gRPC 底层是基于 HTTP/2 协议的,HTTP 本身不带任何加密传输功能,基于 SSL 的 HTTPS 协议才是加密传输。gRPC 使用了 HTTP/2 协议但是并未使用 HTTPS,即少了加密传输的部分。

对于加密传输的部分 gRPC 将它抽出来作为一个组件,可以由用户自由选择。gRPC 内默认提供了两种 内置的认证方式:

  1. 基于 CA 证书的 SSL/TLS 认证方式;
  2. 基于 Token 的认证方式。

同时也提供了可扩展的用户自定义认证方式。

gRPC 中的连接类型一共有以下 3 种:

  1. insecure connection:不使用 TLS 加密;
  2. server-side TLS:仅服务端 TLS 加密;
  3. mutual TLS:客户端、服务端都使用 TLS 加密。

我们之前的实例中都是使用 insecure connection:

conn, err := grpc.Dial(":8972", grpc.WithInsecure())

这种方式相当于裸奔的数据在网络上行走,生产环境下这样使用肯定是不行的。下面我们来说一下基于 TLS 认证方式加密操作。

server-side TLS

服务端 TLS 具体包含以下几个步骤:

  1. 制作证书,包含服务端证书和 CA 证书;
  2. 服务端启动时加载证书;
  3. 客户端连接时使用CA 证书校验服务端证书有效性。

CA 证书制作:

# 生成.key  私钥文件
$ openssl genrsa -out ca.key 2048 # 生成.csr 证书签名请求文件
$ openssl req -new -key ca.key -out ca.csr -subj "/C=GB/L=China/O=rickiyang/CN=www.rickiyang.com" # 自签名生成.crt 证书文件
$ openssl req -new -x509 -days 3650 -key ca.key -out ca.crt -subj "/C=GB/L=China/O=rickiyang/CN=www.rickiyang.com"

服务端证书

和生成 CA证书类似,不过最后一步由 CA 证书进行签名,而不是自签名。

然后openssl 配置文件可能位置不同,需要自己修改一下。

首先找到你的 openssl 位置:

$ find / -name "openssl.cnf"

然后生成签名证书:

# 生成.key  私钥文件
$ openssl genrsa -out server.key 2048 # 生成.csr 证书签名请求文件
$ openssl req -new -key server.key -out server.csr \
-subj "/C=GB/L=China/O=rickiyang/CN=www.rickiyang.com" \
-reqexts SAN \
-config <(cat /usr/local/etc/openssl@1.1/openssl.cnf <(printf "\n[SAN]\nsubjectAltName=DNS:*.rickiyang.com")) # 签名生成.crt 证书文件
$ openssl x509 -req -days 3650 \
-in server.csr -out server.crt \
-CA ca.crt -CAkey ca.key -CAcreateserial \
-extensions SAN \
-extfile <(cat /usr/local/etc/openssl@1.1/openssl.cnf <(printf "\n[SAN]\nsubjectAltName=DNS:*.rickiyang.com"))

至此会生成如下文件:

-rw-r--r--   1 rickiyang  staff  1119  6 30 10:32 ca.crt
-rw-r--r-- 1 rickiyang staff 964 6 30 10:32 ca.csr
-rw-r--r-- 1 rickiyang staff 1679 6 30 10:31 ca.key
-rw-r--r-- 1 rickiyang staff 17 6 30 10:34 ca.srl
-rw-r--r-- 1 rickiyang staff 1164 6 30 10:34 server.crt
-rw-r--r-- 1 rickiyang staff 1017 6 30 10:33 server.csr
-rw-r--r-- 1 rickiyang staff 1679 6 30 10:32 server.key

下面我们用到的会有这三个:

ca.crt
server.key
server.crt

下面来看一下如何将加密校验逻辑加入到代码中。相关代码在 Github 上,自行下载查看

服务端的修改点如下:

  • NewServerTLSFromFile 加载证书
  • NewServer 时指定 Creds。
func TestGrpcServer(t *testing.T) {
// 监听本地的8972端口
lis, err := net.Listen("tcp", ":8972")
if err != nil {
fmt.Printf("failed to listen: %v", err)
return
} // TLS认证
creds, err := credentials.NewServerTLSFromFile("/Users/rickiyang/server.crt", "/Users/rickiyang/server.key")
if err != nil {
grpclog.Fatalf("Failed to generate credentials %v", err)
} //开启TLS认证, 注册拦截器
s := grpc.NewServer(grpc.Creds(creds), grpc.UnaryInterceptor(LoggingInterceptor)) // 创建gRPC服务器
pb.RegisterGreeterServer(s, &server{}) // 在gRPC服务端注册服务 reflection.Register(s) //在给定的gRPC服务器上注册服务器反射服务
// Serve方法在lis上接受传入连接,为每个连接创建一个ServerTransport和server的goroutine。
// 该goroutine读取gRPC请求,然后调用已注册的处理程序来响应它们。
err = s.Serve(lis)
if err != nil {
fmt.Printf("failed to serve: %v", err)
return
} }

同样服务端使用 TLS 连接,客户端也需要使用对应的连接方式才能进行传输。客户端代码主要修改点:

  • NewClientTLSFromFile 指定使用 CA 证书来校验服务端的证书有效性。注意:第二个参数域名就是前面生成服务端证书时指定的CN参数
  • 建立连接时 指定建立安全连接 WithTransportCredentials。

对应代码逻辑如下:

func TestGrpcClient(t *testing.T) {
// TLS连接
creds, err := credentials.NewClientTLSFromFile("/Users/rickiyang2/ca.crt", "www.rickiyang.com")
if err != nil {
grpclog.Fatalf("Failed to create TLS credentials %v", err)
} // 连接服务器
conn, err := grpc.Dial(":8972", grpc.WithTransportCredentials(creds))
if err != nil {
fmt.Printf("faild to connect: %v", err)
}
defer conn.Close() c := pb.NewGreeterClient(conn)
// 调用服务端的SayHello
r, err := c.SayHello(context.Background(), &pb.HelloRequest{Name: "CN"})
if err != nil {
fmt.Printf("could not greet: %v", err)
} fmt.Printf("Greeting: %s !\n", r.Message)
}
mutual TLS

server-side TLS 中虽然服务端使用了证书,但是客户端却没有使用证书,本章节会给客户端也生成一个证书,并完成 mutual TLS。

生成客户端证书和生成服务端证书没有什么不同:

# 生成.key  私钥文件
openssl genrsa -out client.key 2048 # 生成.csr 证书签名请求文件
openssl req -new -key client.key -out client.csr \
-subj "/C=GB/L=China/O=lixd/CN=www.rickiyang.com" \
-reqexts SAN \
-config <(cat /usr/local/etc/openssl@1.1/openssl.cnf <(printf "\n[SAN]\nsubjectAltName=DNS:*.rickiyang.com")) # 签名生成.crt 证书文件
openssl x509 -req -days 3650 \
-in client.csr -out client.crt \
-CA ca.crt -CAkey ca.key -CAcreateserial \
-extensions SAN \
-extfile <(cat /usr/local/etc/openssl@1.1/openssl.cnf <(printf "\n[SAN]\nsubjectAltName=DNS:*.rickiyang.com"))

执行上面的命令之后我们会得到这两个重要的文件:

client.crt
client.key

下面就是在代码中去引用这些文件,mutual TLS 配置客户端和服务端都需要修改,相关代码点击查看

具体改动如下:

服务端:

func TestGrpcServer(t *testing.T) {
// 从证书相关文件中读取和解析信息,得到证书公钥、密钥对
certificate, err := tls.LoadX509KeyPair(data.Path("/Users/rickiyang2/server.crt"), data.Path("/Users/rickiyang2/server.key"))
if err != nil {
fmt.Errorf("err, %v", err)
}
// 创建CertPool,后续就用池里的证书来校验客户端证书有效性
// 所以如果有多个客户端 可以给每个客户端使用不同的 CA 证书,来实现分别校验的目的
certPool := x509.NewCertPool()
ca, err := ioutil.ReadFile(data.Path("/Users/rickiyang2/ca.crt"))
if err != nil {
fmt.Errorf("err, %v", err)
}
if ok := certPool.AppendCertsFromPEM(ca); !ok {
fmt.Errorf("failed to append certs")
} // 构建基于 TLS 的 TransportCredentials
creds := credentials.NewTLS(&tls.Config{
// 设置证书链,允许包含一个或多个
Certificates: []tls.Certificate{certificate},
// 要求必须校验客户端的证书 可以根据实际情况选用其他参数
ClientAuth: tls.RequireAndVerifyClientCert, // NOTE: this is optional!
// 设置根证书的集合,校验方式使用 ClientAuth 中设定的模式
ClientCAs: certPool,
}) //开启TLS认证, 注册拦截器
s := grpc.NewServer(grpc.Creds(creds), grpc.UnaryInterceptor(LoggingInterceptor)) // 创建gRPC服务器
pb.RegisterGreeterServer(s, &server{}) // 在gRPC服务端注册服务 // 监听本地的8972端口
lis, err := net.Listen("tcp", ":8972")
if err != nil {
fmt.Printf("failed to listen: %v", err)
return
} reflection.Register(s) //在给定的gRPC服务器上注册服务器反射服务
// Serve方法在lis上接受传入连接,为每个连接创建一个ServerTransport和server的goroutine。
// 该goroutine读取gRPC请求,然后调用已注册的处理程序来响应它们。
err = s.Serve(lis)
if err != nil {
fmt.Printf("failed to serve: %v", err)
return
} }

客户端:

func TestGrpcClient(t *testing.T) {

	// 加载客户端证书
certificate, err := tls.LoadX509KeyPair(data.Path("/Users/rickiyang2/client.crt"), data.Path("/Users/rickiyang2/client.key"))
if err != nil {
fmt.Errorf("err, %v", err)
}
// 构建CertPool以校验服务端证书有效性
certPool := x509.NewCertPool()
ca, err := ioutil.ReadFile(data.Path("/Users/rickiyang2/ca.crt"))
if err != nil {
fmt.Errorf("err, %v", err)
}
if ok := certPool.AppendCertsFromPEM(ca); !ok {
fmt.Errorf("failed to append ca certs")
} creds := credentials.NewTLS(&tls.Config{
Certificates: []tls.Certificate{certificate},
ServerName: "www.rickiyang.com", // NOTE: this is required!
RootCAs: certPool,
}) // 连接服务器
conn, err := grpc.Dial(":8972", grpc.WithTransportCredentials(creds))
if err != nil {
fmt.Printf("faild to connect: %v", err)
}
defer conn.Close() c := pb.NewGreeterClient(conn)
// 调用服务端的SayHello
r, err := c.SayHello(context.Background(), &pb.HelloRequest{Name: "CN"})
if err != nil {
fmt.Printf("could not greet: %v", err)
} fmt.Printf("Greeting: %s !\n", r.Message)
}

本篇只介绍 SSL/TLS 认证相关的方式,生成证书相关操作本文是在 Mac 上操作,不同系统可能会有不一样的环境问题, 如果出现问题按照相关提示排除错误。下一篇我们继续介绍 Token 认证和自定义认证方式。

跟我一起学Go系列:Go gRPC 安全认证机制-SSL/TLS认证的更多相关文章

  1. 跟我一起学 Go 系列:gRPC 拦截器

    Go gRPC 学习系列: 跟我一起学Go系列:gRPC 入门必备 第一篇内容我们已经基本了解到 gRPC 如何使用 .对应的三种流模式.现在已经可以让服务端和客户端互相发送消息.本篇仍然讲解功能性的 ...

  2. .NET Core下使用gRpc公开服务(SSL/TLS)

    一.前言 前一阵子关于.NET的各大公众号都发表了关于gRpc的消息,而随之而来的就是一波关于.NET Core下如何使用的教程,但是在这众多的教程中基本都是泛泛而谈,难以实际在实际环境中使用,而该篇 ...

  3. 跟我一起学Go系列:gRPC 入门必备

    RPC 的定义这里就不再说,看文章的同学都是成熟的开发.gRPC 是 Google 开源的高性能跨语言的 RPC 方案,该框架的作者 Louis Ryan 阐述了设计这款框架的动机,有兴趣的同学可以看 ...

  4. 跟我一起学Go系列:gRPC 全局数据传输和超时处理

    gRPC 在多个 GoRoutine 之间传递数据使用的是 Go SDK 提供的 Context 包.关于 Context 的使用可以看我之前的一篇文章:Context 使用. 但是 Context ...

  5. 跟我一起学Go系列:Go gRPC 安全认证方式-Token和自定义认证

    Go gRPC 系列: 跟我一起学Go系列:gRPC安全认证机制-SSL/TLS认证 跟我一起学 Go 系列:gRPC 拦截器使用 跟我一起学 Go 系列:gRPC 入门必备 接上一篇继续讲 gRPC ...

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

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

  7. 跟着鸟哥学Linux系列笔记3-第11章BASH学习

    跟着鸟哥学Linux系列笔记0-扫盲之概念 跟着鸟哥学Linux系列笔记0-如何解决问题 跟着鸟哥学Linux系列笔记1 跟着鸟哥学Linux系列笔记2-第10章VIM学习 认识与学习bash 1. ...

  8. 跟着鸟哥学Linux系列笔记2-第10章VIM学习

    跟着鸟哥学Linux系列笔记0-扫盲之概念 跟着鸟哥学Linux系列笔记0-如何解决问题 跟着鸟哥学Linux系列笔记1 常用的文本编辑器:Emacs, pico, nano, joe, vim VI ...

  9. 跟着鸟哥学Linux系列笔记0-如何解决问题

    跟着鸟哥学Linux系列笔记0-扫盲之概念 在发生问题怎么处理: 1.  在自己的主机.网络数据库上查询How-To或FAQ -Linux 自身的文件数据: /usr/share/doc -CLDP中 ...

随机推荐

  1. Shell脚本 /dev/null 2>&1详解

    Shell脚本---- /dev/null 2>&1详解   1.可以将/dev/null看作"黑洞". 它非常等价于一个只写文件. 所有写入它的内容都会永远丢失. ...

  2. dpkg -S {file} #ubuntu 14.04 rpm -qf {file} #centos 7

    Linux查找命令或组件对应安装包的方法原创FJEagle 最后发布于2017-12-15 19:10:06 阅读数 4603 收藏展开Linux查找命令或组件对应安装包的方法当新搭建服务器或者维护不 ...

  3. docker容器中日志文件过大处理方法

    背景 :在日常工作中一个基于centos镜像构建起来的python爬虫程序,日志文件在两个月内到了500G,日志存放在根目录下面,在不扩容的情况下把这个问题给解决掉.通过定时任务和脚本的方法,定期的清 ...

  4. 分布式存储ceph---ceph添加/删除osd(5)

    一.添加osd 当前ceph集群中有如下osd,现在准备新添加osd: 1.选择一个osd节点,添加好新的硬盘: 2.显示osd节点中的硬盘,并重置新的osd硬盘: 列出节点磁盘:ceph-deplo ...

  5. C语言关于指针函数与函数指针个人理解

    1,函数指针 顾名思义,即指向函数的指针,功能与其他指针相同,该指针变量保存的是所指向函数的地址. 假如是void类型函数指针定义方式可以是 void (*f)(参数列表);亦可以先用 typedef ...

  6. Consistent 与 Mirrored 视角

    Consistent 与 Mirrored 视角 在进行分布式训练时,OneFlow 框架提供了两种角度看待数据与模型的关系,被称作 consistent 视角与 mirrored 视角. 本文将介绍 ...

  7. CUDA 11功能展示

    CUDA 11功能展示 CUDA 11 Features Revealed 新的NVIDIA A100 GPU基于NVIDIA安培GPU架构,实现了加速计算的最大一代飞跃.A100 GPU具有革命性的 ...

  8. GPU端到端目标检测YOLOV3全过程(下)

    GPU端到端目标检测YOLOV3全过程(下) Ubuntu18.04系统下最新版GPU环境配置 安装显卡驱动 安装Cuda 10.0 安装cuDNN 1.安装显卡驱动 (1)这里采用的是PPA源的安装 ...

  9. 什么是视觉Visual SLAM

    什么是视觉Visual SLAM What Is Visual SLAM? What are the origins of visual SLAM? and what are some other a ...

  10. 6, java数据结构和算法: 栈的应用, 逆波兰计算器, 中缀表达式--> 后缀表达式

    直接上代码: public class PolandCalculator { //栈的应用:波兰计算器: 即: 输入一个字符串,来计算结果, 比如 1+((2+3)×4)-5 结果为16 public ...