Skip to content

Commit

Permalink
[OTLP] Implement TLS certificates integration tests
Browse files Browse the repository at this point in the history
  • Loading branch information
dhhoang committed Aug 12, 2023
1 parent 12014f0 commit 9a9cc91
Show file tree
Hide file tree
Showing 4 changed files with 195 additions and 77 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Self-signed cert generated by integration test
otel-collector.crt
otel-collector.key
otel-client-cert.pem
otel-client-key.pem
otel-ca-cert.pem
otel-client.crt
otel-client.key
otel-untrusted-collector.crt
otel-untrusted-collector.key
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,8 @@ public void Dispose()
[InlineData(OtlpExportProtocol.HttpProtobuf, ":5318/v1/traces", ExportProcessorType.Simple, true, "https")]
[Trait("CategoryName", "CollectorIntegrationTests")]
[SkipUnlessEnvVarFoundTheory(CollectorHostnameEnvVarName)]
public void TraceExportResultIsSuccess(OtlpExportProtocol protocol, string endpoint, ExportProcessorType exportProcessorType, bool forceFlush, string scheme = "http")
public void ExportTrace_ReturnsSucceedResult(OtlpExportProtocol protocol, string endpoint, ExportProcessorType exportProcessorType, bool forceFlush, string scheme = "http")
{
using EventWaitHandle handle = new ManualResetEvent(false);

var exporterOptions = new OtlpExporterOptions
{
Endpoint = new Uri($"{scheme}://{CollectorHostname}{endpoint}"),
Expand All @@ -71,60 +69,107 @@ public void TraceExportResultIsSuccess(OtlpExportProtocol protocol, string endpo
},
};

DelegatingExporter<Activity> delegatingExporter = null;
var exportResults = new List<ExportResult>();
this.VerifyTraceExportResult(exporterOptions, forceFlush, ExportResult.Success);
}

var activitySourceName = "otlp.collector.test";
[InlineData(OtlpExportProtocol.Grpc, ":6317", ExportProcessorType.Simple, true)]
[InlineData(OtlpExportProtocol.HttpProtobuf, ":6318/v1/traces", ExportProcessorType.Simple, true)]
[Trait("CategoryName", "CollectorIntegrationTests")]
[Trait("CategoryName", "CollectorIntegrationTests")]
[SkipUnlessEnvVarFoundTheory(CollectorHostnameEnvVarName)]
public void ExportTrace_UntrustedRoot_CustomCATrustStore_ReturnsSucceedResult(
OtlpExportProtocol protocol,
string endpoint,
ExportProcessorType exportProcessorType,
bool forceFlush)
{
var exporterOptions = new OtlpExporterOptions
{
Endpoint = new Uri($"https://{CollectorHostname}{endpoint}"),
Protocol = protocol,
ExportProcessorType = exportProcessorType,
BatchExportProcessorOptions = new()
{
ScheduledDelayMilliseconds = ExportIntervalMilliseconds,
},
CertificateFile = "/cfg/otel-untrusted-collector.crt",
};

var builder = Sdk.CreateTracerProviderBuilder()
.AddSource(activitySourceName);
this.VerifyTraceExportResult(exporterOptions, forceFlush, ExportResult.Success);
}

builder.AddProcessor(OtlpTraceExporterHelperExtensions.BuildOtlpExporterProcessor(
exporterOptions,
DefaultSdkLimitOptions,
serviceProvider: null,
configureExporterInstance: otlpExporter =>
[InlineData(OtlpExportProtocol.Grpc, ":6317", ExportProcessorType.Simple)]
[InlineData(OtlpExportProtocol.HttpProtobuf, ":6318/v1/traces", ExportProcessorType.Simple)]
[Trait("CategoryName", "CollectorIntegrationTests")]
[Trait("CategoryName", "CollectorIntegrationTests")]
[SkipUnlessEnvVarFoundTheory(CollectorHostnameEnvVarName)]
public void ExportTrace_UntrustedRoot_NoCertificateSet_ReturnsFailureResult(
OtlpExportProtocol protocol,
string endpoint,
ExportProcessorType exportProcessorType)
{
var exporterOptions = new OtlpExporterOptions
{
Endpoint = new Uri($"https://{CollectorHostname}{endpoint}"),
Protocol = protocol,
ExportProcessorType = exportProcessorType,
BatchExportProcessorOptions = new()
{
delegatingExporter = new DelegatingExporter<Activity>
{
OnExportFunc = (batch) =>
{
var result = otlpExporter.Export(batch);
exportResults.Add(result);
handle.Set();
return result;
},
};
return delegatingExporter;
}));
ScheduledDelayMilliseconds = ExportIntervalMilliseconds,
},
};

using (var tracerProvider = builder.Build())
this.VerifyTraceExportResult(exporterOptions, true, ExportResult.Failure);
}

[InlineData(OtlpExportProtocol.Grpc, ":7317", ExportProcessorType.Simple)]
[InlineData(OtlpExportProtocol.HttpProtobuf, ":7318/v1/traces", ExportProcessorType.Simple)]
[Trait("CategoryName", "CollectorIntegrationTests")]
[Trait("CategoryName", "CollectorIntegrationTests")]
[SkipUnlessEnvVarFoundTheory(CollectorHostnameEnvVarName)]
public void ExportTrace_MTLS_WithClientCertificate_ReturnsSucceedResult(
OtlpExportProtocol protocol,
string endpoint,
ExportProcessorType exportProcessorType)
{
var exporterOptions = new OtlpExporterOptions
{
using var source = new ActivitySource(activitySourceName);
var activity = source.StartActivity($"{protocol} Test Activity");
activity?.Stop();
Endpoint = new Uri($"https://{CollectorHostname}{endpoint}"),
Protocol = protocol,
ExportProcessorType = exportProcessorType,
BatchExportProcessorOptions = new()
{
ScheduledDelayMilliseconds = ExportIntervalMilliseconds,
},
ClientCertificateFile = "/cfg/otel-client.crt",
ClientKeyFile = "/cfg/otel-client.key",
};

Assert.NotNull(delegatingExporter);
this.VerifyTraceExportResult(exporterOptions, true, ExportResult.Success);
}

if (forceFlush)
{
Assert.True(tracerProvider.ForceFlush());
Assert.Single(exportResults);
Assert.Equal(ExportResult.Success, exportResults[0]);
}
else if (exporterOptions.ExportProcessorType == ExportProcessorType.Batch)
[InlineData(OtlpExportProtocol.Grpc, ":7317", ExportProcessorType.Simple)]
[InlineData(OtlpExportProtocol.HttpProtobuf, ":7318/v1/traces", ExportProcessorType.Simple)]
[Trait("CategoryName", "CollectorIntegrationTests")]
[Trait("CategoryName", "CollectorIntegrationTests")]
[SkipUnlessEnvVarFoundTheory(CollectorHostnameEnvVarName)]
public void ExportTrace_MTLS_NoClientCertificate_ReturnsFailureResult(
OtlpExportProtocol protocol,
string endpoint,
ExportProcessorType exportProcessorType)
{
var exporterOptions = new OtlpExporterOptions
{
Endpoint = new Uri($"https://{CollectorHostname}{endpoint}"),
Protocol = protocol,
ExportProcessorType = exportProcessorType,
BatchExportProcessorOptions = new()
{
Assert.True(handle.WaitOne(ExportIntervalMilliseconds * 2));
Assert.Single(exportResults);
Assert.Equal(ExportResult.Success, exportResults[0]);
}
}
ScheduledDelayMilliseconds = ExportIntervalMilliseconds,
},
};

if (!forceFlush && exportProcessorType == ExportProcessorType.Simple)
{
Assert.Single(exportResults);
Assert.Equal(ExportResult.Success, exportResults[0]);
}
this.VerifyTraceExportResult(exporterOptions, true, ExportResult.Failure);
}

[InlineData(OtlpExportProtocol.Grpc, ":4317", false, false)]
Expand Down Expand Up @@ -237,6 +282,66 @@ public void ConstructingGrpcExporterFailsWhenHttp2UnencryptedSupportIsDisabledFo
}
}

private void VerifyTraceExportResult(OtlpExporterOptions exporterOptions, bool forceFlush, ExportResult expectedResult)
{
using EventWaitHandle handle = new ManualResetEvent(false);

DelegatingExporter<Activity> delegatingExporter = null;
var exportResults = new List<ExportResult>();

var activitySourceName = "otlp.collector.test";

var builder = Sdk.CreateTracerProviderBuilder()
.AddSource(activitySourceName);

builder.AddProcessor(OtlpTraceExporterHelperExtensions.BuildOtlpExporterProcessor(
exporterOptions,
DefaultSdkLimitOptions,
serviceProvider: null,
configureExporterInstance: otlpExporter =>
{
delegatingExporter = new DelegatingExporter<Activity>
{
OnExportFunc = (batch) =>
{
var result = otlpExporter.Export(batch);
exportResults.Add(result);
handle.Set();
return result;
},
};
return delegatingExporter;
}));

using (var tracerProvider = builder.Build())
{
using var source = new ActivitySource(activitySourceName);
var activity = source.StartActivity($"{exporterOptions.Protocol} Test Activity");
activity?.Stop();

Assert.NotNull(delegatingExporter);

if (forceFlush)
{
Assert.True(tracerProvider.ForceFlush());
Assert.Single(exportResults);
Assert.Equal(expectedResult, exportResults[0]);
}
else if (exporterOptions.ExportProcessorType == ExportProcessorType.Batch)
{
Assert.True(handle.WaitOne(ExportIntervalMilliseconds * 2));
Assert.Single(exportResults);
Assert.Equal(expectedResult, exportResults[0]);
}
}

if (!forceFlush && exporterOptions.ExportProcessorType == ExportProcessorType.Simple)
{
Assert.Single(exportResults);
Assert.Equal(expectedResult, exportResults[0]);
}
}

private sealed class OpenTelemetryEventListener : EventListener
{
private readonly ITestOutputHelper outputHelper;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,7 @@ cp /otel-collector.crt /otel-collector.key /cfg

chmod 644 /cfg/otel-collector.key

# Generate CA and client cert for mTLS
echo "\
basicConstraints = CA:FALSE
nsCertType = server
nsComment = "OpenSSL Generated CA Certificate"
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer:always
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
extendedKeyUsage = serverAuth
" > /ca_cert_ext.cnf

openssl ecparam -genkey -name prime256v1 -out /otel-ca-key.pem
openssl req -new -sha256 -key /otel-ca-key.pem -out /otel-ca-csr.pem -subj "/CN=otel-test-ca"
openssl x509 -req -in /otel-ca-csr.pem -sha256 -days 365 -signkey /otel-ca-key.pem -out /otel-ca-cert.pem -extfile /ca_cert_ext.cnf

# Generate client certificate for mTLS
echo "\
basicConstraints = CA:FALSE
nsCertType = client, email
Expand All @@ -34,14 +20,28 @@ subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
extendedKeyUsage = clientAuth, emailProtection
" > client_cert_ext.cnf
" > /client_ext.cnf

openssl ecparam -genkey -name prime256v1 -out /otel-client-key.pem
openssl req -new -key /otel-client-key.pem -out /otel-client-csr.pem -subj "/CN=otel-test-client"
openssl x509 -req -in /otel-client-csr.pem -CA /otel-ca-cert.pem -CAkey /otel-ca-key.pem -out /otel-client-cert.pem -CAcreateserial -days 365 -sha256 -extfile /client_cert_ext.cnf
openssl req -new -newkey rsa:2048 -days 365 -nodes \
-subj "/CN=otel-client" \
-keyout /otel-client.key -out /otel-client.csr

openssl x509 -req -in /otel-client.csr \
-CA /otel-collector.crt -CAkey /otel-collector.key \
-out /otel-client.crt -CAcreateserial -days 365 -sha256 \
-extfile ./client_ext.cnf

cp /otel-client.crt /otel-client.key /cfg
chmod 644 /cfg/otel-client.key

# Generate an self-signed certificate that is NOT included in the test runner's trust store
# Generate self-signed certificate for the collector
openssl req -new -newkey rsa:2048 -days 365 -nodes -x509 \
-subj "/CN=otel-collector" \
-keyout /otel-untrusted-collector.key -out /otel-untrusted-collector.crt

cp /otel-ca-cert.pem /otel-client-cert.pem /otel-client-key.pem /cfg
cp /otel-ca-cert.pem /usr/local/share/ca-certificates/otel-ca-cert.pem
cp /otel-untrusted-collector.crt /otel-untrusted-collector.key /cfg
chmod 644 /cfg/otel-untrusted-collector.key

# The integration test is run via docker-compose with the --exit-code-from
# option. The --exit-code-from option implies --abort-on-container-exit
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,20 +23,32 @@ receivers:
tls:
cert_file: /cfg/otel-collector.crt
key_file: /cfg/otel-collector.key
otlp/mtls:
otlp/untrustedtls:
protocols:
grpc:
endpoint: 0.0.0.0:6317
tls:
cert_file: /cfg/otel-untrusted-collector.crt
key_file: /cfg/otel-untrusted-collector.key
http:
endpoint: 0.0.0.0:6318
tls:
cert_file: /cfg/otel-untrusted-collector.crt
key_file: /cfg/otel-untrusted-collector.key
otlp/mtls:
protocols:
grpc:
endpoint: 0.0.0.0:7317
tls:
cert_file: /cfg/otel-collector.crt
key_file: /cfg/otel-collector.key
client_ca_file: /cfg/otel-ca-cert.pem
client_ca_file: /cfg/otel-collector.crt
http:
endpoint: 0.0.0.0:6318
endpoint: 0.0.0.0:7318
tls:
cert_file: /cfg/otel-collector.crt
key_file: /cfg/otel-collector.key
client_ca_file: /cfg/otel-ca-cert.pem
client_ca_file: /cfg/otel-collector.crt

exporters:
logging:
Expand All @@ -45,8 +57,8 @@ exporters:
service:
pipelines:
traces:
receivers: [otlp, otlp/tls, otlp/mtls]
receivers: [otlp, otlp/tls, otlp/untrustedtls, otlp/mtls]
exporters: [logging]
metrics:
receivers: [otlp, otlp/tls, otlp/mtls]
receivers: [otlp, otlp/tls, otlp/untrustedtls, otlp/mtls]
exporters: [logging]

0 comments on commit 9a9cc91

Please sign in to comment.