Files
pangolin/CLAUDE.md
T
2026-05-30 23:30:08 +08:00

5.5 KiB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

这个仓库是什么

Pangolin 是面向中国大陆用户的终端 VPN(科学上网)项目。本仓库不是应用代码,而是:

  • deploy/ —— 数据/分发面的容器化部署(Docker + Gitea Actions CI/CD)
  • docs/ —— 设计与运维文档(技术方案.md回滚手册.md)
  • plan/ —— 分阶段实施计划(phase-0 → phase-3 + 安全/获客横切)

客户端(Flutter)与后端面板尚未进入本仓库;当前实质内容是 deploy/

部署目标与不可破坏的约束(最重要)

部署目标是一台已在生产运行的 EC2(18.136.60.128,ap-southeast-1,user ec2-user),上面还跑着与本项目无关、不能搞坏的服务:marzban(线上 REALITY 代理 + 面板)、jiu(jiu_mysql + jiu.service :8080 + 静态)、blog(blog/umami/postgres)、billing.service :18080。

  • 线上代理 = Marzban 的 REALITY(443 → 127.0.0.1:10443),绝不能中断。 运维者本人通过 Shadowrocket 连的就是它。
  • 切换/重建 pangolin-edge(占用 443)会有 ~1-2 秒 TCP 交接,代理客户端会自动重连——选空闲时段做。
  • 注意自噬隧道:运维者本机常挂 VPN,且该 VPN 可能正穿这台的 443。重建 edge 时 443 闪断会把你自己的 SSH 也掐了(SSH 经代理隧道)。重建后等几秒重连即可,不是故障。
  • 从运维者 Mac 做"外部"验证不可信(fake-IP DNS / 经 Cloudflare)。权威验证一律在 EC2 内部做(ssh ec2 后 curl 127.0.0.1 / openssl 127.0.0.1)。

目标主机的关键事实(代码里看不出来)

  • 部署目录:~/pangolin(即 /home/ec2-user/pangolin),不是 /opt
  • ec2-user 免 sudo 跑 docker;但通用 sudo 需要密码(Claude 跑不了)。需要 root 的步骤(如 systemctl stop nginx)必须让用户执行;crontab 在用户设置里被 deny。
  • Compose 是独立 docker-compose(无 docker compose v2 插件),且不支持 compose build。因此:
    • 命令一律用 docker-compose -p pangolin ...
    • edge 镜像用 docker build -t pangolin-edge:local ./edge 单独构建,compose 只 up(不要 --build)。
  • edgesingboxnetwork_mode: host(才能绑 80/443/udp443 并回连一堆只听 127.0.0.1 的后端)。
  • 证书在 /etc/letsencrypt(Let's Encrypt:blog/jiu/vpn.51yanmei.com),rw 挂进 edge

架构(部署后)

EC2 :443/tcp + :80  →  pangolin-edge (自定义镜像: nginx + certbot)
                         stream ssl_preread 按 SNI 分流(edge/stream.conf):
                           www.cloudflare.com/default → 10443  Marzban REALITY(线上,勿断)
                           vpn/blog/jiu.51yanmei.com  → 8443/8444/8445(edge 内 http vhost)
                         http vhost(edge/conf.d/*)→ marzban面板8899 / blog3000+umami3001 / jiu8080 + 静态
EC2 :443/udp        →  pangolin-singbox (Hysteria2)
  • pangolin-edge:分发层。自定义镜像自带 certbot,容器内每 12h 自动 DNS-01 续期 + 本容器内 nginx -s reload(见 deploy/edge/pangolin-entrypoint.sh)——无宿主 cron、无 docker socket。
  • pangolin-singbox:Hysteria2 加速线(UDP 443)。
  • pangolin-xray:REALITY,本台不启用(compose profile: newnode,留给新节点;本台 REALITY 仍由 Marzban 提供)。
  • certbot 服务:仅 profile: certbot,用于手动首次签发/重配;日常续期由 edge 自身完成。

deploy/edge/conf.d/*.confstream.conf现网宿主 nginx 配置的逐字拷贝——改动需与现网后端地址保持一致。镜像版本固定(nginx 1.27 / sing-box v1.13.12 / xray 26.3.27)。

常用命令(在 deploy/ 下)

# 幂等部署(免 root):起 singbox;宿主 nginx 已停则构建+起 edge,否则只校验 edge 配置
./scripts/deploy.sh

# 一次性切换:宿主 nginx → edge 容器(需 root,用户执行;失败自动回滚)
sudo bash scripts/cutover.sh

# 回滚:停 edge,恢复宿主 nginx(需 root,用户执行)
sudo bash scripts/rollback.sh

# 仅构建 edge 镜像 / 重建 edge(会有 ~1-2s 443 闪断)
docker build -t pangolin-edge:local ./edge
docker-compose -p pangolin up -d edge

# 离线校验 edge 配置(不绑端口,挂真实证书)
docker run --rm -v "$PWD/edge/nginx.conf:/etc/nginx/nginx.conf:ro" \
  -v "$PWD/edge/stream.conf:/etc/nginx/stream.conf:ro" -v "$PWD/edge/conf.d:/etc/nginx/conf.d:ro" \
  -v /etc/letsencrypt:/etc/letsencrypt:ro -v /opt/jiu:/opt/jiu:ro -v /var/www/pay:/var/www/pay:ro \
  nginx:1.27-alpine nginx -t

# 验证证书自续期(staging,不动正式证书)
docker exec pangolin-edge certbot renew --dry-run

# 取客户端导入串(Hy2,含口令,不进日志)
cat secrets/clients.txt

# 脚本改动后:本地 shellcheck -S warning deploy/scripts/*.sh

CI/CD

push deploy/** → NAS 上的 Gitea(ssh://git@192.168.3.200:2222/wangjia/pangolin.git)→ Gitea Actions runner → ssh ec2rsync deploy/ → ~/pangolin/deploy.sh(免 root,幂等)。

  • 需在 Gitea 配 Secrets:EC2_SSH_KEY / EC2_HOST / EC2_USER / CF_API_TOKEN,并把 .gitea/workflows/deploy.ymlruns-on 改成实际 runner label。
  • 手动等价流程:rsync -az --exclude secrets deploy/ ec2:pangolin/ && ssh ec2 'cd ~/pangolin && ./scripts/deploy.sh'

密钥与渲染产物

deploy/secrets/*/config.jsoncloudflare.inigitignoregen-secrets.sh 在 EC2 首跑生成 Hy2 口令/自签证书;CF_API_TOKEN(本机 ~/.env 已加载,CI 由 Secret 注入)渲染 cloudflare.ini。模板用 __PLACEHOLDER__,脚本 sed 渲染。