diff --git a/docs/src/main/asciidoc/native-and-ssl.adoc b/docs/src/main/asciidoc/native-and-ssl.adoc index 2a6d688205768..e1a7d0be7d63f 100644 --- a/docs/src/main/asciidoc/native-and-ssl.adoc +++ b/docs/src/main/asciidoc/native-and-ssl.adoc @@ -77,7 +77,8 @@ As SSL is de facto the standard nowadays, we decided to enable its support autom * the MongoDB extension (`quarkus-mongodb-client`), * the Neo4j extension (`quarkus-neo4j`), * the OAuth2 extension (`quarkus-elytron-security-oauth2`), - * the REST client extension (`quarkus-rest-client`). + * the REST client extension (`quarkus-rest-client`), + * the Reactive client for PostgreSQL extension (`quarkus-reactive-pg-client`). As long as you have one of those extensions in your project, the SSL support will be enabled by default. diff --git a/extensions/reactive-pg-client/deployment/src/main/java/io/quarkus/reactive/pg/client/deployment/ReactivePgClientProcessor.java b/extensions/reactive-pg-client/deployment/src/main/java/io/quarkus/reactive/pg/client/deployment/ReactivePgClientProcessor.java index 6f7c19af1e6a1..10aa83b23300b 100644 --- a/extensions/reactive-pg-client/deployment/src/main/java/io/quarkus/reactive/pg/client/deployment/ReactivePgClientProcessor.java +++ b/extensions/reactive-pg-client/deployment/src/main/java/io/quarkus/reactive/pg/client/deployment/ReactivePgClientProcessor.java @@ -10,6 +10,7 @@ import io.quarkus.deployment.annotations.BuildStep; import io.quarkus.deployment.annotations.ExecutionTime; import io.quarkus.deployment.annotations.Record; +import io.quarkus.deployment.builditem.ExtensionSslNativeSupportBuildItem; import io.quarkus.deployment.builditem.FeatureBuildItem; import io.quarkus.deployment.builditem.ServiceStartBuildItem; import io.quarkus.deployment.builditem.ShutdownContextBuildItem; @@ -43,6 +44,7 @@ ServiceStartBuildItem build(BuildProducer feature, BuildProduc PgPoolRecorder recorder, VertxBuildItem vertx, BeanContainerBuildItem beanContainer, ShutdownContextBuildItem shutdown, + BuildProducer sslNativeSupport, DataSourcesBuildTimeConfig dataSourcesBuildTimeConfig, DataSourcesRuntimeConfig dataSourcesRuntimeConfig, DataSourceReactiveBuildTimeConfig dataSourceReactiveBuildTimeConfig, DataSourceReactiveRuntimeConfig dataSourceReactiveRuntimeConfig, @@ -68,6 +70,9 @@ ServiceStartBuildItem build(BuildProducer feature, BuildProduc legacyDataSourcesRuntimeConfig, legacyDataSourceReactivePostgreSQLConfig, isLegacy, shutdown))); + // Enable SSL support by default + sslNativeSupport.produce(new ExtensionSslNativeSupportBuildItem(FeatureBuildItem.REACTIVE_PG_CLIENT)); + return serviceStart; } diff --git a/extensions/reactive-pg-client/runtime/src/main/java/io/quarkus/reactive/pg/client/runtime/DataSourceReactivePostgreSQLConfig.java b/extensions/reactive-pg-client/runtime/src/main/java/io/quarkus/reactive/pg/client/runtime/DataSourceReactivePostgreSQLConfig.java index 0e76d19347eed..6d97b913d8474 100644 --- a/extensions/reactive-pg-client/runtime/src/main/java/io/quarkus/reactive/pg/client/runtime/DataSourceReactivePostgreSQLConfig.java +++ b/extensions/reactive-pg-client/runtime/src/main/java/io/quarkus/reactive/pg/client/runtime/DataSourceReactivePostgreSQLConfig.java @@ -6,6 +6,11 @@ import io.quarkus.runtime.annotations.ConfigItem; import io.quarkus.runtime.annotations.ConfigPhase; import io.quarkus.runtime.annotations.ConfigRoot; +import io.quarkus.vertx.core.runtime.config.JksConfiguration; +import io.quarkus.vertx.core.runtime.config.PemKeyCertConfiguration; +import io.quarkus.vertx.core.runtime.config.PemTrustCertConfiguration; +import io.quarkus.vertx.core.runtime.config.PfxConfiguration; +import io.vertx.pgclient.SslMode; @ConfigRoot(name = "datasource.reactive.postgresql", phase = ConfigPhase.RUN_TIME) public class DataSourceReactivePostgreSQLConfig { @@ -21,4 +26,49 @@ public class DataSourceReactivePostgreSQLConfig { */ @ConfigItem public OptionalInt pipeliningLimit; + + /** + * SSL operating mode of the client. + *

+ * See Protection Provided in + * Different Modes. + */ + @ConfigItem + public Optional sslMode; + + /** + * Trust configuration in the PEM format. + */ + @ConfigItem + public PemTrustCertConfiguration trustPem; + + /** + * Trust configuration in the JKS format. + */ + @ConfigItem + public JksConfiguration trustJks; + + /** + * Trust configuration in the PFX format. + */ + @ConfigItem + public PfxConfiguration trustPfx; + + /** + * Key/cert configuration in the PEM format. + */ + @ConfigItem + public PemKeyCertConfiguration keyCertPem; + + /** + * Key/cert configuration in the JKS format. + */ + @ConfigItem + public JksConfiguration keyCertJks; + + /** + * Key/cert configuration in the PFX format. + */ + @ConfigItem + public PfxConfiguration keyCertPfx; } diff --git a/extensions/reactive-pg-client/runtime/src/main/java/io/quarkus/reactive/pg/client/runtime/PgPoolRecorder.java b/extensions/reactive-pg-client/runtime/src/main/java/io/quarkus/reactive/pg/client/runtime/PgPoolRecorder.java index c8cac7fa275bc..6100f4d363045 100644 --- a/extensions/reactive-pg-client/runtime/src/main/java/io/quarkus/reactive/pg/client/runtime/PgPoolRecorder.java +++ b/extensions/reactive-pg-client/runtime/src/main/java/io/quarkus/reactive/pg/client/runtime/PgPoolRecorder.java @@ -2,6 +2,7 @@ import static io.quarkus.credentials.CredentialsProvider.PASSWORD_PROPERTY_NAME; import static io.quarkus.credentials.CredentialsProvider.USER_PROPERTY_NAME; +import static io.quarkus.vertx.core.runtime.SSLConfigHelper.*; import java.util.Map; @@ -121,6 +122,18 @@ private PgConnectOptions toPgConnectOptions(DataSourceRuntimeConfig dataSourceRu pgConnectOptions.setPipeliningLimit(dataSourceReactivePostgreSQLConfig.pipeliningLimit.getAsInt()); } + if (dataSourceReactivePostgreSQLConfig.sslMode.isPresent()) { + pgConnectOptions.setSslMode(dataSourceReactivePostgreSQLConfig.sslMode.get()); + } + + configurePemTrustOptions(pgConnectOptions, dataSourceReactivePostgreSQLConfig.trustPem); + configureJksTrustOptions(pgConnectOptions, dataSourceReactivePostgreSQLConfig.trustJks); + configurePfxTrustOptions(pgConnectOptions, dataSourceReactivePostgreSQLConfig.trustPfx); + + configurePemKeyCertOptions(pgConnectOptions, dataSourceReactivePostgreSQLConfig.keyCertPem); + configureJksKeyCertOptions(pgConnectOptions, dataSourceReactivePostgreSQLConfig.keyCertJks); + configurePfxKeyCertOptions(pgConnectOptions, dataSourceReactivePostgreSQLConfig.keyCertPfx); + return pgConnectOptions; } diff --git a/extensions/vertx-core/runtime/src/main/java/io/quarkus/vertx/core/runtime/SSLConfigHelper.java b/extensions/vertx-core/runtime/src/main/java/io/quarkus/vertx/core/runtime/SSLConfigHelper.java new file mode 100644 index 0000000000000..15af8e1bcfe56 --- /dev/null +++ b/extensions/vertx-core/runtime/src/main/java/io/quarkus/vertx/core/runtime/SSLConfigHelper.java @@ -0,0 +1,119 @@ +package io.quarkus.vertx.core.runtime; + +import java.util.regex.Pattern; + +import io.quarkus.vertx.core.runtime.config.JksConfiguration; +import io.quarkus.vertx.core.runtime.config.PemKeyCertConfiguration; +import io.quarkus.vertx.core.runtime.config.PemTrustCertConfiguration; +import io.quarkus.vertx.core.runtime.config.PfxConfiguration; +import io.vertx.core.net.*; + +public class SSLConfigHelper { + + private static final Pattern COMMA_PATTERN = Pattern.compile(","); + + public static void configurePemTrustOptions(TCPSSLOptions options, PemTrustCertConfiguration configuration) { + if (configuration.enabled) { + ensureTrustOptionsNotSet(options); + options.setTrustOptions(toPemTrustOptions(configuration)); + } + } + + private static PemTrustOptions toPemTrustOptions(PemTrustCertConfiguration configuration) { + PemTrustOptions pemTrustOptions = new PemTrustOptions(); + if (configuration.certs.isPresent()) { + for (String cert : COMMA_PATTERN.split(configuration.certs.get())) { + pemTrustOptions.addCertPath(cert.trim()); + } + } + return pemTrustOptions; + } + + public static void configureJksTrustOptions(TCPSSLOptions options, JksConfiguration configuration) { + if (configuration.enabled) { + ensureTrustOptionsNotSet(options); + options.setTrustOptions(toJksOptions(configuration)); + } + } + + private static JksOptions toJksOptions(JksConfiguration configuration) { + JksOptions jksOptions = new JksOptions(); + if (configuration.path.isPresent()) { + jksOptions.setPath(configuration.path.get()); + } + if (configuration.password.isPresent()) { + jksOptions.setPassword(configuration.password.get()); + } + return jksOptions; + } + + public static void configurePfxTrustOptions(TCPSSLOptions options, PfxConfiguration configuration) { + if (configuration.enabled) { + ensureTrustOptionsNotSet(options); + options.setTrustOptions(toPfxOptions(configuration)); + } + } + + private static PfxOptions toPfxOptions(PfxConfiguration configuration) { + PfxOptions pfxOptions = new PfxOptions(); + if (configuration.path.isPresent()) { + pfxOptions.setPath(configuration.path.get()); + } + if (configuration.password.isPresent()) { + pfxOptions.setPassword(configuration.password.get()); + } + return pfxOptions; + } + + private static void ensureTrustOptionsNotSet(TCPSSLOptions options) { + if (options.getTrustOptions() != null) { + throw new IllegalArgumentException("Trust options have already been set"); + } + } + + public static void configurePemKeyCertOptions(TCPSSLOptions options, PemKeyCertConfiguration configuration) { + if (configuration.enabled) { + ensureKeyCertOptionsNotSet(options); + options.setKeyCertOptions(toPemKeyCertOptions(configuration)); + } + } + + private static KeyCertOptions toPemKeyCertOptions(PemKeyCertConfiguration configuration) { + PemKeyCertOptions pemKeyCertOptions = new PemKeyCertOptions(); + if (configuration.certs.isPresent()) { + for (String cert : COMMA_PATTERN.split(configuration.certs.get())) { + pemKeyCertOptions.addCertPath(cert.trim()); + } + } + if (configuration.keys.isPresent()) { + for (String cert : COMMA_PATTERN.split(configuration.keys.get())) { + pemKeyCertOptions.addKeyPath(cert.trim()); + } + } + return pemKeyCertOptions; + } + + public static void configureJksKeyCertOptions(TCPSSLOptions options, JksConfiguration configuration) { + if (configuration.enabled) { + ensureKeyCertOptionsNotSet(options); + options.setKeyCertOptions(toJksOptions(configuration)); + } + } + + public static void configurePfxKeyCertOptions(TCPSSLOptions options, PfxConfiguration configuration) { + if (configuration.enabled) { + ensureKeyCertOptionsNotSet(options); + options.setKeyCertOptions(toPfxOptions(configuration)); + } + } + + private static void ensureKeyCertOptionsNotSet(TCPSSLOptions options) { + if (options.getKeyCertOptions() != null) { + throw new IllegalArgumentException("Key cert options have already been set"); + } + } + + private SSLConfigHelper() { + // Utility + } +} diff --git a/extensions/vertx-core/runtime/src/main/java/io/quarkus/vertx/core/runtime/config/JksConfiguration.java b/extensions/vertx-core/runtime/src/main/java/io/quarkus/vertx/core/runtime/config/JksConfiguration.java index 1449dcedcb5f0..77ec9e0d44919 100644 --- a/extensions/vertx-core/runtime/src/main/java/io/quarkus/vertx/core/runtime/config/JksConfiguration.java +++ b/extensions/vertx-core/runtime/src/main/java/io/quarkus/vertx/core/runtime/config/JksConfiguration.java @@ -8,6 +8,12 @@ @ConfigGroup public class JksConfiguration { + /** + * JKS config is disabled by default. + */ + @ConfigItem(name = ConfigItem.PARENT, defaultValue = "false") + public boolean enabled; + /** * Path of the key file (JKS format). */ diff --git a/extensions/vertx-core/runtime/src/main/java/io/quarkus/vertx/core/runtime/config/PemKeyCertConfiguration.java b/extensions/vertx-core/runtime/src/main/java/io/quarkus/vertx/core/runtime/config/PemKeyCertConfiguration.java index a96087ac891db..dc471b7b429d9 100644 --- a/extensions/vertx-core/runtime/src/main/java/io/quarkus/vertx/core/runtime/config/PemKeyCertConfiguration.java +++ b/extensions/vertx-core/runtime/src/main/java/io/quarkus/vertx/core/runtime/config/PemKeyCertConfiguration.java @@ -8,6 +8,12 @@ @ConfigGroup public class PemKeyCertConfiguration { + /** + * PEM Key/cert config is disabled by default. + */ + @ConfigItem(name = ConfigItem.PARENT, defaultValue = "false") + public boolean enabled; + /** * Comma-separated list of the path to the key files (Pem format). */ diff --git a/extensions/vertx-core/runtime/src/main/java/io/quarkus/vertx/core/runtime/config/PemTrustCertConfiguration.java b/extensions/vertx-core/runtime/src/main/java/io/quarkus/vertx/core/runtime/config/PemTrustCertConfiguration.java index 64e415fb5b6ef..2e5991dcc8d5d 100644 --- a/extensions/vertx-core/runtime/src/main/java/io/quarkus/vertx/core/runtime/config/PemTrustCertConfiguration.java +++ b/extensions/vertx-core/runtime/src/main/java/io/quarkus/vertx/core/runtime/config/PemTrustCertConfiguration.java @@ -8,6 +8,12 @@ @ConfigGroup public class PemTrustCertConfiguration { + /** + * PEM Trust config is disabled by default. + */ + @ConfigItem(name = ConfigItem.PARENT, defaultValue = "false") + public boolean enabled; + /** * Comma-separated list of the trust certificate files (Pem format). */ diff --git a/extensions/vertx-core/runtime/src/main/java/io/quarkus/vertx/core/runtime/config/PfxConfiguration.java b/extensions/vertx-core/runtime/src/main/java/io/quarkus/vertx/core/runtime/config/PfxConfiguration.java index e1dcf787186c7..4e4256805905b 100644 --- a/extensions/vertx-core/runtime/src/main/java/io/quarkus/vertx/core/runtime/config/PfxConfiguration.java +++ b/extensions/vertx-core/runtime/src/main/java/io/quarkus/vertx/core/runtime/config/PfxConfiguration.java @@ -9,7 +9,13 @@ public class PfxConfiguration { /** - * Path to the key file (PFX format) + * PFX config is disabled by default. + */ + @ConfigItem(name = ConfigItem.PARENT, defaultValue = "false") + public boolean enabled; + + /** + * Path to the key file (PFX format). */ @ConfigItem public Optional path;