-
Notifications
You must be signed in to change notification settings - Fork 796
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[otlp] Add mTLS Support for OTLP Exporter #5918
base: main
Are you sure you want to change the base?
Conversation
@@ -28,6 +28,26 @@ protected override HttpContent CreateHttpContent(OtlpCollector.ExportTraceServic | |||
return new ExportRequestContent(exportRequest); | |||
} | |||
|
|||
private static HttpClient ModifyHttpClient(OtlpExporterOptions options, HttpClient httpClient) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can't just throw away the HttpClient
and make a new one. Users are able to configure SSL/TLS today using factory:
opentelemetry-dotnet/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterOptions.cs
Line 134 in 5dff99f
public Func<HttpClient> HttpClientFactory |
This change will break any user doing that or doing anything else to the HttpClient they are intending to use here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I modified a little bit, does it look fine? @CodeBlanch
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the design looks ok, I will start to write tests :)
…9CertificateLoader
…9CertificateLoader
…9CertificateLoader
…9CertificateLoader
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #5918 +/- ##
==========================================
- Coverage 85.15% 85.12% -0.03%
==========================================
Files 272 272
Lines 12420 12548 +128
==========================================
+ Hits 10576 10682 +106
- Misses 1844 1866 +22
Flags with carried forward coverage won't be shown. Click here to find out more.
|
Just for your info, |
@sandy2008 I recommend putting this on hold for a month. We are working on a custom serialization (#5730) and also we plan to handle GRPC without relying on Grpc.Net packages and removing dependencies on Google Protobuf and GRPC libraries. This will impact the part where you are adding support for mTLS. |
Hi :) @rajkumar-rangaraj |
Today, we had a discussion on this topic in the SIG meeting. We all feel it is better to hold off on this work as this space is expected to change drastically in the coming weeks. We thought it would be beneficial if you could join the next SIG meeting to discuss it further. |
Thank you for the update. @rajkumar-rangaraj Could you please let me know the time and participation details for the next SIG meeting? I think it’s important for @sokoide , as a co-author, to attend as well, since I might not be able to cover everything alone. |
Please check the details here - https://github.com/open-telemetry/opentelemetry-dotnet?tab=readme-ov-file#contributing |
Thank you for the details! We’ll join the SIG meeting scheduled for November 5 at 16:00 PT (November 6, 11:00 JST). Looking forward to it! |
{ | ||
var trustedCertificate = X509Certificate2.CreateFromPemFile(this.CertificateFile); | ||
|
||
handler.ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you want users of this API being able to add their custom validation callbacks next to this one?
public static void TryEnableIHttpClientFactoryIntegration(this OtlpExporterOptions options, IServiceProvider serviceProvider, string httpClientName)
{
if (serviceProvider != null
&& options.Protocol == OtlpExportProtocol.HttpProtobuf
&& options.HttpClientFactory == options.DefaultHttpClientFactory)
{
options.HttpClientFactory = () =>
{
Type? httpClientFactoryType = Type.GetType("System.Net.Http.IHttpClientFactory, Microsoft.Extensions.Http", throwOnError: false);
if (httpClientFactoryType != null)
{
object? httpClientFactory = serviceProvider.GetService(httpClientFactoryType);
if (httpClientFactory != null)
{
MethodInfo? createClientMethod = httpClientFactoryType.GetMethod(
"CreateClient",
BindingFlags.Public | BindingFlags.Instance,
binder: null,
new Type[] { typeof(string) },
modifiers: null);
if (createClientMethod != null)
{
HttpClient? client = (HttpClient?)createClientMethod.Invoke(httpClientFactory, new object[] { httpClientName });
if (client != null)
{
client.Timeout = TimeSpan.FromMilliseconds(options.TimeoutMilliseconds);
// Set up a new HttpClientHandler to configure certificates and callbacks
var handler = new HttpClientHandler();
#if NET6_0_OR_GREATER
// Add server certificate validation
if (!string.IsNullOrEmpty(options.CertificateFile))
{
var trustedCertificate = X509Certificate2.CreateFromPemFile(options.CertificateFile);
handler.ServerCertificateCustomValidationCallback = (message, cert, chain, errors) =>
{
if (cert != null && chain != null)
{
chain.ChainPolicy.TrustMode = X509ChainTrustMode.CustomRootTrust;
chain.ChainPolicy.CustomTrustStore.Add(trustedCertificate);
return chain.Build(cert);
}
return false;
};
}
// Add client certificate
if (!string.IsNullOrEmpty(options.ClientCertificateFile) && !string.IsNullOrEmpty(options.ClientKeyFile))
{
var clientCertificate = X509Certificate2.CreateFromPemFile(options.ClientCertificateFile, options.ClientKeyFile);
handler.ClientCertificates.Add(clientCertificate);
}
#else
throw new PlatformNotSupportedException("mTLS support requires .NET 6.0 or later.");
#endif
// Re-create HttpClient using the custom handler
return new HttpClient(handler) { Timeout = client.Timeout };
}
}
}
}
return options.();
};
}
} Something like this would work? |
@sandy2008 Sorry, I don't have the bandwidth to review this now. As we discussed in the SIG, I will get to this part once the dependencies on |
@rajkumar-rangaraj Hi! Does the correct logics look good? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Copilot reviewed 4 out of 4 changed files in this pull request and generated no comments.
Comments suppressed due to low confidence (2)
src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterOptionsExtensions.cs:39
- Reading certificate files using File.ReadAllText without any error handling may lead to unhandled exceptions if the file is missing or inaccessible. Consider adding try/catch blocks or validating file existence before reading.
if (!string.IsNullOrEmpty(options.ClientCertificateFile) && !string.IsNullOrEmpty(options.ClientKeyFile))
test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/IntegrationTest/IntegrationTests.cs:230
- The mTLS branch is not exercised by any test cases since none of the InlineData provide a true value for 'useMtls'. Consider adding an InlineData test case that explicitly sets useMtls to true.
if (useMtls)
I will fix the comments by Copilot first :) @rajkumar-rangaraj Thank you! |
Could you also update the documentation on how it can be used? Additionally, updating the PR description with testing steps for the OpenTelemetry Collector would be helpful. |
@@ -0,0 +1,6 @@ | |||
OpenTelemetry.Exporter.OtlpExporterOptions.CertificateFile.get -> string! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shall we keep this internal and rely only on the environment variable? If there is a request, we can work on exposing these as public APIs.
client.Timeout = TimeSpan.FromMilliseconds(this.TimeoutMilliseconds); | ||
return client; | ||
#else | ||
// For earlier .NET versions |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is incorrect; only the latest .NET versions use HttpClient
. .NET Framework and older versions of .NET Core still rely on Grpc.Core
.
if (!string.IsNullOrEmpty(this.CertificateFile)) | ||
{ | ||
// Load the certificate from the file | ||
var trustedCertificate = X509Certificate2.CreateFromPemFile(this.CertificateFile); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The implementation does not verify if CertificateFile, ClientCertificateFile, or ClientKeyFile exist before attempting to load them.
@@ -220,6 +255,41 @@ internal OtlpExporterOptions ApplyDefaults(OtlpExporterOptions defaultExporterOp | |||
return this; | |||
} | |||
|
|||
#if NET6_0_OR_GREATER | |||
internal HttpClient AddCertificatesToHttpClient(HttpClientHandler handler) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How you plan to handle errors within this method?
{ | ||
chain.ChainPolicy.TrustMode = X509ChainTrustMode.CustomRootTrust; | ||
chain.ChainPolicy.CustomTrustStore.Add(trustedCertificate); | ||
return chain.Build(cert); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
chain.Build(cert) might fail if the certificate chain is incomplete or missing necessary intermediate certificates. Consider checking chain.ChainStatus for errors before returning true. Also, logging failures can help diagnose why validation failed
var trustedCertificate = X509Certificate2.CreateFromPemFile(this.CertificateFile); | ||
|
||
// Set custom server certificate validation callback | ||
handler.ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using a custom server certificate validation callback can introduce security risks if not handled properly. Can you list what measures have been taken to mitigate potential risks, such as unintended trust, proper error handling, revocation checks, and restricting trust scope?
// Add client certificate if both files are provided | ||
if (!string.IsNullOrEmpty(this.ClientCertificateFile) && !string.IsNullOrEmpty(this.ClientKeyFile)) | ||
{ | ||
var clientCertificate = X509Certificate2.CreateFromPemFile(this.ClientCertificateFile, this.ClientKeyFile); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Loading a client certificate from a PEM file with a private key should be done cautiously. How do you ensure or inform customers that the private key file should be stored securely and access-restricted?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This PR introduces potential security risks, particularly in certificate validation and private key handling, which need further clarification. There are concerns about trusting unverified certificates and missing security measures for private key storage. Before proceeding, clear guidance on mitigating these risks and best practices for secure implementation should be provided. Until these concerns are addressed, I am blocking this PR to ensure security is not compromised. Looking forward to further clarifications.
Thank you for your reply! Will review on security :) |
Fixes #2009
Based on PR
Changes
This pull request introduces support for mutual TLS (mTLS) in the OpenTelemetry Protocol (OTLP) Exporter, allowing secure gRPC connections over HTTPS and mTLS supports for HTTP Protocol.
Key Changes:
OTEL_EXPORTER_OTLP_CERTIFICATE
OTEL_EXPORTER_OTLP_CLIENT_KEY
OTEL_EXPORTER_OTLP_CLIENT_CERTIFICATE
Merge requirement checklist
CHANGELOG.md
files updated for non-trivial changes