Hardening Redis in Production: Authentication, TLS, ACLs, and Command Restriction

Hardening Redis in Production: Authentication, TLS, ACLs, and Command Restriction

Problem

Redis defaults prioritise developer convenience: no authentication, no TLS, all 200+ commands available, and binding to all interfaces. In production, an unprotected Redis instance is a direct path to data theft (dump all keys), command injection (EVAL executes arbitrary Lua), and denial of service (FLUSHALL deletes all data, DEBUG SLEEP freezes the server). Redis is one of the most commonly exploited services in production breaches.

Threat Model

  • Adversary: Network attacker who can reach the Redis port (6379), or compromised application with Redis credentials.
  • Objective: Read all cached data (sessions, tokens, user data). Inject Lua code via EVAL. Delete all data via FLUSHALL. Write SSH keys to disk via CONFIG SET dir + CONFIG SET dbfilename (the classic Redis RCE).
  • Blast radius: All data in all Redis databases on the instance.

Configuration

Redis 6+ ACLs

# /etc/redis/users.acl
# Redis 6+ ACL file - per-user authentication and command restrictions.

# Default user: disabled (no anonymous access)
user default off

# Application user: can read/write keys but cannot run admin commands
user app_user on >strong-password-here ~* &* +@all -@admin -@dangerous
# Breakdown:
# on         = user is active
# >password  = password (use >HASH for hashed passwords)
# ~*         = can access all key patterns
# &*         = can access all pub/sub channels
# +@all      = allow all command categories
# -@admin    = deny admin commands (CONFIG, DEBUG, SHUTDOWN, etc.)
# -@dangerous = deny dangerous commands (FLUSHALL, FLUSHDB, KEYS, etc.)

# Read-only user: for monitoring and analytics
user readonly_user on >another-strong-password ~* &* +@read -@write -@admin -@dangerous

# Admin user: full access (use only for maintenance)
user admin_user on >admin-strong-password ~* &* +@all
# redis.conf - reference the ACL file
aclfile /etc/redis/users.acl

# Or inline in redis.conf:
# user app_user on >strong-password ~* &* +@all -@admin -@dangerous

Dangerous Command Restriction

Even with ACLs, explicitly rename or disable the most dangerous commands:

# redis.conf - rename dangerous commands
# Use ACLs (above) as the primary control. Rename as defense-in-depth.

rename-command FLUSHALL ""       # Disable completely
rename-command FLUSHDB ""        # Disable completely
rename-command DEBUG ""          # Disable completely
rename-command CONFIG "CONFIG_b4f8c2a1"  # Rename to a secret string
rename-command SHUTDOWN "SHUTDOWN_e7d3f5"  # Rename
rename-command KEYS ""           # Disable (use SCAN instead. KEYS blocks the server)
rename-command EVAL ""           # Disable Lua scripting if not used
rename-command SCRIPT ""         # Disable script management

TLS Configuration

# redis.conf - TLS settings (Redis 6+)
port 0                           # Disable unencrypted port
tls-port 6379                    # TLS-only on standard port

tls-cert-file /etc/redis/tls/redis.crt
tls-key-file /etc/redis/tls/redis.key
tls-ca-cert-file /etc/redis/tls/ca.crt

# Minimum TLS version
tls-protocols "TLSv1.3"

# Require client certificate (mutual TLS - for admin connections)
# tls-auth-clients yes

# For replication TLS
tls-replication yes

# Verify client certificates against CA
tls-auth-clients optional
# Client connection with TLS:
redis-cli --tls \
  --cert /etc/redis/tls/client.crt \
  --key /etc/redis/tls/client.key \
  --cacert /etc/redis/tls/ca.crt \
  -h redis.example.com \
  -p 6379 \
  --user app_user \
  --pass 'strong-password-here'

Network Isolation

# redis.conf - bind to specific interfaces only
bind 127.0.0.1 10.0.1.5
# NEVER bind to 0.0.0.0 unless behind TLS + ACL + network policy

# Protected mode (rejects connections from non-localhost without auth)
protected-mode yes
# Kubernetes NetworkPolicy: restrict Redis access
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: redis-access
  namespace: data
spec:
  podSelector:
    matchLabels:
      app: redis
  policyTypes:
    - Ingress
  ingress:
    - from:
        - podSelector:
            matchLabels:
              app: api-server
      ports:
        - port: 6379
          protocol: TCP

Connection Limits

# redis.conf - connection management
maxclients 1000          # Maximum simultaneous connections
timeout 300              # Close idle connections after 5 minutes
tcp-keepalive 60         # TCP keepalive interval

# Memory limits
maxmemory 2gb            # Maximum memory usage
maxmemory-policy allkeys-lru  # Eviction policy when maxmemory is reached

Monitoring

# Prometheus alert rules for Redis security
groups:
  - name: redis-security
    rules:
      - alert: RedisAuthFailure
        expr: increase(redis_rejected_connections_total[5m]) > 10
        labels:
          severity: warning
        annotations:
          summary: "Redis authentication failures: {{ $value }} in 5 minutes"

      - alert: RedisExposedToInternet
        expr: redis_connected_clients > 100
        for: 5m
        labels:
          severity: critical
        annotations:
          summary: "Redis has {{ $value }} connected clients, possible exposure"

      - alert: RedisDangerousCommand
        expr: increase(redis_commands_total{cmd=~"flushall|flushdb|debug|config|keys"}[5m]) > 0
        labels:
          severity: critical
        annotations:
          summary: "Dangerous Redis command executed: {{ $labels.cmd }}"

Expected Behaviour

  • redis-cli without TLS and authentication is rejected
  • Application user can read/write data but cannot run FLUSHALL, CONFIG, DEBUG, or KEYS
  • Admin commands require the admin user with a separate strong password
  • All connections encrypted with TLS 1.3
  • Redis bound to specific interfaces; not reachable from the internet
  • Authentication failures generate alerts

Trade-offs

Control Impact Risk Mitigation
TLS on all connections 1-5% throughput reduction; latency increase for new connections Applications must configure TLS certificates Use connection pooling to maintain persistent TLS connections.
Disable KEYS command Applications using KEYS must switch to SCAN KEYS is a common but dangerous pattern (blocks server on large datasets) Migrate to SCAN in application code before disabling.
Disable EVAL Lua scripting unavailable Some applications use Lua scripts for atomic operations Keep EVAL enabled only if the application requires it; restrict to the app_user ACL.
ACLs per user Each application needs its own credentials More credentials to manage Use Vault (#65) for dynamic Redis credentials.

Failure Modes

Failure Symptom Detection Recovery
ACL misconfigured Application can’t connect; NOAUTH or NOPERM error Application logs show Redis auth/permission error Fix ACL file. Reload ACLs: redis-cli ACL LOAD. No restart needed.
TLS certificate expired All new connections fail cert-manager alerts (Article #70); Redis logs show TLS errors Renew certificate. Redis requires restart to load new cert (or use CONFIG SET tls-cert-file).
FLUSHALL executed All data lost Monitoring shows key count drops to zero; application errors spike Restore from RDB/AOF backup. Investigate who executed the command (check Redis slow log and ACL audit).
CONFIG SET dir exploit Attacker writes files to disk via Redis Unexpected files in /var/lib/redis or other directories; Redis slow log shows CONFIG command Disable CONFIG command via rename-command. Audit written files. Rotate any credentials that may have been compromised.

When to Consider a Managed Alternative

Redis HA (Sentinel/Cluster) is operationally complex. TLS certificate management, ACL maintenance, and backup verification add ongoing burden.

  • Upstash (#157): Serverless Redis with per-request pricing. TLS and auth built-in. Scale to zero.
  • Redis Inc. (#158): Official managed Redis Cloud. From $12/month.
  • Aiven (#156): Multi-database managed platform with Redis. From $19/month.
  • Grafana Cloud (#108): For Redis monitoring dashboards and alerting.

Premium content pack: Redis hardening configuration pack. ACL templates by use case (cache, session store, queue), TLS configuration, dangerous command lockdown, Sentinel hardened config, and Prometheus alert rules.