Skip to content

Commit

Permalink
IGNITE-18828 Add cyphers support to SSL (jdbc, client, scalecube)
Browse files Browse the repository at this point in the history
  • Loading branch information
valepakh committed Mar 10, 2023
1 parent 4112618 commit 9dad12c
Show file tree
Hide file tree
Showing 13 changed files with 247 additions and 42 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,7 @@ 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 @@ -26,6 +26,7 @@
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.util.Arrays;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.TrustManagerFactory;
import org.apache.ignite.internal.network.configuration.SslView;
Expand All @@ -46,6 +47,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 +75,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 +94,11 @@ 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()) {
String[] ciphers = ssl.ciphers().split(",");
builder.ciphers(Arrays.asList(ciphers));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ public String clientAuth() {
return clientAuth;
}

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

@Override
public KeyStoreView keyStore() {
return keyStore;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,8 @@ private Micronaut buildMicronautContext(int portCandidate, int sslPortCandidate)
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);
}

Expand All @@ -191,14 +192,6 @@ private void setFactories(Micronaut micronaut) {
}
}

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();
Expand All @@ -207,33 +200,30 @@ private Map<String, Object> serverProperties(int port, int sslPort) {
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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,8 @@ public CompletableFuture<Ignite> igniteNodeFuture() {
}

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"
Expand All @@ -130,23 +132,23 @@ private String bootstrapCfg() {
+ " clientAuth: " + (sslClientAuthEnabled ? "require" : "none") + ",\n"
+ " port: " + httpsPort + ",\n"
+ " keyStore: {\n"
+ " path: \"" + getResourcePath(keyStorePath) + "\",\n"
+ " path: \"" + keyStoreFilePath + "\",\n"
+ " password: " + keyStorePassword + "\n"
+ " }, \n"
+ " trustStore: {\n"
+ " type: JKS,\n"
+ " path: \"" + getResourcePath(trustStorePath) + "\",\n"
+ " path: \"" + trustStoreFilePath + "\",\n"
+ " password: " + trustStorePassword + "\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ "}";
}

private static String getResourcePath(String resource) {
/** Converts URL gotten from classloader to proper file system path and escape backslashes so it could be used in the config. */
public static String getResourcePath(URL url) {
try {
URL url = ItRestSslTest.class.getClassLoader().getResource(resource);
Objects.requireNonNull(url, "Resource " + resource + " not found.");
Objects.requireNonNull(url);
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
} catch (URISyntaxException e) {
Expand Down
Loading

0 comments on commit 9dad12c

Please sign in to comment.