fix(provision): authenticate to password-protected shared Redis; stop leaking cluster DNS into mongo/nats customer URLs - #62
Merged
Conversation
…p leaking cluster DNS into mongo/nats customer URLs
Two customer-facing breakages found by live-testing the Azure cluster.
BUG 1 — /cache/new 503s on every call.
redis-provision now runs with --requirepass, but the local backend's admin
client was built as goredis.Options{Addr: REDIS_PROVISION_HOST} — no password
field anywhere in the path — so ACL SETUSER failed with "ERR Protocol error:
unauthenticated multibulk length" and Provision correctly fell closed rather
than handing out a credential-less shared URL.
New env REDIS_PROVISION_URL ("redis://[user]:password@host:port[/db]", parsed
with goredis.ParseURL) supplies the credentials. Unset → the existing
REDIS_PROVISION_HOST Addr form, byte-for-byte today's behaviour. Malformed →
logged (with the userinfo redacted, since net/url echoes the whole URL back in
its error) and the same fallback, because the factory chain has no error
channel and refusing to boot over one typo is worse than the 503.
Threaded as a separate constructor argument rather than read from env inside
the backend, mirroring mongo.NewBackend(backendType, adminURI, mongoHost):
credentialed admin endpoint and customer-facing host stay distinct inputs, and
the precedence rule lives in exactly one function. The backend records
opts.Addr as its host, never the admin URL — otherwise the admin password would
be interpolated into customer connection strings.
BUG 2 — /nosql/new and /queue/new emitted internal cluster DNS
(mongodb://…@mongodb.instant-data.svc.cluster.local:27017/…,
nats://nats.instant-data.svc.cluster.local:4222), unreachable for customers.
The public host was applied only inside the `case "k8s"` branch of each
NewBackend, and the cluster runs MONGO_PROVISION_BACKEND=local /
QUEUE_PROVISION_BACKEND=local.
Both now use the postgres local backend's mechanism verbatim: a buildXURL
helper (mirroring postgres.buildDBURL) over an env-resolved publicHostPort()
that falls back to the in-cluster admin host — never to an empty host. The
resolution order adds K8S_MONGO_PUBLIC_HOST / K8S_NATS_PUBLIC_HOST as a source,
so the already-configured cluster env fixes prod with no ops change, and takes
no built-in default, so a dev box keeps emitting localhost.
Call sites (rg -F, all updated): redis.NewBackend x2 prod (server.New,
pool.NewWithConfig) + 1 test; redis.NewSharedCarveBackend x1 prod + 1 test;
newLocalBackend x3 in the redis factory + 8 in tests. Customer-URL emitters:
one per shared backend (mongo.go Provision, queue/local.go Provision); the k8s
emitters were already correct.
Tests: table-driven over REDIS_PROVISION_URL set/unset/malformed and mongo+nats
public host set/unset, plus registry-iterating guards that every shared-carve
constructor honours the admin URL (rule 18) and that the admin password never
reaches a log line or a customer URL. New funcs are at 100% statement coverage;
make gate green.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Both bugs found by live-testing the Azure AKS cluster on 2026-08-12.
1.
/cache/newreturned 503 — 100% broken on any fresh clusterredis-provisiongained--requirepass, but the shared cache backend built its adminclient as
goredis.Options{Addr: redisHost}with no password field, soACL SETUSERfailed with
ERR Protocol error: unauthenticated multibulk length.This was never a regression. DigitalOcean ran the dedicated Redis backend (a pod per
token — 188 such namespaces were counted live), so the shared path was never in the
production request path. Switching Azure to shared backends executed it for the first time.
New env
REDIS_PROVISION_URL(redis://[user]:pass@host:port[/db],rediss://for TLS),parsed with
goredis.ParseURL. Unset → falls back to the existingREDIS_PROVISION_HOSTAddr form, so current behaviour is unchanged. Malformed → error log with userinfo redacted,
then the same fallback.
Chosen over
REDIS_PROVISION_PASSWORDto match every other backend —PostgresCustomersURLand
MongoAdminURIare already credentialed URLs inconfig.Config— and because it carriesusername, db index, TLS and dial options a bare password cannot.
2.
/nosql/newand/queue/newemitted unreachable URLspublicHostwas applied only inside thecase "k8s"branch, so the shared backend leakedraw cluster DNS to customers. The postgres shared backend already did this correctly, which
is why
/db/newreturnedpg.instanode.devand these two did not. Mirrorspostgres.buildDBURL/publicHostPortexactly rather than inventing a third pattern:env resolved at Provision time,
""sentinel so the caller falls back to the in-clusterhost, and no built-in default so a dev box keeps emitting localhost.
Ops impact: one new value,
REDIS_PROVISION_URL. Bug 2 needs no config change —K8S_MONGO_PUBLIC_HOSTandK8S_NATS_PUBLIC_HOSTare already set in the cluster and arenow honoured by the shared backends.
Call-site enumeration (rule 16)
newLocalBackend(— 3 prod sites inredis/backend.go(k8s-init-failure fallback,default arm, shared-carve) + 8 test sites. All updated.
redis.NewBackend—server.go:163,pool/factory.go:24,server_live_roundtrip_test.go:84.NewSharedCarveBackend—server.go:165,dispatch_test.go:342.mongodb://andnats://; the k8s emitters were already correct. Both fixed.Rule-18 registry-iterating guards added:
TestSharedBackendConstructors_AllHonourAdminURLiterates every constructor yielding a shared
LocalBackend.Verification
make gategreen — all 17 packagesok,golangci-lint run: 0 issues.Every new function at 100% coverage. One Mongo-gated line in
mongo.gocould not be provedlocally (no Docker on the dev machine); it sits in a block covered by
TestLocalProvision_Happy,which runs against the CI service container.
Known, deliberately out of scope
slog.Warnsites log aParseURLerror, andnet/urlechoes the fullURL including the password. New code redacts; those three don't. Separate PR.
/queue/newURL carries no credentials (subject-prefix isolation only), sopointing it at a public host exposes an unauthenticated broker. Reachability fix is correct;
the exposure decision is an operator call.
🤖 Generated with Claude Code