security: rustdesk出库+ignore;nginx静态location补齐安全头(修add_header继承丢失);tasks.py原子写;backup.sh可选异地;拍板清单落盘
vscode-planning-ci / planning-smoke (push) Waiting to run
vscode-planning-ci / planning-smoke (push) Waiting to run
This commit is contained in:
+3
-1
@@ -6,6 +6,9 @@
|
|||||||
(服务器数据)重要!!!禁止上传git/
|
(服务器数据)重要!!!禁止上传git/
|
||||||
*.pem
|
*.pem
|
||||||
id_ed25519*
|
id_ed25519*
|
||||||
|
|
||||||
|
# 远控配置(含中转服务器地址与永久密码哈希,绝不入库)
|
||||||
|
rustdesk-config-export/
|
||||||
.dsh/
|
.dsh/
|
||||||
.dsh-runtime-backup/
|
.dsh-runtime-backup/
|
||||||
.pnpm-store/
|
.pnpm-store/
|
||||||
@@ -14,7 +17,6 @@ node_modules/
|
|||||||
android-chunyu/
|
android-chunyu/
|
||||||
chunyu_project/
|
chunyu_project/
|
||||||
chunyu_project_react/
|
chunyu_project_react/
|
||||||
node_modules/
|
|
||||||
shots/
|
shots/
|
||||||
docker.env
|
docker.env
|
||||||
backups/
|
backups/
|
||||||
|
|||||||
+15
-3
@@ -21,8 +21,10 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import json
|
import json
|
||||||
|
import os
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
|
import tempfile
|
||||||
from datetime import date
|
from datetime import date
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
@@ -56,9 +58,19 @@ def load_state() -> dict:
|
|||||||
|
|
||||||
|
|
||||||
def save_state(state: dict) -> None:
|
def save_state(state: dict) -> None:
|
||||||
STATE.write_text(
|
# 原子写:先落同目录临时文件再 os.replace,避免并发写或中断产生半截 state.json
|
||||||
json.dumps(state, ensure_ascii=False, indent=2) + "\n", encoding="utf-8"
|
payload = json.dumps(state, ensure_ascii=False, indent=2) + "\n"
|
||||||
)
|
fd, tmp = tempfile.mkstemp(dir=str(STATE.parent), prefix=".state-", suffix=".tmp")
|
||||||
|
try:
|
||||||
|
with os.fdopen(fd, "w", encoding="utf-8") as f:
|
||||||
|
f.write(payload)
|
||||||
|
os.replace(tmp, STATE)
|
||||||
|
except BaseException:
|
||||||
|
try:
|
||||||
|
os.unlink(tmp)
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
raise
|
||||||
|
|
||||||
|
|
||||||
def effective_status(task: dict, state: dict) -> str:
|
def effective_status(task: dict, state: dict) -> str:
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
# 安全遗留拍板清单 / SECURITY_DECISIONS.md
|
||||||
|
日期: 2026-09-25 · 前置: 全局检测报告(架构/安全/业务/部署四方向)
|
||||||
|
|
||||||
|
## 本轮已修(仅本仓库,不触碰线上运行)
|
||||||
|
1. **rustdesk-config-export 出库 + ignore**:远控中转服务器地址与永久密码哈希不再随 HEAD 分发(历史对象仍在,见 D1)。磁盘文件已保留。
|
||||||
|
2. **nginx-docker.conf**:4 个自带 `add_header` 的 location(/api-directory/、/assets、静态资源、/index.html)补齐 nosniff / X-Frame-Options / Referrer-Policy,修复 add_header 层级覆盖继承导致的静态响应裸奔。
|
||||||
|
- 生效条件:服务器上 `docker compose restart frontend`(pull 本仓不会热生效,线上运行不受影响)。
|
||||||
|
3. **PLANNING/tasks.py**:state.json 改为临时文件 + `os.replace` 原子写(防并发/中断写坏;多进程同时读改写的丢失更新窗口仍在,量级可接受)。
|
||||||
|
4. **infra/backup.sh**:新增可选 `BACKUP_REMOTE_DIR`(设置后自动 rsync 异地副本,不设则行为不变)。
|
||||||
|
5. **B6 产物出库复核**:当前跟踪的 png/log/tar.gz 为 0,88 项实际已在 6d22fb1 完成,SECURITY_FIX 文档该条滞后。
|
||||||
|
|
||||||
|
## 待拍板(不可逆 / 涉及线上或远程仓库,一句话确认即可执行对应项)
|
||||||
|
### D1 git 历史清理(docker.env 真实密码 + rustdesk 配置仍可从历史取回)
|
||||||
|
```bash
|
||||||
|
pip install git-filter-repo
|
||||||
|
git filter-repo --invert-paths --path docker.env --path rustdesk-config-export --force
|
||||||
|
git remote add origin https://gitea.mymoyu.top/root/vscode-workbench.git # filter-repo 会移除 remote
|
||||||
|
git push origin master --force
|
||||||
|
```
|
||||||
|
影响:改写远程历史,所有 clone 需重新拉取;需确认无其他协作者持有旧 clone。
|
||||||
|
|
||||||
|
### D2 RustDesk 服务器侧加固(需操作远控,改即生效)
|
||||||
|
- 被控端更换永久密码(客户端 设置→安全→永久密码),更换后本机 rustdesk-config-export 重新导出才会更新。
|
||||||
|
- 103.40.14.100 的 hbbs/hbbr(51116-51118)加来源 IP 白名单或防火墙限制。
|
||||||
|
|
||||||
|
### D3 线上 B1-B5(沿用 SECURITY_FIX_SPEC.md deferred 清单,均需停机/线上授权)
|
||||||
|
B1 线上密钥全量轮换(含 DJANGO_SECRET_KEY,会使现有 session 失效) / B2 生产 TLS+HSTS / B3 服务器防火墙收紧 / B5 容器 USER 非 root。
|
||||||
|
|
||||||
|
### D4 model-registry.json 分值基准(六维合计 42 vs total_max=36)
|
||||||
|
修哪边决定 grade 换算基准(dim5<50% veto 的分母含义不同),需评分口径拍板后改,未拍板前不动。
|
||||||
|
|
||||||
|
### D5 主工程源码仓库归属
|
||||||
|
chunyu_project / chunyu_project_react 不在本仓版本控制与备份范围(.gitignore),compose build 与 task-manifest 均指向本地目录。需确认是否已有独立 repo;若无,建仓并纳入备份。
|
||||||
|
|
||||||
|
## 注意事项
|
||||||
|
- 本轮提交推送后,其他机器 `git pull` 会从其工作区移除 rustdesk-config-export 目录(git rm --cached 语义);若服务器上有引用请先拷贝。
|
||||||
|
- nginx-docker.conf 属已跟踪文件,本仓修改不会自动影响线上;需在服务器手动 restart frontend 才生效。
|
||||||
@@ -12,5 +12,10 @@ sha256sum "$ARCHIVE" > "$ARCHIVE.sha256"
|
|||||||
# 保留策略:只留最近 7 个归档
|
# 保留策略:只留最近 7 个归档
|
||||||
ls -1t "$OUT_DIR"/planning-backup-*.tar.gz 2>/dev/null | tail -n +8 | xargs -r rm -f
|
ls -1t "$OUT_DIR"/planning-backup-*.tar.gz 2>/dev/null | tail -n +8 | xargs -r rm -f
|
||||||
ls -1t "$OUT_DIR"/planning-backup-*.tar.gz.sha256 2>/dev/null | tail -n +8 | xargs -r rm -f
|
ls -1t "$OUT_DIR"/planning-backup-*.tar.gz.sha256 2>/dev/null | tail -n +8 | xargs -r rm -f
|
||||||
|
# 可选异地副本:设置 BACKUP_REMOTE_DIR(形如 user@host:/backups/planning)即同步,不设则跳过
|
||||||
|
if [ -n "${BACKUP_REMOTE_DIR:-}" ] && command -v rsync >/dev/null 2>&1; then
|
||||||
|
rsync -a "$OUT_DIR"/planning-backup-*.tar.gz "$OUT_DIR"/planning-backup-*.tar.gz.sha256 "$BACKUP_REMOTE_DIR/"
|
||||||
|
echo "offsite: $BACKUP_REMOTE_DIR"
|
||||||
|
fi
|
||||||
echo "backup: $ARCHIVE"
|
echo "backup: $ARCHIVE"
|
||||||
cat "$ARCHIVE.sha256"
|
cat "$ARCHIVE.sha256"
|
||||||
|
|||||||
@@ -15,6 +15,10 @@ server {
|
|||||||
try_files $uri =404;
|
try_files $uri =404;
|
||||||
expires 1y;
|
expires 1y;
|
||||||
add_header Cache-Control "public, max-age=31536000, immutable" always;
|
add_header Cache-Control "public, max-age=31536000, immutable" always;
|
||||||
|
# nginx 的 add_header 按层级整体覆盖继承:自带 add_header 的 location 必须重复安全头
|
||||||
|
add_header X-Content-Type-Options "nosniff" always;
|
||||||
|
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||||
|
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
|
||||||
types {
|
types {
|
||||||
image/avif avif;
|
image/avif avif;
|
||||||
image/gif gif;
|
image/gif gif;
|
||||||
@@ -237,6 +241,9 @@ server {
|
|||||||
location ~* ^/assets/.*\.(js|css|png|jpg|jpeg|gif|webp|avif|ico|svg|woff|woff2|ttf|eot)$ {
|
location ~* ^/assets/.*\.(js|css|png|jpg|jpeg|gif|webp|avif|ico|svg|woff|woff2|ttf|eot)$ {
|
||||||
expires 1y;
|
expires 1y;
|
||||||
add_header Cache-Control "public, immutable";
|
add_header Cache-Control "public, immutable";
|
||||||
|
add_header X-Content-Type-Options "nosniff" always;
|
||||||
|
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||||
|
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
|
||||||
access_log off;
|
access_log off;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -244,12 +251,18 @@ server {
|
|||||||
location ~* \.(png|jpg|jpeg|gif|webp|ico|svg|woff|woff2|ttf|eot)$ {
|
location ~* \.(png|jpg|jpeg|gif|webp|ico|svg|woff|woff2|ttf|eot)$ {
|
||||||
expires 7d;
|
expires 7d;
|
||||||
add_header Cache-Control "public";
|
add_header Cache-Control "public";
|
||||||
|
add_header X-Content-Type-Options "nosniff" always;
|
||||||
|
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||||
|
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
|
||||||
access_log off;
|
access_log off;
|
||||||
}
|
}
|
||||||
|
|
||||||
# index.html 不缓存,保证发版后立即生效
|
# index.html 不缓存,保证发版后立即生效
|
||||||
location = /index.html {
|
location = /index.html {
|
||||||
add_header Cache-Control "no-cache, no-store, must-revalidate";
|
add_header Cache-Control "no-cache, no-store, must-revalidate";
|
||||||
|
add_header X-Content-Type-Options "nosniff" always;
|
||||||
|
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||||
|
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
|
||||||
}
|
}
|
||||||
|
|
||||||
# SPA 路由回退(放在最后,避免拦截上面的 API 请求)
|
# SPA 路由回退(放在最后,避免拦截上面的 API 请求)
|
||||||
|
|||||||
Reference in New Issue
Block a user