### 一、Nginx 配置文件(3 个) #### 1. [nginx-static-production.conf](computer:///sessions/69e889f08f8c93c2514713f1/workspace/nginx-static-production.conf) **用途**: 生产环境完整 Nginx 主配置文件(含反向代理),是 `docker-compose-nginx.yml` 挂载的配置。 **关键内容**: - **全局配置**: `worker_processes auto`,使用 epoll,连接数 1024,Docker DNS 解析器 `127.0.0.11` - **安全头**: X-Frame-Options、X-XSS-Protection、X-Content-Type-Options、Referrer-Policy - **限流**: `limit_req_zone` 全局限速 100r/s - **Gzip 压缩**: 级别 6,覆盖 text/css/json/javascript/xml 等类型 - **Upstream 定义**: `gitea_app` (gitea:3000)、`jenkins_app` (jenkins:8080) - **虚拟主机**: - `novalon.cn / www.novalon.cn` -- 主站静态文件服务,根目录 `/var/www/novalon`,SSL/TLS 1.2+1.3,HSTS 2年,`_next/static/` 和字体/图片缓存 1 年,`try_files $uri $uri.html $uri/ /404.html` - `git.f.novalon.cn` -- 反向代理到 Gitea(端口 3000) - `ci.f.novalon.cn` -- 反向代理到 Jenkins(端口 8080),超时 60s - **SSL**: Let's Encrypt ACME challenge 支持,独立子域名证书路径 #### 2. [nginx-static.conf](computer:///sessions/69e889f08f8c93c2514713f1/workspace/nginx-static.conf) **用途**: 简化版 Nginx 配置,仅包含主站静态服务,用于 `Dockerfile` 构建镜像。 **关键内容**: - 仅配置 `novalon.cn / www.novalon.cn`,无 Gitea/Jenkins 反向代理 - 静态根目录 `/usr/share/nginx/html` - SSL 配置较简化(`ssl_ciphers HIGH:!aNULL:!MD5`) - 包含与 production 版相同的安全头、Gzip、静态资源缓存策略 - 额外包含 `Permissions-Policy` 头(禁用摄像头/麦克风/地理位置) #### 3. [nginx-internal.conf](computer:///sessions/69e889f08f8c93c2514713f1/workspace/nginx-internal.conf) **用途**: 内部/开发环境配置,监听端口 3000,无 SSL。 **关键内容**: - 监听 `localhost:3000`,根目录 `/var/www/novalon` - 无 SSL、无 HSTS、无 ACME challenge - 包含基本的 Gzip、安全头、静态资源缓存 - 用于 `Dockerfile.static` 构建的内部容器 --- ### 二、Docker 相关文件(5 个) #### 4. [Dockerfile](computer:///sessions/69e889f08f8c93c2514713f1/workspace/Dockerfile) **用途**: 多阶段构建 -- Node.js 编译 + Nginx 静态服务。 **关键内容**: - **构建阶段**: `node:20-alpine`,`npm ci` 安装依赖,`npm run build` 构建 - **运行阶段**: `nginx:alpine`,将 `dist/` 复制到 `/usr/share/nginx/html`,挂载 `nginx-static.conf` - 暴露端口 80 #### 5. [Dockerfile.static](computer:///sessions/69e889f08f8c93c2514713f1/workspace/Dockerfile.static) **用途**: 纯静态文件 Nginx 容器(无构建阶段),用于内部部署。 **关键内容**: - 基础镜像 `nginx:alpine` - 将 `html/` 目录复制到 `/var/www/novalon` - 挂载 `nginx-internal.conf` 到 `/etc/nginx/conf.d/default.conf` - 暴露端口 3000 #### 6. [docker-compose.yml](computer:///sessions/69e889f08f8c93c2514713f1/workspace/docker-compose.yml) **用途**: 基础 Docker Compose 配置,使用 `Dockerfile` 构建。 **关键内容**: - 服务名 `novalon-website`,镜像标签 `novalon-website:1.0.0` - 容器名 `novalon-website` - 端口映射 `80:80` 和 `443:443` - 挂载 `nginx-static.conf` 和 `ssl/` 目录(只读) #### 7. [docker-compose.server.yml](computer:///sessions/69e889f08f8c93c2514713f1/workspace/docker-compose.server.yml) **用途**: 服务器端 Docker Compose 配置,使用 `Dockerfile.static` 构建。 **关键内容**: - 服务名 `novalon-website`,镜像标签 `novalon-website:latest` - 容器名 `novalon-website` - 端口映射 `3000:3000` - 使用外部网络 `novalon-network`(bridge 驱动) #### 8. [docker-compose-nginx.yml](computer:///sessions/69e889f08f8c93c2514713f1/workspace/docker-compose-nginx.yml) **用途**: 独立 Nginx 反向代理容器配置(生产环境网关)。 **关键内容**: - 服务名 `nginx`,使用官方 `nginx:alpine` 镜像 - **容器名 `novalon-nginx-secure`** - 端口映射 `80:80` 和 `443:443` - 挂载: - `nginx-static-production.conf` -> `/etc/nginx/nginx.conf`(只读) - `ssl/` -> `/etc/nginx/ssl`(只读) - `logs/` -> `/var/log/nginx` - `../certbot` -> `/var/www/certbot`(ACME 验证) - `../novalon-website/dist` -> `/var/www/novalon`(只读,Novalon 主站静态文件) - `../novalon-static` -> `/var/www/novavis-landing`(只读,Novavis 落地页) - 使用外部网络 `novalon-network` --- ### 三、部署脚本(1 个统一入口) #### 9. [scripts/deploy.sh](../scripts/deploy.sh) **用途**: 统一发布脚本(唯一发布入口),负责构建、发布、回滚与状态查看。 **关键内容**: - 默认服务器 `139.155.109.62`(root 用户),远程目标目录 `/home/novalon/docker-app/novalon-website/dist`(生产 Nginx 实际挂载目录) - 命令: - `build`:构建静态产物 - `deploy`:构建 + 远程发布(备份旧版本 -> rsync 上传 -> 设置权限 -> 重载 Nginx -> 验证页面) - `rollback`:回滚到最近一次远程备份 - `status`:查看生产环境发布状态 - 支持 `--skip-build`、`--auto-rollback`、`--dry-run` 及服务器/域名/备份保留数等参数 - 支持环境变量覆盖:`SERVER_IP`、`SERVER_USER`、`DEPLOY_ROOT`、`PROJECT_NAME`、`NGINX_CONTAINER`、`DOMAIN`、`BACKUP_RETENTION_COUNT`、`DIST_DIR` --- ### 四、引用 "novalon-nginx" / "novalon-website" 的其他文件(29 个) 除上述 9 个核心文件外,以下文件也引用了这两个关键词,按类别归纳: | 类别 | 文件路径 | |------|----------| | **文档** | `README.md`, `DEPLOYMENT.md`, `docs/deployment.md`, `docs/deployment/DEPLOYMENT.md`, `docs/deployment/phase1-deployment-guide.md`, `docs/deployment/rollback-procedure.md`, `docs/development/getting-started.md`, `docs/STRUCTURE_PLAN.md`, `docs/PRODUCTION_RELEASE_REPORT.md`, `docs/PRODUCTION_DEPLOYMENT_LIGHTWEIGHT.md`, `docs/PRODUCTION_DEPLOYMENT.md`, `docs/PERFORMANCE_OPTIMIZATION.md`, `docs/OPTIMIZATION_REPORT.md`, `docs/MONITORING_SETUP.md`, `docs/MONITORING_QUICKSTART.md`, `docs/HMR-ERROR-SOLUTIONS.md`, `docs/CDN_CONFIGURATION.md` | | **脚本** | `scripts/ssl-wildcard-dns.sh`, `scripts/ssl-individual-http.sh`, `scripts/ssl-individual-http-v2.sh`, `scripts/setup-wildcard-ssl.sh`, `scripts/monitoring/setup-monitoring.sh`, `scripts/deploy-wildcard-domain.sh`, `scripts/deploy-subdomain-ssl.sh` | --- ### 五、架构总结(2026-04-22 更新) 该项目的部署架构已优化为**单层容器**模式: #### 当前架构 ``` ┌─────────────────────────────────────────────────────────┐ │ novalon-nginx-secure (唯一容器) - 端口 80/443 │ │ │ │ nginx-static-production.conf (主配置) │ │ └── include /etc/nginx/conf.d/*.conf; │ │ │ │ conf.d/ (模块化配置目录) │ │ ├── git.f.novalon.cn.conf (Gitea 反向代理) │ │ ├── ci.f.novalon.cn.conf (Jenkins 反向代理) │ │ └── *.novalon.cn.conf (产品站点配置) │ │ │ │ sites/ (产品静态文件目录) │ │ ├── product-a/ │ │ └── product-b/ │ │ │ │ 挂载卷: │ │ - ./conf.d → /etc/nginx/conf.d │ │ - ./sites → /var/www/sites │ │ - ../novalon-static → /var/www/novalon │ │ - ./ssl → /etc/nginx/ssl │ └─────────────────────────────────────────────────────────┘ ``` #### 架构优化说明 **已移除的组件**: - `novalon-website` 容器(原端口 3000)-- 功能已合并到 `novalon-nginx-secure` 容器 **优化收益**: - 资源节省:减少一个容器实例 - 架构简化:单一网关,职责明确 - 配置模块化:每个服务独立配置文件,互不影响 #### 新增自动化脚本 | 脚本 | 用途 | |------|------| | `scripts/add-product-site.sh` | 一键添加新产品站点(配置文件 + 静态文件) | | `scripts/ssl-product-site.sh` | 为产品站点申请 SSL 证书 | #### 添加新产品站点流程 ```bash # 1. 添加 DNS A 记录: product-a.novalon.cn → 服务器IP # 2. 运行添加脚本 ./scripts/add-product-site.sh product-a ./product-a-files # 3. 申请 SSL 证书 ./scripts/ssl-product-site.sh product-a.novalon.cn # 4. 重载 Nginx docker exec novalon-nginx-secure nginx -s reload ``` --- ### 历史架构(已废弃) ~~该项目的部署架构为**双层容器**模式~~: ~~1. **内层**: `novalon-website` 容器(端口 3000)-- 由 `Dockerfile.static` 构建,仅提供静态文件服务~~ ~~2. **外层**: `novalon-nginx-secure` 容器(端口 80/443)-- 由 `docker-compose-nginx.yml` 启动,使用 `nginx-static-production.conf` 作为完整网关,负责 SSL 终止、静态文件服务、以及到 Gitea (`git.f.novalon.cn`) 和 Jenkins (`ci.f.novalon.cn`) 的反向代理~~ ~~两个容器通过外部 Docker 网络 `novalon-network`(bridge 驱动)互联。域名 `novalon.cn` / `www.novalon.cn` 指向主站静态资源,SSL 证书通过 Let's Encrypt 获取并配置了自动续期 cron 任务。~~ 现在我对你的生产环境架构有了清晰的了解。让我再确认几个关键细节: 现在我对你的架构完全清楚了。在给出方案之前,我需要确认几个关键信息: 好的,需求明确了。让我整理完整的部署方案。 ## 任务理解 - **目标**:在现有 `novalon-nginx` 网关上,为 6-15 个独立 HTML 产品官网配置子域名路由 + SSL - **约束**:每个产品用独立子域名(如 `product-a.novalon.cn`),SSL 需要新申请 - **当前架构**:`novalon-nginx-secure` 容器(nginx:alpine)作为网关,静态文件通过 volume 挂载 ## 方案设计 ### 推荐方案:Nginx 模板化 + 目录约定 + Let's Encrypt 自动化 核心思路是利用你现有的 `novalon-nginx-secure` 网关容器,通过**目录约定 + 配置模板**来管理多个产品站点,避免为每个产品创建独立容器。 #### 1. 目录结构约定 在服务器上按以下结构组织产品页面: ``` /home/novalon/docker-app/novalon-nginx/ ├── nginx-static-production.conf # 主配置(include 子配置) ├── conf.d/ # 产品站点配置目录(新增) │ ├── product-a.novalon.cn.conf │ ├── product-b.novalon.cn.conf │ └── ... ├── ssl/ # SSL 证书目录 │ ├── novalon.cn/ │ ├── git.f.novalon.cn/ │ ├── ci.f.novalon.cn/ │ ├── product-a.novalon.cn/ # 每个产品独立子目录 │ └── product-b.novalon.cn/ ├── sites/ # 产品静态文件目录(新增) │ ├── product-a/ │ │ └── index.html │ ├── product-b/ │ │ └── index.html │ └── ... └── docker-compose-nginx.yml ``` #### 2. Nginx 主配置修改 在 `nginx-static-production.conf` 的 `http {}` 块末尾添加一行 include: ```nginx # 在 http {} 块末尾,现有 server 块之后添加: include /etc/nginx/conf.d/*.conf; ``` #### 3. 产品站点配置模板 每个产品一个独立配置文件,例如 `conf.d/product-a.novalon.cn.conf`: ```nginx # 产品A官网 - product-a.novalon.cn server { listen 80; server_name product-a.novalon.cn; location /.well-known/acme-challenge/ { root /var/www/certbot; } location / { return 301 https://$host$request_uri; } } server { listen 443 ssl http2; server_name product-a.novalon.cn; ssl_certificate /etc/nginx/ssl/product-a.novalon.cn/fullchain.pem; ssl_certificate_key /etc/nginx/ssl/product-a.novalon.cn/privkey.pem; ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305; ssl_prefer_server_ciphers off; ssl_session_cache shared:SSL:10m; ssl_session_timeout 1d; add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always; add_header X-Frame-Options "SAMEORIGIN" always; add_header X-Content-Type-Options "nosniff" always; add_header X-XSS-Protection "1; mode=block" always; root /var/www/sites/product-a; index index.html; # 静态资源缓存 location ~* \.(css|js|jpg|jpeg|png|gif|webp|avif|svg|ico|woff|woff2|ttf|eot)$ { expires 1y; add_header Cache-Control "public, max-age=31536000, immutable"; add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always; try_files $uri =404; } location /.well-known/acme-challenge/ { root /var/www/certbot; } location / { limit_req zone=general burst=20 nodelay; try_files $uri $uri/ =404; } error_page 404 /404.html; access_log /var/log/nginx/product-a-access.log; error_log /var/log/nginx/product-a-error.log; } ``` #### 4. Docker Compose 修改 在 `docker-compose-nginx.yml` 中添加新的 volume 挂载: ```yaml services: nginx: image: nginx:alpine container_name: novalon-nginx-secure restart: unless-stopped ports: - "80:80" - "443:443" volumes: - ./nginx-static-production.conf:/etc/nginx/nginx.conf:ro - ./conf.d:/etc/nginx/conf.d:ro # 新增:产品站点配置 - ./ssl:/etc/nginx/ssl:ro - ./logs:/var/log/nginx - ../certbot:/var/www/certbot - ../novalon-static:/var/www/novalon:ro - ./sites:/var/www/sites:ro # 新增:产品静态文件 networks: - novalon-network networks: novalon-network: driver: bridge external: true ``` #### 5. SSL 证书申请脚本 为每个产品子域名申请 Let's Encrypt 证书: ```bash #!/bin/bash # scripts/ssl-product-site.sh # 用法: ./ssl-product-site.sh product-a.novalon.cn DOMAIN=$1 if [ -z "$DOMAIN" ]; then echo "用法: $0 .novalon.cn" exit 1 fi CERTBOT_DIR="/home/novalon/docker-app/certbot" SSL_DIR="/home/novalon/docker-app/novalon-nginx/ssl" # 创建证书目录 mkdir -p "${SSL_DIR}/${DOMAIN}" # 申请证书(使用 standalone 模式,需临时停止 nginx 的 80 端口) docker run --rm \ -p 80:80 \ -v "${CERTBOT_DIR}:/var/www/certbot" \ -v "/etc/letsencrypt:/etc/letsencrypt" \ certbot/certbot certonly \ --webroot \ --webroot-path /var/www/certbot \ -d "${DOMAIN}" \ --email admin@novalon.cn \ --agree-tos \ --no-eff-email # 复制证书到 nginx ssl 目录 cp "/etc/letsencrypt/live/${DOMAIN}/fullchain.pem" "${SSL_DIR}/${DOMAIN}/" cp "/etc/letsencrypt/live/${DOMAIN}/privkey.pem" "${SSL_DIR}/${DOMAIN}/" echo "✅ ${DOMAIN} 证书申请完成" ``` #### 6. 一键添加新产品脚本 ```bash #!/bin/bash # scripts/add-product-site.sh # 用法: ./add-product-site.sh product-a /path/to/product-a-files PRODUCT_NAME=$1 FILES_SOURCE=$2 if [ -z "$PRODUCT_NAME" ] || [ -z "$FILES_SOURCE" ]; then echo "用法: $0 " echo "示例: $0 product-a ./product-a-website" exit 1 fi DOMAIN="${PRODUCT_NAME}.novalon.cn" NGINX_DIR="/home/novalon/docker-app/novalon-nginx" CONF_DIR="${NGINX_DIR}/conf.d" SITES_DIR="${NGINX_DIR}/sites" SSL_DIR="${NGINX_DIR}/ssl" # 1. 创建目录 mkdir -p "${SITES_DIR}/${PRODUCT_NAME}" mkdir -p "${SSL_DIR}/${DOMAIN}" mkdir -p "${CONF_DIR}" # 2. 复制静态文件 cp -r "${FILES_SOURCE}/"* "${SITES_DIR}/${PRODUCT_NAME}/" # 3. 生成 Nginx 配置 cat > "${CONF_DIR}/${DOMAIN}.conf" << 'CONF_TEMPLATE' # 产品官网 - {{DOMAIN}} server { listen 80; server_name {{DOMAIN}}; location /.well-known/acme-challenge/ { root /var/www/certbot; } location / { return 301 https://$host$request_uri; } } server { listen 443 ssl http2; server_name {{DOMAIN}}; ssl_certificate /etc/nginx/ssl/{{DOMAIN}}/fullchain.pem; ssl_certificate_key /etc/nginx/ssl/{{DOMAIN}}/privkey.pem; ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305; ssl_prefer_server_ciphers off; ssl_session_cache shared:SSL:10m; ssl_session_timeout 1d; add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always; add_header X-Frame-Options "SAMEORIGIN" always; add_header X-Content-Type-Options "nosniff" always; add_header X-XSS-Protection "1; mode=block" always; root /var/www/sites/{{PRODUCT_NAME}}; index index.html; location ~* \.(css|js|jpg|jpeg|png|gif|webp|avif|svg|ico|woff|woff2|ttf|eot)$ { expires 1y; add_header Cache-Control "public, max-age=31536000, immutable"; add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always; try_files $uri =404; } location /.well-known/acme-challenge/ { root /var/www/certbot; } location / { limit_req zone=general burst=20 nodelay; try_files $uri $uri/ =404; } error_page 404 /404.html; access_log /var/log/nginx/{{PRODUCT_NAME}}-access.log; error_log /var/log/nginx/{{PRODUCT_NAME}}-error.log; } CONF_TEMPLATE # 替换模板变量 sed -i "s/{{DOMAIN}}/${DOMAIN}/g" "${CONF_DIR}/${DOMAIN}.conf" sed -i "s/{{PRODUCT_NAME}}/${PRODUCT_NAME}/g" "${CONF_DIR}/${DOMAIN}.conf" echo "✅ 产品站点 ${PRODUCT_NAME} 配置完成" echo "" echo "后续步骤:" echo " 1. 添加 DNS A 记录: ${DOMAIN} -> 服务器IP" echo " 2. 申请 SSL 证书: ./ssl-product-site.sh ${DOMAIN}" echo " 3. 重载 Nginx: docker exec novalon-nginx-secure nginx -s reload" ``` #### 7. 完整部署流程 添加一个新产品站点的完整步骤: ``` 步骤 1: DNS 解析 → 在域名服务商添加 A 记录: product-a.novalon.cn → 139.155.109.62 步骤 2: 上传文件 + 生成配置 → ./add-product-site.sh product-a ./product-a-html-files 步骤 3: 申请 SSL 证书 → ./ssl-product-site.sh product-a.novalon.cn 步骤 4: 验证配置 + 重载 Nginx → docker exec novalon-nginx-secure nginx -t → docker exec novalon-nginx-secure nginx -s reload 步骤 5: 验证访问 → curl -I https://product-a.novalon.cn ``` ### 方案优势 | 特性 | 说明 | |------|------| | **零额外容器** | 所有产品站点复用现有 `novalon-nginx-secure` 网关 | | **模板化** | 一键脚本生成配置,避免手动复制粘贴出错 | | **隔离性** | 每个产品独立配置文件、独立日志、独立 SSL 证书目录 | | **可扩展** | 新增产品只需 3 条命令 | | **统一管理** | 所有配置集中在 `conf.d/` 目录,便于审查和维护 | | **资源高效** | 不需要为每个 HTML 页面启动独立容器 | ### 注意事项 1. **DNS 解析必须先完成**:SSL 证书申请需要域名已指向服务器 IP 2. **证书续期**:建议在现有 cron 任务中加入产品子域名的自动续期 3. **如果未来产品数量超过 15 个**,建议升级为泛域名证书(`*.novalon.cn`),可大幅简化证书管理 4. **文件更新**:更新某个产品的 HTML 文件后,只需替换 `sites/product-x/` 下的文件,无需重启 Nginx(静态文件通过 volume 挂载) --- ## 六、安全加固(2026-08-15 新增) ### 6.1 安全事件摘要(2026-08-14 发现并处置) **事件**:生产服务器 Gitea 容器被入侵,git 操作被注入恶意钩子,导致 `git pull` 报 `bad pack header` / `early EOF`、响应延迟 4-6 秒。 **攻击链还原**: 1. Gitea 内部 API(`/api/internal/manager/add-logger`)经 nginx `location /` 暴露到公网,配合泄露的 `INTERNAL_TOKEN` 可远程调用(攻击来源 IP:187.15.89.220,巴西) 2. 利用 `add-logger` 的日志文件路径能力向 `/data/gitea/home/.gitconfig` 追加写入 `[uploadpack] packObjectsHook = ` 3. 每次 git 操作(upload-pack)触发钩子 → 从 C2 域名 `sitego-step-hu.pages.dev/setup-mon`(Cloudflare Pages,挖矿/后门分发)下载并执行 payload 4. 恶意脚本以 git 用户权限回写 `.gitconfig` 与仓库 `hooks/` 实现持久化 —— **持久化位于 `/data` 卷,容器重建后依然保留**,这是本次排查最关键的教训 **影响范围**:Gitea 容器(git 用户权限);宿主机深度排查未发现入侵痕迹。 ### 6.2 已执行的加固措施 | 措施 | 详情 | |------|------| | Gitea 升级 | `1.27.0` → `1.27.2`(compose 文件:`novalon-cicd/gitea/docker-compose.yml`) | | 清理恶意文件 | 重写 `/data/gitea/home/.gitconfig`(移除 packObjectsHook 与日志注入行);恢复 `everything-is-suitable.git/hooks/pre-applypatch.sample` 为标准样本 | | 封堵内部 API | nginx `git.f.novalon.cn.conf` 增加 `location ~ ^/api/internal/ { return 403; }`(80/443 两个 server 块均已加) | | 收紧代理信任 | `REVERSE_PROXY_TRUSTED_PROXIES = *` → `172.20.0.0/16,127.0.0.1` | | 凭据轮换 | `INTERNAL_TOKEN`、`SECRET_KEY`、`LFS_JWT_SECRET`、OAuth `JWT_SECRET`、postgres 密码、4 个管理员密码全部更换 | | 数据库清理 | 删除遗留 `forgejo` / `woodpecker` 数据库;引导超级用户角色改名 `postgres`(OID 不变,系统目录归属不受影响) | | 内存优化 | Jenkins `JAVA_OPTS` 从 `-Xmx2g -Xms512m` 调为 `-Xmx1024m -Xms256m`;`daemon.json` 移除失效镜像源(dockerproxy.com 已死,会卡住镜像拉取) | | SSE 长连接 | `location ~ /events$` 增加 `proxy_buffering off` + `proxy_read_timeout 3600s`(修复 `/user/events` 每 30s 被掐断的问题) | ### 6.3 安全基线(必须长期保持) 1. **内部 API 禁止公网可达**:`/api/internal/*` 必须保持 403,任何新 nginx 配置不得代理该路径 2. **`INTERNAL_TOKEN` 不得泄露**:禁止出现在日志、备份、代码仓库中;定期轮换 3. **`REVERSE_PROXY_TRUSTED_PROXIES` 保持最小化**:仅 docker 网段 + 本机 4. **Gitea 版本保持最新**:当前 1.27.2,关注官方安全公告(1.26.4 / 1.27.x 有安全修复) 5. **管理员密码定期轮换**;评估切换到 `gitea/gitea:*-rootless` 镜像(当前非 rootless,容器内 s6 以 root 运行) 6. **凭据统一管理**:postgres 密码同时存在于 `app.ini` 与 `secrets/db_passwd`,更换时必须两处同步 ### 6.4 攻击排查速查表 | 项目 | 内容 | |------|------| | 恶意标记 | `giteahook` / `sitego-step` / `setup-mon` / `packObjectsHook` / `add-logger` | | 重点检查点 | `/data/gitea/home/.gitconfig`、各仓库 `hooks/`、`docker diff `、`ps aux \| grep base64` | | 根因验证 | 原始 `git upload-pack` 正常 + 经 Gitea HTTP 失败 ⇒ 怀疑钩子/转发层被篡改 | | 取证目录 | `/root/incident-20260814/`(恶意 .gitconfig、恶意 hook、容器 inspect、日志) | | 数据库备份 | `/root/db-cleanup-20260815/`(forgejo.sql.gz / woodpecker.sql.gz) | ### 6.5 已知限制与待办 - **出网带宽 ≈ 1Mbps**(腾讯云实例限制,实测 110KB/s):248MB 仓库全量 clone 约需 35 分钟;建议在腾讯云控制台升级带宽(如 5-10Mbps),这是当前 git 使用体验的唯一瓶颈 - **jenkins 容器**:以 root 运行且挂载 `/var/run/docker.sock` 与 `/root/.ssh`(CI 构建需要,但一旦被攻破即等于宿主机沦陷,建议评估最小权限方案) - **遗留 forgejo 引用**:`dist_backup_20260422_161751/` 与 `novalon-website/.woodpecker.yml` 等旧文件中仍含 forgejo/woodpecker 配置,属历史归档,无需处理 - **监控缺失**:本次攻击 2026-08-11 开始,3 天后才被发现;建议配置 fail2ban + 容器日志告警 + 定时恶意文件扫描