create-dwy 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,12 +1,3 @@
1
- ---
2
- name: server-security
3
- description: 服务器安全部署规则
4
- type: claude-rule
5
- tags: [security, server, docker, nginx]
6
- version: 1.0.0
7
- author: chances
8
- ---
9
-
10
1
  # 服务器安全部署规则
11
2
 
12
3
  所有项目部署到云服务器时必须遵循以下规则。如果任何操作违反这些规则,必须**立即提示用户并获得明确确认**后才能继续。
@@ -37,7 +28,40 @@ backend:
37
28
  - "{port}:{port}"
38
29
  ```
39
30
 
40
- ## 二、凭证与密钥管理
31
+ ## 二、防火墙
32
+
33
+ ### 强制规则
34
+ - 生产服务器**必须启用防火墙**(ufw 或 iptables)
35
+ - **只开放业务必需端口**,其余全部关闭
36
+
37
+ ### 最小端口策略
38
+
39
+ | 端口 | 用途 | 开放方式 |
40
+ |------|------|---------|
41
+ | 80 | HTTP(Nginx) | `ufw allow 80/tcp` |
42
+ | 443 | HTTPS(Nginx) | `ufw allow 443/tcp` |
43
+ | SSH 非标端口 | SSH 远程管理 | `ufw allow {ssh_port}/tcp` |
44
+
45
+ **禁止开放的端口:**
46
+ - 5432(PostgreSQL)
47
+ - 6379(Redis)
48
+ - 9000(MinIO)
49
+ - 8000-9999(后端应用)
50
+
51
+ ### 配置示例
52
+ ```bash
53
+ ufw default deny incoming
54
+ ufw default allow outgoing
55
+ ufw allow 80/tcp
56
+ ufw allow 443/tcp
57
+ ufw allow {ssh_port}/tcp
58
+ ufw enable
59
+ ```
60
+
61
+ ### 违规检测
62
+ 如果在部署脚本或命令中发现 `ufw allow 5432` 等数据库端口开放命令,必须立即提示用户。
63
+
64
+ ## 三、凭证与密钥管理
41
65
 
42
66
  ### 强制规则
43
67
  - **禁止在代码中硬编码**数据库密码、API Key、Secret 等凭证
@@ -52,7 +76,7 @@ backend:
52
76
  - API Key 出现在非 `.env` 文件中
53
77
  - SSH 密码、私钥路径出现在代码或文档中
54
78
 
55
- ## 三、Nginx 安全配置(最小访问控制)
79
+ ## 四、Nginx 安全配置
56
80
 
57
81
  ### 核心原则
58
82
  **只开放业务必需的路径和方法,其余一律拒绝。**
@@ -64,19 +88,39 @@ backend:
64
88
  **1. 频率限制(http 块)**
65
89
  ```nginx
66
90
  limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
91
+ limit_req_zone $binary_remote_addr zone=login_limit:10m rate=3r/m;
67
92
  limit_req_zone $binary_remote_addr zone=page_limit:10m rate=30r/s;
68
93
  limit_conn_zone $binary_remote_addr zone=conn_limit:10m;
69
94
  ```
70
95
 
96
+ 注意:`limit_req_zone` 和 `limit_conn_zone` **必须放在 `http` 块**,不能放在 `server` 块。
97
+
71
98
  **2. 安全响应头(server 块)**
72
99
  ```nginx
73
100
  add_header X-Frame-Options "SAMEORIGIN" always;
74
101
  add_header X-Content-Type-Options "nosniff" always;
75
102
  add_header X-XSS-Protection "1; mode=block" always;
76
103
  add_header Referrer-Policy "strict-origin-when-cross-origin" always;
104
+ add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'" always;
105
+ add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
106
+ ```
107
+
108
+ **3. HTTPS 强制(server 块)**
109
+ ```nginx
110
+ # HTTP → HTTPS 重定向
111
+ server {
112
+ listen 80;
113
+ server_name example.com;
114
+ return 301 https://$server_name$request_uri;
115
+ }
77
116
  ```
78
117
 
79
- **3. 全局限制(server 块)**
118
+ 生产环境**必须启用 HTTPS**,使用 certbot 自动续期:
119
+ ```bash
120
+ certbot --nginx -d example.com
121
+ ```
122
+
123
+ **4. 全局限制(server 块)**
80
124
  ```nginx
81
125
  client_max_body_size 1m; # 无文件上传业务时默认 1m
82
126
  client_body_timeout 10s;
@@ -84,7 +128,7 @@ client_header_timeout 10s;
84
128
  limit_conn conn_limit 20;
85
129
  ```
86
130
 
87
- **4. 敏感文件屏蔽(最高优先级)**
131
+ **5. 敏感文件屏蔽(最高优先级)**
88
132
  ```nginx
89
133
  location ~ /\. {
90
134
  deny all;
@@ -97,7 +141,7 @@ location ~* \.(env|git|xlsx|sql|log|bak|dump|tar|gz|zip|csv)$ {
97
141
  }
98
142
  ```
99
143
 
100
- **5. API 路径最小化**
144
+ **6. API 路径最小化**
101
145
  ```nginx
102
146
  location /api {
103
147
  limit_except GET POST {
@@ -108,14 +152,44 @@ location /api {
108
152
  }
109
153
  ```
110
154
 
111
- **6. 攻击路径屏蔽**
155
+ **7. 登录接口专用限流**
112
156
  ```nginx
113
- location ~ ^/(admin|phpmyadmin|wp-admin|cgi-bin|actuator|console|debug) {
157
+ location /api/auth/login {
158
+ limit_req zone=login_limit burst=5 nodelay;
159
+ proxy_pass http://127.0.0.1:{backend_port};
160
+ proxy_set_header Host $host;
161
+ proxy_set_header X-Real-IP $remote_addr;
162
+ }
163
+ ```
164
+
165
+ **8. 攻击路径屏蔽**
166
+ ```nginx
167
+ location ~ ^/(admin|phpmyadmin|wp-admin|cgi-bin|actuator|console|debug|\.well-known/security\.txt) {
114
168
  deny all;
115
169
  return 404;
116
170
  }
117
171
  ```
118
172
 
173
+ **9. 扫描器 User-Agent 屏蔽(http 块)**
174
+ ```nginx
175
+ map $http_user_agent $is_scanner {
176
+ default 0;
177
+ ~*sqlmap 1;
178
+ ~*nmap 1;
179
+ ~*nikto 1;
180
+ ~*dirbuster 1;
181
+ ~*gobuster 1;
182
+ ~*masscan 1;
183
+ ~*zgrab 1;
184
+ ~*python-requests 1;
185
+ }
186
+
187
+ # 在 server 块中
188
+ if ($is_scanner) {
189
+ return 403;
190
+ }
191
+ ```
192
+
119
193
  ### 违规检测
120
194
  如果 Nginx 配置中出现以下情况,必须提示用户:
121
195
  - 缺少 `limit_req` 频率限制
@@ -123,6 +197,8 @@ location ~ ^/(admin|phpmyadmin|wp-admin|cgi-bin|actuator|console|debug) {
123
197
  - API 路径允许 PUT/DELETE 但业务不需要
124
198
  - 缺少 `location ~ /\.` 敏感文件屏蔽
125
199
  - 使用 `proxy_pass` 但未设置 `proxy_connect_timeout`
200
+ - 生产环境未启用 HTTPS
201
+ - 缺少登录接口专用限流
126
202
 
127
203
  ### 部署后必须验证
128
204
  ```bash
@@ -131,8 +207,9 @@ curl -s -o /dev/null -w "%{http_code}" http://{server}/.git/config
131
207
  curl -s -o /dev/null -w "%{http_code}" http://{server}/.env
132
208
 
133
209
  # 2. 安全头存在
134
- curl -sI http://{server}/ | grep -i "x-frame-options"
135
- curl -sI http://{server}/ | grep -i "x-content-type-options"
210
+ curl -sI https://{server}/ | grep -i "x-frame-options"
211
+ curl -sI https://{server}/ | grep -i "x-content-type-options"
212
+ curl -sI https://{server}/ | grep -i "strict-transport-security"
136
213
 
137
214
  # 3. 非法方法被拒绝
138
215
  curl -s -o /dev/null -w "%{http_code}" -X DELETE http://{server}/api/health
@@ -140,19 +217,49 @@ curl -s -o /dev/null -w "%{http_code}" -X DELETE http://{server}/api/health
140
217
  # 4. 攻击路径被屏蔽
141
218
  curl -s -o /dev/null -w "%{http_code}" http://{server}/phpmyadmin
142
219
 
143
- # 5. 正常服务可用
220
+ # 5. 扫描器 UA 被拒绝
221
+ curl -s -o /dev/null -w "%{http_code}" -A "sqlmap/1.0" http://{server}/
222
+
223
+ # 6. 登录限流生效
224
+ for i in {1..10}; do curl -s -o /dev/null -w "%{http_code}\n" -X POST http://{server}/api/auth/login; done
225
+
226
+ # 7. 正常服务可用
144
227
  curl -s http://{server}/api/health
145
228
  ```
146
229
 
147
- ## 四、Docker 容器安全
230
+ ## 五、Docker 容器安全
148
231
 
149
232
  ### 强制规则
150
- - Dockerfile 必须使用非 root 用户运行应用
233
+ - Dockerfile **必须使用非 root 用户**运行应用
151
234
  - 必须创建 `.dockerignore` 排除 `.env`、`.git`、`cache/`、`__pycache__`
152
235
  - Python 项目设置 `ENV PYTHONUNBUFFERED=1`
153
236
  - Node.js 项目设置 `ENV NODE_ENV=production`
237
+ - **使用最小化基础镜像**(`python:3.11-slim`,不用 `python:3.11`)
238
+ - **禁止在镜像中安装不必要的工具**(ssh、telnet、curl 等调试工具生产不需要)
239
+ - 容器文件系统**建议只读挂载**(`read_only: true`),需要写入的目录用 `tmpfs` 或 volume
240
+
241
+ ### Dockerfile 安全示例
242
+ ```dockerfile
243
+ FROM python:3.11-slim
244
+
245
+ # 只安装必要的编译依赖,安装后清理
246
+ RUN apt-get update && apt-get install -y --no-install-recommends gcc \
247
+ && rm -rf /var/lib/apt/lists/*
154
248
 
155
- ## 五、CORS 配置
249
+ WORKDIR /app
250
+ COPY pyproject.toml .
251
+ RUN pip install --no-cache-dir .
252
+
253
+ COPY src/ src/
254
+
255
+ # 创建非 root 用户
256
+ RUN useradd -m appuser
257
+ USER appuser
258
+
259
+ CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
260
+ ```
261
+
262
+ ## 六、CORS 配置
156
263
 
157
264
  ### 强制规则
158
265
  - **禁止生产环境使用 `*` 通配符**
@@ -161,28 +268,12 @@ curl -s http://{server}/api/health
161
268
 
162
269
  ```
163
270
  # ✅ 正确 — 明确列出
164
- ALLOWED_ORIGINS=http://localhost,http://{server_ip}
271
+ ALLOWED_ORIGINS=["https://example.com","https://www.example.com"]
165
272
 
166
273
  # ❌ 违规
167
274
  ALLOWED_ORIGINS=*
168
275
  ```
169
276
 
170
- ## 六、API 安全
171
-
172
- ### 强制规则
173
- - 敏感数据查询接口必须有限流(推荐 5-10 次/分钟/IP)
174
- - 错误消息不得回显用户输入内容(防止信息泄露)
175
- - 不得提供批量查询/列表接口暴露全量数据
176
- - 如项目含认证机制,未配置时必须在日志中输出 WARNING
177
-
178
- ```
179
- # ✅ 正确 — 通用错误消息
180
- return {"message": "记录不存在"}
181
-
182
- # ❌ 违规 — 回显用户输入
183
- return {"message": f"未找到: {user_input}"}
184
- ```
185
-
186
277
  ## 七、数据安全
187
278
 
188
279
  ### 强制规则
@@ -240,7 +331,7 @@ LoginGraceTime 30 # 登录超时 30 秒
240
331
  ```
241
332
 
242
333
  ### 配合 fail2ban
243
- 建议服务器配置 fail2ban 防暴力破解:
334
+ 服务器**必须配置** fail2ban 防暴力破解:
244
335
  ```
245
336
  # /etc/fail2ban/jail.local
246
337
  [sshd]
@@ -248,6 +339,14 @@ enabled = true
248
339
  port = 66
249
340
  maxretry = 5
250
341
  bantime = 3600
342
+
343
+ # Nginx 暴力请求封禁
344
+ [nginx-limit-req]
345
+ enabled = true
346
+ filter = nginx-limit-req
347
+ logpath = /var/log/nginx/error.log
348
+ maxretry = 10
349
+ bantime = 600
251
350
  ```
252
351
 
253
352
  ### 部署操作规范
@@ -262,7 +361,42 @@ Host myserver
262
361
  ```
263
362
  - 部署脚本中使用 `ssh myserver` 而非完整连接命令
264
363
 
265
- ## 十、数据持久化与防删除
364
+ ## 十、系统安全加固
365
+
366
+ ### 自动安全更新
367
+ 生产服务器**必须启用自动安全更新**:
368
+ ```bash
369
+ apt install -y unattended-upgrades
370
+ dpkg-reconfigure -plow unattended-upgrades
371
+ ```
372
+
373
+ ### 文件权限
374
+ | 文件 | 权限 | 命令 |
375
+ |------|------|------|
376
+ | `.env` | 600(仅所有者读写) | `chmod 600 .env` |
377
+ | SSH 私钥 | 600 | `chmod 600 ~/.ssh/id_*` |
378
+ | `authorized_keys` | 644 | `chmod 644 ~/.ssh/authorized_keys` |
379
+ | 应用目录 | 755 | `chmod 755 /opt/myapp` |
380
+
381
+ **禁止出现 777 权限。** 如果在部署脚本中发现 `chmod 777`,必须立即提示用户。
382
+
383
+ ### 日志监控
384
+ 生产服务器必须定期检查以下日志:
385
+ ```bash
386
+ # SSH 登录记录
387
+ journalctl -u sshd --since "1 hour ago"
388
+
389
+ # Nginx 错误日志
390
+ tail -100 /var/log/nginx/error.log
391
+
392
+ # 被 fail2ban 封禁的 IP
393
+ fail2ban-client status sshd
394
+
395
+ # Docker 容器日志
396
+ docker compose logs --tail 100 backend
397
+ ```
398
+
399
+ ## 十一、数据持久化与防删除
266
400
 
267
401
  ### 核心原则
268
402
  **所有业务数据必须通过 Docker Named Volume 持久化。任何可能导致数据丢失的操作,必须提前说明原因并获得用户确认。**
@@ -277,8 +411,7 @@ Host myserver
277
411
  # ✅ 正确 — Named Volume 持久化
278
412
  db:
279
413
  volumes:
280
- - dbdata:/var/lib/postgresql/data # PostgreSQL
281
- # 或 - dbdata:/var/lib/mysql # MySQL
414
+ - dbdata:/var/lib/postgresql/data
282
415
  restart: unless-stopped
283
416
 
284
417
  volumes:
@@ -315,57 +448,58 @@ db:
315
448
  4. **提供替代方案**(如有):是否有不删除数据的方式达到目的
316
449
  5. **等待用户明确回复"确认"**后才能执行
317
450
 
318
- 示例对话:
319
- ```
320
- AI: 我需要执行 `docker compose down -v` 来重建数据库。
321
- 原因:数据库 schema 发生了不兼容的变更。
322
- 影响:将删除数据库 Volume,所有业务数据将永久丢失。
323
- 替代方案:可以用 ALTER TABLE 迁移 schema,不需要删库。
324
- 是否确认执行?
325
- ```
326
-
327
- ### 备份建议
328
- 生产环境建议配置定期备份:
451
+ ### 备份
452
+ 生产环境**必须配置定期备份**:
329
453
  ```bash
330
- # PostgreSQL 备份
454
+ # PostgreSQL 每日备份(建议 cron 每天凌晨执行)
331
455
  docker exec <db_container> pg_dump -U <user> <database> > backup_$(date +%Y%m%d).sql
332
456
 
333
- # MySQL 备份
334
- docker exec <db_container> mysqldump -u <user> -p<password> <database> > backup_$(date +%Y%m%d).sql
335
-
336
- # Volume 备份
337
- docker run --rm -v <volume_name>:/data -v $(pwd):/backup alpine tar czf /backup/volume_$(date +%Y%m%d).tar.gz /data
457
+ # 备份文件不得放在 Web 可访问目录
458
+ # 建议放在 /var/backups/ 下,定期清理超过 30 天的备份
459
+ find /var/backups/ -name "backup_*.sql" -mtime +30 -delete
338
460
  ```
339
461
 
340
- ## 十一、部署后检查清单
462
+ ## 十二、部署后检查清单
341
463
 
342
464
  每次部署完成后,必须执行以下验证:
343
465
 
344
466
  ```bash
345
- # 1. 数据库端口不可外部访问
346
- timeout 3 nc -z {server_ip} {db_port} && echo "FAIL" || echo "PASS"
467
+ # 1. 防火墙状态
468
+ ufw status | grep -E "80|443|{ssh_port}"
469
+
470
+ # 2. 数据库端口不可外部访问
471
+ timeout 3 nc -z {server_ip} 5432 && echo "FAIL" || echo "PASS"
472
+ timeout 3 nc -z {server_ip} 6379 && echo "FAIL" || echo "PASS"
347
473
 
348
- # 2. 后端端口不可外部直连
474
+ # 3. 后端端口不可外部直连
349
475
  timeout 3 nc -z {server_ip} {backend_port} && echo "FAIL" || echo "PASS"
350
476
 
351
- # 3. 敏感文件不可下载
352
- curl -s -o /dev/null -w "%{http_code}" http://{server}/.git/config # 必须 404
353
- curl -s -o /dev/null -w "%{http_code}" http://{server}/.env # 必须 404
477
+ # 4. 敏感文件不可下载
478
+ curl -s -o /dev/null -w "%{http_code}" https://{server}/.git/config # 必须 404
479
+ curl -s -o /dev/null -w "%{http_code}" https://{server}/.env # 必须 404
354
480
 
355
- # 4. 正常服务可用
356
- curl -s http://{server}/api/health # 必须返回 200
481
+ # 5. HTTPS 工作正常
482
+ curl -sI https://{server}/ | grep "strict-transport-security" # 必须有输出
357
483
 
358
- # 5. 安全头存在
359
- curl -sI http://{server}/ | grep -i "x-frame-options" # 必须有输出
484
+ # 6. 安全头完整
485
+ curl -sI https://{server}/ | grep -i "x-frame-options"
486
+ curl -sI https://{server}/ | grep -i "x-content-type-options"
487
+ curl -sI https://{server}/ | grep -i "content-security-policy"
360
488
 
361
- # 6. 非法方法被拒绝
362
- curl -s -o /dev/null -w "%{http_code}" -X DELETE http://{server}/api/health # 必须 403/405
489
+ # 7. 非法方法被拒绝
490
+ curl -s -o /dev/null -w "%{http_code}" -X DELETE https://{server}/api/health # 必须 403/405
363
491
 
364
- # 7. Volume 存在且数据完整
492
+ # 8. Volume 存在且数据完整
365
493
  docker volume ls | grep {volume_name} # 必须存在
494
+
495
+ # 9. fail2ban 运行中
496
+ fail2ban-client status sshd
497
+
498
+ # 10. 正常服务可用
499
+ curl -s https://{server}/api/health # 必须返回 200
366
500
  ```
367
501
 
368
- ## 十二、违规处理流程
502
+ ## 十三、违规处理流程
369
503
 
370
504
  当 AI 检测到操作违反以上任何规则时:
371
505