-
如果您只需要将证书分发到信任,那么添加 Bosh 受信任证书并允许 Bosh 将它们分发到 CF 中的所有 VM 和容器是一个不错的方法。在 Ops Manager 的 Bosh 磁贴下,转到 Security,您可以添加任意数量的受信任证书。
当多个应用需要信任证书时,这是一种很好的方法,例如使用公司/私有 CA。它也适用于不同的环境/基础,因为您有不同的 Ops Manager 可以维护不同的受信任证书列表。如果您有许多需要受信任的证书和/或证书仅由一个或少数应用程序使用,则可能难以管理。它还需要 CF 的管理权限,并且可能需要一段时间才能在整个基础上部署更新。
-
这是您在问题中提到的选项。将证书导入 Java 信任库文件,将文件打包到 Java 应用程序中,并通过 JAVA_OPTS 环境变量指定其路径。信任库文件可以放在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 等网站。每个 JRE 都附带默认 CA 证书,但它可能因版本而异,因此您应该保持最新。
-
这与#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 打包的默认信任库。该脚本将在您的应用程序启动之前附加额外的证书。
这种方法的缺点是添加证书很麻烦,很容易被跳过/忘记,从而导致您的应用程序崩溃。它还需要位于一个非常特定的位置,以便 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 Boot 应用程序中放置初始化代码的各个位置。基本上,你只是用它来调用System.setProperty("javax.net.ssl.trustStore", "path/to/custom/truststore"),如果你更改密码,System.setProperty("javax.net.ssl.trustStorePassword", "newpassword")。
我不喜欢这种方法,因为如果在您的代码运行之前初始化 TLS,事情可能会意外中断。恕我直言,控制这一点很棘手,尤其是在具有所有自动配置的 Spring Boot 中(并不是说这些不好,只是你必须非常小心顺序)。我发现在保证在您的应用启动之前运行的配置文件脚本中执行此操作更容易。
-
另一种变体是手动配置您的 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 时,您只需要方便地使用它们。缺点可能很明显,需要编写更多代码。
您还需要更加小心密钥,因为它们是高度敏感的信息。恕我直言,确保它们没有存储在 JAR/WAR 中并像在 CredHub 或 Spring Cloud Config Server 中一样保持安全是值得的。