1069 lines
25 KiB
Markdown
1069 lines
25 KiB
Markdown
# Redis 用户名查找和认证配置指南
|
||
|
||
**问题日期:** 2026年2月22日
|
||
**环境:** juwan namespace
|
||
**Redis 版本:** 7.0.12
|
||
**部署模式:** RedisReplication (OpsTree Operator)
|
||
|
||
---
|
||
|
||
## 📋 目录
|
||
|
||
1. [问题背景](#问题背景)
|
||
2. [Redis 认证机制演进](#redis-认证机制演进)
|
||
3. [诊断过程](#诊断过程)
|
||
4. [用户名发现结果](#用户名发现结果)
|
||
5. [各语言连接配置](#各语言连接配置)
|
||
6. [常见问题解答](#常见问题解答)
|
||
7. [安全建议](#安全建议)
|
||
|
||
---
|
||
|
||
## 🎯 问题背景
|
||
|
||
### 问题描述
|
||
在配置应用连接 Redis 时,发现只有密码信息(存储在 Kubernetes Secret `user-redis` 中),但不清楚:
|
||
- Redis 是否需要用户名?
|
||
- 如果需要,用户名是什么?
|
||
- 如何查找 Redis 的用户名配置?
|
||
|
||
### 初始已知信息
|
||
```yaml
|
||
Secret: user-redis
|
||
Namespace: juwan
|
||
Key: password
|
||
Value: (base64 编码的密码)
|
||
```
|
||
|
||
### 疑问
|
||
- 使用 RedisInsight 等管理工具时,Username 字段应该填什么?
|
||
- 应用代码中是否需要配置 Username?
|
||
- 不同 Redis 版本的认证方式是否有差异?
|
||
|
||
---
|
||
|
||
## 📚 Redis 认证机制演进
|
||
|
||
### Redis 6.0 之前:传统密码认证
|
||
|
||
**特点:**
|
||
- ❌ 不支持多用户
|
||
- ✅ 只需配置一个全局密码
|
||
- 🔧 配置项:`requirepass`
|
||
- 👤 隐式用户名:`default`(客户端不需要指定)
|
||
|
||
**配置示例:**
|
||
```conf
|
||
# redis.conf
|
||
requirepass mypassword
|
||
```
|
||
|
||
**连接方式:**
|
||
```bash
|
||
# 只需要密码
|
||
redis-cli -h host -p 6379 -a mypassword
|
||
|
||
# 或使用 AUTH 命令
|
||
redis-cli -h host -p 6379
|
||
> AUTH mypassword
|
||
```
|
||
|
||
**优点:**
|
||
- ✅ 配置简单
|
||
- ✅ 向后兼容
|
||
|
||
**缺点:**
|
||
- ❌ 所有客户端共享同一密码
|
||
- ❌ 无法区分不同应用的访问权限
|
||
- ❌ 无法限制特定命令
|
||
- ❌ 审计困难
|
||
|
||
---
|
||
|
||
### Redis 6.0+:ACL(访问控制列表)
|
||
|
||
**引入时间:** Redis 6.0 (2020年5月)
|
||
|
||
**新特性:**
|
||
- ✅ 支持多用户
|
||
- ✅ 每个用户有独立的密码
|
||
- ✅ 细粒度权限控制(命令、key、channel)
|
||
- ✅ 默认用户 `default` 兼容旧版本
|
||
- ✅ 支持运行时动态修改
|
||
|
||
**架构对比:**
|
||
```
|
||
传统模式:
|
||
┌─────────────┐
|
||
│ 所有客户端 │
|
||
└──────┬──────┘
|
||
│ (一个密码)
|
||
↓
|
||
┌─────────────┐
|
||
│ Redis │
|
||
│ (全权限) │
|
||
└─────────────┘
|
||
|
||
ACL 模式:
|
||
┌────────┐ ┌────────┐ ┌────────┐
|
||
│ App A │ │ App B │ │ Admin │
|
||
└───┬────┘ └───┬────┘ └───┬────┘
|
||
│ user1 │ user2 │ admin
|
||
│ (读写) │ (只读) │ (全部)
|
||
↓ ↓ ↓
|
||
┌───────────────────────────────┐
|
||
│ Redis ACL │
|
||
│ user1: +@write +@read │
|
||
│ user2: +@read ~cache:* │
|
||
│ admin: +@all ~* │
|
||
└───────────────────────────────┘
|
||
```
|
||
|
||
**ACL 规则语法:**
|
||
```
|
||
user <username> on <password> ~<keypattern> +<command>
|
||
|
||
示例:
|
||
user alice on >secret123 ~* +@all # 全部权限
|
||
user bob on >pass456 ~cache:* +get +set # 只能操作 cache:* 的 key
|
||
user readonly on >readonly ~* +@read -@write # 只读权限
|
||
```
|
||
|
||
---
|
||
|
||
## 🔍 诊断过程
|
||
|
||
### 步骤 1:确认 Pod 容器名称
|
||
|
||
**目的:** 找到 Redis 容器的正确名称,以便执行命令
|
||
|
||
**命令:**
|
||
```bash
|
||
kubectl get pod user-redis-0 -n juwan -o jsonpath='{.spec.containers[*].name}'
|
||
```
|
||
|
||
**输出:**
|
||
```
|
||
user-redis redis-exporter
|
||
```
|
||
|
||
**分析:**
|
||
- ✅ Pod 中有 2 个容器
|
||
- 📦 `user-redis`:Redis 主容器
|
||
- 📊 `redis-exporter`:Prometheus 监控容器
|
||
- 🎯 **需要连接到 `user-redis` 容器**
|
||
|
||
**关键信息:**
|
||
```yaml
|
||
Pod: user-redis-0
|
||
Containers:
|
||
- name: user-redis # ← Redis 服务容器
|
||
image: quay.io/opstree/redis:v7.0.12
|
||
port: 6379
|
||
- name: redis-exporter # ← 监控容器
|
||
image: quay.io/opstree/redis-exporter
|
||
port: 9121
|
||
```
|
||
|
||
---
|
||
|
||
### 步骤 2:获取 Redis 密码
|
||
|
||
**目的:** 从 Kubernetes Secret 中提取密码,用于认证
|
||
|
||
**命令:**
|
||
```bash
|
||
kubectl get secret user-redis -n juwan -o jsonpath='{.data.password}' | base64 -d
|
||
```
|
||
|
||
**输出:**
|
||
```
|
||
<密码内容> # 实际密码已隐藏
|
||
```
|
||
|
||
**说明:**
|
||
- Secret 中的数据以 base64 编码存储
|
||
- 需要解码才能得到明文密码
|
||
- `base64 -d` 在 Linux/Mac 上;Windows PowerShell 需要用其他方法
|
||
|
||
**Windows PowerShell 解码方法:**
|
||
```powershell
|
||
$encoded = kubectl get secret user-redis -n juwan -o jsonpath='{.data.password}'
|
||
[System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($encoded))
|
||
```
|
||
|
||
---
|
||
|
||
### 步骤 3:检查 ACL 配置(关键步骤)
|
||
|
||
**目的:** 查询 Redis 的用户配置,确认是否启用 ACL 以及有哪些用户
|
||
|
||
**命令:**
|
||
```bash
|
||
kubectl exec -it user-redis-0 -n juwan -c user-redis -- \
|
||
redis-cli -a $(kubectl get secret user-redis -n juwan -o jsonpath='{.data.password}' | base64 -d) \
|
||
ACL LIST
|
||
```
|
||
|
||
**命令解析:**
|
||
```bash
|
||
kubectl exec -it user-redis-0 \ # 在 user-redis-0 Pod 中执行命令
|
||
-n juwan \ # 命名空间
|
||
-c user-redis \ # 指定容器
|
||
-- \ # 分隔符
|
||
redis-cli \ # Redis 命令行工具
|
||
-a $(kubectl get secret ...) \ # 使用密码认证
|
||
ACL LIST # 列出所有 ACL 用户
|
||
```
|
||
|
||
**输出:**
|
||
```
|
||
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
|
||
1) "user default on #e3e7d4b9413497efc274c747b2ee88023e00e6416080db92c7bdd49a73f32d3d ~* &* +@all"
|
||
```
|
||
|
||
**输出分析:**
|
||
|
||
#### 警告信息
|
||
```
|
||
Warning: Using a password with '-a' option on the command line interface may not be safe.
|
||
```
|
||
- ⚠️ 安全提醒:命令行中的密码可能被历史记录或进程列表泄露
|
||
- 💡 生产环境建议:使用配置文件或环境变量传递密码
|
||
|
||
#### ACL 规则详解
|
||
```
|
||
user default on #e3e7d4b...73f32d3d ~* &* +@all
|
||
| | | | | | |
|
||
| | | | | | └─ 权限:所有命令
|
||
| | | | | └──── 所有 Pub/Sub channel
|
||
| | | | └─────── 所有 key pattern
|
||
| | | └──────────────────────────── 密码哈希(SHA256)
|
||
| | └─────────────────────────────── 状态:已启用
|
||
| └─────────────────────────────────────── 用户名
|
||
└──────────────────────────────────────────── 类型:用户
|
||
```
|
||
|
||
**权限标识含义:**
|
||
|
||
| 标识 | 含义 | 说明 |
|
||
|-----|------|------|
|
||
| `on` | 用户已启用 | 可以登录 |
|
||
| `off` | 用户已禁用 | 无法登录 |
|
||
| `~*` | Key Pattern | `*` = 所有 key;`~cache:*` = 只能访问 cache 开头的 key |
|
||
| `&*` | Pub/Sub Pattern | `*` = 所有 channel |
|
||
| `+@all` | 命令权限 | `@all` = 所有命令;`+get +set` = 只能用 GET/SET |
|
||
| `-@dangerous` | 禁止命令组 | 禁止危险命令(FLUSHDB, KEYS 等)|
|
||
| `#hash` | 密码哈希 | SHA256 哈希值,不存储明文 |
|
||
|
||
**命令组示例:**
|
||
- `@read`:只读命令(GET, HGET, LRANGE 等)
|
||
- `@write`:写入命令(SET, HSET, LPUSH 等)
|
||
- `@admin`:管理命令(CONFIG, SHUTDOWN 等)
|
||
- `@dangerous`:危险命令(FLUSHALL, KEYS 等)
|
||
- `@all`:所有命令
|
||
|
||
**结论:**
|
||
- ✅ Redis 启用了 ACL 模式
|
||
- 👤 **用户名是:`default`**
|
||
- 🔑 密码已配置(存储为 SHA256 哈希)
|
||
- 🔓 权限:完全访问(所有命令、所有 key、所有 channel)
|
||
|
||
---
|
||
|
||
### 步骤 4:验证用户名配置
|
||
|
||
**目的:** 确认 `default` 用户可以正常工作
|
||
|
||
**方法 1:使用密码(不指定用户名)**
|
||
```bash
|
||
kubectl exec -it user-redis-0 -n juwan -c user-redis -- \
|
||
redis-cli -a <password> PING
|
||
```
|
||
|
||
**预期输出:**
|
||
```
|
||
Warning: Using a password with '-a' option on the command line interface may not be safe.
|
||
PONG
|
||
```
|
||
|
||
✅ **说明:只提供密码可以正常连接(默认使用 default 用户)**
|
||
|
||
---
|
||
|
||
**方法 2:显式指定用户名**
|
||
```bash
|
||
kubectl exec -it user-redis-0 -n juwan -c user-redis -- \
|
||
redis-cli --user default --pass <password> PING
|
||
```
|
||
|
||
**预期输出:**
|
||
```
|
||
PONG
|
||
```
|
||
|
||
✅ **说明:显式指定 default 用户也可以正常连接**
|
||
|
||
---
|
||
|
||
**方法 3:测试错误的用户名**
|
||
```bash
|
||
kubectl exec -it user-redis-0 -n juwan -c user-redis -- \
|
||
redis-cli --user wronguser --pass <password> PING
|
||
```
|
||
|
||
**预期输出:**
|
||
```
|
||
(error) WRONGPASS invalid username-password pair or user is disabled.
|
||
```
|
||
|
||
❌ **说明:不存在的用户名会认证失败**
|
||
|
||
---
|
||
|
||
### 步骤 5:查看完整的 ACL 信息
|
||
|
||
**目的:** 了解 `default` 用户的详细权限配置
|
||
|
||
**命令:**
|
||
```bash
|
||
kubectl exec -it user-redis-0 -n juwan -c user-redis -- \
|
||
redis-cli -a <password> ACL GETUSER default
|
||
```
|
||
|
||
**输出:**
|
||
```
|
||
1) "flags"
|
||
2) 1) "on"
|
||
2) "allkeys"
|
||
3) "allchannels"
|
||
4) "allcommands"
|
||
3) "passwords"
|
||
4) 1) "e3e7d4b9413497efc274c747b2ee88023e00e6416080db92c7bdd49a73f32d3d"
|
||
5) "commands"
|
||
6) "+@all"
|
||
7) "keys"
|
||
8) 1) "*"
|
||
9) "channels"
|
||
10) 1) "*"
|
||
11) "selectors"
|
||
12) (empty array)
|
||
```
|
||
|
||
**详细分析:**
|
||
|
||
| 字段 | 值 | 含义 |
|
||
|-----|---|------|
|
||
| `flags` | `on, allkeys, allchannels, allcommands` | 用户已启用,可访问所有资源 |
|
||
| `passwords` | `e3e7d4b...` (SHA256) | 密码哈希 |
|
||
| `commands` | `+@all` | 允许所有命令组 |
|
||
| `keys` | `*` | 可访问所有 key |
|
||
| `channels` | `*` | 可访问所有 Pub/Sub channel |
|
||
|
||
**权限总结:**
|
||
- ✅ 用户状态:启用(on)
|
||
- ✅ 命令权限:所有命令(+@all)
|
||
- ✅ Key 权限:所有 key(~*)
|
||
- ✅ Channel 权限:所有 channel(&*)
|
||
- 🔒 密码:已设置且加密存储
|
||
|
||
---
|
||
|
||
## 🎯 用户名发现结果
|
||
|
||
### 最终结论
|
||
|
||
#### ✅ 当前 Redis 配置
|
||
```yaml
|
||
认证模式: ACL (Redis 6.0+)
|
||
用户名: default
|
||
密码: (存储在 Secret user-redis 中)
|
||
权限: 完全访问(超级用户)
|
||
状态: 已启用
|
||
```
|
||
|
||
#### 📝 连接参数汇总
|
||
|
||
| 参数 | 值 | 备注 |
|
||
|-----|---|------|
|
||
| **用户名** | `default` | 可省略,客户端默认使用 |
|
||
| **密码** | `kubectl get secret user-redis -n juwan -o jsonpath='{.data.password}' \| base64 -d` | 必需 |
|
||
| **Host (集群内)** | `user-redis-master.juwan.svc.cluster.local` | 写操作 |
|
||
| **Host (集群内)** | `user-redis-replica.juwan.svc.cluster.local` | 读操作 |
|
||
| **Host (Sentinel)** | `user-redis-sentinel-sentinel.juwan.svc.cluster.local` | 推荐 |
|
||
| **Port** | `6379` | Redis 数据端口 |
|
||
| **Sentinel Port** | `26379` | Sentinel 端口 |
|
||
|
||
---
|
||
|
||
### 为什么大多数情况不需要指定用户名?
|
||
|
||
#### Redis 客户端的默认行为
|
||
|
||
1. **向后兼容**
|
||
```
|
||
Redis 6.0+ 保持了与旧版本的兼容性
|
||
当客户端只提供密码时,自动使用 'default' 用户
|
||
```
|
||
|
||
2. **客户端实现**
|
||
```go
|
||
// go-redis 内部逻辑(伪代码)
|
||
if password != "" && username == "" {
|
||
username = "default" // 自动补全
|
||
}
|
||
```
|
||
|
||
3. **AUTH 命令演进**
|
||
```bash
|
||
# Redis 5.x 及之前
|
||
AUTH password # 只有密码
|
||
|
||
# Redis 6.0+(向后兼容)
|
||
AUTH password # 等价于 AUTH default password
|
||
AUTH username password # 新格式
|
||
```
|
||
|
||
#### 何时需要显式指定用户名?
|
||
|
||
| 场景 | 是否需要 | 原因 |
|
||
|-----|---------|------|
|
||
| 使用 `default` 用户 | ❌ 不需要 | 客户端自动使用 |
|
||
| 创建了自定义用户 | ✅ 需要 | 必须明确指定 |
|
||
| 多应用共享 Redis | ✅ 推荐 | 权限隔离 |
|
||
| 审计需求 | ✅ 推荐 | 区分访问来源 |
|
||
| 管理工具连接 | ⚠️ 可选 | 有些工具要求填写 |
|
||
|
||
---
|
||
|
||
## 💻 各语言连接配置
|
||
|
||
### Go (go-redis)
|
||
|
||
#### 方式 1:只用密码(推荐)
|
||
```go
|
||
import "github.com/redis/go-redis/v9"
|
||
|
||
// 不指定 Username,自动使用 default
|
||
rdb := redis.NewClient(&redis.Options{
|
||
Addr: "user-redis-master.juwan.svc.cluster.local:6379",
|
||
Password: os.Getenv("REDIS_PASSWORD"), // 只需密码
|
||
DB: 0,
|
||
})
|
||
```
|
||
|
||
#### 方式 2:显式指定用户名
|
||
```go
|
||
// 显式指定 Username(效果相同)
|
||
rdb := redis.NewClient(&redis.Options{
|
||
Addr: "user-redis-master.juwan.svc.cluster.local:6379",
|
||
Username: "default", // 明确指定
|
||
Password: os.Getenv("REDIS_PASSWORD"),
|
||
DB: 0,
|
||
})
|
||
```
|
||
|
||
#### Sentinel 模式(生产推荐)
|
||
```go
|
||
// Sentinel 模式也支持用户名
|
||
rdb := redis.NewFailoverClient(&redis.FailoverOptions{
|
||
MasterName: "mymaster",
|
||
SentinelAddrs: []string{
|
||
"user-redis-sentinel-sentinel.juwan.svc.cluster.local:26379",
|
||
},
|
||
// Username 可省略(默认 default)
|
||
Password: os.Getenv("REDIS_PASSWORD"),
|
||
DB: 0,
|
||
})
|
||
```
|
||
|
||
---
|
||
|
||
### Go-Zero 框架
|
||
|
||
#### 配置文件
|
||
```yaml
|
||
# app/users/rpc/etc/pb.yaml
|
||
Redis:
|
||
Type: sentinel
|
||
MasterName: mymaster
|
||
SentinelAddrs:
|
||
- user-redis-sentinel-sentinel.juwan.svc.cluster.local:26379
|
||
Pass: ${REDIS_PASSWORD} # 只需密码,不需要用户名
|
||
```
|
||
|
||
#### 如果需要自定义用户
|
||
```yaml
|
||
Redis:
|
||
Type: sentinel
|
||
MasterName: mymaster
|
||
SentinelAddrs:
|
||
- user-redis-sentinel-sentinel.juwan.svc.cluster.local:26379
|
||
Username: default # 可选:显式指定
|
||
Pass: ${REDIS_PASSWORD}
|
||
```
|
||
|
||
---
|
||
|
||
### Python (redis-py)
|
||
|
||
#### 只用密码
|
||
```python
|
||
import redis
|
||
|
||
# 不指定 username
|
||
r = redis.Redis(
|
||
host='user-redis-master.juwan.svc.cluster.local',
|
||
port=6379,
|
||
password=os.getenv('REDIS_PASSWORD'), # 只需密码
|
||
db=0
|
||
)
|
||
```
|
||
|
||
#### 显式指定用户名(redis-py 4.3+)
|
||
```python
|
||
# 显式指定 username
|
||
r = redis.Redis(
|
||
host='user-redis-master.juwan.svc.cluster.local',
|
||
port=6379,
|
||
username='default', # 明确指定
|
||
password=os.getenv('REDIS_PASSWORD'),
|
||
db=0
|
||
)
|
||
```
|
||
|
||
#### Sentinel 模式
|
||
```python
|
||
from redis.sentinel import Sentinel
|
||
|
||
sentinel = Sentinel([
|
||
('user-redis-sentinel-sentinel.juwan.svc.cluster.local', 26379)
|
||
])
|
||
|
||
# 获取主节点(不指定 username)
|
||
master = sentinel.master_for(
|
||
'mymaster',
|
||
password=os.getenv('REDIS_PASSWORD')
|
||
)
|
||
|
||
# 或显式指定
|
||
master = sentinel.master_for(
|
||
'mymaster',
|
||
username='default',
|
||
password=os.getenv('REDIS_PASSWORD')
|
||
)
|
||
```
|
||
|
||
---
|
||
|
||
### Java (Spring Data Redis)
|
||
|
||
#### application.yml(只用密码)
|
||
```yaml
|
||
spring:
|
||
redis:
|
||
password: ${REDIS_PASSWORD} # 只需密码
|
||
sentinel:
|
||
master: mymaster
|
||
nodes:
|
||
- user-redis-sentinel-sentinel.juwan.svc.cluster.local:26379
|
||
```
|
||
|
||
#### 显式指定用户名(Spring Boot 2.6+)
|
||
```yaml
|
||
spring:
|
||
redis:
|
||
username: default # 可选
|
||
password: ${REDIS_PASSWORD}
|
||
sentinel:
|
||
master: mymaster
|
||
nodes:
|
||
- user-redis-sentinel-sentinel.juwan.svc.cluster.local:26379
|
||
```
|
||
|
||
#### Java 代码(Jedis)
|
||
```java
|
||
// 只用密码
|
||
JedisPoolConfig poolConfig = new JedisPoolConfig();
|
||
JedisPool pool = new JedisPool(
|
||
poolConfig,
|
||
"user-redis-master.juwan.svc.cluster.local",
|
||
6379,
|
||
2000,
|
||
"password" // 只需密码
|
||
);
|
||
|
||
// 显式指定用户名(Jedis 4.0+)
|
||
JedisPool pool = new JedisPool(
|
||
poolConfig,
|
||
"user-redis-master.juwan.svc.cluster.local",
|
||
6379,
|
||
2000,
|
||
"default", // 用户名
|
||
"password" // 密码
|
||
);
|
||
```
|
||
|
||
---
|
||
|
||
### Node.js (ioredis)
|
||
|
||
#### 只用密码
|
||
```javascript
|
||
const Redis = require('ioredis');
|
||
|
||
// 不指定 username
|
||
const redis = new Redis({
|
||
host: 'user-redis-master.juwan.svc.cluster.local',
|
||
port: 6379,
|
||
password: process.env.REDIS_PASSWORD, // 只需密码
|
||
});
|
||
```
|
||
|
||
#### 显式指定用户名(ioredis 4.0+)
|
||
```javascript
|
||
// 显式指定 username
|
||
const redis = new Redis({
|
||
host: 'user-redis-master.juwan.svc.cluster.local',
|
||
port: 6379,
|
||
username: 'default', // 明确指定
|
||
password: process.env.REDIS_PASSWORD,
|
||
});
|
||
```
|
||
|
||
#### Sentinel 模式
|
||
```javascript
|
||
const redis = new Redis({
|
||
sentinels: [
|
||
{
|
||
host: 'user-redis-sentinel-sentinel.juwan.svc.cluster.local',
|
||
port: 26379
|
||
}
|
||
],
|
||
name: 'mymaster',
|
||
password: process.env.REDIS_PASSWORD, // username 可省略
|
||
});
|
||
```
|
||
|
||
---
|
||
|
||
### redis-cli 命令行
|
||
|
||
#### 只用密码
|
||
```bash
|
||
# 方式 1:命令行参数
|
||
redis-cli -h user-redis-master.juwan.svc.cluster.local -p 6379 -a <password>
|
||
|
||
# 方式 2:交互式登录
|
||
redis-cli -h user-redis-master.juwan.svc.cluster.local -p 6379
|
||
> AUTH <password>
|
||
OK
|
||
```
|
||
|
||
#### 显式指定用户名
|
||
```bash
|
||
# 方式 1:命令行参数
|
||
redis-cli -h host -p 6379 --user default --pass <password>
|
||
|
||
# 方式 2:交互式登录
|
||
redis-cli -h host -p 6379
|
||
> AUTH default <password>
|
||
OK
|
||
```
|
||
|
||
---
|
||
|
||
## 🛠️ 管理工具配置
|
||
|
||
### RedisInsight
|
||
|
||
**配置示例:**
|
||
```
|
||
Name: juwan-user-redis
|
||
Host: localhost (使用 kubectl port-forward)
|
||
Port: 6379
|
||
Username: default ← 填这个(或留空)
|
||
Password: <从 Secret 获取>
|
||
```
|
||
|
||
**说明:**
|
||
- ✅ Username 可以填 `default`
|
||
- ✅ Username 也可以留空(某些版本支持)
|
||
- 🔧 建议先 port-forward:`kubectl port-forward -n juwan svc/user-redis-master 6379:6379`
|
||
|
||
---
|
||
|
||
### Redis Commander
|
||
|
||
**Docker 运行:**
|
||
```bash
|
||
docker run -d \
|
||
-e REDIS_HOSTS=juwan:user-redis-master.juwan.svc.cluster.local:6379:0:password \
|
||
-p 8081:8081 \
|
||
rediscommander/redis-commander
|
||
```
|
||
|
||
**URL 格式:**
|
||
```
|
||
# 不带用户名
|
||
redis://:<password>@host:6379
|
||
|
||
# 带用户名
|
||
redis://default:<password>@host:6379
|
||
```
|
||
|
||
---
|
||
|
||
### Another Redis Desktop Manager
|
||
|
||
**连接配置:**
|
||
```json
|
||
{
|
||
"name": "juwan-redis",
|
||
"host": "localhost",
|
||
"port": 6379,
|
||
"auth": "password", // 只需密码
|
||
"username": "" // 留空(或填 "default")
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## ❓ 常见问题解答
|
||
|
||
### Q1: 为什么有些地方说 Redis 不需要用户名?
|
||
|
||
**A:** 这取决于 Redis 版本:
|
||
|
||
```
|
||
Redis 5.x 及之前:
|
||
└─ ❌ 不支持用户名,只有全局密码
|
||
└─ 配置:requirepass password
|
||
|
||
Redis 6.0+:
|
||
└─ ✅ 支持 ACL 多用户
|
||
└─ 但保持向后兼容
|
||
└─ 如果只提供密码,自动使用 'default' 用户
|
||
```
|
||
|
||
### Q2: 我的 Redis 是哪个版本?
|
||
|
||
**查看方法:**
|
||
```bash
|
||
kubectl exec -it user-redis-0 -n juwan -c user-redis -- redis-cli -v
|
||
# 或
|
||
kubectl exec -it user-redis-0 -n juwan -c user-redis -- redis-server --version
|
||
```
|
||
|
||
**你的环境:**
|
||
```
|
||
Redis 7.0.12 (quay.io/opstree/redis:v7.0.12)
|
||
✅ 支持 ACL
|
||
✅ 用户名: default
|
||
```
|
||
|
||
### Q3: 如何创建自定义用户?
|
||
|
||
**场景:** 为不同应用创建独立的用户账号
|
||
|
||
#### 创建只读用户
|
||
```bash
|
||
kubectl exec -it user-redis-0 -n juwan -c user-redis -- \
|
||
redis-cli -a <password> ACL SETUSER readonly on \
|
||
'>readonly_password' \
|
||
~* \
|
||
+@read -@write -@dangerous
|
||
```
|
||
|
||
**验证:**
|
||
```bash
|
||
# 使用新用户登录
|
||
kubectl exec -it user-redis-0 -n juwan -c user-redis -- \
|
||
redis-cli --user readonly --pass readonly_password GET somekey
|
||
|
||
# 尝试写操作(应该失败)
|
||
kubectl exec -it user-redis-0 -n juwan -c user-redis -- \
|
||
redis-cli --user readonly --pass readonly_password SET somekey value
|
||
# (error) NOPERM User has no permissions to run the 'set' command
|
||
```
|
||
|
||
#### 创建应用专用用户
|
||
```bash
|
||
# 只能访问 app:* 开头的 key
|
||
kubectl exec -it user-redis-0 -n juwan -c user-redis -- \
|
||
redis-cli -a <password> ACL SETUSER app_user on \
|
||
'>app_password' \
|
||
~app:* \
|
||
+@all
|
||
```
|
||
|
||
#### 保存 ACL 配置
|
||
```bash
|
||
# 持久化到配置文件
|
||
kubectl exec -it user-redis-0 -n juwan -c user-redis -- \
|
||
redis-cli -a <password> ACL SAVE
|
||
```
|
||
|
||
⚠️ **注意:** 在 Kubernetes 环境中,Pod 重启可能丢失 ACL 配置。建议:
|
||
1. 使用 ConfigMap 存储 ACL 配置
|
||
2. 在 StatefulSet 启动脚本中加载配置
|
||
3. 或使用 Redis Operator 的 ACL 管理功能
|
||
|
||
### Q4: 如何查看所有用户?
|
||
|
||
```bash
|
||
kubectl exec -it user-redis-0 -n juwan -c user-redis -- \
|
||
redis-cli -a <password> ACL LIST
|
||
```
|
||
|
||
### Q5: 如何重置用户密码?
|
||
|
||
#### 重置 default 用户密码
|
||
```bash
|
||
# 1. 生成新密码
|
||
NEW_PASSWORD=$(openssl rand -base64 32)
|
||
|
||
# 2. 在 Redis 中更新
|
||
kubectl exec -it user-redis-0 -n juwan -c user-redis -- \
|
||
redis-cli -a <old_password> ACL SETUSER default ">$NEW_PASSWORD"
|
||
|
||
# 3. 更新 Kubernetes Secret
|
||
kubectl create secret generic user-redis \
|
||
--from-literal=password=$NEW_PASSWORD \
|
||
--dry-run=client -o yaml | kubectl apply -f -
|
||
|
||
# 4. 重启应用 Pods(使新密码生效)
|
||
kubectl rollout restart deployment/user-rpc -n juwan
|
||
```
|
||
|
||
### Q6: 客户端报错 "WRONGPASS invalid username-password pair"
|
||
|
||
**可能原因:**
|
||
|
||
1. **密码错误**
|
||
```bash
|
||
# 验证密码
|
||
kubectl get secret user-redis -n juwan -o jsonpath='{.data.password}' | base64 -d
|
||
```
|
||
|
||
2. **用户名错误**
|
||
```bash
|
||
# 检查用户是否存在
|
||
kubectl exec -it user-redis-0 -n juwan -c user-redis -- \
|
||
redis-cli -a <admin_password> ACL LIST
|
||
```
|
||
|
||
3. **用户被禁用**
|
||
```bash
|
||
# 启用用户
|
||
kubectl exec -it user-redis-0 -n juwan -c user-redis -- \
|
||
redis-cli -a <admin_password> ACL SETUSER default on
|
||
```
|
||
|
||
4. **网络连接到了错误的 Redis 实例**
|
||
```bash
|
||
# 确认连接的主机
|
||
kubectl get svc -n juwan | grep redis
|
||
```
|
||
|
||
### Q7: 在 Kubernetes 中如何安全地传递密码?
|
||
|
||
**推荐方案:环境变量 + Secret**
|
||
|
||
```yaml
|
||
# Deployment
|
||
apiVersion: apps/v1
|
||
kind: Deployment
|
||
metadata:
|
||
name: user-rpc
|
||
spec:
|
||
template:
|
||
spec:
|
||
containers:
|
||
- name: user-rpc
|
||
env:
|
||
- name: REDIS_PASSWORD
|
||
valueFrom:
|
||
secretKeyRef:
|
||
name: user-redis
|
||
key: password
|
||
```
|
||
|
||
**应用代码:**
|
||
```go
|
||
// 从环境变量读取
|
||
password := os.Getenv("REDIS_PASSWORD")
|
||
|
||
rdb := redis.NewClient(&redis.Options{
|
||
Addr: "user-redis-master.juwan.svc.cluster.local:6379",
|
||
Password: password,
|
||
})
|
||
```
|
||
|
||
**❌ 不推荐:**
|
||
```yaml
|
||
# 不要在配置文件中硬编码密码
|
||
Redis:
|
||
Password: "hardcoded_password" # ❌ 不安全
|
||
```
|
||
|
||
---
|
||
|
||
## 🔒 安全建议
|
||
|
||
### 1. 密码强度
|
||
|
||
**检查当前密码强度:**
|
||
```bash
|
||
PASSWORD=$(kubectl get secret user-redis -n juwan -o jsonpath='{.data.password}' | base64 -d)
|
||
echo "密码长度: ${#PASSWORD}"
|
||
```
|
||
|
||
**推荐:**
|
||
- ✅ 至少 32 字符
|
||
- ✅ 包含大小写字母、数字、特殊字符
|
||
- ✅ 使用密码生成器:`openssl rand -base64 32`
|
||
|
||
### 2. 权限最小化
|
||
|
||
**不要所有应用都用 default 超级用户!**
|
||
|
||
```bash
|
||
# 为不同应用创建独立用户
|
||
# 用户 A:只能读写自己的 key
|
||
kubectl exec -it user-redis-0 -n juwan -c user-redis -- \
|
||
redis-cli -a <password> ACL SETUSER app_a on \
|
||
'>password_a' ~app_a:* +@all
|
||
|
||
# 用户 B:只读
|
||
kubectl exec -it user-redis-0 -n juwan -c user-redis -- \
|
||
redis-cli -a <password> ACL SETUSER app_b on \
|
||
'>password_b' ~* +@read
|
||
```
|
||
|
||
### 3. 禁止危险命令
|
||
|
||
**即使是 default 用户,也应该限制危险命令:**
|
||
|
||
```bash
|
||
# 禁止 FLUSHALL, FLUSHDB, KEYS 等命令
|
||
kubectl exec -it user-redis-0 -n juwan -c user-redis -- \
|
||
redis-cli -a <password> ACL SETUSER default -flushall -flushdb -keys
|
||
|
||
# 查看限制后的权限
|
||
kubectl exec -it user-redis-0 -n juwan -c user-redis -- \
|
||
redis-cli -a <password> ACL GETUSER default
|
||
```
|
||
|
||
### 4. 审计日志
|
||
|
||
**启用 ACL 日志记录:**
|
||
```bash
|
||
# 查看最近的 ACL 事件
|
||
kubectl exec -it user-redis-0 -n juwan -c user-redis -- \
|
||
redis-cli -a <password> ACL LOG 10
|
||
|
||
# 输出示例:
|
||
# 1) reason: auth
|
||
# username: default
|
||
# timestamp: 1234567890
|
||
```
|
||
|
||
### 5. 定期轮换密码
|
||
|
||
**建议:每 90 天轮换一次**
|
||
|
||
```bash
|
||
#!/bin/bash
|
||
# rotate-redis-password.sh
|
||
|
||
# 1. 生成新密码
|
||
NEW_PWD=$(openssl rand -base64 32)
|
||
|
||
# 2. 更新 Redis
|
||
kubectl exec -it user-redis-0 -n juwan -c user-redis -- \
|
||
redis-cli -a "$OLD_PWD" ACL SETUSER default ">$NEW_PWD"
|
||
|
||
# 3. 更新 Secret
|
||
kubectl create secret generic user-redis \
|
||
--from-literal=password="$NEW_PWD" \
|
||
-n juwan --dry-run=client -o yaml | kubectl apply -f -
|
||
|
||
# 4. 滚动重启应用
|
||
kubectl rollout restart deployment/user-rpc -n juwan
|
||
|
||
echo "密码已轮换,新密码: $NEW_PWD"
|
||
```
|
||
|
||
### 6. 网络隔离
|
||
|
||
**NetworkPolicy 限制访问:**
|
||
```yaml
|
||
apiVersion: networking.k8s.io/v1
|
||
kind: NetworkPolicy
|
||
metadata:
|
||
name: redis-access-policy
|
||
namespace: juwan
|
||
spec:
|
||
podSelector:
|
||
matchLabels:
|
||
app: user-redis
|
||
policyTypes:
|
||
- Ingress
|
||
ingress:
|
||
# 只允许 user-rpc 访问
|
||
- from:
|
||
- podSelector:
|
||
matchLabels:
|
||
app: user-rpc
|
||
ports:
|
||
- protocol: TCP
|
||
port: 6379
|
||
```
|
||
|
||
---
|
||
|
||
## 📝 总结
|
||
|
||
### 核心发现
|
||
|
||
| 项目 | 值 |
|
||
|-----|---|
|
||
| 认证模式 | ✅ ACL (Redis 6.0+) |
|
||
| 用户名 | `default` |
|
||
| 密码位置 | Secret `user-redis` in namespace `juwan` |
|
||
| 权限级别 | 超级用户(+@all ~* &*) |
|
||
| 是否必须指定用户名 | ❌ 不必须(客户端默认使用 default) |
|
||
|
||
### 最佳实践
|
||
|
||
1. **开发/测试环境**
|
||
```yaml
|
||
# 简单配置即可
|
||
Password: <from_secret>
|
||
# Username 省略
|
||
```
|
||
|
||
2. **生产环境(推荐)**
|
||
```yaml
|
||
# 为每个应用创建独立用户
|
||
Username: app_user
|
||
Password: <dedicated_password>
|
||
# 限制权限和 key 范围
|
||
```
|
||
|
||
3. **连接方式**
|
||
```
|
||
⭐⭐⭐ Sentinel 模式(推荐)
|
||
- 自动故障转移
|
||
- 高可用
|
||
- 只需密码(username 可省略)
|
||
```
|
||
|
||
---
|
||
|
||
**文档版本:** 1.0
|
||
**创建日期:** 2026年2月22日
|
||
**维护者:** DevOps Team
|
||
**下次审查:** 2026年3月22日
|