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.
5.5 KiB
用 HTTPS 提供网页界面
https://k6v3.mckero.dn42/ 在这台主机上是怎么配起来的。模拟器界面本身只在 loopback 上
讲普通 HTTP;由 nginx 终结 TLS 并反代到它。
English: reverse-proxy.md · 两份内容对应,改动请同步。
为什么要用反向代理
tools/webui.py 既没有 TLS 也没有任何认证。让它跑在 loopback 上、由 nginx 面向网络,
意味着可以复用已有的证书和已有的 443 监听,而且这个界面根本不能被直接访问到。
vhost 配置
放在 /etc/nginx/sites-available/k6v3,软链接进 sites-enabled/。仓库里在
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 的日志行正确显示没有客户端。