Skip to content

Commit

Permalink
Merge pull request quarkusio#17036 from cescoffier/http-sni
Browse files Browse the repository at this point in the history
Add support for SNI (Server Name Indication) for the HTTP server
  • Loading branch information
cescoffier authored May 7, 2021
2 parents 0bd6c39 + 272119d commit a398a88
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 10 deletions.
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;
Expand All @@ -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.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
}

Expand Down

0 comments on commit a398a88

Please sign in to comment.