This is an automated email from the ASF dual-hosted git repository.
ibessonov pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/ignite-3.git
The following commit(s) were added to refs/heads/main by this push:
new 4b188ee303 IGNITE-18828 Add ciphers support to SSL (jdbc, client,
scalecube) (#1779)
4b188ee303 is described below
commit 4b188ee30301e8564ebff0051288795703f33699
Author: Vadim Pakhnushev <[email protected]>
AuthorDate: Wed Mar 15 11:44:55 2023 +0300
IGNITE-18828 Add ciphers support to SSL (jdbc, client, scalecube) (#1779)
---
.../org/apache/ignite/client/SslConfiguration.java | 3 +
.../internal/client/SslConfigurationBuilder.java | 12 +-
.../internal/client/SslConfigurationImpl.java | 9 +
.../io/netty/NettyClientConnectionMultiplexer.java | 4 +-
.../ignite/internal/jdbc/ConnectionProperties.java | 14 ++
.../internal/jdbc/ConnectionPropertiesImpl.java | 17 +-
.../ignite/internal/jdbc/JdbcConnection.java | 1 +
.../AbstractSslConfigurationSchema.java | 4 +
.../SslConfigurationValidatorImpl.java | 35 ++++
.../internal/network/ssl/SslContextProvider.java | 16 ++
.../SslConfigurationValidatorImplTest.java | 22 ++-
.../network/configuration/StubSslView.java | 9 +-
.../apache/ignite/internal/rest/RestComponent.java | 48 ++---
.../org/apache/ignite/internal/rest/RestNode.java | 27 ++-
.../ignite/internal/rest/RestNodeBuilder.java | 10 +-
.../ignite/internal/rest/ssl/ItRestSslTest.java | 67 +++++--
.../org/apache/ignite/internal/ssl/ItSslTest.java | 207 ++++++++++++++++++++-
17 files changed, 436 insertions(+), 69 deletions(-)
diff --git
a/modules/client/src/main/java/org/apache/ignite/client/SslConfiguration.java
b/modules/client/src/main/java/org/apache/ignite/client/SslConfiguration.java
index 2bf4c8c25c..075aa82829 100644
---
a/modules/client/src/main/java/org/apache/ignite/client/SslConfiguration.java
+++
b/modules/client/src/main/java/org/apache/ignite/client/SslConfiguration.java
@@ -28,6 +28,9 @@ public interface SslConfiguration {
/** Client authentication configuration. */
ClientAuthenticationMode clientAuthenticationMode();
+ /** List of ciphers that will be used to setup the SSL connection. */
+ @Nullable Iterable<String> ciphers();
+
/** Keystore path that will be used to setup the SSL connection. */
@Nullable String keyStorePath();
diff --git
a/modules/client/src/main/java/org/apache/ignite/internal/client/SslConfigurationBuilder.java
b/modules/client/src/main/java/org/apache/ignite/internal/client/SslConfigurationBuilder.java
index cd188009e6..5f69dc6264 100644
---
a/modules/client/src/main/java/org/apache/ignite/internal/client/SslConfigurationBuilder.java
+++
b/modules/client/src/main/java/org/apache/ignite/internal/client/SslConfigurationBuilder.java
@@ -29,6 +29,8 @@ public class SslConfigurationBuilder {
private ClientAuthenticationMode clientAuth =
ClientAuthenticationMode.NONE;
+ private @Nullable Iterable<String> ciphers;
+
private @Nullable String keyStorePath;
private @Nullable String keyStorePassword;
@@ -58,6 +60,12 @@ public class SslConfigurationBuilder {
return this;
}
+ /** Ciphers setter. */
+ public SslConfigurationBuilder ciphers(@Nullable Iterable<String> ciphers)
{
+ this.ciphers = ciphers;
+ return this;
+ }
+
/** Keystore path setter. */
public SslConfigurationBuilder keyStorePath(@Nullable String keyStorePath)
{
this.keyStorePath = keyStorePath;
@@ -107,7 +115,9 @@ public class SslConfigurationBuilder {
/** Build SslConfiguration instance. */
public SslConfiguration build() {
return new SslConfigurationImpl(
- enabled, clientAuth, keyStorePath, keyStorePassword,
keyStoreType, trustStorePath, trustStorePassword, trustStoreType
+ enabled, clientAuth, ciphers,
+ keyStorePath, keyStorePassword, keyStoreType,
+ trustStorePath, trustStorePassword, trustStoreType
);
}
}
diff --git
a/modules/client/src/main/java/org/apache/ignite/internal/client/SslConfigurationImpl.java
b/modules/client/src/main/java/org/apache/ignite/internal/client/SslConfigurationImpl.java
index d7a7c4ba13..a38eaa8e2f 100644
---
a/modules/client/src/main/java/org/apache/ignite/internal/client/SslConfigurationImpl.java
+++
b/modules/client/src/main/java/org/apache/ignite/internal/client/SslConfigurationImpl.java
@@ -27,6 +27,8 @@ public class SslConfigurationImpl implements SslConfiguration
{
private final ClientAuthenticationMode clientAuth;
+ private final @Nullable Iterable<String> ciphers;
+
private final @Nullable String keyStorePath;
private final @Nullable String keyStorePassword;
@@ -43,6 +45,7 @@ public class SslConfigurationImpl implements SslConfiguration
{
SslConfigurationImpl(
boolean enabled,
ClientAuthenticationMode clientAuth,
+ @Nullable Iterable<String> ciphers,
@Nullable String keyStorePath,
@Nullable String keyStorePassword,
String keyStoreType,
@@ -52,6 +55,7 @@ public class SslConfigurationImpl implements SslConfiguration
{
) {
this.enabled = enabled;
this.clientAuth = clientAuth;
+ this.ciphers = ciphers;
this.keyStorePath = keyStorePath;
this.keyStorePassword = keyStorePassword;
this.keyStoreType = keyStoreType;
@@ -72,6 +76,11 @@ public class SslConfigurationImpl implements
SslConfiguration {
return clientAuth;
}
+ @Override
+ public @Nullable Iterable<String> ciphers() {
+ return ciphers;
+ }
+
/** {@inheritDoc} */
@Override
public @Nullable String keyStorePath() {
diff --git
a/modules/client/src/main/java/org/apache/ignite/internal/client/io/netty/NettyClientConnectionMultiplexer.java
b/modules/client/src/main/java/org/apache/ignite/internal/client/io/netty/NettyClientConnectionMultiplexer.java
index 8406f5489c..b0c17be0c4 100644
---
a/modules/client/src/main/java/org/apache/ignite/internal/client/io/netty/NettyClientConnectionMultiplexer.java
+++
b/modules/client/src/main/java/org/apache/ignite/internal/client/io/netty/NettyClientConnectionMultiplexer.java
@@ -53,7 +53,6 @@ import
org.apache.ignite.internal.client.io.ClientMessageHandler;
import org.apache.ignite.internal.client.proto.ClientMessageDecoder;
import org.apache.ignite.lang.ErrorGroups.Client;
import org.apache.ignite.lang.IgniteException;
-import org.jetbrains.annotations.NotNull;
/**
* Netty-based multiplexer.
@@ -105,6 +104,7 @@ public class NettyClientConnectionMultiplexer implements
ClientConnectionMultipl
SslConfiguration ssl = clientCfg.ssl();
SslContextBuilder builder =
SslContextBuilder.forClient().trustManager(loadTrustManagerFactory(ssl));
+ builder.ciphers(ssl.ciphers());
ClientAuth clientAuth =
toNettyClientAuth(ssl.clientAuthenticationMode());
if (ClientAuth.NONE != clientAuth) {
builder.clientAuth(clientAuth).keyManager(loadKeyManagerFactory(ssl));
@@ -119,7 +119,6 @@ public class NettyClientConnectionMultiplexer implements
ClientConnectionMultipl
}
- @NotNull
private static KeyManagerFactory loadKeyManagerFactory(SslConfiguration
ssl)
throws KeyStoreException, IOException, NoSuchAlgorithmException,
CertificateException, UnrecoverableKeyException {
KeyStore ks = KeyStore.getInstance(ssl.keyStoreType());
@@ -138,7 +137,6 @@ public class NettyClientConnectionMultiplexer implements
ClientConnectionMultipl
return keyManagerFactory;
}
- @NotNull
private static TrustManagerFactory
loadTrustManagerFactory(SslConfiguration ssl)
throws KeyStoreException, IOException, NoSuchAlgorithmException,
CertificateException {
KeyStore ts = KeyStore.getInstance(ssl.trustStoreType());
diff --git
a/modules/jdbc/src/main/java/org/apache/ignite/internal/jdbc/ConnectionProperties.java
b/modules/jdbc/src/main/java/org/apache/ignite/internal/jdbc/ConnectionProperties.java
index ce29e5e572..f0d1820037 100644
---
a/modules/jdbc/src/main/java/org/apache/ignite/internal/jdbc/ConnectionProperties.java
+++
b/modules/jdbc/src/main/java/org/apache/ignite/internal/jdbc/ConnectionProperties.java
@@ -156,6 +156,20 @@ public interface ConnectionProperties {
*/
ClientAuthenticationMode getClientAuth();
+ /**
+ * SSL ciphers.
+ *
+ * @param ciphers list of ciphers.
+ */
+ void setCiphers(String ciphers);
+
+ /**
+ * SSL ciphers.
+ *
+ * @return list of ciphers.
+ */
+ Iterable<String> getCiphers();
+
/**
* Set trust store path that will be used to setup SSL connection.
*
diff --git
a/modules/jdbc/src/main/java/org/apache/ignite/internal/jdbc/ConnectionPropertiesImpl.java
b/modules/jdbc/src/main/java/org/apache/ignite/internal/jdbc/ConnectionPropertiesImpl.java
index 385008f74f..44cca8524f 100644
---
a/modules/jdbc/src/main/java/org/apache/ignite/internal/jdbc/ConnectionPropertiesImpl.java
+++
b/modules/jdbc/src/main/java/org/apache/ignite/internal/jdbc/ConnectionPropertiesImpl.java
@@ -109,6 +109,10 @@ public class ConnectionPropertiesImpl implements
ConnectionProperties, Serializa
private final StringProperty clientAuth = new StringProperty("clientAuth",
"SSL client authentication", "none", clientAuthValues(), false,
null);
+ /** SSL ciphers list. */
+ private final StringProperty ciphers = new StringProperty("ciphers",
+ "SSL ciphers", null, null, false, null);
+
@NotNull
private static String[] clientAuthValues() {
return Arrays.stream(ClientAuthenticationMode.values())
@@ -125,7 +129,7 @@ public class ConnectionPropertiesImpl implements
ConnectionProperties, Serializa
/** Properties array. */
private final ConnectionProperty[] propsArray = {
qryTimeout, connTimeout, trustStorePath, trustStorePassword,
trustStoreType,
- sslEnabled, clientAuth, keyStorePath, keyStorePassword,
keyStoreType
+ sslEnabled, clientAuth, ciphers, keyStorePath, keyStorePassword,
keyStoreType
};
/** {@inheritDoc} */
@@ -334,6 +338,17 @@ public class ConnectionPropertiesImpl implements
ConnectionProperties, Serializa
return
ClientAuthenticationMode.valueOf(this.clientAuth.value().toUpperCase());
}
+ @Override
+ public void setCiphers(String ciphers) {
+ this.ciphers.setValue(ciphers);
+ }
+
+ @Override
+ public Iterable<String> getCiphers() {
+ String value = ciphers.value();
+ return value != null ? Arrays.asList(value.split(",")) : null;
+ }
+
/**
* Init connection properties.
*
diff --git
a/modules/jdbc/src/main/java/org/apache/ignite/internal/jdbc/JdbcConnection.java
b/modules/jdbc/src/main/java/org/apache/ignite/internal/jdbc/JdbcConnection.java
index 0c39eb4034..d157be23a5 100644
---
a/modules/jdbc/src/main/java/org/apache/ignite/internal/jdbc/JdbcConnection.java
+++
b/modules/jdbc/src/main/java/org/apache/ignite/internal/jdbc/JdbcConnection.java
@@ -179,6 +179,7 @@ public class JdbcConnection implements Connection {
.trustStorePath(connProps.getTrustStorePath())
.trustStorePassword(connProps.getTrustStorePassword())
.clientAuth(connProps.getClientAuth())
+ .ciphers(connProps.getCiphers())
.keyStoreType(connProps.getKeyStoreType())
.keyStorePath(connProps.getKeyStorePath())
.keyStorePassword(connProps.getKeyStorePassword())
diff --git
a/modules/network/src/main/java/org/apache/ignite/internal/network/configuration/AbstractSslConfigurationSchema.java
b/modules/network/src/main/java/org/apache/ignite/internal/network/configuration/AbstractSslConfigurationSchema.java
index 958a28732d..b58890748b 100644
---
a/modules/network/src/main/java/org/apache/ignite/internal/network/configuration/AbstractSslConfigurationSchema.java
+++
b/modules/network/src/main/java/org/apache/ignite/internal/network/configuration/AbstractSslConfigurationSchema.java
@@ -42,6 +42,10 @@ public class AbstractSslConfigurationSchema {
@Value(hasDefault = true)
public final String clientAuth = "none";
+ /** List of ciphers to enable, separated by comma. */
+ @Value(hasDefault = true)
+ public String ciphers = "";
+
/** SSL keystore configuration. */
@ConfigValue
public KeyStoreConfigurationSchema keyStore;
diff --git
a/modules/network/src/main/java/org/apache/ignite/internal/network/configuration/SslConfigurationValidatorImpl.java
b/modules/network/src/main/java/org/apache/ignite/internal/network/configuration/SslConfigurationValidatorImpl.java
index 0efc970d06..ab08f9d659 100644
---
a/modules/network/src/main/java/org/apache/ignite/internal/network/configuration/SslConfigurationValidatorImpl.java
+++
b/modules/network/src/main/java/org/apache/ignite/internal/network/configuration/SslConfigurationValidatorImpl.java
@@ -19,13 +19,23 @@ package org.apache.ignite.internal.network.configuration;
import static org.apache.ignite.internal.util.StringUtils.nullOrBlank;
+import io.netty.buffer.ByteBufAllocator;
import io.netty.handler.ssl.ClientAuth;
+import io.netty.handler.ssl.SslContext;
+import io.netty.handler.ssl.SslContextBuilder;
import java.nio.file.Files;
import java.nio.file.InvalidPathException;
import java.nio.file.Path;
+import java.util.Arrays;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+import javax.net.ssl.SSLException;
import org.apache.ignite.configuration.validation.ValidationContext;
import org.apache.ignite.configuration.validation.ValidationIssue;
import org.apache.ignite.configuration.validation.Validator;
+import org.apache.ignite.internal.logger.IgniteLogger;
+import org.apache.ignite.internal.logger.Loggers;
/**
* SSL configuration validator implementation.
@@ -34,6 +44,8 @@ public class SslConfigurationValidatorImpl implements
Validator<SslConfiguration
public static final SslConfigurationValidatorImpl INSTANCE = new
SslConfigurationValidatorImpl();
+ private static final IgniteLogger LOG =
Loggers.forClass(SslConfigurationValidatorImpl.class);
+
@Override
public void validate(SslConfigurationValidator annotation,
ValidationContext<AbstractSslView> ctx) {
AbstractSslView ssl = ctx.getNewValue();
@@ -48,6 +60,10 @@ public class SslConfigurationValidatorImpl implements
Validator<SslConfiguration
} catch (IllegalArgumentException e) {
ctx.addIssue(new ValidationIssue(ctx.currentKey(), "Incorrect
client auth parameter " + ssl.clientAuth()));
}
+
+ if (!ssl.ciphers().isBlank()) {
+ validateCiphers(ctx, ssl);
+ }
}
}
@@ -73,4 +89,23 @@ public class SslConfigurationValidatorImpl implements
Validator<SslConfiguration
}
}
}
+
+ private static void validateCiphers(ValidationContext<AbstractSslView>
ctx, AbstractSslView ssl) {
+ try {
+ SslContext context = SslContextBuilder.forClient().build();
+ Set<String> supported =
Arrays.stream(context.newEngine(ByteBufAllocator.DEFAULT).getSupportedCipherSuites())
+ .filter(Objects::nonNull) // OpenSSL engine returns null
string in the array so we need to filter them out
+ .collect(Collectors.toSet());
+ Set<String> ciphers = Arrays.stream(ssl.ciphers().split(","))
+ .map(String::strip)
+ .collect(Collectors.toSet());
+ if (!supported.containsAll(ciphers)) {
+ ciphers.removeAll(supported);
+ ctx.addIssue(new ValidationIssue(ctx.currentKey(), "There are
unsupported cipher suites: " + ciphers));
+ }
+ } catch (SSLException e) {
+ ctx.addIssue(new ValidationIssue(ctx.currentKey(), "Can't create
SSL engine"));
+ LOG.warn("Can't create SSL engine", e);
+ }
+ }
}
diff --git
a/modules/network/src/main/java/org/apache/ignite/internal/network/ssl/SslContextProvider.java
b/modules/network/src/main/java/org/apache/ignite/internal/network/ssl/SslContextProvider.java
index d50b1acc3f..be1e570086 100644
---
a/modules/network/src/main/java/org/apache/ignite/internal/network/ssl/SslContextProvider.java
+++
b/modules/network/src/main/java/org/apache/ignite/internal/network/ssl/SslContextProvider.java
@@ -26,6 +26,9 @@ import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.TrustManagerFactory;
import org.apache.ignite.internal.network.configuration.SslView;
@@ -46,6 +49,8 @@ public final class SslContextProvider {
var builder =
SslContextBuilder.forClient().trustManager(trustManagerFactory);
+ setCiphers(builder, ssl);
+
ClientAuth clientAuth =
ClientAuth.valueOf(ssl.clientAuth().toUpperCase());
if (ClientAuth.NONE == clientAuth) {
return builder.build();
@@ -72,6 +77,8 @@ public final class SslContextProvider {
var builder = SslContextBuilder.forServer(keyManagerFactory);
+ setCiphers(builder, ssl);
+
ClientAuth clientAuth =
ClientAuth.valueOf(ssl.clientAuth().toUpperCase());
if (ClientAuth.NONE == clientAuth) {
return builder.build();
@@ -89,4 +96,13 @@ public final class SslContextProvider {
throw new IgniteException(Common.SSL_CONFIGURATION_ERR, e);
}
}
+
+ private static void setCiphers(SslContextBuilder builder, SslView ssl) {
+ if (!ssl.ciphers().isBlank()) {
+ List<String> ciphers = Arrays.stream(ssl.ciphers().split(","))
+ .map(String::strip)
+ .collect(Collectors.toList());
+ builder.ciphers(ciphers);
+ }
+ }
}
diff --git
a/modules/network/src/test/java/org/apache/ignite/internal/network/configuration/SslConfigurationValidatorImplTest.java
b/modules/network/src/test/java/org/apache/ignite/internal/network/configuration/SslConfigurationValidatorImplTest.java
index 2ab49b09ba..c2bc2e90da 100644
---
a/modules/network/src/test/java/org/apache/ignite/internal/network/configuration/SslConfigurationValidatorImplTest.java
+++
b/modules/network/src/test/java/org/apache/ignite/internal/network/configuration/SslConfigurationValidatorImplTest.java
@@ -56,6 +56,20 @@ class SslConfigurationValidatorImplTest {
"Key store type must not be blank", "Key store file doesn't
exist at /path/to/keystore.p12");
}
+ @Test
+ public void incorrectCipherName(@WorkDirectory Path workDir) throws
IOException {
+ KeyStoreView keyStore = createValidKeyStoreConfig(workDir);
+ validate(new StubSslView(true, "NONE", "foo, TLS_AES_256_GCM_SHA384",
keyStore, null),
+ "There are unsupported cipher suites: [foo]");
+ }
+
+ @Test
+ public void validCipherName(@WorkDirectory Path workDir) throws
IOException {
+ KeyStoreView keyStore = createValidKeyStoreConfig(workDir);
+ validate(new StubSslView(true, "NONE", "TLS_AES_256_GCM_SHA384",
keyStore, null),
+ (String[]) null);
+ }
+
@Test
public void nullTrustStorePath(@WorkDirectory Path workDir) throws
IOException {
validate(createTrustStoreConfig(workDir, "PKCS12", null, "changeIt"),
@@ -83,7 +97,7 @@ class SslConfigurationValidatorImplTest {
@Test
public void incorrectAuthType(@WorkDirectory Path workDir) throws
IOException {
KeyStoreView keyStore = createValidKeyStoreConfig(workDir);
- StubSslView sslView = new StubSslView(true, "foo", keyStore, null);
+ StubSslView sslView = new StubSslView(true, "foo", "", keyStore, null);
validate(sslView, "Incorrect client auth parameter foo");
}
@@ -91,7 +105,7 @@ class SslConfigurationValidatorImplTest {
@Test
public void validKeyStoreConfig(@WorkDirectory Path workDir) throws
IOException {
KeyStoreView keyStore = createValidKeyStoreConfig(workDir);
- validate(new StubSslView(true, "NONE", keyStore, null), (String[])
null);
+ validate(new StubSslView(true, "NONE", "", keyStore, null), (String[])
null);
}
@Test
@@ -109,13 +123,13 @@ class SslConfigurationValidatorImplTest {
}
private static AbstractSslView createKeyStoreConfig(String type, String
path, String password) {
- return new StubSslView(true, "NONE", new StubKeyStoreView(type, path,
password), null);
+ return new StubSslView(true, "NONE", "", new StubKeyStoreView(type,
path, password), null);
}
private static AbstractSslView createTrustStoreConfig(Path workDir, String
type, String path, String password) throws IOException {
KeyStoreView keyStore = createValidKeyStoreConfig(workDir);
KeyStoreView trustStore = new StubKeyStoreView(type, path, password);
- return new StubSslView(true, "OPTIONAL", keyStore, trustStore);
+ return new StubSslView(true, "OPTIONAL", "", keyStore, trustStore);
}
private static KeyStoreView createValidKeyStoreConfig(Path workDir) throws
IOException {
diff --git
a/modules/network/src/test/java/org/apache/ignite/internal/network/configuration/StubSslView.java
b/modules/network/src/test/java/org/apache/ignite/internal/network/configuration/StubSslView.java
index c074054dda..e3d6cca2f0 100644
---
a/modules/network/src/test/java/org/apache/ignite/internal/network/configuration/StubSslView.java
+++
b/modules/network/src/test/java/org/apache/ignite/internal/network/configuration/StubSslView.java
@@ -22,13 +22,15 @@ public class StubSslView implements SslView {
private final boolean enabled;
private final String clientAuth;
+ private String ciphers;
private final KeyStoreView keyStore;
private final KeyStoreView trustStore;
/** Constructor. */
- public StubSslView(boolean enabled, String clientAuth, KeyStoreView
keyStore, KeyStoreView trustStore) {
+ public StubSslView(boolean enabled, String clientAuth, String ciphers,
KeyStoreView keyStore, KeyStoreView trustStore) {
this.enabled = enabled;
this.clientAuth = clientAuth;
+ this.ciphers = ciphers;
this.keyStore = keyStore;
this.trustStore = trustStore;
}
@@ -43,6 +45,11 @@ public class StubSslView implements SslView {
return clientAuth;
}
+ @Override
+ public String ciphers() {
+ return ciphers;
+ }
+
@Override
public KeyStoreView keyStore() {
return keyStore;
diff --git
a/modules/rest/src/main/java/org/apache/ignite/internal/rest/RestComponent.java
b/modules/rest/src/main/java/org/apache/ignite/internal/rest/RestComponent.java
index 7aa8b5bde0..518bb06ffb 100644
---
a/modules/rest/src/main/java/org/apache/ignite/internal/rest/RestComponent.java
+++
b/modules/rest/src/main/java/org/apache/ignite/internal/rest/RestComponent.java
@@ -181,7 +181,8 @@ public class RestComponent implements IgniteComponent {
return micronaut
.properties(properties)
.banner(false)
- .mapError(ServerStartupException.class,
RestComponent::mapServerStartupException)
+ // -1 forces the micronaut to throw an
ApplicationStartupException instead of doing System.exit
+ .mapError(ServerStartupException.class, ex -> -1)
.mapError(ApplicationStartupException.class, ex -> -1);
}
@@ -191,14 +192,6 @@ public class RestComponent implements IgniteComponent {
}
}
- private static int mapServerStartupException(ServerStartupException
exception) {
- if (exception.getCause() instanceof BindException) {
- return -1; // -1 forces the micronaut to throw an
ApplicationStartupException
- } else {
- return 1;
- }
- }
-
private Map<String, Object> serverProperties(int port, int sslPort) {
RestSslView restSslView = restConfiguration.ssl().value();
boolean sslEnabled = restSslView.enabled();
@@ -207,33 +200,30 @@ public class RestComponent implements IgniteComponent {
KeyStoreView keyStore = restSslView.keyStore();
boolean dualProtocol = restConfiguration.dualProtocol().value();
- Map<String, Object> micronautSslConfig = Map.of(
- "micronaut.server.port", port, // Micronaut is not going
to handle requests on that port, but it's required
- "micronaut.server.dual-protocol", dualProtocol,
- "micronaut.server.ssl.port", sslPort,
- "micronaut.server.ssl.enabled", sslEnabled,
- "micronaut.server.ssl.key-store.path", "file:" +
keyStore.path(),
- "micronaut.server.ssl.key-store.password",
keyStore.password(),
- "micronaut.server.ssl.key-store.type", keyStore.type()
- );
+ Map<String, Object> result = new HashMap<>();
+ // Micronaut is not going to handle requests on that port, but
it's required
+ result.put("micronaut.server.port", port);
+ result.put("micronaut.server.dual-protocol", dualProtocol);
+ result.put("micronaut.server.ssl.port", sslPort);
+ if (!restSslView.ciphers().isBlank()) {
+ result.put("micronaut.server.ssl.ciphers",
restSslView.ciphers());
+ }
+ result.put("micronaut.server.ssl.enabled", sslEnabled);
+ result.put("micronaut.server.ssl.key-store.path", "file:" +
keyStore.path());
+ result.put("micronaut.server.ssl.key-store.password",
keyStore.password());
+ result.put("micronaut.server.ssl.key-store.type", keyStore.type());
ClientAuth clientAuth =
ClientAuth.valueOf(restSslView.clientAuth().toUpperCase());
if (ClientAuth.NONE == clientAuth) {
- return micronautSslConfig;
+ return result;
}
KeyStoreView trustStore = restSslView.trustStore();
- Map<String, Object> micronautClientAuthConfig = Map.of(
- "micronaut.server.ssl.client-authentication",
toMicronautClientAuth(clientAuth),
- "micronaut.server.ssl.trust-store.path", "file:" +
trustStore.path(),
- "micronaut.server.ssl.trust-store.password",
trustStore.password(),
- "micronaut.server.ssl.trust-store.type", trustStore.type()
- );
-
- HashMap<String, Object> result = new HashMap<>();
- result.putAll(micronautSslConfig);
- result.putAll(micronautClientAuthConfig);
+ result.put("micronaut.server.ssl.client-authentication",
toMicronautClientAuth(clientAuth));
+ result.put("micronaut.server.ssl.trust-store.path", "file:" +
trustStore.path());
+ result.put("micronaut.server.ssl.trust-store.password",
trustStore.password());
+ result.put("micronaut.server.ssl.trust-store.type",
trustStore.type());
return result;
} else {
diff --git
a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/rest/RestNode.java
b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/rest/RestNode.java
index 1a955c1154..069087de94 100644
---
a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/rest/RestNode.java
+++
b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/rest/RestNode.java
@@ -41,6 +41,7 @@ public class RestNode {
private final boolean sslEnabled;
private final boolean sslClientAuthEnabled;
private final boolean dualProtocol;
+ private final String ciphers;
private CompletableFuture<Ignite> igniteNodeFuture;
/** Constructor. */
@@ -56,7 +57,8 @@ public class RestNode {
int httpsPort,
boolean sslEnabled,
boolean sslClientAuthEnabled,
- boolean dualProtocol
+ boolean dualProtocol,
+ String ciphers
) {
this.keyStorePath = keyStorePath;
this.keyStorePassword = keyStorePassword;
@@ -70,6 +72,7 @@ public class RestNode {
this.sslEnabled = sslEnabled;
this.sslClientAuthEnabled = sslClientAuthEnabled;
this.dualProtocol = dualProtocol;
+ this.ciphers = ciphers;
}
public static RestNodeBuilder builder() {
@@ -115,6 +118,8 @@ public class RestNode {
}
private String bootstrapCfg() {
+ String keyStoreFilePath =
getResourcePath(ItRestSslTest.class.getClassLoader().getResource(keyStorePath));
+ String trustStoreFilePath =
getResourcePath(ItRestSslTest.class.getClassLoader().getResource(trustStorePath));
return "{\n"
+ " network: {\n"
+ " port: " + networkPort + ",\n"
@@ -128,14 +133,15 @@ public class RestNode {
+ " ssl: {\n"
+ " enabled: " + sslEnabled + ",\n"
+ " clientAuth: " + (sslClientAuthEnabled ? "require" :
"none") + ",\n"
+ + " ciphers: \"" + ciphers + "\",\n"
+ " port: " + httpsPort + ",\n"
+ " keyStore: {\n"
- + " path: \"" + getResourcePath(keyStorePath) + "\",\n"
+ + " path: \"" + escapeWindowsPath(keyStoreFilePath) +
"\",\n"
+ " password: " + keyStorePassword + "\n"
+ " }, \n"
+ " trustStore: {\n"
+ " type: JKS,\n"
- + " path: \"" + getResourcePath(trustStorePath) +
"\",\n"
+ + " path: \"" + escapeWindowsPath(trustStoreFilePath) +
"\",\n"
+ " password: " + trustStorePassword + "\n"
+ " }\n"
+ " }\n"
@@ -143,14 +149,19 @@ public class RestNode {
+ "}";
}
- private static String getResourcePath(String resource) {
+ /** Converts URL gotten from classloader to proper file system path. */
+ public static String getResourcePath(URL url) {
try {
- URL url =
ItRestSslTest.class.getClassLoader().getResource(resource);
- Objects.requireNonNull(url, "Resource " + resource + " not
found.");
- Path path = Path.of(url.toURI()); // Properly extract file system
path from the "file:" URL
- return path.toString().replace("\\", "\\\\"); // Escape
backslashes for the config parser
+ Objects.requireNonNull(url);
+ // Properly extract file system path from the "file:" URL
+ return Path.of(url.toURI()).toString();
} catch (URISyntaxException e) {
throw new RuntimeException(e); // Shouldn't happen since URL is
obtained from the class loader
}
}
+
+ /** Use this to escape backslashes for the HOCON config parser. */
+ public static String escapeWindowsPath(String path) {
+ return path.replace("\\", "\\\\");
+ }
}
diff --git
a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/rest/RestNodeBuilder.java
b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/rest/RestNodeBuilder.java
index 9b2f5eead5..6250bb1038 100644
---
a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/rest/RestNodeBuilder.java
+++
b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/rest/RestNodeBuilder.java
@@ -35,6 +35,8 @@ public class RestNodeBuilder {
private boolean sslClientAuthEnabled = false;
private boolean dualProtocol = false;
+ private String ciphers = "";
+
public RestNodeBuilder keyStorePath(String keyStorePath) {
this.keyStorePath = keyStorePath;
return this;
@@ -95,6 +97,11 @@ public class RestNodeBuilder {
return this;
}
+ public RestNodeBuilder ciphers(String ciphers) {
+ this.ciphers = ciphers;
+ return this;
+ }
+
/** Builds {@link RestNode}. */
public RestNode build() {
return new RestNode(
@@ -109,7 +116,8 @@ public class RestNodeBuilder {
httpsPort,
sslEnabled,
sslClientAuthEnabled,
- dualProtocol
+ dualProtocol,
+ ciphers
);
}
}
diff --git
a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/rest/ssl/ItRestSslTest.java
b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/rest/ssl/ItRestSslTest.java
index 66ed790197..9455e37eb2 100644
---
a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/rest/ssl/ItRestSslTest.java
+++
b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/rest/ssl/ItRestSslTest.java
@@ -39,6 +39,7 @@ import java.security.cert.CertificateException;
import java.util.stream.Stream;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLParameters;
import javax.net.ssl.TrustManagerFactory;
import org.apache.ignite.internal.IgniteIntegrationTest;
import org.apache.ignite.internal.rest.RestNode;
@@ -73,15 +74,18 @@ public class ItRestSslTest extends IgniteIntegrationTest {
@WorkDirectory
private static Path workDir;
- /** HTTP client that is expected to be defined in subclasses. */
+ /** HTTP client. */
private static HttpClient client;
- /** SSL HTTP client that is expected to be defined in subclasses. */
+ /** SSL HTTP client. */
private static HttpClient sslClient;
- /** SSL HTTP client that is expected to be defined in subclasses. */
+ /** SSL HTTP client with client auth. */
private static HttpClient sslClientWithClientAuth;
+ /** SSL HTTP client with custom cipher. */
+ private static HttpClient sslClientWithCustomCipher;
+
private static RestNode httpNode;
private static RestNode httpsNode;
@@ -90,6 +94,8 @@ public class ItRestSslTest extends IgniteIntegrationTest {
private static RestNode httpsWithClientAuthNode;
+ private static RestNode httpsWithCustomCipherNode;
+
@BeforeAll
static void beforeAll(TestInfo testInfo) throws Exception {
@@ -104,6 +110,14 @@ public class ItRestSslTest extends IgniteIntegrationTest {
.sslContext(sslContextWithClientAuth())
.build();
+ SSLContext sslContext = sslContext();
+ SSLParameters sslParameters = sslContext.getDefaultSSLParameters();
+ sslParameters.setCipherSuites(new
String[]{"TLS_DHE_RSA_WITH_AES_128_CBC_SHA"});
+ sslClientWithCustomCipher = HttpClient.newBuilder()
+ .sslContext(sslContext)
+ .sslParameters(sslParameters)
+ .build();
+
httpNode = RestNode.builder()
.workDir(workDir)
.name(testNodeName(testInfo, 3344))
@@ -145,12 +159,23 @@ public class ItRestSslTest extends IgniteIntegrationTest {
.dualProtocol(false)
.build();
- Stream.of(httpNode, httpsNode, dualProtocolNode,
httpsWithClientAuthNode).parallel()
+ httpsWithCustomCipherNode = RestNode.builder()
+ .workDir(workDir)
+ .name(testNodeName(testInfo, 3348))
+ .networkPort(3348)
+ .httpPort(10304)
+ .httpsPort(10404)
+ .sslEnabled(true)
+ .dualProtocol(false)
+ .ciphers("TLS_AES_256_GCM_SHA384")
+ .build();
+
+ Stream.of(httpNode, httpsNode, dualProtocolNode,
httpsWithClientAuthNode, httpsWithCustomCipherNode).parallel()
.forEach(RestNode::start);
}
@Test
- void httpsProtocol() throws IOException, InterruptedException {
+ void httpsProtocol() throws Exception {
// When GET /management/v1/configuration/node
URI uri = URI.create(httpsNode.httpsAddress() +
"/management/v1/configuration/node");
HttpRequest request = HttpRequest.newBuilder(uri).build();
@@ -161,7 +186,7 @@ public class ItRestSslTest extends IgniteIntegrationTest {
}
@Test
- void httpProtocol(TestInfo testInfo) throws IOException,
InterruptedException {
+ void httpProtocol(TestInfo testInfo) throws Exception {
// When GET /management/v1/configuration/node
URI uri = URI.create(httpNode.httpAddress() +
"/management/v1/configuration/node");
HttpRequest request = HttpRequest.newBuilder(uri).build();
@@ -172,7 +197,7 @@ public class ItRestSslTest extends IgniteIntegrationTest {
}
@Test
- void dualProtocol() throws IOException, InterruptedException {
+ void dualProtocol() throws Exception {
// When GET /management/v1/configuration/node
URI httpUri = URI.create(dualProtocolNode.httpAddress() +
"/management/v1/configuration/node");
HttpRequest httpRequest = HttpRequest.newBuilder(httpUri).build();
@@ -200,7 +225,7 @@ public class ItRestSslTest extends IgniteIntegrationTest {
}
@Test
- void httpProtocolNotSslClient() throws IOException, InterruptedException {
+ void httpProtocolNotSslClient() {
// When GET /management/v1/configuration/node
URI uri = URI.create(httpsNode.httpAddress() +
"/management/v1/configuration/node");
HttpRequest request = HttpRequest.newBuilder(uri).build();
@@ -210,7 +235,7 @@ public class ItRestSslTest extends IgniteIntegrationTest {
}
@Test
- void httpsWithClientAuthProtocol(TestInfo testInfo) throws IOException,
InterruptedException {
+ void httpsWithClientAuthProtocol(TestInfo testInfo) throws Exception {
// When GET /management/v1/configuration/node
URI uri = URI.create(httpsWithClientAuthNode.httpsAddress() +
"/management/v1/configuration/node");
HttpRequest request = HttpRequest.newBuilder(uri).build();
@@ -221,7 +246,7 @@ public class ItRestSslTest extends IgniteIntegrationTest {
}
@Test
- void httpsWithClientAuthProtocolButClientWithoutAuth(TestInfo testInfo)
throws IOException, InterruptedException {
+ void httpsWithClientAuthProtocolButClientWithoutAuth(TestInfo testInfo) {
// When GET /management/v1/configuration/node
URI uri = URI.create(httpsWithClientAuthNode.httpsAddress() +
"/management/v1/configuration/node");
HttpRequest request = HttpRequest.newBuilder(uri).build();
@@ -230,9 +255,29 @@ public class ItRestSslTest extends IgniteIntegrationTest {
assertThrows(IOException.class, () -> sslClient.send(request,
BodyHandlers.ofString()));
}
+ void httpsWithCustomCipher(TestInfo testInfo) throws Exception {
+ // When GET /management/v1/configuration/node
+ URI uri = URI.create(httpsWithCustomCipherNode.httpsAddress() +
"/management/v1/configuration/node");
+ HttpRequest request = HttpRequest.newBuilder(uri).build();
+
+ // Then response code is 200
+ HttpResponse<String> response = sslClient.send(request,
BodyHandlers.ofString());
+ assertEquals(200, response.statusCode());
+ }
+
+ @Test
+ void httpsWithCustomCipherButClientWithIncompatibleCipher(TestInfo
testInfo) {
+ // When GET /management/v1/configuration/node
+ URI uri = URI.create(httpsWithCustomCipherNode.httpsAddress() +
"/management/v1/configuration/node");
+ HttpRequest request = HttpRequest.newBuilder(uri).build();
+
+ // Expect IOException for SSL client that configures incompatible
cipher
+ assertThrows(IOException.class, () ->
sslClientWithCustomCipher.send(request, BodyHandlers.ofString()));
+ }
+
@AfterAll
static void afterAll() {
- Stream.of(httpNode, httpsNode, dualProtocolNode,
httpsWithClientAuthNode)
+ Stream.of(httpNode, httpsNode, dualProtocolNode,
httpsWithClientAuthNode, httpsWithCustomCipherNode)
.parallel()
.forEach(RestNode::stop);
}
diff --git
a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/ssl/ItSslTest.java
b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/ssl/ItSslTest.java
index 4e5a4471fe..164433becf 100644
---
a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/ssl/ItSslTest.java
+++
b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/ssl/ItSslTest.java
@@ -18,6 +18,11 @@
package org.apache.ignite.internal.ssl;
import static org.apache.ignite.client.ClientAuthenticationMode.REQUIRE;
+import static org.apache.ignite.internal.rest.RestNode.escapeWindowsPath;
+import static org.apache.ignite.internal.rest.RestNode.getResourcePath;
+import static
org.apache.ignite.internal.testframework.IgniteTestUtils.testNodeName;
+import static
org.apache.ignite.internal.testframework.matchers.CompletableFutureMatcher.willCompleteSuccessfully;
+import static
org.apache.ignite.internal.testframework.matchers.CompletableFutureMatcher.willTimeoutIn;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.is;
@@ -27,11 +32,17 @@ import java.nio.file.Path;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.TimeUnit;
+import org.apache.ignite.IgnitionManager;
+import org.apache.ignite.InitParameters;
import org.apache.ignite.client.IgniteClient;
import org.apache.ignite.client.IgniteClientConnectionException;
import org.apache.ignite.client.SslConfiguration;
import org.apache.ignite.internal.Cluster;
import org.apache.ignite.internal.IgniteIntegrationTest;
+import org.apache.ignite.internal.app.IgniteImpl;
import org.apache.ignite.internal.testframework.WorkDirectory;
import org.intellij.lang.annotations.Language;
import org.junit.jupiter.api.AfterEach;
@@ -54,8 +65,8 @@ public class ItSslTest extends IgniteIntegrationTest {
@BeforeAll
static void beforeAll() {
password = "changeit";
- trustStorePath =
ItSslTest.class.getClassLoader().getResource("ssl/truststore.jks").getPath();
- keyStorePath =
ItSslTest.class.getClassLoader().getResource("ssl/keystore.p12").getPath();
+ trustStorePath =
getResourcePath(ItSslTest.class.getClassLoader().getResource("ssl/truststore.jks"));
+ keyStorePath =
getResourcePath(ItSslTest.class.getClassLoader().getResource("ssl/keystore.p12"));
}
@Nested
@@ -105,7 +116,7 @@ public class ItSslTest extends IgniteIntegrationTest {
@Test
@DisplayName("Client can not connect with ssl configured when ssl
disabled on the cluster")
- void clientCanNotConnectWithoutSsl() {
+ void clientCanNotConnectWithSsl() {
var sslConfiguration =
SslConfiguration.builder()
.enabled(true)
@@ -156,11 +167,11 @@ public class ItSslTest extends IgniteIntegrationTest {
+ " enabled: true,\n"
+ " trustStore: {\n"
+ " password: \"" + password + "\","
- + " path: \"" + trustStorePath + "\""
+ + " path: \"" + escapeWindowsPath(trustStorePath) + "\""
+ " },\n"
+ " keyStore: {\n"
+ " password: \"" + password + "\","
- + " path: \"" + keyStorePath + "\""
+ + " path: \"" + escapeWindowsPath(keyStorePath) + "\""
+ " }\n"
+ " },\n"
+ " port: 3345,\n"
@@ -172,7 +183,7 @@ public class ItSslTest extends IgniteIntegrationTest {
+ " clientConnector.ssl: {\n"
+ " enabled: true, "
+ " keyStore: {\n"
- + " path: \"" + keyStorePath + "\",\n"
+ + " path: \"" + escapeWindowsPath(keyStorePath) + "\",\n"
+ " password: \"" + password + "\"\n"
+ " }\n"
+ " }\n"
@@ -250,6 +261,120 @@ public class ItSslTest extends IgniteIntegrationTest {
}
}
+ @Nested
+ @DisplayName("Given SSL enabled on the cluster and specific cipher
enabled")
+ class ClusterWithSslCustomCipher {
+
+ String sslEnabledWithCipherBoostrapConfig =
createBoostrapConfig("TLS_AES_256_GCM_SHA384");
+
+ @WorkDirectory
+ private Path workDir;
+ private Cluster cluster;
+
+ @BeforeEach
+ void setUp(TestInfo testInfo) {
+ cluster = new Cluster(testInfo, workDir,
sslEnabledWithCipherBoostrapConfig);
+ cluster.startAndInit(2);
+ }
+
+ @AfterEach
+ void tearDown() {
+ cluster.shutdown();
+ }
+
+ @Test
+ @DisplayName("SSL enabled and setup correctly then cluster starts")
+ void clusterStartsWithEnabledSsl(TestInfo testInfo) {
+ assertThat(cluster.runningNodes().count(), is(2L));
+ }
+
+ @Test
+ @DisplayName("Client cannot connect without SSL configured")
+ void clientCannotConnectWithoutSsl() {
+ assertThrows(IgniteClientConnectionException.class, () -> {
+ try (IgniteClient ignored =
IgniteClient.builder().addresses("localhost:10800").build()) {
+ // no-op
+ } catch (IgniteClientConnectionException e) {
+ throw e;
+ }
+ });
+ }
+
+ @Test
+ void jdbcCannotConnectWithoutSsl() {
+ var url = "jdbc:ignite:thin://127.0.0.1:10800";
+ assertThrows(SQLException.class, () ->
DriverManager.getConnection(url));
+ }
+
+ @Test
+ @DisplayName("Client can connect with SSL configured")
+ void clientCanConnectWithSsl() throws Exception {
+ var sslConfiguration =
+ SslConfiguration.builder()
+ .enabled(true)
+ .trustStoreType("JKS")
+ .trustStorePath(trustStorePath)
+ .trustStorePassword(password)
+ .build();
+
+ try (IgniteClient client = IgniteClient.builder()
+ .addresses("localhost:10800")
+ .ssl(sslConfiguration)
+ .build()
+ ) {
+ assertThat(client.clusterNodes(), hasSize(2));
+ }
+ }
+
+ @Test
+ @DisplayName("Client cannot connect with SSL configured with
non-matcher cipher")
+ void clientCannotConnectWithSslAndWrongCipher() {
+ var sslConfiguration =
+ SslConfiguration.builder()
+ .enabled(true)
+
.ciphers(List.of("TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384"))
+ .trustStoreType("JKS")
+ .trustStorePath(trustStorePath)
+ .trustStorePassword(password)
+ .build();
+
+ assertThrows(IgniteClientConnectionException.class, () -> {
+ try (IgniteClient ignored = IgniteClient.builder()
+ .addresses("localhost:10800")
+ .ssl(sslConfiguration)
+ .build()) {
+ // no-op
+ }
+ });
+ }
+
+ @Test
+ void jdbcCannotConnectWithSslAndWrongCipher() {
+ var url =
+ "jdbc:ignite:thin://127.0.0.1:10800"
+ + "?sslEnabled=true"
+ +
"&ciphers=TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384"
+ + "&trustStorePath=" + trustStorePath
+ + "&trustStoreType=JKS"
+ + "&trustStorePassword=" + password;
+ assertThrows(SQLException.class, () ->
DriverManager.getConnection(url));
+ }
+
+ @Test
+ @DisplayName("Jdbc client can connect with SSL configured")
+ void jdbcCanConnectWithSsl() throws SQLException {
+ var url =
+ "jdbc:ignite:thin://127.0.0.1:10800"
+ + "?sslEnabled=true"
+ + "&trustStorePath=" + trustStorePath
+ + "&trustStoreType=JKS"
+ + "&trustStorePassword=" + password;
+ try (Connection conn = DriverManager.getConnection(url)) {
+ // No-op.
+ }
+ }
+ }
+
@Nested
@DisplayName("Given SSL enabled client auth is set to require on the
cluster")
class ClusterWithSslAndClientAuth {
@@ -262,11 +387,11 @@ public class ItSslTest extends IgniteIntegrationTest {
+ " clientAuth: \"require\",\n"
+ " trustStore: {\n"
+ " password: \"" + password + "\","
- + " path: \"" + trustStorePath + "\""
+ + " path: \"" + escapeWindowsPath(trustStorePath) + "\""
+ " },\n"
+ " keyStore: {\n"
+ " password: \"" + password + "\","
- + " path: \"" + keyStorePath + "\""
+ + " path: \"" + escapeWindowsPath(keyStorePath) + "\""
+ " }\n"
+ " },\n"
+ " port: 3365,\n"
@@ -279,13 +404,13 @@ public class ItSslTest extends IgniteIntegrationTest {
+ " enabled: true, "
+ " clientAuth: \"require\", "
+ " keyStore: {\n"
- + " path: \"" + keyStorePath + "\",\n"
+ + " path: \"" + escapeWindowsPath(keyStorePath) + "\",\n"
+ " password: \"" + password + "\"\n"
+ " }, \n"
+ " trustStore: {\n"
+ " type: JKS,"
+ " password: \"" + password + "\","
- + " path: \"" + trustStorePath + "\""
+ + " path: \"" + escapeWindowsPath(trustStorePath) + "\""
+ " }\n"
+ " }\n"
+ "}";
@@ -387,4 +512,66 @@ public class ItSslTest extends IgniteIntegrationTest {
}
}
}
+
+ @Test
+ @DisplayName("Cluster is not initialized when nodes are configured with
incompatible ciphers")
+ void incompatibleCiphersNodes(@WorkDirectory Path workDir, TestInfo
testInfo) {
+ Cluster cluster = new Cluster(testInfo, workDir);
+
+ String sslEnabledWithCipher1BoostrapConfig =
createBoostrapConfig("TLS_AES_256_GCM_SHA384");
+ String sslEnabledWithCipher2BoostrapConfig =
createBoostrapConfig("TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384");
+
+ CompletableFuture<IgniteImpl> node1 = cluster.startClusterNode(0,
sslEnabledWithCipher1BoostrapConfig);
+
+ String metaStorageAndCmgNodeName = testNodeName(testInfo, 0);
+
+ InitParameters initParameters = InitParameters.builder()
+ .destinationNodeName(metaStorageAndCmgNodeName)
+ .metaStorageNodeNames(List.of(metaStorageAndCmgNodeName))
+ .clusterName("cluster")
+ .build();
+
+ IgnitionManager.init(initParameters);
+
+ // First node will initialize the cluster with single node
successfully since the second node can't connect to it.
+ assertThat(node1, willCompleteSuccessfully());
+
+ CompletableFuture<IgniteImpl> node2 = cluster.startClusterNode(1,
sslEnabledWithCipher2BoostrapConfig);
+ assertThat(node2, willTimeoutIn(1, TimeUnit.SECONDS));
+
+ cluster.shutdown();
+ }
+
+ @Language("JSON")
+ private static String createBoostrapConfig(String ciphers) {
+ return "{\n"
+ + " network: {\n"
+ + " ssl : {"
+ + " enabled: true,\n"
+ + " ciphers: " + ciphers + ",\n"
+ + " trustStore: {\n"
+ + " password: \"" + password + "\","
+ + " path: \"" + escapeWindowsPath(trustStorePath) + "\""
+ + " },\n"
+ + " keyStore: {\n"
+ + " password: \"" + password + "\","
+ + " path: \"" + escapeWindowsPath(keyStorePath) + "\""
+ + " }\n"
+ + " },\n"
+ + " port: 3345,\n"
+ + " portRange: 2,\n"
+ + " nodeFinder:{\n"
+ + " netClusterNodes: [ \"localhost:3345\",
\"localhost:3346\" ]\n"
+ + " }\n"
+ + " },\n"
+ + " clientConnector.ssl: {\n"
+ + " enabled: true, "
+ + " ciphers: " + ciphers + ",\n"
+ + " keyStore: {\n"
+ + " path: \"" + escapeWindowsPath(keyStorePath) + "\",\n"
+ + " password: \"" + password + "\"\n"
+ + " }\n"
+ + " }\n"
+ + "}";
+ }
}