feat(kafka source, kafka sink): support AWS MSK IAM auth via SASL/OAUTHBEARER#25751
feat(kafka source, kafka sink): support AWS MSK IAM auth via SASL/OAUTHBEARER#25751gecube wants to merge 10 commits into
Conversation
|
All contributors have signed the CLA ✍️ ✅ |
This comment has been minimized.
This comment has been minimized.
|
I have read the CLA Document and I hereby sign the CLA |
|
recheck |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 5356cb98b6
ℹ️ 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".
|
Thanks for the review — good catches. Addressed in feb7e1f: P1 (source OAuth refresh not delegated) — fixed. Correct: the consumer's rdkafka context is P2a ( P2b (refresh callback enabled globally when Also added a full auth-matrix test for Still Draft pending Cue component-doc regeneration and an end-to-end handshake against a live MSK IAM listener. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: feb7e1f35e
ℹ️ 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".
|
Round 2 addressed in df39835 — all four are real, thanks. P1 (nested-runtime panic in the source OAuth callback) — fixed. You're right: for the source the refresh callback is served while the P1 (healthcheck never polls) — fixed. Switched the sink healthcheck from P2 (require TLS) — fixed. P2 (integration-test literal) — fixed. Added Verified: 29/29 kafka unit tests pass with |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: df39835faa
ℹ️ 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".
|
Addressed in d585c20, with a caveat on the "scope to an MSK-specific context" suggestion. The constraint: What I did instead: fail fast. In Worth noting this isn't removing a working path: pre-PR the const was If you'd rather preserve librdkafka-native OAUTHBEARER (OIDC / unsecured JWT via 30/30 kafka unit tests green ( |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d585c206d4
ℹ️ 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".
|
All three addressed in 6c1d8c3 — good catches, especially the load-timeout one. Honor MSK IAM credential load timeouts — fixed. Added Gate the OAUTHBEARER rejection on Reject librdkafka_options that override MSK IAM settings — fixed. Both the source and sink apply 34/34 kafka unit tests green ( |
6c1d8c3 to
07ce70e
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 07ce70ed72
ℹ️ 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".
|
All three addressed in 3896391 — thanks, the credential-expiry and partition ones are genuinely important for correctness. Clamp token lifetime to credential expiry — fixed. Partition-aware signing host — fixed. Added Reject Also regenerated the Cue component docs for the new 37/37 kafka unit tests green ( |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 3896391de1
ℹ️ 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".
Verification: MSK IAM SASL/OAUTHBEARER, proven locally end-to-end (no live MSK needed)The one thing I couldn't prove offline was that rust-rdkafka 0.39 actually delivers the OAUTHBEARER token-refresh event to 1. The token is byte-identical to the official AWS signer (offline, deterministic)Unit tests sign a fixed 2. The refresh callback actually fires (rust-rdkafka 0.39, no broker, no AWS)A Confirms the event reaches the callback despite no 3. Full SASL/OAUTHBEARER handshake + message round-trip against a real broker (local Apache Kafka)Local Broker side confirms authentication (no failures): Here the callback returns a token shaped for the local unsecured broker (a real MSK broker validates the SigV4 token from step 1 instead) — the point is the callback → librdkafka → wire → broker → auth-success → produce/consume path is exercised for real. Conclusion
The only step not literally executed is a real MSK broker accepting the SigV4 token; that's on the maintainers'/my AWS account and is the last box before taking this out of Draft. Reproducers (docker-compose + the two rdkafka tests) are ready — happy to contribute them as opt-in integration tests if you'd like them in-tree. |
|
Fixed in 3200177 — good catch,
Extended |
Review statusAll Codex review threads to date are addressed and resolved (latest at
Test coverage: 37 kafka unit tests (auth matrix mapped to the MSK port matrix, known-answer SigV4 signatures vs an independent oracle, lifetime clamp, timeouts, override rejection). Builds with End-to-end verification (callback fires + full SASL/OAUTHBEARER handshake + round-trip against a live broker, plus byte-for-byte SigV4 vs the AWS signer) is documented above. The only remaining item before taking this out of Draft is a handshake against a real MSK IAM listener on our AWS account. @codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 32001774bf
ℹ️ 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".
|
All three addressed in 9899c91. Strip endpoint paths before signing. Validate config before loading credentials. Both Reject MSK IAM config when aws-core is disabled. The Regenerated the Cue docs. 37 kafka tests green with aws-core, 9 auth-matrix tests green without it (incl. the new rejection); integration-test target compiles. |
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 9899c910ac
ℹ️ 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".
|
Good catch — and you're right, my earlier rejection was too aggressive. Fixed in e17ffec. I verified the behavior directly against librdkafka 0.39: with Changes:
A non-MSK |
|
@codex review |
…THBEARER librdkafka has no native AWS_MSK_IAM SASL mechanism (that lives only in the JVM client), so the portable path to authenticate against MSK with IAM is SASL/OAUTHBEARER, where the OAuth token is a SigV4-presigned kafka-cluster:Connect request, base64url-encoded. This ports the official aws-msk-iam-sasl-signer recipe. Add a sasl.aws_msk_iam config block (region + standard AwsAuthentication) to the kafka source and sink. When enabled, the mechanism is forced to OAUTHBEARER and KafkaStatisticsContext::generate_oauth_token mints/refreshes the token, reusing the existing AWS credential providers (static/profile/assume-role/IMDS). Gated behind the aws-core feature. Token generation correctness is proven offline against an independent SigV4 oracle (fixed inputs -> known-answer signature and full presigned URL). Closes vectordotdev#1516 Signed-off-by: Gaál György <gb12335@gmail.com>
…uth matrix tests Addresses review feedback: - Source: the consumer's rdkafka context is KafkaSourceContext (not the inner KafkaStatisticsContext), so it now enables ENABLE_REFRESH_OAUTH_TOKEN and forwards generate_oauth_token to the inner context. Without this, MSK IAM sources were configured with sasl.mechanism=OAUTHBEARER but had no token provider and failed to authenticate. - apply(): sasl.aws_msk_iam.enabled now implies SASL on its own; previously it only took effect when the legacy sasl.enabled was also set, so MSK IAM silently stayed on plaintext/ssl. Add a KafkaAuthConfig::apply test matrix covering the full auth surface mapped to the MSK port matrix (plaintext :9092, TLS/mTLS :9094, SASL/SCRAM :9096, SASL/IAM :9098) across the TLS/non-TLS axis, plus a source-context regression test that the OAUTHBEARER refresh callback is enabled and delegated. Signed-off-by: Gaál György <gb12335@gmail.com>
…hcheck Second round of review fixes: - Nested-runtime panic: the source serves the OAUTHBEARER refresh callback while the StreamConsumer is polled inside the tokio runtime, so the previous Handle::block_on in generate_oauth_token would panic. Token minting is now fully synchronous: AWS credentials are resolved once and cached in an Arc<Mutex<Credentials>> that a background task refreshes before expiry (Weak ref, so it stops when the component drops); the callback only reads the cache and signs. Removes the runtime handle from the context entirely. - Healthcheck: switch the sink healthcheck from BaseProducer (never polled) to FutureProducer, whose background poll thread serves the token-refresh event so the initial MSK IAM token is installed before fetch_metadata. - Require TLS: reject sasl.aws_msk_iam without tls.enabled instead of silently negotiating sasl_plaintext, which MSK IAM's SASL_SSL listener rejects. - Fix the KafkaSaslConfig literal in the sink integration tests so the aws-core + kafka-integration-tests build compiles. Add tests: refresh skew/floor, credential-cache path, and MSK-IAM-without-TLS rejection. Signed-off-by: Gaál György <gb12335@gmail.com>
rust-rdkafka gates the OAUTHBEARER refresh callback on a compile-time associated const (ClientContext::ENABLE_REFRESH_OAUTH_TOKEN), so in aws-core builds it is enabled for the context whenever it exists; it cannot be toggled per-instance by runtime config without monomorphizing the whole consumer/producer path over both values. Vector only provides a token for OAUTHBEARER via MSK IAM, so a bare sasl.mechanism=OAUTHBEARER (without sasl.aws_msk_iam) is now rejected at config time with guidance, instead of failing later in the refresh callback with no token provider. Add a regression test. Signed-off-by: Gaál György <gb12335@gmail.com>
…l, reject conflicting librdkafka_options Third round of review fixes: - Credential load timeout: MSK IAM credential resolution now honors the configured load_timeout_secs (5s default) via a new AwsAuthentication::load_timeout() accessor, wrapping both the initial resolve and the background refresh in a timeout so a stalled IMDS/STS/profile lookup can't hang the component build. - The bare-OAUTHBEARER rejection is now gated on sasl_enabled, so an inactive SASL stanza (enabled=false) kept around for templating/rollbacks is ignored as before. - Reject librdkafka_options that would override MSK IAM's security.protocol / sasl.* (both source and sink apply librdkafka_options after auth.apply, which would otherwise silently drop OAUTHBEARER/SASL_SSL). Add tests for the load timeout, disabled-stanza pass-through, and conflicting/benign librdkafka_options. Signed-off-by: Gaál György <gb12335@gmail.com>
… signing host, reject librdkafka OAUTHBEARER; add cue docs
Fourth round of review fixes:
- Clamp the OAUTHBEARER token lifetime to the credential expiry: temporary
(STS/IMDS) credentials can expire before the fixed 15-minute presign window, so
advertise the earlier of (signing_time + 900s) and credentials.expiry() to force
a refresh before the AWS session dies.
- Partition-aware signing host: resolve kafka.{region}.amazonaws.com.cn for China
regions (and honor an explicit sasl.aws_msk_iam.endpoint) instead of hard-coding
the commercial suffix, which would fail SigV4 validation in other partitions.
- Reject librdkafka_options.sasl.mechanism = OAUTHBEARER when MSK IAM is not
enabled: the refresh callback is always-on in aws-core builds, so this closes the
gap where OAUTHBEARER set via librdkafka_options (not the mechanism field) would
reach it with no token provider. validate_librdkafka_overrides now inspects
values, not just keys.
- Regenerate Cue component docs for the new sasl.aws_msk_iam options (kafka source
and sink).
Add tests: signing host (partition + endpoint), lifetime clamp, and librdkafka
OAUTHBEARER rejection.
Signed-off-by: Gaál György <gb12335@gmail.com>
…a_options librdkafka accepts both sasl.mechanism (singular) and sasl.mechanisms (plural) for the same property. validate_librdkafka_overrides only matched the singular form, so a leftover librdkafka_options.sasl.mechanisms could override MSK IAM's OAUTHBEARER (when enabled) or bypass the non-MSK OAUTHBEARER rejection. Match both aliases; extend tests. Signed-off-by: Gaál György <gb12335@gmail.com>
…ect MSK IAM without aws-core Fifth round of review fixes: - signing_host now reduces an explicit endpoint to the bare authority (host[:port]), dropping any /path, ?query, or #fragment — a documented full-URL endpoint would otherwise be signed as an invalid Host and fail SigV4 validation. - Validate the client/auth config (TLS requirement, conflicting librdkafka_options, unsupported-feature) before resolving AWS credentials in both the source and sink build paths, so an invalid config fails fast with an actionable error instead of blocking on IMDS/STS and reporting a credential error. - The aws_msk_iam block now parses even without the aws-core feature (its AWS-typed fields are cfg-gated), and apply() rejects an *enabled* MSK IAM config in a non-aws-core build instead of silently connecting without OAUTHBEARER/SASL_SSL. Regenerate Cue docs; add tests for endpoint-path stripping and the aws-core rejection. Signed-off-by: Gaál György <gb12335@gmail.com>
…IDC) Previous rounds proactively rejected a non-MSK OAUTHBEARER mechanism (top-level and via librdkafka_options). That was too aggressive: librdkafka has a built-in OIDC flow (sasl.oauthbearer.method=oidc) that does not use the application refresh callback, so those configs work fine even with ENABLE_REFRESH_OAUTH_TOKEN on (verified: method=oidc never invokes the app callback). Rejecting them broke a legitimate, historically-supported path. Remove both rejections. validate_librdkafka_overrides now only guards the keys MSK IAM manages when MSK IAM is actually enabled, and passes everything through otherwise. A non-MSK method=default OAUTHBEARER with no token provider still fails, but clearly at connect time rather than being pre-emptively rejected. Update tests accordingly. Signed-off-by: Gaál György <gb12335@gmail.com>
e17ffec to
249a686
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 249a686e07
ℹ️ 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".
When MSK IAM is enabled, a leftover librdkafka_options.sasl.oauthbearer.* (e.g. sasl.oauthbearer.method=oidc) applied after auth.apply would divert librdkafka to its built-in OIDC flow and bypass the MSK SigV4 token callback — the component would load AWS credentials but authenticate with the wrong token. Reserve the whole sasl.oauthbearer.* namespace (alongside security.protocol / sasl.mechanism[s] / sasl.username / sasl.password) when MSK IAM is enabled. Extend tests. Signed-off-by: Gaál György <gb12335@gmail.com>
|
Fixed in the latest push. When MSK IAM is enabled, |
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a98c3a8cf0
ℹ️ 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".
| // Required for `generate_oauth_token` to be invoked by librdkafka. Harmless for non-OAUTHBEARER | ||
| // mechanisms (SCRAM/PLAIN), where the callback is simply never called. | ||
| #[cfg(feature = "aws-core")] | ||
| const ENABLE_REFRESH_OAUTH_TOKEN: bool = true; |
There was a problem hiding this comment.
Keep default OAuth refresh for non-MSK configs
When Vector is built with aws-core, this installs Vector's OAuth refresh callback for every Kafka sink context, even when sasl.aws_msk_iam is not enabled; KafkaSourceContext repeats the same setting for sources. Existing non-MSK OAUTHBEARER/OIDC configs using sasl.mechanism or librdkafka_options then no longer use librdkafka's default token refresh callback (rdkafka documents that enabling this calls generate_oauth_token instead), so our callback runs with msk_iam: None and returns “MSK IAM is not configured,” breaking those connections. Please only enable the callback for MSK IAM clients, or otherwise preserve the default callback for non-MSK OAuth modes.
Useful? React with 👍 / 👎.
|
Leaving this one open for a maintainer's call, with the trade-off spelled out — it's the one point where a clean fix isn't proportionate to the regression. Scope of the regression (verified against librdkafka 0.39):
Why not a one-line fix: I'm happy to implement that monomorphization if you'd like librdkafka-native |
|
@pront — would you (or whoever owns the kafka components these days) be up for a look when you have a moment? This adds AWS MSK IAM auth to the It's gone through several Codex passes and all concrete findings are addressed; there's one open design question I'd like a maintainer steer on before finalizing:
Details + full reasoning are in the thread on |
Summary
Adds AWS MSK IAM authentication to the
kafkasource and sink via SASL/OAUTHBEARER.librdkafka (which Vector's kafka components use) has no native
AWS_MSK_IAMSASL mechanism — that exists only in the JVM client. The portable way to authenticate against MSK with IAM isSASL/OAUTHBEARER, where the OAuth token is a SigV4-presignedkafka-cluster:Connectrequest, base64url-encoded. This PR ports the officialaws-msk-iam-sasl-signerrecipe and wires it into the OAUTHBEARER refresh callback, so users can reuse the IAM roles they already have (Pod Identity / IRSA / assume-role) instead of managing static SCRAM secrets.Addresses #1516 (long-standing request for AWS auth on the kafka components); complements #25328 (general OAUTHBEARER support) with the MSK-specific token provider.
Configuration
When
aws_msk_iam.enabledis set, the mechanism is forced toOAUTHBEARERandKafkaStatisticsContext::generate_oauth_tokenmints and refreshes the token. The signer signs a synthetickafka.{region}.amazonaws.comhost (as the AWS signers do), so only region + credentials are needed — no per-broker plumbing. Gated behind theaws-corefeature.How correctness is verified
The token generator (
src/kafka/msk_iam.rs) is the only non-mechanical part, so it's covered by known-answer tests that don't require a live cluster: an independent, from-scratch SigV4 implementation computes a golden signature and full presigned URL for fixed inputs (credentials, region, signing time), and theaws-sigv4-based implementation must reproduce it byte-for-byte — both with and without a session token. There are also tests for the required MSK URL shape, base64url-nopad encoding, absolute-epoch token lifetime, and the async provider path.Compiles cleanly with
--features sources-kafka,sinks-kafka,aws-coreand, for feature-correctness, also with--features sources-kafka,sinks-kafka(aws-core off).Still to do (marked Draft)
make generate-component-docs) for the newsasl.aws_msk_iamoptions — couldn't run the full schema build in my dev environment.Happy to adjust the config surface / approach based on maintainer preference.