From 895075f9e55cb4474110283a66333365c5ceae1d Mon Sep 17 00:00:00 2001 From: jason plumb <75337021+breedx-splk@users.noreply.github.com> Date: Tue, 7 Mar 2023 10:54:26 -0800 Subject: [PATCH] Add TlsConfigHelper for additional TLS configurability (#5246) * add TlsConfigHelper for additional TLS configurability and wire up internal builders. * add javadoc and spotless * add keys for "validity" testing. * fix tests * fix tests * address code review comments * fix typo * allow keymanager to be nullable. * fix test * so....much...null......away * backfill tests * checkstyle * test coverage * address code review comments --- .../exporter/internal/TlsConfigHelper.java | 197 +++++++++++++++++ .../exporter/internal/TlsUtil.java | 2 +- .../internal/grpc/GrpcExporterBuilder.java | 33 +-- .../internal/grpc/ManagedChannelUtil.java | 71 +++---- .../okhttp/OkHttpExporterBuilder.java | 49 +++-- .../internal/TlsConfigHelperTest.java | 201 ++++++++++++++++++ .../jaeger/JaegerGrpcSpanExporterBuilder.java | 4 +- .../jaeger/JaegerGrpcSpanExporterTest.java | 17 +- .../OtlpHttpMetricExporterBuilder.java | 4 +- .../trace/OtlpHttpSpanExporterBuilder.java | 4 +- .../OtlpGrpcMetricExporterBuilder.java | 4 +- .../trace/OtlpGrpcSpanExporterBuilder.java | 4 +- .../OtlpHttpLogRecordExporterBuilder.java | 4 +- .../OtlpGrpcLogRecordExporterBuilder.java | 4 +- .../AbstractGrpcTelemetryExporterTest.java | 12 +- .../AbstractHttpTelemetryExporterTest.java | 8 +- ...anagedChannelTelemetryExporterBuilder.java | 27 +-- .../sampler/DefaultGrpcServiceBuilder.java | 26 +-- .../DefaultGrpcServiceBuilderTest.java | 9 +- 19 files changed, 524 insertions(+), 156 deletions(-) create mode 100644 exporters/common/src/main/java/io/opentelemetry/exporter/internal/TlsConfigHelper.java create mode 100644 exporters/common/src/test/java/io/opentelemetry/exporter/internal/TlsConfigHelperTest.java diff --git a/exporters/common/src/main/java/io/opentelemetry/exporter/internal/TlsConfigHelper.java b/exporters/common/src/main/java/io/opentelemetry/exporter/internal/TlsConfigHelper.java new file mode 100644 index 00000000000..ce5555e83d7 --- /dev/null +++ b/exporters/common/src/main/java/io/opentelemetry/exporter/internal/TlsConfigHelper.java @@ -0,0 +1,197 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.exporter.internal; + +import com.google.common.annotations.VisibleForTesting; +import java.util.logging.Logger; +import javax.annotation.Nullable; +import javax.net.ssl.SSLException; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.X509KeyManager; +import javax.net.ssl.X509TrustManager; + +/** + * Utility class to help with management of TLS related components. This class is ultimately + * responsible for enabling TLS via callbacks passed to the configure[...]() methods. This class is + * only intended for internal OpenTelemetry exporter usage and should not be used by end-users. + * + *

This class is internal and is hence not for public use. Its APIs are unstable and can change + * at any time. + */ +public class TlsConfigHelper { + + private static final Logger logger = Logger.getLogger(TlsConfigHelper.class.getName()); + + private final TlsUtility tlsUtil; + + @Nullable private X509KeyManager keyManager; + @Nullable private X509TrustManager trustManager; + @Nullable private SSLSocketFactory sslSocketFactory; + + public TlsConfigHelper() { + this(new TlsUtility() {}); + } + + @VisibleForTesting + TlsConfigHelper(TlsUtility tlsUtil) { + this.tlsUtil = tlsUtil; + } + + /** Sets the X509TrustManager. */ + public TlsConfigHelper setTrustManager(X509TrustManager trustManager) { + this.trustManager = trustManager; + return this; + } + + /** + * Creates a new X509TrustManager from the given cert content. + * + * @param trustedCertsPem Certificate in PEM format. + * @return this + */ + public TlsConfigHelper createTrustManager(byte[] trustedCertsPem) { + try { + this.trustManager = tlsUtil.trustManager(trustedCertsPem); + } catch (SSLException e) { + throw new IllegalStateException( + "Error creating X509TrustManager with provided certs. Are they valid X.509 in PEM format?", + e); + } + return this; + } + + /** + * Creates a new X509KeyManager from the given private key and certificate, both in PEM format. + * + * @param privateKeyPem Private key content in PEM format. + * @param certificatePem Certificate content in PEM format. + * @return this + */ + public TlsConfigHelper createKeyManager(byte[] privateKeyPem, byte[] certificatePem) { + try { + if (keyManager != null) { + logger.warning( + "Previous X509 Key manager is being replaced. This is probably an error and should only be set once."); + } + keyManager = tlsUtil.keyManager(privateKeyPem, certificatePem); + return this; + } catch (SSLException e) { + throw new IllegalStateException( + "Error creating X509KeyManager with provided certs. Are they valid X.509 in PEM format?", + e); + } + } + + /** + * Assigns the X509KeyManager. + * + * @return this + */ + public TlsConfigHelper setKeyManager(X509KeyManager keyManager) { + if (this.keyManager != null) { + logger.warning( + "Previous X509 Key manager is being replaced. This is probably an error and should only be set once."); + } + this.keyManager = keyManager; + return this; + } + + /** + * Sets the SSLSocketFactory, which is passed into the callback within + * configureWithSocketFactory(). + */ + public TlsConfigHelper setSslSocketFactory(SSLSocketFactory sslSocketFactory) { + this.sslSocketFactory = sslSocketFactory; + return this; + } + + /** + * Functional wrapper type used in configure methods. Exists primarily to declare checked + * SSLException. + */ + public interface SslSocketFactoryConfigurer { + void configure(SSLSocketFactory sslSocketFactory, X509TrustManager trustManager) + throws SSLException; + } + + /** + * Functional wrapper type used in configure methods. Exists primarily to declare checked + * SSLException. + */ + public interface KeyManagerConfigurer { + void configure(X509TrustManager trustManager, @Nullable X509KeyManager keyManager) + throws SSLException; + } + + /** + * Configures TLS by invoking the given callback with the X509TrustManager and X509KeyManager. If + * the trust manager or key manager have not yet been configured, this method does nothing. + */ + public void configureWithKeyManager(KeyManagerConfigurer configurer) { + if (trustManager == null) { + return; + } + try { + configurer.configure(trustManager, keyManager); + } catch (SSLException e) { + wrapException(e); + } + } + + /** + * Configures TLS by invoking the provided consumer with a new SSLSocketFactory and the + * preconfigured X509TrustManager. If the trust manager has not been configured, this method does + * nothing. + */ + public void configureWithSocketFactory(SslSocketFactoryConfigurer configurer) { + if (trustManager == null) { + warnIfOtherComponentsConfigured(); + return; + } + + try { + SSLSocketFactory sslSocketFactory = this.sslSocketFactory; + if (sslSocketFactory == null) { + sslSocketFactory = tlsUtil.sslSocketFactory(keyManager, trustManager); + } + configurer.configure(sslSocketFactory, trustManager); + } catch (SSLException e) { + wrapException(e); + } + } + + private static void wrapException(SSLException e) { + throw new IllegalStateException( + "Could not configure TLS connection, are certs in valid X.509 in PEM format?", e); + } + + private void warnIfOtherComponentsConfigured() { + if (sslSocketFactory != null) { + logger.warning("sslSocketFactory has been configured without an X509TrustManager."); + return; + } + if (keyManager != null) { + logger.warning("An X509KeyManager has been configured without an X509TrustManager."); + } + } + + // Exists for testing + interface TlsUtility { + default SSLSocketFactory sslSocketFactory( + @Nullable X509KeyManager keyManager, X509TrustManager trustManager) throws SSLException { + return TlsUtil.sslSocketFactory(keyManager, trustManager); + } + + default X509TrustManager trustManager(byte[] trustedCertificatesPem) throws SSLException { + return TlsUtil.trustManager(trustedCertificatesPem); + } + + default X509KeyManager keyManager(byte[] privateKeyPem, byte[] certificatePem) + throws SSLException { + return TlsUtil.keyManager(privateKeyPem, certificatePem); + } + } +} diff --git a/exporters/common/src/main/java/io/opentelemetry/exporter/internal/TlsUtil.java b/exporters/common/src/main/java/io/opentelemetry/exporter/internal/TlsUtil.java index 83d9d89f2f1..2be05aed694 100644 --- a/exporters/common/src/main/java/io/opentelemetry/exporter/internal/TlsUtil.java +++ b/exporters/common/src/main/java/io/opentelemetry/exporter/internal/TlsUtil.java @@ -89,7 +89,7 @@ public static SSLSocketFactory sslSocketFactory( } /** - * Creates {@link KeyManager} initiaded by keystore containing single private key with matching + * Creates {@link KeyManager} initiated by keystore containing single private key with matching * certificate chain. */ public static X509KeyManager keyManager(byte[] privateKeyPem, byte[] certificatePem) diff --git a/exporters/common/src/main/java/io/opentelemetry/exporter/internal/grpc/GrpcExporterBuilder.java b/exporters/common/src/main/java/io/opentelemetry/exporter/internal/grpc/GrpcExporterBuilder.java index b77d825e324..cade0f018ca 100644 --- a/exporters/common/src/main/java/io/opentelemetry/exporter/internal/grpc/GrpcExporterBuilder.java +++ b/exporters/common/src/main/java/io/opentelemetry/exporter/internal/grpc/GrpcExporterBuilder.java @@ -14,7 +14,7 @@ import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.api.metrics.MeterProvider; import io.opentelemetry.exporter.internal.ExporterBuilderUtil; -import io.opentelemetry.exporter.internal.TlsUtil; +import io.opentelemetry.exporter.internal.TlsConfigHelper; import io.opentelemetry.exporter.internal.marshal.Marshaler; import io.opentelemetry.exporter.internal.okhttp.OkHttpUtil; import io.opentelemetry.exporter.internal.retry.RetryInterceptor; @@ -29,9 +29,6 @@ import java.util.function.BiFunction; import java.util.function.Supplier; import javax.annotation.Nullable; -import javax.net.ssl.SSLException; -import javax.net.ssl.X509KeyManager; -import javax.net.ssl.X509TrustManager; import okhttp3.Headers; import okhttp3.OkHttpClient; import okhttp3.Protocol; @@ -55,9 +52,7 @@ public class GrpcExporterBuilder { private URI endpoint; private boolean compressionEnabled = false; private final Map headers = new HashMap<>(); - @Nullable private byte[] trustedCertificatesPem; - @Nullable private byte[] privateKeyPem; - @Nullable private byte[] certificatePem; + private final TlsConfigHelper tlsConfigHelper = new TlsConfigHelper(); @Nullable private RetryPolicy retryPolicy; private Supplier meterProviderSupplier = GlobalOpenTelemetry::getMeterProvider; @@ -103,14 +98,13 @@ public GrpcExporterBuilder setCompression(String compressionMethod) { return this; } - public GrpcExporterBuilder setTrustedCertificates(byte[] trustedCertificatesPem) { - this.trustedCertificatesPem = trustedCertificatesPem; + public GrpcExporterBuilder configureTrustManager(byte[] trustedCertificatesPem) { + tlsConfigHelper.createTrustManager(trustedCertificatesPem); return this; } - public GrpcExporterBuilder setClientTls(byte[] privateKeyPem, byte[] certificatePem) { - this.privateKeyPem = privateKeyPem; - this.certificatePem = certificatePem; + public GrpcExporterBuilder configureKeyManager(byte[] privateKeyPem, byte[] certificatePem) { + tlsConfigHelper.createKeyManager(privateKeyPem, certificatePem); return this; } @@ -139,20 +133,7 @@ public GrpcExporter build() { clientBuilder.callTimeout(Duration.ofNanos(timeoutNanos)); - if (trustedCertificatesPem != null) { - try { - X509TrustManager trustManager = TlsUtil.trustManager(trustedCertificatesPem); - X509KeyManager keyManager = null; - if (privateKeyPem != null && certificatePem != null) { - keyManager = TlsUtil.keyManager(privateKeyPem, certificatePem); - } - clientBuilder.sslSocketFactory( - TlsUtil.sslSocketFactory(keyManager, trustManager), trustManager); - } catch (SSLException e) { - throw new IllegalStateException( - "Could not set trusted certificates, are they valid X.509 in PEM format?", e); - } - } + tlsConfigHelper.configureWithSocketFactory(clientBuilder::sslSocketFactory); String endpoint = this.endpoint.resolve(grpcEndpointPath).toString(); if (endpoint.startsWith("http://")) { diff --git a/exporters/common/src/main/java/io/opentelemetry/exporter/internal/grpc/ManagedChannelUtil.java b/exporters/common/src/main/java/io/opentelemetry/exporter/internal/grpc/ManagedChannelUtil.java index ce8972b6c61..df2b3141d78 100644 --- a/exporters/common/src/main/java/io/opentelemetry/exporter/internal/grpc/ManagedChannelUtil.java +++ b/exporters/common/src/main/java/io/opentelemetry/exporter/internal/grpc/ManagedChannelUtil.java @@ -12,6 +12,7 @@ import io.grpc.ManagedChannelBuilder; import io.grpc.netty.GrpcSslContexts; import io.grpc.netty.NettyChannelBuilder; +import io.netty.handler.ssl.SslContext; import io.opentelemetry.exporter.internal.TlsUtil; import io.opentelemetry.exporter.internal.retry.RetryPolicy; import io.opentelemetry.exporter.internal.retry.RetryUtil; @@ -46,47 +47,45 @@ public final class ManagedChannelUtil { */ public static void setClientKeysAndTrustedCertificatesPem( ManagedChannelBuilder managedChannelBuilder, - @Nullable byte[] privateKeyPem, - @Nullable byte[] certificatePem, - byte[] trustedCertificatesPem) + X509TrustManager tmf, + @Nullable X509KeyManager kmf) throws SSLException { requireNonNull(managedChannelBuilder, "managedChannelBuilder"); - requireNonNull(trustedCertificatesPem, "trustedCertificatesPem"); - - X509TrustManager tmf = TlsUtil.trustManager(trustedCertificatesPem); - X509KeyManager kmf = null; - if (privateKeyPem != null && certificatePem != null) { - kmf = TlsUtil.keyManager(privateKeyPem, certificatePem); - } + requireNonNull(tmf, "X509TrustManager"); // gRPC does not abstract TLS configuration so we need to check the implementation and act // accordingly. - if (managedChannelBuilder.getClass().getName().equals("io.grpc.netty.NettyChannelBuilder")) { - NettyChannelBuilder nettyBuilder = (NettyChannelBuilder) managedChannelBuilder; - nettyBuilder.sslContext( - GrpcSslContexts.forClient().keyManager(kmf).trustManager(tmf).build()); - } else if (managedChannelBuilder - .getClass() - .getName() - .equals("io.grpc.netty.shaded.io.grpc.netty.NettyChannelBuilder")) { - io.grpc.netty.shaded.io.grpc.netty.NettyChannelBuilder nettyBuilder = - (io.grpc.netty.shaded.io.grpc.netty.NettyChannelBuilder) managedChannelBuilder; - nettyBuilder.sslContext( - io.grpc.netty.shaded.io.grpc.netty.GrpcSslContexts.forClient() - .trustManager(tmf) - .keyManager(kmf) - .build()); - } else if (managedChannelBuilder - .getClass() - .getName() - .equals("io.grpc.okhttp.OkHttpChannelBuilder")) { - io.grpc.okhttp.OkHttpChannelBuilder okHttpBuilder = - (io.grpc.okhttp.OkHttpChannelBuilder) managedChannelBuilder; - okHttpBuilder.sslSocketFactory(TlsUtil.sslSocketFactory(kmf, tmf)); - } else { - throw new SSLException( - "TLS certificate configuration not supported for unrecognized ManagedChannelBuilder " - + managedChannelBuilder.getClass().getName()); + String channelBuilderClassName = managedChannelBuilder.getClass().getName(); + switch (channelBuilderClassName) { + case "io.grpc.netty.NettyChannelBuilder": + { + NettyChannelBuilder nettyBuilder = (NettyChannelBuilder) managedChannelBuilder; + SslContext sslContext = + GrpcSslContexts.forClient().keyManager(kmf).trustManager(tmf).build(); + nettyBuilder.sslContext(sslContext); + break; + } + case "io.grpc.netty.shaded.io.grpc.netty.NettyChannelBuilder": + { + io.grpc.netty.shaded.io.grpc.netty.NettyChannelBuilder nettyBuilder = + (io.grpc.netty.shaded.io.grpc.netty.NettyChannelBuilder) managedChannelBuilder; + io.grpc.netty.shaded.io.netty.handler.ssl.SslContext sslContext = + io.grpc.netty.shaded.io.grpc.netty.GrpcSslContexts.forClient() + .trustManager(tmf) + .keyManager(kmf) + .build(); + nettyBuilder.sslContext(sslContext); + break; + } + case "io.grpc.okhttp.OkHttpChannelBuilder": + io.grpc.okhttp.OkHttpChannelBuilder okHttpBuilder = + (io.grpc.okhttp.OkHttpChannelBuilder) managedChannelBuilder; + okHttpBuilder.sslSocketFactory(TlsUtil.sslSocketFactory(kmf, tmf)); + break; + default: + throw new SSLException( + "TLS certificate configuration not supported for unrecognized ManagedChannelBuilder " + + channelBuilderClassName); } } diff --git a/exporters/common/src/main/java/io/opentelemetry/exporter/internal/okhttp/OkHttpExporterBuilder.java b/exporters/common/src/main/java/io/opentelemetry/exporter/internal/okhttp/OkHttpExporterBuilder.java index 37c7090fc0e..39bc659d9d9 100644 --- a/exporters/common/src/main/java/io/opentelemetry/exporter/internal/okhttp/OkHttpExporterBuilder.java +++ b/exporters/common/src/main/java/io/opentelemetry/exporter/internal/okhttp/OkHttpExporterBuilder.java @@ -8,7 +8,7 @@ import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.api.metrics.MeterProvider; import io.opentelemetry.exporter.internal.ExporterBuilderUtil; -import io.opentelemetry.exporter.internal.TlsUtil; +import io.opentelemetry.exporter.internal.TlsConfigHelper; import io.opentelemetry.exporter.internal.auth.Authenticator; import io.opentelemetry.exporter.internal.marshal.Marshaler; import io.opentelemetry.exporter.internal.retry.RetryInterceptor; @@ -18,7 +18,7 @@ import java.util.concurrent.TimeUnit; import java.util.function.Supplier; import javax.annotation.Nullable; -import javax.net.ssl.SSLException; +import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.X509KeyManager; import javax.net.ssl.X509TrustManager; import okhttp3.Headers; @@ -44,9 +44,8 @@ public final class OkHttpExporterBuilder { private boolean compressionEnabled = false; private boolean exportAsJson = false; @Nullable private Headers.Builder headersBuilder; - @Nullable private byte[] trustedCertificatesPem; - @Nullable private byte[] privateKeyPem; - @Nullable private byte[] certificatePem; + + private final TlsConfigHelper tlsConfigHelper = new TlsConfigHelper(); @Nullable private RetryPolicy retryPolicy; private Supplier meterProviderSupplier = GlobalOpenTelemetry::getMeterProvider; @Nullable private Authenticator authenticator; @@ -91,14 +90,28 @@ public OkHttpExporterBuilder setAuthenticator(Authenticator authenticator) { return this; } - public OkHttpExporterBuilder setTrustedCertificates(byte[] trustedCertificatesPem) { - this.trustedCertificatesPem = trustedCertificatesPem; + public OkHttpExporterBuilder configureTrustManager(byte[] trustedCertificatesPem) { + tlsConfigHelper.createTrustManager(trustedCertificatesPem); + return this; + } + + public OkHttpExporterBuilder setTrustManager(X509TrustManager trustManager) { + tlsConfigHelper.setTrustManager(trustManager); return this; } - public OkHttpExporterBuilder setClientTls(byte[] privateKeyPem, byte[] certificatePem) { - this.privateKeyPem = privateKeyPem; - this.certificatePem = certificatePem; + public OkHttpExporterBuilder configureKeyManager(byte[] privateKeyPem, byte[] certificatePem) { + tlsConfigHelper.createKeyManager(privateKeyPem, certificatePem); + return this; + } + + public OkHttpExporterBuilder setKeyManager(X509KeyManager keyManager) { + tlsConfigHelper.setKeyManager(keyManager); + return this; + } + + public OkHttpExporterBuilder setSslSocketFactory(SSLSocketFactory sslSocketFactory) { + tlsConfigHelper.setSslSocketFactory(sslSocketFactory); return this; } @@ -123,21 +136,7 @@ public OkHttpExporter build() { .dispatcher(OkHttpUtil.newDispatcher()) .callTimeout(Duration.ofNanos(timeoutNanos)); - if (trustedCertificatesPem != null) { - try { - X509TrustManager trustManager = TlsUtil.trustManager(trustedCertificatesPem); - X509KeyManager keyManager = null; - if (privateKeyPem != null && certificatePem != null) { - keyManager = TlsUtil.keyManager(privateKeyPem, certificatePem); - } - clientBuilder.sslSocketFactory( - TlsUtil.sslSocketFactory(keyManager, trustManager), trustManager); - } catch (SSLException e) { - throw new IllegalStateException( - "Could not set trusted certificate for OTLP HTTP connection, are they valid X.509 in PEM format?", - e); - } - } + tlsConfigHelper.configureWithSocketFactory(clientBuilder::sslSocketFactory); Headers headers = headersBuilder == null ? null : headersBuilder.build(); diff --git a/exporters/common/src/test/java/io/opentelemetry/exporter/internal/TlsConfigHelperTest.java b/exporters/common/src/test/java/io/opentelemetry/exporter/internal/TlsConfigHelperTest.java new file mode 100644 index 00000000000..ccdac920265 --- /dev/null +++ b/exporters/common/src/test/java/io/opentelemetry/exporter/internal/TlsConfigHelperTest.java @@ -0,0 +1,201 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.exporter.internal; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.fail; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verifyNoInteractions; +import static org.mockito.Mockito.when; + +import com.linecorp.armeria.testing.junit5.server.SelfSignedCertificateExtension; +import io.github.netmikey.logunit.api.LogCapturer; +import java.util.concurrent.atomic.AtomicReference; +import javax.net.ssl.SSLException; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.X509KeyManager; +import javax.net.ssl.X509TrustManager; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +class TlsConfigHelperTest { + @RegisterExtension + static final SelfSignedCertificateExtension serverTls = new SelfSignedCertificateExtension(); + + @RegisterExtension + LogCapturer logs = LogCapturer.create().captureForLogger(TlsConfigHelper.class.getName()); + + @Mock TlsConfigHelper.TlsUtility tlsUtil; + + @Test + void createTrustManager_exceptionMapped() throws Exception { + when(tlsUtil.trustManager(any())).thenThrow(new SSLException("boom")); + TlsConfigHelper helper = new TlsConfigHelper(tlsUtil); + assertThrows( + IllegalStateException.class, + () -> { + helper.createTrustManager(serverTls.certificate().getEncoded()); + }); + } + + @Test + void createKeymanager_exceptionMapped() throws Exception { + when(tlsUtil.keyManager(any(), any())).thenThrow(new SSLException("boom")); + TlsConfigHelper helper = new TlsConfigHelper(tlsUtil); + assertThrows( + IllegalStateException.class, + () -> { + helper.createKeyManager( + serverTls.privateKey().getEncoded(), serverTls.certificate().getEncoded()); + }); + } + + @Test + void socketFactory_happyPathWithBytes() throws Exception { + + byte[] cert = serverTls.certificate().getEncoded(); + byte[] key = serverTls.privateKey().getEncoded(); + AtomicReference seenTrustManager = new AtomicReference<>(); + AtomicReference seenSocketFactory = new AtomicReference<>(); + + X509TrustManager trustManager = mock(X509TrustManager.class); + X509KeyManager keyManager = mock(X509KeyManager.class); + SSLSocketFactory sslSocketFactory = mock(SSLSocketFactory.class); + + when(tlsUtil.trustManager(cert)).thenReturn(trustManager); + when(tlsUtil.keyManager(key, cert)).thenReturn(keyManager); + when(tlsUtil.sslSocketFactory(keyManager, trustManager)).thenReturn(sslSocketFactory); + + TlsConfigHelper helper = new TlsConfigHelper(tlsUtil); + + helper.createKeyManager(key, cert); + helper.createTrustManager(cert); + helper.configureWithSocketFactory( + (sf, tm) -> { + seenTrustManager.set(tm); + seenSocketFactory.set(sf); + }); + assertSame(trustManager, seenTrustManager.get()); + assertSame(sslSocketFactory, seenSocketFactory.get()); + } + + @Test + void socketFactory_noTrustManager() { + TlsConfigHelper helper = new TlsConfigHelper(tlsUtil); + helper.configureWithSocketFactory( + (sf, tm) -> { + fail("Should not have called due to missing trust manager"); + }); + verifyNoInteractions(tlsUtil); + } + + @Test + void socketFactoryCallbackExceptionHandled() { + X509TrustManager trustManager = mock(X509TrustManager.class); + + TlsConfigHelper helper = new TlsConfigHelper(tlsUtil); + + helper.setTrustManager(trustManager); + assertThrows( + IllegalStateException.class, + () -> { + helper.configureWithSocketFactory( + (sf, tm) -> { + throw new SSLException("bad times"); + }); + }); + } + + @Test + void configureWithKeyManagerHappyPath() { + AtomicReference seenTrustManager = new AtomicReference<>(); + AtomicReference seenKeyManager = new AtomicReference<>(); + + X509TrustManager trustManager = mock(X509TrustManager.class); + X509KeyManager keyManager = mock(X509KeyManager.class); + + TlsConfigHelper helper = new TlsConfigHelper(tlsUtil); + + helper.setTrustManager(trustManager); + helper.setKeyManager(keyManager); + helper.configureWithKeyManager( + (tm, km) -> { + seenTrustManager.set(tm); + seenKeyManager.set(km); + }); + assertThat(seenTrustManager.get()).isSameAs(trustManager); + assertThat(seenKeyManager.get()).isSameAs(keyManager); + } + + @Test + void configureWithKeyManagerExceptionHandled() { + X509TrustManager trustManager = mock(X509TrustManager.class); + X509KeyManager keyManager = mock(X509KeyManager.class); + + TlsConfigHelper helper = new TlsConfigHelper(tlsUtil); + + helper.setTrustManager(trustManager); + helper.setKeyManager(keyManager); + assertThrows( + IllegalStateException.class, + () -> + helper.configureWithKeyManager( + (trustManager1, keyManager1) -> { + throw new SSLException("ouchey"); + })); + } + + @Test + void configureWithKeyManagerNoTrustManager() { + TlsConfigHelper helper = new TlsConfigHelper(tlsUtil); + helper.configureWithKeyManager( + (tm, km) -> { + fail(); + }); + verifyNoInteractions(tlsUtil); + } + + @Test + void setKeyManagerReplacesAndWarns() { + X509KeyManager keyManager1 = mock(X509KeyManager.class); + X509KeyManager keyManager2 = mock(X509KeyManager.class); + + TlsConfigHelper helper = new TlsConfigHelper(tlsUtil); + + helper.setTrustManager(mock(X509TrustManager.class)); + helper.setKeyManager(keyManager1); + helper.setKeyManager(keyManager2); + + helper.configureWithKeyManager((tm, km) -> assertSame(km, keyManager2)); + logs.assertContains("Previous X509 Key manager is being replaced"); + } + + @Test + void createKeyManagerReplacesAndWarns() throws Exception { + X509KeyManager keyManager1 = mock(X509KeyManager.class); + X509KeyManager keyManager2 = mock(X509KeyManager.class); + + byte[] cert = serverTls.certificate().getEncoded(); + byte[] key = serverTls.privateKey().getEncoded(); + + when(tlsUtil.keyManager(key, cert)).thenReturn(keyManager2); + TlsConfigHelper helper = new TlsConfigHelper(tlsUtil); + + helper.setTrustManager(mock(X509TrustManager.class)); + helper.setKeyManager(keyManager1); + helper.createKeyManager(key, cert); + + helper.configureWithKeyManager((tm, km) -> assertSame(km, keyManager2)); + logs.assertContains("Previous X509 Key manager is being replaced"); + } +} diff --git a/exporters/jaeger/src/main/java/io/opentelemetry/exporter/jaeger/JaegerGrpcSpanExporterBuilder.java b/exporters/jaeger/src/main/java/io/opentelemetry/exporter/jaeger/JaegerGrpcSpanExporterBuilder.java index c30ad9f1651..8c8d9671ea0 100644 --- a/exporters/jaeger/src/main/java/io/opentelemetry/exporter/jaeger/JaegerGrpcSpanExporterBuilder.java +++ b/exporters/jaeger/src/main/java/io/opentelemetry/exporter/jaeger/JaegerGrpcSpanExporterBuilder.java @@ -109,13 +109,13 @@ public JaegerGrpcSpanExporterBuilder setTimeout(Duration timeout) { * use the system default trusted certificates. */ public JaegerGrpcSpanExporterBuilder setTrustedCertificates(byte[] trustedCertificatesPem) { - delegate.setTrustedCertificates(trustedCertificatesPem); + delegate.configureTrustManager(trustedCertificatesPem); return this; } /** Sets the client key and chain to use for verifying servers when mTLS is enabled. */ public JaegerGrpcSpanExporterBuilder setClientTls(byte[] privateKeyPem, byte[] certificatePem) { - delegate.setClientTls(privateKeyPem, certificatePem); + delegate.configureKeyManager(privateKeyPem, certificatePem); return this; } diff --git a/exporters/jaeger/src/test/java/io/opentelemetry/exporter/jaeger/JaegerGrpcSpanExporterTest.java b/exporters/jaeger/src/test/java/io/opentelemetry/exporter/jaeger/JaegerGrpcSpanExporterTest.java index 0d364bdbc82..d9b0e66f1db 100644 --- a/exporters/jaeger/src/test/java/io/opentelemetry/exporter/jaeger/JaegerGrpcSpanExporterTest.java +++ b/exporters/jaeger/src/test/java/io/opentelemetry/exporter/jaeger/JaegerGrpcSpanExporterTest.java @@ -14,6 +14,7 @@ import com.linecorp.armeria.server.ServerBuilder; import com.linecorp.armeria.server.ServiceRequestContext; import com.linecorp.armeria.server.grpc.protocol.AbstractUnaryGrpcService; +import com.linecorp.armeria.testing.junit5.server.SelfSignedCertificateExtension; import com.linecorp.armeria.testing.junit5.server.ServerExtension; import io.github.netmikey.logunit.api.LogCapturer; import io.opentelemetry.api.common.AttributeKey; @@ -39,7 +40,6 @@ import io.opentelemetry.semconv.resource.attributes.ResourceAttributes; import java.net.InetAddress; import java.net.URISyntaxException; -import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -88,6 +88,12 @@ protected CompletionStage handleMessage( @RegisterExtension LogCapturer logs = LogCapturer.create().captureForType(OkHttpGrpcExporter.class); + @RegisterExtension + static final SelfSignedCertificateExtension serverTls = new SelfSignedCertificateExtension(); + + @RegisterExtension + static final SelfSignedCertificateExtension clientTls = new SelfSignedCertificateExtension(); + private static JaegerGrpcSpanExporter exporter; @BeforeAll @@ -285,22 +291,21 @@ private static SpanData testSpanData(Resource resource, String spanName) { } @Test - void validTrustedConfig() { + void validTrustedConfig() throws Exception { assertThatCode( () -> JaegerGrpcSpanExporter.builder() - .setTrustedCertificates("foobar".getBytes(StandardCharsets.UTF_8))) + .setTrustedCertificates(serverTls.certificate().getEncoded())) .doesNotThrowAnyException(); } @Test - void validClientKeyConfig() { + void validClientKeyConfig() throws Exception { assertThatCode( () -> JaegerGrpcSpanExporter.builder() .setClientTls( - "foobar".getBytes(StandardCharsets.UTF_8), - "foobar".getBytes(StandardCharsets.UTF_8))) + clientTls.privateKey().getEncoded(), serverTls.certificate().getEncoded())) .doesNotThrowAnyException(); } diff --git a/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/http/metrics/OtlpHttpMetricExporterBuilder.java b/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/http/metrics/OtlpHttpMetricExporterBuilder.java index 83c19c14534..1ab0f5c8e05 100644 --- a/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/http/metrics/OtlpHttpMetricExporterBuilder.java +++ b/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/http/metrics/OtlpHttpMetricExporterBuilder.java @@ -99,7 +99,7 @@ public OtlpHttpMetricExporterBuilder addHeader(String key, String value) { * use the system default trusted certificates. */ public OtlpHttpMetricExporterBuilder setTrustedCertificates(byte[] trustedCertificatesPem) { - delegate.setTrustedCertificates(trustedCertificatesPem); + delegate.configureTrustManager(trustedCertificatesPem); return this; } @@ -108,7 +108,7 @@ public OtlpHttpMetricExporterBuilder setTrustedCertificates(byte[] trustedCertif * The key must be PKCS8, and both must be in PEM format. */ public OtlpHttpMetricExporterBuilder setClientTls(byte[] privateKeyPem, byte[] certificatePem) { - delegate.setClientTls(privateKeyPem, certificatePem); + delegate.configureKeyManager(privateKeyPem, certificatePem); return this; } diff --git a/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/http/trace/OtlpHttpSpanExporterBuilder.java b/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/http/trace/OtlpHttpSpanExporterBuilder.java index 6ce05a36482..3d8ddbc68df 100644 --- a/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/http/trace/OtlpHttpSpanExporterBuilder.java +++ b/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/http/trace/OtlpHttpSpanExporterBuilder.java @@ -87,7 +87,7 @@ public OtlpHttpSpanExporterBuilder addHeader(String key, String value) { * use the system default trusted certificates. */ public OtlpHttpSpanExporterBuilder setTrustedCertificates(byte[] trustedCertificatesPem) { - delegate.setTrustedCertificates(trustedCertificatesPem); + delegate.configureTrustManager(trustedCertificatesPem); return this; } @@ -96,7 +96,7 @@ public OtlpHttpSpanExporterBuilder setTrustedCertificates(byte[] trustedCertific * The key must be PKCS8, and both must be in PEM format. */ public OtlpHttpSpanExporterBuilder setClientTls(byte[] privateKeyPem, byte[] certificatePem) { - delegate.setClientTls(privateKeyPem, certificatePem); + delegate.configureKeyManager(privateKeyPem, certificatePem); return this; } diff --git a/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/metrics/OtlpGrpcMetricExporterBuilder.java b/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/metrics/OtlpGrpcMetricExporterBuilder.java index 3bc796b36e9..5197c17887e 100644 --- a/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/metrics/OtlpGrpcMetricExporterBuilder.java +++ b/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/metrics/OtlpGrpcMetricExporterBuilder.java @@ -131,7 +131,7 @@ public OtlpGrpcMetricExporterBuilder setCompression(String compressionMethod) { * use the system default trusted certificates. */ public OtlpGrpcMetricExporterBuilder setTrustedCertificates(byte[] trustedCertificatesPem) { - delegate.setTrustedCertificates(trustedCertificatesPem); + delegate.configureTrustManager(trustedCertificatesPem); return this; } @@ -140,7 +140,7 @@ public OtlpGrpcMetricExporterBuilder setTrustedCertificates(byte[] trustedCertif * The key must be PKCS8, and both must be in PEM format. */ public OtlpGrpcMetricExporterBuilder setClientTls(byte[] privateKeyPem, byte[] certificatePem) { - delegate.setClientTls(privateKeyPem, certificatePem); + delegate.configureKeyManager(privateKeyPem, certificatePem); return this; } diff --git a/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/trace/OtlpGrpcSpanExporterBuilder.java b/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/trace/OtlpGrpcSpanExporterBuilder.java index 505f8e56dcc..8ead941f91d 100644 --- a/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/trace/OtlpGrpcSpanExporterBuilder.java +++ b/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/trace/OtlpGrpcSpanExporterBuilder.java @@ -115,7 +115,7 @@ public OtlpGrpcSpanExporterBuilder setCompression(String compressionMethod) { * use the system default trusted certificates. */ public OtlpGrpcSpanExporterBuilder setTrustedCertificates(byte[] trustedCertificatesPem) { - delegate.setTrustedCertificates(trustedCertificatesPem); + delegate.configureTrustManager(trustedCertificatesPem); return this; } @@ -124,7 +124,7 @@ public OtlpGrpcSpanExporterBuilder setTrustedCertificates(byte[] trustedCertific * The key must be PKCS8, and both must be in PEM format. */ public OtlpGrpcSpanExporterBuilder setClientTls(byte[] privateKeyPem, byte[] certificatePem) { - delegate.setClientTls(privateKeyPem, certificatePem); + delegate.configureKeyManager(privateKeyPem, certificatePem); return this; } diff --git a/exporters/otlp/logs/src/main/java/io/opentelemetry/exporter/otlp/http/logs/OtlpHttpLogRecordExporterBuilder.java b/exporters/otlp/logs/src/main/java/io/opentelemetry/exporter/otlp/http/logs/OtlpHttpLogRecordExporterBuilder.java index 4a201104e8d..657b2dd819b 100644 --- a/exporters/otlp/logs/src/main/java/io/opentelemetry/exporter/otlp/http/logs/OtlpHttpLogRecordExporterBuilder.java +++ b/exporters/otlp/logs/src/main/java/io/opentelemetry/exporter/otlp/http/logs/OtlpHttpLogRecordExporterBuilder.java @@ -83,7 +83,7 @@ public OtlpHttpLogRecordExporterBuilder addHeader(String key, String value) { * use the system default trusted certificates. */ public OtlpHttpLogRecordExporterBuilder setTrustedCertificates(byte[] trustedCertificatesPem) { - delegate.setTrustedCertificates(trustedCertificatesPem); + delegate.configureTrustManager(trustedCertificatesPem); return this; } @@ -93,7 +93,7 @@ public OtlpHttpLogRecordExporterBuilder setTrustedCertificates(byte[] trustedCer */ public OtlpHttpLogRecordExporterBuilder setClientTls( byte[] privateKeyPem, byte[] certificatePem) { - delegate.setClientTls(privateKeyPem, certificatePem); + delegate.configureKeyManager(privateKeyPem, certificatePem); return this; } diff --git a/exporters/otlp/logs/src/main/java/io/opentelemetry/exporter/otlp/logs/OtlpGrpcLogRecordExporterBuilder.java b/exporters/otlp/logs/src/main/java/io/opentelemetry/exporter/otlp/logs/OtlpGrpcLogRecordExporterBuilder.java index 2802bbdc5f0..f6ec0e0b908 100644 --- a/exporters/otlp/logs/src/main/java/io/opentelemetry/exporter/otlp/logs/OtlpGrpcLogRecordExporterBuilder.java +++ b/exporters/otlp/logs/src/main/java/io/opentelemetry/exporter/otlp/logs/OtlpGrpcLogRecordExporterBuilder.java @@ -115,7 +115,7 @@ public OtlpGrpcLogRecordExporterBuilder setCompression(String compressionMethod) * use the system default trusted certificates. */ public OtlpGrpcLogRecordExporterBuilder setTrustedCertificates(byte[] trustedCertificatesPem) { - delegate.setTrustedCertificates(trustedCertificatesPem); + delegate.configureTrustManager(trustedCertificatesPem); return this; } @@ -125,7 +125,7 @@ public OtlpGrpcLogRecordExporterBuilder setTrustedCertificates(byte[] trustedCer */ public OtlpGrpcLogRecordExporterBuilder setClientTls( byte[] privateKeyPem, byte[] certificatePem) { - delegate.setClientTls(privateKeyPem, certificatePem); + delegate.configureKeyManager(privateKeyPem, certificatePem); return this; } diff --git a/exporters/otlp/testing-internal/src/main/java/io/opentelemetry/exporter/otlp/testing/internal/AbstractGrpcTelemetryExporterTest.java b/exporters/otlp/testing-internal/src/main/java/io/opentelemetry/exporter/otlp/testing/internal/AbstractGrpcTelemetryExporterTest.java index ea7001f602f..6b45c8a0f9c 100644 --- a/exporters/otlp/testing-internal/src/main/java/io/opentelemetry/exporter/otlp/testing/internal/AbstractGrpcTelemetryExporterTest.java +++ b/exporters/otlp/testing-internal/src/main/java/io/opentelemetry/exporter/otlp/testing/internal/AbstractGrpcTelemetryExporterTest.java @@ -304,8 +304,10 @@ void withHeaders() { void tls() throws Exception { TelemetryExporter exporter = exporterBuilder() - .setEndpoint(server.httpsUri().toString()) .setTrustedCertificates(Files.readAllBytes(certificate.certificateFile().toPath())) + .setClientTls( + certificate.privateKey().getEncoded(), certificate.certificate().getEncoded()) + .setEndpoint(server.httpsUri().toString()) .build(); try { CompletableResultCode result = @@ -337,10 +339,9 @@ void tls_badCert() { () -> exporterBuilder() .setEndpoint(server.httpsUri().toString()) - .setTrustedCertificates("foobar".getBytes(StandardCharsets.UTF_8)) - .build()) + .setTrustedCertificates("foobar".getBytes(StandardCharsets.UTF_8))) .isInstanceOf(IllegalStateException.class) - .hasMessageContaining("Could not set trusted certificates"); + .hasMessageContaining("Error creating X509TrustManager with provided certs."); } @ParameterizedTest @@ -652,8 +653,7 @@ void validConfig() { .doesNotThrowAnyException(); assertThatCode( - () -> - exporterBuilder().setTrustedCertificates("foobar".getBytes(StandardCharsets.UTF_8))) + () -> exporterBuilder().setTrustedCertificates(certificate.certificate().getEncoded())) .doesNotThrowAnyException(); } diff --git a/exporters/otlp/testing-internal/src/main/java/io/opentelemetry/exporter/otlp/testing/internal/AbstractHttpTelemetryExporterTest.java b/exporters/otlp/testing-internal/src/main/java/io/opentelemetry/exporter/otlp/testing/internal/AbstractHttpTelemetryExporterTest.java index 47f26524435..10e6b3d1fdf 100644 --- a/exporters/otlp/testing-internal/src/main/java/io/opentelemetry/exporter/otlp/testing/internal/AbstractHttpTelemetryExporterTest.java +++ b/exporters/otlp/testing-internal/src/main/java/io/opentelemetry/exporter/otlp/testing/internal/AbstractHttpTelemetryExporterTest.java @@ -368,10 +368,9 @@ void tls_badCert() { () -> exporterBuilder() .setEndpoint(server.httpsUri() + path) - .setTrustedCertificates("foobar".getBytes(StandardCharsets.UTF_8)) - .build()) + .setTrustedCertificates("foobar".getBytes(StandardCharsets.UTF_8))) .isInstanceOf(IllegalStateException.class) - .hasMessageContaining("Could not set trusted certificate"); + .hasMessageContaining("Error creating X509TrustManager with provided certs"); } @ParameterizedTest @@ -555,8 +554,7 @@ void validConfig() { .doesNotThrowAnyException(); assertThatCode( - () -> - exporterBuilder().setTrustedCertificates("foobar".getBytes(StandardCharsets.UTF_8))) + () -> exporterBuilder().setTrustedCertificates(certificate.certificate().getEncoded())) .doesNotThrowAnyException(); } diff --git a/exporters/otlp/testing-internal/src/main/java/io/opentelemetry/exporter/otlp/testing/internal/ManagedChannelTelemetryExporterBuilder.java b/exporters/otlp/testing-internal/src/main/java/io/opentelemetry/exporter/otlp/testing/internal/ManagedChannelTelemetryExporterBuilder.java index 7ef720419f6..7ff40964207 100644 --- a/exporters/otlp/testing-internal/src/main/java/io/opentelemetry/exporter/otlp/testing/internal/ManagedChannelTelemetryExporterBuilder.java +++ b/exporters/otlp/testing-internal/src/main/java/io/opentelemetry/exporter/otlp/testing/internal/ManagedChannelTelemetryExporterBuilder.java @@ -9,6 +9,7 @@ import io.grpc.ManagedChannel; import io.grpc.ManagedChannelBuilder; +import io.opentelemetry.exporter.internal.TlsConfigHelper; import io.opentelemetry.exporter.internal.grpc.ManagedChannelUtil; import io.opentelemetry.exporter.internal.otlp.OtlpUserAgent; import io.opentelemetry.exporter.internal.retry.RetryPolicy; @@ -18,7 +19,6 @@ import java.util.Collection; import java.util.concurrent.TimeUnit; import javax.annotation.Nullable; -import javax.net.ssl.SSLException; /** * Wraps a {@link TelemetryExporterBuilder}, delegating methods to upstream gRPC's {@link @@ -40,9 +40,7 @@ private ManagedChannelTelemetryExporterBuilder(TelemetryExporterBuilder deleg @Nullable private ManagedChannelBuilder channelBuilder; - @Nullable private byte[] privateKeyPem; - @Nullable private byte[] certificatePem; - @Nullable private byte[] trustedCertificatesPem; + private final TlsConfigHelper tlsConfigHelper = new TlsConfigHelper(); @Override public TelemetryExporterBuilder setEndpoint(String endpoint) { @@ -89,14 +87,13 @@ public TelemetryExporterBuilder addHeader(String key, String value) { @Override public TelemetryExporterBuilder setTrustedCertificates(byte[] certificates) { - this.trustedCertificatesPem = certificates; + tlsConfigHelper.createTrustManager(certificates); return this; } @Override public TelemetryExporterBuilder setClientTls(byte[] privateKeyPem, byte[] certificatePem) { - this.privateKeyPem = privateKeyPem; - this.certificatePem = certificatePem; + tlsConfigHelper.createKeyManager(privateKeyPem, certificatePem); return this; } @@ -126,15 +123,13 @@ public TelemetryExporterBuilder setChannel(ManagedChannel channel) { @Override public TelemetryExporter build() { requireNonNull(channelBuilder, "channel"); - if (trustedCertificatesPem != null) { - try { - ManagedChannelUtil.setClientKeysAndTrustedCertificatesPem( - channelBuilder, privateKeyPem, certificatePem, trustedCertificatesPem); - } catch (SSLException e) { - throw new IllegalStateException( - "Could not set trusted certificates, are they valid X.509 in PEM format?", e); - } - } + + tlsConfigHelper.configureWithKeyManager( + (tm, km) -> { + requireNonNull(channelBuilder, "channel"); + ManagedChannelUtil.setClientKeysAndTrustedCertificatesPem(channelBuilder, tm, km); + }); + ManagedChannel channel = channelBuilder.build(); delegate.setChannel(channel); TelemetryExporter delegateExporter = delegate.build(); diff --git a/sdk-extensions/jaeger-remote-sampler/src/main/java/io/opentelemetry/sdk/extension/trace/jaeger/sampler/DefaultGrpcServiceBuilder.java b/sdk-extensions/jaeger-remote-sampler/src/main/java/io/opentelemetry/sdk/extension/trace/jaeger/sampler/DefaultGrpcServiceBuilder.java index b7e7a398c4b..e0d39cfa23b 100644 --- a/sdk-extensions/jaeger-remote-sampler/src/main/java/io/opentelemetry/sdk/extension/trace/jaeger/sampler/DefaultGrpcServiceBuilder.java +++ b/sdk-extensions/jaeger-remote-sampler/src/main/java/io/opentelemetry/sdk/extension/trace/jaeger/sampler/DefaultGrpcServiceBuilder.java @@ -16,6 +16,7 @@ import io.grpc.Metadata; import io.grpc.stub.MetadataUtils; import io.opentelemetry.exporter.internal.ExporterBuilderUtil; +import io.opentelemetry.exporter.internal.TlsConfigHelper; import io.opentelemetry.exporter.internal.grpc.ManagedChannelUtil; import io.opentelemetry.exporter.internal.grpc.MarshalerServiceStub; import io.opentelemetry.exporter.internal.marshal.Marshaler; @@ -25,7 +26,6 @@ import java.util.concurrent.TimeUnit; import java.util.function.Function; import javax.annotation.Nullable; -import javax.net.ssl.SSLException; final class DefaultGrpcServiceBuilder implements GrpcServiceBuilder { @@ -39,10 +39,8 @@ final class DefaultGrpcServiceBuilder setCompression(String compressionMe public DefaultGrpcServiceBuilder setTrustedCertificates( byte[] trustedCertificatesPem) { requireNonNull(trustedCertificatesPem, "trustedCertificatesPem"); - this.trustedCertificatesPem = trustedCertificatesPem; + tlsConfigHelper.createTrustManager(trustedCertificatesPem); return this; } @Override public GrpcServiceBuilder setClientTls(byte[] privateKeyPem, byte[] certificatePem) { - this.privateKeyPem = privateKeyPem; - this.certificatePem = certificatePem; + tlsConfigHelper.createKeyManager(privateKeyPem, certificatePem); return this; } @@ -147,17 +144,10 @@ public GrpcService build() { managedChannelBuilder.intercept(MetadataUtils.newAttachHeadersInterceptor(metadata)); } - if (trustedCertificatesPem != null) { - try { - ManagedChannelUtil.setClientKeysAndTrustedCertificatesPem( - managedChannelBuilder, privateKeyPem, certificatePem, trustedCertificatesPem); - } catch (SSLException e) { - throw new IllegalStateException( - "Could not set trusted certificates for gRPC TLS connection, are they valid " - + "X.509 in PEM format?", - e); - } - } + tlsConfigHelper.configureWithKeyManager( + (tm, km) -> + ManagedChannelUtil.setClientKeysAndTrustedCertificatesPem( + managedChannelBuilder, tm, km)); if (retryPolicy != null) { managedChannelBuilder.defaultServiceConfig(toServiceConfig(grpcServiceName, retryPolicy)); diff --git a/sdk-extensions/jaeger-remote-sampler/src/testGrpcNetty/java/io/opentelemetry/sdk/extension/trace/jaeger/sampler/DefaultGrpcServiceBuilderTest.java b/sdk-extensions/jaeger-remote-sampler/src/testGrpcNetty/java/io/opentelemetry/sdk/extension/trace/jaeger/sampler/DefaultGrpcServiceBuilderTest.java index dd8d231e03c..efe3bc193b6 100644 --- a/sdk-extensions/jaeger-remote-sampler/src/testGrpcNetty/java/io/opentelemetry/sdk/extension/trace/jaeger/sampler/DefaultGrpcServiceBuilderTest.java +++ b/sdk-extensions/jaeger-remote-sampler/src/testGrpcNetty/java/io/opentelemetry/sdk/extension/trace/jaeger/sampler/DefaultGrpcServiceBuilderTest.java @@ -8,15 +8,19 @@ import static org.assertj.core.api.Assertions.assertThatCode; import static org.assertj.core.api.Assertions.assertThatThrownBy; +import com.linecorp.armeria.testing.junit5.server.SelfSignedCertificateExtension; import io.opentelemetry.exporter.internal.retry.RetryPolicy; import java.net.URI; -import java.nio.charset.StandardCharsets; import java.time.Duration; import java.util.concurrent.TimeUnit; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; class DefaultGrpcServiceBuilderTest { + @RegisterExtension + static final SelfSignedCertificateExtension serverTls = new SelfSignedCertificateExtension(); + private static DefaultGrpcServiceBuilder< SamplingStrategyParametersMarshaler, SamplingStrategyResponseUnMarshaler> exporterBuilder() { @@ -52,8 +56,7 @@ void validConfig() { .doesNotThrowAnyException(); assertThatCode( - () -> - exporterBuilder().setTrustedCertificates("foobar".getBytes(StandardCharsets.UTF_8))) + () -> exporterBuilder().setTrustedCertificates(serverTls.certificate().getEncoded())) .doesNotThrowAnyException(); assertThatCode(() -> exporterBuilder().addRetryPolicy(RetryPolicy.getDefault()))