From da0f0274a48586bbe098b1c72c77c5b4f67eb0c5 Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Wed, 10 Jun 2020 13:42:08 +0300 Subject: [PATCH 1/4] Ensure that SSL logs can be printed at runtime --- .../io/quarkus/deployment/SslProcessor.java | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/core/deployment/src/main/java/io/quarkus/deployment/SslProcessor.java b/core/deployment/src/main/java/io/quarkus/deployment/SslProcessor.java index be1ccc3d8137f..4448a9223e7b6 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/SslProcessor.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/SslProcessor.java @@ -2,14 +2,19 @@ import java.util.Optional; +import io.quarkus.deployment.annotations.BuildProducer; import io.quarkus.deployment.annotations.BuildStep; import io.quarkus.deployment.builditem.SslNativeConfigBuildItem; +import io.quarkus.deployment.builditem.nativeimage.RuntimeReinitializedClassBuildItem; import io.quarkus.runtime.annotations.ConfigItem; import io.quarkus.runtime.annotations.ConfigPhase; import io.quarkus.runtime.annotations.ConfigRoot; public class SslProcessor { + private static final String JAVA_11_PLUS_SSL_LOGGER = "sun.security.ssl.SSLLogger"; + private static final String JAVA_8_PLUS_SSL_LOGGER = "sun.security.ssl.Debug"; + SslConfig ssl; @ConfigRoot(phase = ConfigPhase.BUILD_TIME) @@ -25,4 +30,19 @@ static class SslConfig { SslNativeConfigBuildItem setupNativeSsl() { return new SslNativeConfigBuildItem(ssl.native_); } + + @BuildStep + void runtime(BuildProducer reinitialized) { + registerIfExists(reinitialized, JAVA_11_PLUS_SSL_LOGGER); + registerIfExists(reinitialized, JAVA_8_PLUS_SSL_LOGGER); + } + + private void registerIfExists(BuildProducer reinitialized, String className) { + try { + Class.forName(className); + reinitialized.produce(new RuntimeReinitializedClassBuildItem(className)); + } catch (ClassNotFoundException ignored) { + + } + } } From 533f758f67c929ac6faf527fa9cdb6ffeb089a00 Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Wed, 10 Jun 2020 15:58:54 +0300 Subject: [PATCH 2/4] Bring generated main in line with how GraalVM handles javax.net.ssl.trustStore --- .../SslTrustStoreSystemPropertyBuildItem.java | 16 ------- .../pkg/steps/NativeImageBuildStep.java | 48 +++++++++++++++++-- .../deployment/steps/MainClassBuildStep.java | 32 ++++++------- .../steps/NativeImageConfigBuildStep.java | 22 +-------- 4 files changed, 61 insertions(+), 57 deletions(-) delete mode 100644 core/deployment/src/main/java/io/quarkus/deployment/builditem/SslTrustStoreSystemPropertyBuildItem.java diff --git a/core/deployment/src/main/java/io/quarkus/deployment/builditem/SslTrustStoreSystemPropertyBuildItem.java b/core/deployment/src/main/java/io/quarkus/deployment/builditem/SslTrustStoreSystemPropertyBuildItem.java deleted file mode 100644 index 532e570a8c849..0000000000000 --- a/core/deployment/src/main/java/io/quarkus/deployment/builditem/SslTrustStoreSystemPropertyBuildItem.java +++ /dev/null @@ -1,16 +0,0 @@ -package io.quarkus.deployment.builditem; - -import io.quarkus.builder.item.SimpleBuildItem; - -public final class SslTrustStoreSystemPropertyBuildItem extends SimpleBuildItem { - - private final String path; - - public SslTrustStoreSystemPropertyBuildItem(String path) { - this.path = path; - } - - public String getPath() { - return path; - } -} diff --git a/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildStep.java b/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildStep.java index c4c6a945b23fe..b5447c4b401a2 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildStep.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildStep.java @@ -5,6 +5,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; +import java.io.UncheckedIOException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; @@ -61,6 +62,9 @@ public class NativeImageBuildStep { private static final int OOM_ERROR_VALUE = 137; private static final String QUARKUS_XMX_PROPERTY = "quarkus.native.native-image-xmx"; + private static final String CONTAINER_BUILD_VOLUME_PATH = "/project"; + private static final String TRUST_STORE_SYSTEM_PROPERTY_MARKER = "-Djavax.net.ssl.trustStore="; + private static final String MOVED_TRUST_STORE_NAME = "trustStore"; @BuildStep(onlyIf = NativeBuild.class) ArtifactResultBuildItem result(NativeImageBuildItem image) { @@ -83,7 +87,8 @@ public NativeImageBuildItem build(NativeConfig nativeConfig, NativeImageSourceJa String noPIE = ""; - if (nativeConfig.containerRuntime.isPresent() || nativeConfig.containerBuild) { + boolean isContainerBuild = nativeConfig.containerRuntime.isPresent() || nativeConfig.containerBuild; + if (isContainerBuild) { String containerRuntime = nativeConfig.containerRuntime.orElse("docker"); // E.g. "/usr/bin/docker run -v {{PROJECT_DIR}}:/project --rm quarkus/graalvm-native-image" nativeImage = new ArrayList<>(); @@ -92,7 +97,8 @@ public NativeImageBuildItem build(NativeConfig nativeConfig, NativeImageSourceJa if (IS_WINDOWS) { outputPath = FileUtil.translateToVolumePath(outputPath); } - Collections.addAll(nativeImage, containerRuntime, "run", "-v", outputPath + ":/project:z", "--env", "LANG=C"); + Collections.addAll(nativeImage, containerRuntime, "run", "-v", + outputPath + ":" + CONTAINER_BUILD_VOLUME_PATH + ":z", "--env", "LANG=C"); if (IS_LINUX) { if ("docker".equals(containerRuntime)) { @@ -227,7 +233,7 @@ public NativeImageBuildItem build(NativeConfig nativeConfig, NativeImageSourceJa nativeConfig.enableAllSecurityServices = true; } - nativeConfig.additionalBuildArgs.ifPresent(l -> l.stream().map(String::trim).forEach(command::add)); + handleAdditionalProperties(nativeConfig, command, isContainerBuild, outputDir); nativeConfig.resources.includes.ifPresent(l -> l.stream() .map(GlobUtil::toRegexPattern) .map(re -> "-H:IncludeResources=" + re.trim()) @@ -343,7 +349,7 @@ public NativeImageBuildItem build(NativeConfig nativeConfig, NativeImageSourceJa if (exitCode != 0) { throw imageGenerationFailed(exitCode, command); } - if (IS_WINDOWS && !(nativeConfig.containerRuntime.isPresent() || nativeConfig.containerBuild)) { + if (IS_WINDOWS && !(isContainerBuild)) { //once image is generated it gets added .exe on Windows executableName = executableName + ".exe"; } @@ -359,6 +365,40 @@ public NativeImageBuildItem build(NativeConfig nativeConfig, NativeImageSourceJa } } + private void handleAdditionalProperties(NativeConfig nativeConfig, List command, boolean isContainerBuild, + Path outputDir) { + if (nativeConfig.additionalBuildArgs.isPresent()) { + List strings = nativeConfig.additionalBuildArgs.get(); + for (String buildArg : strings) { + String trimmedBuildArg = buildArg.trim(); + if (trimmedBuildArg.contains(TRUST_STORE_SYSTEM_PROPERTY_MARKER) && isContainerBuild) { + /* + * When the native binary is being built with a docker container, because a volume is create, + * we need to copy the trustStore file into the output directory (which is the root of volume) + * and change the value of 'javax.net.ssl.trustStore' property to point to this value + * + * TODO: we might want to introduce a dedicated property in order to overcome this ugliness + */ + int index = trimmedBuildArg.indexOf(TRUST_STORE_SYSTEM_PROPERTY_MARKER); + if (trimmedBuildArg.length() > index + 2) { + String configuredTrustStorePath = trimmedBuildArg + .substring(index + TRUST_STORE_SYSTEM_PROPERTY_MARKER.length()); + try { + IoUtils.copy(Paths.get(configuredTrustStorePath), outputDir.resolve(MOVED_TRUST_STORE_NAME)); + command.add(trimmedBuildArg.substring(0, index) + TRUST_STORE_SYSTEM_PROPERTY_MARKER + + CONTAINER_BUILD_VOLUME_PATH + "/" + MOVED_TRUST_STORE_NAME); + } catch (IOException e) { + throw new UncheckedIOException("Unable to copy trustStore file '" + configuredTrustStorePath + + "' to volume root directory '" + outputDir.toAbsolutePath().toString() + "'", e); + } + } + } else { + command.add(trimmedBuildArg); + } + } + } + } + private RuntimeException imageGenerationFailed(int exitValue, List command) { if (exitValue == OOM_ERROR_VALUE) { if (command.contains("docker") && !IS_LINUX) { diff --git a/core/deployment/src/main/java/io/quarkus/deployment/steps/MainClassBuildStep.java b/core/deployment/src/main/java/io/quarkus/deployment/steps/MainClassBuildStep.java index c35928cf1af9b..12820aae2f01b 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/steps/MainClassBuildStep.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/steps/MainClassBuildStep.java @@ -43,7 +43,6 @@ import io.quarkus.deployment.builditem.MainClassBuildItem; import io.quarkus.deployment.builditem.ObjectSubstitutionBuildItem; import io.quarkus.deployment.builditem.QuarkusApplicationClassBuildItem; -import io.quarkus.deployment.builditem.SslTrustStoreSystemPropertyBuildItem; import io.quarkus.deployment.builditem.StaticBytecodeRecorderBuildItem; import io.quarkus.deployment.builditem.SystemPropertyBuildItem; import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem; @@ -87,7 +86,6 @@ void build(List staticInitTasks, List mainMethod, List properties, List javaLibraryPathAdditionalPaths, - Optional sslTrustStoreSystemProperty, List features, BuildProducer appClassNameProducer, List loaders, @@ -184,20 +182,22 @@ void build(List staticInitTasks, mv.invokeVirtualMethod(ofMethod(StringBuilder.class, "toString", String.class), javaLibraryPath)); } - if (sslTrustStoreSystemProperty.isPresent()) { - ResultHandle alreadySetTrustStore = mv.invokeStaticMethod( - ofMethod(System.class, "getProperty", String.class, String.class), - mv.load(JAVAX_NET_SSL_TRUST_STORE)); - - BytecodeCreator inGraalVMCode = mv - .ifNonZero(mv.invokeStaticMethod(ofMethod(ImageInfo.class, "inImageRuntimeCode", boolean.class))) - .trueBranch(); - - inGraalVMCode.ifNull(alreadySetTrustStore).trueBranch().invokeStaticMethod( - ofMethod(System.class, "setProperty", String.class, String.class, String.class), - inGraalVMCode.load(JAVAX_NET_SSL_TRUST_STORE), - inGraalVMCode.load(sslTrustStoreSystemProperty.get().getPath())); - } + // GraalVM uses the build-time trustStore and bakes the backing classes of the TrustStore into the the native binary, + // so we need to warn users trying to set the 'javax.net.ssl.trustStore' property that it won't have an effect + ResultHandle trustStoreSystemProp = mv.invokeStaticMethod( + ofMethod(System.class, "getProperty", String.class, String.class), + mv.load(JAVAX_NET_SSL_TRUST_STORE)); + + BytecodeCreator inGraalVMCode = mv + .ifNonZero(mv.invokeStaticMethod(ofMethod(ImageInfo.class, "inImageRuntimeCode", boolean.class))) + .trueBranch(); + + BytecodeCreator inGraalVMCodeAndTrustStoreSet = inGraalVMCode.ifNull(trustStoreSystemProp).falseBranch(); + inGraalVMCodeAndTrustStoreSet.invokeVirtualMethod( + ofMethod(Logger.class, "warn", void.class, Object.class), + inGraalVMCodeAndTrustStoreSet.readStaticField(logField.getFieldDescriptor()), + inGraalVMCodeAndTrustStoreSet.load("Setting the '" + JAVAX_NET_SSL_TRUST_STORE + + "' system property will not have any effect at runtime. Make sure to set this property at build time (for example by setting 'quarkus.native.additional-build-args=-J-Djavax.net.ssl.trustStore=/some/path').")); mv.invokeStaticMethod(ofMethod(Timing.class, "mainStarted", void.class)); startupContext = mv.readStaticField(scField.getFieldDescriptor()); diff --git a/core/deployment/src/main/java/io/quarkus/deployment/steps/NativeImageConfigBuildStep.java b/core/deployment/src/main/java/io/quarkus/deployment/steps/NativeImageConfigBuildStep.java index 7fac0136c6b85..74bd913c1eb8c 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/steps/NativeImageConfigBuildStep.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/steps/NativeImageConfigBuildStep.java @@ -1,8 +1,5 @@ package io.quarkus.deployment.steps; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; import java.util.List; import java.util.Map; import java.util.stream.Collectors; @@ -20,7 +17,6 @@ import io.quarkus.deployment.builditem.NativeImageEnableAllCharsetsBuildItem; import io.quarkus.deployment.builditem.NativeImageEnableAllTimeZonesBuildItem; import io.quarkus.deployment.builditem.SslNativeConfigBuildItem; -import io.quarkus.deployment.builditem.SslTrustStoreSystemPropertyBuildItem; import io.quarkus.deployment.builditem.SystemPropertyBuildItem; import io.quarkus.deployment.builditem.nativeimage.NativeImageConfigBuildItem; import io.quarkus.deployment.builditem.nativeimage.NativeImageProxyDefinitionBuildItem; @@ -51,8 +47,7 @@ void build(SslContextConfigurationRecorder sslContextConfigurationRecorder, BuildProducer runtimeReinit, BuildProducer nativeImage, BuildProducer systemProperty, - BuildProducer javaLibraryPathAdditionalPath, - BuildProducer sslTrustStoreSystemProperty) { + BuildProducer javaLibraryPathAdditionalPath) { for (NativeImageConfigBuildItem nativeImageConfigBuildItem : nativeImageConfigBuildItems) { for (String i : nativeImageConfigBuildItem.getRuntimeInitializedClasses()) { runtimeInit.produce(new RuntimeInitializedClassBuildItem(i)); @@ -76,21 +71,6 @@ void build(SslContextConfigurationRecorder sslContextConfigurationRecorder, sslContextConfigurationRecorder.setSslNativeEnabled(!sslNativeConfig.isExplicitlyDisabled()); Boolean sslNativeEnabled = isSslNativeEnabled(sslNativeConfig, extensionSslNativeSupport); - if (sslNativeEnabled) { - // This makes the native image dependent on the local path used to build it. - // This is useful for testing but the user will have to override it. - String graalVmHome = System.getenv("GRAALVM_HOME"); - if (graalVmHome != null) { - // JDK 8 path - Path graalVmCacertsPath = Paths.get(graalVmHome, "jre", "lib", "security", "cacerts"); - if (!Files.exists(graalVmCacertsPath)) { - // Path starting with GraalVM JDK 11 - graalVmCacertsPath = Paths.get(graalVmHome, "lib", "security", "cacerts"); - } - - sslTrustStoreSystemProperty.produce(new SslTrustStoreSystemPropertyBuildItem(graalVmCacertsPath.toString())); - } - } nativeImage.produce(new NativeImageSystemPropertyBuildItem("quarkus.ssl.native", sslNativeEnabled.toString())); if (!enableAllSecurityServicesBuildItems.isEmpty()) { From bc861de992615cd655134361ec4adfbbfd24a07f Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Wed, 10 Jun 2020 17:35:53 +0300 Subject: [PATCH 3/4] Add test to make sure that trustStore work in native mode --- .../deployment/steps/MainClassBuildStep.java | 39 ++++++++++----- .../generate-trust-store-for-self-signed.sh | 10 ++++ integration-tests/rest-client/pom.xml | 6 ++- integration-tests/rest-client/self-signed | Bin 0 -> 1194 bytes .../ExternalSelfSignedResource.java | 45 ++++++++++++++++++ .../selfsigned/ExternalSelfSignedITCase.java | 8 ++++ .../ExternalSelfSignedTestCase.java | 23 +++++++++ 7 files changed, 117 insertions(+), 14 deletions(-) create mode 100755 integration-tests/rest-client/generate-trust-store-for-self-signed.sh create mode 100644 integration-tests/rest-client/self-signed create mode 100644 integration-tests/rest-client/src/main/java/io/quarkus/it/rest/client/selfsigned/ExternalSelfSignedResource.java create mode 100644 integration-tests/rest-client/src/test/java/io/quarkus/it/rest/client/selfsigned/ExternalSelfSignedITCase.java create mode 100644 integration-tests/rest-client/src/test/java/io/quarkus/it/rest/client/selfsigned/ExternalSelfSignedTestCase.java diff --git a/core/deployment/src/main/java/io/quarkus/deployment/steps/MainClassBuildStep.java b/core/deployment/src/main/java/io/quarkus/deployment/steps/MainClassBuildStep.java index 12820aae2f01b..0d06a11c0e6df 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/steps/MainClassBuildStep.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/steps/MainClassBuildStep.java @@ -6,7 +6,9 @@ import java.io.File; import java.lang.reflect.Modifier; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -75,7 +77,15 @@ class MainClassBuildStep { static final String STARTUP_CONTEXT = "STARTUP_CONTEXT"; static final String LOG = "LOG"; static final String JAVA_LIBRARY_PATH = "java.library.path"; - static final String JAVAX_NET_SSL_TRUST_STORE = "javax.net.ssl.trustStore"; + + private static final String JAVAX_NET_SSL_TRUST_STORE = "javax.net.ssl.trustStore"; + private static final String JAVAX_NET_SSL_TRUST_STORE_TYPE = "javax.net.ssl.trustStoreType"; + private static final String JAVAX_NET_SSL_TRUST_STORE_PROVIDER = "javax.net.ssl.trustStoreProvider"; + private static final String JAVAX_NET_SSL_TRUST_STORE_PASSWORD = "javax.net.ssl.trustStorePassword"; + private static final List BUILD_TIME_TRUST_STORE_PROPERTIES = Collections.unmodifiableList(Arrays.asList( + JAVAX_NET_SSL_TRUST_STORE, + JAVAX_NET_SSL_TRUST_STORE_TYPE, JAVAX_NET_SSL_TRUST_STORE_PROVIDER, + JAVAX_NET_SSL_TRUST_STORE_PASSWORD)); private static final FieldDescriptor STARTUP_CONTEXT_FIELD = FieldDescriptor.of(Application.APP_CLASS_NAME, STARTUP_CONTEXT, StartupContext.class); @@ -182,22 +192,25 @@ void build(List staticInitTasks, mv.invokeVirtualMethod(ofMethod(StringBuilder.class, "toString", String.class), javaLibraryPath)); } - // GraalVM uses the build-time trustStore and bakes the backing classes of the TrustStore into the the native binary, - // so we need to warn users trying to set the 'javax.net.ssl.trustStore' property that it won't have an effect - ResultHandle trustStoreSystemProp = mv.invokeStaticMethod( - ofMethod(System.class, "getProperty", String.class, String.class), - mv.load(JAVAX_NET_SSL_TRUST_STORE)); - BytecodeCreator inGraalVMCode = mv .ifNonZero(mv.invokeStaticMethod(ofMethod(ImageInfo.class, "inImageRuntimeCode", boolean.class))) .trueBranch(); - BytecodeCreator inGraalVMCodeAndTrustStoreSet = inGraalVMCode.ifNull(trustStoreSystemProp).falseBranch(); - inGraalVMCodeAndTrustStoreSet.invokeVirtualMethod( - ofMethod(Logger.class, "warn", void.class, Object.class), - inGraalVMCodeAndTrustStoreSet.readStaticField(logField.getFieldDescriptor()), - inGraalVMCodeAndTrustStoreSet.load("Setting the '" + JAVAX_NET_SSL_TRUST_STORE - + "' system property will not have any effect at runtime. Make sure to set this property at build time (for example by setting 'quarkus.native.additional-build-args=-J-Djavax.net.ssl.trustStore=/some/path').")); + // GraalVM uses the build-time trustStore and bakes the backing classes of the TrustStore into the the native binary, + // so we need to warn users trying to set the trust store related system properties that it won't have an effect + for (String property : BUILD_TIME_TRUST_STORE_PROPERTIES) { + ResultHandle trustStoreSystemProp = inGraalVMCode.invokeStaticMethod( + ofMethod(System.class, "getProperty", String.class, String.class), + mv.load(property)); + + BytecodeCreator inGraalVMCodeAndTrustStoreSet = inGraalVMCode.ifNull(trustStoreSystemProp).falseBranch(); + inGraalVMCodeAndTrustStoreSet.invokeVirtualMethod( + ofMethod(Logger.class, "warn", void.class, Object.class), + inGraalVMCodeAndTrustStoreSet.readStaticField(logField.getFieldDescriptor()), + inGraalVMCodeAndTrustStoreSet.load(String.format( + "Setting the '%s' system property will not have any effect at runtime. Make sure to set this property at build time (for example by setting 'quarkus.native.additional-build-args=-J-D%s=someValue').", + property, property))); + } mv.invokeStaticMethod(ofMethod(Timing.class, "mainStarted", void.class)); startupContext = mv.readStaticField(scField.getFieldDescriptor()); diff --git a/integration-tests/rest-client/generate-trust-store-for-self-signed.sh b/integration-tests/rest-client/generate-trust-store-for-self-signed.sh new file mode 100755 index 0000000000000..a15a1994a3902 --- /dev/null +++ b/integration-tests/rest-client/generate-trust-store-for-self-signed.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash + +########################################################################################################################################## +# Script that generates the trustStore containing the self signed certificate used in 'io.quarkus.it.rest.client.selfsigned.ExternalSelfSignedResource' # +########################################################################################################################################## + + +echo -n | openssl s_client -connect self-signed.badssl.com:443 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > self-signed.cert +keytool -importcert -file self-signed.cert -alias self-signed -keystore self-signed -storepass changeit -noprompt +rm self-signed.cert diff --git a/integration-tests/rest-client/pom.xml b/integration-tests/rest-client/pom.xml index bf77e6b144705..b271a14d68b0e 100644 --- a/integration-tests/rest-client/pom.xml +++ b/integration-tests/rest-client/pom.xml @@ -70,6 +70,8 @@ en + ${project.basedir}/self-signed + changeit @@ -119,9 +121,11 @@ false true true + true false false ${graalvmHome} + -J-Djavax.net.ssl.trustStore=${project.basedir}/self-signed, -J-Djavax.net.ssl.trustStorePassword=changeit @@ -131,4 +135,4 @@ - \ No newline at end of file + diff --git a/integration-tests/rest-client/self-signed b/integration-tests/rest-client/self-signed new file mode 100644 index 0000000000000000000000000000000000000000..52feac47daf37dda0c68c044e9b5aa440f6abc96 GIT binary patch literal 1194 zcmV;b1XcSmf&``l0Ru3C1YZUTDuzgg_YDCD0ic2ePy~VmOfZ53NHBr~L^znQ!Suj+rm!5`!B#@{06|+(KQRgFn zgKt(M2^<>tAC0Z!<(UOl)#(JxTAu%CRhpV;HZRS|E6;D9zzAkUPIIUcxwQzrUo!YN zc>Z`}zF&h<+nFsH{v!p~>q-?-ao!Kr@SaNdT1e0VK-_Asp~C^WY8RbY{E#wegW4cJ z=nB5XtaDYKYo?$Xid0>O-19s~faKS&;Yq5qtNPPAXww(ceOUWp!uNZIDl(uw z>CF?IekcdxFMQ6$_cHM=OJZw29|)9N!ABGcMZC7M-@&l?BB$aVh|la>DEE9qqfjkdf}Ry-2+)Ql4ZU*R5JYp^aWtAhU6`M?;4gq zzk=DL7Pw2C@^VKQ+!LU<0inq%GEm7a-42^ZUxfb+oS^Kne&Cmi&LfT6l+n_J z>IZWpb|$NBTzdx@3Y9LQS(kbsgiKK`8+_krv3lX`OSD$i>|UEhuim>Dz$L&CO25UQ zG7mBid2<9%huYTEef;TO5?YfS6n%q4TS`2==Kq2c&56^Du)4{Dfo=<9xHtvWnqY?d&?mH!h!b-jeB=U1P&(z?TpvV%FeI@H~&Jni=6YDmzX64 z@)BxU8JuQW!bS2pmEEo1efk}HOFk&52x~B|!8PbQ5Rcpqe^u}3eTJkKt)BgTa>m|j zj-jTkPTa6S%)p&Xe_zsZ&0sHmQ152}uLW8d^f&q$46uFel+?1;BSYsLAuv8LAutIB z1uG5%0vZJX1Qe1L=7=H1H$5#%Ds1LspnJ}Xd9(x+BcHm_1QUxlaNFSLAxW+Dtxk`w I0s{etpe9^A!vFvP literal 0 HcmV?d00001 diff --git a/integration-tests/rest-client/src/main/java/io/quarkus/it/rest/client/selfsigned/ExternalSelfSignedResource.java b/integration-tests/rest-client/src/main/java/io/quarkus/it/rest/client/selfsigned/ExternalSelfSignedResource.java new file mode 100644 index 0000000000000..f62a6f7d67462 --- /dev/null +++ b/integration-tests/rest-client/src/main/java/io/quarkus/it/rest/client/selfsigned/ExternalSelfSignedResource.java @@ -0,0 +1,45 @@ +package io.quarkus.it.rest.client.selfsigned; + +import java.io.IOException; +import java.net.URL; + +import javax.net.ssl.HttpsURLConnection; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; + +/** + * This has nothing to do with rest-client, but we add it here in order to avoid creating + * a new integration test that would slow down our CI + */ +@Path("/self-signed") +public class ExternalSelfSignedResource { + + @GET + @Produces(MediaType.TEXT_PLAIN) + public String perform() throws IOException { + try { + return doGetCipher(); + } catch (IOException e) { + + // if it fails it might be because the remote service is down, so sleep and try again + try { + Thread.sleep(1000); + } catch (InterruptedException ignored) { + + } + + return doGetCipher(); + } + } + + private String doGetCipher() throws IOException { + // this URL provides an always on example of an HTTPS URL utilizing self-signed certificate + URL url = new URL("https://self-signed.badssl.com/"); + HttpsURLConnection con = (HttpsURLConnection) url.openConnection(); + con.setRequestMethod("GET"); + con.getResponseCode(); + return con.getCipherSuite(); + } +} diff --git a/integration-tests/rest-client/src/test/java/io/quarkus/it/rest/client/selfsigned/ExternalSelfSignedITCase.java b/integration-tests/rest-client/src/test/java/io/quarkus/it/rest/client/selfsigned/ExternalSelfSignedITCase.java new file mode 100644 index 0000000000000..d7d20bcc28bc9 --- /dev/null +++ b/integration-tests/rest-client/src/test/java/io/quarkus/it/rest/client/selfsigned/ExternalSelfSignedITCase.java @@ -0,0 +1,8 @@ +package io.quarkus.it.rest.client.selfsigned; + +import io.quarkus.test.junit.NativeImageTest; + +@NativeImageTest +public class ExternalSelfSignedITCase extends ExternalSelfSignedTestCase { + +} diff --git a/integration-tests/rest-client/src/test/java/io/quarkus/it/rest/client/selfsigned/ExternalSelfSignedTestCase.java b/integration-tests/rest-client/src/test/java/io/quarkus/it/rest/client/selfsigned/ExternalSelfSignedTestCase.java new file mode 100644 index 0000000000000..7b2530e7d93bb --- /dev/null +++ b/integration-tests/rest-client/src/test/java/io/quarkus/it/rest/client/selfsigned/ExternalSelfSignedTestCase.java @@ -0,0 +1,23 @@ +package io.quarkus.it.rest.client.selfsigned; + +import static org.hamcrest.Matchers.empty; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.not; + +import org.junit.jupiter.api.Test; + +import io.quarkus.test.junit.QuarkusTest; +import io.restassured.RestAssured; + +@QuarkusTest +public class ExternalSelfSignedTestCase { + + @Test + public void included() { + RestAssured.when() + .get("/self-signed") + .then() + .statusCode(200) + .body(is(not(empty()))); + } +} From aa5820ed0c231ffdf373ee9f9988a7644bc9b199 Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Thu, 11 Jun 2020 14:10:30 +0300 Subject: [PATCH 4/4] Update the Quarkus SSL guide to reflect the latest state Co-authored-by: Guillaume Smet --- .../pkg/steps/NativeImageBuildStep.java | 2 +- docs/src/main/asciidoc/native-and-ssl.adoc | 81 ++++++------------- 2 files changed, 24 insertions(+), 59 deletions(-) diff --git a/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildStep.java b/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildStep.java index b5447c4b401a2..09b93d375f40d 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildStep.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildStep.java @@ -373,7 +373,7 @@ private void handleAdditionalProperties(NativeConfig nativeConfig, List String trimmedBuildArg = buildArg.trim(); if (trimmedBuildArg.contains(TRUST_STORE_SYSTEM_PROPERTY_MARKER) && isContainerBuild) { /* - * When the native binary is being built with a docker container, because a volume is create, + * When the native binary is being built with a docker container, because a volume is created, * we need to copy the trustStore file into the output directory (which is the root of volume) * and change the value of 'javax.net.ssl.trustStore' property to point to this value * diff --git a/docs/src/main/asciidoc/native-and-ssl.adoc b/docs/src/main/asciidoc/native-and-ssl.adoc index 2a6d688205768..9fc708011a8d8 100644 --- a/docs/src/main/asciidoc/native-and-ssl.adoc +++ b/docs/src/main/asciidoc/native-and-ssl.adoc @@ -84,8 +84,8 @@ As long as you have one of those extensions in your project, the SSL support wil Now, let's just check the size of our native executable as it will be useful later: ``` -$ ls -lh target/rest-client-1.0-SNAPSHOT-runner --rwxrwxr-x. 1 gsmet gsmet 34M Feb 22 15:27 target/rest-client-1.0-SNAPSHOT-runner +$ ls -lh target/rest-client-quickstart-1.0-SNAPSHOT-runner +-rwxrwxr-x. 1 gandrian gandrian 46M Jun 11 13:01 target/rest-client-quickstart-1.0-SNAPSHOT-runner ``` == Let's disable SSL and see how it goes @@ -138,11 +138,11 @@ And we end up with: You remember we checked the size of the native executable with SSL enabled? Let's check again with SSL support entirely disabled: ``` -$ ls -lh target/rest-client-1.0-SNAPSHOT-runner --rwxrwxr-x. 1 gsmet gsmet 25M Feb 22 15:19 target/rest-client-1.0-SNAPSHOT-runner +$ ls -lh target/rest-client-quickstart-1.0-SNAPSHOT-runner +-rwxrwxr-x. 1 gandrian gandrian 35M Jun 11 13:06 target/rest-client-quickstart-1.0-SNAPSHOT-runner ``` -Yes, it is now **25 MB** whereas it used to be **34 MB**. SSL comes with a 9 MB overhead in native executable size. +Yes, it is now **35 MB** whereas it used to be **46 MB**. SSL comes with a 11 MB overhead in native executable size. And there's more to it. @@ -161,71 +161,36 @@ And let's build the native executable again: [#the-truststore-path] == The TrustStore path -You haven't noticed anything but, while building the image, -Quarkus has automatically set `javax.net.ssl.trustStore` to point to the `cacerts` file bundled in the GraalVM distribution. -This file contains the root certificates. - -This is useful when running tests but, obviously, it is not portable as this path is hardcoded. +[WARNING] +==== +This behavior is new to GraalVM 19.3+. +==== -You can check that pretty easily: +When creating a native binary, GraalVM embraces the principle of "immutable security" for the root certificates. +This essentially means that the root certificates are fixed at image build time, based on the certificate configuration used at that build time +(which for Quarkus means when you perform a build having `quarkus.package.type=native` set). +This avoids shipping a `cacerts` file or requiring a system property be set in order to set up root +certificates that are provided by the OS where the binary runs. - * move your GraalVM directory to another place (let's call it ``) - * run the native executable `./target/rest-client-1.0-SNAPSHOT-runner` - * in a browser, go to `http://localhost:8080/country/name/greece` - * you will have an Internal Server Error - * in your terminal, you should have an exception: `java.security.InvalidAlgorithmParameterException: the trustAnchors parameter must be non-empty` - * hit `Ctrl+C` to stop the application +As a consequence, system properties such as `javax.net.ssl.trustStore` do not have an effect at +run time, so when the defaults need to be changed, these system properties must be provided at image build time. +The easiest way to do so is by setting `quarkus.native.additional-build-args`. For example: -To make it work, you need to manually set `javax.net.ssl.trustStore` to point to the new GraalVM home: ``` -./target/rest-client-1.0-SNAPSHOT-runner -Djavax.net.ssl.trustStore=/jre/lib/security/cacerts +quarkus.native.additional-build-args=-J-Djavax.net.ssl.trustStore=/tmp/mycerts,-J-Djavax.net.ssl.trustStorePassword=changeit ``` -Now, the application should work as expected: +will ensure that the certificates of `/tmp/mycerts` are baked into the native binary and used *in addition* to the default cacerts. - * in a browser, go to `http://localhost:8080/country/name/greece` - * you should see a JSON output with some information about Greece - * hit `Ctrl+C` to stop the application - -[TIP] +[IMPORTANT] ==== -The root certificates file of GraalVM might not be totally up to date. -If you have issues with some certificates, your best bet is to include the `cacerts` file of a regular JDK instead. -==== - -[WARNING] -==== -Don't forget to move your GraalVM directory back to where it was. +The file containing the custom TrustStore does *not* have to be present at runtime as its content has been baked into the native binary. ==== === Working with containers -When working with containers, the idea is to bundle the certificates in the container and to point your binary to them using the system property mentioned above. - -You can for example modify your `Dockerfile.native` as follows to copy the required files to your final image: - -[source, subs=attributes+] ----- -FROM quay.io/quarkus/ubi-quarkus-native-image:{graalvm-flavor} as nativebuilder -RUN mkdir -p /tmp/ssl \ - && cp /opt/graalvm/lib/security/cacerts /tmp/ssl/ - -FROM registry.access.redhat.com/ubi8/ubi-minimal -WORKDIR /work/ -COPY --from=nativebuilder /tmp/ssl/ /work/ -COPY target/*-runner /work/application - -# set up permissions for user `1001` -RUN chmod 775 /work /work/application \ - && chown -R 1001 /work \ - && chmod -R "g+rwX" /work \ - && chown -R 1001:root /work - -EXPOSE 8080 -USER 1001 - -CMD ["./application", "-Dquarkus.http.host=0.0.0.0", "-Djavax.net.ssl.trustStore=/work/cacerts"] ----- +No special action needs to be taken when running the native binary in a container. If the native binary was properly built with the custom TrustStore +as described in the previous section, it will work properly in container as well. == Conclusion