Skip to content

Commit

Permalink
add ssl on okhttpclient for zipkin
Browse files Browse the repository at this point in the history
Signed-off-by: Matthieu MOREL <matthieu.morel35@gmail.com>
  • Loading branch information
mmorel-35 committed Jan 5, 2023
1 parent 1f975b3 commit 8947559
Show file tree
Hide file tree
Showing 2 changed files with 134 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,18 @@

import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.api.metrics.MeterProvider;
import io.opentelemetry.exporter.internal.TlsUtil;
import io.opentelemetry.exporter.internal.retry.RetryInterceptor;
import io.opentelemetry.exporter.internal.retry.RetryPolicy;
import io.opentelemetry.exporter.internal.retry.RetryUtil;
import java.net.InetAddress;
import java.time.Duration;
import java.util.concurrent.TimeUnit;
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 zipkin2.Span;
import zipkin2.codec.BytesEncoder;
import zipkin2.codec.SpanBytesEncoder;
Expand All @@ -27,11 +34,21 @@ public final class ZipkinSpanExporterBuilder {
private Supplier<InetAddress> localIpAddressSupplier = LocalInetAddressSupplier.getInstance();
@Nullable private Sender sender;
private String endpoint = ZipkinSpanExporter.DEFAULT_ENDPOINT;
// compression is enabled by default, because this is the default of OkHttpSender,
// compression is enabled by default, because this is the default of
// OkHttpSender,
// which is created when no custom sender is set (see OkHttpSender.Builder)
private boolean compressionEnabled = true;
private long readTimeoutMillis = TimeUnit.SECONDS.toMillis(10);
private Supplier<MeterProvider> meterProviderSupplier = GlobalOpenTelemetry::getMeterProvider;
private final OkHttpSender.Builder okHttpSenderBuilder;
@Nullable private byte[] trustedCertificatesPem;
@Nullable private byte[] privateKeyPem;
@Nullable private byte[] certificatePem;
@Nullable private RetryPolicy retryPolicy;

public ZipkinSpanExporterBuilder() {
this.okHttpSenderBuilder = OkHttpSender.newBuilder();
}

/**
* Sets the Zipkin sender. Implements the client side of the span transport. An {@link
Expand Down Expand Up @@ -151,6 +168,41 @@ public ZipkinSpanExporterBuilder setMeterProvider(MeterProvider meterProvider) {
return this;
}

/**
* Sets the certificate chain to use for verifying servers when TLS is enabled. The {@code byte[]}
* should contain an X.509 certificate collection in PEM format. If not set, TLS connections will
* use the system default trusted certificates.
*/
public ZipkinSpanExporterBuilder setTrustedCertificates(byte[] trustedCertificatesPem) {
requireNonNull(trustedCertificatesPem, "trustedCertificatesPem");
this.trustedCertificatesPem = trustedCertificatesPem;
return this;
}

/**
* Sets ths client key and the certificate chain to use for verifying client when TLS is enabled.
* The key must be PKCS8, and both must be in PEM format.
*/
public ZipkinSpanExporterBuilder setClientTls(byte[] privateKeyPem, byte[] certificatePem) {
requireNonNull(privateKeyPem, "privateKeyPem");
requireNonNull(certificatePem, "certificatePem");
this.privateKeyPem = privateKeyPem;
this.certificatePem = certificatePem;
return this;
}

/**
* Set the retry policy to use by the client.
*
* @param retryPolicy The retry policy.
* @return this.
*/
public ZipkinSpanExporterBuilder setRetryPolicy(RetryPolicy retryPolicy) {
requireNonNull(retryPolicy, "retryPolicy");
this.retryPolicy = retryPolicy;
return this;
}

/**
* Builds a {@link ZipkinSpanExporter}.
*
Expand All @@ -159,8 +211,35 @@ public ZipkinSpanExporterBuilder setMeterProvider(MeterProvider meterProvider) {
public ZipkinSpanExporter build() {
Sender sender = this.sender;
if (sender == null) {
if (trustedCertificatesPem != null) {
try {
X509TrustManager trustManager = TlsUtil.trustManager(trustedCertificatesPem);
X509KeyManager keyManager = null;
if (privateKeyPem != null && certificatePem != null) {
keyManager = TlsUtil.keyManager(privateKeyPem, certificatePem);
}
this.okHttpSenderBuilder
.clientBuilder()
.sslSocketFactory(TlsUtil.sslSocketFactory(keyManager, trustManager), trustManager);
} catch (SSLException e) {
throw new IllegalStateException(
"Could not set trusted certificate for Zipkin HTTP connection, are they valid X.509 in PEM format?",
e);
}
}

if (retryPolicy != null) {
this.okHttpSenderBuilder
.clientBuilder()
.addInterceptor(
new RetryInterceptor(
retryPolicy,
(response) ->
RetryUtil.retryableHttpResponseCodes().contains(response.code())));
}

sender =
OkHttpSender.newBuilder()
this.okHttpSenderBuilder
.endpoint(endpoint)
.compressionEnabled(compressionEnabled)
.readTimeout((int) readTimeoutMillis)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,18 @@

package io.opentelemetry.exporter.zipkin.internal;

import io.opentelemetry.exporter.internal.retry.RetryPolicy;
import io.opentelemetry.exporter.zipkin.ZipkinSpanExporter;
import io.opentelemetry.exporter.zipkin.ZipkinSpanExporterBuilder;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException;
import io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSpanExporterProvider;
import io.opentelemetry.sdk.trace.export.SpanExporter;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.time.Duration;
import javax.annotation.Nullable;

/**
* {@link SpanExporter} SPI implementation for {@link ZipkinSpanExporter}.
Expand Down Expand Up @@ -38,6 +44,53 @@ public SpanExporter createExporter(ConfigProperties config) {
builder.setReadTimeout(timeout);
}

String certificatePath = config.getString("otel.exporter.zipkin.certificate");
String clientKeyPath = config.getString("otel.exporter.zipkin.client.key");
String clientKeyChainPath = config.getString("otel.exporter.zipkin.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");
}

byte[] certificateBytes = readFileBytes(certificatePath);
if (certificateBytes != null) {
builder.setTrustedCertificates(certificateBytes);
}

byte[] clientKeyBytes = readFileBytes(clientKeyPath);
byte[] clientKeyChainBytes = readFileBytes(clientKeyChainPath);

if (clientKeyBytes != null && clientKeyChainBytes != null) {
builder.setClientTls(clientKeyBytes, clientKeyChainBytes);
}

boolean retryEnabled =
config.getBoolean("otel.experimental.exporter.zipkin.retry.enabled", false);
if (retryEnabled) {
builder.setRetryPolicy(RetryPolicy.getDefault());
}

return builder.build();
}

@Nullable
private static byte[] readFileBytes(@Nullable String filePath) {
if (filePath == null) {
return null;
}
File file = new File(filePath);
if (!file.exists()) {
throw new ConfigurationException("Invalid Zipkin certificate/key path: " + filePath);
}
try {
RandomAccessFile raf = new RandomAccessFile(file, "r");
byte[] bytes = new byte[(int) raf.length()];
raf.readFully(bytes);
return bytes;
} catch (IOException e) {
throw new ConfigurationException("Error reading content of file (" + filePath + ")", e);
}
}
}

0 comments on commit 8947559

Please sign in to comment.