This is an automated email from the ASF dual-hosted git repository.
ptupitsyn 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 9c48f83e7e IGNITE-18791 Add SSL Client authentication to REST (#1690)
9c48f83e7e is described below
commit 9c48f83e7e22e097a7db10937db57a2a87202d59
Author: Aleksandr <[email protected]>
AuthorDate: Tue Feb 21 11:47:47 2023 +0400
IGNITE-18791 Add SSL Client authentication to REST (#1690)
TCP Client and JDBC support this feature, now REST does it too.
---
modules/rest/build.gradle | 6 +
.../apache/ignite/internal/rest/RestComponent.java | 96 +++++++++++++--
.../configuration/RestSslConfigurationSchema.java | 15 +--
.../ignite/internal/rest/RestComponentTest.java | 134 +++++++++++++++++++++
.../ignite/internal/rest/TestController.java | 33 +++++
.../ignite/internal/rest/ItPortRangeTest.java | 1 +
.../ignite/internal/rest/ssl/ItRestSslTest.java | 87 +++++++++++--
.../apache/ignite/internal/rest/ssl/RestNode.java | 19 ++-
8 files changed, 356 insertions(+), 35 deletions(-)
diff --git a/modules/rest/build.gradle b/modules/rest/build.gradle
index 3e1dea30d4..c9b2951bd4 100644
--- a/modules/rest/build.gradle
+++ b/modules/rest/build.gradle
@@ -40,8 +40,14 @@ dependencies {
annotationProcessor libs.auto.service
+ testAnnotationProcessor libs.micronaut.inject.annotation.processor
+
testImplementation project(':ignite-configuration')
+ testImplementation(testFixtures(project(':ignite-core')))
+ testImplementation(testFixtures(project(':ignite-configuration')))
+ testImplementation libs.hamcrest.core
testImplementation libs.slf4j.jdk14
+
}
compileJava {
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 d5fc16c084..755a253f1d 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
@@ -21,9 +21,11 @@ import static
io.micronaut.context.env.Environment.BARE_METAL;
import io.micronaut.context.ApplicationContext;
import io.micronaut.http.server.exceptions.ServerStartupException;
+import io.micronaut.http.ssl.ClientAuthentication;
import io.micronaut.openapi.annotation.OpenAPIInclude;
import io.micronaut.runtime.Micronaut;
import io.micronaut.runtime.exceptions.ApplicationStartupException;
+import io.netty.handler.ssl.ClientAuth;
import io.swagger.v3.oas.annotations.OpenAPIDefinition;
import io.swagger.v3.oas.annotations.info.Contact;
import io.swagger.v3.oas.annotations.info.Info;
@@ -31,6 +33,9 @@ import io.swagger.v3.oas.annotations.info.License;
import java.net.BindException;
import java.net.InetAddress;
import java.net.UnknownHostException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.ignite.internal.logger.IgniteLogger;
@@ -43,8 +48,11 @@ import
org.apache.ignite.internal.rest.api.configuration.NodeConfigurationApi;
import org.apache.ignite.internal.rest.api.metric.NodeMetricApi;
import org.apache.ignite.internal.rest.api.node.NodeManagementApi;
import org.apache.ignite.internal.rest.configuration.RestConfiguration;
+import org.apache.ignite.internal.rest.configuration.RestSslConfiguration;
import org.apache.ignite.internal.rest.configuration.RestSslView;
import org.apache.ignite.internal.rest.configuration.RestView;
+import org.apache.ignite.lang.ErrorGroups.Common;
+import org.apache.ignite.lang.IgniteException;
import org.apache.ignite.lang.IgniteInternalException;
import org.jetbrains.annotations.Nullable;
@@ -147,7 +155,7 @@ public class RestComponent implements IgniteComponent {
+ " [HTTP ports=[" + desiredHttpPort + ", " + desiredHttpPort
+ portRange + "]],"
+ " [HTTPS ports=[" + desiredHttpsPort + ", " +
desiredHttpsPort + httpsPortRange + "]]";
- throw new RuntimeException(msg);
+ throw new IgniteException(Common.UNEXPECTED_ERR, msg);
}
/** Starts Micronaut application using the provided ports.
@@ -171,7 +179,7 @@ public class RestComponent implements IgniteComponent {
if (bindException != null) {
return false;
}
- throw new RuntimeException(e);
+ throw new IgniteException(Common.UNEXPECTED_ERR, e);
}
}
@@ -212,14 +220,20 @@ public class RestComponent implements IgniteComponent {
}
private Map<String, Object> properties(int port, int sslPort) {
- boolean dualProtocol = restConfiguration.dualProtocol().value();
- boolean sslEnabled = restConfiguration.ssl().enabled().value();
- String keyStoreType =
restConfiguration.ssl().keyStore().type().value();
- String keyStorePath =
restConfiguration.ssl().keyStore().path().value();
- String keyStorePassword =
restConfiguration.ssl().keyStore().password().value();
+ RestSslConfiguration sslCfg = restConfiguration.ssl();
+ boolean sslEnabled = sslCfg.enabled().value();
if (sslEnabled) {
- return Map.of(
+ String keyStorePath = sslCfg.keyStore().path().value();
+ // todo: replace with configuration-level validation
https://issues.apache.org/jira/browse/IGNITE-18850
+ validateKeyStorePath(keyStorePath);
+
+ String keyStoreType = sslCfg.keyStore().type().value();
+ String keyStorePassword = sslCfg.keyStore().password().value();
+
+ 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,
@@ -228,11 +242,77 @@ public class RestComponent implements IgniteComponent {
"micronaut.server.ssl.key-store.password",
keyStorePassword,
"micronaut.server.ssl.key-store.type", keyStoreType
);
+
+ ClientAuth clientAuth =
ClientAuth.valueOf(sslCfg.clientAuth().value().toUpperCase());
+ if (ClientAuth.NONE == clientAuth) {
+ return micronautSslConfig;
+ }
+
+
+ String trustStorePath = sslCfg.trustStore().path().value();
+ // todo: replace with configuration-level validation
https://issues.apache.org/jira/browse/IGNITE-18850
+ validateTrustStore(trustStorePath);
+
+ String trustStoreType = sslCfg.trustStore().type().value();
+ String trustStorePassword = sslCfg.trustStore().password().value();
+
+ Map<String, Object> micronautClientAuthConfig = Map.of(
+ "micronaut.server.ssl.client-authentication",
toMicronautClientAuth(clientAuth),
+ "micronaut.server.ssl.trust-store.path", "file:" +
trustStorePath,
+ "micronaut.server.ssl.trust-store.password",
trustStorePassword,
+ "micronaut.server.ssl.trust-store.type", trustStoreType
+ );
+
+ HashMap<String, Object> result = new HashMap<>();
+ result.putAll(micronautSslConfig);
+ result.putAll(micronautClientAuthConfig);
+
+ return result;
} else {
return Map.of("micronaut.server.port", port);
}
}
+ private static void validateKeyStorePath(String keyStorePath) {
+ if (keyStorePath.trim().isEmpty()) {
+ throw new IgniteException(
+ Common.SSL_CONFIGURATION_ERR,
+ "Trust store path is not configured. Please check your
rest.ssl.keyStore.path configuration."
+ );
+ }
+
+ if (!Files.exists(Path.of(keyStorePath))) {
+ throw new IgniteException(
+ Common.SSL_CONFIGURATION_ERR,
+ "Trust store file not found: " + keyStorePath + ". Please
check your rest.ssl.keyStore.path configuration."
+ );
+ }
+ }
+
+ private static void validateTrustStore(String trustStorePath) {
+ if (trustStorePath.trim().isEmpty()) {
+ throw new IgniteException(
+ Common.SSL_CONFIGURATION_ERR,
+ "Key store path is not configured. Please check your
rest.ssl.trustStore.path configuration."
+ );
+ }
+
+ if (!Files.exists(Path.of(trustStorePath))) {
+ throw new IgniteException(
+ Common.SSL_CONFIGURATION_ERR,
+ "Key store file not found: " + trustStorePath + ". Please
check your rest.ssl.trustStore.path configuration."
+ );
+ }
+ }
+
+ private String toMicronautClientAuth(ClientAuth clientAuth) {
+ switch (clientAuth) {
+ case OPTIONAL: return
ClientAuthentication.WANT.name().toLowerCase();
+ case REQUIRE: return
ClientAuthentication.NEED.name().toLowerCase();
+ default: throw new IllegalArgumentException("Can not convert " +
clientAuth.name() + " to micronaut type");
+ }
+ }
+
/** {@inheritDoc} */
@Override
public synchronized void stop() throws Exception {
diff --git
a/modules/rest/src/main/java/org/apache/ignite/internal/rest/configuration/RestSslConfigurationSchema.java
b/modules/rest/src/main/java/org/apache/ignite/internal/rest/configuration/RestSslConfigurationSchema.java
index 00da64cad5..dbd2908d41 100644
---
a/modules/rest/src/main/java/org/apache/ignite/internal/rest/configuration/RestSslConfigurationSchema.java
+++
b/modules/rest/src/main/java/org/apache/ignite/internal/rest/configuration/RestSslConfigurationSchema.java
@@ -18,19 +18,13 @@
package org.apache.ignite.internal.rest.configuration;
import org.apache.ignite.configuration.annotation.Config;
-import org.apache.ignite.configuration.annotation.ConfigValue;
import org.apache.ignite.configuration.annotation.Value;
import org.apache.ignite.configuration.validation.Range;
-import
org.apache.ignite.internal.network.configuration.KeyStoreConfigurationSchema;
-import
org.apache.ignite.internal.network.configuration.KeyStoreConfigurationValidator;
+import
org.apache.ignite.internal.network.configuration.AbstractSslConfigurationSchema;
/** REST SSL configuration. */
@Config
-public class RestSslConfigurationSchema {
-
- /** Whether SSL is enabled. */
- @Value(hasDefault = true)
- public final boolean enabled = false;
+public class RestSslConfigurationSchema extends AbstractSslConfigurationSchema
{
/** SSL port. */
@Range(min = 1024, max = 0xFFFF)
@@ -41,9 +35,4 @@ public class RestSslConfigurationSchema {
@Range(min = 0)
@Value(hasDefault = true)
public final int portRange = 100;
-
- /** SSL keystore. */
- @KeyStoreConfigurationValidator
- @ConfigValue
- public KeyStoreConfigurationSchema keyStore;
}
diff --git
a/modules/rest/src/test/java/org/apache/ignite/internal/rest/RestComponentTest.java
b/modules/rest/src/test/java/org/apache/ignite/internal/rest/RestComponentTest.java
new file mode 100644
index 0000000000..a1eaa2d6fd
--- /dev/null
+++
b/modules/rest/src/test/java/org/apache/ignite/internal/rest/RestComponentTest.java
@@ -0,0 +1,134 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.rest;
+
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import io.netty.handler.ssl.util.SelfSignedCertificate;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.net.URI;
+import java.net.http.HttpClient;
+import java.net.http.HttpRequest;
+import java.net.http.HttpResponse;
+import java.net.http.HttpResponse.BodyHandlers;
+import java.nio.file.Path;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.util.List;
+import
org.apache.ignite.internal.configuration.testframework.ConfigurationExtension;
+import
org.apache.ignite.internal.configuration.testframework.InjectConfiguration;
+import org.apache.ignite.internal.rest.configuration.RestConfiguration;
+import org.apache.ignite.internal.testframework.WorkDirectory;
+import org.apache.ignite.internal.testframework.WorkDirectoryExtension;
+import org.apache.ignite.lang.ErrorGroups.Common;
+import org.apache.ignite.lang.IgniteException;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+
+@ExtendWith(ConfigurationExtension.class)
+@ExtendWith(WorkDirectoryExtension.class)
+class RestComponentTest {
+
+ private String keyStorePath;
+
+ @WorkDirectory
+ private Path workDir;
+
+ private static void pingOk(RestComponent component) throws IOException,
InterruptedException {
+ HttpClient client = HttpClient.newBuilder().build();
+ HttpResponse<String> response = client.send(
+ HttpRequest.newBuilder()
+ .uri(URI.create("http://" + component.host() + ":" +
component.httpPort() + "/ping")).GET()
+ .build(),
+ BodyHandlers.ofString()
+ );
+
+ assertThat(response.statusCode(), is(200));
+ assertThat(response.body(), is("pong"));
+ }
+
+ @BeforeEach
+ void setUp() {
+ keyStorePath =
workDir.resolve("keystore.p12").toAbsolutePath().toString();
+ }
+
+ @Test
+ @DisplayName("REST component stars with default configuration")
+ void defaultConfiguration(@InjectConfiguration RestConfiguration
restConfiguration) throws Exception {
+ // Given
+ RestComponent component = new RestComponent(List.of(),
restConfiguration);
+
+ // When
+ component.start();
+
+ // Then
+ pingOk(component);
+ }
+
+ @Test
+ @DisplayName("REST component does not start with ssl.enabled=true and no
keystore")
+ void sslConfiguration(@InjectConfiguration("mock.ssl.enabled: true")
RestConfiguration restConfiguration) {
+ // Given
+ RestComponent component = new RestComponent(List.of(),
restConfiguration);
+
+ // When
+ IgniteException thrown = assertThrows(IgniteException.class,
component::start);
+
+ // Then
+ assertThat(thrown.code(), is(Common.SSL_CONFIGURATION_ERR));
+ }
+
+ @Test
+ @DisplayName("REST component does not start with ssl.clientAuth=require
and no truststore")
+ void clientAuthConfiguration(@InjectConfiguration RestConfiguration
restConfiguration) throws Exception {
+ // Given correct keystore
+ generateKeystore(new SelfSignedCertificate());
+ restConfiguration.ssl().enabled().update(true).get();
+ restConfiguration.ssl().keyStore().path().update(keyStorePath).get();
+ restConfiguration.ssl().keyStore().password().update("changeit").get();
+ // And clientAuth=require But no truststore
+ restConfiguration.ssl().clientAuth().update("require").get();
+
+ RestComponent component = new RestComponent(List.of(),
restConfiguration);
+
+ // When
+ IgniteException thrown = assertThrows(IgniteException.class,
component::start);
+
+ // Then
+ assertThat(thrown.code(), is(Common.SSL_CONFIGURATION_ERR));
+ }
+
+ private void generateKeystore(SelfSignedCertificate cert)
+ throws KeyStoreException, IOException, NoSuchAlgorithmException,
CertificateException {
+ KeyStore ks = KeyStore.getInstance("PKCS12");
+ ks.load(null, null);
+ ks.setKeyEntry("key", cert.key(), null, new
Certificate[]{cert.cert()});
+ try (FileOutputStream fos = new FileOutputStream(keyStorePath)) {
+ ks.store(fos, "changeit".toCharArray());
+ }
+ }
+}
diff --git
a/modules/rest/src/test/java/org/apache/ignite/internal/rest/TestController.java
b/modules/rest/src/test/java/org/apache/ignite/internal/rest/TestController.java
new file mode 100644
index 0000000000..214c22d401
--- /dev/null
+++
b/modules/rest/src/test/java/org/apache/ignite/internal/rest/TestController.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.rest;
+
+import io.micronaut.http.annotation.Controller;
+import io.micronaut.http.annotation.Get;
+
+/**
+ * Test controller. Exposes a single ping endpoint.
+ */
+@Controller
+public class TestController {
+ @Get("ping")
+ public String ping() {
+ return "pong";
+ }
+
+}
diff --git
a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/rest/ItPortRangeTest.java
b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/rest/ItPortRangeTest.java
index fb5f7078ad..ed5e48165f 100644
---
a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/rest/ItPortRangeTest.java
+++
b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/rest/ItPortRangeTest.java
@@ -99,6 +99,7 @@ public class ItPortRangeTest {
10300,
10400,
true,
+ false,
true
))
.collect(Collectors.toList());
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 e2e608cf31..82e213b829 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
@@ -34,8 +34,10 @@ import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
+import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.util.stream.Stream;
+import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManagerFactory;
import org.apache.ignite.internal.testframework.WorkDirectory;
@@ -62,6 +64,12 @@ public class ItRestSslTest {
/** Trust store password. */
private static final String trustStorePassword = "changeit";
+ /** Key store path. */
+ private static final String keyStorePath = "ssl/keystore.p12";
+
+ /** Key store password. */
+ private static final String keyStorePassword = "changeit";
+
/** Path to the working directory. */
@WorkDirectory
private static Path workDir;
@@ -72,12 +80,17 @@ public class ItRestSslTest {
/** SSL HTTP client that is expected to be defined in subclasses. */
private static HttpClient sslClient;
+ /** SSL HTTP client that is expected to be defined in subclasses. */
+ private static HttpClient sslClientWithClientAuth;
+
private static RestNode httpNode;
private static RestNode httpsNode;
private static RestNode dualProtocolNode;
+ private static RestNode httpsWithClientAuthNode;
+
@BeforeAll
static void beforeAll(TestInfo testInfo) throws Exception {
@@ -88,11 +101,16 @@ public class ItRestSslTest {
.sslContext(sslContext())
.build();
- httpNode = new RestNode(workDir, testNodeName(testInfo, 3344), 3344,
10300, 10400, false, false);
- httpsNode = new RestNode(workDir, testNodeName(testInfo, 3345), 3345,
10301, 10401, true, false);
- dualProtocolNode = new RestNode(workDir, testNodeName(testInfo, 3346),
3346, 10302, 10402, true, true);
- Stream.of(httpNode, httpsNode, dualProtocolNode)
- .forEach(RestNode::start);
+ sslClientWithClientAuth = HttpClient.newBuilder()
+ .sslContext(sslContextWithClientAuth())
+ .build();
+
+ httpNode = new RestNode(workDir, testNodeName(testInfo, 3344), 3344,
10300, 10400, false, false, false);
+ httpsNode = new RestNode(workDir, testNodeName(testInfo, 3345), 3345,
10301, 10401, true, false, false);
+ dualProtocolNode = new RestNode(workDir, testNodeName(testInfo, 3346),
3346, 10302, 10402, true, false, true);
+ httpsWithClientAuthNode = new RestNode(workDir, testNodeName(testInfo,
3347), 3347, 10303, 10403, true, true, false);
+
+ Stream.of(httpNode, httpsNode, dualProtocolNode,
httpsWithClientAuthNode).forEach(RestNode::start);
}
@Test
@@ -143,7 +161,6 @@ public class ItRestSslTest {
// Then IOException
assertThrows(IOException.class, () -> client.send(request,
BodyHandlers.ofString()));
-
}
@Test
@@ -156,20 +173,64 @@ public class ItRestSslTest {
assertThrows(IOException.class, () -> client.send(request,
BodyHandlers.ofString()));
}
+ @Test
+ void httpsWithClientAuthProtocol(TestInfo testInfo) throws IOException,
InterruptedException {
+ // When GET /management/v1/configuration/node
+ URI uri = URI.create(httpsWithClientAuthNode.httpsAddress() +
"/management/v1/configuration/node");
+ HttpRequest request = HttpRequest.newBuilder(uri).build();
+
+ // Then response code is 200
+ HttpResponse<String> response = sslClientWithClientAuth.send(request,
BodyHandlers.ofString());
+ assertEquals(200, response.statusCode());
+ }
+
+ @Test
+ void httpsWithClientAuthProtocolButClientWithoutAuth(TestInfo testInfo)
throws IOException, InterruptedException {
+ // When GET /management/v1/configuration/node
+ URI uri = URI.create(httpsWithClientAuthNode.httpsAddress() +
"/management/v1/configuration/node");
+ HttpRequest request = HttpRequest.newBuilder(uri).build();
+
+ // Expect IOException for SSL client that does not configure client
auth
+ assertThrows(IOException.class, () -> sslClient.send(request,
BodyHandlers.ofString()));
+ }
+
@AfterAll
static void afterAll() {
- Stream.of(httpNode, httpsNode, dualProtocolNode)
- .forEach(RestNode::stop);
+ Stream.of(httpNode, httpsNode, dualProtocolNode,
httpsWithClientAuthNode).forEach(RestNode::stop);
}
- private static SSLContext sslContext()
- throws CertificateException, KeyStoreException, IOException,
NoSuchAlgorithmException, KeyManagementException {
- String path =
ItRestSslTest.class.getClassLoader().getResource(trustStorePath).getPath();
+ private static SSLContext sslContext() throws CertificateException,
KeyStoreException, IOException,
+ NoSuchAlgorithmException, KeyManagementException {
+
+ String tsPath =
ItRestSslTest.class.getClassLoader().getResource(trustStorePath).getPath();
+
+ KeyStore trustStore = KeyStore.getInstance(new File(tsPath),
trustStorePassword.toCharArray());
TrustManagerFactory trustManagerFactory =
TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
- KeyStore keyStore = KeyStore.getInstance(new File(path),
trustStorePassword.toCharArray());
- trustManagerFactory.init(keyStore);
+ trustManagerFactory.init(trustStore);
+
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, trustManagerFactory.getTrustManagers(), new
SecureRandom());
+
+ return sslContext;
+ }
+
+ private static SSLContext sslContextWithClientAuth() throws
CertificateException, KeyStoreException, IOException,
+ NoSuchAlgorithmException, KeyManagementException,
UnrecoverableKeyException {
+
+ String tsPath =
ItRestSslTest.class.getClassLoader().getResource(trustStorePath).getPath();
+ String ksPath =
ItRestSslTest.class.getClassLoader().getResource(keyStorePath).getPath();
+
+ KeyStore trustStore = KeyStore.getInstance(new File(tsPath),
trustStorePassword.toCharArray());
+ TrustManagerFactory trustManagerFactory =
TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
+ trustManagerFactory.init(trustStore);
+
+ KeyStore keyStore = KeyStore.getInstance(new File(ksPath),
keyStorePassword.toCharArray());
+ KeyManagerFactory keyManagerFactory =
KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
+ keyManagerFactory.init(keyStore, keyStorePassword.toCharArray());
+
+ SSLContext sslContext = SSLContext.getInstance("TLS");
+ sslContext.init(keyManagerFactory.getKeyManagers(),
trustManagerFactory.getTrustManagers(), new SecureRandom());
+
return sslContext;
}
}
diff --git
a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/rest/ssl/RestNode.java
b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/rest/ssl/RestNode.java
index ca19ddb61f..ca3f9cb06f 100644
---
a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/rest/ssl/RestNode.java
+++
b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/rest/ssl/RestNode.java
@@ -29,12 +29,19 @@ public class RestNode {
/** Key store password. */
private static final String keyStorePassword = "changeit";
+ /** Trust store path. */
+ private static final String trustStorePath = "ssl/truststore.jks";
+
+ /** Trust store password. */
+ private static final String trustStorePassword = "changeit";
+
private final Path workDir;
private final String name;
private final int networkPort;
private final int httpPort;
private final int httpsPort;
private final boolean sslEnabled;
+ private final boolean sslClientAuthEnabled;
private final boolean dualProtocol;
/** Constructor. */
@@ -45,6 +52,7 @@ public class RestNode {
int httpPort,
int httpsPort,
boolean sslEnabled,
+ boolean sslClientAuthEnabled,
boolean dualProtocol
) {
this.workDir = workDir;
@@ -53,6 +61,7 @@ public class RestNode {
this.httpPort = httpPort;
this.httpsPort = httpsPort;
this.sslEnabled = sslEnabled;
+ this.sslClientAuthEnabled = sslClientAuthEnabled;
this.dualProtocol = dualProtocol;
}
@@ -75,6 +84,8 @@ public class RestNode {
private String bootstrapCfg() {
String keyStoreAbsolutPath =
ItRestSslTest.class.getClassLoader().getResource(keyStorePath).getPath();
+ String trustStoreAbsolutPath =
ItRestSslTest.class.getClassLoader().getResource(trustStorePath).getPath();
+
return "{\n"
+ " network: {\n"
+ " port: " + networkPort + ",\n"
@@ -87,10 +98,16 @@ public class RestNode {
+ " dualProtocol: " + dualProtocol + ",\n"
+ " ssl: {\n"
+ " enabled: " + sslEnabled + ",\n"
+ + " clientAuth: " + (sslClientAuthEnabled ? "require" :
"none") + ",\n"
+ " port: " + httpsPort + ",\n"
+ " keyStore: {\n"
+ " path: " + keyStoreAbsolutPath + ",\n"
- + " password: " + keyStorePassword + "\n"
+ + " password: " + keyStorePassword + "\n"
+ + " }, \n"
+ + " trustStore: {\n"
+ + " type: JKS, "
+ + " path: " + trustStoreAbsolutPath + ",\n"
+ + " password: " + trustStorePassword + "\n"
+ " }\n"
+ " }\n"
+ " }"