第 40 章

生产配置与安全加固

第40章 生产配置与安全加固

Redis 的默认配置面向开发便利,而非生产安全。本章系统覆盖网络绑定、认证体系、危险命令防护、TLS 加密、内核参数调优,以及完整的25条生产安全 Checklist。


1. 网络绑定配置

1.1 bind 与 protected-mode

# redis.conf

# 只监听内网接口,绝不绑定 0.0.0.0 或公网 IP
bind 127.0.0.1 192.168.1.100

# protected-mode:在无密码且未配置 bind 时,拒绝非 loopback 连接
# 生产中有密码时可设为 no,但建议保持 yes 作为双重保护
protected-mode yes

# 监听端口(可改非默认端口作为额外防护)
port 6379

# Unix socket(性能最好,同机通信)
unixsocket /var/run/redis/redis.sock
unixsocketperm 770
# 验证绑定是否生效
ss -tlnp | grep 6379
# 正确:只显示 127.0.0.1:6379 和 192.168.1.100:6379
# 危险:显示 0.0.0.0:6379 或 :::6379

2. 认证:requirepass 与 ACL

2.1 传统 requirepass(Redis 6.0 前)

# redis.conf
requirepass "YourStr0ng!Password#2024"

# 客户端认证
AUTH YourStr0ng!Password#2024

# 密码强度要求:
# - 长度 >= 32 字符
# - 包含大小写字母、数字、特殊字符
# - 避免字典词、生日、公司名

2.2 ACL(Redis 6.0+,强烈推荐)

ACL 支持细粒度权限控制:指定用户可访问哪些 key、执行哪些命令。

# 语法:ACL SETUSER <name> [on|off] [>password] [~key-pattern] [+command|-command]

# 应用只读用户(只能读取 cache: 前缀的 key)
ACL SETUSER readonly_user on >ReadPass#123 ~cache:* +@read

# 应用读写用户(只能操作 cache: 和 session: 前缀)
ACL SETUSER app_user on >AppPass#456 ~cache:* ~session:* +get +set +del +expire +hset +hget +hgetall +hgetall

# 管理员(全部权限)
ACL SETUSER admin_user on >AdminPass#789 ~* &* +@all

# 只允许发布订阅
ACL SETUSER pubsub_user on >PubSubPass ~* &events:* +subscribe +publish +unsubscribe

# 查看所有用户
ACL LIST
# 输出:
# user default off nopass ~* &* +@all
# user app_user on #<sha256密码哈希> ~cache:* ~session:* +get +set ...

# 查看当前用户
ACL WHOAMI

# 验证某用户是否可执行某命令
ACL DRYRUN app_user get cache:user:1001   # OK 或 报错

# 加载 ACL 文件(推荐将 ACL 配置独立文件)
ACL LOAD
ACL SAVE
# redis.conf 指定 ACL 文件
aclfile /etc/redis/users.acl

# users.acl 内容示例:
user default off
user app_user on #5e884898da28047151d0e56f8dc629277b7b1ad36601f39a6e4c6a40f5e0a16 ~cache:* ~session:* +get +set +del +expire +hset +hget +hgetall +ttl
user admin_user on #<admin_password_sha256> ~* &* +@all

2.3 命令分组

# Redis 内置命令组
+@all        # 所有命令
+@read       # GET/HGET/LRANGE/SMEMBERS 等只读命令
+@write      # SET/HSET/LPUSH/SADD 等写命令
+@string     # 字符串相关命令
+@hash       # Hash 相关命令
+@list       # List 相关命令
+@set        # Set 相关命令
+@sortedset  # ZSet 相关命令
+@geo        # GEO 命令
+@stream     # Stream 命令
+@pubsub     # 发布订阅命令
+@admin      # CONFIG/INFO/DEBUG 等管理命令
+@dangerous  # FLUSHALL/KEYS/DEBUG 等危险命令
-@dangerous  # 禁止危险命令(在 +@all 后减去)

3. rename-command 隐藏危险命令

# redis.conf
# 禁用危险命令(设为空字符串)
rename-command FLUSHDB ""
rename-command FLUSHALL ""
rename-command DEBUG ""
rename-command KEYS ""
rename-command CONFIG ""       # 禁用后需通过配置文件管理,不能动态修改
rename-command SHUTDOWN ""

# 改名为随机字符串(内部工具仍可使用)
rename-command CONFIG "CONFIG_a8f3d2c1b4e5"
rename-command DEBUG "DEBUG_x9y8z7w6v5u4"
rename-command OBJECT "OBJECT_q1w2e3r4t5"

# 注意:Cluster 模式下 rename-command 不支持,需用 ACL 替代
# 客户端适配(改名后)
CONFIG_COMMAND = "CONFIG_a8f3d2c1b4e5"

def redis_config_get(param: str):
    return r.execute_command(CONFIG_COMMAND, "GET", param)

4. TLS 双向认证(mTLS)

4.1 生成证书

# 创建 CA
openssl genrsa -out ca.key 4096
openssl req -new -x509 -days 3650 -key ca.key -out ca.crt \
    -subj "/C=CN/O=YourCompany/CN=Redis CA"

# 生成服务端证书
openssl genrsa -out redis.key 2048
openssl req -new -key redis.key -out redis.csr \
    -subj "/C=CN/O=YourCompany/CN=redis.internal"
openssl x509 -req -days 365 -in redis.csr -CA ca.crt -CAkey ca.key \
    -CAcreateserial -out redis.crt

# 生成客户端证书(mTLS 要求)
openssl genrsa -out client.key 2048
openssl req -new -key client.key -out client.csr \
    -subj "/C=CN/O=YourCompany/CN=redis-client"
openssl x509 -req -days 365 -in client.csr -CA ca.crt -CAkey ca.key \
    -CAcreateserial -out client.crt

4.2 Redis TLS 配置

# redis.conf

# 关闭明文端口(或保留用于内部监控)
port 0

# 开启 TLS 端口
tls-port 6380

# 证书路径
tls-cert-file /etc/redis/certs/redis.crt
tls-key-file  /etc/redis/certs/redis.key
tls-ca-cert-file /etc/redis/certs/ca.crt

# 要求客户端提供证书(双向认证)
tls-auth-clients yes

# TLS 版本(最低 TLS 1.2)
tls-protocols "TLSv1.2 TLSv1.3"

# 密码套件
tls-ciphers "ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384"
tls-ciphersuites "TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256"
tls-prefer-server-ciphers yes

# 复制也使用 TLS
tls-replication yes
tls-cluster yes   # Cluster 内部通信 TLS

4.3 客户端 TLS 连接

import redis
import ssl

r = redis.Redis(
    host="redis.internal",
    port=6380,
    ssl=True,
    ssl_certfile="/path/to/client.crt",
    ssl_keyfile="/path/to/client.key",
    ssl_ca_certs="/path/to/ca.crt",
    ssl_check_hostname=True,
)

# 测试连接
print(r.ping())  # True
# redis-cli TLS 连接
redis-cli -h redis.internal -p 6380 \
    --tls \
    --cert /path/to/client.crt \
    --key /path/to/client.key \
    --cacert /path/to/ca.crt \
    ping

5. maxmemory 计算与配置

5.1 计算公式

maxmemory = (物理内存 - OS预留 - 其他进程内存) × 0.75~0.85

示例(32GB服务器,仅运行Redis):
  OS预留: 2GB
  Prometheus exporter + 监控agent: 0.5GB
  可用: 32 - 2 - 0.5 = 29.5GB
  maxmemory = 29.5 × 0.80 ≈ 23GB

注意:预留20%给 BGSAVE 的 fork COW(Copy-On-Write)开销
写入密集场景 fork 瞬间内存可翻倍!

5.2 配置示例

# redis.conf
maxmemory 23gb
maxmemory-policy allkeys-lru     # 推荐策略(无需应用层设置 TTL 也能自动淘汰)

# 淘汰算法采样数(默认5,增大提高精度但消耗CPU)
maxmemory-samples 10

# 淘汰策略说明:
# noeviction       - 不淘汰,写入报错(适合做数据库)
# allkeys-lru      - 淘汰最久未使用的key(通用缓存首选)
# allkeys-lfu      - 淘汰使用频率最低的key(热点数据保留)
# allkeys-random   - 随机淘汰
# volatile-lru     - 只淘汰有TTL的key中最久未使用的
# volatile-lfu     - 只淘汰有TTL的key中频率最低的
# volatile-random  - 随机淘汰有TTL的key
# volatile-ttl     - 优先淘汰TTL最短的key

6. 内核参数优化

# /etc/sysctl.conf

# 允许过度提交内存(避免 BGSAVE fork 因内存不足失败)
# 0: 启发式(默认)1: 允许过度提交 2: 严格限制
vm.overcommit_memory = 1

# 增大TCP连接队列(防止高并发时连接被丢弃)
net.core.somaxconn = 65535
net.ipv4.tcp_max_syn_backlog = 65535

# TCP keepalive(及时发现死连接)
net.ipv4.tcp_keepalive_time = 300
net.ipv4.tcp_keepalive_intvl = 30
net.ipv4.tcp_keepalive_probes = 3

# 增大文件描述符限制
fs.file-max = 1000000

# 应用配置
sysctl -p
# 透明大页(THP)—— 必须关闭
# THP 会导致 Redis fork 时 COW 延迟抖动,以及内存碎片问题
echo never > /sys/kernel/mm/transparent_hugepage/enabled
echo never > /sys/kernel/mm/transparent_hugepage/defrag

# 持久化(开机自动关闭 THP)
cat >> /etc/rc.local << 'EOF'
echo never > /sys/kernel/mm/transparent_hugepage/enabled
echo never > /sys/kernel/mm/transparent_hugepage/defrag
EOF
chmod +x /etc/rc.local

# 验证
cat /sys/kernel/mm/transparent_hugepage/enabled
# 输出应为:always madvise [never]
# 进程级文件描述符限制
# /etc/security/limits.conf
redis soft nofile 65535
redis hard nofile 65535

# 或在 systemd service 中设置
# /etc/systemd/system/redis.service
[Service]
LimitNOFILE=65535
# redis.conf 内的相关配置
tcp-backlog 511          # 与 somaxconn 配合,建议 >= 512
tcp-keepalive 300        # Redis 级别的 TCP keepalive(秒)
timeout 0                # 客户端空闲超时(0=不超时,可设300)

7. 持久化安全配置

# redis.conf

# RDB 快照
save 3600 1      # 1小时内有1个写操作时保存
save 300 100     # 5分钟内有100个写操作时保存
save 60 10000    # 1分钟内有10000个写操作时保存

# RDB 写失败时停止写入(防止数据丢失)
stop-writes-on-bgsave-error yes

# RDB 文件压缩
rdbcompression yes
rdbchecksum yes   # CRC64 校验

# AOF 配置
appendonly yes
appendfilename "appendonly.aof"
appendfsync everysec    # 每秒 fsync(平衡性能和安全)
# appendfsync always    # 每次写都 fsync(最安全,性能最差)
# appendfsync no        # 交给 OS 决定(性能最好,可能丢数据)

# AOF 重写(减少 AOF 文件大小)
no-appendfsync-on-rewrite no    # 重写期间照常 fsync
auto-aof-rewrite-percentage 100  # AOF 增长到原来的 2 倍时触发重写
auto-aof-rewrite-min-size 64mb   # AOF >= 64MB 才触发重写

# 目录权限
dir /var/lib/redis
# chown redis:redis /var/lib/redis
# chmod 750 /var/lib/redis

8. 审计日志

# Redis 本身无审计日志功能
# 方案1:redis-cli MONITOR 输出重定向(影响性能,仅短时使用)
redis-cli monitor >> /var/log/redis/audit.log &

# 方案2:使用 redis-audit 工具(第三方)
# https://github.com/snmaynard/redis-audit

# 方案3:代理层审计(推荐生产)
# Twemproxy / Envoy sidecar 拦截并记录所有命令
# 支持按用户/IP/命令类型过滤记录

# 方案4:keyspace notifications + 外部监听
CONFIG SET notify-keyspace-events "KEA"   # 所有事件
redis-cli --no-auth-warning -p 6379 subscribe '__keyevent@0__:set'

9. 生产安全 Checklist(25条)

网络隔离(5条)

✅ 1. bind 配置只绑定内网 IP,禁止 0.0.0.0
✅ 2. 防火墙规则:6379/6380 只开放给应用服务器 IP,对公网全部关闭
✅ 3. Redis 与应用部署在同一 VPC/安全组,不经过公网
✅ 4. Sentinel/Cluster 管理端口(26379/17000+)单独防火墙策略
✅ 5. 使用 Unix Socket 代替 TCP(同机应用,性能+安全双优)

认证与授权(5条)

✅ 6. 必须配置认证(requirepass 或 ACL),禁止空密码
✅ 7. 使用 ACL 实现最小权限:应用用户只能操作自己的 key 前缀
✅ 8. 禁用 default 用户(ACL SETUSER default off)
✅ 9. 不同服务使用不同 Redis 用户,DB 隔离(SELECT 0/1/2...)
✅ 10. 密码存储在密钥管理服务(Vault / AWS Secrets Manager),不硬编码

危险命令防护(4条)

✅ 11. rename-command 禁用 FLUSHDB / FLUSHALL / DEBUG / SHUTDOWN
✅ 12. rename-command 禁用 KEYS(强制使用 SCAN)
✅ 13. ACL 中 -@dangerous 禁止应用用户执行危险命令
✅ 14. 生产 DBA 操作前需二次确认(FLUSHALL 等操作必须双人确认)

传输安全(2条)

✅ 15. 生产环境启用 TLS,最低 TLS 1.2,推荐 TLS 1.3
✅ 16. Cluster 节点间通信也启用 tls-cluster yes / tls-replication yes

内存与配置(4条)

✅ 17. 配置 maxmemory 和合适的 maxmemory-policy,防止内存无限增长
✅ 18. 配置 timeout(客户端空闲超时),防止连接泄漏
✅ 19. 关闭透明大页(THP):echo never > .../transparent_hugepage/enabled
✅ 20. vm.overcommit_memory = 1,防止 BGSAVE fork 失败

持久化与备份(3条)

✅ 21. 生产启用 AOF(appendonly yes,appendfsync everysec)
✅ 22. 定期备份 RDB 到异地存储(S3/OSS),保留至少7天
✅ 23. 定期测试备份恢复流程(每季度至少一次演练)

监控与告警(2条)

✅ 24. 部署 redis_exporter + Prometheus + Grafana,核心告警全覆盖(见第39章)
✅ 25. 配置 slowlog-log-slower-than 5000(5ms),慢查询告警和定期审查

10. 完整生产 redis.conf 模板

# ==== 网络 ====
bind 127.0.0.1 192.168.1.100
port 6379
protected-mode yes
tcp-backlog 511
timeout 300
tcp-keepalive 300

# ==== TLS(按需开启)====
# tls-port 6380
# port 0
# tls-cert-file /etc/redis/certs/redis.crt
# tls-key-file  /etc/redis/certs/redis.key
# tls-ca-cert-file /etc/redis/certs/ca.crt
# tls-auth-clients yes
# tls-protocols "TLSv1.2 TLSv1.3"
# tls-replication yes

# ==== 认证 ====
# requirepass "YourStr0ng!Password#2024"  # 用 ACL 替代
aclfile /etc/redis/users.acl

# ==== 危险命令 ====
rename-command FLUSHDB   ""
rename-command FLUSHALL  ""
rename-command DEBUG     ""
rename-command KEYS      ""
rename-command SHUTDOWN  "SHUTDOWN_9x8y7z"
rename-command CONFIG    "CONFIG_a1b2c3d4"

# ==== 内存 ====
maxmemory 23gb
maxmemory-policy allkeys-lru
maxmemory-samples 10

# ==== 持久化 ====
save 3600 1
save 300 100
save 60 10000
stop-writes-on-bgsave-error yes
rdbcompression yes
rdbchecksum yes
dbfilename dump.rdb
dir /var/lib/redis

appendonly yes
appendfilename "appendonly.aof"
appendfsync everysec
no-appendfsync-on-rewrite no
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
aof-use-rdb-preamble yes

# ==== 性能 ====
hz 10
dynamic-hz yes
aof-rewrite-incremental-fsync yes
rdb-save-incremental-fsync yes

# ==== 慢日志 ====
slowlog-log-slower-than 5000
slowlog-max-len 256

# ==== 日志 ====
loglevel notice
logfile /var/log/redis/redis.log

# ==== 进程 ====
daemonize yes
pidfile /var/run/redis/redis.pid

# ==== 客户端 ====
maxclients 10000

本章总结

本章评分
4.5  / 5  (3 评分)

💬 留言讨论