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 9, 2023
1 parent 1f975b3 commit 9f4e070
Show file tree
Hide file tree
Showing 4 changed files with 112 additions and 7 deletions.
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
Comparing source compatibility of against
No changes.
*** MODIFIED CLASS: PUBLIC FINAL io.opentelemetry.exporter.zipkin.ZipkinSpanExporterBuilder (not serializable)
=== CLASS FILE FORMAT VERSION: 52.0 <- 52.0
+++ NEW METHOD: PUBLIC(+) io.opentelemetry.exporter.zipkin.ZipkinSpanExporterBuilder setClientTls(byte[], byte[])
+++ NEW METHOD: PUBLIC(+) io.opentelemetry.exporter.zipkin.ZipkinSpanExporterBuilder setTrustedCertificates(byte[])
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,15 @@

import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.api.metrics.MeterProvider;
import io.opentelemetry.exporter.internal.TlsUtil;
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 +31,20 @@ 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;

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 +164,29 @@ 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;
}

/**
* Builds a {@link ZipkinSpanExporter}.
*
Expand All @@ -159,8 +195,25 @@ 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);
}
}

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 @@ -8,9 +8,14 @@
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 +43,47 @@ 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);
}

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);
}
}
}
11 changes: 7 additions & 4 deletions sdk-extensions/autoconfigure/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,10 +131,13 @@ The [Jaeger](https://www.jaegertracing.io/docs/1.21/apis/#protobuf-via-grpc-stab

The [Zipkin](https://zipkin.io/zipkin-api/) exporter. It sends JSON in [Zipkin format](https://zipkin.io/zipkin-api/#/default/post_spans) to a specified HTTP URL.

| System property | Environment variable | Description |
|-------------------------------|-------------------------------|-----------------------------------------------------------------------------------------------------------------------|
| otel.traces.exporter=zipkin | OTEL_TRACES_EXPORTER=zipkin | Select the Zipkin exporter |
| otel.exporter.zipkin.endpoint | OTEL_EXPORTER_ZIPKIN_ENDPOINT | The Zipkin endpoint to connect to. Default is `http://localhost:9411/api/v2/spans`. Currently only HTTP is supported. |
| System property | Environment variable | Description |
|-----------------------------------------|-----------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| otel.traces.exporter=zipkin | OTEL_TRACES_EXPORTER=zipkin | Select the Zipkin exporter |
| otel.exporter.zipkin.endpoint | OTEL_EXPORTER_ZIPKIN_ENDPOINT | The Zipkin endpoint to connect to. Default is `http://localhost:9411/api/v2/spans`. Currently only HTTP is supported. |
| otel.exporter.zipkin.certificate | OTEL_EXPORTER_ZIPKIN_CERTIFICATE | The path to the file containing trusted certificates to use when verifying a 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.zipkin.client.certificate | OTEL_EXPORTER_ZIPKIN_CLIENT_CERTIFICATE | The path to the file containing trusted certificates to use when verifying a 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.zipkin.client.key | OTEL_EXPORTER_ZIPKIN_CLIENT_KEY | The path to the file containing private client key to use when verifying a client's TLS credentials. The file should contain one private key PKCS8 PEM format. By default no client key is used. |

### Prometheus exporter

Expand Down

0 comments on commit 9f4e070

Please sign in to comment.