[grpc]使用mTLS加密认证

本文涉及的产品
密钥管理服务KMS,1000个密钥,100个凭据,1个月
简介: [grpc]使用mTLS加密认证

前言

假设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;
    }
}
相关文章
|
3月前
|
JSON Go 网络安全
golang使用JWX进行认证和加密
golang使用JWX进行认证和加密
50 5
|
1月前
|
NoSQL Java Redis
shiro学习四:使用springboot整合shiro,正常的企业级后端开发shiro认证鉴权流程。使用redis做token的过滤。md5做密码的加密。
这篇文章介绍了如何使用Spring Boot整合Apache Shiro框架进行后端开发,包括认证和授权流程,并使用Redis存储Token以及MD5加密用户密码。
26 0
shiro学习四:使用springboot整合shiro,正常的企业级后端开发shiro认证鉴权流程。使用redis做token的过滤。md5做密码的加密。
|
1月前
|
存储 安全 Java
shiro学习二:shiro的加密认证详解,加盐与不加盐两个版本。
这篇文章详细介绍了Apache Shiro安全框架中密码的加密认证机制,包括不加盐和加盐两种加密方式的实现和测试。
83 0
|
2月前
|
存储 NoSQL Java
|
3月前
|
Kubernetes 安全 Cloud Native
解锁安全新纪元:利用服务网格Istio,打造全链路mTLS加密隧道,从入口网关到出口网关,守护数据安全的每一步
【8月更文挑战第2天】随着云原生技术的发展,服务网格(Service Mesh)如Istio已成为微服务架构的核心,通过双向TLS(mTLS)确保通信安全。首先,在Kubernetes部署Istio以管理服务通信。接着,配置入口网关实现所有入向流量的加密处理,防止数据泄露。最后,通过配置Sidecar代理如Envoy,确保服务网格安全访问外部mTLS服务,从而构建起全链路的数据安全防护。
80 11
|
4月前
|
存储 网络安全 数据安全/隐私保护
[flask]使用mTLS双向加密认证http通信
【7月更文挑战第16天】在Flask应用中实现mTLS双向TLS加密认证可增强HTTP通信安全性。步骤包括: 1. 使用OpenSSL为服务器和客户端生成证书和密钥。 2. 配置Flask服务器使用这些证书: - 安装`flask`和`pyopenssl`. - 设置SSL上下文并启用mTLS验证: 注意事项: - 保持证书有效期并及时更新. - 确保证书链信任. - 充分测试mTLS配置.
|
3月前
|
安全 网络安全 数据安全/隐私保护
[flask]使用mTLS双向加密认证http通信
[flask]使用mTLS双向加密认证http通信
105 0
|
3月前
|
网络协议 应用服务中间件 Go
[golang]使用mTLS双向加密认证http通信
[golang]使用mTLS双向加密认证http通信
|
6月前
|
NoSQL 安全 MongoDB
MongoDB安全机制:认证、授权与加密
【4月更文挑战第30天】MongoDB提供全面的安全机制,包括认证(用户名/密码、LDAP、Kerberos、x.509证书)、授权(基于角色的访问控制,RBAC)和加密(TLS/SSL、透明数据加密TDE、字段级加密FLE),确保数据保密性、完整性和可用性。通过合理配置这些机制,企业可保障数据安全,应对不断变化的安全威胁。
|
6月前
|
存储 缓存 安全
https跳过SSL认证时是不是就是不加密的,相当于http?
https跳过SSL认证时是不是就是不加密的,相当于http?
343 0