Skip to content

Commit

Permalink
Merge pull request quarkusio#1213 from stuartwdouglas/ssl
Browse files Browse the repository at this point in the history
Initial Undertow SSL config
  • Loading branch information
stuartwdouglas authored Mar 6, 2019
2 parents 8571377 + 0943a0b commit c3b3359
Show file tree
Hide file tree
Showing 7 changed files with 104 additions and 39 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
Expand Down Expand Up @@ -67,6 +68,7 @@
import io.quarkus.runtime.configuration.InetAddressConverter;
import io.quarkus.runtime.configuration.InetSocketAddressConverter;
import io.quarkus.runtime.configuration.NameIterator;
import io.quarkus.runtime.configuration.PathConverter;
import io.quarkus.runtime.configuration.RegexConverter;
import io.quarkus.runtime.configuration.SimpleConfigurationProviderResolver;
import io.quarkus.runtime.configuration.ssl.CipherSuiteSelectorConverter;
Expand Down Expand Up @@ -150,6 +152,10 @@ public void setUpConverters(BuildProducer<ConfigurationCustomConverterBuildItem>
200,
Protocol.class,
ProtocolConverter.class));
configurationTypes.produce(new ConfigurationCustomConverterBuildItem(
200,
Path.class,
PathConverter.class));
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package io.quarkus.runtime.configuration;

import java.nio.file.Path;
import java.nio.file.Paths;

import org.eclipse.microprofile.config.spi.Converter;

public class PathConverter implements Converter<Path> {
@Override
public Path convert(String value) {
return Paths.get(value);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,6 @@
*/
@ConfigGroup
public class ServerSslConfig {

static final Logger log = Logger.getLogger("io.quarkus.configuration.ssl");

private static final Protocol[] NO_PROTOCOLS = new Protocol[0];
private static final X509Certificate[] NO_CERTS = new X509Certificate[0];

/**
* The server certificate configuration.
*/
Expand Down Expand Up @@ -88,12 +82,15 @@ public class ServerSslConfig {
* @throws GeneralSecurityException if something failed in the context setup
*/
public SSLContext toSSLContext() throws GeneralSecurityException, IOException {
//TODO: static fields break config
Logger log = Logger.getLogger("io.quarkus.configuration.ssl");
final Optional<Path> certFile = certificate.file;
final Optional<Path> keyFile = certificate.keyFile;
final Optional<Path> keyStoreFile = certificate.keyStoreFile;
final KeyStore keyStore;
if (certFile.isPresent() && keyFile.isPresent()) {
keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null, "password".toCharArray());
final Path certPath = certFile.get();
final Iterator<PemEntry<?>> certItr = Pem.parsePemContent(load(certPath));
final ArrayList<X509Certificate> certList = new ArrayList<>();
Expand Down Expand Up @@ -129,7 +126,8 @@ public SSLContext toSSLContext() throws GeneralSecurityException, IOException {
if (keyItr.hasNext()) {
log.warnf("Ignoring extra content in key file \"%s\"", keyPath);
}
keyStore.setEntry("default", new KeyStore.PrivateKeyEntry(privateKey, certList.toArray(NO_CERTS)), null);
keyStore.setEntry("default", new KeyStore.PrivateKeyEntry(privateKey, certList.toArray(new X509Certificate[0])),
new KeyStore.PasswordProtection("password".toCharArray()));
} else if (keyStoreFile.isPresent()) {
final Path keyStorePath = keyStoreFile.get();
final Optional<String> keyStoreFileType = certificate.keyStoreFileType;
Expand Down Expand Up @@ -157,14 +155,14 @@ public SSLContext toSSLContext() throws GeneralSecurityException, IOException {
return null;
}
final KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
keyManagerFactory.init(keyStore, null);
keyManagerFactory.init(keyStore, "password".toCharArray());
final SSLContextBuilder sslContextBuilder = new SSLContextBuilder();
sslContextBuilder.setCipherSuiteSelector(cipherSuites.orElse(CipherSuiteSelector.openSslDefault()));
ProtocolSelector protocolSelector;
if (protocols.isEmpty()) {
protocolSelector = ProtocolSelector.defaultProtocols();
} else {
protocolSelector = ProtocolSelector.empty().add(protocols.toArray(NO_PROTOCOLS));
protocolSelector = ProtocolSelector.empty().add(protocols.toArray(new Protocol[0]));
}
sslContextBuilder.setProtocolSelector(protocolSelector);
sslContextBuilder.setKeyManager((X509ExtendedKeyManager) keyManagerFactory.getKeyManagers()[0]);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package io.quarkus.runtime.graal;

import java.lang.invoke.MethodHandle;
import java.security.Principal;

import javax.security.auth.x500.X500Principal;

import org.wildfly.security.x500.util.X500PrincipalUtil;

import com.oracle.svm.core.annotate.Alias;
import com.oracle.svm.core.annotate.Delete;
import com.oracle.svm.core.annotate.RecomputeFieldValue;
import com.oracle.svm.core.annotate.Substitute;
import com.oracle.svm.core.annotate.TargetClass;

/**
*
*/
@TargetClass(X500PrincipalUtil.class)
final class Target_org_wildfly_security_x500_util_X500PrincipalUtil {
@RecomputeFieldValue(kind = RecomputeFieldValue.Kind.Reset)
@Alias
static Class<?> X500_NAME_CLASS;
@Delete
static MethodHandle AS_X500_PRINCIPAL_HANDLE;

@Substitute
public static X500Principal asX500Principal(Principal principal, boolean convert) {
return null;
}
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,7 @@
package io.quarkus.undertow.deployment.devmode;

import java.util.OptionalInt;

import javax.servlet.ServletException;

import io.quarkus.deployment.QuarkusConfig;
import io.quarkus.deployment.devmode.HotReplacementContext;
import io.quarkus.deployment.devmode.HotReplacementSetup;
import io.quarkus.runtime.LaunchMode;
import io.quarkus.undertow.runtime.HttpConfig;
import io.quarkus.undertow.runtime.UndertowDeploymentTemplate;
import io.undertow.server.HandlerWrapper;
import io.undertow.server.HttpHandler;
Expand All @@ -25,18 +18,7 @@ public class UndertowHotReplacementSetup implements HotReplacementSetup {
public void setupHotDeployment(HotReplacementContext context) {
this.context = context;
HandlerWrapper wrapper = createHandlerWrapper();
//TODO: we need to get these values from the config in runtime mode
HttpConfig config = new HttpConfig();
config.port = QuarkusConfig.getInt("quarkus.http.port", "8080");
config.host = QuarkusConfig.getString("quarkus.http.host", "localhost", true);
config.ioThreads = OptionalInt.empty();
config.workerThreads = OptionalInt.empty();

try {
UndertowDeploymentTemplate.startUndertowEagerly(config, wrapper, LaunchMode.DEVELOPMENT);
} catch (ServletException e) {
throw new RuntimeException(e);
}
UndertowDeploymentTemplate.setHotDeployment(wrapper);
}

private HandlerWrapper createHandlerWrapper() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import io.quarkus.runtime.annotations.ConfigItem;
import io.quarkus.runtime.annotations.ConfigPhase;
import io.quarkus.runtime.annotations.ConfigRoot;
import io.quarkus.runtime.configuration.ssl.ServerSslConfig;

@ConfigRoot(phase = ConfigPhase.RUN_TIME)
public class HttpConfig {
Expand All @@ -32,12 +33,23 @@ public class HttpConfig {
@ConfigItem(defaultValue = "8080")
public int port;

/**
* The HTTPS port
*/
@ConfigItem(defaultValue = "8443")
public int sslPort;

/**
* The HTTP port used to run tests
*/
@ConfigItem(defaultValue = "8081")
public int testPort;

/**
* The HTTPS port used to run tests
*/
@ConfigItem(defaultValue = "8444")
public int testSslPort;
/**
* The HTTP host
*/
Expand All @@ -58,8 +70,17 @@ public class HttpConfig {
@ConfigItem
public OptionalInt ioThreads;

/**
* The SSL config
*/
public ServerSslConfig ssl;

public int determinePort(LaunchMode launchMode) {
return launchMode == LaunchMode.TEST ? testPort : port;
}

public int determineSslPort(LaunchMode launchMode) {
return launchMode == LaunchMode.TEST ? testSslPort : sslPort;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import java.util.Set;
import java.util.stream.Collectors;

import javax.net.ssl.SSLContext;
import javax.servlet.DispatcherType;
import javax.servlet.Filter;
import javax.servlet.MultipartConfigElement;
Expand Down Expand Up @@ -90,6 +91,7 @@ public void handleRequest(HttpServerExchange exchange) throws Exception {
private static final String RESOURCES_PROP = "quarkus.undertow.resources";

private static volatile Undertow undertow;
private static volatile HandlerWrapper hotDeploymentWrapper;
private static volatile HttpHandler currentRoot = ResponseCodeHandler.HANDLE_404;

public RuntimeValue<DeploymentInfo> createDeployment(String name, Set<String> knownFile, Set<String> knownDirectories,
Expand Down Expand Up @@ -252,19 +254,22 @@ public void addServltInitParameter(RuntimeValue<DeploymentInfo> info, String nam
}

public RuntimeValue<Undertow> startUndertow(ShutdownContext shutdown, DeploymentManager manager, HttpConfig config,
List<HandlerWrapper> wrappers, LaunchMode launchMode) throws ServletException {
List<HandlerWrapper> wrappers, LaunchMode launchMode) throws Exception {

if (undertow == null) {
startUndertowEagerly(config, null, launchMode);
SSLContext context = config.ssl.toSSLContext();
doServerStart(config, launchMode, context);

//in development mode undertow is started eagerly
shutdown.addShutdownTask(new Runnable() {
@Override
public void run() {
undertow.stop();
undertow = null;
}
});
if (launchMode != LaunchMode.DEVELOPMENT) {
//in development mode undertow should not be shut down
shutdown.addShutdownTask(new Runnable() {
@Override
public void run() {
undertow.stop();
undertow = null;
}
});
}
}
shutdown.addShutdownTask(new Runnable() {
@Override
Expand Down Expand Up @@ -298,17 +303,22 @@ public void run() {
return new RuntimeValue<>(undertow);
}

public static void setHotDeployment(HandlerWrapper handlerWrapper) {
hotDeploymentWrapper = handlerWrapper;
}

/**
* Used for quarkus:run, where we want undertow to start very early in the process.
* <p>
* This enables recovery from errors on boot. In a normal boot undertow is one of the last things start, so there would
* be no chance to use hot deployment to fix the error. In development mode we start Undertow early, so any error
* on boot can be corrected via the hot deployment handler
*/
public static void startUndertowEagerly(HttpConfig config, HandlerWrapper hotDeploymentWrapper, LaunchMode launchMode)
private static void doServerStart(HttpConfig config, LaunchMode launchMode, SSLContext sslContext)
throws ServletException {
if (undertow == null) {
int port = config.determinePort(launchMode);
int sslPort = config.determineSslPort(launchMode);
log.debugf("Starting Undertow on port %d", port);
HttpHandler rootHandler = new CanonicalPathHandler(ROOT_HANDLER);
if (hotDeploymentWrapper != null) {
Expand All @@ -329,6 +339,10 @@ public static void startUndertowEagerly(HttpConfig config, HandlerWrapper hotDep
} else if (launchMode.isDevOrTest()) {
builder.setWorkerThreads(6);
}
if (sslContext != null) {
log.debugf("Starting Undertow HTTPS listener on port %d", sslPort);
builder.addHttpsListener(sslPort, config.host, sslContext);
}
undertow = builder
.build();
undertow.start();
Expand Down

0 comments on commit c3b3359

Please sign in to comment.