Skip to content

tls: openssl: Fail verification if client certificate is not provided#11254

Merged
edsiper merged 2 commits intofluent:masterfrom
edbingo:master
Mar 22, 2026
Merged

tls: openssl: Fail verification if client certificate is not provided#11254
edsiper merged 2 commits intofluent:masterfrom
edbingo:master

Conversation

@edbingo
Copy link
Copy Markdown
Contributor

@edbingo edbingo commented Dec 4, 2025

We are interested in using fluentbit with mTLS. However, currently fluentbit accepts connections from clients who do not provide a certificate at all. This change to the openssl library forces the connection to fail if certificates are not provided when fluent-bit is in server mode.

Tested with following configuration:

pipeline:
  inputs:
    - name: syslog
      mode: tcp
      parser: syslog-rfc5424
      listen: 0.0.0.0
      port: 6514
      tag: syslog.remote.log

      tls: on
      tls.verify: on
      tls.debug: 1
      tls.crt_file: ./certs/server.crt
      tls.key_file: ./certs/server.key
      tls.ca_file: ./certs/ca.crt

Output without client certificate

[2025/12/04 16:04:26.322586020] [debug] [tls] connection #102 SSL_accept: before SSL initialization
[2025/12/04 16:04:26.322606762] [debug] [tls] connection #102 WANT_READ
[2025/12/04 16:04:26.822746151] [debug] [tls] connection #102 SSL_accept: before SSL initialization
[2025/12/04 16:04:26.823028373] [debug] [tls] connection #102 SSL_accept: SSLv3/TLS read client hello
[2025/12/04 16:04:26.823332483] [debug] [tls] connection #102 SSL_accept: SSLv3/TLS write server hello
[2025/12/04 16:04:26.823417104] [debug] [tls] connection #102 SSL_accept: SSLv3/TLS write change cipher spec
[2025/12/04 16:04:26.823435740] [debug] [tls] connection #102 SSL_accept: TLSv1.3 write encrypted extensions
[2025/12/04 16:04:26.823458629] [debug] [tls] connection #102 SSL_accept: SSLv3/TLS write certificate request
[2025/12/04 16:04:26.823796114] [debug] [tls] connection #102 SSL_accept: SSLv3/TLS write certificate
[2025/12/04 16:04:26.825319389] [debug] [tls] connection #102 SSL_accept: TLSv1.3 write server certificate verify
[2025/12/04 16:04:26.825394738] [debug] [tls] connection #102 SSL_accept: SSLv3/TLS write finished
[2025/12/04 16:04:26.825401950] [debug] [tls] connection #102 SSL_accept: TLSv1.3 early data
[2025/12/04 16:04:26.825411284] [debug] [tls] connection #102 WANT_READ
[2025/12/04 16:04:27.325601341] [debug] [tls] connection #102 SSL_accept: TLSv1.3 early data
[2025/12/04 16:04:27.325751148] [debug] [tls] connection #102 SSL3 alert write:fatal:unknown
[2025/12/04 16:04:27.325765551] [error] [tls] connection #102 SSL_accept: error in error
[2025/12/04 16:04:27.325771902] [error] [tls] error: unexpected EOF
[2025/12/04 16:04:27.325826387] [debug] [downstream] connection #102 failed
[2025/12/04 16:04:27.325867515] [error] [input:syslog:syslog.4] could not accept new connection

Output with client certificate

[2025/12/04 16:05:52.618787245] [debug] [tls] connection #102 SSL_accept: before SSL initialization
[2025/12/04 16:05:52.618802905] [debug] [tls] connection #102 WANT_READ
[2025/12/04 16:05:53.118952005] [debug] [tls] connection #102 SSL_accept: before SSL initialization
[2025/12/04 16:05:53.119237744] [debug] [tls] connection #102 SSL_accept: SSLv3/TLS read client hello
[2025/12/04 16:05:53.119554356] [debug] [tls] connection #102 SSL_accept: SSLv3/TLS write server hello
[2025/12/04 16:05:53.119639994] [debug] [tls] connection #102 SSL_accept: SSLv3/TLS write change cipher spec
[2025/12/04 16:05:53.119658718] [debug] [tls] connection #102 SSL_accept: TLSv1.3 write encrypted extensions
[2025/12/04 16:05:53.119679475] [debug] [tls] connection #102 SSL_accept: SSLv3/TLS write certificate request
[2025/12/04 16:05:53.120048759] [debug] [tls] connection #102 SSL_accept: SSLv3/TLS write certificate
[2025/12/04 16:05:53.121813125] [debug] [tls] connection #102 SSL_accept: TLSv1.3 write server certificate verify
[2025/12/04 16:05:53.121901348] [debug] [tls] connection #102 SSL_accept: SSLv3/TLS write finished
[2025/12/04 16:05:53.121908052] [debug] [tls] connection #102 SSL_accept: TLSv1.3 early data
[2025/12/04 16:05:53.121920830] [debug] [tls] connection #102 WANT_READ
[2025/12/04 16:05:53.622055891] [debug] [tls] connection #102 SSL_accept: TLSv1.3 early data
[2025/12/04 16:05:53.622447387] [debug] [tls] connection #102 SSL_accept: TLSv1.3 read client compressed certificate
[2025/12/04 16:05:53.622526465] [debug] [tls] connection #102 SSL_accept: SSLv3/TLS read certificate verify
[2025/12/04 16:05:53.622563918] [debug] [tls] connection #102 SSL_accept: SSLv3/TLS read finished
[2025/12/04 16:05:53.622741077] [debug] [tls] connection #102 SSL_accept: SSLv3/TLS write session ticket
[2025/12/04 16:05:53.622810213] [debug] [tls] connection #102 SSL_accept: SSLv3/TLS write session ticket

Fluent Bit is licensed under Apache 2.0, by submitting this pull request I understand that this code will be released under the terms of that license.

Summary by CodeRabbit

  • New Features

    • Added a configuration option and public API to enable server-side TLS client certificate verification (disabled by default).
    • Inputs can opt into client-certificate verification per-instance via a new input property.
  • Bug Fixes

    • Server TLS now correctly enforces client certificate verification when enabled.
    • TLS initialization propagates the client-verification setting to the underlying TLS context.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Dec 4, 2025

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds server-side client-certificate verification control: new input-level config flag, TLS API to set verify_client, and OpenSSL backend wiring to apply SSL_VERIFY_FAIL_IF_NO_PEER_CERT when enabled. Changes span input parsing, TLS core API, and the OpenSSL backend.

Changes

Cohort / File(s) Summary
OpenSSL backend
src/tls/openssl.c
Add tls_context_set_verify_client(void *ctx_backend, int verify_client) and wire it into tls_openssl as .context_set_verify_client. Read existing verify mode, OR SSL_VERIFY_FAIL_IF_NO_PEER_CERT in server mode when requested, and apply via SSL_CTX_set_verify.
TLS core & API
include/fluent-bit/tls/flb_tls.h, src/tls/flb_tls.c
Add verify_client to struct flb_tls, new backend hook int (*context_set_verify_client)(void *, int), implement flb_tls_set_verify_client(struct flb_tls *tls, int verify_client), and add tls.verify_client_cert config entry. Forward setting to backend context when present.
Input config & usage
include/fluent-bit/flb_input.h, src/flb_input.c
Add tls_verify_client field to input instance, parse tls.verify_client_cert property, initialize default (false), and call flb_tls_set_verify_client during input TLS setup (abort on failure).

Sequence Diagram(s)

sequenceDiagram
    participant Input as Input (flb_input)
    participant TLS as flb_tls
    participant Backend as OpenSSL backend
    participant SSL as SSL_CTX (OpenSSL)

    Input->>TLS: flb_tls_set_verify_client(tls, verify_client)
    TLS->>TLS: tls->verify_client = verify_client
    TLS->>Backend: if (tls->ctx && api->context_set_verify_client) call context_set_verify_client(ctx, verify_client)
    Backend->>SSL: read current verify mode
    Backend->>SSL: compute verify_flags (SSL_VERIFY_PEER [+ SSL_VERIFY_FAIL_IF_NO_PEER_CERT])
    Backend->>SSL: SSL_CTX_set_verify(ctx, verify_flags, verify_cb)
    SSL-->>Backend: return status
    Backend-->>TLS: return status
    TLS-->>Input: return status
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested reviewers

  • edsiper
  • cosmo0920

Poem

🐰 I hopped through headers, nudged a verify bit bright,
Servers now ask kindly for certs in the night.
I twitched my nose, set flags with care and cheer,
Handshakes snug and certain, no doubts creeping near.
🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 22.22% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: adding OpenSSL server-side behavior to fail verification when client certificates are not provided. It directly aligns with the core objective of enforcing client certificate requirements.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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

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

@chatgpt-codex-connector
Copy link
Copy Markdown

💡 Codex Review

int verify_flags = SSL_VERIFY_PEER;
if (mode == FLB_TLS_SERVER_MODE) {
verify_flags |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
}
SSL_CTX_set_verify(ssl_ctx, verify_flags, NULL);

P1 Badge mTLS now forced for TLS inputs using default verify

When verify is enabled you now OR SSL_VERIFY_FAIL_IF_NO_PEER_CERT into the server verify flags, which causes the handshake to abort if a client does not present a certificate. All input plugins default tls.verify to on (see src/flb_input.c:400 and the call at line 1308), so simply enabling TLS on an input without explicitly turning verification off will now require client certificates and reject ordinary TLS clients that do not send one—a behavior change from the previous optional client-auth flow.

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

@bachp
Copy link
Copy Markdown

bachp commented Dec 13, 2025

💡 Codex Review

int verify_flags = SSL_VERIFY_PEER;
if (mode == FLB_TLS_SERVER_MODE) {
verify_flags |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
}
SSL_CTX_set_verify(ssl_ctx, verify_flags, NULL);

P1 Badge mTLS now forced for TLS inputs using default verify

When verify is enabled you now OR SSL_VERIFY_FAIL_IF_NO_PEER_CERT into the server verify flags, which causes the handshake to abort if a client does not present a certificate. All input plugins default tls.verify to on (see src/flb_input.c:400 and the call at line 1308), so simply enabling TLS on an input without explicitly turning verification off will now require client certificates and reject ordinary TLS clients that do not send one—a behavior change from the previous optional client-auth flow.
ℹ️ About Codex in GitHub

This is a breaking change as it would break already running deployments.

On the other hand one could also argue that this is a security issue as people might expect the client certificate to be checked but it is not, which might be surprising.

Should we consider a new option for this? e.g. something like verify = enforced?

@cosmo0920
Copy link
Copy Markdown
Contributor

💡 Codex Review

int verify_flags = SSL_VERIFY_PEER;
if (mode == FLB_TLS_SERVER_MODE) {
verify_flags |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
}
SSL_CTX_set_verify(ssl_ctx, verify_flags, NULL);

P1 Badge mTLS now forced for TLS inputs using default verify
When verify is enabled you now OR SSL_VERIFY_FAIL_IF_NO_PEER_CERT into the server verify flags, which causes the handshake to abort if a client does not present a certificate. All input plugins default tls.verify to on (see src/flb_input.c:400 and the call at line 1308), so simply enabling TLS on an input without explicitly turning verification off will now require client certificates and reject ordinary TLS clients that do not send one—a behavior change from the previous optional client-auth flow.
ℹ️ About Codex in GitHub

This is a breaking change as it would break already running deployments.

On the other hand one could also argue that this is a security issue as people might expect the client certificate to be checked but it is not, which might be surprising.

Should we consider a new option for this? e.g. something like verify = enforced?

Yes. We need to keep backward compatibility. So, introducing new option would be great.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (4)
src/tls/flb_tls.c (2)

38-42: Clarify help text for tls.verify_client_cert

Consider making the description more explicit, e.g., that it requires a client certificate when Fluent Bit acts as a TLS server and complements tls.verify, to avoid confusing it with server‑certificate validation on client connections.


293-306: flb_tls_set_verify_client cleanly extends the TLS API

The function’s null‑check, internal state update, and conditional delegation to context_set_verify_client match existing patterns (e.g., ALPN / hostname setters) and correctly propagate backend errors. You might optionally normalize the flag with tls->verify_client = !!verify_client; for stricter boolean semantics, but it’s not required given the current callers.

src/flb_input.c (2)

655-658: tls.verify_client_cert property wiring is consistent with other TLS flags

Hooking tls.verify_client_cert through flb_utils_bool() into ins->tls_verify_client matches how tls.verify and tls.verify_hostname are handled, so parsing and behavior are consistent. Note that invalid boolean strings result in a value different from FLB_TRUE, effectively leaving the feature disabled—same as the existing TLS options.


1340-1348: Client‑cert verification hookup is correct; consider scoping it to server mode

The call to flb_tls_set_verify_client() when ins->tls_verify_client == FLB_TRUE cleanly propagates the setting to the TLS backend and aborts initialization on failure, which is what you want.

You might optionally:

  • Apply this only when tls_session_mode == FLB_TLS_SERVER_MODE, or
  • Log a warning when tls.verify_client_cert is set on a client‑mode input,

to make it clearer to users that the option is intended for server‑side client‑certificate enforcement.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4cf27c6 and d86276d.

📒 Files selected for processing (5)
  • include/fluent-bit/flb_input.h (1 hunks)
  • include/fluent-bit/tls/flb_tls.h (3 hunks)
  • src/flb_input.c (3 hunks)
  • src/tls/flb_tls.c (2 hunks)
  • src/tls/openssl.c (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/tls/openssl.c
🧰 Additional context used
🧬 Code graph analysis (2)
include/fluent-bit/tls/flb_tls.h (1)
src/tls/flb_tls.c (1)
  • flb_tls_set_verify_client (293-306)
src/flb_input.c (4)
src/flb_config.c (1)
  • prop_key_check (679-688)
src/flb_output.c (1)
  • prop_key_check (830-840)
src/flb_processor.c (1)
  • prop_key_check (1347-1358)
src/flb_utils.c (1)
  • flb_utils_bool (757-771)
🔇 Additional comments (3)
include/fluent-bit/flb_input.h (1)

446-452: New tls_verify_client field is consistent and backward‑compatible

The new flag sits alongside existing TLS options and is defaulted to false in flb_input_new, so it does not alter behavior unless explicitly configured.

include/fluent-bit/tls/flb_tls.h (1)

79-80: TLS backend hook and state for client verification are well‑integrated

Adding context_set_verify_client, the verify_client field, and the public flb_tls_set_verify_client() prototype follows the existing TLS API patterns and keeps older backends safe by allowing the hook to remain NULL.

Also applies to: 107-109, 135-137

src/flb_input.c (1)

397-409: Explicitly defaulting tls_verify_client to FLB_FALSE preserves behavior

Initializing tls_verify_client alongside the other TLS fields keeps existing configs unchanged and makes the default (“don’t require client certs”) explicit in the constructor.

@edbingo
Copy link
Copy Markdown
Contributor Author

edbingo commented Dec 15, 2025

added a new configuration option that can be activated as follows:

      tls: on
      tls.debug: 1
      tls.verify: on
      tls.verify_client_cert: on
      tls.crt_file: ./certs/server.crt
      tls.key_file: ./certs/server.key
      tls.ca_file: ./certs/ca.crt

tls.verify tls.verify_client_cert result
on on client certificate is strictly validated
on off client certificate is optionally validated
off on no validation takes place
off off no validation takes place

cosmo0920
cosmo0920 previously approved these changes Dec 16, 2025
Copy link
Copy Markdown
Contributor

@cosmo0920 cosmo0920 left a comment

Choose a reason for hiding this comment

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

Yes, this patch is really what we want.

@edsiper
Copy link
Copy Markdown
Member

edsiper commented Feb 4, 2026

@edbingo @cosmo0920 let's work on cleanup the git commit history to get this merged

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@src/flb_input.c`:
- Around line 655-658: The code assigns ins->tls_verify_client =
flb_utils_bool(tmp) without validating the return; instead, call
flb_utils_bool(tmp) into an int (e.g., int val = flb_utils_bool(tmp)),
flb_sds_destroy(tmp), check if val == -1 and if so emit an error (like
flb_plg_error or flb_error) indicating an invalid tls.verify_client_cert value
and return an error from the config parsing function; otherwise set
ins->tls_verify_client = val. Mirror the same validation pattern used for the
threaded property (the check around line 721) and reference prop_key_check,
flb_utils_bool, flb_sds_destroy, and ins->tls_verify_client when making the
change.
🧹 Nitpick comments (1)
src/flb_input.c (1)

1339-1348: Guard tls_verify_client setup on the tls_verify flag in server mode, or document the OpenSSL dependency.

This block runs whenever tls.verify_client_cert=on, regardless of tls.verify. The OpenSSL backend does:

mode = SSL_CTX_get_verify_mode(ctx->ctx);
mode |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
SSL_CTX_set_verify(ctx->ctx, mode, NULL);

When tls.verify=off (sets initial mode to SSL_VERIFY_NONE), the SSL_VERIFY_FAIL_IF_NO_PEER_CERT flag has no effect in OpenSSL without SSL_VERIFY_PEER. The code works correctly but relies on this implicit OpenSSL behavior. Either add a guard (&& ins->tls_verify == FLB_TRUE) or explicitly document that this only applies when verify is enabled.

Comment thread src/flb_input.c
@edbingo edbingo force-pushed the master branch 7 times, most recently from 83bf8f0 to 4fcb274 Compare February 11, 2026 11:21
edbingo and others added 2 commits February 11, 2026 12:27
Signed-off-by: Edward Lancaster <edward.lancaster@siemens.com>
Signed-off-by: Edward Lancaster <edward.lancaster@siemens.com>
@edbingo
Copy link
Copy Markdown
Contributor Author

edbingo commented Feb 11, 2026

@edsiper @cosmo0920 should be ready now 👍

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants