Skip to content

Commit

Permalink
Support for HTTP/2 (server-side)
Browse files Browse the repository at this point in the history
Signed-off-by: Andriy Redko <andriy.redko@aiven.io>
  • Loading branch information
reta committed Aug 25, 2022
1 parent 287e945 commit a83b4f7
Show file tree
Hide file tree
Showing 20 changed files with 127 additions and 63 deletions.
3 changes: 2 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import org.opensearch.gradle.test.RestIntegTestTask

buildscript {
ext {
opensearch_version = System.getProperty("opensearch.version", "2.3.0-SNAPSHOT")
opensearch_version = System.getProperty("opensearch.version", "3.0.0-SNAPSHOT")
isSnapshot = "true" == System.getProperty("build.snapshot", "true")
buildVersionQualifier = System.getProperty("build.version_qualifier", "")

Expand Down Expand Up @@ -54,6 +54,7 @@ plugins {
id 'checkstyle'
id 'nebula.ospackage' version "8.3.0"
id "org.gradle.test-retry" version "1.3.1"
id 'eclipse'
}

allprojects {
Expand Down
2 changes: 1 addition & 1 deletion bwc-test/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ ext {

buildscript {
ext {
opensearch_version = System.getProperty("opensearch.version", "2.3.0-SNAPSHOT")
opensearch_version = System.getProperty("opensearch.version", "3.0.0-SNAPSHOT")
opensearch_group = "org.opensearch"
}
repositories {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ public class HTTPSpnegoAuthenticator implements HTTPAuthenticator {
public HTTPSpnegoAuthenticator(final Settings settings, final Path configPath) {
super();
try {
final Path configDir = new Environment(settings, configPath).configFile();
final Path configDir = new Environment(settings, configPath).configDir();
final String krb5PathSetting = settings.get("plugins.security.kerberos.krb5_filepath");

final SecurityManager sm = System.getSecurityManager();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,6 @@ public byte[] run() throws ResolverException {

private static File getMetadataFile(String filePath, Settings settings, Path configPath) {
Environment env = new Environment(settings, configPath);
return env.configFile().resolve(filePath).toAbsolutePath().toFile();
return env.configDir().resolve(filePath).toAbsolutePath().toFile();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,7 @@ public Object run() {
final List<Path> filesWithWrongPermissions = AccessController.doPrivileged(new PrivilegedAction<List<Path>>() {
@Override
public List<Path> run() {
final Path confPath = new Environment(settings, configPath).configFile().toAbsolutePath();
final Path confPath = new Environment(settings, configPath).configDir().toAbsolutePath();
if(Files.isDirectory(confPath, LinkOption.NOFOLLOW_LINKS)) {
try (Stream<Path> s = Files.walk(confPath)) {
return s.distinct().filter(p -> checkFilePermissions(p)).collect(Collectors.toList());
Expand Down Expand Up @@ -356,7 +356,7 @@ public List<Path> run() {
final List<String> files = AccessController.doPrivileged(new PrivilegedAction<List<String>>() {
@Override
public List<String> run() {
final Path confPath = new Environment(settings, configPath).configFile().toAbsolutePath();
final Path confPath = new Environment(settings, configPath).configDir().toAbsolutePath();
if(Files.isDirectory(confPath, LinkOption.NOFOLLOW_LINKS)) {
try (Stream<Path> s = Files.walk(confPath)) {
return s.distinct().map(p -> sha256(p)).collect(Collectors.toList());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -560,7 +560,7 @@ public Map run() {
(key.contains("filepath") || key.contains("file_path"))) {
String value = settings.get(key);
if(value != null && !value.isEmpty()) {
Path path = value.startsWith("/")?Paths.get(value):environment.configFile().resolve(value);
Path path = value.startsWith("/")?Paths.get(value):environment.configDir().resolve(value);
paths.put(key, path);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ public void run() {

try {
String lookupDir = System.getProperty("security.default_init.dir");
final String cd = lookupDir != null? (lookupDir+"/") : new Environment(settings, configPath).configFile().toAbsolutePath().toString()+"/opensearch-security/";
final String cd = lookupDir != null? (lookupDir+"/") : new Environment(settings, configPath).configDir().toAbsolutePath().toString()+"/opensearch-security/";
File confFile = new File(cd+"config.yml");
if(confFile.exists()) {
final ThreadContext threadContext = threadPool.getThreadContext();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,19 +42,27 @@
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;

import javax.crypto.Cipher;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLParameters;

import io.netty.handler.codec.http2.Http2SecurityUtil;
import io.netty.handler.ssl.ApplicationProtocolConfig;
import io.netty.handler.ssl.ApplicationProtocolConfig.Protocol;
import io.netty.handler.ssl.ApplicationProtocolConfig.SelectedListenerFailureBehavior;
import io.netty.handler.ssl.ApplicationProtocolConfig.SelectorFailureBehavior;
import io.netty.handler.ssl.ApplicationProtocolNames;
import io.netty.handler.ssl.ClientAuth;
import io.netty.handler.ssl.OpenSsl;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.SslProvider;
import io.netty.handler.ssl.SupportedCipherSuiteFilter;
import io.netty.util.internal.PlatformDependent;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
Expand Down Expand Up @@ -226,8 +234,8 @@ private String resolve(String propName, boolean mustBeValid) {
log.debug("Value for {} is {}", propName, originalPath);

if (env != null && originalPath != null && originalPath.length() > 0) {
path = env.configFile().resolve(originalPath).toAbsolutePath().toString();
log.debug("Resolved {} to {} against {}", originalPath, path, env.configFile().toAbsolutePath().toString());
path = env.configDir().resolve(originalPath).toAbsolutePath().toString();
log.debug("Resolved {} to {} against {}", originalPath, path, env.configDir().toAbsolutePath().toString());
}

if (mustBeValid) {
Expand All @@ -247,7 +255,7 @@ private void initSSLConfig() {
log.info("No config directory, key- and truststore files are resolved absolutely");
} else {
log.info("Config directory is {}/, from there the key- and truststore files are resolved relatively",
env.configFile().toAbsolutePath());
env.configDir().toAbsolutePath());
}


Expand Down Expand Up @@ -426,7 +434,7 @@ public void initTransportSSLConfig() {
/**
* Initializes certs used for client https communication
*/
public void initHttpSSLConfig() {
public void initHttpSSLConfig() {
final boolean useKeyStore = settings.hasValue(SSLConfigConstants.SECURITY_SSL_HTTP_KEYSTORE_FILEPATH);
final boolean useRawFiles = settings.hasValue(SSLConfigConstants.SECURITY_SSL_HTTP_PEMCERT_FILEPATH);
final ClientAuth httpClientAuthMode = ClientAuth.valueOf(settings
Expand Down Expand Up @@ -879,10 +887,24 @@ private SslContext buildSSLServerContext(final PrivateKey _key, final X509Certif
final X509Certificate[] _trustedCerts, final Iterable<String> ciphers, final SslProvider sslProvider,
final ClientAuth authMode) throws SSLException {

final SslContextBuilder _sslContextBuilder = SslContextBuilder.forServer(_key, _cert).ciphers(ciphers)
.applicationProtocolConfig(ApplicationProtocolConfig.DISABLED)
final SslContextBuilder _sslContextBuilder = SslContextBuilder.forServer(_key, _cert)
.ciphers(Stream
.concat(
Http2SecurityUtil.CIPHERS.stream(),
StreamSupport.stream(ciphers.spliterator(), false))
.collect(Collectors.toSet()), SupportedCipherSuiteFilter.INSTANCE)
.clientAuth(Objects.requireNonNull(authMode)) // https://github.com/netty/netty/issues/4722
.sessionCacheSize(0).sessionTimeout(0).sslProvider(sslProvider);
.sessionCacheSize(0).sessionTimeout(0).sslProvider(sslProvider)
.applicationProtocolConfig(
new ApplicationProtocolConfig(
Protocol.ALPN,
// NO_ADVERTISE is currently the only mode supported by both OpenSsl and JDK providers.
SelectorFailureBehavior.NO_ADVERTISE,
// ACCEPT is currently the only mode supported by both OpenSsl and JDK providers.
SelectedListenerFailureBehavior.ACCEPT,
ApplicationProtocolNames.HTTP_2,
ApplicationProtocolNames.HTTP_1_1
));

if (_trustedCerts != null && _trustedCerts.length > 0) {
_sslContextBuilder.trustManager(_trustedCerts);
Expand All @@ -895,11 +917,24 @@ private SslContext buildSSLServerContext(final File _key, final File _cert, fina
final String pwd, final Iterable<String> ciphers, final SslProvider sslProvider, final ClientAuth authMode)
throws SSLException {

final SslContextBuilder _sslContextBuilder = SslContextBuilder.forServer(_cert, _key, pwd).ciphers(ciphers)
.applicationProtocolConfig(ApplicationProtocolConfig.DISABLED)
final SslContextBuilder _sslContextBuilder = SslContextBuilder.forServer(_cert, _key, pwd)
.ciphers(Stream
.concat(
Http2SecurityUtil.CIPHERS.stream(),
StreamSupport.stream(ciphers.spliterator(), false))
.collect(Collectors.toSet()), SupportedCipherSuiteFilter.INSTANCE)
.clientAuth(Objects.requireNonNull(authMode)) // https://github.com/netty/netty/issues/4722
.sessionCacheSize(0).sessionTimeout(0).sslProvider(sslProvider);

.sessionCacheSize(0).sessionTimeout(0).sslProvider(sslProvider)
.applicationProtocolConfig(
new ApplicationProtocolConfig(
Protocol.ALPN,
// NO_ADVERTISE is currently the only mode supported by both OpenSsl and JDK providers.
SelectorFailureBehavior.NO_ADVERTISE,
// ACCEPT is currently the only mode supported by both OpenSsl and JDK providers.
SelectedListenerFailureBehavior.ACCEPT,
ApplicationProtocolNames.HTTP_2,
ApplicationProtocolNames.HTTP_1_1
));
if (_trustedCerts != null) {
_sslContextBuilder.trustManager(_trustedCerts);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,10 @@

import io.netty.channel.Channel;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.DecoderException;
import io.netty.handler.ssl.ApplicationProtocolNames;
import io.netty.handler.ssl.ApplicationProtocolNegotiationHandler;
import io.netty.handler.ssl.SslHandler;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
Expand Down Expand Up @@ -72,7 +75,27 @@ public void onException(HttpChannel channel, Exception cause0) {
}

protected class SSLHttpChannelHandler extends Netty4HttpServerTransport.HttpChannelHandler {

/**
* Application negotiation handler to select either HTTP 1.1 or HTTP 2 protocol, based
* on client/server ALPN negotiations.
*/
private class Http2OrHttpHandler extends ApplicationProtocolNegotiationHandler {
protected Http2OrHttpHandler() {
super(ApplicationProtocolNames.HTTP_1_1);
}

@Override
protected void configurePipeline(ChannelHandlerContext ctx, String protocol) throws Exception {
if (ApplicationProtocolNames.HTTP_2.equals(protocol)) {
configureDefaultHttp2Pipeline(ctx.pipeline());
} else if (ApplicationProtocolNames.HTTP_1_1.equals(protocol)) {
configureDefaultHttpPipeline(ctx.pipeline());
} else {
throw new IllegalStateException("Unknown application protocol: " + protocol);
}
}
}

protected SSLHttpChannelHandler(Netty4HttpServerTransport transport, final HttpHandlingSettings handlingSettings, final SecurityKeyStore odsks) {
super(transport, handlingSettings);
}
Expand All @@ -83,5 +106,10 @@ protected void initChannel(Channel ch) throws Exception {
final SslHandler sslHandler = new SslHandler(SecuritySSLNettyHttpServerTransport.this.sks.createHTTPSSLEngine());
ch.pipeline().addFirst("ssl_http", sslHandler);
}

@Override
protected void configureDefaultPipeline(Channel ch) {
ch.pipeline().addLast(new Http2OrHttpHandler());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ private static boolean validate(X509Certificate[] x509Certs, final Settings sett
final String crlFile = settings.get(SSLConfigConstants.SSECURITY_SSL_HTTP_CRL_FILE);

if(crlFile != null) {
final File crl = env.configFile().resolve(crlFile).toAbsolutePath().toFile();
final File crl = env.configDir().resolve(crlFile).toAbsolutePath().toFile();
try(FileInputStream crlin = new FileInputStream(crl)) {
crls = CertificateFactory.getInstance("X.509").generateCRLs(crlin);
}
Expand All @@ -222,12 +222,12 @@ private static boolean validate(X509Certificate[] x509Certs, final Settings sett
//final String truststoreAlias = settings.get(SSLConfigConstants.SECURITY_SSL_HTTP_TRUSTSTORE_ALIAS, null);

final KeyStore ts = KeyStore.getInstance(truststoreType);
try(FileInputStream fin = new FileInputStream(new File(env.configFile().resolve(truststore).toAbsolutePath().toString()))) {
try(FileInputStream fin = new FileInputStream(new File(env.configDir().resolve(truststore).toAbsolutePath().toString()))) {
ts.load(fin, (truststorePassword == null || truststorePassword.length() == 0) ?null:truststorePassword.toCharArray());
}
validator = new CertificateValidator(ts, crls);
} else {
final File trustedCas = env.configFile().resolve(settings.get(SSLConfigConstants.SECURITY_SSL_HTTP_PEMTRUSTEDCAS_FILEPATH, "")).toAbsolutePath().toFile();
final File trustedCas = env.configDir().resolve(settings.get(SSLConfigConstants.SECURITY_SSL_HTTP_PEMTRUSTEDCAS_FILEPATH, "")).toAbsolutePath().toFile();
try(FileInputStream trin = new FileInputStream(trustedCas)) {
Collection<? extends Certificate> cert = (Collection<? extends Certificate>) CertificateFactory.getInstance("X.509").generateCertificates(trin);
validator = new CertificateValidator(cert.toArray(new X509Certificate[0]), crls);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -325,8 +325,8 @@ public static String resolve(String originalPath, String propName, Settings sett
final Environment env = new Environment(settings, configPath);

if(env != null && originalPath != null && originalPath.length() > 0) {
path = env.configFile().resolve(originalPath).toAbsolutePath().toString();
log.debug("Resolved {} to {} against {}", originalPath, path, env.configFile().toAbsolutePath().toString());
path = env.configDir().resolve(originalPath).toAbsolutePath().toString();
log.debug("Resolved {} to {} against {}", originalPath, path, env.configDir().toAbsolutePath().toString());
}

if(mustBeValid) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
import org.opensearch.security.test.DynamicSecurityConfig;
import org.opensearch.security.test.SingleClusterTest;
import org.opensearch.threadpool.ThreadPool;
import org.opensearch.transport.Netty4Plugin;
import org.opensearch.transport.Netty4ModulePlugin;
import org.opensearch.watcher.ResourceWatcherService;

public class RolesInjectorIntegTest extends SingleClusterTest {
Expand Down Expand Up @@ -99,7 +99,7 @@ public void testRolesInject() throws Exception {
.build();

//1. Without roles injection.
try (Node node = new PluginAwareNode(false, tcSettings, Netty4Plugin.class,
try (Node node = new PluginAwareNode(false, tcSettings, Netty4ModulePlugin.class,
OpenSearchSecurityPlugin.class, RolesInjectorPlugin.class).start()) {
waitForInit(node.client());

Expand All @@ -112,7 +112,7 @@ public void testRolesInject() throws Exception {
//2. With invalid roles, must throw security exception.
RolesInjectorPlugin.injectedRoles = "invalid_user|invalid_role";
Exception exception = null;
try (Node node = new PluginAwareNode(false, tcSettings, Netty4Plugin.class, OpenSearchSecurityPlugin.class, RolesInjectorPlugin.class).start()) {
try (Node node = new PluginAwareNode(false, tcSettings, Netty4ModulePlugin.class, OpenSearchSecurityPlugin.class, RolesInjectorPlugin.class).start()) {
waitForInit(node.client());

CreateIndexResponse cir = node.client().admin().indices().create(new CreateIndexRequest("captain-logs-2")).actionGet();
Expand All @@ -126,7 +126,7 @@ public void testRolesInject() throws Exception {

//3. With valid roles - which has permission to create index.
RolesInjectorPlugin.injectedRoles = "valid_user|opendistro_security_all_access";
try (Node node = new PluginAwareNode(false, tcSettings, Netty4Plugin.class, OpenSearchSecurityPlugin.class, RolesInjectorPlugin.class).start()) {
try (Node node = new PluginAwareNode(false, tcSettings, Netty4ModulePlugin.class, OpenSearchSecurityPlugin.class, RolesInjectorPlugin.class).start()) {
waitForInit(node.client());

CreateIndexResponse cir = node.client().admin().indices().create(new CreateIndexRequest("captain-logs-3")).actionGet();
Expand Down
Loading

0 comments on commit a83b4f7

Please sign in to comment.