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 support for SNI (Server Name Indication) for the HTTP server #17036

Merged
merged 1 commit into from
May 7, 2021
Merged
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
@@ -1,6 +1,6 @@
# Enable SSL, configure the key store
quarkus.http.ssl.certificate.file=server-cert.pem
quarkus.http.ssl.certificate.key-file=server-key.pem
quarkus.http.ssl.certificate.files=server-cert.pem
quarkus.http.ssl.certificate.key-files=server-key.pem
# Test that server starts with this option
# See https://github.com/quarkusio/quarkus/issues/8336
quarkus.http.insecure-requests=disabled
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package io.quarkus.vertx.http.runtime;

import java.nio.file.Path;
import java.util.List;
import java.util.Optional;

import io.quarkus.runtime.annotations.ConfigGroup;
@@ -10,19 +11,43 @@
* A certificate configuration. Either the certificate and key files must be given, or a key store must be given.
*/
@ConfigGroup
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
public class CertificateConfig {

/**
* The file path to a server certificate or certificate chain in PEM format.
*
* @deprecated Use {@link #files} instead.
*/
@ConfigItem
@Deprecated
public Optional<Path> file;

/**
* The list of path to server certificates using the PEM format.
* Specifying multiple files require SNI to be enabled.
*/
@ConfigItem
public Optional<List<Path>> files;

/**
* The file path to the corresponding certificate private key file in PEM format.
*
* @deprecated Use {@link #keyFiles} instead.
*/
@ConfigItem
@Deprecated
public Optional<Path> keyFile;

/**
* The list of path to server certificates private key file using the PEM format.
* Specifying multiple files require SNI to be enabled.
*
* The order of the key files must match the order of the certificates.
*/
@ConfigItem
public Optional<List<Path>> keyFiles;

/**
* An optional key store which holds the certificate information instead of specifying separate files.
*/
Original file line number Diff line number Diff line change
@@ -30,4 +30,11 @@ public class ServerSslConfig {
@ConfigItem(defaultValue = "TLSv1.3,TLSv1.2")
public List<String> protocols;

/**
* Enables Server Name Indication (SNI), an TLS extension allowing the server to use multiple certificates.
* The client indicate the server name during the TLS handshake, allowing the server to select the right certificate.
*/
@ConfigItem(defaultValue = "false")
public boolean sni;

}
Original file line number Diff line number Diff line change
@@ -53,6 +53,7 @@
import io.quarkus.runtime.ShutdownContext;
import io.quarkus.runtime.annotations.Recorder;
import io.quarkus.runtime.configuration.ConfigInstantiator;
import io.quarkus.runtime.configuration.ConfigurationException;
import io.quarkus.runtime.configuration.MemorySize;
import io.quarkus.runtime.shutdown.ShutdownConfig;
import io.quarkus.vertx.core.runtime.VertxCoreRecorder;
@@ -578,9 +579,24 @@ private static HttpServerOptions createSslOptions(HttpBuildTimeConfig buildTimeC
}

ServerSslConfig sslConfig = httpConfiguration.ssl;
//TODO: static fields break config

final Optional<Path> certFile = sslConfig.certificate.file;
final Optional<Path> keyFile = sslConfig.certificate.keyFile;
final List<Path> keys = new ArrayList<>();
final List<Path> certificates = new ArrayList<>();
if (sslConfig.certificate.keyFiles.isPresent()) {
keys.addAll(sslConfig.certificate.keyFiles.get());
}
if (sslConfig.certificate.files.isPresent()) {
certificates.addAll(sslConfig.certificate.files.get());
}
if (keyFile.isPresent()) {
keys.add(keyFile.get());
}
if (certFile.isPresent()) {
certificates.add(certFile.get());
}

final Optional<Path> keyStoreFile = sslConfig.certificate.keyStoreFile;
final String keystorePassword = sslConfig.certificate.keyStorePassword;
final Optional<Path> trustStoreFile = sslConfig.certificate.trustStoreFile;
@@ -599,8 +615,8 @@ private static HttpServerOptions createSslOptions(HttpBuildTimeConfig buildTimeC
serverOptions.setMaxFormAttributeSize(httpConfiguration.limits.maxFormAttributeSize.asBigInteger().intValueExact());
setIdleTimeout(httpConfiguration, serverOptions);

if (certFile.isPresent() && keyFile.isPresent()) {
createPemKeyCertOptions(certFile.get(), keyFile.get(), serverOptions);
if (!certificates.isEmpty() && !keys.isEmpty()) {
createPemKeyCertOptions(certificates, keys, serverOptions);
} else if (keyStoreFile.isPresent()) {
final Path keyStorePath = keyStoreFile.get();
final Optional<String> keyStoreFileType = sslConfig.certificate.keyStoreFileType;
@@ -649,6 +665,7 @@ private static HttpServerOptions createSslOptions(HttpBuildTimeConfig buildTimeC
}
}
serverOptions.setSsl(true);
serverOptions.setSni(sslConfig.sni);
serverOptions.setHost(httpConfiguration.host);
serverOptions.setPort(httpConfiguration.determineSslPort(launchMode));
serverOptions.setClientAuth(buildTimeConfig.tlsClientAuth);
@@ -676,13 +693,30 @@ private static byte[] getFileContent(Path path) throws IOException {
return data;
}

private static void createPemKeyCertOptions(Path certFile, Path keyFile,
private static void createPemKeyCertOptions(List<Path> certFile, List<Path> keyFile,
HttpServerOptions serverOptions) throws IOException {
final byte[] cert = getFileContent(certFile);
final byte[] key = getFileContent(keyFile);

if (certFile.size() != keyFile.size()) {
throw new ConfigurationException("Invalid certificate configuration - `files` and `keyFiles` must have the "
+ "same number of elements");
}

List<Buffer> certificates = new ArrayList<>();
List<Buffer> keys = new ArrayList<>();

for (Path p : certFile) {
final byte[] cert = getFileContent(p);
certificates.add(Buffer.buffer(cert));
}

for (Path p : keyFile) {
final byte[] key = getFileContent(p);
keys.add(Buffer.buffer(key));
}

PemKeyCertOptions pemKeyCertOptions = new PemKeyCertOptions()
.setCertValue(Buffer.buffer(cert))
.setKeyValue(Buffer.buffer(key));
.setCertValues(certificates)
.setKeyValues(keys);
serverOptions.setPemKeyCertOptions(pemKeyCertOptions);
}