Secure Redis and Valkey with cnspec
Scan Redis and Valkey servers against security and compliance best practices with cnspec.
Scan your Redis and Valkey servers to find security risks before they become incidents. cnspec evaluates the network and authentication posture (protected mode, bound interfaces, password, TLS and client certificates), the access-control users and the command and key patterns that govern them, and the durability configuration.
cnspec connects and inventories the server through read-only commands (INFO, CONFIG GET, and the ACL commands). It never reads your keys.
Redis and Valkey are both supported by one provider and detected automatically. redisdb.instance.isValkey reports which one, and valkeyVersion carries the Valkey version where applicable.
If you're new to cnspec, start with the Quickstart. For an overview of every database cnspec can scan, see the database scanning overview.
Prerequisites
To scan Redis or Valkey with cnspec, you must have:
- cnspec installed on your workstation
- Network access to the server on its port (6379 by default)
- A credential that can run
INFOandCONFIG GET
Authenticate
cnspec authenticates with a password (legacy requirepass) or an ACL user and password on Redis 6 and later:
cnspec shell redisdb redis.contoso.com --ask-passFor a TLS connection, add the TLS flags:
cnspec shell redisdb redis.contoso.com --user auditor --ask-pass --tls --tls-ca ca.pemValkey is queried exactly the same way:
cnspec shell redisdb valkey.contoso.com --ask-passThe auditing credential needs to run INFO and CONFIG GET, which is the common case for a requirepass-authenticated connection or the default user. Listing access-control users additionally needs +acl. Without it, redisdb.instance.users returns empty rather than failing the scan, so confirm it's populated before trusting a passing user check.
Connection options
| Option | Description |
|---|---|
--host | Server hostname or IP address (also accepted as the positional argument) |
--port | Server port (default 6379) |
--user, -u | ACL user to authenticate as; omit for legacy password authentication |
--password, -p | Password, or --ask-pass to be prompted |
--database | Logical database index to select (default 0) |
--tls | Connect over TLS |
--tls-ca | Path to a CA certificate to verify the server |
--tls-insecure | Skip TLS certificate verification (testing only) |
Verify with a quick Redis check
cnspec shell redisdb redis.contoso.com --ask-passcnspec> redisdb.instance { version isValkey mode protectedMode bindsAllInterfaces requirepassSet tlsEnabled }
redisdb.instance: {
version: "7.4.10"
isValkey: false
mode: "standalone"
protectedMode: true
bindsAllInterfaces: true
requirepassSet: true
tlsEnabled: false
}If cnspec connects and shows the version, you're ready to scan.
Scan Redis
cnspec scan redisdb redis.contoso.com --ask-passWhen a scan completes, cnspec prints a summary of all the checks it ran, grouped by policy, along with a risk score from 0 (no risk) to 100 (highest risk). Failed checks include remediation guidance to help you fix issues. To learn more about reading scan results, read Understand cnspec Results.
Mondoo doesn't yet ship an out-of-the-box Redis policy, so use the checks below as a starting point and create your own policies to meet your specific requirements.
Explore and test checks interactively
cnspec shell redisdb redis.contoso.com --ask-passList access-control users
cnspec> redisdb.instance.users { name isDefault enabled nopass passwordCount commandRules keyPatterns }
redisdb.instance.users: [
0: {
name: "auditor"
isDefault: false
enabled: true
nopass: false
commandRules: ["-@all", "+@read", "+@connection"]
keyPatterns: ["app:*"]
}
]Read the network posture
cnspec> redisdb.instance { bind bindsAllInterfaces port tlsPort tlsEnabled tlsAuthClients }Read the durability configuration
cnspec> redisdb.instance.config { rdbEnabled save appendOnly appendFsync maxmemory maxmemoryPolicy }Example security checks
Ensure a password is configured
Without requirepass or an ACL password, reaching the port is the whole authentication story:
cnspec> redisdb.instance.requirepassSet == true
[ok] value: trueEnsure protected mode is on
cnspec> redisdb.instance.protectedMode == true
[ok] value: trueEnsure the server is not listening on every interface
cnspec> redisdb.instance.bindsAllInterfaces == false
[ok] value: trueEnsure TLS is enabled
cnspec> redisdb.instance.tlsEnabled == true
[ok] value: trueEnsure the plaintext listener is disabled
A port of 0 turns off the unencrypted listener, leaving only TLS:
cnspec> redisdb.instance.port == 0
[ok] value: trueEnsure TLS clients present a certificate
cnspec> redisdb.instance.tlsAuthClients == "yes"
[ok] value: trueEnsure no user authenticates without a password
nopass means anyone who names the user is authenticated as it:
cnspec> redisdb.instance.users.none(nopass == true)
[ok] value: trueEnsure the default user does not hold every command
The default user is what an unauthenticated connection becomes, so +@all there is the whole server:
cnspec> redisdb.instance.users.where(isDefault).none(enabled == true && commandRules.contains("+@all"))
[ok] value: trueEnsure no non-default user holds every command
cnspec> redisdb.instance.users.where(isDefault == false).none(commandRules.contains("+@all"))
[ok] value: trueEnsure persistence is enabled
With neither RDB snapshotting nor the append-only file, a restart loses everything held in memory:
cnspec> redisdb.instance.config.rdbEnabled == true || redisdb.instance.config.appendOnly == true
[ok] value: trueEnsure a memory limit is set
Without maxmemory the server grows until the host runs out of memory:
cnspec> redisdb.instance.config.maxmemory > 0
[ok] value: trueReview users that can reach every key
A * key pattern lets the user read and write everything in the database:
cnspec> redisdb.instance.users.where(keyPatterns.contains("*")) { name enabled commandRules }Learn more
- Redis Resource Pack Reference: every Redis and Valkey resource and field cnspec can query
- Write Effective MQL: guide to authoring checks and queries
rename-command remaps are a startup-only directive that CONFIG GET does not return, so they aren't readable over a connection and the provider doesn't report them.