为 gRPC 重新加载 Netty 服务器的 SSL 上下文

问题描述 投票:0回答:3

有人可以告诉我如何在刷新/更新服务器证书时重新加载 SSLContext 而无需重新启动 gRPC 服务器吗?

我有这段代码来构建和启动 gRPC 服务器。 每当证书更改(即当我创建新的 SSL 上下文时)都会调用方法certificateRefreshed(),但除非重新启动 grpc 服务器,否则这不起作用。

public class ServerWithTls {
    Server server;
    SslContext sslContext;

    public ServerWithTls() {
        this.sslContext = getSslContext();

        NettyServerBuilder serverBuilder = NettyServerBuilder
            .forPort(settings.port())
            .executor(executorService)
            .addService(myService);
            .sslContext(this.sslContext);

        server = serverBuilder.build();
        server.start();
    }

    public io.netty.handler.ssl.SslContext getSslContext() {
        // returns ssl context based on cert and key
    }

    // gets notified when a server cert changes
    public void certificateRefreshed() {
        // create a new SSL context when cert changes
        this.sslContext = getSslContext();
    }

}
ssl certificate netty grpc grpc-java
3个回答
0
投票

我不确定是否有更简单的替代方案,但我看到两种可能的方法。

  1. 模仿

    DelegatingSslContext
    ,制作自己的SslContext。当您想要不同的证书时,您可以切换到不同的
    SslContext
    (尤其是在
    newEngine
    期间)。

  2. 使用密钥材料会随时间变化的

    KeyManagerFactory
    。我不知道这样的工厂预先存在的实现,因此您可能需要实现一个委托给 KeyManagerFactorySpi
    KeyManagerFactory
    。随着时间的推移,您可以更换 
    KeyManagerFactory

我会警告说,我很容易错过一些会使方法失效的东西。


0
投票

这个问题与这个问题类似:Reloading a java.net.http.HttpClient's SSLContext

遗憾的是,此选项默认情况下不可用。将 SSLContext 提供给服务器并构建服务器后,您无法更改 SSLContext。您将需要创建一个新的 SSLContext 和一个新的服务器。

我的一个项目也遇到了同样的挑战,我通过使用自定义信任管理器和密钥管理器解决了这个问题,该自定义信任管理器和密钥管理器包围了实际的信任管理器和密钥管理器,同时具有交换实际信任管理器和信任管理器的能力。因此,如果您仍然想完成它而不需要重新创建服务器和 SSLContext,您可以使用以下设置:

SSLFactory baseSslFactory = SSLFactory.builder()
        .withDummyIdentityMaterial()
        .withDummyTrustMaterial()
        .withSwappableIdentityMaterial()
        .withSwappableTrustMaterial()
        .build();

Runnable sslUpdater = () -> {
    SSLFactory updatedSslFactory = SSLFactory.builder()
            .withIdentityMaterial(Paths.get("/path/to/your/identity.jks"), "password".toCharArray())
            .withTrustMaterial(Paths.get("/path/to/your/truststore.jks"), "password".toCharArray())
            .build();

    SSLFactoryUtils.reload(baseSslFactory, updatedSslFactory);
};

// initial update of ssl material to replace the dummies
sslUpdater.run();

// update ssl material every hour
Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate(sslUpdater, 1, 1, TimeUnit.HOURS);

SslContext sslContext = NettySslUtils.forServer(sslFactory).build();
          
Server server = NettyServerBuilder
    .forPort(8443)
    .executor(executorService)
    .addService(myService)
    .sslContext(sslContext)
    .build();

server.start();

请参阅此处了解此选项的文档:运行时重新加载 ssl

这里是 Jetty 的实际工作示例(类似于 Netty):在运行时与 Jetty Server 交换证书的示例

您可以使用以下方法将库添加到您的项目中:

<dependency>
    <groupId>io.github.hakky54</groupId>
    <artifactId>sslcontext-kickstart-for-netty</artifactId>
    <version>7.4.4</version>
</dependency>

您可以在此处查看完整文档和其他示例:GitHub - SSLContext Kickstart

顺便说一句,我需要添加一个小免责声明,我是库的维护者。


0
投票

io.grpc.util.AdvancedTlsX509KeyManager
是 gRPC 提供的一项新的便利功能,可以让这一切变得更容易。它是通用的
KeyManager
(不是 gRPC 特定的),可以轻松地重新加载证书。

您可以将其与 Netty 的 SslContext 一起使用,但最好使用

TlsServerCredentials
,因为 API 不依赖于特定的传输。

使用示例:

AdvancedTlsX509KeyManager keyManager = new AdvancedTlsX509KeyManager();
keyManager.updateIdentityCredentialsFromFile(
    new File("key"), new File("cert"));
ServerCredentials creds = TlsServerCredentials.newBuilder()
    .keyManager(keyManager)
    .build();
// NettyServerBuilder.forPort(port, creds) also works,
// if you need Netty-specific configuration
Server server = Grpc.newServerBuilderForPort(port, creds)
    .executor(executorService)
    .addService(myService)
    .build()
    .start();

// Free to update the certs later
keyManager.updateIdentityCredentialsFromFile(
    new File("key"), new File("cert"));
// You can tell it to poll the files
keyManager.updateIdentityCredentialsFromFile(
    new File("key"), new File("cert"), 10, MINUTES, executor);
© www.soinside.com 2019 - 2024. All rights reserved.