Skip to content

Commit

Permalink
Merge pull request #4663 from CSTDev/add-client-auth-config
Browse files Browse the repository at this point in the history
Add mutual SSL properties
  • Loading branch information
sberyozkin authored Nov 15, 2019
2 parents 84f60ed + 4aa4e70 commit f590c58
Show file tree
Hide file tree
Showing 16 changed files with 194 additions and 23 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import static org.hamcrest.core.Is.is;

import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;

import javax.enterprise.context.ApplicationScoped;
Expand All @@ -18,11 +17,15 @@
import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkus.test.QuarkusUnitTest;
import io.quarkus.test.common.http.TestHTTPResource;
import io.restassured.RestAssured;
import io.vertx.ext.web.Router;

public class SslServerWithJksTest {

@TestHTTPResource(value = "/ssl", ssl = true)
URL url;

@RegisterExtension
static final QuarkusUnitTest config = new QuarkusUnitTest()
.setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)
Expand All @@ -41,8 +44,7 @@ public static void restoreRestAssured() {
}

@Test
public void testSslServerWithJKS() throws MalformedURLException {
URL url = new URL("https://localhost:8444/ssl");
public void testSslServerWithJKS() {
RestAssured.get(url).then().statusCode(200).body(is("ssl"));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import static org.hamcrest.core.Is.is;

import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;

import javax.enterprise.context.ApplicationScoped;
Expand All @@ -18,11 +17,15 @@
import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkus.test.QuarkusUnitTest;
import io.quarkus.test.common.http.TestHTTPResource;
import io.restassured.RestAssured;
import io.vertx.ext.web.Router;

public class SslServerWithP12Test {

@TestHTTPResource(value = "/ssl", ssl = true)
URL url;

@RegisterExtension
static final QuarkusUnitTest config = new QuarkusUnitTest()
.setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)
Expand All @@ -41,8 +44,7 @@ public static void restoreRestAssured() {
}

@Test
public void testSslServerWithPkcs12() throws MalformedURLException {
URL url = new URL("https://localhost:8444/ssl");
public void testSslServerWithPkcs12() {
RestAssured.get(url).then().statusCode(200).body(is("ssl"));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import static org.hamcrest.core.Is.is;

import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;

import javax.enterprise.context.ApplicationScoped;
Expand All @@ -18,11 +17,15 @@
import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkus.test.QuarkusUnitTest;
import io.quarkus.test.common.http.TestHTTPResource;
import io.restassured.RestAssured;
import io.vertx.ext.web.Router;

public class SslServerWithPemTest {

@TestHTTPResource(value = "/ssl", ssl = true)
URL url;

@RegisterExtension
static final QuarkusUnitTest config = new QuarkusUnitTest()
.setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)
Expand All @@ -42,8 +45,7 @@ public static void restoreRestAssured() {
}

@Test
public void testSslServerWithPem() throws MalformedURLException {
URL url = new URL("https://localhost:8444/ssl");
public void testSslServerWithPem() {
RestAssured.get(url).then().statusCode(200).body(is("ssl"));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,23 @@ public class CertificateConfig {
*/
@ConfigItem(defaultValue = "password")
public String keyStorePassword;

/**
* An optional trust store which holds the certificate information of the certificates to trust
*/
@ConfigItem
public Optional<Path> trustStoreFile;

/**
* An optional parameter to specify type of the trust store file. If not given, the type is automatically detected
* based on the file name.
*/
@ConfigItem
public Optional<String> trustStoreFileType;

/**
* A parameter to specify the password of the trust store file.
*/
@ConfigItem
public Optional<String> trustStorePassword;
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import io.quarkus.runtime.annotations.ConfigGroup;
import io.quarkus.runtime.annotations.ConfigItem;
import io.quarkus.runtime.annotations.DefaultConverter;
import io.vertx.core.http.ClientAuth;

/**
* Shared configuration for setting up server-side SSL.
Expand All @@ -29,4 +30,11 @@ public class ServerSslConfig {
@ConfigItem(defaultValue = "TLSv1.3,TLSv1.2")
public List<String> protocols;

/**
* Configures the engine to require/request client authentication.
* NONE, REQUEST, REQUIRED
*/
@ConfigItem(defaultValue = "NONE")
public ClientAuth clientAuth;

}
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,8 @@ private static HttpServerOptions createSslOptions(HttpConfiguration httpConfigur
final Optional<Path> keyFile = sslConfig.certificate.keyFile;
final Optional<Path> keyStoreFile = sslConfig.certificate.keyStoreFile;
final String keystorePassword = sslConfig.certificate.keyStorePassword;
final Optional<Path> trustStoreFile = sslConfig.certificate.trustStoreFile;
final Optional<String> trustStorePassword = sslConfig.certificate.trustStorePassword;
final HttpServerOptions serverOptions = new HttpServerOptions();
serverOptions.setMaxHeaderSize(httpConfiguration.limits.maxHeaderSize.asBigInteger().intValueExact());
setIdleTimeout(httpConfiguration, serverOptions);
Expand All @@ -309,13 +311,7 @@ private static HttpServerOptions createSslOptions(HttpConfiguration httpConfigur
if (keyStoreFileType.isPresent()) {
type = keyStoreFileType.get().toLowerCase();
} else {
final String pathName = keyStorePath.toString();
if (pathName.endsWith(".p12") || pathName.endsWith(".pkcs12") || pathName.endsWith(".pfx")) {
type = "pkcs12";
} else {
// assume jks
type = "jks";
}
type = findKeystoreFileType(keyStorePath);
}

byte[] data = getFileContent(keyStorePath);
Expand Down Expand Up @@ -343,6 +339,22 @@ private static HttpServerOptions createSslOptions(HttpConfiguration httpConfigur
return null;
}

if (trustStoreFile.isPresent()) {
if (!trustStorePassword.isPresent()) {
throw new IllegalArgumentException("No trust store password provided");
}
final String type;
final Optional<String> trustStoreFileType = sslConfig.certificate.trustStoreFileType;
final Path trustStoreFilePath = trustStoreFile.get();
if (trustStoreFileType.isPresent()) {
type = trustStoreFileType.get().toLowerCase();
} else {
type = findKeystoreFileType(trustStoreFilePath);
}
createTrustStoreOptions(trustStoreFilePath, trustStorePassword.get(), type,
serverOptions);
}

for (String cipher : sslConfig.cipherSuites) {
if (!cipher.isEmpty()) {
serverOptions.addEnabledCipherSuite(cipher);
Expand All @@ -357,6 +369,7 @@ private static HttpServerOptions createSslOptions(HttpConfiguration httpConfigur
serverOptions.setSsl(true);
serverOptions.setHost(httpConfiguration.host);
serverOptions.setPort(httpConfiguration.determineSslPort(launchMode));
serverOptions.setClientAuth(sslConfig.clientAuth);
return serverOptions;
}

Expand Down Expand Up @@ -385,6 +398,40 @@ private static void createPemKeyCertOptions(Path certFile, Path keyFile,
serverOptions.setPemKeyCertOptions(pemKeyCertOptions);
}

private static void createTrustStoreOptions(Path trustStoreFile, String trustStorePassword,
String trustStoreFileType, HttpServerOptions serverOptions) throws IOException {
byte[] data = getFileContent(trustStoreFile);
switch (trustStoreFileType) {
case "pkcs12": {
PfxOptions options = new PfxOptions()
.setPassword(trustStorePassword)
.setValue(Buffer.buffer(data));
serverOptions.setPfxTrustOptions(options);
break;
}
case "jks": {
JksOptions options = new JksOptions()
.setPassword(trustStorePassword)
.setValue(Buffer.buffer(data));
serverOptions.setTrustStoreOptions(options);
break;
}
default:
throw new IllegalArgumentException(
"Unknown truststore type: " + trustStoreFileType + " valid types are jks or pkcs12");
}
}

private static String findKeystoreFileType(Path storePath) {
final String pathName = storePath.toString();
if (pathName.endsWith(".p12") || pathName.endsWith(".pkcs12") || pathName.endsWith(".pfx")) {
return "pkcs12";
} else {
// assume jks
return "jks";
}
}

private static byte[] doRead(InputStream is) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
byte[] buf = new byte[1024];
Expand Down
7 changes: 7 additions & 0 deletions integration-tests/vertx-http/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,14 @@
<configuration>
<cleanupServer>true</cleanupServer>
<enableHttpUrlHandler>true</enableHttpUrlHandler>
<!-- Include the server certificate -->
<additionalBuildArgs>
<additionalBuildArg>-H:IncludeResources=.*\.jks</additionalBuildArg>
<additionalBuildArg>-H:EnableURLProtocols=http,https</additionalBuildArg>
</additionalBuildArgs>
<graalvmHome>${graalvmHome}</graalvmHome>
<enableJni>true</enableJni>
<enableAllSecurityServices>true</enableAllSecurityServices>
</configuration>
</execution>
</executions>
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1,6 @@
vertx.event-loops.size=2
vertx.event-loops.size=2
quarkus.http.ssl.certificate.key-store-file=server-keystore.jks
quarkus.http.ssl.certificate.key-store-password=password
quarkus.http.ssl.certificate.trust-store-file=server-truststore.jks
quarkus.http.ssl.certificate.trust-store-password=password
quarkus.http.ssl.client-auth=REQUIRED
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -1,8 +1,44 @@
package io.quarkus.it.vertx;

import static io.restassured.RestAssured.given;
import static org.hamcrest.Matchers.containsString;

import java.security.Provider;
import java.security.Security;

import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;

import io.quarkus.test.junit.NativeImageTest;
import io.restassured.builder.RequestSpecBuilder;
import io.restassured.specification.RequestSpecification;

@NativeImageTest
public class VertxProducerResourceIT extends VertxProducerResourceTest {

}
private static Provider sunECProvider;

@BeforeAll
public static void setupSecProvider() {
//Remove SunEC provider for the test as it's not being provided for tests.
sunECProvider = Security.getProvider("SunEC");
Security.removeProvider("SunEC");
}

@AfterAll
public static void restoreSecProvider() {
Security.addProvider(sunECProvider);
}

@Test
public void testRouteRegistrationMTLS() {
RequestSpecification spec = new RequestSpecBuilder()
.setBaseUri(String.format("%s://%s", url.getProtocol(), url.getHost()))
.setPort(8443)
.setKeyStore("client-keystore.jks", "password")
.setTrustStore("client-truststore.jks", "password")
.build();
given().spec(spec).get("/my-path").then().body(containsString("OK"));
}
}
Original file line number Diff line number Diff line change
@@ -1,25 +1,34 @@
package io.quarkus.it.vertx;

import static io.restassured.RestAssured.get;
import static io.restassured.RestAssured.given;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.is;

import java.net.URL;

import org.junit.jupiter.api.Test;

import io.quarkus.test.common.http.TestHTTPResource;
import io.quarkus.test.junit.DisabledOnNativeImage;
import io.quarkus.test.junit.QuarkusTest;
import io.restassured.RestAssured;
import io.restassured.builder.RequestSpecBuilder;
import io.restassured.specification.RequestSpecification;

@QuarkusTest
public class VertxProducerResourceTest {

@TestHTTPResource(ssl = true)
URL url;

@Test
public void testInjection() {
get("/").then().body(containsString("vert.x has been injected"));
}

@Test
public void testInjectedRouter() {
RestAssured.given().contentType("text/plain").body("Hello world!")
given().contentType("text/plain").body("Hello world!")
.post("/").then().body(is("Hello world!"));
}

Expand All @@ -28,4 +37,16 @@ public void testRouteRegistration() {
get("/my-path").then().body(containsString("OK"));
}

@DisabledOnNativeImage
@Test
public void testRouteRegistrationMTLS() {
RequestSpecification spec = new RequestSpecBuilder()
.setBaseUri(String.format("%s://%s", url.getProtocol(), url.getHost()))
.setPort(url.getPort())
.setKeyStore("client-keystore.jks", "password")
.setTrustStore("client-truststore.jks", "password")
.build();
given().spec(spec).get("/my-path").then().body(containsString("OK"));
}

}
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,10 @@
* @return The path part of the URL
*/
String value() default "";

/**
*
* @return If the URL should use the HTTPS protocol and SSL port
*/
boolean ssl() default false;
}
Loading

0 comments on commit f590c58

Please sign in to comment.