Skip to content

Commit

Permalink
IGNITE-18828 Add ciphers support to SSL (jdbc, client, scalecube) (ap…
Browse files Browse the repository at this point in the history
  • Loading branch information
valepakh authored and lowka committed Mar 18, 2023
1 parent 959abe6 commit f25ee38
Show file tree
Hide file tree
Showing 17 changed files with 436 additions and 69 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -58,6 +60,12 @@ public SslConfigurationBuilder clientAuth(@Nullable ClientAuthenticationMode cli
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;
Expand Down Expand Up @@ -107,7 +115,9 @@ public SslConfigurationBuilder trustStoreType(@Nullable String trustStoreType) {
/** 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
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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,
Expand All @@ -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;
Expand All @@ -72,6 +76,11 @@ public ClientAuthenticationMode clientAuthenticationMode() {
return clientAuth;
}

@Override
public @Nullable Iterable<String> ciphers() {
return ciphers;
}

/** {@inheritDoc} */
@Override
public @Nullable String keyStorePath() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@
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.
Expand Down Expand Up @@ -105,6 +104,7 @@ private void setupSsl(SocketChannel ch, IgniteClientConfiguration clientCfg) {
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));
Expand All @@ -119,7 +119,6 @@ private void setupSsl(SocketChannel ch, IgniteClientConfiguration clientCfg) {

}

@NotNull
private static KeyManagerFactory loadKeyManagerFactory(SslConfiguration ssl)
throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException, UnrecoverableKeyException {
KeyStore ks = KeyStore.getInstance(ssl.keyStoreType());
Expand All @@ -138,7 +137,6 @@ private static KeyManagerFactory loadKeyManagerFactory(SslConfiguration ssl)
return keyManagerFactory;
}

@NotNull
private static TrustManagerFactory loadTrustManagerFactory(SslConfiguration ssl)
throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException {
KeyStore ts = KeyStore.getInstance(ssl.trustStoreType());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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())
Expand All @@ -125,7 +129,7 @@ private static String[] clientAuthValues() {
/** Properties array. */
private final ConnectionProperty[] propsArray = {
qryTimeout, connTimeout, trustStorePath, trustStorePassword, trustStoreType,
sslEnabled, clientAuth, keyStorePath, keyStorePassword, keyStoreType
sslEnabled, clientAuth, ciphers, keyStorePath, keyStorePassword, keyStoreType
};

/** {@inheritDoc} */
Expand Down Expand Up @@ -334,6 +338,17 @@ public ClientAuthenticationMode getClientAuth() {
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.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,7 @@ public JdbcConnection(ConnectionProperties props) throws SQLException {
.trustStorePath(connProps.getTrustStorePath())
.trustStorePassword(connProps.getTrustStorePassword())
.clientAuth(connProps.getClientAuth())
.ciphers(connProps.getCiphers())
.keyStoreType(connProps.getKeyStoreType())
.keyStorePath(connProps.getKeyStorePath())
.keyStorePassword(connProps.getKeyStorePassword())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,23 @@

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.
Expand All @@ -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();
Expand All @@ -48,6 +60,10 @@ public void validate(SslConfigurationValidator annotation, ValidationContext<Abs
} catch (IllegalArgumentException e) {
ctx.addIssue(new ValidationIssue(ctx.currentKey(), "Incorrect client auth parameter " + ssl.clientAuth()));
}

if (!ssl.ciphers().isBlank()) {
validateCiphers(ctx, ssl);
}
}
}

Expand All @@ -73,4 +89,23 @@ private static void validateKeyStore(ValidationContext<AbstractSslView> ctx, Str
}
}
}

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);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@
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;
Expand All @@ -46,6 +49,8 @@ public static SslContext createClientSslContext(SslView ssl) {

var builder = SslContextBuilder.forClient().trustManager(trustManagerFactory);

setCiphers(builder, ssl);

ClientAuth clientAuth = ClientAuth.valueOf(ssl.clientAuth().toUpperCase());
if (ClientAuth.NONE == clientAuth) {
return builder.build();
Expand All @@ -72,6 +77,8 @@ public static SslContext createServerSslContext(SslView ssl) {

var builder = SslContextBuilder.forServer(keyManagerFactory);

setCiphers(builder, ssl);

ClientAuth clientAuth = ClientAuth.valueOf(ssl.clientAuth().toUpperCase());
if (ClientAuth.NONE == clientAuth) {
return builder.build();
Expand All @@ -89,4 +96,13 @@ public static SslContext createServerSslContext(SslView ssl) {
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);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,20 @@ public void blankKeyStoreType() {
"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"),
Expand Down Expand Up @@ -83,15 +97,15 @@ public void blankTrustStoreType(@WorkDirectory Path workDir) throws IOException
@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");
}

@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
Expand All @@ -109,13 +123,13 @@ private static void validate(AbstractSslView config, String ... errorMessagePref
}

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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand All @@ -43,6 +45,11 @@ public String clientAuth() {
return clientAuth;
}

@Override
public String ciphers() {
return ciphers;
}

@Override
public KeyStoreView keyStore() {
return keyStore;
Expand Down
Loading

0 comments on commit f25ee38

Please sign in to comment.