Skip to content

Commit a58eac0

Browse files
committed
添加ACL设计文档
1 parent 27b688d commit a58eac0

File tree

2 files changed

+612
-0
lines changed

2 files changed

+612
-0
lines changed

docs/ACL_CONFIGURATION.md

Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
# ACL Configuration Guide
2+
3+
This guide explains how to use the ACL (Access Control List) feature in asynq to enable multi-tenant task queue isolation.
4+
5+
## Overview
6+
7+
When ACL is enabled (by setting a tenant name), asynq automatically prefixes queue names with a tenant identifier (username). This ensures that different tenants can only access their own tasks in a shared Redis instance.
8+
9+
**Important**: The `default` queue is intentionally excluded from prefixing and serves as a shared public queue accessible by all tenants. All other queue names will be prefixed.
10+
11+
For example, with tenant `tenant1`:
12+
- Queue `default` → Redis key: `asynq:{default}:pending` (shared)
13+
- Queue `critical` → Redis key: `asynq:{tenant1:critical}:pending` (tenant-specific)
14+
15+
## Configuration
16+
17+
### Server Configuration
18+
19+
To enable ACL in the server (consumer):
20+
21+
```rust
22+
use asynq::config::ServerConfig;
23+
use asynq::server::ServerBuilder;
24+
25+
let server_config = ServerConfig::new()
26+
.acl_tenant("tenant1") // Set the tenant name (ACL is automatically enabled)
27+
.concurrency(4)
28+
// ... other configurations
29+
;
30+
31+
let server = ServerBuilder::new()
32+
.redis_config(redis_config)
33+
.server_config(server_config)
34+
.build()
35+
.await?;
36+
```
37+
38+
### Client Configuration
39+
40+
To enable ACL in the client (producer):
41+
42+
```rust
43+
use asynq::config::ClientConfig;
44+
use asynq::client::Client;
45+
46+
let client_config = ClientConfig::new()
47+
.acl_tenant("tenant1"); // Set the tenant name (ACL is automatically enabled)
48+
49+
let client = Client::with_config(redis_config, client_config).await?;
50+
```
51+
52+
## Automatic Configuration from Redis URL
53+
54+
Both the consumer and producer examples include automatic ACL configuration when using Redis URLs with authentication:
55+
56+
```bash
57+
# Redis URL format: redis://username:password@host:port
58+
export REDIS_URL="redis://tenant1:secure_pass123@localhost:6379"
59+
60+
# When the acl feature is enabled, the username is automatically extracted
61+
cargo run --example=consumer --features=acl
62+
cargo run --example=producer --features=acl
63+
```
64+
65+
## Example Usage
66+
67+
### 1. Create Redis ACL User
68+
69+
First, create a Redis user with appropriate permissions:
70+
71+
```bash
72+
redis-cli
73+
> ACL SETUSER tenant1 on >secure_pass123 ~asynq:{tenant1}:* +@all
74+
```
75+
76+
### 2. Run Consumer with ACL
77+
78+
```bash
79+
export REDIS_URL="redis://tenant1:secure_pass123@localhost:6379"
80+
cargo run --manifest-path=asynq/Cargo.toml --example=consumer --features=acl
81+
```
82+
83+
Output:
84+
```
85+
🚀 Starting Asynq worker server...
86+
🔗 Using Redis URL: redis://tenant1:secure_pass123@localhost:6379
87+
🔐 ACL enabled with tenant: tenant1
88+
🔄 Server is running and waiting for tasks...
89+
```
90+
91+
### 3. Run Producer with ACL
92+
93+
```bash
94+
export REDIS_URL="redis://tenant1:secure_pass123@localhost:6379"
95+
cargo run --manifest-path=asynq/Cargo.toml --example=producer --features=acl
96+
```
97+
98+
Output:
99+
```
100+
🔗 Using Redis URL: redis://tenant1:secure_pass123@localhost:6379
101+
🔐 ACL enabled with tenant: tenant1
102+
Email task enqueued: ID = ...
103+
```
104+
105+
## Queue Name Transformation
106+
107+
When ACL is enabled, queue names are automatically prefixed, **except for the default queue**:
108+
109+
| Original Queue | With ACL (tenant1) | Notes |
110+
|---------------|--------------------| ------|
111+
| `default` | `default` (unchanged) | Shared public queue for all tenants |
112+
| `critical` | `tenant1:critical` | Tenant-specific queue |
113+
| `low` | `tenant1:low` | Tenant-specific queue |
114+
115+
The Redis keys become:
116+
- `asynq:{default}:pending` - Shared across all tenants
117+
- `asynq:{tenant1:critical}:pending` - Tenant-specific
118+
- etc.
119+
120+
**Important**: The `default` queue is intentionally left unprefixed as it serves as a shared public queue accessible by all tenants. If you need tenant-specific default behavior, create a custom queue (e.g., `myqueue`) instead.
121+
122+
## Multi-Tenant Isolation
123+
124+
Different tenants can run simultaneously without interfering with each other:
125+
126+
**Tenant 1:**
127+
```bash
128+
export REDIS_URL="redis://tenant1:pass1@localhost:6379"
129+
cargo run --example=consumer --features=acl # Processes tenant1:* queues
130+
```
131+
132+
**Tenant 2:**
133+
```bash
134+
export REDIS_URL="redis://tenant2:pass2@localhost:6379"
135+
cargo run --example=consumer --features=acl # Processes tenant2:* queues
136+
```
137+
138+
Each tenant can only access their own tasks due to Redis ACL rules.
139+
140+
## Backward Compatibility
141+
142+
When ACL is not enabled (no tenant name set), the system works exactly as before:
143+
144+
```rust
145+
// ACL disabled (default - no tenant name)
146+
let config = ServerConfig::new();
147+
// Queue names remain unchanged: "default", "critical", etc.
148+
```
149+
150+
## API Design
151+
152+
Setting the tenant name automatically enables ACL functionality. There is no separate flag to enable/disable ACL:
153+
154+
```rust
155+
// ACL enabled - tenant name is set
156+
let config_with_acl = ServerConfig::new()
157+
.acl_tenant("tenant1"); // ACL is now active
158+
159+
// ACL disabled - no tenant name
160+
let config_without_acl = ServerConfig::new();
161+
```
162+
163+
## Best Practices
164+
165+
1. **Use meaningful tenant names**: Use usernames or organization identifiers as tenant names
166+
2. **Set up Redis ACL properly**: Ensure each tenant user has appropriate Redis ACL permissions
167+
3. **Consistent tenant names**: Both producer and consumer should use the same tenant name
168+
4. **Use environment variables**: Store Redis URLs with credentials in environment variables, not in code
169+
170+
## Troubleshooting
171+
172+
### Error: "No permissions to access a key"
173+
174+
This error occurs when:
175+
1. Tenant name is not set but Redis requires authentication
176+
2. The Redis ACL user doesn't have permission to access the required keys
177+
178+
**Solution**:
179+
- Set tenant name in both client and server configurations
180+
- Ensure Redis ACL user has permissions for `~asynq:{tenant}:*` keys
181+
182+
### Tasks not being processed
183+
184+
**Check**:
185+
1. Both producer and consumer are using the same tenant name
186+
2. Tenant name is set in both configurations
187+
3. Queue names match between producer and consumer
188+
189+
## Reference
190+
191+
For more information about the ACL module, see:
192+
- `asynq/examples/acl_example.rs` - Complete ACL module demonstration
193+
- `asynq/src/acl/mod.rs` - ACL module implementation
194+
- Redis ACL documentation: https://redis.io/docs/management/security/acl/

0 commit comments

Comments
 (0)