原文地址: http://www.ltang.me/2018/11/27/grpc-tls-openssl/
最近在使用grpc做項目,信息安全的同事提出要求,需要將來往報文加密,避免抓包。閱讀grpc的文檔,發現它已經支持ssl(tls),因此直接選這種認證和加密方式。
服務端和客戶端代碼參考grpc-java項目中的demo,摘取關鍵代碼如下:
服務端
private SslContextBuilder getSslContextBuilder() {
InputStream cacert = ZooPorterRpcServer.class.getClassLoader().getResourceAsStream("ssl/server.crt");
InputStream privkey = ZooPorterRpcServer.class.getClassLoader().getResourceAsStream("ssl/server.pem");
SslContextBuilder sslContextBuilder = SslContextBuilder.forServer(cacert, privkey);
return GrpcSslContexts.configure(sslContextBuilder, SslProvider.OPENSSL);
}
private void start() throws IOException {
port = settings.getInt("server.port");
server = NettyServerBuilder.forPort(port).addService(BeanUtil.me().getBean(ElasticsearchServiceGrpcImpl.class))
.sslContext(getSslContextBuilder().build())
.build()
.start();
logger.info("Server started, listening on " + port);
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
ZooPorterRpcServer.this.stop();
logger.error("*** server shut down");
}
});
}
客戶端
protected final ManagedChannel channel;
private static SslContext buildSslContext() throws SSLException {
SslContextBuilder builder = GrpcSslContexts.forClient();
builder.trustManager(ZooPorterClient.class.getClassLoader().getResourceAsStream("ssl/ca.crt"));
return builder.build();
}
public ZooPorterClient() throws SSLException {
channel = NettyChannelBuilder.forAddress(Settings.HOST, Settings.PORT)
.negotiationType(NegotiationType.TLS)
.sslContext(buildSslContext())
.build();
}
密鑰和證書
上述代碼中,ca.cert和server.pem/server.cert是通過openssl生成的密鑰和證書,生成的腳本可以直接參考項目中的readme文檔。
值得注意的是,按照readme中的腳本,只能生成一個指定了域名的證書(localhost),假使我們想通過多個域名甚至多個IP來訪問服務(後端做負載均衡和服務發現),這樣生成的證書就不合用了,訪問時會拋異常。因此修改了腳本,下面腳本僅做記錄使用。
openssl genrsa -passout pass:1111 -des3 -out ca.key 1024
openssl req -passin pass:1111 -new -x509 -days 7300 -key ca.key -out ca.crt -subj "/C=CN/ST=GuangDong/CN=www.ido.com" -extensions SAN -config <(cat /etc/pki/tls/openssl.cnf <(printf "[SAN]\nsubjectAltName=DNS:www.ido.com,IP:127.0.0.1,IP:180.137.128.151"))
openssl genrsa -passout pass:1111 -des3 -out server.key 1024
openssl req -passin pass:1111 -new -key server.key -out server.csr -subj "/C=CN/ST=GuangDong/CN=www.ido.com" -reqexts SAN -config <(cat /etc/pki/tls/openssl.cnf <(printf "[SAN]\nsubjectAltName=DNS:www.ido.com,IP:127.0.0.1,IP:180.137.128.151"))
# 以下這句是grpc官方例子中生成crt文件的命令,但發現這種方式無法指定ip和多域名,因此棄用
# openssl x509 -req -passin pass:1111 -days 7300 -in server.csr -CA ca.crt -CAkey ca.key -set_serial 01 -out server.crt
openssl ca -passin pass:1111 -days 7300 -in server.csr -keyfile ca.key -cert ca.crt -extensions SAN -config <(cat /etc/pki/tls/openssl.cnf <(printf "[SAN]\nsubjectAltName=DNS:www.ido.com,IP:127.0.0.1,IP:180.137.128.151"))
openssl rsa -passin pass:1111 -in server.key -out server.key
openssl pkcs8 -topk8 -nocrypt -in server.key -out server.pem
這樣生成的證書,經過測試,可以部署在多個IP(這裏是127.0.0.1和180.137.128.151)服務器上,正常訪問。
其他
然而,這需要先知道有哪些IP即將部署服務啊!!假如以後部署服務的服務器越來越多,難道要重新生成證書並且更新服務和客戶端嗎?
得考慮下,這個ip能否是*號,或者其他任何方法,使得服務端增加節點,不影響 客戶端正常調用。