Skip to content

Commit

Permalink
Use an ObjectInputFilter to serialize allow deserialization of only c…
Browse files Browse the repository at this point in the history
…ertain objects in peer-to-peer connections. Additionally, it refactors some application configurations to improve integration testing. Fixes opensearch-project#2310. (opensearch-project#2311)

Signed-off-by: David Venable <[email protected]>
  • Loading branch information
dlvenable authored Mar 1, 2023
1 parent 15d590d commit 062ae95
Show file tree
Hide file tree
Showing 10 changed files with 574 additions and 53 deletions.
1 change: 1 addition & 0 deletions data-prepper-core/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ dependencies {
exclude group: 'commons-logging', module: 'commons-logging'
}
implementation 'software.amazon.cloudwatchlogs:aws-embedded-metrics:2.0.0-beta-1'
testImplementation 'org.apache.logging.log4j:log4j-jpl:2.17.0'
testImplementation 'org.springframework:spring-test:5.3.25'
implementation libs.armeria.core
implementation libs.armeria.grpc
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,10 @@

package org.opensearch.dataprepper.peerforwarder;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.opensearch.dataprepper.metrics.PluginMetrics;
import org.opensearch.dataprepper.parser.model.DataPrepperConfiguration;
import org.opensearch.dataprepper.peerforwarder.certificate.CertificateProviderFactory;
import org.opensearch.dataprepper.peerforwarder.client.PeerForwarderClient;
import org.opensearch.dataprepper.peerforwarder.codec.JacksonPeerForwarderCodec;
import org.opensearch.dataprepper.peerforwarder.codec.JavaPeerForwarderCodec;
import org.opensearch.dataprepper.peerforwarder.codec.PeerForwarderCodec;
import org.opensearch.dataprepper.peerforwarder.server.PeerForwarderHttpServerProvider;
import org.opensearch.dataprepper.peerforwarder.server.PeerForwarderHttpService;
Expand All @@ -24,7 +19,6 @@
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.yaml.snakeyaml.LoaderOptions;

@Configuration
class PeerForwarderAppConfig {
Expand All @@ -36,17 +30,6 @@ public PluginMetrics pluginMetrics() {
return PluginMetrics.fromNames(COMPONENT_ID, COMPONENT_SCOPE);
}

@Bean(name = "peerForwarderObjectMapper")
public ObjectMapper objectMapper() {
final JavaTimeModule javaTimeModule = new JavaTimeModule();
final LoaderOptions loaderOptions = new LoaderOptions();
loaderOptions.setCodePointLimit(10 * 1024 * 1024); // 10MB
final YAMLFactory yamlFactory = YAMLFactory.builder()
.loaderOptions(loaderOptions)
.build();
return new ObjectMapper(yamlFactory).registerModule(javaTimeModule);
}

@Bean
public PeerForwarderConfiguration peerForwarderConfiguration(
@Autowired(required = false) final DataPrepperConfiguration dataPrepperConfiguration) {
Expand Down Expand Up @@ -100,14 +83,6 @@ public ResponseHandler responseHandler(@Qualifier("peerForwarderMetrics") final
return new ResponseHandler(pluginMetrics);
}

@Bean
public PeerForwarderCodec peerForwarderCodec(
final PeerForwarderConfiguration peerForwarderConfiguration,
@Qualifier("peerForwarderObjectMapper") final ObjectMapper objectMapper) {
return peerForwarderConfiguration.getBinaryCodec() ?
new JavaPeerForwarderCodec() : new JacksonPeerForwarderCodec(objectMapper);
}

@Bean
public PeerForwarderHttpService peerForwarderHttpService(
final ResponseHandler responseHandler,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,17 @@
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputFilter;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.Objects;

public class JavaPeerForwarderCodec implements PeerForwarderCodec {
private final ObjectInputFilter filter;

public JavaPeerForwarderCodec(final ObjectInputFilter filter) {
this.filter = Objects.requireNonNull(filter);
}

@Override
public byte[] serialize(final PeerForwardingEvents events) throws IOException {
Expand All @@ -24,6 +31,7 @@ public byte[] serialize(final PeerForwardingEvents events) throws IOException {
public PeerForwardingEvents deserialize(final byte[] bytes) throws IOException, ClassNotFoundException {
try (final InputStream inputStream = new ByteArrayInputStream(bytes);
final ObjectInputStream objectInputStream = new ObjectInputStream(inputStream)) {
objectInputStream.setObjectInputFilter(filter);
return (PeerForwardingEvents) objectInputStream.readObject();
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

package org.opensearch.dataprepper.peerforwarder.codec;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.ObjectInputFilter;
import java.util.Objects;

/**
* A decorator for {@link ObjectInputFilter} which logs information when the filter is rejected.
*/
class LoggingObjectInputFilter implements ObjectInputFilter {
private static final Logger LOG = LoggerFactory.getLogger(LoggingObjectInputFilter.class);
private final ObjectInputFilter filter;

public LoggingObjectInputFilter(final ObjectInputFilter filter) {
this.filter = Objects.requireNonNull(filter);
}

@Override
public Status checkInput(final FilterInfo filterInfo) {
final Status status = filter.checkInput(filterInfo);

if(status == Status.REJECTED) {
LOG.warn("Unable to deserialize: {}", filterInfo.serialClass());
}

return status;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

package org.opensearch.dataprepper.peerforwarder.codec;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.opensearch.dataprepper.peerforwarder.PeerForwarderConfiguration;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.yaml.snakeyaml.LoaderOptions;

import java.io.ObjectInputFilter;

@Configuration
public class PeerForwarderCodecAppConfig {
@Bean
public PeerForwarderCodec peerForwarderCodec(
final PeerForwarderConfiguration peerForwarderConfiguration,
final ObjectInputFilter objectInputFilter,
@Qualifier("peerForwarderObjectMapper") final ObjectMapper objectMapper) {
return peerForwarderConfiguration.getBinaryCodec() ?
new JavaPeerForwarderCodec(objectInputFilter) : new JacksonPeerForwarderCodec(objectMapper);
}

@Bean(name = "peerForwarderObjectMapper")
public ObjectMapper objectMapper() {
final JavaTimeModule javaTimeModule = new JavaTimeModule();
final LoaderOptions loaderOptions = new LoaderOptions();
loaderOptions.setCodePointLimit(10 * 1024 * 1024); // 10MB
final YAMLFactory yamlFactory = YAMLFactory.builder()
.loaderOptions(loaderOptions)
.build();
return new ObjectMapper(yamlFactory).registerModule(javaTimeModule);
}

@Bean
public ObjectInputFilter objectInputFilter() {
final String baseModelPackage = "org.opensearch.dataprepper.model";

final String pattern =
"java.lang.Object;" +
"java.util.*;" +
"java.time.*;" +
"com.fasterxml.jackson.databind.node.*;" +
"org.opensearch.dataprepper.peerforwarder.model.*;" +
baseModelPackage + ".event.*;" +
baseModelPackage + ".trace.*;" +
baseModelPackage + ".log.*;" +
baseModelPackage + ".metric.*;" +
baseModelPackage + ".document.*;" +
"com.google.common.collect.ImmutableMap*;" +
"com.google.common.collect.RegularImmutableMap*;" +
"!*";

final ObjectInputFilter filter = ObjectInputFilter.Config.createFilter(pattern);

return new LoggingObjectInputFilter(filter);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,7 @@

package org.opensearch.dataprepper.peerforwarder;

import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.opensearch.dataprepper.metrics.PluginMetrics;
import org.opensearch.dataprepper.model.CheckpointState;
import org.opensearch.dataprepper.model.event.Event;
import org.opensearch.dataprepper.model.event.JacksonEvent;
import org.opensearch.dataprepper.model.record.Record;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.linecorp.armeria.client.UnprocessedRequestException;
import com.linecorp.armeria.common.AggregatedHttpResponse;
import com.linecorp.armeria.common.ClosedSessionException;
Expand All @@ -22,8 +14,16 @@
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.opensearch.dataprepper.metrics.PluginMetrics;
import org.opensearch.dataprepper.model.CheckpointState;
import org.opensearch.dataprepper.model.event.Event;
import org.opensearch.dataprepper.model.event.JacksonEvent;
import org.opensearch.dataprepper.model.record.Record;
import org.opensearch.dataprepper.peerforwarder.certificate.CertificateProviderFactory;
import org.opensearch.dataprepper.peerforwarder.client.PeerForwarderClient;
import org.opensearch.dataprepper.peerforwarder.codec.PeerForwarderCodecAppConfig;
import org.opensearch.dataprepper.peerforwarder.codec.JacksonPeerForwarderCodec;
import org.opensearch.dataprepper.peerforwarder.codec.JavaPeerForwarderCodec;
import org.opensearch.dataprepper.peerforwarder.codec.PeerForwarderCodec;
Expand All @@ -33,6 +33,7 @@
import org.opensearch.dataprepper.peerforwarder.server.PeerForwarderServer;
import org.opensearch.dataprepper.peerforwarder.server.RemotePeerForwarderServer;
import org.opensearch.dataprepper.peerforwarder.server.ResponseHandler;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import javax.net.ssl.SSLHandshakeException;
import java.util.Collection;
Expand Down Expand Up @@ -71,18 +72,16 @@ class PeerForwarder_ClientServerIT {
private ObjectMapper objectMapper;
private JavaPeerForwarderCodec javaPeerForwarderCodec;
private JacksonPeerForwarderCodec jacksonPeerForwarderCodec;
private PeerForwarderConfiguration peerForwarderConfiguration;
private String pipelineName;
private String pluginId;
private List<Record<Event>> outgoingRecords;
private Set<String> expectedMessages;
private PluginMetrics pluginMetrics;
private AnnotationConfigApplicationContext applicationContext;

@BeforeEach
void setUp() {
objectMapper = new ObjectMapper().registerModule(new JavaTimeModule());
javaPeerForwarderCodec = new JavaPeerForwarderCodec();
jacksonPeerForwarderCodec = new JacksonPeerForwarderCodec(objectMapper);

outgoingRecords = IntStream.range(0, 5)
.mapToObj(i -> UUID.randomUUID().toString())
.map(JacksonEvent::fromMessage)
Expand All @@ -99,12 +98,20 @@ void setUp() {
.collect(Collectors.toSet());
}

private void setupApplicationContext() {
applicationContext = new AnnotationConfigApplicationContext();

applicationContext.scan(PeerForwarderCodecAppConfig.class.getPackage().getName());
applicationContext.registerBean("peerForwarderConfiguration", PeerForwarderConfiguration.class, () -> peerForwarderConfiguration);
applicationContext.refresh();
}


private PeerForwarderServer createServer(
final PeerForwarderConfiguration peerForwarderConfiguration,
final CertificateProviderFactory certificateProviderFactory,
final PeerForwarderProvider peerForwarderProvider) {
final PeerForwarderCodec peerForwarderCodec = peerForwarderConfiguration.getBinaryCodec()?
javaPeerForwarderCodec : jacksonPeerForwarderCodec;
final PeerForwarderCodec peerForwarderCodec = applicationContext.getBean(PeerForwarderCodec.class);
final PeerForwarderHttpService peerForwarderHttpService = new PeerForwarderHttpService(new ResponseHandler(pluginMetrics), peerForwarderProvider, peerForwarderConfiguration,
peerForwarderCodec, pluginMetrics);
Objects.requireNonNull(peerForwarderConfiguration, "Nested classes must supply peerForwarderConfiguration");
Expand Down Expand Up @@ -133,8 +140,7 @@ private PeerForwarderClient createClient(
final PeerClientPool peerClientPool = new PeerClientPool();
final PeerForwarderClientFactory peerForwarderClientFactory = new PeerForwarderClientFactory(peerForwarderConfiguration, peerClientPool, certificateProviderFactory, pluginMetrics);
peerForwarderClientFactory.setPeerClientPool();
final PeerForwarderCodec peerForwarderCodec = peerForwarderConfiguration.getBinaryCodec()?
javaPeerForwarderCodec : jacksonPeerForwarderCodec;
final PeerForwarderCodec peerForwarderCodec = applicationContext.getBean(PeerForwarderCodec.class);
return new PeerForwarderClient(peerForwarderConfiguration, peerForwarderClientFactory, peerForwarderCodec, pluginMetrics);
}

Expand All @@ -149,14 +155,14 @@ private Collection<Record<Event>> getServerSideRecords(final PeerForwarderProvid

@Nested
class WithSSL {

private PeerForwarderConfiguration peerForwarderConfiguration;
private PeerForwarderServer server;
private PeerForwarderProvider peerForwarderProvider;

void setUpServer(final boolean binaryCodec) {
peerForwarderConfiguration = createConfiguration(true, ForwardingAuthentication.UNAUTHENTICATED, binaryCodec);

setupApplicationContext();

final CertificateProviderFactory certificateProviderFactory = new CertificateProviderFactory(peerForwarderConfiguration);
peerForwarderProvider = createPeerForwarderProvider(peerForwarderConfiguration, certificateProviderFactory);
peerForwarderProvider.register(pipelineName, pluginId, Collections.singleton(UUID.randomUUID().toString()), PIPELINE_WORKER_THREADS);
Expand Down Expand Up @@ -269,14 +275,14 @@ void send_Events_with_fingerprint_verification_to_unknown_server_should_throw(fi

@Nested
class WithoutSSL {

private PeerForwarderConfiguration peerForwarderConfiguration;
private PeerForwarderServer server;
private PeerForwarderProvider peerForwarderProvider;

void setUpServer(final boolean binaryCodec) {
peerForwarderConfiguration = createConfiguration(false, ForwardingAuthentication.UNAUTHENTICATED, binaryCodec);

setupApplicationContext();

final CertificateProviderFactory certificateProviderFactory = new CertificateProviderFactory(peerForwarderConfiguration);
peerForwarderProvider = createPeerForwarderProvider(peerForwarderConfiguration, certificateProviderFactory);
peerForwarderProvider.register(pipelineName, pluginId, Collections.singleton(UUID.randomUUID().toString()), PIPELINE_WORKER_THREADS);
Expand Down Expand Up @@ -328,14 +334,14 @@ void send_Events_to_server_when_expecting_SSL_should_throw(final boolean binaryC

@Nested
class WithMutualTls {

private PeerForwarderConfiguration peerForwarderConfiguration;
private PeerForwarderServer server;
private PeerForwarderProvider peerForwarderProvider;

void setUpServer(final boolean binaryCodec) {
peerForwarderConfiguration = createConfiguration(true, ForwardingAuthentication.MUTUAL_TLS, binaryCodec);

setupApplicationContext();

final CertificateProviderFactory certificateProviderFactory = new CertificateProviderFactory(peerForwarderConfiguration);
peerForwarderProvider = createPeerForwarderProvider(peerForwarderConfiguration, certificateProviderFactory);
peerForwarderProvider.register(pipelineName, pluginId, Collections.singleton(UUID.randomUUID().toString()), PIPELINE_WORKER_THREADS);
Expand Down
Loading

0 comments on commit 062ae95

Please sign in to comment.