grpc使用ssl(tls) 通過openssl指定多個域名和IP

原文地址: 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能否是*號,或者其他任何方法,使得服務端增加節點,不影響 客戶端正常調用。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章