Skip to content

Commit

Permalink
Merge pull request #9469 from tsegismont/reactive-pg-ssl
Browse files Browse the repository at this point in the history
SSL support for Reactive SQL Clients
  • Loading branch information
FroMage authored Jun 19, 2020
2 parents 9979bd9 + e9eadcc commit 88836ef
Show file tree
Hide file tree
Showing 15 changed files with 296 additions and 61 deletions.
5 changes: 3 additions & 2 deletions docs/src/main/asciidoc/native-and-ssl.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,9 @@ 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`).
* the Reactive client for MySQL extension (`quarkus-reactive-mysql-client`).

As long as you have one of those extensions in your project, the SSL support will be enabled by default.

Expand Down
4 changes: 4 additions & 0 deletions extensions/reactive-datasource/runtime/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@
<groupId>io.vertx</groupId>
<artifactId>vertx-sql-client</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-vertx-core</artifactId>
</dependency>
</dependencies>

<build>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@
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;

/**
* For now, the reactive extensions only support a default datasource.
Expand All @@ -24,4 +28,58 @@ public class DataSourceReactiveRuntimeConfig {
*/
@ConfigItem
public OptionalInt maxSize;

/**
* Whether all server certificates should be trusted.
*/
@ConfigItem(defaultValue = "false")
public boolean trustAll;

/**
* Trust configuration in the PEM format.
* <p>
* When enabled, {@code #trust-certificate-jks} and {@code #trust-certificate-pfx} must be disabled.
*/
@ConfigItem
public PemTrustCertConfiguration trustCertificatePem;

/**
* Trust configuration in the JKS format.
* <p>
* When enabled, {@code #trust-certificate-pem} and {@code #trust-certificate-pfx} must be disabled.
*/
@ConfigItem
public JksConfiguration trustCertificateJks;

/**
* Trust configuration in the PFX format.
* <p>
* When enabled, {@code #trust-certificate-jks} and {@code #trust-certificate-pem} must be disabled.
*/
@ConfigItem
public PfxConfiguration trustCertificatePfx;

/**
* Key/cert configuration in the PEM format.
* <p>
* When enabled, {@code key-certificate-jks} and {@code #key-certificate-pfx} must be disabled.
*/
@ConfigItem
public PemKeyCertConfiguration keyCertificatePem;

/**
* Key/cert configuration in the JKS format.
* <p>
* When enabled, {@code #key-certificate-pem} and {@code #key-certificate-pfx} must be disabled.
*/
@ConfigItem
public JksConfiguration keyCertificateJks;

/**
* Key/cert configuration in the PFX format.
* <p>
* When enabled, {@code key-certificate-jks} and {@code #key-certificate-pem} must be disabled.
*/
@ConfigItem
public PfxConfiguration keyCertificatePfx;
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,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;
Expand Down Expand Up @@ -42,6 +43,7 @@ ServiceStartBuildItem build(BuildProducer<FeatureBuildItem> feature,
MySQLPoolRecorder recorder,
VertxBuildItem vertx,
BeanContainerBuildItem beanContainer, ShutdownContextBuildItem shutdown,
BuildProducer<ExtensionSslNativeSupportBuildItem> sslNativeSupport,
DataSourcesBuildTimeConfig dataSourcesBuildTimeConfig, DataSourcesRuntimeConfig dataSourcesRuntimeConfig,
DataSourceReactiveBuildTimeConfig dataSourceReactiveBuildTimeConfig,
DataSourceReactiveRuntimeConfig dataSourceReactiveRuntimeConfig,
Expand Down Expand Up @@ -72,6 +74,9 @@ ServiceStartBuildItem build(BuildProducer<FeatureBuildItem> feature,
boolean isDefault = true; // assume always the default pool for now
vertxPool.produce(new VertxPoolBuildItem(mySqlPool, DatabaseKind.MYSQL, isDefault));

// Enable SSL support by default
sslNativeSupport.produce(new ExtensionSslNativeSupportBuildItem(Feature.REACTIVE_MYSQL_CLIENT));

return serviceStart;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import io.quarkus.runtime.annotations.ConfigItem;
import io.quarkus.runtime.annotations.ConfigPhase;
import io.quarkus.runtime.annotations.ConfigRoot;
import io.vertx.mysqlclient.SslMode;

@ConfigRoot(name = "datasource.reactive.mysql", phase = ConfigPhase.RUN_TIME)
public class DataSourceReactiveMySQLConfig {
Expand All @@ -26,4 +27,13 @@ public class DataSourceReactiveMySQLConfig {
*/
@ConfigItem
public Optional<String> collation;

/**
* Desired security state of the connection to the server.
* <p>
* See <a href="https://dev.mysql.com/doc/refman/8.0/en/connection-options.html#option_general_ssl-mode">MySQL Reference
* Manual</a>.
*/
@ConfigItem(defaultValueDocumentation = "disabled")
public Optional<SslMode> sslMode;
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@

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.configureJksKeyCertOptions;
import static io.quarkus.vertx.core.runtime.SSLConfigHelper.configureJksTrustOptions;
import static io.quarkus.vertx.core.runtime.SSLConfigHelper.configurePemKeyCertOptions;
import static io.quarkus.vertx.core.runtime.SSLConfigHelper.configurePemTrustOptions;
import static io.quarkus.vertx.core.runtime.SSLConfigHelper.configurePfxKeyCertOptions;
import static io.quarkus.vertx.core.runtime.SSLConfigHelper.configurePfxTrustOptions;

import java.util.Map;

Expand Down Expand Up @@ -122,6 +128,20 @@ private MySQLConnectOptions toMySQLConnectOptions(DataSourceRuntimeConfig dataSo
mysqlConnectOptions.setCollation(dataSourceReactiveMySQLConfig.collation.get());
}

if (dataSourceReactiveMySQLConfig.sslMode.isPresent()) {
mysqlConnectOptions.setSslMode(dataSourceReactiveMySQLConfig.sslMode.get());
}

mysqlConnectOptions.setTrustAll(dataSourceReactiveRuntimeConfig.trustAll);

configurePemTrustOptions(mysqlConnectOptions, dataSourceReactiveRuntimeConfig.trustCertificatePem);
configureJksTrustOptions(mysqlConnectOptions, dataSourceReactiveRuntimeConfig.trustCertificateJks);
configurePfxTrustOptions(mysqlConnectOptions, dataSourceReactiveRuntimeConfig.trustCertificatePfx);

configurePemKeyCertOptions(mysqlConnectOptions, dataSourceReactiveRuntimeConfig.keyCertificatePem);
configureJksKeyCertOptions(mysqlConnectOptions, dataSourceReactiveRuntimeConfig.keyCertificateJks);
configurePfxKeyCertOptions(mysqlConnectOptions, dataSourceReactiveRuntimeConfig.keyCertificatePfx);

return mysqlConnectOptions;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,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;
Expand Down Expand Up @@ -50,6 +51,7 @@ ServiceStartBuildItem build(BuildProducer<FeatureBuildItem> feature,
VertxBuildItem vertx,
BeanContainerBuildItem beanContainer,
ShutdownContextBuildItem shutdown,
BuildProducer<ExtensionSslNativeSupportBuildItem> sslNativeSupport,
DataSourcesBuildTimeConfig dataSourcesBuildTimeConfig, DataSourcesRuntimeConfig dataSourcesRuntimeConfig,
DataSourceReactiveBuildTimeConfig dataSourceReactiveBuildTimeConfig,
DataSourceReactiveRuntimeConfig dataSourceReactiveRuntimeConfig,
Expand Down Expand Up @@ -79,6 +81,9 @@ ServiceStartBuildItem build(BuildProducer<FeatureBuildItem> feature,
boolean isDefault = true; // assume always the default pool for now
vertxPool.produce(new VertxPoolBuildItem(pool, DatabaseKind.POSTGRESQL, isDefault));

// Enable SSL support by default
sslNativeSupport.produce(new ExtensionSslNativeSupportBuildItem(Feature.REACTIVE_PG_CLIENT));

return serviceStart;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import io.quarkus.runtime.annotations.ConfigItem;
import io.quarkus.runtime.annotations.ConfigPhase;
import io.quarkus.runtime.annotations.ConfigRoot;
import io.vertx.pgclient.SslMode;

@ConfigRoot(name = "datasource.reactive.postgresql", phase = ConfigPhase.RUN_TIME)
public class DataSourceReactivePostgreSQLConfig {
Expand All @@ -21,4 +22,13 @@ public class DataSourceReactivePostgreSQLConfig {
*/
@ConfigItem
public OptionalInt pipeliningLimit;

/**
* SSL operating mode of the client.
* <p>
* See <a href="https://www.postgresql.org/docs/current/libpq-ssl.html#LIBPQ-SSL-PROTECTION">Protection Provided in
* Different Modes</a>.
*/
@ConfigItem(defaultValueDocumentation = "disable")
public Optional<SslMode> sslMode;
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@

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.configureJksKeyCertOptions;
import static io.quarkus.vertx.core.runtime.SSLConfigHelper.configureJksTrustOptions;
import static io.quarkus.vertx.core.runtime.SSLConfigHelper.configurePemKeyCertOptions;
import static io.quarkus.vertx.core.runtime.SSLConfigHelper.configurePemTrustOptions;
import static io.quarkus.vertx.core.runtime.SSLConfigHelper.configurePfxKeyCertOptions;
import static io.quarkus.vertx.core.runtime.SSLConfigHelper.configurePfxTrustOptions;

import java.util.Map;

Expand Down Expand Up @@ -121,6 +127,20 @@ private PgConnectOptions toPgConnectOptions(DataSourceRuntimeConfig dataSourceRu
pgConnectOptions.setPipeliningLimit(dataSourceReactivePostgreSQLConfig.pipeliningLimit.getAsInt());
}

if (dataSourceReactivePostgreSQLConfig.sslMode.isPresent()) {
pgConnectOptions.setSslMode(dataSourceReactivePostgreSQLConfig.sslMode.get());
}

pgConnectOptions.setTrustAll(dataSourceReactiveRuntimeConfig.trustAll);

configurePemTrustOptions(pgConnectOptions, dataSourceReactiveRuntimeConfig.trustCertificatePem);
configureJksTrustOptions(pgConnectOptions, dataSourceReactiveRuntimeConfig.trustCertificateJks);
configurePfxTrustOptions(pgConnectOptions, dataSourceReactiveRuntimeConfig.trustCertificatePfx);

configurePemKeyCertOptions(pgConnectOptions, dataSourceReactiveRuntimeConfig.keyCertificatePem);
configureJksKeyCertOptions(pgConnectOptions, dataSourceReactiveRuntimeConfig.keyCertificateJks);
configurePfxKeyCertOptions(pgConnectOptions, dataSourceReactiveRuntimeConfig.keyCertificatePfx);

return pgConnectOptions;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
package io.quarkus.vertx.core.runtime;

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.JksOptions;
import io.vertx.core.net.KeyCertOptions;
import io.vertx.core.net.PemKeyCertOptions;
import io.vertx.core.net.PemTrustOptions;
import io.vertx.core.net.PfxOptions;
import io.vertx.core.net.TCPSSLOptions;

public class SSLConfigHelper {

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 : configuration.certs.get()) {
pemTrustOptions.addCertPath(cert);
}
}
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 : configuration.certs.get()) {
pemKeyCertOptions.addCertPath(cert);
}
}
if (configuration.keys.isPresent()) {
for (String cert : configuration.keys.get()) {
pemKeyCertOptions.addKeyPath(cert);
}
}
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
}
}
Loading

0 comments on commit 88836ef

Please sign in to comment.