This document tracks the threat model, the audit done as part of Phase 3, and the fixes that landed. New code paths must consider these items.
cached is exposed to clients on an unauthenticated TCP port until they
AUTH. The threats we defend against, in order of likelihood:
- Memory-exhaustion DoS — a single malicious client (or a compromised internal service) trying to OOM the pod or burn through CPU.
- AUTH brute force — credential-stuffing against a known endpoint.
- Slow-loris / connection flooding — opening many half-dead connections
to exhaust the
MaxClientscap. - Privilege escalation via ACL gaps — bypassing per-user command allow-lists.
- Replication abuse — submitting forged Raft frames (mitigated by the private Raft network port, NetworkPolicy-restricted).
- Container escape — escalation out of the pod. Mitigated by distroless + nonroot + dropped capabilities.
Out of scope (defer to higher layers): nation-state attackers on the control plane; physical access to nodes; supply-chain compromise of go.mod dependencies (we run Trivy in CI but don't pin transitives).
Issue: RESP allowed $524288000\r\n (500 MB bulk), and readBulk did
make([]byte, n+2) based on the client-supplied length. A single
attacker could request many large bulks in parallel and OOM the pod.
Fix: MaxBulkSize is now a runtime-configurable value defaulting to
64 MiB (was 512 MiB). Exposed via CACHE_MAX_BULK_SIZE. The check
n > MaxBulkSize runs before any allocation.
(internal/resp/reader.go)
Issue: readArray called ReadValue recursively without a depth
limit. A maliciously crafted nested array could blow the Go stack
(default 1MB, growable but bounded).
Fix: Added MaxArrayDepth = 32 enforced in the reader. Configurable
via CACHE_MAX_ARRAY_DEPTH. Anything deeper returns ErrTooDeep.
(internal/resp/reader.go)
Issue: No rate limiting on failed AUTH attempts. An attacker could try thousands of passwords per second.
Fix: Per-IP sliding-window limiter in internal/auth/ratelimit.go.
Defaults to 30 failed attempts per minute per source IP. Exceeding the
limit returns WRONGPASS plus a 1-second sleep before close. Configurable
via CACHE_AUTH_RATELIMIT_PER_MIN.
Issue: A client could send MULTI followed by unlimited queued
commands without ever calling EXEC/DISCARD. TxnQueued would grow
unbounded.
Fix: Cap of 10,000 commands or 50 MiB of queued bytes per
connection's MULTI buffer. Exceeding either limit marks the transaction
Aborted and the next EXEC returns EXECABORT.
(internal/command/txn_cmds.go)
Issue: Running with auth.enabled=false on a port reachable from
the network is dangerous. Operators sometimes do this in dev and forget
to lock it down in prod.
Fix: New --protected-mode flag (default ON). When AUTH is
disabled, only connections from loopback are accepted; anything else
receives an error and is closed. Matches Redis's behavior since 3.2.
Set CACHE_PROTECTED_MODE=false to disable (e.g. when running behind a
strict NetworkPolicy).
(internal/server/conn.go)
Issue: A pre-AUTH client could hold open a connection (taking a
MaxClients slot) and send bytes very slowly to keep the read alive.
Fix: First-frame read uses a 10-second deadline (separate from
IdleTimeout). After AUTH, the longer idle timeout applies. Slow-loris
mitigation.
Issue: KEYS * walks every shard's map under lock. A single client
calling it on a 10M-key cache stalls the entire pod for seconds.
Fix: Logged at WARN level whenever called. Not removed (it's useful for ops). The docs now warn against using it on hot paths.
Issue: ACL SETUSER mutates only the local replica. Different
replicas can end up with different ACLs.
Status: Documented. Replicating ACLs through Raft is Phase 4.
Operators should configure ACLs identically at startup via a config
file (also Phase 4) or run ACL SETUSER against each pod.
These defaults are set in helm/cached/values.yaml:
- AUTH enabled with auto-generated 32-char password.
containerSecurityContext.readOnlyRootFilesystem: true.containerSecurityContext.capabilities.drop: ["ALL"].containerSecurityContext.allowPrivilegeEscalation: false.podSecurityContext.runAsNonRoot: true.podSecurityContext.seccompProfile.type: RuntimeDefault.automountServiceAccountToken: falseon the dedicated SA.- NetworkPolicy restricting ingress to same-namespace + labeled namespaces.
protectedMode: true(refuses non-loopback when AUTH is off).maxBulkSize: 64 MiB,authRateLimitPerMin: 30.
If you find a security issue, please do not open a public GitHub issue. Instead, email the maintainer or use GitHub's "Report a vulnerability" button on the repository.