如何将信任库和密钥库添加到 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 每个选项。
如果您只需要将证书分发给信任,那么添加 Bosh Trusted 证书并允许 Bosh 将它们分发到 CF 中的所有 VM 和容器是一个不错的方法。在 Ops Manager 中,在 Bosh 磁贴下,转到安全,您可以添加任意数量的受信任证书。
当多个应用程序需要信任证书时,这是一个很好的方法,例如 corporate/private CA。它也适用于不同的 environments/foundations,因为您有不同的 Ops Manager,它可以维护不同的受信任证书列表。如果您有许多需要信任的证书 and/or 如果证书仅由一个或少数应用程序使用,则可能难以管理。它还需要 CF 的管理权限,并且可能需要一段时间才能在整个基础上部署更新。
这是您在问题中提到的选项。将证书导入 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 一起提供,但它可能因版本而异,因此您应该保持最新。
这是 #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 的缺点。
下一个选项是对#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
脚本使用起来很棘手,可能会被遗忘。
#2 的一个变体是在您的代码中设置系统属性。您只需要在 JVM 初始化 TLS 之前,在应用程序启动的早期就发生这种情况。如果之后发生,则什么也不会发生,也不会找到您的证书。
此 article 解释了可以在 Spring 启动应用程序中放置初始化代码的不同位置。基本上,您只是用它来调用 System.setProperty("javax.net.ssl.trustStore", "path/to/custom/truststore")
,如果您更改密码,System.setProperty("javax.net.ssl.trustStorePassword", "newpassword")
.
我不喜欢这种方法,因为如果在您的代码 运行 之前初始化 TLS,事情可能会意外中断。恕我直言,控制它很棘手,尤其是在 Spring 启动时所有的自动配置(不是说这些不好,只是你必须非常小心顺序)。我发现在配置文件脚本中执行此操作更容易,保证在您的应用程序启动之前 运行。
另一种变体是手动配置 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
时,你只需要它们在手边。缺点可能很明显,需要编写更多代码。
可能还有其他选择,但希望这能让您思考什么是可能的。上述信息还 仅 涉及信任库的主题。如果您需要提供带有私钥的密钥库,上面的一些选项会略有不同。
- 不行。
- 它会工作,但有一个不同的 JVM 参数:
javax.net.ssl.keyStore
。
3 和 4。可以工作,但不是附加到默认密钥库,您必须在脚本中创建一个新密钥库并设置 javax.net.ssl.keyStore
。与 #2 相比,它的工作量更大,但帮助不大。
- 它将使用相同的警告,但具有不同的 JVM 参数:
javax.net.ssl.keyStore
。
- 它会起作用,您只需要对其进行编码。
您还需要更加小心密钥,因为它们是高度敏感的信息。恕我直言,确保它们不存储在 JAR/WAR 中并像 CredHub 或 Spring 云配置服务器那样保持安全是值得的。
有没有办法在PCF中添加truststore和keystore:
部署到 unix box 的应用程序可以具有信任库,并且可以通过将它们保存在单独的位置并将该位置添加为 vm 参数来外部化密钥库。
但是如何在 PCF 中外部化密钥库和信任库。
我遇到的选项 1 是将密钥库和信任库保存在 spring-boot app 的 /resourse 中,并在 manifest.yml : JAVA_OPTS arguments 中给出路径。但是如果我们有 5 个不同的环境,有 5 组不同的信任库和密钥库呢?
由于这个原因,我们需要将 .请问PCF有没有办法做到这一点
提前致谢!
正如您所提到的,有多种方法可以做到这一点,这取决于您的具体需求,以决定哪种方法最有效。我将尝试列出尽可能多的选项,并 pros/cons 每个选项。
如果您只需要将证书分发给信任,那么添加 Bosh Trusted 证书并允许 Bosh 将它们分发到 CF 中的所有 VM 和容器是一个不错的方法。在 Ops Manager 中,在 Bosh 磁贴下,转到安全,您可以添加任意数量的受信任证书。
当多个应用程序需要信任证书时,这是一个很好的方法,例如 corporate/private CA。它也适用于不同的 environments/foundations,因为您有不同的 Ops Manager,它可以维护不同的受信任证书列表。如果您有许多需要信任的证书 and/or 如果证书仅由一个或少数应用程序使用,则可能难以管理。它还需要 CF 的管理权限,并且可能需要一段时间才能在整个基础上部署更新。
这是您在问题中提到的选项。将证书导入 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 一起提供,但它可能因版本而异,因此您应该保持最新。
这是 #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 的缺点。
下一个选项是对#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
脚本使用起来很棘手,可能会被遗忘。#2 的一个变体是在您的代码中设置系统属性。您只需要在 JVM 初始化 TLS 之前,在应用程序启动的早期就发生这种情况。如果之后发生,则什么也不会发生,也不会找到您的证书。
此 article 解释了可以在 Spring 启动应用程序中放置初始化代码的不同位置。基本上,您只是用它来调用
System.setProperty("javax.net.ssl.trustStore", "path/to/custom/truststore")
,如果您更改密码,System.setProperty("javax.net.ssl.trustStorePassword", "newpassword")
.我不喜欢这种方法,因为如果在您的代码 运行 之前初始化 TLS,事情可能会意外中断。恕我直言,控制它很棘手,尤其是在 Spring 启动时所有的自动配置(不是说这些不好,只是你必须非常小心顺序)。我发现在配置文件脚本中执行此操作更容易,保证在您的应用程序启动之前 运行。
另一种变体是手动配置 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
时,你只需要它们在手边。缺点可能很明显,需要编写更多代码。
可能还有其他选择,但希望这能让您思考什么是可能的。上述信息还 仅 涉及信任库的主题。如果您需要提供带有私钥的密钥库,上面的一些选项会略有不同。
- 不行。
- 它会工作,但有一个不同的 JVM 参数:
javax.net.ssl.keyStore
。 3 和 4。可以工作,但不是附加到默认密钥库,您必须在脚本中创建一个新密钥库并设置javax.net.ssl.keyStore
。与 #2 相比,它的工作量更大,但帮助不大。 - 它将使用相同的警告,但具有不同的 JVM 参数:
javax.net.ssl.keyStore
。 - 它会起作用,您只需要对其进行编码。
您还需要更加小心密钥,因为它们是高度敏感的信息。恕我直言,确保它们不存储在 JAR/WAR 中并像 CredHub 或 Spring 云配置服务器那样保持安全是值得的。