feat: wire SASL/SCRAM authentication into streaming producer#2123
Conversation
WalkthroughThis PR adds SASL authentication support to the ledger's streaming producer. It extends the bootstrap configuration with SASL fields, implements mechanism resolution and validation logic, wires SASL into the streaming builder with plaintext guards, and includes comprehensive tests for all code paths. ChangesStreaming SASL Authentication
Comment |
🔒 Security Scan Results —
|
| Policy | Status |
|---|---|
| Default non-root user | ✅ Passed |
| No fixable critical/high CVEs | ✅ Passed |
| No high-profile vulnerabilities | ✅ Passed |
| No AGPL v3 licenses | ✅ Passed |
Pre-release Version Check
🚫 Found 1 unstable version pin(s). Only stable releases (x.y.z) and SHA-based pins are allowed.
| File | Line | Content |
|---|---|---|
./go.mod |
103 | github.com/LerianStudio/lib-commons/v5 v5.2.0-beta.11 |
Replace pre-release suffixes (
-alpha,-beta,-rc,-dev, etc.) with stable releases.
🔒 Security Scan Results —
|
| Policy | Status |
|---|---|
| Default non-root user | ✅ Passed |
| No fixable critical/high CVEs | ✅ Passed |
| No high-profile vulnerabilities | ✅ Passed |
| No AGPL v3 licenses | ✅ Passed |
Pre-release Version Check
🚫 Found 1 unstable version pin(s). Only stable releases (x.y.z) and SHA-based pins are allowed.
| File | Line | Content |
|---|---|---|
./go.mod |
103 | github.com/LerianStudio/lib-commons/v5 v5.2.0-beta.11 |
Replace pre-release suffixes (
-alpha,-beta,-rc,-dev, etc.) with stable releases.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 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.
Inline comments:
In `@components/ledger/internal/bootstrap/streaming.go`:
- Around line 121-152: BuildStreamingEmitter performs non-trivial setup before
calling builder.Build(ctx) but doesn’t check for context cancellation; add an
early guard that checks ctx.Err() (and returns nil, noopStreamingCloser,
fmt.Errorf("context canceled") or wrap ctx.Err()) immediately at the start of
BuildStreamingEmitter to avoid doing work when the context is canceled, and add
a second quick ctx.Err() check just before calling builder.Build(ctx) (after
SASL/TLS config and resolveSASLMechanism) to avoid the final Build if
cancellation occurred; reference resolveSASLMechanism, builder,
builder.Build(ctx), cfg.StreamingAllowPlaintextSASL, noopStreamingCloser and
streamingPrimaryTargetName when making the changes.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 65434e92-e810-48e5-ae14-7917c3e1b211
📒 Files selected for processing (5)
components/ledger/.env.examplecomponents/ledger/internal/bootstrap/config.gocomponents/ledger/internal/bootstrap/streaming.gocomponents/ledger/internal/bootstrap/streaming_test.gogo.mod
Adds SASL authentication support to the ledger's
lib-streamingproducer, so the service can publish business events to brokers that require auth (Redpanda Cloud, managed Kafka, internal staging clusters, Clotilde dev). Until nowBuildStreamingEmitteronly configured brokers + topics + catalog, and any broker enforcing SASL closed the connection — observable asclass=context_canceled: context deadline exceededon the first emit because franz-go retried the handshake silently until our 5s per-emit deadline fired.Changes
New env vars (
components/ledger/.env.example)STREAMING_SASL_MECHANISMPLAIN|SCRAM-SHA-256|SCRAM-SHA-512(case-insensitive). Empty = no auth, preserving today's behaviourSTREAMING_SASL_USERNAMESTREAMING_SASL_PASSWORDSTREAMING_ALLOW_PLAINTEXT_SASLSASL_PLAINTEXTlistener (e.g. local Redpanda)falseAlso documents
STREAMING_IMPORTANT_EMIT_TIMEOUT_MS— the knob was already wired inpkg/streaming/emit.gobut wasn't discoverable from the env template. Useful for tuning the per-emit deadline against slow or remote brokers.Bootstrap (
components/ledger/internal/bootstrap/streaming.go)resolveSASLMechanism(*Config) (sasl.Mechanism, name, error)— maps the configured mechanism to the matching franz-gosasl.Mechanism(plain.Auth,scram.Auth.AsSha256Mechanism,scram.Auth.AsSha512Mechanism).BuildStreamingEmitternow callsBuilder.SASL(...)andBuilder.AllowPlaintextSASL()when configured.auth=<mechanism>(orauth=none). Username and password are never logged, even at debug level.STREAMING_ALLOW_PLAINTEXT_SASLopt-in is rejected atBuildtime withlib-streaming.ErrPlaintextSASLNotAllowed— matches the lib-streaming contract.OAUTHBEARER,GSSAPI), fail closed at bootstrap with an actionable error message.Config (
components/ledger/internal/bootstrap/config.go)Adds the four fields to the
Configstruct so the SASL knobs sit in the same env inventory as the rest ofSTREAMING_*.Dependency
Promotes
github.com/twmb/franz-gofrom indirect to direct dependency (we now import itssasl/plainandsasl/scrampackages explicitly). Already on the version that lib-streaming pulls in transitively, so no version bump.Tests (
components/ledger/internal/bootstrap/streaming_test.go, +296 LOC)TestResolveSASLMechanism_Disabled— empty / whitespace / credentials-without-mechanism all return nil mechanism (back-compat path).TestResolveSASLMechanism_Supported— all 7 accepted spellings (case-insensitive PLAIN, SCRAM-SHA-256, SCRAM-SHA-512) build the right franz-go mechanism.TestResolveSASLMechanism_MissingCredentials— missing username/password fails closed.TestResolveSASLMechanism_Unsupported— typos and unsupported mechanisms (OAUTHBEARER, GSSAPI, SCRAM-SHA-1, etc.) fail closed with an enumerated error.TestBuildStreamingEmitter_NilConfig/_DisabledReturnsNoop— preserves existing nil-guard and disabled-pilot contracts.TestBuildStreamingEmitter_SASLWithoutTLSFailsClosed— locks in that we never silently downgrade SASL to plaintext: must returnerrors.Is(err, libStreaming.ErrPlaintextSASLNotAllowed).TestBuildStreamingEmitter_SASLWithAllowPlaintextSucceeds— dev-broker happy path with SCRAM-SHA-256 + AllowPlaintextSASL builds without dialling the broker.TestBuildStreamingEmitter_UnsupportedMechanismFailsClosed— resolveSASLMechanism errors propagate out ofBuildStreamingEmitter.Backwards compatibility
Default for every new env var is empty / false. Services that don't set
STREAMING_SASL_MECHANISMget the exact same unauthenticated producer they had before. No migration required for existing deployments. ExistingSTREAMING_*knobs (BROKERS,CLIENT_ID,COMPRESSION, etc.) are unchanged.