ネイティブイメージでのSSLの利用
常時SSL接続の時代へと急速に移行しているので、SSLを利用できることは非常に重要です。
ネイティブ実行可能ファイルは標準ではSSLをサポートしていないので、このガイドではネイティブ実行可能ファイルでSSLをサポートする方法を説明します。
JDK モードでは特に変更しなくても SSL がサポートされているので、ネイティブ実行可能ファイルを使用する予定がない場合はパスできます。 |
前提条件
このガイドを完成させるには、以下が必要です:
-
20分弱
-
IDE
-
GraalVMがインストールされ、
JAVA_HOME
とGRAALVM_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 サービスに接続するように設定しています。
このガイドの目的から、RESTクライアントの応答をスタブ化する組み込み型のWireMockサーバーを起動する設定を削除して、テストが実際に https://stage.code.quarkus.io/api への呼び出しを伝播するようにする必要もあります。テストファイル src/test/java/org/acme/rest/client/ExtensionsResourceTest.java
を更新し、次の行を削除します。
@QuarkusTestResource(WireMockExtensions.class)
ExtensionsResourceTest
クラスから。
それでは、アプリケーションをネイティブ実行可能ファイルとしてビルドし、テストを実行してみましょう:
quarkus build --native
./mvnw install -Dnative
そして、次のような結果が得られます:
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
はい、修正不要で動作し、このガイドはかなり役立たずであるように見えますね。
実際は、そうではありません。マジックはネイティブ実行可能ファイルをビルドするときに起こります:
[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
これらは、ネイティブ実行可能ファイルのネイティブなSSLサポートを有効にします。しかし、手動で設定するべきではありません。この目的のために、以下のような優れた設定プロパティが用意されています。
SSLが事実上の標準となっている現在、私たちのエクステンションの一部では自動的にSSLのサポートを有効にすることにしました:
-
Agroalコネクションプールエクステンション (
quarkus-agroal
)、 -
Amazon Services エクステンション(
quarkus-amazon-*
), -
Consul Config エクステンション(
quarkus-consul-config
), -
Elasticsearch クライアントエクステンション (
quarkus-elasticsearch-rest-client
とquarkus-elasticsearch-java-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
andquarkus-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
), -
RESTEasy Classic REST クライアントエクステンション (
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
ここで、あとで役立つので、ネイティブ実行可能ファイルのサイズを確認してみましょう:
$ 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
そして、またビルドしてみましょう:
quarkus build --native
./mvnw install -Dnative
ネイティブ実行可能ファイルのテストは、以下のエラーで失敗します:
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を使用しようとしたときに発生するエラーです。
では、 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
のテストもスキップする必要があります。
ではまたビルドしてみましょう:
quarkus build --native -DskipTests
./mvnw install -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/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
はい、以前は 46MB だったのが 35MB になりました。SSLはネイティブの実行可能サイズで11MBのオーバーヘッドを持っています。
そして、それだけではありません。
クリーンな状態でもう一回
設定ファイルに変更した内容を元に戻し、以下のコマンドでSSLを有効に戻してみましょう。
git checkout -- src/main/resources/application.properties
そして、もう一度ネイティブ実行可能ファイルをビルドしてみましょう:
quarkus build --native
./mvnw install -Dnative
TrustStoreのパス
この動作は、GraalVM 21.3+の新機能です。 |
GraalVMは、ビルド時と実行時の両方の証明書設定をサポートしています。
ビルド時設定
ビルド時アプローチでは、適切な証明書がビルド時に追加され、その後は決して変更できないという「不変のセキュリティ」の原則を採用しています。これにより、アプリケーションが本番環境にデプロイされる際に、有効な証明書のリストが改ざんされないことが保証されます。
しかし、これにはいくつかの欠点があります。
-
すべての環境で同じ実行可能ファイルを使用している場合、証明書の有効期限が切れると、アプリケーションを再構築し、新しい証明書を使って本番環境に再展開する必要があり、不便です。
-
さらに悪いことに、セキュリティ侵害が原因で証明書が失効した場合、その証明書を組み込んだすべてのアプリケーションを適時に再構築し、再展開する必要があります。
-
これは、すべての環境(例:
dev
、test
、prod
)に対応するすべての証明書をアプリケーションに追加する必要があることを意味します。つまり、開発モードでは必要だが、他の場所では使用すべきでない証明書が、本番環境では結果として含まれてしまうことになります。 -
ビルド時にすべての証明書を提供すると、CIが複雑になります。特に、Kubernetesのような動的な環境では、有効な証明書がプラットフォームから
/var/run/secrets/kubernetes.io/serviceaccount/ca.crt
PEMファイルで提供されます。 -
最後に、お客様の環境に合わせて専用のビルドを提供しないサードパーティのソフトウェアとは相性が良くありません。
ビルド時証明書を使用してネイティブ実行可能ファイルを作成するということは、基本的に、ビルド時(Quarkusの場合は、 quarkus.native.enabled=true
を設定してビルドを実行した時)に使用される証明書設定に基づいて、イメージのビルド時にルート証明書が固定されるということです。これにより、バイナリを実行する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
上記は /tmp/mycerts
の証明書がネイティブバイナリーに焼き込まれ、デフォルトの cacerts に 追加で 使用されることを保証します。カスタム TrustStore を含むファイルはその内容がネイティブバイナリに焼きこまれているので、実行時に存在する必要は ありません (そしておそらく存在すべきではありません)。
実行時設定
21.3以降GraalVMでサポートされている実行時証明書設定を使用すると、通常のJavaプログラムやjvmモードのQuarkusと比べて、特別な追加設定は必要ありません。 詳細については、「ネイティブイメージでのGraalVM証明書管理」ガイドの 実行時オプション セクションを参照してください。