Files
uv-k5-v3-emulator/docs/reverse-proxy.zh-CN.md
T
mckero 3df3c1b16d Add Chinese translations of all three documents
Full translations rather than summaries, section-for-section with the English:
README (13 sections), AGENTS.md (21), and docs/reverse-proxy.md. Each pair
cross-links to the other and says the two are kept in step, since documentation
that has silently diverged is worse than documentation that does not exist.

Verified rather than eyeballed: heading counts and order match in both pairs,
every internal .md link resolves, every tool named in either README exists, and
every test in run_tests.sh appears in both.

Translating turned up four things that were already stale in the English, which
is the honest argument for having done it this way -- a summary would not have
touched them:

  - the endpoint table was missing /api/ptt, /api/power/<action> and
    /api/logs, and did not mention the speaker field on /api/status
  - the modelled-peripheral list omitted TIM2
  - the audit table still called TIM a stub, unchanged since fdcbe80 modelled
    TIM2
  - neither README listed uvk5_logs.py, uvk5_stream.py, uvk5_supervisor.py or
    test_kill_emulator.sh, which are part of the repo rather than scratch

The ad-hoc probe scripts are now acknowledged in one line instead of being
silently absent, and described as what they are: quick to reach for, not
polished.

Unit tests: 89 passed.
2026-08-29 16:48:27 +01:00

128 lines
5.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 用 HTTPS 提供网页界面
`https://k6v3.mckero.dn42/` 在这台主机上是怎么配起来的。模拟器界面本身只在 loopback 上
讲普通 HTTP;由 nginx 终结 TLS 并反代到它。
*English: [reverse-proxy.md](reverse-proxy.md) · 两份内容对应,改动请同步。*
## 为什么要用反向代理
`tools/webui.py` 既没有 TLS 也没有任何认证。让它跑在 loopback 上、由 nginx 面向网络,
意味着可以复用已有的证书和已有的 443 监听,而且**这个界面根本不能被直接访问到**。
## vhost 配置
放在 `/etc/nginx/sites-available/k6v3`,软链接进 `sites-enabled/`。仓库里在
[`deploy/nginx-k6v3.conf`](../deploy/nginx-k6v3.conf) 存了一份副本,
因为这里没有别的东西给 `/etc` 做版本控制。
server {
listen 172.21.91.140:80;
listen [fd3c:3f9b:6424:2::5]:80;
server_name k6v3.mckero.dn42;
return 301 https://$host$request_uri;
}
server {
listen 172.21.91.140:443 ssl;
listen [fd3c:3f9b:6424:2::5]:443 ssl;
server_name k6v3.mckero.dn42;
ssl_certificate /etc/letsencrypt/live/mckero-wildcard/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/mckero-wildcard/privkey.pem;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_buffering off;
proxy_request_buffering off;
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
tcp_nodelay on;
}
}
启动服务时绑定到 loopback,因为只有 nginx 需要访问它:
python3 tools/webui.py --frame-addr 0x200013DC --status-addr 0x2000175C \
--host 127.0.0.1
## 那些不能省的配置项
**`proxy_buffering off`。** `/stream` 是一个无限长的
`multipart/x-mixed-replace` 响应。开着缓冲的话,nginx 会把帧攒起来,
于是画面一阵一阵地到、或者看起来卡死了。**这是别人重写 vhost 时最可能漏掉的一项。**
**`proxy_read_timeout` 要远大于空闲帧间隔。** 这个流每 `IDLE_FRAME_INTERVAL_S`(2 秒)
发一个保活帧,所以默认的 60 秒本来还能活 —— 但**一个被暂停的 guest 什么都不产生**,
那时默认值就会掉连接。
**`X-Forwarded-For`。** 日志面板会把每一行归属到一个客户端 IP。在代理后面
`REMOTE_ADDR` 恒为 127.0.0.1,所以**没有这个头的话每一条都会显示成来自服务器自己**。
`webui.py` 只信任第一跳。
**`tcp_nodelay on`。** 按键响应又小又频繁;Nagle 算法会恰好给那些**以延迟为关键**的
请求增加延迟。
## 不需要新地址,也不需要新证书
两者都是刻意的:
- 443 与这些地址上的其他 vhost 共用,靠 SNI 区分,所以不占用额外的 IP。
- 已有的 `*.mckero.dn42` 通配证书本来就覆盖这个名字,所以不需要签发任何东西。这样检查:
openssl x509 -in /etc/letsencrypt/live/mckero-wildcard/fullchain.pem \
-noout -text | grep -A1 'Subject Alternative Name'
**只绑定了 DN42 地址。** 这台主机上的公网地址同样在 443 上监听,而它们**完全没被碰过** ——
**暴露范围是由 `listen` 地址决定的**,所以这个界面从互联网上访问不到。
## DNS
指向它的记录:
k6v3.mckero.dn42. A 172.21.91.140
k6v3.mckero.dn42. AAAA fd3c:3f9b:6424:2::5
## 配置过程中踩到的坑
**`http2 on;` 需要 nginx 1.25.1+。** 这台主机跑的是 1.22.1,那条指令不存在。
更糟的是 `nginx -t` 是在**创建软链接之前**跑的,所以它通过了,
而随后的 `systemctl reload` 失败并让 nginx **停止运行** ——
在那一行被删掉之前,其他站点全都断了。
**先建软链接,再 `nginx -t`,然后 reload。** 在 1.22 上启用 HTTP/2 的语法是
`listen ... ssl http2;`。
**新加的 `listen` 地址需要 reload 才会生效。** 在上面那次失败的 reload 之后,
`systemctl start` 把 nginx 拉回来了,但它**没有绑定** `[fd3c:3f9b:6424:2::5]:443`;
v6 请求失败,而**日志里没有任何错误**。第二次 `systemctl reload nginx` 才创建了那个 socket。
如果一个新加的地址拒绝连接,**先看 `ss -ltnp | grep 443`**,再去别处找。
## 验证
# 两个协议族都测,而且要校验证书,不要用 -k 跳过
curl -s -o /dev/null -w '%{http_code}\n' \
--resolve 'k6v3.mckero.dn42:443:172.21.91.140' \
https://k6v3.mckero.dn42/
curl -s -g -o /dev/null -w '%{http_code}\n' \
--resolve 'k6v3.mckero.dn42:443:[fd3c:3f9b:6424:2::5]' \
https://k6v3.mckero.dn42/
# 这个流必须持续投递帧,而不是最后一次性全来
curl -sk --resolve 'k6v3.mckero.dn42:443:172.21.91.140' \
https://k6v3.mckero.dn42/stream | head -c 20000 | grep -c PNG
# 日志归属:条目里应该带真实客户端地址,而不是 127.0.0.1
curl -sk --resolve 'k6v3.mckero.dn42:443:172.21.91.140' \
https://k6v3.mckero.dn42/api/logs
配置完成后的实测:两个协议族都是 HTTP 200 且证书校验通过,第一帧在 0.01 秒到,
空闲画面下 12 秒 7 帧,日志条目正确归属到 `172.21.91.140` 和
`fd3c:3f9b:6424:2::5`,而固件串口和 QEMU 的日志行正确显示没有客户端。