Allow client certificate (#4194)
* Allow client certificate * Allow client certificate - changes signature and add parameters * Allow client certificate - rename chain to certificate * Allow client certificate - fix first set of comments * Allow client certificate - comments * Allow client certificate - compile fix * Allow client certificate - tests * Allow client certificate - rename privateKeyChainPem to certificatePem * Allow client certificate - remove incorrect test * Allow client certificate - removed unused function * Allow client certificate - spotlessApply * Allow client certificate - match expected and thrown error messages * Allow client certificate - spotlessApply * Allow client certificate - from spec project it was requested to remove logs entries * Allow client certificate - match expected and thrown error messages * Allow client certificate - improve test coverage * Allow client certificate - document also LOG version of env variables
This commit is contained in:
parent
345060e8a3
commit
a355c347b8
|
|
@ -96,6 +96,12 @@ public final class JaegerGrpcSpanExporterBuilder {
|
|||
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);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new instance of the exporter based on the builder's values.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -301,8 +301,7 @@ class JaegerGrpcSpanExporterTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("PreferJavaTimeOverload")
|
||||
void validConfig() {
|
||||
void validTrustedConfig() {
|
||||
assertThatCode(
|
||||
() ->
|
||||
JaegerGrpcSpanExporter.builder()
|
||||
|
|
@ -310,6 +309,17 @@ class JaegerGrpcSpanExporterTest {
|
|||
.doesNotThrowAnyException();
|
||||
}
|
||||
|
||||
@Test
|
||||
void validClientKeyConfig() {
|
||||
assertThatCode(
|
||||
() ->
|
||||
JaegerGrpcSpanExporter.builder()
|
||||
.setClientTls(
|
||||
"foobar".getBytes(StandardCharsets.UTF_8),
|
||||
"foobar".getBytes(StandardCharsets.UTF_8)))
|
||||
.doesNotThrowAnyException();
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("PreferJavaTimeOverload")
|
||||
void invalidConfig() {
|
||||
|
|
|
|||
|
|
@ -84,6 +84,14 @@ public final class OtlpHttpLogExporterBuilder {
|
|||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets ths client key and the certificate chain to use for verifying client when TLS is enabled.
|
||||
*/
|
||||
public OtlpHttpLogExporterBuilder setClientTls(byte[] privateKeyPem, byte[] certificatePem) {
|
||||
delegate.setClientTls(privateKeyPem, certificatePem);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link MeterProvider} to use to collect metrics related to export. If not set, metrics
|
||||
* will not be collected.
|
||||
|
|
|
|||
|
|
@ -143,6 +143,14 @@ class OtlpHttpLogExporterTest {
|
|||
OtlpHttpLogExporter.builder()
|
||||
.setTrustedCertificates("foobar".getBytes(StandardCharsets.UTF_8)))
|
||||
.doesNotThrowAnyException();
|
||||
|
||||
assertThatCode(
|
||||
() ->
|
||||
OtlpHttpLogExporter.builder()
|
||||
.setClientTls(
|
||||
"foobar".getBytes(StandardCharsets.UTF_8),
|
||||
"foobar".getBytes(StandardCharsets.UTF_8)))
|
||||
.doesNotThrowAnyException();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
|||
|
|
@ -88,6 +88,14 @@ public final class OtlpHttpMetricExporterBuilder {
|
|||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets ths client key and the certificate chain to use for verifying client when TLS is enabled.
|
||||
*/
|
||||
public OtlpHttpMetricExporterBuilder setClientTls(byte[] privateKeyPem, byte[] certificatePem) {
|
||||
delegate.setClientTls(privateKeyPem, certificatePem);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the preferred aggregation temporality. If unset, defaults to {@link
|
||||
* AggregationTemporality#CUMULATIVE}.
|
||||
|
|
|
|||
|
|
@ -137,6 +137,14 @@ class OtlpHttpMetricExporterTest {
|
|||
.setTrustedCertificates("foobar".getBytes(StandardCharsets.UTF_8)))
|
||||
.doesNotThrowAnyException();
|
||||
|
||||
assertThatCode(
|
||||
() ->
|
||||
OtlpHttpMetricExporter.builder()
|
||||
.setClientTls(
|
||||
"foobar".getBytes(StandardCharsets.UTF_8),
|
||||
"foobar".getBytes(StandardCharsets.UTF_8)))
|
||||
.doesNotThrowAnyException();
|
||||
|
||||
assertThatCode(
|
||||
() ->
|
||||
OtlpHttpMetricExporter.builder()
|
||||
|
|
|
|||
|
|
@ -84,6 +84,14 @@ public final class OtlpHttpSpanExporterBuilder {
|
|||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets ths client key and the certificate chain to use for verifying client when TLS is enabled.
|
||||
*/
|
||||
public OtlpHttpSpanExporterBuilder setClientTls(byte[] privateKeyPem, byte[] certificatePem) {
|
||||
delegate.setClientTls(privateKeyPem, certificatePem);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link MeterProvider} to use to collect metrics related to export. If not set, metrics
|
||||
* will not be collected.
|
||||
|
|
|
|||
|
|
@ -136,6 +136,14 @@ class OtlpHttpSpanExporterTest {
|
|||
OtlpHttpSpanExporter.builder()
|
||||
.setTrustedCertificates("foobar".getBytes(StandardCharsets.UTF_8)))
|
||||
.doesNotThrowAnyException();
|
||||
|
||||
assertThatCode(
|
||||
() ->
|
||||
OtlpHttpSpanExporter.builder()
|
||||
.setClientTls(
|
||||
"foobar".getBytes(StandardCharsets.UTF_8),
|
||||
"foobar".getBytes(StandardCharsets.UTF_8)))
|
||||
.doesNotThrowAnyException();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
|||
|
|
@ -9,18 +9,30 @@ import static java.util.Objects.requireNonNull;
|
|||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.security.KeyFactory;
|
||||
import java.security.KeyManagementException;
|
||||
import java.security.KeyStore;
|
||||
import java.security.KeyStoreException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.UnrecoverableKeyException;
|
||||
import java.security.cert.Certificate;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.CertificateFactory;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
import java.security.spec.PKCS8EncodedKeySpec;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.net.ssl.KeyManager;
|
||||
import javax.net.ssl.KeyManagerFactory;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLException;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
import javax.net.ssl.TrustManager;
|
||||
import javax.net.ssl.TrustManagerFactory;
|
||||
import javax.net.ssl.X509KeyManager;
|
||||
import javax.net.ssl.X509TrustManager;
|
||||
|
||||
/**
|
||||
|
|
@ -31,13 +43,20 @@ import javax.net.ssl.X509TrustManager;
|
|||
*/
|
||||
public final class TlsUtil {
|
||||
|
||||
/** Returns a {@link SSLSocketFactory} configured to use the given trust manager. */
|
||||
public static SSLSocketFactory sslSocketFactory(TrustManager trustManager) throws SSLException {
|
||||
private TlsUtil() {}
|
||||
|
||||
/** Returns a {@link SSLSocketFactory} configured to use the given key and trust manager. */
|
||||
public static SSLSocketFactory sslSocketFactory(
|
||||
@Nullable KeyManager keyManager, TrustManager trustManager) throws SSLException {
|
||||
|
||||
SSLContext sslContext;
|
||||
try {
|
||||
sslContext = SSLContext.getInstance("TLS");
|
||||
sslContext.init(null, new TrustManager[] {trustManager}, null);
|
||||
if (keyManager == null) {
|
||||
sslContext.init(null, new TrustManager[] {trustManager}, null);
|
||||
} else {
|
||||
sslContext.init(new KeyManager[] {keyManager}, new TrustManager[] {trustManager}, null);
|
||||
}
|
||||
} catch (NoSuchAlgorithmException | KeyManagementException e) {
|
||||
throw new SSLException(
|
||||
"Could not set trusted certificates for TLS connection, are they valid "
|
||||
|
|
@ -47,6 +66,46 @@ public final class TlsUtil {
|
|||
return sslContext.getSocketFactory();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates {@link KeyManager} initiaded by keystore containing single private key with matching
|
||||
* certificate chain.
|
||||
*/
|
||||
public static X509KeyManager keyManager(byte[] privateKeyPem, byte[] certificatePem)
|
||||
throws SSLException {
|
||||
requireNonNull(privateKeyPem, "privateKeyPem");
|
||||
requireNonNull(certificatePem, "certificatePem");
|
||||
try {
|
||||
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
|
||||
ks.load(null);
|
||||
KeyFactory factory = KeyFactory.getInstance("RSA");
|
||||
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKeyPem);
|
||||
PrivateKey key = factory.generatePrivate(keySpec);
|
||||
|
||||
CertificateFactory cf = CertificateFactory.getInstance("X.509");
|
||||
|
||||
List<Certificate> chain = new ArrayList<>();
|
||||
|
||||
ByteArrayInputStream is = new ByteArrayInputStream(certificatePem);
|
||||
while (is.available() > 0) {
|
||||
chain.add(cf.generateCertificate(is));
|
||||
}
|
||||
|
||||
ks.setKeyEntry("trusted", key, "".toCharArray(), chain.toArray(new Certificate[] {}));
|
||||
|
||||
KeyManagerFactory kmf =
|
||||
KeyManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
|
||||
kmf.init(ks, "".toCharArray());
|
||||
return (X509KeyManager) kmf.getKeyManagers()[0];
|
||||
} catch (CertificateException
|
||||
| KeyStoreException
|
||||
| IOException
|
||||
| NoSuchAlgorithmException
|
||||
| UnrecoverableKeyException
|
||||
| InvalidKeySpecException e) {
|
||||
throw new SSLException("Could not build KeyManagerFactory from clientKeysPem.", e);
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns a {@link TrustManager} for the given trusted certificates. */
|
||||
public static X509TrustManager trustManager(byte[] trustedCertificatesPem) throws SSLException {
|
||||
requireNonNull(trustedCertificatesPem, "trustedCertificatesPem");
|
||||
|
|
@ -71,6 +130,4 @@ public final class TlsUtil {
|
|||
throw new SSLException("Could not build TrustManagerFactory from trustedCertificatesPem.", e);
|
||||
}
|
||||
}
|
||||
|
||||
private TlsUtil() {}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,6 +43,8 @@ public final class DefaultGrpcExporterBuilder<T extends Marshaler>
|
|||
private boolean compressionEnabled = false;
|
||||
@Nullable private Metadata metadata;
|
||||
@Nullable private byte[] trustedCertificatesPem;
|
||||
@Nullable private byte[] privateKeyPem;
|
||||
@Nullable private byte[] certificatePem;
|
||||
@Nullable RetryPolicy retryPolicy;
|
||||
private MeterProvider meterProvider = MeterProvider.noop();
|
||||
|
||||
|
|
@ -96,6 +98,13 @@ public final class DefaultGrpcExporterBuilder<T extends Marshaler>
|
|||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GrpcExporterBuilder<T> setClientTls(byte[] privateKeyPem, byte[] certificatePem) {
|
||||
this.privateKeyPem = privateKeyPem;
|
||||
this.certificatePem = certificatePem;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DefaultGrpcExporterBuilder<T> addHeader(String key, String value) {
|
||||
if (metadata == null) {
|
||||
|
|
@ -136,8 +145,8 @@ public final class DefaultGrpcExporterBuilder<T extends Marshaler>
|
|||
|
||||
if (trustedCertificatesPem != null) {
|
||||
try {
|
||||
ManagedChannelUtil.setTrustedCertificatesPem(
|
||||
managedChannelBuilder, trustedCertificatesPem);
|
||||
ManagedChannelUtil.setClientKeysAndTrustedCertificatesPem(
|
||||
managedChannelBuilder, privateKeyPem, certificatePem, trustedCertificatesPem);
|
||||
} catch (SSLException e) {
|
||||
throw new IllegalStateException(
|
||||
"Could not set trusted certificates for gRPC TLS connection, are they valid "
|
||||
|
|
|
|||
|
|
@ -31,6 +31,8 @@ public interface GrpcExporterBuilder<T extends Marshaler> {
|
|||
|
||||
GrpcExporterBuilder<T> setTrustedCertificates(byte[] trustedCertificatesPem);
|
||||
|
||||
GrpcExporterBuilder<T> setClientTls(byte[] privateKeyPem, byte[] certificatePem);
|
||||
|
||||
GrpcExporterBuilder<T> addHeader(String key, String value);
|
||||
|
||||
GrpcExporterBuilder<T> setRetryPolicy(RetryPolicy retryPolicy);
|
||||
|
|
|
|||
|
|
@ -23,7 +23,9 @@ import java.util.Map;
|
|||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.net.ssl.SSLException;
|
||||
import javax.net.ssl.X509KeyManager;
|
||||
import javax.net.ssl.X509TrustManager;
|
||||
|
||||
/**
|
||||
|
|
@ -42,19 +44,27 @@ public final class ManagedChannelUtil {
|
|||
*
|
||||
* @throws SSLException if error occur processing the certificates
|
||||
*/
|
||||
public static void setTrustedCertificatesPem(
|
||||
ManagedChannelBuilder<?> managedChannelBuilder, byte[] trustedCertificatesPem)
|
||||
public static void setClientKeysAndTrustedCertificatesPem(
|
||||
ManagedChannelBuilder<?> managedChannelBuilder,
|
||||
@Nullable byte[] privateKeyPem,
|
||||
@Nullable byte[] certificatePem,
|
||||
byte[] trustedCertificatesPem)
|
||||
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);
|
||||
}
|
||||
|
||||
// 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().trustManager(tmf).build());
|
||||
nettyBuilder.sslContext(
|
||||
GrpcSslContexts.forClient().keyManager(kmf).trustManager(tmf).build());
|
||||
} else if (managedChannelBuilder
|
||||
.getClass()
|
||||
.getName()
|
||||
|
|
@ -62,14 +72,17 @@ public final class ManagedChannelUtil {
|
|||
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).build());
|
||||
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(tmf));
|
||||
okHttpBuilder.sslSocketFactory(TlsUtil.sslSocketFactory(kmf, tmf));
|
||||
} else {
|
||||
throw new SSLException(
|
||||
"TLS certificate configuration not supported for unrecognized ManagedChannelBuilder "
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import java.util.Collections;
|
|||
import java.util.concurrent.TimeUnit;
|
||||
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;
|
||||
|
|
@ -42,6 +43,8 @@ public final class OkHttpGrpcExporterBuilder<T extends Marshaler>
|
|||
private boolean compressionEnabled = false;
|
||||
private final Headers.Builder headers = new Headers.Builder();
|
||||
@Nullable private byte[] trustedCertificatesPem;
|
||||
@Nullable private byte[] privateKeyPem;
|
||||
@Nullable private byte[] certificatePem;
|
||||
@Nullable private RetryPolicy retryPolicy;
|
||||
private MeterProvider meterProvider = MeterProvider.noop();
|
||||
|
||||
|
|
@ -89,6 +92,13 @@ public final class OkHttpGrpcExporterBuilder<T extends Marshaler>
|
|||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GrpcExporterBuilder<T> setClientTls(byte[] privateKeyPem, byte[] certificatePem) {
|
||||
this.privateKeyPem = privateKeyPem;
|
||||
this.certificatePem = certificatePem;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OkHttpGrpcExporterBuilder<T> addHeader(String key, String value) {
|
||||
headers.add(key, value);
|
||||
|
|
@ -117,7 +127,12 @@ public final class OkHttpGrpcExporterBuilder<T extends Marshaler>
|
|||
if (trustedCertificatesPem != null) {
|
||||
try {
|
||||
X509TrustManager trustManager = TlsUtil.trustManager(trustedCertificatesPem);
|
||||
clientBuilder.sslSocketFactory(TlsUtil.sslSocketFactory(trustManager), trustManager);
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import java.time.Duration;
|
|||
import java.util.concurrent.TimeUnit;
|
||||
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;
|
||||
|
|
@ -38,6 +39,8 @@ public final class OkHttpExporterBuilder<T extends Marshaler> {
|
|||
private boolean compressionEnabled = false;
|
||||
@Nullable private Headers.Builder headersBuilder;
|
||||
@Nullable private byte[] trustedCertificatesPem;
|
||||
@Nullable private byte[] privateKeyPem;
|
||||
@Nullable private byte[] certificatePem;
|
||||
@Nullable private RetryPolicy retryPolicy;
|
||||
private MeterProvider meterProvider = MeterProvider.noop();
|
||||
|
||||
|
|
@ -82,6 +85,12 @@ public final class OkHttpExporterBuilder<T extends Marshaler> {
|
|||
return this;
|
||||
}
|
||||
|
||||
public OkHttpExporterBuilder<T> setClientTls(byte[] privateKeyPem, byte[] certificatePem) {
|
||||
this.privateKeyPem = privateKeyPem;
|
||||
this.certificatePem = certificatePem;
|
||||
return this;
|
||||
}
|
||||
|
||||
public OkHttpExporterBuilder<T> setMeterProvider(MeterProvider meterProvider) {
|
||||
this.meterProvider = meterProvider;
|
||||
return this;
|
||||
|
|
@ -101,7 +110,12 @@ public final class OkHttpExporterBuilder<T extends Marshaler> {
|
|||
if (trustedCertificatesPem != null) {
|
||||
try {
|
||||
X509TrustManager trustManager = TlsUtil.trustManager(trustedCertificatesPem);
|
||||
clientBuilder.sslSocketFactory(TlsUtil.sslSocketFactory(trustManager), trustManager);
|
||||
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?",
|
||||
|
|
|
|||
|
|
@ -112,6 +112,14 @@ public final class OtlpGrpcLogExporterBuilder {
|
|||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets ths client key and the certificate chain to use for verifying client when TLS is enabled.
|
||||
*/
|
||||
public OtlpGrpcLogExporterBuilder setClientTls(byte[] privateKeyPem, byte[] certificatePem) {
|
||||
delegate.setClientTls(privateKeyPem, certificatePem);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add header to request. Optional. Applicable only if {@link
|
||||
* OtlpGrpcLogExporterBuilder#setChannel(ManagedChannel)} is not used to set channel.
|
||||
|
|
|
|||
|
|
@ -90,6 +90,13 @@ class OtlpGrpcLogExporterTest extends AbstractGrpcTelemetryExporterTest<LogData,
|
|||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TelemetryExporterBuilder<LogData> setClientTls(
|
||||
byte[] privateKeyPem, byte[] certificatePem) {
|
||||
builder.setClientTls(privateKeyPem, certificatePem);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TelemetryExporterBuilder<LogData> setRetryPolicy(RetryPolicy retryPolicy) {
|
||||
builder.delegate.setRetryPolicy(retryPolicy);
|
||||
|
|
|
|||
|
|
@ -91,6 +91,13 @@ class OtlpGrpcNettyLogExporterTest
|
|||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TelemetryExporterBuilder<LogData> setClientTls(
|
||||
byte[] privateKeyPem, byte[] certificatePem) {
|
||||
builder.setClientTls(privateKeyPem, certificatePem);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TelemetryExporterBuilder<LogData> setRetryPolicy(RetryPolicy retryPolicy) {
|
||||
builder.delegate.setRetryPolicy(retryPolicy);
|
||||
|
|
|
|||
|
|
@ -80,6 +80,13 @@ class OtlpGrpcNettyShadedLogExporterTest
|
|||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TelemetryExporterBuilder<LogData> setClientTls(
|
||||
byte[] privateKeyPem, byte[] certificatePem) {
|
||||
builder.setClientTls(privateKeyPem, certificatePem);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TelemetryExporterBuilder<LogData> setRetryPolicy(RetryPolicy retryPolicy) {
|
||||
builder.delegate.setRetryPolicy(retryPolicy);
|
||||
|
|
|
|||
|
|
@ -80,6 +80,13 @@ class OtlpGrpcNettyOkHttpLogExporterTest
|
|||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TelemetryExporterBuilder<LogData> setClientTls(
|
||||
byte[] privateKeyPem, byte[] certificatePem) {
|
||||
builder.setClientTls(privateKeyPem, certificatePem);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TelemetryExporterBuilder<LogData> setRetryPolicy(RetryPolicy retryPolicy) {
|
||||
builder.delegate.setRetryPolicy(retryPolicy);
|
||||
|
|
|
|||
|
|
@ -116,6 +116,14 @@ public final class OtlpGrpcMetricExporterBuilder {
|
|||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets ths client key and the certificate chain to use for verifying client when TLS is enabled.
|
||||
*/
|
||||
public OtlpGrpcMetricExporterBuilder setClientTls(byte[] privateKeyPem, byte[] certificatePem) {
|
||||
delegate.setClientTls(privateKeyPem, certificatePem);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add header to request. Optional. Applicable only if {@link
|
||||
* OtlpGrpcMetricExporterBuilder#setChannel(ManagedChannel)} is not used to set channel.
|
||||
|
|
|
|||
|
|
@ -94,6 +94,13 @@ class OtlpGrpcMetricExporterTest
|
|||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TelemetryExporterBuilder<MetricData> setClientTls(
|
||||
byte[] privateKeyPem, byte[] certificatePem) {
|
||||
builder.setClientTls(privateKeyPem, certificatePem);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TelemetryExporterBuilder<MetricData> setRetryPolicy(RetryPolicy retryPolicy) {
|
||||
builder.delegate.setRetryPolicy(retryPolicy);
|
||||
|
|
|
|||
|
|
@ -94,6 +94,13 @@ class OtlpGrpcNettyMetricExporterTest
|
|||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TelemetryExporterBuilder<MetricData> setClientTls(
|
||||
byte[] privateKeyPem, byte[] certificatePem) {
|
||||
builder.setClientTls(privateKeyPem, certificatePem);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TelemetryExporterBuilder<MetricData> setRetryPolicy(RetryPolicy retryPolicy) {
|
||||
builder.delegate.setRetryPolicy(retryPolicy);
|
||||
|
|
|
|||
|
|
@ -83,6 +83,13 @@ class OtlpGrpcNettyShadedMetricExporterTest
|
|||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TelemetryExporterBuilder<MetricData> setClientTls(
|
||||
byte[] privateKeyPem, byte[] certificatePem) {
|
||||
builder.setClientTls(privateKeyPem, certificatePem);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TelemetryExporterBuilder<MetricData> setRetryPolicy(RetryPolicy retryPolicy) {
|
||||
builder.delegate.setRetryPolicy(retryPolicy);
|
||||
|
|
|
|||
|
|
@ -83,6 +83,13 @@ class OtlpGrpcOkHttpMetricExporterTest
|
|||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TelemetryExporterBuilder<MetricData> setClientTls(
|
||||
byte[] privateKeyPem, byte[] certificatePem) {
|
||||
builder.setClientTls(privateKeyPem, certificatePem);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TelemetryExporterBuilder<MetricData> setRetryPolicy(RetryPolicy retryPolicy) {
|
||||
builder.delegate.setRetryPolicy(retryPolicy);
|
||||
|
|
|
|||
|
|
@ -79,6 +79,11 @@ public abstract class AbstractGrpcTelemetryExporterTest<T, U extends Message> {
|
|||
|
||||
@RegisterExtension
|
||||
@Order(2)
|
||||
static final SelfSignedCertificateExtension clientCertificate =
|
||||
new SelfSignedCertificateExtension();
|
||||
|
||||
@RegisterExtension
|
||||
@Order(3)
|
||||
static final ServerExtension server =
|
||||
new ServerExtension() {
|
||||
@Override
|
||||
|
|
@ -105,6 +110,7 @@ public abstract class AbstractGrpcTelemetryExporterTest<T, U extends Message> {
|
|||
sb.http(0);
|
||||
sb.https(0);
|
||||
sb.tls(certificate.certificateFile(), certificate.privateKeyFile());
|
||||
sb.tlsCustomizer(ssl -> ssl.trustManager(clientCertificate.certificate()));
|
||||
sb.decorator(LoggingService.newDecorator());
|
||||
}
|
||||
};
|
||||
|
|
@ -249,6 +255,25 @@ public abstract class AbstractGrpcTelemetryExporterTest<T, U extends Message> {
|
|||
.hasMessageContaining("Could not set trusted certificates");
|
||||
}
|
||||
|
||||
@Test
|
||||
void clientTls() throws Exception {
|
||||
TelemetryExporter<T> exporter =
|
||||
exporterBuilder()
|
||||
.setEndpoint(server.httpsUri().toString())
|
||||
.setTrustedCertificates(Files.readAllBytes(certificate.certificateFile().toPath()))
|
||||
.setClientTls(
|
||||
clientCertificate.privateKey().getEncoded(),
|
||||
clientCertificate.certificate().getEncoded())
|
||||
.build();
|
||||
try {
|
||||
CompletableResultCode result =
|
||||
exporter.export(Collections.singletonList(generateFakeTelemetry()));
|
||||
assertThat(result.join(10, TimeUnit.SECONDS).isSuccess()).isTrue();
|
||||
} finally {
|
||||
exporter.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void deadlineSetPerExport() throws InterruptedException {
|
||||
TelemetryExporter<T> exporter =
|
||||
|
|
|
|||
|
|
@ -22,6 +22,8 @@ public interface TelemetryExporterBuilder<T> {
|
|||
|
||||
TelemetryExporterBuilder<T> setTrustedCertificates(byte[] certificates);
|
||||
|
||||
TelemetryExporterBuilder<T> setClientTls(byte[] privateKeyPem, byte[] certificatePem);
|
||||
|
||||
TelemetryExporterBuilder<T> setRetryPolicy(RetryPolicy retryPolicy);
|
||||
|
||||
TelemetryExporter<T> build();
|
||||
|
|
|
|||
|
|
@ -112,6 +112,14 @@ public final class OtlpGrpcSpanExporterBuilder {
|
|||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets ths client key and the certificate chain to use for verifying client when TLS is enabled.
|
||||
*/
|
||||
public OtlpGrpcSpanExporterBuilder setClientTls(byte[] privateKeyPem, byte[] certificatePem) {
|
||||
delegate.setClientTls(privateKeyPem, certificatePem);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add header to request. Optional. Applicable only if {@link
|
||||
* OtlpGrpcSpanExporterBuilder#setChannel(ManagedChannel)} is not called.
|
||||
|
|
|
|||
|
|
@ -95,6 +95,13 @@ class OtlpGrpcSpanExporterTest extends AbstractGrpcTelemetryExporterTest<SpanDat
|
|||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TelemetryExporterBuilder<SpanData> setClientTls(
|
||||
byte[] privateKeyPem, byte[] certificatePem) {
|
||||
builder.setClientTls(privateKeyPem, certificatePem);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TelemetryExporterBuilder<SpanData> setRetryPolicy(RetryPolicy retryPolicy) {
|
||||
builder.delegate.setRetryPolicy(retryPolicy);
|
||||
|
|
|
|||
|
|
@ -96,6 +96,13 @@ class OtlpGrpcNettySpanExporterTest
|
|||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TelemetryExporterBuilder<SpanData> setClientTls(
|
||||
byte[] privateKeyPem, byte[] certificatePem) {
|
||||
builder.setClientTls(privateKeyPem, certificatePem);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TelemetryExporterBuilder<SpanData> setRetryPolicy(RetryPolicy retryPolicy) {
|
||||
builder.delegate.setRetryPolicy(retryPolicy);
|
||||
|
|
|
|||
|
|
@ -85,6 +85,13 @@ class OtlpGrpcNettyShadedSpanExporterTest
|
|||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TelemetryExporterBuilder<SpanData> setClientTls(
|
||||
byte[] privateKeyPem, byte[] certificatePem) {
|
||||
builder.setClientTls(privateKeyPem, certificatePem);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TelemetryExporterBuilder<SpanData> setRetryPolicy(RetryPolicy retryPolicy) {
|
||||
builder.delegate.setRetryPolicy(retryPolicy);
|
||||
|
|
|
|||
|
|
@ -85,6 +85,13 @@ class OtlpGrpcOkHttpSpanExporterTest
|
|||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TelemetryExporterBuilder<SpanData> setClientTls(
|
||||
byte[] privateKeyPem, byte[] certificatePem) {
|
||||
builder.setClientTls(privateKeyPem, certificatePem);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TelemetryExporterBuilder<SpanData> setRetryPolicy(RetryPolicy retryPolicy) {
|
||||
builder.delegate.setRetryPolicy(retryPolicy);
|
||||
|
|
|
|||
|
|
@ -62,6 +62,14 @@ The [OpenTelemetry Protocol (OTLP)](https://github.com/open-telemetry/openteleme
|
|||
| otel.exporter.otlp.traces.certificate | OTEL_EXPORTER_OTLP_TRACES_CERTIFICATE | The path to the file containing trusted certificates to use when verifying an OTLP trace server's TLS credentials. The file should contain one or more X.509 certificates in PEM format. By default the host platform's trusted root certificates are used. |
|
||||
| otel.exporter.otlp.metrics.certificate | OTEL_EXPORTER_OTLP_METRICS_CERTIFICATE | The path to the file containing trusted certificates to use when verifying an OTLP metric server's TLS credentials. The file should contain one or more X.509 certificates in PEM format. By default the host platform's trusted root certificates are used. |
|
||||
| otel.exporter.otlp.logs.certificate | OTEL_EXPORTER_OTLP_LOGS_CERTIFICATE | The path to the file containing trusted certificates to use when verifying an OTLP log server's TLS credentials. The file should contain one or more X.509 certificates in PEM format. By default the host platform's trusted root certificates are used. |
|
||||
| otel.exporter.otlp.client.key | OTEL_EXPORTER_OTLP_CERTIFICATE | The path to the file containing private client key to use when verifying an OTLP trace, metric, or log client's TLS credentials. The file should contain one private key PKCS8 PEM format. By default no client key is used. |
|
||||
| otel.exporter.otlp.traces.client.key | OTEL_EXPORTER_OTLP_TRACES_CLIENT_KEY | The path to the file containing private client key to use when verifying an OTLP trace client's TLS credentials. The file should contain one private key PKCS8 PEM format. By default no client key file is used. |
|
||||
| otel.exporter.otlp.metrics.client.key | OTEL_EXPORTER_OTLP_METRICS_CLIENT_KEY | The path to the file containing private client key to use when verifying an OTLP metric client's TLS credentials. The file should contain one private key PKCS8 PEM format. By default no client key file is used. |
|
||||
| otel.exporter.otlp.logs.client.key | OTEL_EXPORTER_OTLP_LOGS_CLIENT_KEY | The path to the file containing private client key to use when verifying an OTLP log client's TLS credentials. The file should contain one private key PKCS8 PEM format. By default no client key file is used. |
|
||||
| otel.exporter.otlp.client.certificate | OTEL_EXPORTER_OTLP_CLIENT_CERTIFICATE | The path to the file containing trusted certificates to use when verifying an OTLP trace, metric, or log client's TLS credentials. The file should contain one or more X.509 certificates in PEM format. By default no chain file is used. |
|
||||
| otel.exporter.otlp.traces.client.certificate | OTEL_EXPORTER_OTLP_TRACES_CLIENT_CERTIFICATE | The path to the file containing trusted certificates to use when verifying an OTLP trace server's TLS credentials. The file should contain one or more X.509 certificates in PEM format. By default no chain file is used. |
|
||||
| otel.exporter.otlp.metrics.client.certificate | OTEL_EXPORTER_OTLP_METRICS_CLIENT_CERTIFICATE | The path to the file containing trusted certificates to use when verifying an OTLP metric server's TLS credentials. The file should contain one or more X.509 certificates in PEM format. By default no chain file is used. |
|
||||
| otel.exporter.otlp.logs.client.certificate | OTEL_EXPORTER_OTLP_LOGS_CLIENT_CERTIFICATE | The path to the file containing trusted certificates to use when verifying an OTLP log server's TLS credentials. The file should contain one or more X.509 certificates in PEM format. By default no chain file is used. |
|
||||
| otel.exporter.otlp.headers | OTEL_EXPORTER_OTLP_HEADERS | Key-value pairs separated by commas to pass as request headers on OTLP trace, metric, and log requests. |
|
||||
| otel.exporter.otlp.traces.headers | OTEL_EXPORTER_OTLP_TRACES_HEADERS | Key-value pairs separated by commas to pass as request headers on OTLP trace requests. |
|
||||
| otel.exporter.otlp.metrics.headers | OTEL_EXPORTER_OTLP_METRICS_HEADERS | Key-value pairs separated by commas to pass as request headers on OTLP metrics requests. |
|
||||
|
|
|
|||
|
|
@ -106,6 +106,7 @@ class LogExporterConfiguration {
|
|||
builder::setCompression,
|
||||
builder::setTimeout,
|
||||
builder::setTrustedCertificates,
|
||||
builder::setClientTls,
|
||||
retryPolicy -> RetryUtil.setRetryPolicyOnDelegate(builder, retryPolicy));
|
||||
|
||||
builder.setMeterProvider(meterProvider);
|
||||
|
|
@ -131,6 +132,7 @@ class LogExporterConfiguration {
|
|||
builder::setCompression,
|
||||
builder::setTimeout,
|
||||
builder::setTrustedCertificates,
|
||||
builder::setClientTls,
|
||||
retryPolicy -> RetryUtil.setRetryPolicyOnDelegate(builder, retryPolicy));
|
||||
builder.setMeterProvider(meterProvider);
|
||||
|
||||
|
|
|
|||
|
|
@ -115,6 +115,7 @@ final class MetricExporterConfiguration {
|
|||
builder::setCompression,
|
||||
builder::setTimeout,
|
||||
builder::setTrustedCertificates,
|
||||
builder::setClientTls,
|
||||
retryPolicy -> RetryUtil.setRetryPolicyOnDelegate(builder, retryPolicy));
|
||||
OtlpConfigUtil.configureOtlpAggregationTemporality(config, builder::setPreferredTemporality);
|
||||
|
||||
|
|
@ -141,6 +142,7 @@ final class MetricExporterConfiguration {
|
|||
builder::setCompression,
|
||||
builder::setTimeout,
|
||||
builder::setTrustedCertificates,
|
||||
builder::setClientTls,
|
||||
retryPolicy -> RetryUtil.setRetryPolicyOnDelegate(builder, retryPolicy));
|
||||
OtlpConfigUtil.configureOtlpAggregationTemporality(config, builder::setPreferredTemporality);
|
||||
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ final class OtlpConfigUtil {
|
|||
Consumer<String> setCompression,
|
||||
Consumer<Duration> setTimeout,
|
||||
Consumer<byte[]> setTrustedCertificates,
|
||||
BiConsumer<byte[], byte[]> setClientTls,
|
||||
Consumer<RetryPolicy> setRetryPolicy) {
|
||||
String protocol = getOtlpProtocol(dataType, config);
|
||||
boolean isHttpProtobuf = protocol.equals(PROTOCOL_HTTP_PROTOBUF);
|
||||
|
|
@ -93,24 +94,34 @@ final class OtlpConfigUtil {
|
|||
setTimeout.accept(timeout);
|
||||
}
|
||||
|
||||
String certificate = config.getString("otel.exporter.otlp." + dataType + ".certificate");
|
||||
if (certificate == null) {
|
||||
certificate = config.getString("otel.exporter.otlp.certificate");
|
||||
String certificatePath =
|
||||
config.getString(
|
||||
determinePropertyByType(config, "otel.exporter.otlp", dataType, "certificate"));
|
||||
String clientKeyPath =
|
||||
config.getString(
|
||||
determinePropertyByType(config, "otel.exporter.otlp", dataType, "client.key"));
|
||||
String clientKeyChainPath =
|
||||
config.getString(
|
||||
determinePropertyByType(config, "otel.exporter.otlp", dataType, "client.certificate"));
|
||||
|
||||
if (clientKeyPath != null && clientKeyChainPath == null) {
|
||||
throw new ConfigurationException("Client key provided but certification chain is missing");
|
||||
} else if (clientKeyPath == null && clientKeyChainPath != null) {
|
||||
throw new ConfigurationException("Client key chain provided but key is missing");
|
||||
}
|
||||
if (certificate != null) {
|
||||
Path path = Paths.get(certificate);
|
||||
if (!Files.exists(path)) {
|
||||
throw new ConfigurationException("Invalid OTLP certificate path: " + path);
|
||||
}
|
||||
byte[] certificateBytes;
|
||||
try {
|
||||
certificateBytes = Files.readAllBytes(path);
|
||||
} catch (IOException e) {
|
||||
throw new ConfigurationException("Error reading OTLP certificate.", e);
|
||||
}
|
||||
|
||||
byte[] certificateBytes = readFileBytes(certificatePath);
|
||||
if (certificateBytes != null) {
|
||||
setTrustedCertificates.accept(certificateBytes);
|
||||
}
|
||||
|
||||
byte[] clientKeyBytes = readFileBytes(clientKeyPath);
|
||||
byte[] clientKeyChainBytes = readFileBytes(clientKeyChainPath);
|
||||
|
||||
if (clientKeyBytes != null && clientKeyChainBytes != null) {
|
||||
setClientTls.accept(clientKeyBytes, clientKeyChainBytes);
|
||||
}
|
||||
|
||||
Boolean retryEnabled = config.getBoolean("otel.experimental.exporter.otlp.retry.enabled");
|
||||
if (retryEnabled != null && retryEnabled) {
|
||||
setRetryPolicy.accept(RetryPolicy.getDefault());
|
||||
|
|
@ -171,6 +182,33 @@ final class OtlpConfigUtil {
|
|||
return endpointUrl;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static byte[] readFileBytes(@Nullable String filePath) {
|
||||
if (filePath == null) {
|
||||
return null;
|
||||
}
|
||||
Path path = Paths.get(filePath);
|
||||
if (!Files.exists(path)) {
|
||||
throw new ConfigurationException("Invalid OTLP certificate/key path: " + path);
|
||||
}
|
||||
try {
|
||||
return Files.readAllBytes(path);
|
||||
} catch (IOException e) {
|
||||
throw new ConfigurationException("Error reading content of file (" + path + ")", e);
|
||||
}
|
||||
}
|
||||
|
||||
private static String determinePropertyByType(
|
||||
ConfigProperties config, String prefix, String dataType, String suffix) {
|
||||
String propertyToRead = prefix + "." + dataType + "." + suffix;
|
||||
String value = config.getString(propertyToRead);
|
||||
if (value == null) {
|
||||
return prefix + "." + suffix;
|
||||
} else {
|
||||
return propertyToRead;
|
||||
}
|
||||
}
|
||||
|
||||
private static String signalPath(String dataType) {
|
||||
switch (dataType) {
|
||||
case DATA_TYPE_METRICS:
|
||||
|
|
|
|||
|
|
@ -126,6 +126,7 @@ final class SpanExporterConfiguration {
|
|||
builder::setCompression,
|
||||
builder::setTimeout,
|
||||
builder::setTrustedCertificates,
|
||||
builder::setClientTls,
|
||||
retryPolicy -> RetryUtil.setRetryPolicyOnDelegate(builder, retryPolicy));
|
||||
|
||||
builder.setMeterProvider(meterProvider);
|
||||
|
|
@ -146,6 +147,7 @@ final class SpanExporterConfiguration {
|
|||
builder::setCompression,
|
||||
builder::setTimeout,
|
||||
builder::setTrustedCertificates,
|
||||
builder::setClientTls,
|
||||
retryPolicy -> RetryUtil.setRetryPolicyOnDelegate(builder, retryPolicy));
|
||||
builder.setMeterProvider(meterProvider);
|
||||
|
||||
|
|
|
|||
|
|
@ -319,6 +319,7 @@ class OtlpConfigUtilTest {
|
|||
value -> {},
|
||||
value -> {},
|
||||
value -> {},
|
||||
(value1, value2) -> {},
|
||||
value -> {});
|
||||
|
||||
return endpoint.get();
|
||||
|
|
|
|||
|
|
@ -242,16 +242,62 @@ class OtlpGrpcConfigTest {
|
|||
SpanExporterConfiguration.configureExporter(
|
||||
"otlp", properties, NamedSpiManager.createEmpty(), MeterProvider.noop()))
|
||||
.isInstanceOf(ConfigurationException.class)
|
||||
.hasMessageContaining("Invalid OTLP certificate path:");
|
||||
.hasMessageContaining("Invalid OTLP certificate/key path:");
|
||||
|
||||
assertThatThrownBy(() -> MetricExporterConfiguration.configureOtlpMetrics(properties))
|
||||
.isInstanceOf(ConfigurationException.class)
|
||||
.hasMessageContaining("Invalid OTLP certificate path:");
|
||||
.hasMessageContaining("Invalid OTLP certificate/key path:");
|
||||
|
||||
assertThatThrownBy(
|
||||
() -> LogExporterConfiguration.configureOtlpLogs(properties, MeterProvider.noop()))
|
||||
.isInstanceOf(ConfigurationException.class)
|
||||
.hasMessageContaining("Invalid OTLP certificate path:");
|
||||
.hasMessageContaining("Invalid OTLP certificate/key path:");
|
||||
}
|
||||
|
||||
@Test
|
||||
void configureTlsMissingClientCertificatePath() {
|
||||
Map<String, String> props = new HashMap<>();
|
||||
props.put("otel.exporter.otlp.client.key", Paths.get("foo", "bar", "baz").toString());
|
||||
ConfigProperties properties = DefaultConfigProperties.createForTest(props);
|
||||
|
||||
assertThatThrownBy(
|
||||
() ->
|
||||
SpanExporterConfiguration.configureExporter(
|
||||
"otlp", properties, NamedSpiManager.createEmpty(), MeterProvider.noop()))
|
||||
.isInstanceOf(ConfigurationException.class)
|
||||
.hasMessageContaining("Client key provided but certification chain is missing");
|
||||
|
||||
assertThatThrownBy(() -> MetricExporterConfiguration.configureOtlpMetrics(properties))
|
||||
.isInstanceOf(ConfigurationException.class)
|
||||
.hasMessageContaining("Client key provided but certification chain is missing");
|
||||
|
||||
assertThatThrownBy(
|
||||
() -> LogExporterConfiguration.configureOtlpLogs(properties, MeterProvider.noop()))
|
||||
.isInstanceOf(ConfigurationException.class)
|
||||
.hasMessageContaining("Client key provided but certification chain is missing");
|
||||
}
|
||||
|
||||
@Test
|
||||
void configureTlsMissingClientKeyPath() {
|
||||
Map<String, String> props = new HashMap<>();
|
||||
props.put("otel.exporter.otlp.client.certificate", Paths.get("foo", "bar", "baz").toString());
|
||||
ConfigProperties properties = DefaultConfigProperties.createForTest(props);
|
||||
|
||||
assertThatThrownBy(
|
||||
() ->
|
||||
SpanExporterConfiguration.configureExporter(
|
||||
"otlp", properties, NamedSpiManager.createEmpty(), MeterProvider.noop()))
|
||||
.isInstanceOf(ConfigurationException.class)
|
||||
.hasMessageContaining("Client key chain provided but key is missing");
|
||||
|
||||
assertThatThrownBy(() -> MetricExporterConfiguration.configureOtlpMetrics(properties))
|
||||
.isInstanceOf(ConfigurationException.class)
|
||||
.hasMessageContaining("Client key chain provided but key is missing");
|
||||
|
||||
assertThatThrownBy(
|
||||
() -> LogExporterConfiguration.configureOtlpLogs(properties, MeterProvider.noop()))
|
||||
.isInstanceOf(ConfigurationException.class)
|
||||
.hasMessageContaining("Client key chain provided but key is missing");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
|||
|
|
@ -268,16 +268,64 @@ class OtlpHttpConfigTest {
|
|||
SpanExporterConfiguration.configureExporter(
|
||||
"otlp", properties, NamedSpiManager.createEmpty(), MeterProvider.noop()))
|
||||
.isInstanceOf(ConfigurationException.class)
|
||||
.hasMessageContaining("Invalid OTLP certificate path:");
|
||||
.hasMessageContaining("Invalid OTLP certificate/key path:");
|
||||
|
||||
assertThatThrownBy(() -> MetricExporterConfiguration.configureOtlpMetrics(properties))
|
||||
.isInstanceOf(ConfigurationException.class)
|
||||
.hasMessageContaining("Invalid OTLP certificate path:");
|
||||
.hasMessageContaining("Invalid OTLP certificate/key path:");
|
||||
|
||||
assertThatThrownBy(
|
||||
() -> LogExporterConfiguration.configureOtlpLogs(properties, MeterProvider.noop()))
|
||||
.isInstanceOf(ConfigurationException.class)
|
||||
.hasMessageContaining("Invalid OTLP certificate path:");
|
||||
.hasMessageContaining("Invalid OTLP certificate/key path:");
|
||||
}
|
||||
|
||||
@Test
|
||||
void configureTlsMissingClientCertificatePath() {
|
||||
Map<String, String> props = new HashMap<>();
|
||||
props.put("otel.exporter.otlp.protocol", "http/protobuf");
|
||||
props.put("otel.exporter.otlp.client.key", Paths.get("foo", "bar", "baz").toString());
|
||||
ConfigProperties properties = DefaultConfigProperties.createForTest(props);
|
||||
|
||||
assertThatThrownBy(
|
||||
() ->
|
||||
SpanExporterConfiguration.configureExporter(
|
||||
"otlp", properties, NamedSpiManager.createEmpty(), MeterProvider.noop()))
|
||||
.isInstanceOf(ConfigurationException.class)
|
||||
.hasMessageContaining("Client key provided but certification chain is missing");
|
||||
|
||||
assertThatThrownBy(() -> MetricExporterConfiguration.configureOtlpMetrics(properties))
|
||||
.isInstanceOf(ConfigurationException.class)
|
||||
.hasMessageContaining("Client key provided but certification chain is missing");
|
||||
|
||||
assertThatThrownBy(
|
||||
() -> LogExporterConfiguration.configureOtlpLogs(properties, MeterProvider.noop()))
|
||||
.isInstanceOf(ConfigurationException.class)
|
||||
.hasMessageContaining("Client key provided but certification chain is missing");
|
||||
}
|
||||
|
||||
@Test
|
||||
void configureTlsMissingClientKeyPath() {
|
||||
Map<String, String> props = new HashMap<>();
|
||||
props.put("otel.exporter.otlp.protocol", "http/protobuf");
|
||||
props.put("otel.exporter.otlp.client.certificate", Paths.get("foo", "bar", "baz").toString());
|
||||
ConfigProperties properties = DefaultConfigProperties.createForTest(props);
|
||||
|
||||
assertThatThrownBy(
|
||||
() ->
|
||||
SpanExporterConfiguration.configureExporter(
|
||||
"otlp", properties, NamedSpiManager.createEmpty(), MeterProvider.noop()))
|
||||
.isInstanceOf(ConfigurationException.class)
|
||||
.hasMessageContaining("Client key chain provided but key is missing");
|
||||
|
||||
assertThatThrownBy(() -> MetricExporterConfiguration.configureOtlpMetrics(properties))
|
||||
.isInstanceOf(ConfigurationException.class)
|
||||
.hasMessageContaining("Client key chain provided but key is missing");
|
||||
|
||||
assertThatThrownBy(
|
||||
() -> LogExporterConfiguration.configureOtlpLogs(properties, MeterProvider.noop()))
|
||||
.isInstanceOf(ConfigurationException.class)
|
||||
.hasMessageContaining("Client key chain provided but key is missing");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
|||
|
|
@ -41,6 +41,8 @@ final class DefaultGrpcServiceBuilder<ReqT extends Marshaler, ResT extends UnMar
|
|||
private boolean compressionEnabled = false;
|
||||
@Nullable private Metadata metadata;
|
||||
@Nullable private byte[] trustedCertificatesPem;
|
||||
@Nullable private byte[] privateKeyPem;
|
||||
@Nullable private byte[] certificatePem;
|
||||
@Nullable private RetryPolicy retryPolicy;
|
||||
|
||||
/** Creates a new {@link OkHttpGrpcExporterBuilder}. */
|
||||
|
|
@ -105,6 +107,13 @@ final class DefaultGrpcServiceBuilder<ReqT extends Marshaler, ResT extends UnMar
|
|||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GrpcServiceBuilder<ReqT, ResT> setClientTls(byte[] privateKeyPem, byte[] certificatePem) {
|
||||
this.privateKeyPem = privateKeyPem;
|
||||
this.certificatePem = certificatePem;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DefaultGrpcServiceBuilder<ReqT, ResT> addHeader(String key, String value) {
|
||||
requireNonNull(key, "key");
|
||||
|
|
@ -142,8 +151,8 @@ final class DefaultGrpcServiceBuilder<ReqT extends Marshaler, ResT extends UnMar
|
|||
|
||||
if (trustedCertificatesPem != null) {
|
||||
try {
|
||||
ManagedChannelUtil.setTrustedCertificatesPem(
|
||||
managedChannelBuilder, trustedCertificatesPem);
|
||||
ManagedChannelUtil.setClientKeysAndTrustedCertificatesPem(
|
||||
managedChannelBuilder, privateKeyPem, certificatePem, trustedCertificatesPem);
|
||||
} catch (SSLException e) {
|
||||
throw new IllegalStateException(
|
||||
"Could not set trusted certificates for gRPC TLS connection, are they valid "
|
||||
|
|
|
|||
|
|
@ -25,6 +25,9 @@ interface GrpcServiceBuilder<ReqMarshalerT extends Marshaler, ResUnMarshalerT ex
|
|||
GrpcServiceBuilder<ReqMarshalerT, ResUnMarshalerT> setTrustedCertificates(
|
||||
byte[] trustedCertificatesPem);
|
||||
|
||||
GrpcServiceBuilder<ReqMarshalerT, ResUnMarshalerT> setClientTls(
|
||||
byte[] privateKeyPem, byte[] certificatePem);
|
||||
|
||||
GrpcServiceBuilder<ReqMarshalerT, ResUnMarshalerT> addHeader(String key, String value);
|
||||
|
||||
GrpcServiceBuilder<ReqMarshalerT, ResUnMarshalerT> addRetryPolicy(RetryPolicy retryPolicy);
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import java.util.Collections;
|
|||
import java.util.concurrent.TimeUnit;
|
||||
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;
|
||||
|
|
@ -40,6 +41,8 @@ final class OkHttpGrpcServiceBuilder<
|
|||
private boolean compressionEnabled = false;
|
||||
private final Headers.Builder headers = new Headers.Builder();
|
||||
@Nullable private byte[] trustedCertificatesPem;
|
||||
@Nullable private byte[] privateKeyPem;
|
||||
@Nullable private byte[] certificatePem;
|
||||
@Nullable private RetryPolicy retryPolicy;
|
||||
|
||||
OkHttpGrpcServiceBuilder(
|
||||
|
|
@ -97,6 +100,16 @@ final class OkHttpGrpcServiceBuilder<
|
|||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GrpcServiceBuilder<ReqMarshalerT, ResUnMarshalerT> setClientTls(
|
||||
byte[] privateKeyPem, byte[] certificatePem) {
|
||||
requireNonNull(privateKeyPem, "privateKeyPem");
|
||||
requireNonNull(certificatePem, "certificatePem");
|
||||
this.privateKeyPem = privateKeyPem;
|
||||
this.certificatePem = certificatePem;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OkHttpGrpcServiceBuilder<ReqMarshalerT, ResUnMarshalerT> addHeader(
|
||||
String key, String value) {
|
||||
|
|
@ -124,7 +137,12 @@ final class OkHttpGrpcServiceBuilder<
|
|||
if (trustedCertificatesPem != null) {
|
||||
try {
|
||||
X509TrustManager trustManager = TlsUtil.trustManager(trustedCertificatesPem);
|
||||
clientBuilder.sslSocketFactory(TlsUtil.sslSocketFactory(trustManager), trustManager);
|
||||
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);
|
||||
|
|
|
|||
Loading…
Reference in New Issue