Requiring a Redis password¶
The Kubernetes reference Redis (deploy/k8s/6-deployment-redis.yaml) uses a password when the
Secret has one and starts without one otherwise, so updating never takes Redis down. The
redis service in deploy/docker-compose.example.yml, the starting point for new installs,
requires one. Without a password, anything that can reach port 6379 can read the queue, the
event streams and the steps the server sends to its sandboxes, and the per-job sandbox users
protect nothing. The server shows a startup notice while that is the case. The server and the sandbox agent both parse
REDIS_URL with the same library, which honours host:port,password=…. The steps below
are what the reference files do; an older copy of them needs the same changes.
REDIS_URL stays in the host:port form, not a URI:
Kubernetes¶
- Put the password into the Secret under its own key, and the same value into
redis-url:
stringData:
redis-password: "<a long random value>"
redis-url: "redis:6379,password=<the same value>"
With deploy/apply-k8s-secret.sh, set REDIS_PASSWORD in .env instead; the script
writes both keys.
- Apply the Secret, then restart Redis and the server:
6-deployment-redis.yaml already reads redis-password: Redis starts with --requirepass
when the key is set, and its probes authenticate with the same value. An empty or missing
key starts Redis without a password, as before.
Docker Compose¶
Put the password in ../.env as REDIS_PASSWORD=…, then in the compose file:
- the
redisservice runssh -c 'test -n "$REDIS_PASSWORD" && exec redis-server --requirepass "$REDIS_PASSWORD" --maxmemory 256mb --maxmemory-policy allkeys-lru --save "" --appendonly yes'withREDIS_PASSWORDandREDISCLI_AUTHset from the same value; - its healthcheck
redis-cli pingthen authenticates throughREDISCLI_AUTH; - the server's literal
REDIS_URL=redis:6379becomesREDIS_URL=redis:6379,password=${REDIS_PASSWORD}.
Two ways this fails open, and what catches each¶
- A missing key. With
optional: truea missing key leaves the variable unset, and a composed value such as$(REDIS_PASSWORD)stays literal text: a password anyone can read in the manifest. The key is thereforeoptional: false, and the pod does not start without it. - An empty value.
requirepass ""turns the password off, andoptional: falsedoes not catch it, because the template ships""for every key. Thetest -n "$REDIS_PASSWORD"guard makes the Redis container exit instead of starting open.
What this protects, and what it does not¶
It keeps other workloads in the cluster, and anything else that reaches port 6379, out of Redis. On its own it does not keep repository scripts out: a sandbox has to reach Redis, so a command running inside it can read the credential the sandbox was handed. The per-job credential below limits what that credential reaches.
The per-job sandbox credential¶
With SANDBOX_BUS_CREDENTIAL=per-job (the default) the server creates a Redis ACL user
sbx-<job> for every sandbox it starts, with a random password, and hands the sandbox a
URL for that user instead of its own. The user reaches only sandbox:<job>:*, may read
the active-run set, and may run only the commands the sandbox agent and its Redis client
need. A command in one sandbox can therefore not read another job's steps (which carry
that job's repository token), push steps into another sandbox, or wait on its results.
The user is deleted when the sandbox goes, and the reapers delete the user of a sandbox
they remove. After a Redis restart the server re-issues the users of its live sandboxes
when it reconnects.
What the server's Redis user needs: the right to run ACL SETUSER, ACL DELUSER and
ACL GETUSER. The reference Redis's default user has it.
- The default user must have a password. A per-job user protects nothing while the
default user needs none, because a sandbox can connect as the default user. The server
says so with a startup finding at
GET /api/config/findings. - A Redis that refuses ACL commands (some managed services manage users only through
their own API) makes the server hand sandboxes its shared credential instead, with a
startup finding naming
SANDBOX_BUS_CREDENTIAL. Runs keep working; the isolation is gone. SetSANDBOX_BUS_CREDENTIAL=sharedto make that the explicit setting; the finding then names the choice.
The server never shows the URL: a startup finding or probe result about Redis names the
endpoints it parsed, or (value hidden).