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. k8s滚动更新(8)

    一.k8s版本更新 滚动更新是一次只更新一小部分副本,成功后,再更新更多的副本,最终完成所有副本的更新.滚动更新的最大的好处是零停机,整个更新过程始终有副本在运行,从而保证了业务的连续性. 实践:部署 ...

  2. 010.Python字符串的格式化

    字符串的格式化 顺序传参 索引传参 关键字传参 容器类型传参(列表和元组) {}相当于占位符 1 顺序传参 strvar = "他{}牺牲自己,{}出卖组织" res = strv ...

  3. Linux 目录管理

    tree命令的基本使用 tree 查看当前目录的树状结构 -a 查看所有包含隐藏文件 -L 1 查看目录层级 tree /root 指定目录 根目录下的主要文件 /bin 普通用户可以执行的二进制文件 ...

  4. 9.random_os_sys_shutil_shelve_xml_hashlib

    此章未能精读,待回顾random模块import randomrandom.random() 随机生成一个0-1之间随机的浮点数random.randint(a,b) 随机生成一个a-b之间的整数 a ...

  5. ASP.NET Core MVC 入门到精通 - 3. 使用MediatR

    ASP.NET Core MVC 入门到精通 - 3. 使用MediatR 环境: .NET 5 ASP.NET Core MVC (project) 1. MediatR MediatR .NET中 ...

  6. 西门子S7系列以太网通讯处理器安装调式操作

    北京华科远创科技有限研发的远创智控ETH-YC模块,PLC转以太网型号有MPI-ETH-YC01和MPI-ETH-YC01,适用于西门子S7-200/S7-300/S7-400.SMART S7-20 ...

  7. 2021 docker安装宝塔面板

    拉取CentOS镜像 docker pull centos 创建 docker 容器 启动镜像,映射主机与容器内8888(宝塔面板).888(PHPMYADMIN)端口 -v 挂在目录 为了能够保存( ...

  8. skynet debug console 使用

    预读 关于如何使用 skynet 可以参考 wiki 文档 更多实战内容见 https://www.lanqiao.cn/courses/2770 优惠码:2CZ2UA5u 环境测试搭建 使用示例代码 ...

  9. TensorFlow多元线性回归实现

    多元线性回归的具体实现 导入需要的所有软件包:   因为各特征的数据范围不同,需要归一化特征数据.为此定义一个归一化函数.另外,这里添加一个额外的固定输入值将权重和偏置结合起来.为此定义函数 appe ...

  10. 聊一聊.NET Core结合Nacos实现配置加解密

    背景 当我们把应用的配置都放到配置中心后,很多人会想到这样一个问题,配置里面有敏感的信息要怎么处理呢? 信息既然敏感的话,那么加个密就好了嘛,相信大部分人的第一感觉都是这个,确实这个是最简单也是最合适 ...