Skip to content

Fix "Empty input" when loading SSL_CERT_FILE/SSL_CERT_DIR cert bundles. - #1706

Merged
balamurugana merged 4 commits into
minio:masterfrom
PavolMisudikAta:fix-setting-certificates-entries
Sep 16, 2026
Merged

balamurugana merged 4 commits into
minio:masterfrom
PavolMisudikAta:fix-setting-certificates-entries

Conversation

@PavolMisudikAta

@PavolMisudikAta PavolMisudikAta commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Problem description

Constructing a MinioClient fails at startup when SSL_CERT_FILE (or
SSL_CERT_DIR) points at a normal CA bundle:

io.minio.errors.MinioException: java.security.cert.CertificateException:
Could not parse certificate: java.io.IOException: Empty input
    at io.minio.Http.setCertificateEntry(Http.java:455)
    at io.minio.Http.getTrustManagerFromFile(Http.java:467)
    at io.minio.Http.getCompositeTrustManager(Http.java:529)
    at io.minio.Http.enableExternalCertificates(Http.java:620)
    at io.minio.Http.enableExternalCertificatesFromEnv(Http.java:641)
    at io.minio.Http.newDefaultClient(Http.java:828)

This reproduces on 9.0.0 and 9.0.1. It bites any base image that exports
SSL_CERT_FILE/SSL_CERT_DIR (Chainguard/Wolfi, Alpine, Debian, …), which is
increasingly common.

Root cause

Http#setCertificateEntry reads the bundle with:

while (in.available() > 0) {
  X509Certificate cert = (X509Certificate) cf.generateCertificate(in);
  ks.setCertificateEntry(namePrefix + (index++), cert);
}

After the final -----END CERTIFICATE-----, any trailing bytes (a trailing
blank line, or trailing whitespace — both present in real CA bundles) leave
available() > 0, so the loop calls generateCertificate once more on the
remaining whitespace and X509Factory throws IOException: Empty input.
InputStream.available() is also not a reliable end-of-stream test, so the
exact failure is environment-dependent.

Fix

Read the whole bundle with CertificateFactory#generateCertificates(InputStream),
which parses all certificates in one pass, stops at EOF, and ignores trailing
whitespace. One-line change in setCertificateEntry; covers both the
SSL_CERT_FILE and SSL_CERT_DIR paths (both call it).

Summary by CodeRabbit

  • Bug Fixes

    • Improved external TLS certificate loading for PEM bundles, including files with or without trailing newlines and whitespace.
  • Tests

    • Added coverage for loading external certificate bundles across multiple PEM end-of-file formats.

@coderabbitai

coderabbitai Bot commented Jun 9, 2026

Copy link
Copy Markdown

Review Change StackReview Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Advanced

Run ID: 659002b9-84e5-4d36-8dbe-e51fbb202f2e

📥 Commits

Reviewing files that changed from the base of the PR and between af73a85 and 4577e79.

📒 Files selected for processing (1)
  • api/src/main/java/io/minio/Http.java

Included review availability: Your plan provides up to 4 included reviews per hour; 3 remain after this review.


📝 Walkthrough

Walkthrough

This PR updates certificate bundle loading to parse all certificates in one pass. It adds parameterized tests for PEM files with four trailing newline and whitespace variants.

Changes

Certificate Bundle Loading

Layer / File(s) Summary
Certificate loading implementation
api/src/main/java/io/minio/Http.java
Adds the Certificate import and updates setCertificateEntry() to iterate over CertificateFactory.generateCertificates(in) when populating the KeyStore.
Certificate bundle test coverage
api/src/test/java/io/minio/HttpExternalCertificatesTest.java
Adds parameterized tests for four PEM bundle variants. Each variant is written to a temporary file and passed to Http.enableExternalCertificates(...).

Priority: ➖ Normal

Estimated code review effort: 2 (Simple) | ~10 minutes

Change: Bug fix

Suggested reviewers: balamurugana

Merge Risk: ⚪ Minimal · up to 4577e

The stated change addresses whole-stream certificate parsing and adds trailing-whitespace coverage; no merge-blocking failure is established.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 4 functions across 2 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: fixing empty-input errors when loading SSL certificate bundles from SSL_CERT_FILE or SSL_CERT_DIR.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
  • Fix all pre-merge checks with AI

Warning

Git: CodeRabbit could not clone the repository, so clone-backed analysis was skipped and this review may be incomplete. Verify repository clone access, such as SSH credentials, before requesting another full review. If clone access is intentionally unavailable, use path_filters to narrow the review scope.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

A rabbit loads each certificate bright
Through newline, blank line, trailing white
One bundle call reads them all
Tests check each ending small
The KeyStore holds them right

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
api/src/test/java/io/minio/HttpExternalCertificatesTest.java (2)

55-63: ⚡ Quick win

Add a SSL_CERT_DIR-path test for parity with the production fix scope.

Current coverage exercises only the file-path flow. Add a case that writes the PEM into a temp directory and calls Http.enableExternalCertificates(baseClient, null, dirPath) to validate the directory code path too.

Also applies to: 73-77

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@api/src/test/java/io/minio/HttpExternalCertificatesTest.java` around lines 55
- 63, Update HttpExternalCertificatesTest.bundles() to include a new
parameterized case that exercises the SSL_CERT_DIR directory path: create a
temporary directory, write the PEM content into a file inside it, and add a test
case name like "ssl_cert_dir" that will be used by the test harness; then in the
corresponding test execution branch call
Http.enableExternalCertificates(baseClient, null, dirPath) (instead of the file
path) so the directory code path in Http.enableExternalCertificates is
validated; ensure you add the same directory-based case for the other
parameterized list at lines 73-77 as well.

73-77: ⚡ Quick win

Strengthen the assertion to prove certificates were actually applied.

Assert.assertNotNull(client) can pass even when no external certs are loaded (the method may return the original client). Assert that a new client instance is returned.

Suggested test tightening
   `@Test`
   public void loadsExternalCertificateBundle() throws Exception {
     String path = writeBundle(bundle);
-    OkHttpClient client = Http.enableExternalCertificates(new OkHttpClient(), path, null);
+    OkHttpClient baseClient = new OkHttpClient();
+    OkHttpClient client = Http.enableExternalCertificates(baseClient, path, null);
     Assert.assertNotNull(client);
+    Assert.assertNotSame(baseClient, client);
   }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@api/src/test/java/io/minio/HttpExternalCertificatesTest.java` around lines 73
- 77, The test currently only asserts the returned client is non-null; instead
create a named original OkHttpClient (e.g., OkHttpClient original = new
OkHttpClient()), call Http.enableExternalCertificates(original, path, null) into
client, then assert the method returned a new instance
(Assert.assertNotSame(original, client)) and additionally assert that the
returned client's SSL configuration changed (for example verify
client.sslSocketFactory() differs from original.sslSocketFactory() or that the
client's SSL/TrustManager references the loaded certificate) to prove the bundle
was actually applied. Use the existing test method
loadsExternalCertificateBundle and the Http.enableExternalCertificates call to
locate where to update assertions.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@api/src/test/java/io/minio/HttpExternalCertificatesTest.java`:
- Around line 55-63: Update HttpExternalCertificatesTest.bundles() to include a
new parameterized case that exercises the SSL_CERT_DIR directory path: create a
temporary directory, write the PEM content into a file inside it, and add a test
case name like "ssl_cert_dir" that will be used by the test harness; then in the
corresponding test execution branch call
Http.enableExternalCertificates(baseClient, null, dirPath) (instead of the file
path) so the directory code path in Http.enableExternalCertificates is
validated; ensure you add the same directory-based case for the other
parameterized list at lines 73-77 as well.
- Around line 73-77: The test currently only asserts the returned client is
non-null; instead create a named original OkHttpClient (e.g., OkHttpClient
original = new OkHttpClient()), call Http.enableExternalCertificates(original,
path, null) into client, then assert the method returned a new instance
(Assert.assertNotSame(original, client)) and additionally assert that the
returned client's SSL configuration changed (for example verify
client.sslSocketFactory() differs from original.sslSocketFactory() or that the
client's SSL/TrustManager references the loaded certificate) to prove the bundle
was actually applied. Use the existing test method
loadsExternalCertificateBundle and the Http.enableExternalCertificates call to
locate where to update assertions.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 39cf8c2f-fb3a-46ab-b275-692568504bfc

📥 Commits

Reviewing files that changed from the base of the PR and between 5a41af5 and d3f1fc6.

📒 Files selected for processing (2)
  • api/src/main/java/io/minio/Http.java
  • api/src/test/java/io/minio/HttpExternalCertificatesTest.java

@balamurugana balamurugana left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are those env var set to empty?

@PavolMisudikAta

PavolMisudikAta commented Jun 9, 2026

Copy link
Copy Markdown
Contributor Author

Hi @balamurugana , sorry, not sure, what you mean :/

I encountered the issue, when using the chainguard image, which exports sets
SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt

The crt file was not empty, but the MinIO still failed to start ( exception in the description ).
After the investigation, the problem seems to be the incorrect parsing of the file.

balamurugana
balamurugana previously approved these changes Jun 12, 2026
@PavolMisudikAta

Copy link
Copy Markdown
Contributor Author

Hi @balamurugana, may I ask for a review again? 🙏

@PavolMisudikAta

Copy link
Copy Markdown
Contributor Author

Hi @balamurugana , can you please rerun the pipeline again? I want to rule-out the possibility of the functional test flakiness.

@balamurugana

balamurugana commented Sep 16, 2026

Copy link
Copy Markdown
Member

@PavolMisudikAta Please rebase this PR

@balamurugana
balamurugana merged commit d23b8b2 into minio:master Sep 16, 2026
13 checks passed
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.

2 participants