feat: authenticate to Redis via AWS ElastiCache secret (Secrets Manager)#3
Merged
Merged
Conversation
Mirror the silo app's ELASTICACHE_SECRET_ARN pattern. When set (and CACHE_ENGINE=redis), the Redis auth token (and optionally host/port) is fetched from AWS Secrets Manager and injected into the connection options. A background timer re-fetches the secret every AWS_SECRET_CACHE_TTL seconds and rebuilds the client when the token rotates, so reconnections use fresh credentials without a restart. - New lib/aws-secrets.js: TTL-cached getSecret(), lazy-imports the AWS SDK - Rework lib/cache-engines/redis.js: secret resolution, build options (standard + cluster), refresh timer; set/get unchanged - Config/IFRAMELY_REDIS_HOST/PORT/TLS take precedence; TLS not forced on - Configurable secret keys (defaults match silo so the same secret works) - Add @aws-sdk/client-secrets-manager dependency (lazy-imported) - Document new env vars in config.loader.js, config.local.js.SAMPLE, SETUP.md Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The Dockerfile installs with --frozen-lockfile, so the new dependency must be present in the lockfile. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The default per-worker memory cap (CLUSTER_WORKER_RESTART_ON_MEMORY_USED, 120 MB) is below the ~122 MB a worker uses just loading domains+plugins, causing an immediate restart loop. Expose it (and the periodic restart interval) via IFRAMELY_WORKER_MAX_MEMORY_MB / IFRAMELY_WORKER_RESTART_PERIOD_SEC. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Previously the AWS Secrets Manager fetch was gated solely on
ELASTICACHE_SECRET_ARN, so any non-EKS deployment with that var set would
still attempt to reach Secrets Manager (and fail/fall back). Mirror silo's
resolveSecrets() gate: skip secret resolution unless IRSA or EKS Pod Identity
credentials are detected via one of:
- AWS_ROLE_ARN / AWS_WEB_IDENTITY_TOKEN_FILE (IRSA)
- AWS_CONTAINER_CREDENTIALS_FULL_URI /
AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE (EKS Pod Identity)
Static AWS_ACCESS_KEY_ID creds are intentionally not accepted. Log a one-time
warning when the ARN is set but no managed credentials are present.
Co-Authored-By: Claude Opus 4.8 (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.
Context
The
siloapp (plane-ee/apps/silo) authenticates to AWS ElastiCache by reading anELASTICACHE_SECRET_ARN, fetching the secret JSON from AWS Secrets Manager, and using theREDIS_AUTH_TOKEN(plus host/port) it contains to connect — refreshing on a timer so rotated tokens are picked up without a restart.This PR brings the same pattern to iframely so the same AWS secret can be reused. It builds on the existing "Plane fork extension" env-var layer (
config.loader.js) and the pluggable Redis cache engine (lib/cache-engines/redis.js).What changed
lib/aws-secrets.js(new) — Port of silo'saws-secrets.ts.getSecret(arn, region, forceRefresh)does a TTL-cachedGetSecretValueCommand, lazy-importing@aws-sdk/client-secrets-managerso non-AWS deployments never load it.lib/cache-engines/redis.js(reworked) — WhenELASTICACHE_SECRET_ARNis set, fetches the auth token (and optionally host/port) from Secrets Manager and injects it into the connection options. A.unref()'d background timer re-fetches everyAWS_SECRET_CACHE_TTLseconds and rebuilds + swaps the client when the token rotates (handles live rotation despite iframely's single long-lived client).set/getare unchanged.package.json— adds@aws-sdk/client-secrets-manager(lazy-imported).config.local.js.SAMPLE,docs/SETUP.md, and theconfig.loader.jsenv-var header document the new variables.Behavior
IFRAMELY_REDIS_HOST/PORT/TLStake precedence for host/port/TLS; only the auth token is always taken from the secret.IFRAMELY_REDIS_TLS, not forced on (differs from silo's always-rediss://).redisOptions.auth_pass/password(redis-clustr@1.7.0 → node_redis v2).REDIS_AUTH_TOKEN/REDIS_HOST/REDIS_PORT).New env vars
ELASTICACHE_SECRET_ARNAWS_REGIONus-east-1AWS_SECRET_CACHE_TTL300REDIS_AUTH_TOKEN_KEYREDIS_AUTH_TOKENREDIS_HOST_KEYREDIS_HOSTREDIS_PORT_KEYREDIS_PORTTesting
node --checkon all modified files (ESM + top-level await) — clean.redis-clustr@1.7.0→ node_redis v2 auth key (auth_pass, withpasswordalias) from the published package.buildOptionsmerge logic (secret-only, config-wins, no-ARN regression, cluster, partial-secret, TLS-not-forced) — all pass.AWS_SECRET_CACHE_TTL.🤖 Generated with Claude Code