Credentials
gRPC 建立在HTTP/2协议之上,对 TLS 提供了很好的支持。
当不需要证书认证时,gRPCity
默认是跳过了对服务器证书的验证。
没有启用证书的 gRPC 服务和客户端进行的是明文通信,信息面临被任何第三方监听的风险。
为了保证 gRPC 通信不被第三方监听、篡改或伪造,可以对服务器启动 TLS 加密特性。
注意: 启动 TSL 后会对 CPU 的资源消耗是会增加的,如果是你的业务场景是高 QPS 的,不建议启动 TSL。
生成证书
创建自己的SSL证书。首先,我们需要为服务器和客户端创建自己的SSL证书。可以使用 OpenSSL 命令来生成自己的证书。
我们使用bash
脚本来完成证书的生成,先设置全局变量,如密码和证书有效期。
# 创建 certs 保存目录
mkdir -p certs && cd certs
# 设置密码变量
password="grpcity"
# 证书有效期变量
days=365
生成 ca 证书
# 设置CA相关变量
ca_key="ca.key"
ca_crt="ca.crt"
ca_subject="/C=CN/ST=GD/L=Guangzhou/O=gRPCity/OU=gRPCity/CN=ca"
# 生成CA密钥和证书
openssl genrsa -passout pass:$password -des3 -out $ca_key 4096
openssl req -passin pass:$password -new -x509 -days $days -key $ca_key -out $ca_crt -subj "$ca_subject"
生成 server 证书
# 设置服务器相关变量
server_key="server.key"
server_csr="server.csr"
server_crt="server.crt"
server_subject="/C=CN/ST=GD/L=Guangzhou/O=gRPCity/OU=Server/CN=localhost"
# 生成服务器密钥和证书
openssl genrsa -passout pass:$password -des3 -out $server_key 4096
openssl req -passin pass:$password -new -key $server_key -out $server_csr -subj "$server_subject"
openssl x509 -req -passin pass:$password -days $days -in $server_csr -CA $ca_crt -CAkey $ca_key -set_serial 01 -out $server_crt
openssl rsa -passin pass:$password -in $server_key -out $server_key
生成 client 证书
# 设置客户端相关变量
client_key="client.key"
client_csr="client.csr"
client_crt="client.crt"
client_subject="/C=CN/ST=GD/L=Guangzhou/O=gRPCity/OU=Client/CN=localhost"
# 生成客户端密钥和证书
openssl genrsa -passout pass:$password -des3 -out $client_key 4096
openssl req -passin pass:$password -new -key $client_key -out $client_csr -subj "$client_subject"
openssl x509 -passin pass:$password -req -days $days -in $client_csr -CA $ca_crt -CAkey $ca_key -set_serial 01 -out $client_crt
openssl rsa -passin pass:$password -in $client_key -out $client_key
最终我们获得如下的证书目录:
Terminal
.
└── certs
├── ca.crt
├── ca.key
├── client.crt
├── client.csr
├── client.key
├── server.crt
├── server.csr
└── server.key
服务端配置
我们开始进行服务端证书配置的工作。使用loader.makeServerCredentials()
进行证书加载。
loader.makeServerCredentials(rootCerts, keyCertPairs, checkClientCertificate)
的参数说明如下:
rootCerts
: 根证书,一般为 ca 证书;keyCertPairs
: 证书对,key和crt,必须数组;checkClientCertificate
: 是否检查客户端证书;
示例:
import fs from 'node:fs'
import loader from './loader.js'
const credentials = loader.makeServerCredentials(
fs.readFileSync(path.resolve(__dirname, 'certs/ca.crt')), [{
private_key: fs.readFileSync(path.resolve(__dirname, 'certs/server.key')),
cert_chain: fs.readFileSync(path.resolve(__dirname, 'certs/server.crt'))
}], true)
两种方式把证书加载到服务端中:
1.在初始化的时候
const server = await loader.initServer({ credentials })
2.在监听启动的时候
await server.listen(addr, credentials)
优先级:listen()
优先于 initServer()
⚠️
使用了证书后,不能使用 ip 来启动服务,可以使用localhost
或域名。
客户端配置
我们开始进行服务端证书配置的工作。使用loader.makeCredentials()
进行证书加载。
import fs from 'node:fs'
import path from 'node:path'
const credentials = loader.makeClientCredentials(
fs.readFileSync(path.resolve(__dirname, 'certs/ca.crt')),
fs.readFileSync(path.resolve(__dirname, 'certs/client.key')),
fs.readFileSync(path.resolve(__dirname, 'certs/client.crt'))
)
获得credentials
后,有两种加载方式,如下所示:
initClients()
时加载
const clients = await loader.initClients({
services: {
'test.helloworld.Greeter': addr,
'test.helloworld.Hellor': addr
},
credentials
})
get()
或getReal()
时加载
const greeterClient = clients.get('test.helloworld.Greeter', { credentials })
const result = await greeterClient.sayHello({ name: 'greeter' })
⚠️
initClients()
适合所有服务端都是使用同一个证书的场景get()
适合不同服务端的不同证书的场景