The English version of quarkus.io is the official project site. Translated sites are community supported on a best-effort basis.

ネイティブイメージでのSSLの利用

常時SSL接続の時代へと急速に移行しているので、SSLを利用できることは非常に重要です。

ネイティブ実行可能ファイルは標準ではSSLをサポートしていないので、このガイドではネイティブ実行可能ファイルでSSLをサポートする方法を説明します。

JDK モードでは特に変更しなくても SSL がサポートされているので、ネイティブ実行可能ファイルを使用する予定がない場合はパスできます。

前提条件

このガイドを完成させるには、以下が必要です:

  • 20分弱

  • IDE

  • GraalVM (Java 11) をインストールし、 JAVA_HOMEGRAALVM_HOME を適切に設定しました。

  • Apache Maven 3.8.1+

This guide is based on the REST client guide, so you should get this Maven project first.

Gitレポジトリをクローンするか git clone https://github.com/quarkusio/quarkus-quickstarts.gitアーカイブ をダウンロードします。

プロジェクトは rest-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 サービスに接続するように設定しています。

このガイドの目的から、RESTクライアントの応答をスタブ化する組み込み型のWireMockサーバーを起動する設定を削除して、テストが実際に https://stage.code.quarkus.io/api への呼び出しを伝播するようにする必要もあります。テストファイル src/test/java/org/acme/rest/client/ExtensionsResourceTest.java を更新し、次の行を削除します。

@QuarkusTestResource(WireMockExtensions.class)

ExtensionsResourceTest クラスから。

それでは、アプリケーションをネイティブ実行可能ファイルとしてビルドし、テストを実行してみましょう:

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

そして、次のような結果が得られます:

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

はい、修正不要で動作し、このガイドはかなり役立たずであるように見えますね。

実際は、そうではありません。マジックはネイティブ実行可能ファイルをビルドするときに起こります:

[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,https -H:-SpawnIsolates -H:+JNI --no-server -H:-UseServiceLoaderFeature -H:+StackTrace

重要なのは、Quarkusによって自動的に追加された以下の2つのオプションです。

-H:EnableURLProtocols=http,https -H:+JNI

これらは、あなたのネイティブ実行可能ファイルのネイティブなSSLサポートを有効にします。しかし、手動で設定するべきではありません。この目的のために、以下のような優れた設定プロパティが用意されています。

SSLが事実上の標準となっている現在、私たちのエクステンションの一部では自動的にSSLのサポートを有効にすることにしました:

  • Agroalコネクションプールエクステンション ( quarkus-agroal )、

  • Amazon Services エクステンション(quarkus-amazon-*),

  • Consul Config エクステンション(quarkus-consul-config),

  • Elasticsearch client エクステンション(quarkus-elasticsearch-rest-client and quarkus-elasticsearch-rest-high-level-client) と Hibernate Search Elasticsearch エクステンション(quarkus-hibernate-search-orm-elasticsearch),

  • Elytron Security OAuth2 エクステンション(quarkus-elytron-security-oauth2),

  • gRPC エクステンション(quarkus-grpc),

  • Infinispan クライアントエクステンション ( quarkus-infinispan-client )、

  • Jaeger エクステンション ( quarkus-jaeger )、

  • JGit エクステンション ( quarkus-jgit )、

  • JSch エクステンション(quarkus-jsch),

  • Apicurio Registry 2.xのAvroライブラリが使用されている場合、Kafka Client エクステンション( quarkus-kafka-client)。

  • Keycloak Authorization エクステンション(quarkus-keycloak-authorization),

  • Kubernetes クライアントエクステンション ( quarkus-kubernetes-client )、

  • Logging Sentry エクステンション(quarkus-logging-sentry),

  • Mailer エクステンション ( quarkus-mailer ) 、

  • MongoDB client エクステンション(quarkus-mongodb-client),

  • Neo4j エクステンション ( quarkus-neo4j ) 、

  • OIDC とOIDC client エクステンション(quarkus-oidc and quarkus-oidc-client),

  • IBM DB2 エクステンションのReactiveクライアント(quarkus-reactive-db2-client),

  • PostgreSQLエクステンションのReactiveクライアント ( quarkus-reactive-pg-client ) 、

  • MySQL エクステンションの Reactive クライアント ( quarkus-reactive-mysql-client ) 、

  • Reactive client for Microsoft SQL Server エクステンション(quarkus-reactive-mssql-client),

  • Redis クライアントエクステンション ( quarkus-redis-client ) 、

  • REST Client エクステンション(quarkus-rest-client),

  • REST Client Reactive エクステンション(quarkus-rest-client-reactive),

  • the SmallRye GraphQL Client extension (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

ここで、あとで役立つので、ネイティブ実行可能ファイルのサイズを確認してみましょう:

$ ls -lh target/rest-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 package -Dnative

ネイティブ実行可能ファイルのテストは、以下のエラーで失敗します:

Caused by: java.net.MalformedURLException: Accessing an URL protocol that was not enabled. The URL protocol https is supported but not enabled by default. It must be enabled by adding the --enable-url-protocols=https option to the native-image command..

このエラーは、ネイティブ実行可能ファイルで明示的に有効にしない状態でSSLを使用しようとしたときに発生するエラーです。

では、 src/main/resources/application.properties でRESTサービスのURLを変更して、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 package -Dnative -DskipTests

ネイティブ実行可能ファイルのビルドオプションをよく確認してみると、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を有効にしたネイティブ実行可能ファイルのサイズを確認したのを覚えていますか?SSLサポートを完全に無効にした状態でもう一度確認してみましょう:

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

Yes, it is now 35 MB whereas it used to be 46 MB. SSL comes with an 11 MB overhead in native executable size.

そして、それだけではありません。

クリーンな状態でもう一回

設定ファイルに変更した内容を元に戻し、以下のコマンドでSSLを有効に戻してみましょう。

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

そして、もう一度ネイティブ実行可能ファイルをビルドしてみましょう:

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

TrustStoreのパス

この動作は、GraalVM 21.3+の新機能です。

GraalVMは、ビルド時と実行時の両方の証明書設定をサポートしています。

ビルド時設定

ビルド時アプローチでは、適切な証明書がビルド時に追加され、その後は決して変更できないという「不変のセキュリティ」の原則を採用しています。これにより、アプリケーションが本番環境にデプロイされる際に、有効な証明書のリストが改ざんされないことが保証されます。

しかし、これにはいくつかの欠点があります。

  • すべての環境で同じ実行ファイルを使用している場合、証明書の有効期限が切れると、アプリケーションを再構築し、新しい証明書を使って本番環境に再展開する必要があり、不便です。

  • さらに悪いことに、セキュリティ侵害が原因で証明書が失効した場合、その証明書を組み込んだすべてのアプリケーションを適時に再構築し、再展開する必要があります。

  • このため、すべての環境(DEV、TEST、PRODなど)に対応したすべての証明書をアプリケーションに追加する必要があります。つまり、DEVでは必要だが他の場所では使用してはいけない証明書が、本番環境ではそのまま使用されてしまうということです。

  • ビルド時にすべての証明書を提供すると、CIが複雑になります。特に、Kubernetesのような動的な環境では、有効な証明書がプラットフォームから /var/run/secrets/kubernetes.io/serviceaccount/ca.crt PEMファイルで提供されます。

  • 最後に、お客様の環境に合わせて専用のビルドを提供しないサードパーティのソフトウェアとは相性が良くありません。

ビルド時の証明書を使用してネイティブ実行可能ファイルを作成することは、基本的に、ビルド時(Quarkusでは、 quarkus.package.type=native が設定されているビルドの実行時を意味します)に使用される証明書構成に基づいて、イメージのビルド時にルート証明書が固定されることを意味します。これにより、バイナリを実行するOSによって提供されるルート証明書を設定するために、 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

will ensure that the certificates of /tmp/mycerts are baked into the native binary and used in addition to the default cacerts. The file containing the custom TrustStore does not (and probably should not) have to be present at runtime as its content has been baked into the native binary.

実行時設定

Using the runtime certificate configuration, supported by GraalVM since 21.3 does not require any special or additional configuration compared to regular java programs or Quarkus in jvm mode. See the GraalVM documentation for more information.

コンテナーへの対応

コンテナー内でネイティブバイナリーを実行する際に特別な対応を取る必要はありません。前のセクションで説明したように、ネイティブバイナリーがカスタムTrustStoreを使って適切にビルドされていれば、コンテナー内でも正常に動作します。

まとめ

私たちは、SSLを使ったネイティブ実行可能ファイルの構築を容易にし、様々なタイプのセキュリティ要件に対処するための複数のオプションを提供します。