编辑此页面

在 Native Executables 中使用 SSL

我们正迅速进入一个 SSL 无处不在的世界,因此能够使用 SSL 至关重要。

在本指南中,我们将讨论如何让您的 Native 可执行文件支持 SSL,因为 Native 可执行文件默认情况下不支持它。

如果您不打算使用 Native 可执行文件,则可以跳过此步骤,因为在 JDK 模式下,无需进一步操作即可支持 SSL。

先决条件

要完成本指南,您需要

  • 少于 20 分钟

  • 一个 IDE

  • 已安装 GraalVM,并已正确配置 JAVA_HOMEGRAALVM_HOME

  • Apache Maven 3.9.9

本指南基于 REST 客户端指南,因此您应该首先获取此 Maven 项目。

克隆 Git 仓库:git clone https://github.com/quarkusio/quarkus-quickstarts.git,或下载 存档

该项目位于 resteasy-client-quickstart 目录中。

看起来好像开箱即用?!?

如果您打开应用程序的配置文件 (src/main/resources/application.properties),您可以看到以下行

quarkus.rest-client."org.acme.rest.client.ExtensionsService".url=https://stage.code.quarkus.io/api

这会将我们的 REST 客户端配置为连接到 SSL REST 服务。

就本指南而言,我们还需要删除启动嵌入式 WireMock 服务器的配置,该服务器存根 REST 客户端响应,以便测试实际将调用传播到 https://stage.code.quarkus.io/api。 更新测试文件 src/test/java/org/acme/rest/client/ExtensionsResourceTest.java 并删除该行

@QuarkusTestResource(WireMockExtensions.class)

ExtensionsResourceTest 类中。

现在让我们将应用程序构建为 Native 可执行文件并运行测试

CLI
quarkus build --native
Maven
./mvnw install -Dnative

我们得到以下结果

[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------

所以,是的,看起来它开箱即用,本指南非常无用。

并非如此。 魔术发生在构建 Native 可执行文件时

[INFO] [io.quarkus.creator.phase.nativeimage.NativeImagePhase] /opt/graalvm/bin/native-image -J-Dsun.nio.ch.maxUpdateArraySize=100 -J-Djava.util.logging.manager=org.jboss.logmanager.LogManager -J-Dio.netty.leakDetection.level=DISABLED -J-Dio.netty.allocator.maxOrder=3 -J-Dvertx.logger-delegate-factory-class-name=io.quarkus.vertx.core.runtime.VertxLogDelegateFactory -J-Dvertx.disableDnsResolver=true -J-Duser.language=en -J-Duser.country=IE -J-Dfile.encoding=UTF-8 --features=io.quarkus.runner.Feature,io.quarkus.runtime.graal.ResourcesFeature,io.quarkus.runtime.graal.DisableLoggingFeature -J--add-exports=java.security.jgss/sun.security.krb5=ALL-UNNAMED -J--add-opens=java.base/java.text=ALL-UNNAMED -J--add-opens=java.base/java.io=ALL-UNNAMED -J--add-opens=java.base/java.lang.invoke=ALL-UNNAMED -J--add-opens=java.base/java.util=ALL-UNNAMED -H:InitialCollectionPolicy=com.oracle.svm.core.genscavenge.CollectionPolicy\$BySpaceAndTime -H:+AllowFoldMethods -J-Djava.awt.headless=true -H:FallbackThreshold=0 --link-at-build-time -H:+ReportExceptionStackTraces -H:-AddAllCharsets -H:EnableURLProtocols=http,https -H:NativeLinkerOption=-no-pie -H:-UseServiceLoaderFeature -H:+StackTrace -J--add-exports=org.graalvm.sdk/org.graalvm.nativeimage.impl=ALL-UNNAMED -J--add-exports=org.graalvm.nativeimage.builder/com.oracle.svm.core.jdk=ALL-UNNAMED -J--add-exports=org.graalvm.nativeimage.builder/com.oracle.svm.core.configure=ALL-UNNAMED -J--add-exports=org.graalvm.nativeimage.builder/com.oracle.svm.core.jdk.proxy=ALL-UNNAMED -J--add-exports=org.graalvm.nativeimage.builder/com.oracle.svm.core.jdk.localization=ALL-UNNAMED rest-client-quickstart-1.0.0-SNAPSHOT-runner -jar rest-client-quickstart-1.0.0-SNAPSHOT-runner.jar

重要的部分是 Quarkus 自动添加的以下选项

-H:EnableURLProtocols=http,https

它为您的 Native 可执行文件启用 Native SSL 支持。 但您不应手动设置它,我们为此目的提供了一个很好的配置属性,如下所述。

由于 SSL 实际上已成为当今的标准,因此我们决定为我们的一些扩展自动启用对其的支持

  • Agroal 连接池扩展 (quarkus-agroal),

  • Amazon Services 扩展 (quarkus-amazon-*),

  • Consul Config 扩展 (quarkus-config-consul),

  • Elasticsearch 客户端扩展(quarkus-elasticsearch-rest-clientquarkus-elasticsearch-java-client)以及 Hibernate Search Elasticsearch 扩展 (quarkus-hibernate-search-orm-elasticsearch),

  • Elytron Security OAuth2 扩展 (quarkus-elytron-security-oauth2),

  • gRPC 扩展 (quarkus-grpc),

  • Infinispan Client 扩展 (quarkus-infinispan-client)。

  • Jaeger 扩展 (quarkus-jaeger),

  • JGit 扩展 (quarkus-jgit),

  • JSch 扩展 (quarkus-jsch),

  • Kafka Client 扩展 (quarkus-kafka-client),如果使用 Apicurio Registry 2.x Avro 库

  • Keycloak Authorization 扩展 (quarkus-keycloak-authorization),

  • Kubernetes 客户端扩展 (quarkus-kubernetes-client),

  • Logging Sentry 扩展 (quarkus-logging-sentry),

  • Mailer 扩展 (quarkus-mailer),

  • MongoDB 客户端扩展 (quarkus-mongodb-client),

  • Neo4j 扩展 (quarkus-neo4j),

  • OIDC 和 OIDC 客户端扩展(quarkus-oidcquarkus-oidc-client),

  • 用于 IBM DB2 的 Reactive 客户端扩展 (quarkus-reactive-db2-client),

  • 用于 PostgreSQL 的 Reactive 客户端扩展 (quarkus-reactive-pg-client),

  • 用于 MySQL 的 Reactive 客户端扩展 (quarkus-reactive-mysql-client),

  • 用于 Microsoft SQL Server 的 Reactive 客户端扩展 (quarkus-reactive-mssql-client),

  • Redis 客户端扩展 (quarkus-redis-client),

  • RESTEasy Classic REST Client 扩展 (quarkus-resteasy-client),

  • REST Client 扩展 (quarkus-rest-client),

  • SmallRye GraphQL Client 扩展 (quarkus-smallrye-graphql-client),

  • Spring Cloud Config client 扩展 (quarkus-spring-cloud-config-client),

  • Vault 扩展 (quarkus-vault),

  • Cassandra 客户端扩展 (cassandra-quarkus-client)

只要您的项目中包含这些扩展之一,默认情况下将启用 SSL 支持。

如果您未使用任何这些扩展,但仍想启用 SSL 支持,请将以下内容添加到您的配置中

quarkus.ssl.native=true

现在,让我们检查一下我们的 Native 可执行文件的大小,因为它稍后会很有用

$ ls -lh target/resteasy-client-quickstart-1.0.0-SNAPSHOT-runner
-rwxrwxr-x. 1 gandrian gandrian 46M Jun 11 13:01 target/rest-client-quickstart-1.0.0-SNAPSHOT-runner

让我们禁用 SSL 并看看会发生什么

Quarkus 有一个完全禁用 SSL 支持的选项。 为什么? 因为它有一定的成本。 因此,如果您确定不需要它,您可以完全禁用它。

首先,让我们在不更改 REST 服务 URL 的情况下禁用它,看看会发生什么。

打开 src/main/resources/application.properties 并添加以下行

quarkus.ssl.native=false

让我们尝试再次构建

CLI
quarkus build --native
Maven
./mvnw install -Dnative

Native 可执行文件测试将失败,并显示以下错误

Caused by: java.lang.IllegalArgumentException: https://stage.code.quarkus.io/api requires SSL support but it is disabled. You probably have set quarkus.ssl.native to false.

当您尝试使用 SSL,但在您的 Native 可执行文件中未明确启用它时,会收到此错误。

现在,让我们更改 REST 服务 URL 以src/main/resources/application.properties 中使用 SSL

quarkus.rest-client."org.acme.rest.client.ExtensionsService".url=http://stage.code.quarkus.io/api

并且由于 http://stage.code.quarkus.io/api 响应 302 状态代码,我们还需要使用 -DskipTests 跳过测试。

现在我们可以再次构建

CLI
quarkus build --native -DskipTests
Maven
./mvnw install -Dnative -DskipTests

如果您仔细检查 Native 可执行文件构建选项,您可以看到 SSL 相关选项已消失

[INFO] [io.quarkus.creator.phase.nativeimage.NativeImagePhase] /opt/graalvm/bin/native-image -J-Djava.util.logging.manager=org.jboss.logmanager.LogManager -J-Dcom.sun.xml.internal.bind.v2.bytecode.ClassTailor.noOptimize=true -H:InitialCollectionPolicy=com.oracle.svm.core.genscavenge.CollectionPolicy$BySpaceAndTime -jar rest-client-1.0.0-SNAPSHOT-runner.jar -J-Djava.util.concurrent.ForkJoinPool.common.parallelism=1 -H:+PrintAnalysisCallTree -H:EnableURLProtocols=http -H:-SpawnIsolates -H:+JNI --no-server -H:-UseServiceLoaderFeature -H:+StackTrace

我们最终得到

[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------

您还记得我们检查了启用 SSL 的 Native 可执行文件的大小吗? 让我们再次检查完全禁用 SSL 支持的情况

$ ls -lh target/resteasy-client-quickstart-1.0.0-SNAPSHOT-runner
-rwxrwxr-x. 1 gandrian gandrian 35M Jun 11 13:06 target/resteasy-client-quickstart-1.0.0-SNAPSHOT-runner

是的,现在是 35 MB,而过去是 46 MB。 SSL 在 Native 可执行文件大小中带来了 11 MB 的开销。

还有更多。

让我们从头开始

让我们使用以下命令恢复对配置文件所做的更改并返回到 SSL

git checkout -- src/main/resources/application.properties

让我们再次构建 Native 可执行文件

CLI
quarkus build --native
Maven
./mvnw install -Dnative

TrustStore 路径

本节介绍如何在构建 Native 可执行文件时配置默认信任存储。 但是,**强烈建议**改用 TLS 注册表。 TLS 注册表可确保在 JVM 和 Native 模式下都能获得一致的体验和功能集。

此行为是 GraalVM 21.3+ 的新增功能。

GraalVM 支持构建时和运行时证书配置。

构建时配置

构建时方法倾向于“不可变安全性”原则,即在构建时添加适当的证书,并且之后永远无法更改。 这保证了在生产中部署应用程序时,有效证书的列表不会被篡改。

但是,这有一些缺点

  • 如果您在所有环境中使用相同的可执行文件,并且证书过期,则需要重新构建应用程序,并使用新证书重新部署到生产环境中,这很不方便。

  • 更糟糕的是,如果由于安全漏洞而吊销了证书,则所有嵌入该证书的应用程序都需要及时重新构建和重新部署。

  • 这还需要将所有环境(例如 devtestprod)的所有证书添加到应用程序中,这意味着 dev 模式所需的证书但不能在其他地方使用,无论如何都会进入生产环境。

  • 在构建时提供所有证书会使 CI 复杂化,尤其是在 Kubernetes 等动态环境中,有效证书由平台在 /var/run/secrets/kubernetes.io/serviceaccount/ca.crt PEM 文件中提供。

  • 最后,这与不为每个客户环境提供专用版本的第三方软件不兼容。

使用构建时证书创建 Native 可执行文件本质上意味着根证书在映像构建时固定,基于构建时使用的证书配置(对于 Quarkus 意味着当您执行构建且设置了 quarkus.native.enabled=true 时)。 这避免了运送 cacerts 文件或需要设置系统属性才能设置由二进制文件运行的操作系统提供的根证书。

在这种情况下,诸如 javax.net.ssl.trustStore 之类的系统属性在运行时不起作用,因此当需要更改默认值时,必须在映像构建时提供这些系统属性。 最简单的方法是设置 quarkus.native.additional-build-args。 例如

quarkus.native.additional-build-args=-J-Djavax.net.ssl.trustStore=/tmp/mycerts,-J-Djavax.net.ssl.trustStorePassword=changeit

将确保 /tmp/mycerts 的证书被烘焙到 Native 二进制文件中,并代替默认的 cacerts 使用。 包含自定义 TrustStore 的文件必(并且可能不应该)在运行时存在,因为其内容已烘焙到 Native 二进制文件中。

运行时配置

自 21.3 起 GraalVM 支持的运行时证书配置与常规 Java 程序或 jvm 模式下的 Quarkus 相比,不需要任何特殊或额外的配置。 有关更多信息,请参见“Native Image 中的 GraalVM 证书管理”指南的 运行时选项部分。

使用容器

在容器中运行 Native 二进制文件时,无需采取任何特殊操作。 如果 Native 二进制文件已按照上一节中描述的那样使用自定义 TrustStore 正确构建,则它也可以在容器中正常工作。

结论

我们使使用 SSL 构建 Native 可执行文件变得容易,并提供了多种选项来很好地应对不同类型的安全要求。

相关内容