enhancement(tls): add configurable minimum and maximum TLS versions - #26265
enhancement(tls): add configurable minimum and maximum TLS versions#26265sainad2222 wants to merge 2 commits into
Conversation
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.
There was a problem hiding this comment.
💡 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: { |
There was a problem hiding this comment.
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: { |
There was a problem hiding this comment.
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 👍 / 👎.
| do not read Vector's `tls` block at all, such as `kafka` (librdkafka) and the AWS SDK-based sinks, | ||
| are unaffected. |
There was a problem hiding this comment.
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: { |
There was a problem hiding this comment.
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 👍 / 👎.
Summary
Adds
min_tls_versionandmax_tls_versionto Vector's sharedtlsconfiguration block, so userscan constrain which TLS protocol versions Vector negotiates. Accepted values are
TLSv1,TLSv1.1,TLSv1.2andTLSv1.3.Today the negotiated version is fixed by the library defaults and cannot be changed:
TlsSettings::acceptorbuilds fromSslAcceptor::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 versionin theopensslcrate.)tls_connector_builderusesSslConnector::builder, which sets nominimum 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 withthe 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, soenforcement lives in one place — a new
apply_protocol_versions— and every component that readsthe
tlsblock picks it up.The subtlety is that OpenSSL treats the
SSL_OP_NO_*options as a veto that outranksSSL_CTX_set_min_proto_version/set_max_proto_version. Because the acceptor profile hard-setsSSL_OP_NO_TLSv1_3, setting the bounds alone would leave TLS v1.3 excluded, andmin_tls_version: TLSv1.3would produce a context with no usable version at all. The option istherefore 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_versiongreater thanmax_tls_version) is rejected at config loadrather than producing an unusable context.
Components that cannot honor these options
Some components accept a
tlsblock but hand the PEM material to a third-party TLS stack insteadof 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:
mqttsource and sink (rumqttc)gcp_pubsubsource (tonic)greptimedb_metricssink — folded into its existing unsupported-options warning; that sinkdestructures
TlsConfigexhaustively, so it needed updating regardless.Components that never read Vector's
tlsblock (kafkavia 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_versiontoTLSv1.2— is a behavior change with release-timingimplications, 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
tlsblock:Omitting both options preserves today's behavior exactly. Setting
min_tls_version: TLSv1.2asabove refuses TLS v1.0/v1.1 handshakes and, as a side effect of the
SSL_OP_NO_*handlingdescribed 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 withcargo test -p vector-core --lib tls::(27 passed, 0 failed):TLSv1,TLSv1.1,TLSv1.2,TLSv1.3) andrenders back to it
protocol versions are unchanged from the library defaults
SslAcceptor::mozilla_intermediate— and asserting as a precondition that theprofile really does set
SSL_OP_NO_TLSv1_3— applying a window that includes TLS v1.3 clears itmin/maxof TLS v1.2 negotiates exactlyTLSv1.2with a default client, and a client configured withmin_tls_version: TLSv1.3failsthe 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:
Note that some distributions set a system-wide
MinProtocolinopenssl.cnf, which can refuseTLS v1.0/v1.1 before Vector is consulted; overriding that is necessary to observe the old
permissive default.
Is this a breaking change?
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?
no-changeloglabel to this PR.Changelog fragment:
changelog.d/11959_tls_min_max_version.enhancement.md. The generated Cuereference is regenerated in a separate commit (74 files, additions only).
Contributor Guidelines
@vectordotdev/vectorto reach out to us regarding this PR.pre-pushhook (template) or run the following locally before pushing:make fmtmake check-clippy(auto-fix withmake clippy-fix)make testgit merge origin masterandgit push.Cargo.lock), pleaserun
make build-licensesto regenerate the license inventory and commit the changes (if any). More details on the dd-rust-license-tool.