Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add mutual SSL properties #4663

Merged
merged 6 commits into from
Nov 15, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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