Skip to content

Commit bebeff9

Browse files
committed
feat(): add mtls support
1 parent b67f2ef commit bebeff9

File tree

3 files changed

+77
-76
lines changed

3 files changed

+77
-76
lines changed

src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpMtlsOptions.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,8 @@ public Func<
5353
X509Chain,
5454
SslPolicyErrors,
5555
bool
56-
>? ServerCertificateValidationCallback { get; set; }
56+
>? ServerCertificateValidationCallback
57+
{ get; set; }
5758

5859
/// <summary>
5960
/// Gets a value indicating whether mTLS is enabled.

test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpMtlsCertificateManagerTests.cs

Lines changed: 39 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -43,91 +43,91 @@ public class OtlpMtlsCertificateManagerTests
4343
[Xunit.Fact]
4444
public void LoadClientCertificate_ThrowsFileNotFoundException_WhenCertificateFileDoesNotExist()
4545
{
46-
var exception = Xunit.Assert.Throws<System.IO.FileNotFoundException>(() =>
47-
OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.OtlpMtlsCertificateManager.LoadClientCertificate(
46+
var exception = Xunit.Assert.Throws<FileNotFoundException>(() =>
47+
OpenTelemetryProtocol.Implementation.OtlpMtlsCertificateManager.LoadClientCertificate(
4848
"/nonexistent/client.crt",
4949
"/nonexistent/client.key"));
5050

51-
Xunit.Assert.Contains("Certificate file not found", exception.Message, System.StringComparison.OrdinalIgnoreCase);
52-
Xunit.Assert.Contains("/nonexistent/client.crt", exception.Message, System.StringComparison.OrdinalIgnoreCase);
51+
Xunit.Assert.Contains("Certificate file not found", exception.Message, StringComparison.OrdinalIgnoreCase);
52+
Xunit.Assert.Contains("/nonexistent/client.crt", exception.Message, StringComparison.OrdinalIgnoreCase);
5353
}
5454

5555
[Xunit.Fact]
5656
public void LoadClientCertificate_ThrowsFileNotFoundException_WhenPrivateKeyFileDoesNotExist()
5757
{
58-
var tempCertFile = System.IO.Path.GetTempFileName();
59-
System.IO.File.WriteAllText(tempCertFile, TestCertPem);
58+
var tempCertFile = Path.GetTempFileName();
59+
File.WriteAllText(tempCertFile, TestCertPem);
6060

6161
try
6262
{
63-
var exception = Xunit.Assert.Throws<System.IO.FileNotFoundException>(() =>
64-
OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.OtlpMtlsCertificateManager.LoadClientCertificate(
63+
var exception = Xunit.Assert.Throws<FileNotFoundException>(() =>
64+
OpenTelemetryProtocol.Implementation.OtlpMtlsCertificateManager.LoadClientCertificate(
6565
tempCertFile,
6666
"/nonexistent/client.key"));
6767

68-
Xunit.Assert.Contains("Private key file not found", exception.Message, System.StringComparison.OrdinalIgnoreCase);
69-
Xunit.Assert.Contains("/nonexistent/client.key", exception.Message, System.StringComparison.OrdinalIgnoreCase);
68+
Xunit.Assert.Contains("Private key file not found", exception.Message, StringComparison.OrdinalIgnoreCase);
69+
Xunit.Assert.Contains("/nonexistent/client.key", exception.Message, StringComparison.OrdinalIgnoreCase);
7070
}
7171
finally
7272
{
73-
System.IO.File.Delete(tempCertFile);
73+
File.Delete(tempCertFile);
7474
}
7575
}
7676

7777
[Xunit.Fact]
7878
public void LoadCaCertificate_ThrowsFileNotFoundException_WhenTrustStoreFileDoesNotExist()
7979
{
80-
var exception = Xunit.Assert.Throws<System.IO.FileNotFoundException>(() =>
81-
OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.OtlpMtlsCertificateManager.LoadCaCertificate("/nonexistent/ca.crt"));
80+
var exception = Xunit.Assert.Throws<FileNotFoundException>(() =>
81+
OpenTelemetryProtocol.Implementation.OtlpMtlsCertificateManager.LoadCaCertificate("/nonexistent/ca.crt"));
8282

83-
Xunit.Assert.Contains("CA certificate file not found", exception.Message, System.StringComparison.OrdinalIgnoreCase);
84-
Xunit.Assert.Contains("/nonexistent/ca.crt", exception.Message, System.StringComparison.OrdinalIgnoreCase);
83+
Xunit.Assert.Contains("CA certificate file not found", exception.Message, StringComparison.OrdinalIgnoreCase);
84+
Xunit.Assert.Contains("/nonexistent/ca.crt", exception.Message, StringComparison.OrdinalIgnoreCase);
8585
}
8686

8787
[Xunit.Fact]
8888
public void LoadClientCertificate_ThrowsInvalidOperationException_WhenCertificateFileIsEmpty()
8989
{
90-
var tempCertFile = System.IO.Path.GetTempFileName();
91-
var tempKeyFile = System.IO.Path.GetTempFileName();
92-
System.IO.File.WriteAllText(tempCertFile, string.Empty);
93-
System.IO.File.WriteAllText(tempKeyFile, string.Empty);
90+
var tempCertFile = Path.GetTempFileName();
91+
var tempKeyFile = Path.GetTempFileName();
92+
File.WriteAllText(tempCertFile, string.Empty);
93+
File.WriteAllText(tempKeyFile, string.Empty);
9494

9595
try
9696
{
97-
var exception = Xunit.Assert.Throws<System.InvalidOperationException>(() =>
98-
OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.OtlpMtlsCertificateManager.LoadClientCertificate(tempCertFile, tempKeyFile));
97+
var exception = Xunit.Assert.Throws<InvalidOperationException>(() =>
98+
OpenTelemetryProtocol.Implementation.OtlpMtlsCertificateManager.LoadClientCertificate(tempCertFile, tempKeyFile));
9999

100100
Xunit.Assert.Contains(
101101
"Failed to load client certificate",
102102
exception.Message,
103-
System.StringComparison.OrdinalIgnoreCase);
103+
StringComparison.OrdinalIgnoreCase);
104104
}
105105
finally
106106
{
107-
System.IO.File.Delete(tempCertFile);
108-
System.IO.File.Delete(tempKeyFile);
107+
File.Delete(tempCertFile);
108+
File.Delete(tempKeyFile);
109109
}
110110
}
111111

112112
[Xunit.Fact]
113113
public void LoadCaCertificate_ThrowsInvalidOperationException_WhenTrustStoreFileIsEmpty()
114114
{
115-
var tempTrustStoreFile = System.IO.Path.GetTempFileName();
116-
System.IO.File.WriteAllText(tempTrustStoreFile, string.Empty);
115+
var tempTrustStoreFile = Path.GetTempFileName();
116+
File.WriteAllText(tempTrustStoreFile, string.Empty);
117117

118118
try
119119
{
120-
var exception = Xunit.Assert.Throws<System.InvalidOperationException>(() =>
121-
OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.OtlpMtlsCertificateManager.LoadCaCertificate(tempTrustStoreFile));
120+
var exception = Xunit.Assert.Throws<InvalidOperationException>(() =>
121+
OpenTelemetryProtocol.Implementation.OtlpMtlsCertificateManager.LoadCaCertificate(tempTrustStoreFile));
122122

123123
Xunit.Assert.Contains(
124124
"Failed to load CA certificate",
125125
exception.Message,
126-
System.StringComparison.OrdinalIgnoreCase);
126+
StringComparison.OrdinalIgnoreCase);
127127
}
128128
finally
129129
{
130-
System.IO.File.Delete(tempTrustStoreFile);
130+
File.Delete(tempTrustStoreFile);
131131
}
132132
}
133133

@@ -138,7 +138,7 @@ public void ValidateCertificateChain_DoesNotThrow_WithValidCertificate()
138138
using var cert = CreateSelfSignedCertificate();
139139

140140
// Should not throw for self-signed certificate with proper validation
141-
var result = OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.OtlpMtlsCertificateManager.ValidateCertificateChain(cert, "test certificate");
141+
var result = OpenTelemetryProtocol.Implementation.OtlpMtlsCertificateManager.ValidateCertificateChain(cert, "test certificate");
142142

143143
// For self-signed certificates, validation may fail, but method should not throw
144144
Xunit.Assert.True(result || !result); // Just check that it returns a boolean
@@ -150,13 +150,13 @@ public void ValidateCertificateChain_ThrowsInvalidOperationException_WhenCertifi
150150
// Create an expired certificate for testing
151151
using var cert = CreateExpiredCertificate();
152152

153-
var exception = Xunit.Assert.Throws<System.InvalidOperationException>(() =>
154-
OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.OtlpMtlsCertificateManager.ValidateCertificateChain(cert, "expired certificate"));
153+
var exception = Xunit.Assert.Throws<InvalidOperationException>(() =>
154+
OpenTelemetryProtocol.Implementation.OtlpMtlsCertificateManager.ValidateCertificateChain(cert, "expired certificate"));
155155

156156
Xunit.Assert.Contains(
157157
"Certificate chain validation failed",
158158
exception.Message,
159-
System.StringComparison.OrdinalIgnoreCase);
159+
StringComparison.OrdinalIgnoreCase);
160160
}
161161

162162
[Xunit.Fact]
@@ -166,7 +166,7 @@ public void ValidateCertificateChain_ReturnsResult_WithValidCertificate()
166166
using var cert = CreateSelfSignedCertificate();
167167

168168
// Should return a boolean result
169-
var result = OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.OtlpMtlsCertificateManager.ValidateCertificateChain(cert, "test certificate");
169+
var result = OpenTelemetryProtocol.Implementation.OtlpMtlsCertificateManager.ValidateCertificateChain(cert, "test certificate");
170170

171171
// The result can be true or false, but the method should not throw
172172
Xunit.Assert.True(result || !result);
@@ -182,8 +182,8 @@ private static System.Security.Cryptography.X509Certificates.X509Certificate2 Cr
182182
System.Security.Cryptography.RSASignaturePadding.Pkcs1);
183183

184184
var cert = req.CreateSelfSigned(
185-
System.DateTimeOffset.UtcNow.AddDays(-1),
186-
System.DateTimeOffset.UtcNow.AddDays(30));
185+
DateTimeOffset.UtcNow.AddDays(-1),
186+
DateTimeOffset.UtcNow.AddDays(30));
187187
return cert;
188188
}
189189

@@ -198,8 +198,8 @@ private static System.Security.Cryptography.X509Certificates.X509Certificate2 Cr
198198

199199
// Create a certificate that expired yesterday
200200
var cert = req.CreateSelfSigned(
201-
System.DateTimeOffset.UtcNow.AddDays(-30),
202-
System.DateTimeOffset.UtcNow.AddDays(-1));
201+
DateTimeOffset.UtcNow.AddDays(-30),
202+
DateTimeOffset.UtcNow.AddDays(-1));
203203
return cert;
204204
}
205205
}

test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpMtlsHttpClientFactoryTests.cs

Lines changed: 36 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -10,128 +10,128 @@ public class OtlpMtlsHttpClientFactoryTests
1010
[Xunit.Fact]
1111
public void CreateHttpClient_ReturnsHttpClient_WhenMtlsIsDisabled()
1212
{
13-
var baseFactory = () => new System.Net.Http.HttpClient();
14-
var options = new OpenTelemetry.Exporter.OtlpMtlsOptions(); // Disabled by default
13+
var baseFactory = () => new HttpClient();
14+
var options = new OtlpMtlsOptions(); // Disabled by default
1515

16-
using var httpClient = OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.OtlpMtlsHttpClientFactory.CreateMtlsHttpClient(options, baseFactory);
16+
using var httpClient = OpenTelemetryProtocol.Implementation.OtlpMtlsHttpClientFactory.CreateMtlsHttpClient(options, baseFactory);
1717

1818
Xunit.Assert.NotNull(httpClient);
19-
Xunit.Assert.IsType<System.Net.Http.HttpClient>(httpClient);
19+
Xunit.Assert.IsType<HttpClient>(httpClient);
2020
}
2121

2222
[Xunit.Fact]
2323
public void CreateHttpClient_ThrowsFileNotFoundException_WhenCertificateFileDoesNotExist()
2424
{
25-
var baseFactory = () => new System.Net.Http.HttpClient();
26-
var options = new OpenTelemetry.Exporter.OtlpMtlsOptions { ClientCertificatePath = "/nonexistent/client.crt" };
25+
var baseFactory = () => new HttpClient();
26+
var options = new OtlpMtlsOptions { ClientCertificatePath = "/nonexistent/client.crt" };
2727

28-
var exception = Xunit.Assert.Throws<System.IO.FileNotFoundException>(() =>
29-
OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.OtlpMtlsHttpClientFactory.CreateMtlsHttpClient(options, baseFactory));
28+
var exception = Xunit.Assert.Throws<FileNotFoundException>(() =>
29+
OpenTelemetryProtocol.Implementation.OtlpMtlsHttpClientFactory.CreateMtlsHttpClient(options, baseFactory));
3030

31-
Xunit.Assert.Contains("Certificate file not found", exception.Message, System.StringComparison.OrdinalIgnoreCase);
31+
Xunit.Assert.Contains("Certificate file not found", exception.Message, StringComparison.OrdinalIgnoreCase);
3232
}
3333

3434
[Xunit.Fact]
3535
public void CreateHttpClient_ConfiguresClientCertificate_WhenValidCertificateProvided()
3636
{
37-
var tempCertFile = System.IO.Path.GetTempFileName();
37+
var tempCertFile = Path.GetTempFileName();
3838
try
3939
{
4040
// Create a self-signed certificate for testing
4141
using var cert = CreateSelfSignedCertificate();
4242
var certBytes = cert.Export(System.Security.Cryptography.X509Certificates.X509ContentType.Pfx, "testpassword");
43-
System.IO.File.WriteAllBytes(tempCertFile, certBytes);
43+
File.WriteAllBytes(tempCertFile, certBytes);
4444

45-
var baseFactory = () => new System.Net.Http.HttpClient();
46-
var options = new OpenTelemetry.Exporter.OtlpMtlsOptions
45+
var baseFactory = () => new HttpClient();
46+
var options = new OtlpMtlsOptions
4747
{
4848
ClientCertificatePath = tempCertFile,
4949

5050
// Note: Password support would need to be added to OtlpMtlsOptions
5151
EnableCertificateChainValidation = false, // Ignore validation for test cert
5252
};
5353

54-
using var httpClient = OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.OtlpMtlsHttpClientFactory.CreateMtlsHttpClient(options, baseFactory);
54+
using var httpClient = OpenTelemetryProtocol.Implementation.OtlpMtlsHttpClientFactory.CreateMtlsHttpClient(options, baseFactory);
5555

5656
Xunit.Assert.NotNull(httpClient);
5757

5858
// Verify the HttpClientHandler has client certificates configured
59-
var handlerField = typeof(System.Net.Http.HttpClient).GetField(
59+
var handlerField = typeof(HttpClient).GetField(
6060
"_handler",
6161
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
62-
if (handlerField?.GetValue(httpClient) is System.Net.Http.HttpClientHandler handler)
62+
if (handlerField?.GetValue(httpClient) is HttpClientHandler handler)
6363
{
6464
Xunit.Assert.NotEmpty(handler.ClientCertificates);
6565
}
6666
}
6767
finally
6868
{
69-
if (System.IO.File.Exists(tempCertFile))
69+
if (File.Exists(tempCertFile))
7070
{
71-
System.IO.File.Delete(tempCertFile);
71+
File.Delete(tempCertFile);
7272
}
7373
}
7474
}
7575

7676
[Xunit.Fact]
7777
public void CreateHttpClient_ConfiguresServerCertificateValidation_WhenTrustedRootCertificatesProvided()
7878
{
79-
var tempTrustStoreFile = System.IO.Path.GetTempFileName();
79+
var tempTrustStoreFile = Path.GetTempFileName();
8080
try
8181
{
8282
// Create a self-signed certificate for testing as trusted root
8383
using var trustedCert = CreateSelfSignedCertificate();
84-
var trustedCertPem = System.Convert.ToBase64String(trustedCert.Export(System.Security.Cryptography.X509Certificates.X509ContentType.Cert));
84+
var trustedCertPem = Convert.ToBase64String(trustedCert.Export(System.Security.Cryptography.X509Certificates.X509ContentType.Cert));
8585
var pemContent =
8686
$"-----BEGIN CERTIFICATE-----\n{trustedCertPem}\n-----END CERTIFICATE-----";
87-
System.IO.File.WriteAllText(tempTrustStoreFile, pemContent);
87+
File.WriteAllText(tempTrustStoreFile, pemContent);
8888

89-
var baseFactory = () => new System.Net.Http.HttpClient();
90-
var options = new OpenTelemetry.Exporter.OtlpMtlsOptions
89+
var baseFactory = () => new HttpClient();
90+
var options = new OtlpMtlsOptions
9191
{
9292
CaCertificatePath = tempTrustStoreFile,
9393
};
9494

95-
using var httpClient = OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.OtlpMtlsHttpClientFactory.CreateMtlsHttpClient(options, baseFactory);
95+
using var httpClient = OpenTelemetryProtocol.Implementation.OtlpMtlsHttpClientFactory.CreateMtlsHttpClient(options, baseFactory);
9696

9797
Xunit.Assert.NotNull(httpClient);
9898

9999
// Verify the HttpClientHandler has server certificate validation configured
100-
var handlerField = typeof(System.Net.Http.HttpClient).GetField(
100+
var handlerField = typeof(HttpClient).GetField(
101101
"_handler",
102102
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
103-
if (handlerField?.GetValue(httpClient) is System.Net.Http.HttpClientHandler handler)
103+
if (handlerField?.GetValue(httpClient) is HttpClientHandler handler)
104104
{
105105
Xunit.Assert.NotNull(handler.ServerCertificateCustomValidationCallback);
106106
}
107107
}
108108
finally
109109
{
110-
if (System.IO.File.Exists(tempTrustStoreFile))
110+
if (File.Exists(tempTrustStoreFile))
111111
{
112-
System.IO.File.Delete(tempTrustStoreFile);
112+
File.Delete(tempTrustStoreFile);
113113
}
114114
}
115115
}
116116

117117
[Xunit.Fact]
118118
public void CreateMtlsHttpClient_ThrowsArgumentNullException_WhenBaseFactoryIsNull()
119119
{
120-
var options = new OpenTelemetry.Exporter.OtlpMtlsOptions();
120+
var options = new OtlpMtlsOptions();
121121

122-
var exception = Xunit.Assert.Throws<System.ArgumentNullException>(() =>
123-
OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.OtlpMtlsHttpClientFactory.CreateMtlsHttpClient(options, null!));
122+
var exception = Xunit.Assert.Throws<ArgumentNullException>(() =>
123+
OpenTelemetryProtocol.Implementation.OtlpMtlsHttpClientFactory.CreateMtlsHttpClient(options, null!));
124124

125125
Xunit.Assert.Equal("baseFactory", exception.ParamName);
126126
}
127127

128128
[Xunit.Fact]
129129
public void CreateMtlsHttpClient_ThrowsArgumentNullException_WhenOptionsIsNull()
130130
{
131-
var baseFactory = () => new System.Net.Http.HttpClient();
131+
var baseFactory = () => new HttpClient();
132132

133-
var exception = Xunit.Assert.Throws<System.ArgumentNullException>(() =>
134-
OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.OtlpMtlsHttpClientFactory.CreateMtlsHttpClient(null!, baseFactory));
133+
var exception = Xunit.Assert.Throws<ArgumentNullException>(() =>
134+
OpenTelemetryProtocol.Implementation.OtlpMtlsHttpClientFactory.CreateMtlsHttpClient(null!, baseFactory));
135135

136136
Xunit.Assert.Equal("mtlsOptions", exception.ParamName);
137137
}
@@ -146,8 +146,8 @@ private static System.Security.Cryptography.X509Certificates.X509Certificate2 Cr
146146
System.Security.Cryptography.RSASignaturePadding.Pkcs1);
147147

148148
var cert = req.CreateSelfSigned(
149-
System.DateTimeOffset.UtcNow.AddDays(-1),
150-
System.DateTimeOffset.UtcNow.AddDays(30));
149+
DateTimeOffset.UtcNow.AddDays(-1),
150+
DateTimeOffset.UtcNow.AddDays(30));
151151
return cert;
152152
}
153153
}

0 commit comments

Comments
 (0)