Redis · lesson 9 of 14
Beginner
4Intermediate
7- 5 MULTI/EXEC Transactions and the WATCH-Based Optimistic Lock
- 6 Pub/Sub: Channels, Patterns, and the Message That Never Arrives
- 7 RDB vs AOF: What Survives a Hard Kill and What Doesn't
- 8 Replication: A Read-Only Replica, and What WAIT Actually Guarantees
- 9 ACL: a User Restricted to One Key Pattern and One Command
- 12 maxmemory and Eviction: What Redis Throws Away When It's Full
- 13 Cache-Aside with Redis: Stale Reads, Races and Stampedes
Advanced
3Also part of Redis
3Also part of: Security Fundamentals
ACL: a User Restricted to One Key Pattern and One Command
A default Redis install lets every connection run every command against every key. ACL SETUSER changes that — including a real NOPERM the moment a restricted user reaches outside its allowed keys or commands.
By SkillByExample · Published
By default, any client that can reach Redis at all can run any command against any key. ACL is what turns that into something scoped.
A user restricted to one command and one key pattern
docker exec redis-lesson redis-cli ACL SETUSER alice on ">p1pp0" "~cached:*" +get
docker exec redis-lesson redis-cli ACL LIST
OK
user alice on sanitize-payload #2d9c75273d72b32df726fb545c8a4edc719f0a95a6fd993950b10c474ad9c927 ~cached:* resetchannels -@all +get
user default on nopass sanitize-payload ~* &* +@all
alice can authenticate (on, with a password whose SHA-256 hash is shown, never the
plaintext), can only touch keys matching cached:*, and can only run get — everything else
starts from -@all (deny everything) with get added back explicitly.
The bug — three ways NOPERM shows up
docker exec redis-lesson redis-cli SET cached:1234 "zap"
# authenticated as alice, then:
GET foo
NOPERM No permissions to access a key
foo doesn’t match ~cached:* — rejected before Redis even checks whether get itself was
allowed.
# authenticated as alice, then:
GET cached:1234
zap
cached:1234 matches the key pattern and get is explicitly granted — this one succeeds.
# authenticated as alice, then:
SET cached:1234 zap2
NOPERM User alice has no permissions to run the 'set' command
Same key, different command — alice was only ever granted get, so set is refused even on
a key she can otherwise read.
Selectors: a second, independent rule set
docker exec redis-lesson redis-cli ACL SETUSER alice "(+set ~logs:*)"
docker exec redis-lesson redis-cli ACL LIST
OK
user alice on sanitize-payload #2d9c75273d72b32df726fb545c8a4edc719f0a95a6fd993950b10c474ad9c927 ~cached:* resetchannels -@all +get (~logs:* resetchannels -@all +set)
The part in parentheses is a selector — a completely separate rule set. alice now has
two independent permission sets: root (get on cached:*) and selector (set on logs:*).
# authenticated as alice, then:
SET logs:1 entry
OK
Allowed — matches the selector. But:
# authenticated as alice, then:
GET logs:1
NOPERM No permissions to access a key
Refused — even though alice can now write to logs:* via the selector, reading from
logs:* matches neither her root permissions (cached:* only) nor her selector (set only,
no get). Root and selector permissions don’t combine into a union; a command has to fully
satisfy one or the other on its own.
Key-level read/write permissions
docker exec redis-lesson redis-cli ACL SETUSER reader on nopass "+@read" "%R~data:*"
# authenticated as reader, then:
GET data:1
(nil)
# authenticated as reader, then:
SET data:1 x
NOPERM User reader has no permissions to run the 'set' command
+@read grants the whole category of read-only commands; %R~data:* scopes that to a key
pattern with explicitly read-only access. GET succeeds (returning nil because the key
doesn’t exist — not a permissions failure); SET is refused outright, since set was never
in the @read category to begin with.
Practice
1. Generate a strong random password the way ACL SETUSER expects.
docker exec redis-lesson redis-cli ACL GENPASS
fd6d2c60a18a01ca30ec39b5de36c235ced9b1b6d6a7cfdc4170a6e4d643cf25
A 256-bit random value as a 64-character hex string — meant to be pasted into an ACL SETUSER
>password rule, not memorized by a human.
2. List every command in a category before granting it wholesale.
docker exec redis-lesson redis-cli ACL CAT transaction
multi
exec
watch
discard
unwatch
ACL CAT <category> shows exactly what +@transaction would actually grant — worth checking
before adding a whole category to a restricted user, rather than assuming from the name alone.
3. Remove a user's ability to run a specific dangerous command while keeping the rest of a category.
docker exec redis-lesson redis-cli ACL SETUSER ops on nopass "+@all" "-flushall" "-flushdb"
printf "AUTH ops ''\nFLUSHALL\n" | docker exec -i redis-lesson redis-cli
OK
OK
NOPERM User ops has no permissions to run the 'flushall' command
+@all grants everything, then -flushall/-flushdb remove two specific commands — the
additive-then-subtractive pattern for “trust this user with almost everything, except these
few genuinely dangerous operations.”
Next: scripting — Lua’s EVAL versus the newer Functions API.