gpt4 book ai didi

java - 如何在不重新启动的情况下更新 Spring Data Geode 连接中的 keystore (SSLContext)?

转载 作者:行者123 更新时间:2023-12-04 22:42:27 60 4
gpt4 key购买 nike

背景是我正在开发一个 Kubernetes 项目,我们使用 Geode 集群和 Spring Boot,以及 Spring Boot Data Geode (SBDG)。我们用它开发了一个应用程序,一个ClientCache。此外,我们有一个专有的内部机制来生成集群内部证书,这种机制会根据最佳实践自动更新证书。我们将 App 代码中的 PEM 格式证书转换为 JKS,并使用 @EnableSSL 注释配置 Spring 以获取它们。
所以问题是,在第一个周期中,一切都运行良好,当使用应用程序最初启动的 JKS 文件创建连接时,但是如果证书更新,比如每小时更新一次(在云中这是最佳实践),Geode 失败与一堆异常连接,有时是 SSLException (readHandshakeRecord),很多时候是“无法连接到列表中的任何定位器”(但我调试过,它也是一个 HandshakeException,只是包装在一个连接异常中) .定位器和服务器已启动并正在运行(我检查了 GFSH),只是我认为尝试与旧 SSLContext 连接并在 SSL 握手中失败的应用程序。
到目前为止,我发现的唯一方法是完全重新启动应用程序,但我们需要这个系统是自动的、高度可用的,所以这不应该是解决这个问题的唯一方法。
我认为这个问题正在影响很多 Spring/Java 项目,因为我发现这个问题无处不在(Kafka、PGSQL 等......)。
你们有什么方法可以做到这一点吗?
有没有办法:

  • 无需我重新启动应用程序即可重新创建所有连接?
  • 以某种方式使当前使用的连接无效,并强制
    ClientCache 创建新的,重新读取 JKS 文件?
  • 也许让客户端应用程序超时连接并销毁它们,并使用刷新的 SSLContext 创建新的连接?

  • 我没有找到任何可能性。
    编辑:让我添加一些代码,以展示我们如何做事,因为我们使用 Spring,它非常简单:
    @Configuration
    @EnableGemfireRepositories(basePackages = "...")
    @EnableEntityDefinedRegions(basePackages = "...")
    @ClientCacheApplication
    @EnableSsl(
    truststore = "truststore.jks",
    keystore = "keystore.jks",
    truststorePassword = "pwd",
    keystorePassword = "pwd"
    )
    public class GeodeTls {}
    就是这样!然后我们对@Regions 和@Repositories 使用普通注解,并且我们有我们的@RestControllers,我们在其中调用存储库方法,它们中的大多数只是空的,或者是默认的,因为我们使用OQL annotate 方法来处理Spring。由于 Geode 有一个基于属性的配置,我们从不设置 KeyStores、TrustStores,我只是在调试期间在代码中看到它们。
    EDIT2:感谢以下评论,我终于解决了,正是这张 Geode 票帮助了很多(感谢 Jen D): https://github.com/apache/geode/pull/2244 ,自 Geode 1.8.0 起可用。此外,下面的代码片段对 Swappable KeyManager 非常有用(感谢 Hakan54),我最后做了一个组合解决方案。不过我必须小心,只设置一次默认 SSLContext,因为后续设置无效,并且不会导致任何失败。现在该应用程序很稳定,似乎可以跨越证书更改。

    最佳答案

    我昨天遇到了你的问题,正在制作一个原型(prototype)。我认为在你的情况下是可能的。但是,我只是在本地使用 http 客户端和服务器进行了尝试,我能够在运行时更改证书,而无需重新启动这些应用程序或重新创建 SSLContext。
    选项 1
    从你的问题我可以理解你正在阅读 PEM从某处获取文件并将其转换为其他内容,最后您使用的是 SSLContext .在这种情况下,我假设您正在创建一个 KeyManager 和一个 TrustManager。如果是这种情况,您需要做的是创建 KeyManager 和 TrustManager 的自定义实现作为包装类,以将方法调用委托(delegate)给包装类中的实际 KeyManager 和 TrustManager。并且还添加了一个 setter 方法来在证书更新时更改内部 KeyManager 和 TrustManager。
    在您的情况下,这将是一个文件观察器,当 PEM 文件被更改时会被触发。在这种情况下,您只需要使用新证书重新生成 KeyManager 和 TrustManager 并通过调用 setter 方法将其提供给包装类。以下是您可以使用的示例代码片段:
    HotSwappableX509ExtendedKeyManager

    import javax.net.ssl.SSLEngine;
    import javax.net.ssl.X509ExtendedKeyManager;
    import java.net.Socket;
    import java.security.Principal;
    import java.security.PrivateKey;
    import java.security.cert.X509Certificate;
    import java.util.Objects;

    public final class HotSwappableX509ExtendedKeyManager extends X509ExtendedKeyManager {

    private X509ExtendedKeyManager keyManager;

    public HotSwappableX509ExtendedKeyManager(X509ExtendedKeyManager keyManager) {
    this.keyManager = Objects.requireNonNull(keyManager);
    }

    @Override
    public String chooseClientAlias(String[] keyType, Principal[] issuers, Socket socket) {
    return keyManager.chooseClientAlias(keyType, issuers, socket);
    }

    @Override
    public String chooseEngineClientAlias(String[] keyTypes, Principal[] issuers, SSLEngine sslEngine) {
    return keyManager.chooseEngineClientAlias(keyTypes, issuers, sslEngine);
    }

    @Override
    public String chooseServerAlias(String keyType, Principal[] issuers, Socket socket) {
    return keyManager.chooseServerAlias(keyType, issuers, socket);
    }

    @Override
    public String chooseEngineServerAlias(String keyType, Principal[] issuers, SSLEngine sslEngine) {
    return keyManager.chooseEngineServerAlias(keyType, issuers, sslEngine);
    }

    @Override
    public PrivateKey getPrivateKey(String alias) {
    return keyManager.getPrivateKey(alias);
    }

    @Override
    public X509Certificate[] getCertificateChain(String alias) {
    return keyManager.getCertificateChain(alias);
    }

    @Override
    public String[] getClientAliases(String keyType, Principal[] issuers) {
    return keyManager.getClientAliases(keyType, issuers);
    }

    @Override
    public String[] getServerAliases(String keyType, Principal[] issuers) {
    return keyManager.getServerAliases(keyType, issuers);
    }

    public void setKeyManager(X509ExtendedKeyManager keyManager) {
    this.keyManager = Objects.requireNonNull(keyManager);
    }

    }
    HotSwappableX509ExtendedTrustManager
    import javax.net.ssl.SSLEngine;
    import javax.net.ssl.X509ExtendedTrustManager;
    import java.net.Socket;
    import java.security.cert.CertificateException;
    import java.security.cert.X509Certificate;
    import java.util.Arrays;
    import java.util.Objects;

    public class HotSwappableX509ExtendedTrustManager extends X509ExtendedTrustManager {

    private X509ExtendedTrustManager trustManager;

    public HotSwappableX509ExtendedTrustManager(X509ExtendedTrustManager trustManager) {
    this.trustManager = Objects.requireNonNull(trustManager);
    }

    @Override
    public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
    trustManager.checkClientTrusted(chain, authType);
    }

    @Override
    public void checkClientTrusted(X509Certificate[] chain, String authType, Socket socket) throws CertificateException {
    trustManager.checkClientTrusted(chain, authType, socket);
    }

    @Override
    public void checkClientTrusted(X509Certificate[] chain, String authType, SSLEngine sslEngine) throws CertificateException {
    trustManager.checkClientTrusted(chain, authType, sslEngine);
    }

    @Override
    public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
    trustManager.checkServerTrusted(chain, authType);
    }

    @Override
    public void checkServerTrusted(X509Certificate[] chain, String authType, Socket socket) throws CertificateException {
    trustManager.checkServerTrusted(chain, authType, socket);
    }

    @Override
    public void checkServerTrusted(X509Certificate[] chain, String authType, SSLEngine sslEngine) throws CertificateException {
    trustManager.checkServerTrusted(chain, authType, sslEngine);
    }

    @Override
    public X509Certificate[] getAcceptedIssuers() {
    X509Certificate[] acceptedIssuers = trustManager.getAcceptedIssuers();
    return Arrays.copyOf(acceptedIssuers, acceptedIssuers.length);
    }

    public void setTrustManager(X509ExtendedTrustManager trustManager) {
    this.trustManager = Objects.requireNonNull(trustManager);
    }

    }
    用法
    // Your key and trust manager created from the pem files
    X509ExtendedKeyManager aKeyManager = ...
    X509ExtendedTrustManager aTrustManager = ...

    // Wrapping it into your hot swappable key and trust manager
    HotSwappableX509ExtendedKeyManager swappableKeyManager = new HotSwappableX509ExtendedKeyManager(aKeyManager);
    HotSwappableX509ExtendedTrustManager swappableTrustManager = new HotSwappableX509ExtendedTrustManager(aTrustManager);

    SSLContext sslContext = SSLContext.getInstance("TLS");
    sslContext.init(new KeyManager[]{ swappableKeyManager }, new TrustManager[]{ swappableTrustManager })

    // Give the sslContext instance to your server or client
    // After some time change the KeyManager and TrustManager with the following snippet:

    X509ExtendedKeyManager anotherKeyManager = ... // Created from the new pem files
    X509ExtendedTrustManager anotherTrustManager = ... // Created from the new pem files

    // Set your new key and trust manager into your swappable managers
    swappableKeyManager.setKeyManager(anotherKeyManager)
    swappableTrustManager.setTrustManager(anotherTrustManager)
    因此,即使您的 SSLContext 实例缓存在您的客户端服务器中,您仍然可以换入和换出新的 keymanager 和 trustmanager。
    代码片段可在此处获得:
    Github - SSLContext Kickstart
  • HotSwappableX509ExtendedKeyManager
  • HotSwappableX509ExtendedTrustManager

  • 选项 2
    如果您不想将自定义(HotSwappableKeyManager 和 HotSwappableTrustManager)代码添加到您的代码库中,您也可以使用我的库:
    <dependency>
    <groupId>io.github.hakky54</groupId>
    <artifactId>sslcontext-kickstart</artifactId>
    <version>6.6.0</version>
    </dependency>
    用法
    SSLFactory sslFactory = SSLFactory.builder()
    .withSwappableIdentityMaterial()
    .withIdentityMaterial("identity.jks", "password".toCharArray())
    .withSwappableTrustMaterial()
    .withTrustMaterial("truststore.jks", "password".toCharArray())
    .build();

    SSLContext sslContext = sslFactory.getSslContext();

    // Give the sslContext instance to your server or client
    // After some time change the KeyManager and TrustManager with the following snippet:

    // swap identity and trust materials and reuse existing http client
    KeyManagerUtils.swapKeyManager(sslFactory.getKeyManager().get(), anotherKeyManager);
    TrustManagerUtils.swapTrustManager(sslFactory.getTrustManager().get(), anotherTrustManager);

    // Cleanup old ssl sessions by invalidating them all. Forces to use new ssl sessions which will be created by the swapped KeyManager/TrustManager
    SSLSessionUtils.invalidateCaches(sslContext);

    关于java - 如何在不重新启动的情况下更新 Spring Data Geode 连接中的 keystore (SSLContext)?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65868599/

    60 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com