cccbb7b56b
CLAUDE.md 补充控制面经 Cloudflare Tunnel 对外的端口/URL 布局说明;生成 docs/control-plane-tls-tunnel.html 阅读版并登记 docs/index.html「实现计划」; 顺带修正 deploy/single-node/deploy.sh 摘要 echo 里残留的旧明文口描述(Task 4/5 落地后措辞漂移,实际已是 CF Tunnel + loopback)。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013nMthbVEmQquxBRKb9Fj8u
137 lines
9.4 KiB
HTML
137 lines
9.4 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="zh-CN">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Pangolin 控制面 TLS(Cloudflare Tunnel 前置)实现计划</title>
|
|
<style>
|
|
:root{
|
|
--bg:#0f1117; --panel:#171a22; --panel2:#1d2129; --fg:#e6e8ee; --fg2:#a8afbd;
|
|
--accent:#e0884f; --accent2:#5fb0c9; --ok:#5ec27a; --bad:#e06a6a; --warn:#e0b84f;
|
|
--border:#272c36; --mono:"SF Mono",ui-monospace,Menlo,Consolas,monospace;
|
|
--sans:-apple-system,"PingFang SC","Helvetica Neue",Arial,sans-serif;
|
|
}
|
|
*{box-sizing:border-box}
|
|
body{margin:0;background:var(--bg);color:var(--fg);font-family:var(--sans);line-height:1.7;font-size:15px}
|
|
.wrap{max-width:960px;margin:0 auto;padding:48px 24px 96px}
|
|
h1{font-size:30px;line-height:1.3;margin:0 0 8px;letter-spacing:-.01em}
|
|
.sub{color:var(--fg2);font-size:15px;margin:0 0 32px}
|
|
h2{font-size:21px;margin:44px 0 14px;padding-bottom:8px;border-bottom:1px solid var(--border)}
|
|
h3{font-size:16px;margin:24px 0 8px;color:var(--fg)}
|
|
p{margin:10px 0}
|
|
code{font-family:var(--mono);font-size:.86em;background:var(--panel2);padding:1px 6px;border-radius:5px;color:#f0d9c4}
|
|
pre{background:#0a0c11;border:1px solid var(--border);border-radius:10px;padding:14px 16px;overflow-x:auto;font-family:var(--mono);font-size:12.5px;line-height:1.55;color:#cdd3df}
|
|
.tag{display:inline-block;font-size:12px;font-weight:600;padding:2px 9px;border-radius:999px;vertical-align:middle}
|
|
.tag.ok{background:rgba(94,194,122,.16);color:var(--ok)}
|
|
.tag.warn{background:rgba(224,184,79,.16);color:var(--warn)}
|
|
.tag.info{background:rgba(95,176,201,.16);color:var(--accent2)}
|
|
.card{background:var(--panel);border:1px solid var(--border);border-radius:12px;padding:16px 20px;margin:14px 0}
|
|
.card h3{margin-top:0;color:var(--accent2)}
|
|
.files{font-family:var(--mono);font-size:12px;color:var(--fg2);margin:6px 0 10px}
|
|
table{width:100%;border-collapse:collapse;margin:16px 0;font-size:14px}
|
|
th,td{text-align:left;padding:9px 12px;border-bottom:1px solid var(--border);vertical-align:top}
|
|
th{color:var(--fg2);font-weight:600;font-size:13px}
|
|
ul,ol{padding-left:22px;margin:8px 0}
|
|
li{margin:5px 0}
|
|
.lead{background:linear-gradient(180deg,rgba(224,136,79,.10),transparent);border:1px solid var(--border);border-radius:12px;padding:18px 20px;margin:0 0 8px}
|
|
.small{color:var(--fg2);font-size:13px}
|
|
a{color:var(--accent2)}
|
|
.back{display:inline-block;margin-bottom:24px;font-size:13px}
|
|
b{color:#fff}
|
|
.phase{font-size:19px;margin:40px 0 6px;color:var(--accent);font-weight:700}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="wrap">
|
|
<a class="back" href="index.html">← 文档索引</a>
|
|
<h1>Pangolin 控制面 TLS(Cloudflare Tunnel 前置)实现计划</h1>
|
|
<p class="sub">2026-07-06 · <span class="tag info">阅读版</span> · 执行真相源 <code>docs/superpowers/plans/2026-07-06-control-plane-tls-tunnel.md</code>(带 checkbox)</p>
|
|
|
|
<div class="lead">
|
|
<b>目标:</b>把 pangolin-server 控制面 API 从明文 <code>http://103.119.13.48:8080</code> 迁到
|
|
<code>https://api.yanmeiai.com</code>,经 Cloudflare Tunnel 前置(隐藏源站 IP、白嫖标准 443 + 免证书)。
|
|
数据面 sing-box REALITY <code>:443</code> 全程不动。
|
|
</div>
|
|
|
|
<h2>架构</h2>
|
|
<p>
|
|
pangolin1 上跑 <code>cloudflared</code> <b>出站</b>隧道(不监听任何入站端口 → 与 sing-box 独占的
|
|
<code>:443</code> 零冲突),CF 边缘把 <code>api.yanmeiai.com</code> 的请求经隧道回送到
|
|
<code>127.0.0.1:8080</code>。客户端(Flutter 四端共享 <code>kApiBaseUrl</code>)默认改 https 域名;
|
|
控制面下发给客户端 sing-box 的 <code>.srs</code> 规则集下载基址(<code>PANGOLIN_PUBLIC_URL</code>)
|
|
同步改 https。最后一步把 <code>:8080</code> 收回 loopback 并关防火墙,彻底退役明文口——该步有
|
|
<b>上线顺序闸</b>(须待现网客户端更新后再做)。
|
|
</p>
|
|
|
|
<h2>端口 / URL 布局</h2>
|
|
<table>
|
|
<tr><th>用途</th><th>对外</th><th>源站/绑定</th><th>本轮变更</th></tr>
|
|
<tr><td>控制面 HTTP API</td><td><code>https://api.yanmeiai.com</code>(CF Tunnel)</td><td><code>127.0.0.1:8080</code></td><td>新增 CF Tunnel 前置 + 收 loopback</td></tr>
|
|
<tr><td>数据面 sing-box REALITY</td><td><code>:443</code>(节点公网 IP)</td><td>同端口</td><td>不动</td></tr>
|
|
<tr><td>gRPC agent(mTLS)</td><td>—(仅节点内)</td><td><code>:9443</code></td><td>不动</td></tr>
|
|
</table>
|
|
|
|
<h2>全局约束</h2>
|
|
<ul>
|
|
<li>Bash 禁 <code>$()</code> 命令替换、禁 <code>set -a</code>/<code>set +a</code>;需捕获输出拆多步或用管道。</li>
|
|
<li>凭证走 Bitwarden/rbw,不写 <code>~/.env</code>/明文配置/git。Cloudflare 用 <code>cf-api</code> 封装(token 内部从 Bitwarden 取)。隧道 token 等密钥一律不入 git,只落 <code>/etc/pangolin/*</code>(已 gitignore)+ Bitwarden。</li>
|
|
<li>改机器(装包/改配置/重启服务)前必须先问用户(只读操作除外)。</li>
|
|
<li>pangolin1 = <code>103.119.13.48</code>,ssh 别名 <code>pangolin1</code>。数据面 sing-box REALITY 独占入站 <code>:443</code>,<b>不得触碰</b>;gRPC agent mTLS <code>:9443</code> 不动。</li>
|
|
<li><b>上线顺序铁律:</b>现网客户端硬编码 <code>http://103.119.13.48:8080</code>。隧道与 https 端点必须<b>加法上线</b>(与旧口并存),客户端切 https 发版后,收 loopback 才能做,否则旧客户端全挂。</li>
|
|
</ul>
|
|
|
|
<h2>6 个任务</h2>
|
|
|
|
<div class="card">
|
|
<h3>Task 1 · Cloudflare Tunnel 供给</h3>
|
|
<div class="files">Create: deploy/single-node/systemd/cloudflared.service · Modify: deploy/single-node/deploy.sh</div>
|
|
CF 账户侧(cf-api)建 remotely-managed 隧道 + ingress(<code>api.yanmeiai.com</code> → <code>http://localhost:8080</code>)+ 代理 CNAME;pangolin1 装 cloudflared(Debian apt 源)+ committed systemd unit(token 经 <code>EnvironmentFile</code> 注入,不入 unit 本体)。验证:<code>https://api.yanmeiai.com/healthz</code> 与旧的 <code>http://103.119.13.48:8080/healthz</code> <b>并存可用</b>(加法,不破坏现网)。
|
|
</div>
|
|
|
|
<div class="card">
|
|
<h3>Task 2 · 客户端控制面基址切 https + Android 去明文</h3>
|
|
<div class="files">Modify: client/lib/services/api_config.dart · client/android/.../AndroidManifest.xml · Create: client/test/unit/api_config_test.dart</div>
|
|
先写守护测试(断言 <code>kApiBaseUrl</code> 必须 <code>https://</code> 且不含节点 IP)→ 确认失败 → 把 <code>kApiBaseUrl</code> 默认值改为 <code>https://api.yanmeiai.com</code>(仍保留 <code>String.fromEnvironment</code> 可本地覆盖)→ 测试转绿。同步移除 Android manifest 的 <code>android:usesCleartextTraffic="true"</code>(控制面已 https,不再需要明文豁免;iOS/macOS 无 ATS 配置,无需改动)。跑 <code>flutter analyze</code> + 全量单测。
|
|
</div>
|
|
|
|
<div class="card">
|
|
<h3>Task 3 · CI 守护:Android release manifest 禁明文</h3>
|
|
<div class="files">Create: ci/scan-cleartext.sh · Modify: .gitea/workflows/ci.yml</div>
|
|
新增扫描脚本:manifest 一旦重新出现 <code>usesCleartextTraffic="true"</code> 就 CI 失败(防止将来有人把明文开关加回来,退回到 #25 之前的不安全态)。接入 <code>ci.yml</code> 新 job + shellcheck 列表。
|
|
</div>
|
|
|
|
<div class="card">
|
|
<h3>Task 4 · <code>PANGOLIN_PUBLIC_URL</code> 切 https</h3>
|
|
<div class="files">Modify: deploy/single-node/deploy.sh</div>
|
|
该变量被嵌进客户端 sing-box 配置当 <code>.srs</code> 分流规则集下载基址(<code>clientconfig.go</code>)。不改的话新客户端仍去明文 IP 拉。改为 <code>https://api.yanmeiai.com</code>;与 Task 1 隧道并存,对新旧客户端都安全(URL 由服务端下发,客户端只是照着 GET)。pangolin1 上应用 + 重启 server,验证规则集经隧道可 200 下载。
|
|
</div>
|
|
|
|
<div class="card">
|
|
<h3>Task 5 · 退役明文口(收 loopback + 关防火墙 + 修健康检查)<span class="tag warn">上线顺序闸</span></h3>
|
|
<div class="files">Modify: deploy/single-node/deploy.sh · scripts/ci/deploy-server.sh</div>
|
|
<b>此 Task 会切断外部 <code>http://103.119.13.48:8080</code>,只有当现网客户端都已更新到 Task 2 的 https 版本后才能执行</b>,执行前需与用户确认「旧客户端可弃」。内容:<code>ADDR</code> 收 <code>127.0.0.1:8080</code>;不再 ufw 放行 8080;<code>deploy-server.sh</code> 健康检查从「runner 远程 curl 公网 IP」改为「ssh 内本地 curl loopback」+「经隧道 curl https 域名」双路验证。验证:明文口不可达、隧道仍活、<code>ss</code> 显示 8080 仅监听 <code>127.0.0.1</code>。
|
|
</div>
|
|
|
|
<div class="card">
|
|
<h3>Task 6 · 文档更新(本任务)</h3>
|
|
<div class="files">Modify: CLAUDE.md · docs/index.html · docs/control-plane-tls-tunnel.html(本页)</div>
|
|
CLAUDE.md 补充端口/URL 布局说明;生成本 HTML 阅读版并登记 <code>docs/index.html</code>「实现计划」分类;顺带修正 <code>deploy/single-node/deploy.sh</code> 摘要 echo 里残留的旧明文口描述(Task 4/5 落地后的措辞漂移)。
|
|
</div>
|
|
|
|
<h2>上线顺序</h2>
|
|
<p>
|
|
Task 1(隧道加法)→ Task 4(<code>PANGOLIN_PUBLIC_URL</code>,新旧客户端皆安全)→ Task 2(客户端切 https,发版)→
|
|
<b>待客户端更新</b> → Task 5(收口)。Task 3(CI 守护)、Task 6(文档)无顺序耦合,可随时并行推进。
|
|
</p>
|
|
|
|
<h2>不在本轮</h2>
|
|
<ul class="small">
|
|
<li>#32 控制面 fallback(CF 域名被 SNI 封 → 客户端退回直连节点 IP 的 https 控制口)。</li>
|
|
<li>控制面 API 的 CF WAF/rate-limit 规则精调。</li>
|
|
<li>usercenter(web/usercenter)也接入同域名 API(其部署属 #30 30A)。</li>
|
|
</ul>
|
|
|
|
</div>
|
|
</body>
|
|
</html>
|