grpc使用tls 通过openssl指定多个域名和IP
最近在使用grpc做项目,信息安全的同事提出要求,需要将来往报文加密,避免抓包。阅读grpc的文档,发现它已经支持ssl(tls),因此直接选这种认证和加密方式。
服务端和客户端代码参考grpc-java项目中的demo,摘取关键代码如下:
服务端
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| 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"); } }); }
|
客户端
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| 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来访问服务(后端做负载均衡和服务发现),这样生成的证书就不合用了,访问时会抛异常。因此修改了脚本,下面脚本仅做记录使用。
1 2 3 4 5 6 7 8 9
| 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能否是*号,或者其他任何方法,使得服务端增加节点,不影响 客户端正常调用。