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

Support for HTTP/2 (server-side) #2051

Merged
merged 9 commits into from
Sep 29, 2022
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
6 changes: 3 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,9 @@ jobs:
cp -r build/ ./bwc-test/
mkdir ./bwc-test/src/test/resources/security_plugin_version_no_snapshot
cp build/distributions/opensearch-security-${security_plugin_version_no_snapshot}.zip ./bwc-test/src/test/resources/${security_plugin_version_no_snapshot}
mkdir bwc-test/src/test/resources/2.3.0.0
wget https://ci.opensearch.org/ci/dbc/distribution-build-opensearch/2.3.0/latest/linux/x64/tar/builds/opensearch/plugins/opensearch-security-2.3.0.0.zip
mv opensearch-security-2.3.0.0.zip bwc-test/src/test/resources/2.3.0.0/
mkdir bwc-test/src/test/resources/2.4.0.0
wget https://ci.opensearch.org/ci/dbc/distribution-build-opensearch/2.4.0/latest/linux/x64/tar/builds/opensearch/plugins/opensearch-security-2.4.0.0.zip
mv opensearch-security-2.4.0.0.zip bwc-test/src/test/resources/2.4.0.0/
cd bwc-test/
./gradlew bwcTestSuite -Dtests.security.manager=false

Expand Down
3 changes: 2 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import org.opensearch.gradle.test.RestIntegTestTask

buildscript {
ext {
opensearch_version = System.getProperty("opensearch.version", "2.4.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 @@ -56,6 +56,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
8 changes: 4 additions & 4 deletions 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.4.0-SNAPSHOT")
opensearch_version = System.getProperty("opensearch.version", "3.0.0-SNAPSHOT")
opensearch_group = "org.opensearch"
}
repositories {
Expand All @@ -73,16 +73,16 @@ dependencies {
testImplementation "org.opensearch.test:framework:${opensearch_version}"
}

String bwcVersion = "2.3.0.0";
String bwcVersion = "2.4.0.0";
String baseName = "securityBwcCluster"
String bwcFilePath = "src/test/resources/"
String projectVersion = "2.4.0.0"
String projectVersion = "3.0.0.0"

2.times {i ->
testClusters {
"${baseName}$i" {
testDistribution = "ARCHIVE"
versions = ["2.3.0","2.4.0"]
versions = ["2.4.0","3.0.0"]
numberOfNodes = 3
plugin(provider(new Callable<RegularFile>() {
@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,13 @@
import java.util.Objects;
import java.util.stream.Collectors;

import org.opensearch.index.reindex.ReindexPlugin;
import org.opensearch.join.ParentJoinPlugin;
import org.opensearch.percolator.PercolatorPlugin;
import org.opensearch.index.reindex.ReindexModulePlugin;
import org.opensearch.join.ParentJoinModulePlugin;
import org.opensearch.percolator.PercolatorModulePlugin;
import org.opensearch.plugins.Plugin;
import org.opensearch.search.aggregations.matrix.MatrixAggregationPlugin;
import org.opensearch.search.aggregations.matrix.MatrixAggregationModulePlugin;
import org.opensearch.security.OpenSearchSecurityPlugin;
import org.opensearch.transport.Netty4Plugin;
import org.opensearch.transport.Netty4ModulePlugin;

import static java.util.Collections.unmodifiableList;
import static org.opensearch.test.framework.cluster.NodeType.CLIENT;
Expand Down Expand Up @@ -98,8 +98,8 @@ public int getClientNodes() {

public static class NodeSettings {

private final static List<Class<? extends Plugin>> DEFAULT_PLUGINS = List.of(Netty4Plugin.class, OpenSearchSecurityPlugin.class,
MatrixAggregationPlugin.class, ParentJoinPlugin.class, PercolatorPlugin.class, ReindexPlugin.class);
private final static List<Class<? extends Plugin>> DEFAULT_PLUGINS = List.of(Netty4ModulePlugin.class, OpenSearchSecurityPlugin.class,
MatrixAggregationModulePlugin.class, ParentJoinModulePlugin.class, PercolatorModulePlugin.class, ReindexModulePlugin.class);
public final boolean clusterManagerNode;
public final boolean dataNode;
public final List<Class<? extends Plugin>> plugins;
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,8 @@ 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)
.clientAuth(Objects.requireNonNull(authMode)) // https://github.com/netty/netty/issues/4722
.sessionCacheSize(0).sessionTimeout(0).sslProvider(sslProvider);
final SslContextBuilder _sslContextBuilder = configureSSLServerContextBuilder(SslContextBuilder.forServer(_key, _cert),
sslProvider, ciphers, authMode);

if (_trustedCerts != null && _trustedCerts.length > 0) {
_sslContextBuilder.trustManager(_trustedCerts);
Expand All @@ -895,10 +901,8 @@ 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)
.clientAuth(Objects.requireNonNull(authMode)) // https://github.com/netty/netty/issues/4722
.sessionCacheSize(0).sessionTimeout(0).sslProvider(sslProvider);
final SslContextBuilder _sslContextBuilder = configureSSLServerContextBuilder(SslContextBuilder.forServer(_cert, _key, pwd),
sslProvider, ciphers, authMode);

if (_trustedCerts != null) {
_sslContextBuilder.trustManager(_trustedCerts);
Expand All @@ -907,6 +911,28 @@ private SslContext buildSSLServerContext(final File _key, final File _cert, fina
return buildSSLContext0(_sslContextBuilder);
}

private SslContextBuilder configureSSLServerContextBuilder(final SslContextBuilder builder, final SslProvider sslProvider,
final Iterable<String> ciphers, final ClientAuth authMode) {
return builder
.ciphers(Stream
.concat(
Http2SecurityUtil.CIPHERS.stream(),
StreamSupport.stream(ciphers.spliterator(), false))
.collect(Collectors.toSet()), SupportedCipherSuiteFilter.INSTANCE)
.clientAuth(Objects.requireNonNull(authMode))
.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
));
}

private SslContext buildSSLClientContext(final PrivateKey _key, final X509Certificate[] _cert,
final X509Certificate[] _trustedCerts, final Iterable<String> ciphers, final SslProvider sslProvider)
throws SSLException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,12 @@

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 io.netty.util.AttributeKey;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

Expand All @@ -31,13 +35,15 @@
import org.opensearch.common.xcontent.NamedXContentRegistry;
import org.opensearch.http.HttpChannel;
import org.opensearch.http.HttpHandlingSettings;
import org.opensearch.http.netty4.Netty4HttpChannel;
import org.opensearch.http.netty4.Netty4HttpServerTransport;
import org.opensearch.security.ssl.SecurityKeyStore;
import org.opensearch.security.ssl.SslExceptionHandler;
import org.opensearch.threadpool.ThreadPool;
import org.opensearch.transport.SharedGroupFactory;

public class SecuritySSLNettyHttpServerTransport extends Netty4HttpServerTransport {
static final AttributeKey<Netty4HttpChannel> HTTP_CHANNEL_KEY = AttributeKey.valueOf("opensearch-http-channel");

private static final Logger logger = LogManager.getLogger(SecuritySSLNettyHttpServerTransport.class);
private final SecurityKeyStore sks;
Expand Down Expand Up @@ -72,7 +78,40 @@ 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);
}
}

@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
super.exceptionCaught(ctx, cause);
Netty4HttpChannel channel = ctx.channel().attr(HTTP_CHANNEL_KEY).get();
if (channel != null) {
if (cause instanceof Error) {
onException(channel, new Exception(cause));
} else {
onException(channel, (Exception) cause);
}
}
}
}

protected SSLHttpChannelHandler(Netty4HttpServerTransport transport, final HttpHandlingSettings handlingSettings, final SecurityKeyStore odsks) {
super(transport, handlingSettings);
}
Expand All @@ -83,5 +122,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 configurePipeline(Channel ch) {
ch.pipeline().addLast(new Http2OrHttpHandler());
}
}
}
Loading