- commit
- 160a425
- parent
- e4bbe2e
- author
- im_wower
- date
- 2026-03-21 19:36:50 +0800 CST
feat: add nginx and VPS ops configuration
5 files changed,
+320,
-18
+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
+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` 和一次带认证的健康检查
+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+}
+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;
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-