Skip to content

enhancement(tls): add configurable minimum and maximum TLS versions - #26265

Open
sainad2222 wants to merge 2 commits into
vectordotdev:masterfrom
sainad2222:tls-min-max-version
Open

enhancement(tls): add configurable minimum and maximum TLS versions#26265
sainad2222 wants to merge 2 commits into
vectordotdev:masterfrom
sainad2222:tls-min-max-version

Conversation

@sainad2222

Copy link
Copy Markdown
Contributor

Summary

Adds min_tls_version and max_tls_version to Vector's shared tls configuration block, so users
can constrain which TLS protocol versions Vector negotiates. Accepted values are TLSv1,
TLSv1.1, TLSv1.2 and TLSv1.3.

Today the negotiated version is fixed by the library defaults and cannot be changed:

  • Accepting connections. TlsSettings::acceptor builds from SslAcceptor::mozilla_intermediate,
    which is Mozilla's v4 intermediate profile. It permits TLS v1.0 and v1.1, and explicitly sets
    SSL_OP_NO_TLSv1_3, so TLS v1.3 is unavailable. (That helper is marked
    // FIXME remove in next major version in the openssl crate.)
  • Initiating connections. tls_connector_builder uses SslConnector::builder, which sets no
    minimum protocol version at all.

This is the problem reported in #11959: compliance scanners flag Vector's listening sources for
accepting TLS v1.0/v1.1, and there is no way to turn them off.

PR #17191 previously proposed swapping the acceptor for mozilla_intermediate_v5. It was closed with
the direction to make the behavior configurable and go through a deprecation process rather than
change it outright, which is the approach taken here: both options are unset by default, so
existing configurations negotiate exactly the same versions as before.

Implementation notes

Both the acceptor and the connector funnel through TlsSettings::apply_context_base, so
enforcement lives in one place — a new apply_protocol_versions — and every component that reads
the tls block picks it up.

The subtlety is that OpenSSL treats the SSL_OP_NO_* options as a veto that outranks
SSL_CTX_set_min_proto_version/set_max_proto_version. Because the acceptor profile hard-sets
SSL_OP_NO_TLSv1_3, setting the bounds alone would leave TLS v1.3 excluded, and
min_tls_version: TLSv1.3 would produce a context with no usable version at all. The option is
therefore cleared for every version inside the requested window before the bounds are applied. A
unit test pins this against the profile so it cannot regress silently.

An inverted range (min_tls_version greater than max_tls_version) is rejected at config load
rather than producing an unusable context.

Components that cannot honor these options

Some components accept a tls block but hand the PEM material to a third-party TLS stack instead
of applying it to an OpenSSL context, so these settings cannot take effect there. Rather than
ignore a security setting silently, they now warn when either option is set:

  • mqtt source and sink (rumqttc)
  • gcp_pubsub source (tonic)
  • greptimedb_metrics sink — folded into its existing unsupported-options warning; that sink
    destructures TlsConfig exhaustively, so it needed updating regardless.

Components that never read Vector's tls block (kafka via librdkafka, the AWS SDK-based sinks)
are unaffected and unchanged.

Deliberately not included

The deprecation half of the #17191 review — warning when a connection negotiates below TLS v1.2,
and eventually defaulting min_tls_version to TLSv1.2 — is a behavior change with release-timing
implications, so I left it out of this PR. Happy to add it here or as a follow-up, whichever the
maintainers prefer.

References

Closes: #11959
Related: #17191

Vector configuration

The new options on any component that exposes a tls block:

sources:
  demo:
    type: http_server
    address: 0.0.0.0:8080
    decoding:
      codec: json
    tls:
      enabled: true
      crt_file: /path/to/localhost.cert.pem
      key_file: /path/to/localhost.key.pem
      min_tls_version: "TLSv1.2"

sinks:
  out:
    type: blackhole
    inputs: [demo]

Omitting both options preserves today's behavior exactly. Setting min_tls_version: TLSv1.2 as
above refuses TLS v1.0/v1.1 handshakes and, as a side effect of the SSL_OP_NO_* handling
described above, makes TLS v1.3 available on the listener.

How did you test this PR?

Six tests added in lib/vector-core/src/tls/settings.rs, run with
cargo test -p vector-core --lib tls:: (27 passed, 0 failed):

  • an inverted version range is rejected, and equal bounds (pinning a single version) are accepted
  • each version deserializes from its config name (TLSv1, TLSv1.1, TLSv1.2, TLSv1.3) and
    renders back to it
  • no-regression guard: with neither option set, the context's options bitfield and min/max
    protocol versions are unchanged from the library defaults
  • with bounds set, they land on the context
  • starting from SslAcceptor::mozilla_intermediate — and asserting as a precondition that the
    profile really does set SSL_OP_NO_TLSv1_3 — applying a window that includes TLS v1.3 clears it
  • end-to-end over a real socket: a server pinned to min/max of TLS v1.2 negotiates exactly
    TLSv1.2 with a default client, and a client configured with min_tls_version: TLSv1.3 fails
    the handshake with a protocol version alert

Also run locally: make fmt, cargo clippy -p vector-core --all-targets (clean),
vdev check generated-docs, vdev check changelog-fragments, vdev check fmt,
vdev check markdown — all passing.

The behavior is reproducible by hand against the config above with:

$ openssl s_client -connect localhost:8080 -tls1_1   # refused
$ openssl s_client -connect localhost:8080 -tls1_2   # accepted
$ openssl s_client -connect localhost:8080 -tls1_3   # accepted

Note that some distributions set a system-wide MinProtocol in openssl.cnf, which can refuse
TLS v1.0/v1.1 before Vector is consulted; overriding that is necessary to observe the old
permissive default.

Is this a breaking change?

  • Yes
  • No

Both options default to unset, which preserves the currently negotiated versions exactly. This is
covered by the no-regression test listed above.

Does this PR include user facing changes?

  • Yes. Please add a changelog fragment based on our guidelines.
  • No. A maintainer will apply the no-changelog label to this PR.

Changelog fragment: changelog.d/11959_tls_min_max_version.enhancement.md. The generated Cue
reference is regenerated in a separate commit (74 files, additions only).

Contributor Guidelines

  • Please read our Vector contributor resources.
  • Do not hesitate to use @vectordotdev/vector to reach out to us regarding this PR.
  • Some CI checks run only after we manually approve them. To catch issues early, add a pre-push hook (template) or run the following locally before pushing:
    • make fmt
    • make check-clippy (auto-fix with make clippy-fix)
    • make test
  • After a review is requested, please avoid force pushes to help us review incrementally.
    • Feel free to push as many commits as you want. They will be squashed into one before merging.
    • For example, you can run git merge origin master and git push.
  • If this PR introduces changes Vector dependencies (modifies Cargo.lock), please
    run make build-licenses to regenerate the license inventory and commit the changes (if any). More details on the dd-rust-license-tool.

Vector's negotiated TLS versions were fixed by the library defaults and could
not be changed. `TlsSettings::acceptor` builds from
`SslAcceptor::mozilla_intermediate` -- Mozilla's v4 intermediate profile --
which permits TLS v1.0 and v1.1 and explicitly sets `SSL_OP_NO_TLSv1_3`, and
`tls_connector_builder` sets no minimum protocol version at all. This is the
compliance problem reported in vectordotdev#11959.

Add `min_tls_version` and `max_tls_version` to `TlsConfig`, accepting `TLSv1`,
`TLSv1.1`, `TLSv1.2` and `TLSv1.3`. Both are unset by default, so existing
configurations negotiate exactly the same versions as before. vectordotdev#17191 attempted
to fix this by moving the acceptor to `mozilla_intermediate_v5` outright and
was closed with the direction to make the behavior configurable instead.

Enforcement lives in `apply_context_base`, which both the acceptor and the
connector already funnel through, so every component that reads the `tls` block
picks it up. OpenSSL treats the `SSL_OP_NO_*` options as a veto outranking
`set_min_proto_version`/`set_max_proto_version`, so the option is cleared for
each version inside the requested window before the bounds are applied;
otherwise the acceptor's `SSL_OP_NO_TLSv1_3` would keep TLS v1.3 excluded and
`min_tls_version: TLSv1.3` would yield a context with no usable version.

Components that pass the certificates to a third-party TLS stack cannot honor
these options, so they warn rather than ignore them silently: the `mqtt` source
and sink, the `gcp_pubsub` source, and the `greptimedb_metrics` sink.
Regenerated by `make generate-component-docs` after adding `min_tls_version`
and `max_tls_version` to `TlsConfig`. Every component that exposes a `tls`
block gains the two options in its generated Cue reference.
@sainad2222
sainad2222 requested review from a team as code owners August 30, 2026 14:32
@github-actions github-actions Bot added docs review on hold The documentation team reviews PRs only after a PR is approved by the COSE team. domain: sources Anything related to the Vector's sources domain: sinks Anything related to the Vector's sinks domain: external docs Anything related to Vector's external, public documentation domain: core Anything related to core crates i.e. vector-core, core-common, etc labels Aug 30, 2026

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4172f3bada

ℹ️ 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".

"TLSv1.3": "TLS v1.3."
}
}
min_tls_version: {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Enforce the advertised Kafka TLS bounds

When a Kafka source or sink sets tls.min_tls_version or tls.max_tls_version, the generated configuration now accepts the option and promises that it constrains negotiation, but KafkaAuthConfig::apply in src/kafka.rs only forwards the existing verification, certificate, key, and password fields to librdkafka and never reads either new bound. Vector therefore starts without a warning while Kafka silently uses librdkafka's default protocol range, defeating an explicitly configured security control.

Useful? React with 👍 / 👎.

"TLSv1.3": "TLS v1.3."
}
}
min_tls_version: {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Enforce the advertised NATS TLS bounds

When either NATS component configures this option, the shared from_tls_auth_config helper in src/nats.rs only applies require_tls, CA certificates, and client certificates to async_nats::ConnectOptions; it never consumes or warns about either protocol bound. Both the NATS source and sink consequently accept the newly documented security setting but negotiate using async-nats defaults instead.

Useful? React with 👍 / 👎.

Comment on lines +20 to +21
do not read Vector's `tls` block at all, such as `kafka` (librdkafka) and the AWS SDK-based sinks,
are unaffected.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Correct the AWS TLS support note

For AWS SDK-based sinks, src/aws/mod.rs::connector passes the configured TlsConfig through MaybeTlsSettings::tls_client and then into build_tls_connector or build_proxy_connector, both of which build the OpenSSL context where the new bounds are applied. The release note therefore incorrectly tells AWS users that these sinks do not read the block and are unaffected, despite the generated AWS component references advertising and the implementation enforcing the new settings.

Useful? React with 👍 / 👎.

"TLSv1.3": "TLS v1.3."
}
}
min_tls_version: {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Enforce the advertised AMQP TLS bounds

When an AMQP source or sink sets either new bound, the shared AmqpConfig::connect path in src/amqp.rs constructs lapin's OwnedTLSConfig using only cert_chain and identity; it never reads or warns about min_tls_version or max_tls_version. Both AMQP components therefore accept and document the new security settings but silently negotiate with lapin's default protocol range.

Useful? React with 👍 / 👎.

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

Labels

docs review on hold The documentation team reviews PRs only after a PR is approved by the COSE team. domain: core Anything related to core crates i.e. vector-core, core-common, etc domain: external docs Anything related to Vector's external, public documentation domain: sinks Anything related to the Vector's sinks domain: sources Anything related to the Vector's sources

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Set min. TLS version

1 participant