Skip to content

Fix OkHttp client mTLS when using the platform default trust store#8565

Open
Debashismitra01 wants to merge 7 commits into
open-telemetry:mainfrom
Debashismitra01:fix/okhttp-client-mtls-default-trust
Open

Fix OkHttp client mTLS when using the platform default trust store#8565
Debashismitra01 wants to merge 7 commits into
open-telemetry:mainfrom
Debashismitra01:fix/okhttp-client-mtls-default-trust

Conversation

@Debashismitra01

Copy link
Copy Markdown

Summary

This PR fixes an inconsistency between the OkHttp and JDK OTLP HTTP senders when client mTLS is configured without custom trusted certificates.

Previously, when only setClientTls(...) (or the equivalent autoconfigure properties) was configured, TlsConfigHelper correctly created an SSLContext containing the client KeyManager, but the OkHttp sender skipped installing the custom SSLSocketFactory because no explicit X509TrustManager was provided. As a result, OkHttp fell back to the platform default SSLSocketFactory, causing the configured client certificate to be silently omitted during the TLS handshake.

This change resolves the platform default X509TrustManager when no custom trust manager is configured and uses it when installing the custom SSLSocketFactory, making the OkHttp sender behave consistently with the JDK sender.

Changes

  • Add a helper for resolving the platform default X509TrustManager.
  • Use the platform default trust manager when configuring the OkHttp HTTP sender if no custom trust manager is supplied.
  • Add a regression test covering client mTLS without custom trusted certificates against an mTLS-required endpoint.

Fixes #8562

@Debashismitra01 Debashismitra01 requested a review from a team as a code owner July 3, 2026 00:58
@linux-foundation-easycla

linux-foundation-easycla Bot commented Jul 3, 2026

Copy link
Copy Markdown

CLA Signed
The committers listed above are authorized under a signed CLA.

@Debashismitra01 Debashismitra01 force-pushed the fix/okhttp-client-mtls-default-trust branch from d6866b5 to 76a1e10 Compare July 3, 2026 01:33
# Conflicts:
#	exporters/otlp/testing-internal/src/main/java/io/opentelemetry/exporter/otlp/testing/internal/AbstractHttpTelemetryExporterTest.java
@Debashismitra01 Debashismitra01 force-pushed the fix/okhttp-client-mtls-default-trust branch from 76a1e10 to 1f4e437 Compare July 3, 2026 02:01
@codecov

codecov Bot commented Jul 3, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 85.71429% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 91.54%. Comparing base (4d974ba) to head (2befe38).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
...va/io/opentelemetry/exporter/internal/TlsUtil.java 62.50% 1 Missing and 2 partials ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##               main    #8565      +/-   ##
============================================
- Coverage     91.55%   91.54%   -0.01%     
- Complexity    10262    10263       +1     
============================================
  Files          1013     1013              
  Lines         27102    27123      +21     
  Branches       3182     3185       +3     
============================================
+ Hits          24812    24831      +19     
+ Misses         1565     1564       -1     
- Partials        725      728       +3     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@Debashismitra01

Copy link
Copy Markdown
Author

Hi! I ran into an issue with the patch coverage check. The remaining uncovered lines are primarily defensive exception paths introduced by the new default trust manager resolution (e.g. TlsUtil.defaultTrustManager() failure handling). These paths are difficult to exercise without introducing static mocking of JDK/Mockito internals.

I considered refactoring the implementation to reduce the uncovered branches, but that would either reintroduce the original bug or require resolving the default trust manager multiple times, which I'd prefer to avoid. Before I proceed further, could you advise on the preferred direction?

Should I add tests using static mocking for these exception paths?
Or would you prefer a different implementation that keeps the fix simpler while preserving the intended behavior?

I'd appreciate your guidance on which approach best fits the project's expectations.

@Debashismitra01 Debashismitra01 force-pushed the fix/okhttp-client-mtls-default-trust branch from 09b53c2 to b6f5f6b Compare July 6, 2026 02:21
@abdessattar23

Copy link
Copy Markdown
Contributor

Hey Mitra, thanks for taking care of this just couple things. The default trust manager gets resolved twice (once in getSslContext(), once in the sender), any way to do it once? And is the getSslContext() change needed? init(..., null, ...) already falls back to the platform default TMs.
Otherwise LGTM

Comment thread CHANGELOG.md Outdated
@Debashismitra01 Debashismitra01 force-pushed the fix/okhttp-client-mtls-default-trust branch 2 times, most recently from 103d4b8 to 1a24c95 Compare July 6, 2026 19:23
@opentelemetry-pr-dashboard

Copy link
Copy Markdown

This PR has review comments. Review suggestions, whether from maintainers or automated reviewers, aren't always correct or required. Please evaluate each comment on its merits, then make sure each thread has a clear outcome.

For example, link to the commit if you applied a suggestion, explain why it wasn't applied, or ask a follow-up question.

Automation flags a PR for human review once every review thread has a reply or is marked as resolved.

Status across open PRs is visible on the pull request dashboard.

Comment thread CHANGELOG.md Outdated
@Debashismitra01 Debashismitra01 force-pushed the fix/okhttp-client-mtls-default-trust branch from 1a24c95 to 2befe38 Compare July 6, 2026 20:46
@psx95

psx95 commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Should I add tests using static mocking for these exception paths? Or would you prefer a different implementation that keeps the fix simpler while preserving the intended behavior?

I could not find any instances where static mocking is used in the repo, so I think we want to avoid that even though the mocking section did not explicitly mention it.

I think you could follow a pattern similar to how keyManager is being tested using a method added explicitly to facilitate testing.

 // Visible for testing
  static X509TrustManager defaultTrustManager(TrustManagerFactory tmf) throws SSLException {
   ...
  }

You could then refactor your existing method:

 public static X509TrustManager defaultTrustManager() throws SSLException {
    try {
      TrustManagerFactory tmf =
          TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
      return defaultTrustManager(tmf); //  <-- the visible for testing method 
    } catch (NoSuchAlgorithmException e) {
      throw new SSLException("Could not build default TrustManager.", e);
    }
  }

Hopefully this should increase coverage by allowing you to pass mocked TrustManagerFactory objects so you can control getTrustManagers and init methods (I can't seem to see exactly which lines are uncovered).

@Debashismitra01

Copy link
Copy Markdown
Author

Should I add tests using static mocking for these exception paths? Or would you prefer a different implementation that keeps the fix simpler while preserving the intended behavior?

I could not find any instances where static mocking is used in the repo, so I think we want to avoid that even though the mocking section did not explicitly mention it.

I think you could follow a pattern similar to how keyManager is being tested using a method added explicitly to facilitate testing.

 // Visible for testing
  static X509TrustManager defaultTrustManager(TrustManagerFactory tmf) throws SSLException {
   ...
  }

You could then refactor your existing method:

 public static X509TrustManager defaultTrustManager() throws SSLException {
    try {
      TrustManagerFactory tmf =
          TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
      return defaultTrustManager(tmf); //  <-- the visible for testing method 
    } catch (NoSuchAlgorithmException e) {
      throw new SSLException("Could not build default TrustManager.", e);
    }
  }

Hopefully this should increase coverage by allowing you to pass mocked TrustManagerFactory objects so you can control getTrustManagers and init methods (I can't seem to see exactly which lines are uncovered).

Thanks for the suggestion! I was able to get the patch coverage above the required threshold without introducing static mocking. I kept the current approach and avoided adding extra testing hooks since coverage is now passing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

OTLP HTTP exporter (OkHttp sender) silently drops client mTLS certificate when no ca certificates are configured

3 participants