redis: add AWS ElastiCache, Azure AD, and GCP Memorystore dynamic-auth backends - #265
redis: add AWS ElastiCache, Azure AD, and GCP Memorystore dynamic-auth backends#265reyortiz3 wants to merge 7 commits into
Conversation
…h backends Mirrors postgres's IAM/Entra ID/OAuth2 dynamic-auth pattern for Redis and Valkey. Unlike pgx, go-redis has no per-dial BeforeConnect hook and keeps long-lived pooled connections, so tokens are refreshed via an OnConnect hook that performs AUTH itself plus a backend-scoped ConnMaxLifetime that forces periodic reconnects before a token would expire. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
ChrisJBurns
left a comment
There was a problem hiding this comment.
Multi-Agent Consensus Review
Agents consulted: cloud-auth-security, redis-lifecycle, general-code-review, codex, aws-conflict-resolution
Consensus Summary
| # | Finding | Consensus | Severity | Action |
|---|---|---|---|---|
| 1 | Dynamic authentication runs after go-redis initialization | 10/10 | HIGH | Fix |
| 2 | Failover sends data-node IAM credentials to Sentinel daemons | 10/10 | HIGH | Fix |
| 3 | GCP token refresh retains the client-construction context | 10/10 | HIGH | Fix |
| 4 | AWS tokens use the wrong SigV4 payload hash | 9/10 | HIGH | Fix |
| 5 | GCP IAM authentication sends the wrong Redis username | 9/10 | HIGH | Fix |
| 6 | Dynamic auth permits plaintext cloud credentials | 8/10 | HIGH | Fix |
| 7 | AWS credentials are rebuilt for every pooled connection | 9/10 | MEDIUM | Fix |
| 8 | ElastiCache serverless tokens omit ResourceType | 8/10 | MEDIUM | Fix |
| 9 | Core token and authenticated-connection paths lack behavioral tests | 8/10 | MEDIUM | Fix |
| 10 | ServiceName accepts values outside its documented enum | 7/10 | MEDIUM | Fix |
Overall
This PR adds Redis dynamic authentication for AWS, Azure, and GCP, but the central OnConnect approach is incompatible with go-redis v9.22's initialization order. Authentication occurs after HELLO and initialization commands, which breaks nonzero databases, downgrades otherwise valid clients to RESP2, and sends data-plane credentials to Sentinel daemons.
The provider implementations also have blocking protocol errors: AWS signs the wrong canonical payload and GCP sends an unsupported service-account username. GCP refresh lifetime and transport-security handling introduce additional runtime and credential-exposure risks. These need to be addressed before merge.
Documentation
redis/doc.go and the comments on Config.Username/DynamicAuth should be updated with the corrected credential-provider mechanism, provider-specific GCP username behavior, mandatory TLS requirements, and the supported ElastiCache resource types.
Generated with Codex using the pr-review workflow
…validation gaps Address PR #265 review findings: - Switch from OnConnect to Options.CredentialsProviderContext: go-redis only calls OnConnect after HELLO/AUTH and SELECT have completed, which broke non-zero DB selection and silently downgraded RESP3 connections to RESP2. CredentialsProviderContext resolves before that handshake. This also stops Sentinel discovery connections from receiving data-node IAM credentials, since FailoverOptions propagates CredentialsProviderContext (unlike OnConnect) only to the master client, never to the internal sentinel-daemon connections. - Use a background context for GCP's DefaultTokenSource construction so a canceled NewClient construction context can't poison later token refreshes. - Fix the AWS SigV4 payload hash: ElastiCache/MemoryDB requires SHA-256 of the empty body, not "UNSIGNED-PAYLOAD". - Make GCP Memorystore IAM auth token-only (no username), matching its documented contract; Config.Username must now be empty for that backend and required for AWS/Azure. - Require verified TLS when DynamicAuth is configured (bearer tokens over plaintext/unverified TLS are replayable), with an explicit AllowInsecureTransport escape hatch. - Cache the AWS credential-provider chain at construction instead of reloading it (IMDS/web-identity/STS discovery) on every token mint. - Add ElastiCache/MemoryDB Serverless support via ResourceType, and validate ServiceName/ResourceType against their supported enums. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…-redis-dynamic-auth
|
Pushed a fix for all 10 findings from the multi-agent review: HIGH
MEDIUM All existing + new tests pass ( |
Fixes the Go Vulnerability Check CI failure on this branch (also present on main's current HEAD independently of this PR's changes). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…-redis-dynamic-auth
Summary
postgrespackage's IAM/Entra ID/OAuth2 dynamic-auth pattern forredis, adding three backends: AWS ElastiCache/MemoryDB IAM, Azure Entra ID (Azure Cache for Redis), and GCP Memorystore for Redis Cluster IAM.BeforeConnecthook and keeps long-lived pooled connections (unlike pgx's frequent reconnects), so tokens are refreshed differently here:NewClientleavesPasswordunset and installs anOptions.OnConnecthook that mints a fresh token and issuesAUTHitself, plus a backend-scopedConnMaxLifetime(used whenConfig.ConnMaxLifetimeis zero) that makes go-redis periodically retire and redial pooled connections before a token would expire.auth.BuildAuthToken-style helper like RDS, soawsiam.gohand-signs a presigned SigV4 "connect" request per AWS's documented IAM-auth token format.Config.DynamicAuthrequiresConfig.Username(the IAM/ACL identity) and forbids a staticConfig.Password.Test plan
task lint— 0 issuestask test—redisandpostgrespackages pass with-race; the one repo-wide failure (networking.TestValidateCallbackPort) is pre-existing/environmental (a localkubectlprocess holding port 8090), confirmed unrelated by reproducing it onmainwithredis/unstagedtask license-check— passesPassword/DynamicAuth, requiredUsername, exactly-one-backend, AWS region/cluster-name requirements), each backend's token-func construction, andclient.go'sOnConnect/ConnMaxLifetimewiring (including that an explicitConfig.ConnMaxLifetimeoverrides the backend default, and that backend-construction errors propagate out ofNewClient)🤖 Generated with Claude Code