baa-conductor

git clone 

commit
3c0421d
parent
88af3af
author
im_wower
date
2026-03-21 21:37:30 +0800 CST
Merge branch 'feat/T-008-ops-nginx'
5 files changed,  +320, -18
M coordination/tasks/T-008-ops-nginx.md
+21, -6
 1@@ -1,7 +1,7 @@
 2 ---
 3 task_id: T-008
 4 title: Nginx 与 VPS 运维
 5-status: todo
 6+status: review
 7 branch: feat/T-008-ops-nginx
 8 repo: /Users/george/code/baa-conductor
 9 base_ref: main
10@@ -53,23 +53,38 @@ updated_at: 2026-03-21
11 
12 ## files_changed
13 
14-- 待填写
15+- `coordination/tasks/T-008-ops-nginx.md`
16+- `docs/ops/README.md`
17+- `ops/nginx/baa-conductor.conf`
18+- `ops/nginx/includes/common-proxy.conf`
19+- `ops/nginx/includes/direct-node-auth.conf`
20 
21 ## commands_run
22 
23-- 待填写
24+- `git worktree add ../baa-conductor-T008 -b feat/T-008-ops-nginx main`
25+- `command -v nginx`
26+- `git diff --check -- ops/nginx docs/ops coordination/tasks/T-008-ops-nginx.md`
27+- `rg -n "upstream conductor_primary|upstream mini_conductor_direct|upstream mac_conductor_direct|return 301 https://\\$host\\$request_uri|server_name conductor\\.makefile\\.so|server_name mini-conductor\\.makefile\\.so|server_name mac-conductor\\.makefile\\.so|ssl_certificate|auth_basic" ops/nginx/baa-conductor.conf ops/nginx/includes/direct-node-auth.conf`
28+- `rg -n "conductor\\.makefile\\.so|mini-conductor\\.makefile\\.so|mac-conductor\\.makefile\\.so|nginx -t|htpasswd|80/tcp|443/tcp|4317" docs/ops/README.md`
29 
30 ## result
31 
32-- 待填写
33+- `ops/nginx/baa-conductor.conf` 已补齐 3 个 upstream、统一 `80 -> 443` 跳转、3 个 TLS server block,以及统一入口与直连 mini/mac 的转发关系
34+- `ops/nginx/includes/common-proxy.conf` 已补齐常用代理头、超时、request id、buffer 和 retry 配置
35+- `ops/nginx/includes/direct-node-auth.conf` 已默认启用 Basic Auth,并补充可选 IP allowlist 的使用说明
36+- `docs/ops/README.md` 已写明 VPS 部署、目录映射、证书准备、启用步骤、上线验证与保护建议
37 
38 ## risks
39 
40-- 待填写
41+- 本机未安装 `nginx`,本次只完成静态合理性检查;上线前仍需在 VPS 上以真实证书和真实路径执行 `sudo nginx -t`
42+- `100.71.210.78`、`100.112.239.13` 与 `4317` 端口来自当前设计文档,真实部署前应再次确认 Tailscale 地址和端口未变化
43+- 若三域名经过 Cloudflare 代理,直连域名的 IP allowlist 需要先配置真实客户端 IP 恢复,否则 `allow/deny` 会基于 Cloudflare 出口 IP 生效
44 
45 ## next_handoff
46 
47-- 提供给后续真实部署
48+- 把仓库中的 `ops/nginx/**` 同步到 VPS 的 `/etc/nginx/sites-available/` 与 `/etc/nginx/includes/baa-conductor/`
49+- 准备 `.htpasswd` 与 TLS 证书后,在 VPS 上执行 `sudo nginx -t && sudo systemctl reload nginx`
50+- 用 `curl` 验证统一入口的 `301/200`,以及 `mini-conductor.makefile.so`、`mac-conductor.makefile.so` 的 `401/200`
51 
52 ## notes
53 
M docs/ops/README.md
+178, -3
  1@@ -1,6 +1,181 @@
  2-# ops
  3+# VPS 与 Nginx 运维说明
  4 
  5-这个目录预留给 VPS、Nginx、部署和运维说明。
  6+本目录记录 `baa-conductor` 在 VPS 上的公网入口、Nginx 转发和启用步骤。
  7 
  8-当前只建立目录边界,具体内容由 `T-008` 完成。
  9+## 域名与转发关系
 10 
 11+- `conductor.makefile.so`
 12+  - DNS 指向 VPS 公网 IP
 13+  - `Nginx -> upstream conductor_primary`
 14+  - `conductor_primary` 先打到 `mini` 的 `100.71.210.78:4317`
 15+  - `mini` 不可达时回退到 `mac` 的 `100.112.239.13:4317`
 16+- `mini-conductor.makefile.so`
 17+  - DNS 指向同一台 VPS
 18+  - `Nginx -> upstream mini_conductor_direct -> 100.71.210.78:4317`
 19+  - 用于直连 `mini` 做调试、健康检查和手工运维
 20+- `mac-conductor.makefile.so`
 21+  - DNS 指向同一台 VPS
 22+  - `Nginx -> upstream mac_conductor_direct -> 100.112.239.13:4317`
 23+  - 用于直连 `mac` 做调试、健康检查和手工运维
 24+
 25+统一原则:
 26+
 27+- 只有 VPS 对公网暴露 `80/tcp` 与 `443/tcp`
 28+- `mini` 与 `mac` 的 `4317` 只应通过 Tailscale 或 WireGuard 被 VPS 访问
 29+- `conductor.makefile.so` 不加 Basic Auth,留给统一入口使用
 30+- `mini-conductor.makefile.so` 与 `mac-conductor.makefile.so` 默认启用 Basic Auth
 31+
 32+## 仓库文件与 VPS 目标路径
 33+
 34+仓库中的配置与 VPS 上建议路径一一对应:
 35+
 36+```text
 37+ops/nginx/baa-conductor.conf                    -> /etc/nginx/sites-available/baa-conductor.conf
 38+ops/nginx/includes/common-proxy.conf           -> /etc/nginx/includes/baa-conductor/common-proxy.conf
 39+ops/nginx/includes/direct-node-auth.conf       -> /etc/nginx/includes/baa-conductor/direct-node-auth.conf
 40+/etc/nginx/sites-enabled/baa-conductor.conf    -> symlink to /etc/nginx/sites-available/baa-conductor.conf
 41+/etc/nginx/.htpasswd-baa-conductor             -> direct-node Basic Auth 凭据
 42+```
 43+
 44+`ops/nginx/baa-conductor.conf` 里写的是实际 VPS 路径,部署时应直接按这个目录结构放置。
 45+
 46+## 前置条件
 47+
 48+部署前确认:
 49+
 50+1. VPS 已安装 `nginx`
 51+2. VPS 能通过 Tailscale 或 WireGuard 访问:
 52+   - `100.71.210.78:4317`
 53+   - `100.112.239.13:4317`
 54+3. 三个域名都已解析到 VPS 公网 IP
 55+4. TLS 证书已经准备好,或已经决定使用哪种签发方式:
 56+   - Let’s Encrypt:默认路径 `/etc/letsencrypt/live/<hostname>/`
 57+   - Cloudflare Origin Cert:把配置中的证书路径改成实际落盘位置
 58+5. 若要启用 Basic Auth,VPS 已安装 `htpasswd` 所在包:
 59+   - Debian/Ubuntu 通常是 `apache2-utils`
 60+
 61+## 部署与启用步骤
 62+
 63+以下步骤假设系统使用 Debian/Ubuntu 风格的 Nginx 目录。
 64+
 65+### 1. 安装依赖
 66+
 67+```bash
 68+sudo apt-get update
 69+sudo apt-get install -y nginx apache2-utils certbot
 70+```
 71+
 72+如果证书通过 Cloudflare DNS challenge 或其他方式获取,按你的现有流程安装对应插件。
 73+
 74+### 2. 验证 VPS 到 mini/mac 的内网连通性
 75+
 76+```bash
 77+curl --fail --max-time 3 http://100.71.210.78:4317/healthz
 78+curl --fail --max-time 3 http://100.112.239.13:4317/healthz
 79+```
 80+
 81+任一命令失败时,不要继续启用公网入口,先修好 VPS 到节点的链路。
 82+
 83+### 3. 准备目录并安装配置
 84+
 85+```bash
 86+sudo install -d -m 0755 /etc/nginx/includes/baa-conductor
 87+sudo install -m 0644 ops/nginx/includes/common-proxy.conf /etc/nginx/includes/baa-conductor/common-proxy.conf
 88+sudo install -m 0644 ops/nginx/includes/direct-node-auth.conf /etc/nginx/includes/baa-conductor/direct-node-auth.conf
 89+sudo install -m 0644 ops/nginx/baa-conductor.conf /etc/nginx/sites-available/baa-conductor.conf
 90+sudo ln -sfn /etc/nginx/sites-available/baa-conductor.conf /etc/nginx/sites-enabled/baa-conductor.conf
 91+```
 92+
 93+如果默认站点会抢占 `80/443`,按你的发行版习惯移除或禁用默认站点。
 94+
 95+### 4. 准备直连域名的 Basic Auth
 96+
 97+```bash
 98+sudo htpasswd -c /etc/nginx/.htpasswd-baa-conductor conductor-ops
 99+```
100+
101+建议:
102+
103+- 至少给 `mini-conductor.makefile.so` 和 `mac-conductor.makefile.so` 打 Basic Auth
104+- 如果运维来源 IP 固定,再把 allowlist 打开,形成“双保险”
105+- 如果域名经过 Cloudflare 代理,先配置真实 IP 恢复,再使用 `allow/deny`
106+
107+### 5. 准备 TLS 证书
108+
109+仓库配置默认引用:
110+
111+```text
112+/etc/letsencrypt/live/conductor.makefile.so/fullchain.pem
113+/etc/letsencrypt/live/conductor.makefile.so/privkey.pem
114+/etc/letsencrypt/live/mini-conductor.makefile.so/fullchain.pem
115+/etc/letsencrypt/live/mini-conductor.makefile.so/privkey.pem
116+/etc/letsencrypt/live/mac-conductor.makefile.so/fullchain.pem
117+/etc/letsencrypt/live/mac-conductor.makefile.so/privkey.pem
118+```
119+
120+两种常见做法:
121+
122+- 直接 Let’s Encrypt:在 DNS 已生效且 `80/tcp` 可达时,用 `certbot` 申请三张证书,或一张覆盖三个 SAN 的证书
123+- Cloudflare 代理:改用 Cloudflare Origin Cert,并把 `baa-conductor.conf` 中的路径改成你实际存放的位置
124+
125+如果证书文件还不存在,`nginx -t` 会失败;先准备好证书,再启用 `443` 配置。
126+
127+### 6. 做语法检查并启用站点
128+
129+```bash
130+sudo nginx -t
131+sudo systemctl enable nginx
132+sudo systemctl reload nginx
133+```
134+
135+首次部署且 Nginx 尚未运行时,把最后一条替换为:
136+
137+```bash
138+sudo systemctl start nginx
139+```
140+
141+## 上线后验证
142+
143+### 入口与跳转
144+
145+```bash
146+curl -I http://conductor.makefile.so
147+curl -I https://conductor.makefile.so/healthz
148+```
149+
150+预期:
151+
152+- `http://conductor.makefile.so` 返回 `301` 到 `https://...`
153+- HTTPS 健康检查返回 `200`
154+
155+### 直连 mini/mac
156+
157+```bash
158+curl -I https://mini-conductor.makefile.so/healthz
159+curl -u conductor-ops:YOUR_PASSWORD https://mini-conductor.makefile.so/healthz
160+curl -u conductor-ops:YOUR_PASSWORD https://mac-conductor.makefile.so/healthz
161+```
162+
163+预期:
164+
165+- 未带认证访问 `mini-conductor.makefile.so` 或 `mac-conductor.makefile.so` 返回 `401`
166+- 带正确认证后返回 `200`
167+
168+### 主备切换边界
169+
170+`conductor.makefile.so` 只做入口层 failover:
171+
172+- 能处理 `mini` 网络不可达、连接超时、`502/503/504`
173+- 不能替代 D1 lease 判断
174+- 不能解决 split-brain
175+
176+也就是说,公网入口切到 `mac` 不代表 `mac` 自动获得合法 leader 身份;真正的写权限仍由 conductor 自己校验。
177+
178+## 建议的运维加固
179+
180+- 只对公网开放 `80/tcp` 与 `443/tcp`
181+- 在主机防火墙里明确拒绝公网访问 `4317`
182+- `mini-conductor.makefile.so` 与 `mac-conductor.makefile.so` 至少保留 Basic Auth
183+- 有固定办公出口时,把 allowlist 叠加到 `direct-node-auth.conf`
184+- 域名走 Cloudflare 代理时,限制直连 origin 的来源
185+- 上线后定期执行 `sudo nginx -t` 和一次带认证的健康检查
M ops/nginx/baa-conductor.conf
+99, -4
  1@@ -1,20 +1,115 @@
  2-# baa-conductor Nginx 配置骨架。
  3-# 具体 upstream、server_name、证书路径与鉴权细节由 T-008 补全。
  4+# 部署目标:
  5+# - /etc/nginx/sites-available/baa-conductor.conf
  6+# - /etc/nginx/sites-enabled/baa-conductor.conf -> symlink to sites-available
  7+# - /etc/nginx/includes/baa-conductor/*.conf 由仓库里的 ops/nginx/includes/* 同步过去
  8+#
  9+# 说明:
 10+# - conductor.makefile.so 作为统一入口,走 mini 主、mac 备的 upstream
 11+# - mini-conductor.makefile.so 与 mac-conductor.makefile.so 直连单节点 upstream
 12+# - 证书路径使用 Let's Encrypt 默认目录,若走 Cloudflare Origin Cert 请替换为实际文件路径
 13+
 14+map $http_upgrade $connection_upgrade {
 15+    default upgrade;
 16+    ''      '';
 17+}
 18 
 19 upstream conductor_primary {
 20-    # TODO: mini 主、mac 备
 21+    server 100.71.210.78:4317 max_fails=2 fail_timeout=5s;
 22+    server 100.112.239.13:4317 backup;
 23+    keepalive 32;
 24+}
 25+
 26+upstream mini_conductor_direct {
 27+    server 100.71.210.78:4317;
 28+    keepalive 16;
 29+}
 30+
 31+upstream mac_conductor_direct {
 32+    server 100.112.239.13:4317;
 33+    keepalive 16;
 34 }
 35 
 36 server {
 37     listen 80;
 38+    listen [::]:80;
 39     server_name conductor.makefile.so mini-conductor.makefile.so mac-conductor.makefile.so;
 40+
 41     return 301 https://$host$request_uri;
 42 }
 43 
 44 server {
 45     listen 443 ssl http2;
 46+    listen [::]:443 ssl http2;
 47     server_name conductor.makefile.so;
 48 
 49-    # TODO: 补全 TLS 证书与反向代理配置
 50+    ssl_certificate     /etc/letsencrypt/live/conductor.makefile.so/fullchain.pem;
 51+    ssl_certificate_key /etc/letsencrypt/live/conductor.makefile.so/privkey.pem;
 52+    ssl_protocols       TLSv1.2 TLSv1.3;
 53+    ssl_session_cache   shared:BAAConductorTLS:10m;
 54+    ssl_session_timeout 1d;
 55+
 56+    access_log /var/log/nginx/baa-conductor.access.log;
 57+    error_log  /var/log/nginx/baa-conductor.error.log warn;
 58+
 59+    location = /healthz {
 60+        proxy_pass http://conductor_primary/healthz;
 61+        include /etc/nginx/includes/baa-conductor/common-proxy.conf;
 62+    }
 63+
 64+    location = /readyz {
 65+        proxy_pass http://conductor_primary/readyz;
 66+        include /etc/nginx/includes/baa-conductor/common-proxy.conf;
 67+    }
 68+
 69+    location = /rolez {
 70+        proxy_pass http://conductor_primary/rolez;
 71+        include /etc/nginx/includes/baa-conductor/common-proxy.conf;
 72+    }
 73+
 74+    location / {
 75+        proxy_pass http://conductor_primary;
 76+        include /etc/nginx/includes/baa-conductor/common-proxy.conf;
 77+    }
 78 }
 79 
 80+server {
 81+    listen 443 ssl http2;
 82+    listen [::]:443 ssl http2;
 83+    server_name mini-conductor.makefile.so;
 84+
 85+    ssl_certificate     /etc/letsencrypt/live/mini-conductor.makefile.so/fullchain.pem;
 86+    ssl_certificate_key /etc/letsencrypt/live/mini-conductor.makefile.so/privkey.pem;
 87+    ssl_protocols       TLSv1.2 TLSv1.3;
 88+    ssl_session_cache   shared:BAAConductorTLS:10m;
 89+    ssl_session_timeout 1d;
 90+
 91+    access_log /var/log/nginx/baa-conductor-mini.access.log;
 92+    error_log  /var/log/nginx/baa-conductor-mini.error.log warn;
 93+
 94+    location / {
 95+        include /etc/nginx/includes/baa-conductor/direct-node-auth.conf;
 96+        proxy_pass http://mini_conductor_direct;
 97+        include /etc/nginx/includes/baa-conductor/common-proxy.conf;
 98+    }
 99+}
100+
101+server {
102+    listen 443 ssl http2;
103+    listen [::]:443 ssl http2;
104+    server_name mac-conductor.makefile.so;
105+
106+    ssl_certificate     /etc/letsencrypt/live/mac-conductor.makefile.so/fullchain.pem;
107+    ssl_certificate_key /etc/letsencrypt/live/mac-conductor.makefile.so/privkey.pem;
108+    ssl_protocols       TLSv1.2 TLSv1.3;
109+    ssl_session_cache   shared:BAAConductorTLS:10m;
110+    ssl_session_timeout 1d;
111+
112+    access_log /var/log/nginx/baa-conductor-mac.access.log;
113+    error_log  /var/log/nginx/baa-conductor-mac.error.log warn;
114+
115+    location / {
116+        include /etc/nginx/includes/baa-conductor/direct-node-auth.conf;
117+        proxy_pass http://mac_conductor_direct;
118+        include /etc/nginx/includes/baa-conductor/common-proxy.conf;
119+    }
120+}
M ops/nginx/includes/common-proxy.conf
+14, -1
 1@@ -1,6 +1,19 @@
 2 proxy_http_version 1.1;
 3+proxy_set_header Upgrade $http_upgrade;
 4+proxy_set_header Connection $connection_upgrade;
 5 proxy_set_header Host $host;
 6+proxy_set_header X-Real-IP $remote_addr;
 7 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
 8+proxy_set_header X-Forwarded-Host $host;
 9+proxy_set_header X-Forwarded-Port $server_port;
10 proxy_set_header X-Forwarded-Proto $scheme;
11 proxy_set_header X-Request-Id $request_id;
12-
13+proxy_connect_timeout 3s;
14+proxy_send_timeout 60s;
15+proxy_read_timeout 60s;
16+proxy_next_upstream error timeout invalid_header http_502 http_503 http_504;
17+proxy_next_upstream_tries 2;
18+proxy_buffering off;
19+proxy_redirect off;
20+client_max_body_size 10m;
21+add_header X-Request-Id $request_id always;
M ops/nginx/includes/direct-node-auth.conf
+8, -4
 1@@ -1,6 +1,10 @@
 2-# 直连节点域名推荐启用的 Basic Auth 配置占位。
 3-# 具体账户文件路径由 T-008 确认。
 4+# 直连 mini/mac 的调试入口默认启用 Basic Auth。
 5+# 如果有固定办公网或 VPN 出口,可再叠加 allowlist:
 6+# satisfy any;
 7+# allow 203.0.113.10;
 8+# allow 2001:db8::/48;
 9+# deny all;
10+# 注意:若域名经过 Cloudflare 代理,要先恢复真实客户端 IP 再使用 allow/deny。
11 
12-auth_basic "Restricted";
13+auth_basic "baa-conductor direct node";
14 auth_basic_user_file /etc/nginx/.htpasswd-baa-conductor;
15-