org.opensearch.migrations.trafficcapture.proxyserver.CaptureProxy Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of trafficCaptureProxyServer Show documentation
Show all versions of trafficCaptureProxyServer Show documentation
Everything opensearch migrations
package org.opensearch.migrations.trafficcapture.proxyserver;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLException;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.nio.file.Paths;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Properties;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.opensearch.common.settings.Settings;
import org.opensearch.migrations.jcommander.NoSplitter;
import org.opensearch.migrations.tracing.ActiveContextTracker;
import org.opensearch.migrations.tracing.ActiveContextTrackerByActivityType;
import org.opensearch.migrations.tracing.CompositeContextTracker;
import org.opensearch.migrations.tracing.RootOtelContext;
import org.opensearch.migrations.trafficcapture.CodedOutputStreamHolder;
import org.opensearch.migrations.trafficcapture.FileConnectionCaptureFactory;
import org.opensearch.migrations.trafficcapture.IConnectionCaptureFactory;
import org.opensearch.migrations.trafficcapture.StreamChannelConnectionCaptureSerializer;
import org.opensearch.migrations.trafficcapture.StreamLifecycleManager;
import org.opensearch.migrations.trafficcapture.kafkaoffloader.KafkaCaptureFactory;
import org.opensearch.migrations.trafficcapture.netty.HeaderValueFilteringCapturePredicate;
import org.opensearch.migrations.trafficcapture.netty.RequestCapturePredicate;
import org.opensearch.migrations.trafficcapture.proxyserver.netty.BacksideConnectionPool;
import org.opensearch.migrations.trafficcapture.proxyserver.netty.HeaderAdderHandler;
import org.opensearch.migrations.trafficcapture.proxyserver.netty.HeaderRemoverHandler;
import org.opensearch.migrations.trafficcapture.proxyserver.netty.NettyScanningHttpProxy;
import org.opensearch.migrations.trafficcapture.proxyserver.netty.ProxyChannelInitializer;
import org.opensearch.migrations.utils.ProcessHelpers;
import org.opensearch.security.ssl.DefaultSecurityKeyStore;
import org.opensearch.security.ssl.util.SSLConfigConstants;
import com.beust.jcommander.JCommander;
import com.beust.jcommander.Parameter;
import com.beust.jcommander.ParameterException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import com.google.protobuf.CodedOutputStream;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
import lombok.Lombok;
import lombok.NonNull;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.kafka.clients.CommonClientConfigs;
import org.apache.kafka.clients.producer.KafkaProducer;
import org.apache.kafka.clients.producer.ProducerConfig;
import org.apache.kafka.common.config.SaslConfigs;
@Slf4j
public class CaptureProxy {
private static final String HTTPS_CONFIG_PREFIX = "plugins.security.ssl.http.";
public static final String DEFAULT_KAFKA_CLIENT_ID = "HttpCaptureProxyProducer";
public static final String SUPPORTED_TLS_PROTOCOLS_LIST_KEY = "plugins.security.ssl.http.enabled_protocols";
public static class Parameters {
@Parameter(required = false,
names = { "--traceDirectory" },
arity = 1,
description = "Directory to store trace files in.")
public String traceDirectory;
@Parameter(required = false,
names = { "--noCapture" },
arity = 0,
description = "If enabled, Does NOT capture traffic to ANY sink.")
public boolean noCapture;
@Parameter(required = false,
names = { "--kafkaConfigFile" },
arity = 1,
description = "Kafka properties file for additional client customization.")
public String kafkaPropertiesFile;
@Parameter(required = false,
names = { "--kafkaClientId" },
arity = 1,
description = "clientId to use for interfacing with Kafka.")
public String kafkaClientId = DEFAULT_KAFKA_CLIENT_ID;
@Parameter(required = false,
names = { "--kafkaConnection" },
arity = 1,
description = "Sequence of values delimited by ','.")
public String kafkaConnection;
@Parameter(required = false,
names = { "--enableMSKAuth" },
arity = 0,
description = "Enables SASL Kafka properties required for connecting to MSK with IAM auth.")
public boolean mskAuthEnabled = false;
@Parameter(required = false,
names = { "--sslConfigFile" },
arity = 1,
description = "YAML configuration of the HTTPS settings. When this is not set, the proxy will not use TLS.")
public String sslConfigFilePath;
@Parameter(required = false,
names = { "--maxTrafficBufferSize" },
arity = 1,
description = "The maximum number of bytes that will be written to a single TrafficStream object.")
public int maximumTrafficStreamSize = 1024 * 1024;
@Parameter(required = false,
names = { "--insecureDestination" },
arity = 0,
description = "Do not check the destination server's certificate")
public boolean allowInsecureConnectionsToBackside;
@Parameter(required = true,
names = { "--destinationUri" },
arity = 1,
description = "URI of the server that the proxy is capturing traffic for.")
public String backsideUriString;
@Parameter(required = true,
names = { "--listenPort" },
arity = 1,
description = "Exposed port for clients to connect to this proxy.")
public int frontsidePort = 0;
@Parameter(required = false,
names = { "--numThreads" },
arity = 1,
description = "How many threads netty should create in its event loop group")
public int numThreads = 1;
@Parameter(required = false,
names = { "--destinationConnectionPoolSize" },
arity = 1,
description = "Number of socket connections that should be maintained to the destination server "
+ "to reduce the perceived latency to clients. Each thread will have its own cache, so the "
+ "total number of outstanding warm connections will be multiplied by numThreads.")
public int destinationConnectionPoolSize = 0;
@Parameter(required = false,
names = { "--destinationConnectionPoolTimeout" },
arity = 1,
description = "Of the socket connections maintained by the destination connection pool, "
+ "how long after connection should the be recycled "
+ "(closed with a new connection taking its place)")
public String destinationConnectionPoolTimeout = "PT30S";
@Parameter(required = false,
names = { "--otelCollectorEndpoint" },
arity = 1,
description = "Endpoint (host:port) for the OpenTelemetry Collector to which metrics logs should be forwarded."
+ "If this is not provided, metrics will not be sent to a collector.")
public String otelCollectorEndpoint;
@Parameter(required = false,
names = "--setHeader",
splitter = NoSplitter.class,
arity = 2,
description = "[header-name header-value] Set an HTTP header (first argument) with to the specified value" +
" (second argument). Any existing headers with that name will be removed.")
public List headerOverrides = new ArrayList<>();
@Parameter(required = false,
names = "--suppressCaptureForHeaderMatch",
splitter = NoSplitter.class,
arity = 2,
description = "The header name (which will be interpreted in a case-insensitive manner) and a regex "
+ "pattern. When the incoming request has a header that matches the regex, it will be passed "
+ "through to the service but will NOT be captured. E.g. user-agent 'healthcheck'.")
public List suppressCaptureHeaderPairs = new ArrayList<>();
}
static Parameters parseArgs(String[] args) {
Parameters p = new Parameters();
JCommander jCommander = new JCommander(p);
try {
jCommander.parse(args);
// Exactly one these 3 options are required. See that exactly one is set by summing up their presence
if (Stream.of(p.traceDirectory, p.kafkaConnection, (p.noCapture ? "" : null))
.mapToInt(s -> s != null ? 1 : 0)
.sum() != 1) {
throw new ParameterException(
"Expected exactly one of '--traceDirectory', '--kafkaConnection', or " + "'--noCapture' to be set"
);
}
return p;
} catch (ParameterException e) {
System.err.println(e.getMessage());
System.err.println("Got args: " + String.join("; ", args));
jCommander.usage();
System.exit(2);
return null;
}
}
@SneakyThrows
protected static Settings getSettings(@NonNull String configFile) {
var objectMapper = new ObjectMapper(new YAMLFactory());
var configMap = objectMapper.readValue(new File(configFile), Map.class);
var configParentDirStr = Paths.get(configFile).toAbsolutePath().getParent();
var httpsSettings =
objectMapper.convertValue(configMap, new TypeReference
© 2015 - 2025 Weber Informatics LLC | Privacy Policy