Skip to content

Commit

Permalink
feat: extend NeonBeeOptions with trust configuration
Browse files Browse the repository at this point in the history
With this commit it is possible to configure a keystore and a truststore to enable event bus encryption.

The keystore and truststore must be in the PKCS #12 format.
  • Loading branch information
pk-work committed Jul 25, 2023
1 parent fccf4b5 commit 69f37c2
Show file tree
Hide file tree
Showing 28 changed files with 932 additions and 27 deletions.
43 changes: 25 additions & 18 deletions docs/usage/neonbee.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,29 +6,36 @@ NeonBee comes with a command line interface for starting and configuring NeonBee
NeonBee options can also be provided via environment variables, which always takes precedence. See the following
table for a full list of configuration options.

| CLI parameter | Environment Variable | Description |
| ---------------------------------------- | -------------------------------- | ------------------------------------------------------------------------------------- |
| `--event-loop-pool-size` or `-elps` | `NEONBEE_EVENT_LOOP_POOL_SIZE` | Set the number of threads `(> 0)` for the event loop pool. |
| `--worker-pool-size` or `-wps` | `NEONBEE_WORKER_POOL_SIZE` | Set the number of threads for the worker pool. Default is `20`. |
| `--instance-name` or `-name` | `NEONBEE_INSTANCE_NAME` | Set the NeonBee instance name. Must have at least one character. |
| `--working-directory` or `-cwd` | `NEONBEE_WORKING_DIRECTORY` | Set the working directory. Default is `"working_dir/".` |
| `--ignore-classpath` or `-no-cp` | `NEONBEE_IGNORE_CLASSPATH` | Ignore verticle / models on the class path. Default is `false`. |
| `--disable-job-scheduling` or `-no-jobs` | `NEONBEE_DISABLE_JOB_SCHEDULING` | Do not schedule any job verticle. Default is `false`. |
| `--do-not-watch-files` or `-no-watchers` | `NEONBEE_DO_NOT_WATCH_FILES` | Do not watch files. Default is `false`. |
| `--cluster-port` or `-clp` | `NEONBEE_CLUSTER_PORT` | Set the port of the event bus. Default is a random port. |
| `--clustered` or `-cl` | `NEONBEE_CLUSTERED` | Start in cluster mode. Default is `false`. |
| `--cluster-config` or `-cc` | `NEONBEE_CLUSTER_CONFIG` | Set the cluster configuration file path. |
| `--cluster-manager` or `-cm` | `NEONBEE_CLUSTER_MANAGER` | Set the cluster manager factory to create the cluster manager. Default is `hazelcast` |
| `--active-profiles` or `-ap` | `NEONBEE_ACTIVE_PROFILES` | Set the active profiles. If not set, the default value is `ALL`. |
| `--module-jar-paths` or `-mjp` | `NEONBEE_MODULE_JARS` | Set the module JAR paths to be loaded during startup. |
| `--metrics-registry-name` or `-mrn` | `NEONBEE_METRICS_REGISTRY_NAME` | Set the metrics registry by name. See [metrics](./metrics.md) for details. |
| `--server-port` or `-port` | `NEONBEE_SERVER_PORT` | Set the HTTP(S) port of the server verticle. |
| `--server-host` or `-sh` | `NEONBEE_SERVER_HOST` | Set the host of the server verticle |
| CLI parameter | Environment Variable | Description |
|----------------------------------------------|---------------------------------------|---------------------------------------------------------------------------------------|
| `--event-loop-pool-size` or `-elps` | `NEONBEE_EVENT_LOOP_POOL_SIZE` | Set the number of threads `(> 0)` for the event loop pool. |
| `--worker-pool-size` or `-wps` | `NEONBEE_WORKER_POOL_SIZE` | Set the number of threads for the worker pool. Default is `20`. |
| `--instance-name` or `-name` | `NEONBEE_INSTANCE_NAME` | Set the NeonBee instance name. Must have at least one character. |
| `--working-directory` or `-cwd` | `NEONBEE_WORKING_DIRECTORY` | Set the working directory. Default is `"working_dir/".` |
| `--ignore-classpath` or `-no-cp` | `NEONBEE_IGNORE_CLASSPATH` | Ignore verticle / models on the class path. Default is `false`. |
| `--disable-job-scheduling` or `-no-jobs` | `NEONBEE_DISABLE_JOB_SCHEDULING` | Do not schedule any job verticle. Default is `false`. |
| `--do-not-watch-files` or `-no-watchers` | `NEONBEE_DO_NOT_WATCH_FILES` | Do not watch files. Default is `false`. |
| `--cluster-port` or `-clp` | `NEONBEE_CLUSTER_PORT` | Set the port of the event bus. Default is a random port. |
| `--clustered` or `-cl` | `NEONBEE_CLUSTERED` | Start in cluster mode. Default is `false`. |
| `--cluster-config` or `-cc` | `NEONBEE_CLUSTER_CONFIG` | Set the cluster configuration file path. |
| `--cluster-truststore` or `-cts` | `NEONBEE_CLUSTER_TRUSTSTORE` | Set the cluster truststore file path (must be PKCS #12). |
| `--cluster-truststore-password` or `-cts-pw` | `NEONBEE_CLUSTER_TRUSTSTORE_PASSWORD` | Set the cluster truststore password. |
| `--cluster-keystore` or `-cks` | `NEONBEE_CLUSTER_KEYSTORE` | Set the cluster keystore file path (must be PKCS #12). |
| `--cluster-keystore-password` or `-cks-pw` | `NEONBEE_CLUSTER_KEYSTORE_PASSWORD` | Set the cluster keystore password. |
| `--cluster-manager` or `-cm` | `NEONBEE_CLUSTER_MANAGER` | Set the cluster manager factory to create the cluster manager. Default is `hazelcast` |
| `--active-profiles` or `-ap` | `NEONBEE_ACTIVE_PROFILES` | Set the active profiles. If not set, the default value is `ALL`. |
| `--module-jar-paths` or `-mjp` | `NEONBEE_MODULE_JARS` | Set the module JAR paths to be loaded during startup. |
| `--metrics-registry-name` or `-mrn` | `NEONBEE_METRICS_REGISTRY_NAME` | Set the metrics registry by name. See [metrics](./metrics.md) for details. |
| `--server-port` or `-port` | `NEONBEE_SERVER_PORT` | Set the HTTP(S) port of the server verticle. |
| `--server-host` or `-sh` | `NEONBEE_SERVER_HOST` | Set the host of the server verticle |

**Note:** For the `--active-profiles` option, only the [available profiles](#neonbee-profiles) are allowed to set.
Multiple profiles can be set by separating them with commas, e.g. `--active-profiles INCUBATOR,STABLE,CORE`. If this
option is not set, the default value is `ALL`.

**Note:** Truststore option can only be set in combination with keystore option and vice versa. Setting the key- and
truststore options automatically activates the eventbus encryption.

The default for the event loop pool size is `2 x number of available processors` on the host. This is the same as
the Vert.x default.

Expand Down
35 changes: 31 additions & 4 deletions src/main/java/io/neonbee/NeonBee.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import static io.vertx.core.Future.all;
import static io.vertx.core.Future.failedFuture;
import static io.vertx.core.Future.succeededFuture;
import static io.vertx.core.http.ClientAuth.REQUIRED;

import java.io.IOException;
import java.nio.file.Files;
Expand Down Expand Up @@ -92,17 +93,19 @@
import io.vertx.core.Verticle;
import io.vertx.core.Vertx;
import io.vertx.core.VertxOptions;
import io.vertx.core.eventbus.EventBusOptions;
import io.vertx.core.eventbus.MessageCodec;
import io.vertx.core.impl.ConcurrentHashSet;
import io.vertx.core.json.Json;
import io.vertx.core.json.JsonObject;
import io.vertx.core.net.PfxOptions;
import io.vertx.core.shareddata.AsyncMap;
import io.vertx.core.shareddata.LocalMap;
import io.vertx.core.spi.cluster.ClusterManager;
import io.vertx.core.spi.cluster.NodeListener;
import io.vertx.micrometer.MicrometerMetricsOptions;

@SuppressWarnings({ "PMD.CouplingBetweenObjects", "PMD.GodClass" })
@SuppressWarnings({ "PMD.CouplingBetweenObjects", "PMD.GodClass", "PMD.ExcessiveImports" })
public class NeonBee {
/* // @formatter:off
*
Expand Down Expand Up @@ -314,9 +317,33 @@ static Future<Vertx> newVertx(VertxOptions vertxOptions, NeonBeeOptions options)
vertxOptions.getEventBusOptions().setPort(options.getClusterPort());
Optional.ofNullable(getHostIp()).filter(Predicate.not(String::isEmpty))
.ifPresent(currentIp -> vertxOptions.getEventBusOptions().setHost(currentIp));
return Vertx.clusteredVertx(vertxOptions).onFailure(throwable -> {
LOGGER.error("Failed to start clustered Vert.x", throwable); // NOPMD slf4j
});

return applyEncryptionOptions(options, vertxOptions.getEventBusOptions())
.compose(v -> Vertx.clusteredVertx(vertxOptions)).onFailure(throwable -> {
LOGGER.error("Failed to start clustered Vert.x", throwable); // NOPMD slf4j
});
}

static Future<Void> applyEncryptionOptions(NeonBeeOptions neonBeeOptions, EventBusOptions ebo) {
if (neonBeeOptions.getClusterKeystore() != null && neonBeeOptions.getClusterTruststore() != null) {
PfxOptions keystoreOpts = new PfxOptions().setPath(neonBeeOptions.getClusterKeystore().toString());
Optional.ofNullable(neonBeeOptions.getClusterKeystorePassword()).ifPresent(keystoreOpts::setPassword);
Optional.ofNullable(neonBeeOptions.getClusterKeystorePassword()).ifPresent(keystoreOpts::setAliasPassword);

PfxOptions truststoreOpts = new PfxOptions().setPath(neonBeeOptions.getClusterTruststore().toString());
Optional.ofNullable(neonBeeOptions.getClusterTruststorePassword()).ifPresent(truststoreOpts::setPassword);
Optional.ofNullable(neonBeeOptions.getClusterTruststorePassword())
.ifPresent(truststoreOpts::setAliasPassword);

ebo.setSsl(true).setClientAuth(REQUIRED).setPfxKeyCertOptions(keystoreOpts)
.setPfxTrustOptions(truststoreOpts);
return succeededFuture();
} else if (neonBeeOptions.getClusterKeystore() == null && neonBeeOptions.getClusterTruststore() == null) {
return succeededFuture();
} else {
return failedFuture(new IllegalArgumentException(
"Failed to start NeonBee: Truststore options require keystore options and vice versa."));
}
}

@VisibleForTesting
Expand Down
120 changes: 118 additions & 2 deletions src/main/java/io/neonbee/NeonBeeOptions.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import static io.neonbee.cluster.ClusterManagerFactory.HAZELCAST_FACTORY;
import static io.neonbee.cluster.ClusterManagerFactory.INFINISPAN_FACTORY;
import static io.neonbee.internal.helper.StringHelper.EMPTY;
import static io.vertx.core.eventbus.EventBusOptions.DEFAULT_CLUSTER_PORT;
import static java.util.Objects.requireNonNull;

import java.nio.file.Path;
Expand Down Expand Up @@ -32,7 +33,6 @@
import io.vertx.core.cli.annotations.Option;
import io.vertx.core.cli.annotations.Summary;
import io.vertx.core.cli.converters.Converter;
import io.vertx.core.eventbus.EventBusOptions;
import io.vertx.micrometer.MicrometerMetricsOptions;

@SuppressWarnings("PMD.ExcessivePublicCount")
Expand Down Expand Up @@ -147,6 +147,34 @@ default Path getLogDirectory() {
*/
String getClusterConfig();

/**
* Gets the cluster truststore file path.
*
* @return cluster truststore file path.
*/
Path getClusterTruststore();

/**
* Gets the cluster truststore password.
*
* @return cluster truststore password.
*/
String getClusterTruststorePassword();

/**
* Gets the cluster keystore file path.
*
* @return cluster keystore file path.
*/
Path getClusterKeystore();

/**
* Gets the cluster keystore password.
*
* @return cluster keystore password.
*/
String getClusterKeystorePassword();

/**
* Gets the factory to create the chosen cluster manager.
*
Expand Down Expand Up @@ -200,12 +228,20 @@ class Mutable implements NeonBeeOptions {

private int workerPoolSize = VertxOptions.DEFAULT_WORKER_POOL_SIZE;

private int clusterPort = EventBusOptions.DEFAULT_CLUSTER_PORT;
private int clusterPort = DEFAULT_CLUSTER_PORT;

private boolean clustered;

private String clusterConfig;

private Path clusterTruststore;

private Path clusterKeystore;

private String clusterTruststorePassword;

private String clusterKeystorePassword;

private ClusterManagerFactory clusterManagerFactory = HAZELCAST_FACTORY;

private String instanceName;
Expand Down Expand Up @@ -432,6 +468,86 @@ public Mutable setClusterConfig(String clusterConfig) {
return this;
}

@Override
public Path getClusterTruststore() {
return clusterTruststore;
}

/**
* Sets the truststore (must be PKCS #12) to encrypt communication in the cluster.
* <p>
* <b>Important:</b> If this option is set, it is required to also set the {@link #setClusterKeystore(Path)
* keystore path}.
*
* @param truststorePath the path to the truststore
* @return this instance for chaining
*/
@Option(longName = "cluster-truststore", shortName = "cts")
@Description("Set the cluster truststore file path")
@ConvertedBy(PathConverter.class)
public Mutable setClusterTruststore(Path truststorePath) {
this.clusterTruststore = truststorePath;
return this;
}

@Override
public String getClusterTruststorePassword() {
return clusterTruststorePassword;
}

/**
* Sets the truststore password.
*
* @param password the password for the truststore
* @return this instance for chaining
*/
@Option(longName = "cluster-truststore-password", shortName = "cts-pw")
@Description("Set the cluster truststore password")
public Mutable setClusterTruststorePassword(String password) {
this.clusterTruststorePassword = password;
return this;
}

@Override
public Path getClusterKeystore() {
return clusterKeystore;
}

/**
* Sets the keystore (must be PKCS #12) to encrypt communication in the cluster.
* <p>
* <b>Important:</b> If this option is set, it is required to also set the {@link #setClusterTruststore(Path)
* truststore path}.
*
* @param keystorePath the path to the keystore
* @return this instance for chaining
*/
@Option(longName = "cluster-keystore", shortName = "cks")
@Description("Set the cluster keystore file path")
@ConvertedBy(PathConverter.class)
public Mutable setClusterKeystore(Path keystorePath) {
this.clusterKeystore = keystorePath;
return this;
}

@Override
public String getClusterKeystorePassword() {
return clusterKeystorePassword;
}

/**
* Sets the keystore password.
*
* @param password the password for the keystore
* @return this instance for chaining
*/
@Option(longName = "cluster-keystore-password", shortName = "cks-pw")
@Description("Set the cluster keystore password")
public Mutable setClusterKeystorePassword(String password) {
this.clusterKeystorePassword = password;
return this;
}

@Override
public ClusterManagerFactory getClusterManager() {
return clusterManagerFactory;
Expand Down
Loading

0 comments on commit 69f37c2

Please sign in to comment.