如何在Python的gRPC中通过TLS启用身份验证?
Python gRPC 启用SSL/TLS身份验证完整指南
一、核心证书文件说明
先明确你提到的三个文件的作用和必要性:
- key.pem:服务器的私钥,用于对通信数据加密签名,是服务器身份的核心凭证,只有服务器自己能持有,绝对不能泄露。没有它,服务器无法证明自己的合法身份,也无法解密客户端发来的加密请求。
- chain.pem:服务器的证书链,包含服务器自身的证书和签发它的根CA证书。用来向客户端证明服务器的身份,客户端通过这个证书链验证服务器是否可信。
- roots.pem:根CA证书,客户端用来验证服务器证书链的合法性,确认服务器的证书是由自己信任的CA机构(测试环境用自签名CA)签发的。
二、测试用证书生成步骤(自签名)
测试环境下不需要第三方CA,用OpenSSL生成自签名证书即可:
- 生成根CA私钥和证书
# 生成根CA私钥(可设置密码,示例密码为123456) openssl genrsa -des3 -out rootCA.key 4096 # 生成根CA证书(有效期10年) openssl req -x509 -new -nodes -key rootCA.key -sha256 -days 3650 -out roots.pem
- 生成服务器私钥和证书签名请求(CSR)
# 生成服务器私钥 openssl genrsa -out key.pem 2048 # 生成CSR,注意填写Common Name时要和服务器的域名/IP/容器服务名一致(后续客户端访问要用这个名称) openssl req -new -key key.pem -out server.csr
- 用根CA签发服务器证书,生成证书链
# 签发服务器证书(有效期1年) openssl x509 -req -in server.csr -CA roots.pem -CAkey rootCA.key -CAcreateserial -out server.crt -days 365 -sha256 # 合并服务器证书和根CA证书,生成证书链chain.pem cat server.crt roots.pem > chain.pem
三、适配异步gRPC代码
客户端代码(启用安全通道)
调整代码确保证书路径正确,服务器地址要和证书Common Name一致:
import grpc import helloworld_pb2 import helloworld_pb2_grpc import asyncio async def send_async_request(): # 加载根CA证书,验证服务器身份 with open('roots.pem', 'rb') as f: creds = grpc.ssl_channel_credentials(f.read()) # 建立安全通道,替换为服务器容器的IP或服务名 channel = grpc.aio.secure_channel('server-service:50051', creds) stub = helloworld_pb2_grpc.GreeterStub(channel) # 发送异步请求(示例方法为SayHello) response = await stub.SayHello(helloworld_pb2.HelloRequest(name="Luca")) print(f"响应结果: {response.message}") if __name__ == '__main__': asyncio.run(send_async_request())
服务器代码(启用安全端口)
完善服务实现,使用异步gRPC服务器:
import grpc import helloworld_pb2 import helloworld_pb2_grpc from concurrent import futures import asyncio # 实现Greeter服务逻辑 class Greeter(helloworld_pb2_grpc.GreeterServicer): async def SayHello(self, request, context): # 这里编写你的计算逻辑 return helloworld_pb2.HelloReply(message=f"Hello, {request.name}! 计算完成") async def start_server(): # 使用异步gRPC服务器 server = grpc.aio.server(futures.ThreadPoolExecutor(max_workers=10)) helloworld_pb2_grpc.add_GreeterServicer_to_server(Greeter(), server) # 加载服务器私钥和证书链 with open('key.pem', 'rb') as f: private_key = f.read() with open('chain.pem', 'rb') as f: certificate_chain = f.read() server_creds = grpc.ssl_server_credentials(((private_key, certificate_chain),)) # 绑定安全端口,允许所有IP访问(容器部署推荐) server.add_secure_port('[::]:50051', server_creds) await server.start() await server.wait_for_termination() if __name__ == '__main__': asyncio.run(start_server())
四、容器部署验证要点
- 证书挂载:将服务器的
key.pem、chain.pem和客户端的roots.pem分别挂载到对应容器的代码可读取路径(比如用Docker的-v参数)。 - 网络配置:确保客户端和服务器容器在同一个Docker网络下,客户端访问时使用服务器容器的服务名(要和证书Common Name一致)。
- 端口检查:服务器容器无需映射端口给外部,容器间直接用内部端口50051通信即可。
内容的提问来源于stack exchange,提问作者LucaM
相关产品推荐
相关产品推荐

