如何将信任库和密钥库添加到 PCF(Pivotal Cloud Foundry)中的 springboot 应用程序

How to add truststore and keystore to a springboot app in PCF(Pivotal Cloud Foundry)

有没有办法在PCF中添加truststore和keystore:

部署到 unix box 的应用程序可以具有信任库,并且可以通过将它们保存在单独的位置并将该位置添加为 vm 参数来外部化密钥库。

但是如何在 PCF 中外部化密钥库和信任库。

我遇到的选项 1 是将密钥库和信任库保存在 spring-boot app 的 /resourse 中,并在 manifest.yml : JAVA_OPTS arguments 中给出路径。但是如果我们有 5 个不同的环境,有 5 组不同的信任库和密钥库呢?

由于这个原因,我们需要将 .请问PCF有没有办法做到这一点

提前致谢!

正如您所提到的,有多种方法可以做到这一点,这取决于您的具体需求,以决定哪种方法最有效。我将尝试列出尽可能多的选项,并 pros/cons 每个选项。

  1. 如果您只需要将证书分发给信任,那么添加 Bosh Trusted 证书并允许 Bosh 将它们分发到 CF 中的所有 VM 和容器是一个不错的方法。在 Ops Manager 中,在 Bosh 磁贴下,转到安全,您可以添加任意数量的受信任证书。

    当多个应用程序需要信任证书时,这是一个很好的方法,例如 corporate/private CA。它也适用于不同的 environments/foundations,因为您有不同的 Ops Manager,它可以维护不同的受信任证书列表。如果您有许多需要信任的证书 and/or 如果证书仅由一个或少数应用程序使用,则可能难以管理。它还需要 CF 的管理权限,并且可能需要一段时间才能在整个基础上部署更新。

  2. 这是您在问题中提到的选项。将证书导入 Java 信任库文件,将文件打包到 Java 应用程序并通过 JAVA_OPTS 环境变量指定其路径。 truststore文件可以放在resource目录下。

    例如:cf set-env <app> JAVA_OPTS '-Djavax.net.ssl.trustStore=/home/vcap/app/BOOT-INF/classes/jssecacerts'

    此选项对于上面选项 #1 未涵盖的场景很有用,例如单个或小型应用程序,其中存在该应用程序独有的证书。例如,您有一个必须信任的数据库服务器,并且只有一个应用程序使用该数据库服务器。

    它有你提到的缺点,信任库必须嵌入到应用程序中,所以要处理多个环境,你需要做一些事情,比如将环境的所有证书打包到一个信任库中,然后将它打包到一个单一的JAR/WAR 用于所有环境或构建多个 JAR/WAR 文件,每个环境一个。

    它还有一个缺点,就是您完全覆盖了默认的信任库。默认信任库中包含许多众所周知的 CA 证书,因此您必须从默认信任库的副本开始,然后附加您希望添加的证书。否则,您将丢失受信任的 CA 的默认列表,并且对 Google 或 Yahoo 等站点的验证将中断。默认的 CA 证书随每个 JRE 一起提供,但它可能因版本而异,因此您应该保持最新。

  3. 这是 #2 的细微变化。在名为.profile 的应用程序根目录下创建一个脚本文件。在其中,运行 keytool 并导入应用程序打包的所有证书。

    例如:

    #!/bin/bash 
    $HOME/.java-buildpack/open_jdk_jre/bin/keytool \
        -keystore $HOME/.java-buildpack/open_jdk_jre/lib/security/cacerts \
        -storepass changeit \
        -importcert \
        -noprompt \
        -alias MyCert \
        -file $HOME/BOOT-INF/classes/ssl/MyCert.crt
    # TODO: repeat this command for all the certs you need to import
    

    然后 运行 jar uf target/your-app.jar .profile 将脚本添加到应用程序的根目录 JAR/WAR。该脚本必须存在于您的 JAR/WAR 文件的根目录中,以便 Cloud Foundry 获取并执行。您可以 运行 jar tf target/your-app.jar | grep profile 确认这一点。输出应指示 .profile 且无前导路径。如果有前导路径,则文件添加到错误的位置。

    这种方法的好处是您不需要提供完整的信任库。这是 #2 的一大缺点,在这种情况下,您将巧妙地使用由 Java buildpack 提供的 JRE 打包的默认信任库。该脚本将在您的应用程序启动之前附加额外的证书。

    这种方法的缺点是添加证书很麻烦,很容易 skipped/forgotten 导致您的应用程序崩溃。它还需要位于非常特定的位置,以便 Cloud Foundry 能够找到并执行它。此选项也存在需要将证书打包到 JAR/WAR 的缺点。

  4. 下一个选项是对#3 的迭代。在这种情况下,您包括 .profile 脚本但不将证书打包到您的 JAR/WAR 中。相反,您通过环境变量或绑定服务提供证书(这与 CredHub 服务代理很好地配合)。

    要使其正常工作,您需要调整脚本,使其从环境变量中取出证书,创建一个临时文件,然后使用 keytool 将其导入默认信任库。以下示例显示将其从环境变量中取出,但您可以使用 jq 并将其从 VCAP_SERVICES 中取出以用于绑定服务。

    例如:

    #!/bin/bash 
    $HOME/.java-buildpack/open_jdk_jre/bin/keytool \
        -keystore $HOME/.java-buildpack/open_jdk_jre/lib/security/cacerts \
        -storepass changeit \
        -importcert \
        -noprompt \
        -alias MyCert \
        -file <(echo $CERT_1)
    # TODO: repeat this command for all the certs you need to import
    

    此方法的好处与 #3 相同,只是您不再需要将证书与您的应用程序捆绑在一起。它们可以从外部喂食。缺点是一样的,.profile 脚本使用起来很棘手,可能会被遗忘。

  5. #2 的一个变体是在您的代码中设置系统属性。您只需要在 JVM 初始化 TLS 之前,在应用程序启动的早期就发生这种情况。如果之后发生,则什么也不会发生,也不会找到您的证书。

    article 解释了可以在 Spring 启动应用程序中放置初始化代码的不同位置。基本上,您只是用它来调用 System.setProperty("javax.net.ssl.trustStore", "path/to/custom/truststore"),如果您更改密码,System.setProperty("javax.net.ssl.trustStorePassword", "newpassword").

    我不喜欢这种方法,因为如果在您的代码 运行 之前初始化 TLS,事情可能会意外中断。恕我直言,控制它很棘手,尤其是在 Spring 启动时所有的自动配置(不是说这些不好,只是你必须非常小心顺序)。我发现在配置文件脚本中执行此操作更容易,保证在您的应用程序启动之前 运行。

  6. 另一种变体是手动配置 WebClient,完全不依赖于默认信任库。您可以创建自己的信任库,这为您提供了最大的灵活性。

    例如:

    @Bean
    public WebClient createWebClient() throws SSLException {
        SslContext sslContext = SslContextBuilder
                .forClient()
                .trustManager( /* TODO */ )
                .build();
        ClientHttpConnector httpConnector = HttpClient.create().secure(t -> t.sslContext(sslContext) )
        return WebClient.builder().clientConnector(httpConnector).build();
    }
    

    上面的代码中有一个 TODO 标记,您需要在其中添加您的信任管理器。 SO post 对如何做到这一点有一些很好的想法,但总的来说,您创建的内容和加载证书的位置非常灵活。

    这种方法的结果是它是完全可定制的。您可以从任何地方、环境变量、VCAP_SERVICES 甚至 Spring Cloud Config 提取您的证书。当你初始化你的 WebClient 时,你只需要它们在手边。缺点可能很明显,需要编写更多代码。


可能还有其他选择,但希望这能让您思考什么是可能的。上述信息还 涉及信任库的主题。如果您需要提供带有私钥的密钥库,上面的一些选项会略有不同。

  1. 不行。
  2. 它会工作,但有一个不同的 JVM 参数:javax.net.ssl.keyStore。 3 和 4。可以工作,但不是附加到默认密钥库,您必须在脚本中创建一个新密钥库并设置 javax.net.ssl.keyStore。与 #2 相比,它的工作量更大,但帮助不大。
  3. 它将使用相同的警告,但具有不同的 JVM 参数:javax.net.ssl.keyStore
  4. 它会起作用,您只需要对其进行编码。

您还需要更加小心密钥,因为它们是高度敏感的信息。恕我直言,确保它们不存储在 JAR/WAR 中并像 CredHub 或 Spring 云配置服务器那样保持安全是值得的。