Web 界面
用 thronectl web 在浏览器中管理 Throne:通过 Docker、systemd 单元或手动运行它,通过 SSH 隧道、HTTPS 或反向代理访问它,并保证它的安全。
thronectl web 为 Throne 提供一个 Web 界面。在浏览器中,你可以查看和切换连接、选择和测试配置档、导入配置档和更新订阅、查看流量和日志、更改设置,以及备份和恢复。它适用于无界面 Throne,也适用于开启了 Throne API 的桌面版 Throne。Throne for Android 没有 Throne API,因此 Web 界面无法管理它。
Web 界面是 thronectl 的一部分,而不是 Throne 的一部分。thronectl web 是一个小型 Web 服务器,即网关(gateway):浏览器与网关通信,网关再通过 Throne API 与 Throne 通信,使用的地址和密钥与其他 thronectl 命令相同。Throne 本身不提供任何网页。
当你想直接查看和点击时,例如在手机上或在没有 thronectl 的电脑上,请使用 Web 界面。脚本、快捷键和自动化请使用 thronectl 命令。两者可以同时使用。
快速入门
请选择与 Throne 运行方式相符的方案。每种方案都让网关运行在 Throne 所在的设备上,Throne 会在那里响应它的所有请求,包括管理请求。
Docker
设置了 THRONE_WEB_LISTEN 时,Docker 镜像会在同一个容器中、在 Throne 旁边运行网关。在 Throne 仓库的 compose.yaml 中,把这个变量添加到服务 throne,并发布端口:
services:
throne:
environment:
THRONE_WEB_LISTEN: "*:9096"
ports: ["2080:2080", "9096:9096"]
在互联网可以访问的主机上,只发布局域网地址,例如 "192.168.1.2:9096:9096"。使用 docker run 时,添加 -e THRONE_WEB_LISTEN='*:9096' -p 9096:9096。然后启动容器并查看它的日志:
docker compose up -d
docker compose logs throne
日志中会显示密码和自签名证书的指纹。打开 https://<host>:9096/,按照局域网中的说明比对指纹,然后登录。
- Throne 的 API 一开始响应,容器的入口点就会在 Throne 旁边启动
thronectl web,并自行找到 API 的地址和密钥。网关退出后,入口点会在 3 秒后再次启动它,因此错误的THRONE_WEB_*值会每 3 秒在日志中出现一次。容器重启时,网关也会随之重启。 - 网关把状态保存在数据卷上的
/var/lib/throne/web中。 - 其他
THRONE_WEB_*变量,例如THRONE_WEB_TLS、THRONE_WEB_ALLOW_HOST或THRONE_WEB_PASSWORD_FILE(参见选项),都放在同一个environment:块中。
Linux 软件包(systemd)
.deb 和 .rpm 软件包会把 thronectl 安装为 /usr/bin/thronectl,并安装第二个单元 throne-web.service,它在 throne.service 之外运行 thronectl web。它不以 root 身份运行:systemd 为它分配一个专用的动态用户,把 Throne 的密钥 /var/lib/throne/config/api.key 作为凭据(credential)传给它,并把它的状态保存在 /var/lib/throne-web 中。软件包不会启用它:
sudo systemctl enable --now throne-web
sudo journalctl -u throne-web | grep 'web UI password'
在 Throne 首次启动创建密钥文件之前,该单元会失败,systemd 每 5 秒重新启动它一次,与其他任何失败之后一样。它监听 127.0.0.1:9096:请通过 SSH 隧道打开它。要更改它的选项,请取消 /etc/throne/web.env 中相应变量的注释(参见选项),然后运行 sudo systemctl restart throne-web。升级会保留你的修改。切勿把密钥或密码写进这个文件。该单元的用户无法读取只有 root 能读取的文件,例如放在其他位置的密钥、TLS 私钥或密码文件;/etc/throne/web.env 说明了如何通过 LoadCredential= 传入它们(sudo systemctl edit throne-web)。低于 1024 的端口需要在该单元的 AmbientCapabilities= 和 CapabilityBoundingSet= 中加入 CAP_NET_BIND_SERVICE。
手动运行,适用于任何系统
在运行 Throne 的设备上安装 thronectl(参见安装 thronectl),像其他命令一样把它连接到 Throne(参见将 thronectl 连接到 Throne),然后启动网关:
export THRONE_KEY_FILE=~/.config/throne/api-key
thronectl web
它会打印一次密码,以及它的服务地址:
thronectl web: created the web UI password: k7mfq-2xhpa-9tnwe-c3rjd
thronectl web: change it with 'thronectl web passwd'
thronectl web: serving on http://127.0.0.1:9096/ for Throne at 127.0.0.1:9095
在同一设备的浏览器中打开 http://127.0.0.1:9096/,用这个密码登录。thronectl web 会一直运行,直到你按下 Ctrl+C。没有 Throne 时它也能启动:在 Throne 响应之前,Web 界面会显示无法访问 Throne。
对于桌面版 Throne,请先开启 API(设置(Settings)→ Integration Settings → Throne API,参见在 Throne 中开启 API),并按照将 thronectl 连接到 Throne 中的说明把密钥保存到文件中。在 Windows 上,在 PowerShell 中:
$env:THRONE_KEY_FILE = "$HOME\throne-api-key.txt"
thronectl web
从其他设备访问
默认情况下,网关监听 127.0.0.1:9096,只有 Throne 所在设备上的程序能访问。要从其他设备访问,请使用 SSH 隧道、局域网中的 HTTPS 或反向代理。
SSH 隧道
如果你能通过 SSH 登录该设备,请让网关保持在环回地址上,并从你的电脑建立到它的隧道:
ssh -L 9096:127.0.0.1:9096 user@host
在该会话打开期间,在你的电脑上打开 http://localhost:9096/(Docker 方案通过 HTTPS 提供服务,请使用 https://)。设备所在网络中的任何东西都无法访问网关,并且 SSH 会加密流量。如果你电脑上的 9096 端口已被占用,请换一个本地端口:ssh -L 19096:127.0.0.1:9096 user@host,然后打开 http://localhost:19096/。
局域网(HTTPS)
要从网络中的其他设备直接打开 Web 界面,请让网关监听一个网络地址:
thronectl web --listen '*:9096'
*:9096 监听设备的所有地址,192.168.1.2:9096 只监听一个地址。在网络地址上,网关通过 HTTPS 提供服务。除非你给它提供证书,否则它会在状态目录中创建一个自签名证书,并在每次启动时打印该证书的指纹:
thronectl web: created a self-signed TLS certificate in /home/you/.config/thronectl/web/tls
thronectl web: TLS certificate SHA-256 fingerprint: 3F:A1:5C:…:9C
thronectl web: serving on https://localhost:9096/ for Throne at 127.0.0.1:9095
thronectl web: serving on https://192.168.1.2:9096/ for Throne at 127.0.0.1:9095
浏览器会警告该证书不受信任,因为没有证书颁发机构为它签名。在接受它之前:
- 从警告页面或地址栏打开证书详情。
- 将它的 SHA-256 指纹与网关打印的指纹进行比对。
- 只有两者一致时才接受证书。如果不一致,说明你和设备之间有东西在拦截连接:不要登录。
Web 界面的 System 页面也会显示指纹。请在设备的防火墙中放行 9096 端口,并且只对你的网络放行。
- 证书包含
localhost、设备的主机名,以及网关创建证书时设备拥有的地址。要创建新证书,例如在设备地址变化后,请删除状态目录中的tls文件夹并重启网关。指纹也会随之改变。 - 自签名证书的有效期为 10 年。证书过期或无法读取时,网关会在下次启动时创建一个新证书。
- 要使用你自己的证书,请通过
--tls-cert和--tls-key提供 PEM 格式的证书链和私钥文件。网关在启动时读取它们;续期后请重启网关。
在反向代理之后
nginx 或 Caddy 等反向代理可以用你自己的主机名、以浏览器信任的证书提供 Web 界面。请让网关保持在环回地址上,它在那里通过普通 HTTP 提供服务,只有本设备上的程序能访问,并把主机名告诉它:
thronectl web --allow-host throne.example.com
在环回地址上,网关只响应发往 localhost、127.0.0.1、::1 以及 --allow-host 中名称的请求(参见 Host 检查)。如果代理运行在另一台设备或另一个容器中,网关就必须监听网络地址:请在那里加上 --tls off,并且只让代理能访问该地址,因为密码和会话会以未加密的方式经过它。
代理必须:
- 按浏览器发送时的原样转发
Host头。 网关会拒绝Origin头与Host头不一致的更改请求(403 csrf)。除非你进行设置,否则 nginx 会替换这个头;Caddy 会保留它。 - 接受最大 512 MiB 的请求体,这是 Web 界面可以恢复的最大备份。除非你设置
client_max_body_size,否则 nginx 只接受 1 MiB。 - 随到随转发响应。 Web 界面以流的形式接收实时更新和日志。网关会发送
X-Accel-Buffering: no,这会为这些响应关闭 nginx 的缓冲。
nginx:
server {
listen 443 ssl;
server_name throne.example.com;
ssl_certificate /etc/ssl/certs/throne.example.com.pem;
ssl_certificate_key /etc/ssl/private/throne.example.com.key;
location / {
proxy_pass http://127.0.0.1:9096;
proxy_set_header Host $http_host;
client_max_body_size 512m;
}
}
Caddy:
throne.example.com {
reverse_proxy 127.0.0.1:9096
}
在代理之后,所有浏览器都从代理的地址访问网关,因此一个浏览器的失败登录也会把其他浏览器锁在外面。
密码
Web 界面只有一个密码,没有用户账户。网关首次启动时会创建一个随机密码,把它的哈希(argon2id)保存在状态目录中的 password 文件里,并打印一次密码:
thronectl web: created the web UI password: k7mfq-2xhpa-9tnwe-c3rjd
thronectl web: change it with 'thronectl web passwd'
| 方案 | 在哪里找到首个密码 |
|---|---|
| Docker | docker compose logs throne |
| systemd 单元 | sudo journalctl -u throne-web | grep 'web UI password' |
| 手动运行 | 你启动 thronectl web 的终端 |
这一行会一直留在那份日志中,所以请在首次登录后更改密码:
- 在 Web 界面的
System页面中更改。这会让其他所有浏览器退出登录。 - 用
thronectl web passwd更改,需要以网关运行时的用户身份、并使用相同的状态目录(--state-dir或THRONE_WEB_STATE_DIR)运行。在终端中,它会询问两次,且不显示你输入的内容;否则它从标准输入读取一行。正在运行的网关会在 5 秒内发现新密码,并让所有人退出登录。在 Docker 方案中:docker compose exec throne thronectl web passwd。
密码至少需要 8 个字符。在 systemd 下,请以 root 身份设置密码;该文件仍归单元的用户所有:
sudo thronectl web passwd --state-dir /var/lib/throne-web
忘记密码。 用 thronectl web passwd 设置新密码,或者删除状态目录中的 password 文件并重启网关。网关随后会创建一个新密码并打印出来。
密码文件。 --password-file FILE(或 THRONE_WEB_PASSWORD_FILE)会从文件中读取明文密码,而不是使用保存的哈希,例如来自 Docker secret 的文件。末尾的一个换行符会被忽略;空文件会报错。网关每 5 秒检查一次该文件,文件改变时会让所有人退出登录。如果文件变为空或无法读取,网关会保留原来的密码,并在日志中说明。使用密码文件时,System 页面无法更改密码;在设置了 THRONE_WEB_PASSWORD_FILE 时,thronectl web passwd 也不行:请直接修改该文件。
services:
throne:
environment:
THRONE_WEB_LISTEN: "*:9096"
THRONE_WEB_PASSWORD_FILE: /run/secrets/throne_web_password
secrets: [throne_web_password]
secrets:
throne_web_password:
file: ./web-password.txt
会话。 只要浏览器空闲不超过 12 小时,登录就一直有效,最长 7 天。System 页面上的 Sign out 会结束会话。网关把会话保存在内存中,因此重启网关会让所有人退出登录。网关最多保留 64 个会话;再有新的登录时,会结束最久未使用的会话。
Web 界面的功能
| 页面 | 可以做什么 |
|---|---|
Status |
查看状态、正在运行的配置档、Kill Switch 和当前速率;连接和断开连接;开启和关闭 TUN 与系统代理(系统代理仅适用于桌面版 Throne);选择路由配置档;在某项设置需要时重启配置档;查看和取消测试。 |
Profiles |
浏览分组和配置档及其测试结果,对它们进行筛选和排序;连接某个配置档或某个分组中最好的配置档;测试延迟、IP 和速度;清除结果;删除配置档。导入分享链接、文件或 URL。添加分组和订阅,更新和删除它们。 |
Traffic |
最近两分钟的速率,以及按配置档或按应用统计的 24 小时、7 天或 30 天流量总计。 |
Logs |
实时显示 Throne 的日志,支持暂停、文本筛选、显示时间和复制,并为警告和错误着色。页面保留最新的 5000 行。 |
Settings |
API 管理的设置、geo 文件,以及正在运行的配置档的规则集。 |
Backup |
下载你所选部分的备份。上传归档,查看其中的内容,并恢复你所选的部分;Throne 会重启,Web 界面会等待它。 |
System |
Throne 和网关的版本、网关是否拥有管理权限、证书指纹、密码、退出登录、重启 Throne 以及关闭 Throne。 |
在其他地方所做的更改,例如在 Throne 窗口中、通过 thronectl 或由订阅更新引起的更改,会在几秒钟内显示在 Web 界面中:网关会询问 Throne 发生了什么变化(GET /v1/events),页面只重新加载变化的部分。
仍需在桌面版中完成的操作
Web 界面不能编辑:
- 路由配置档的规则(它只能激活某个路由配置档),
- WARP 和 OTP 条目,
- 配置档自身的设置,例如服务器、端口或 TLS,
- 实时连接列表,
- API 不管理的设置。
请在桌面版 Throne 中设置这些内容,然后用备份迁移过来:在桌面版中创建备份,然后在 Backup 页面上恢复,或者按照迁移桌面版的配置中的说明用 thronectl backup restore 恢复。Throne 会拒绝含有会在其设备上运行程序或读取文件的配置档(例如额外核心配置档)的归档(invalid_archive)。
选项
thronectl web [--listen ADDR] [--state-dir DIR] [--tls auto|on|off] [--tls-cert FILE --tls-key FILE]
[--allow-host LIST] [--password-file FILE]
thronectl web passwd [--state-dir DIR]
每个选项都有对应的环境变量。参数优先于其对应的变量。
| 参数 | 变量 | 默认值与含义 |
|---|---|---|
--listen ADDR |
THRONE_WEB_LISTEN |
127.0.0.1:9096。接受 PORT(在 127.0.0.1 上)、IP:PORT、[IPv6]:PORT 和 *:PORT(所有地址),与 Throne 的 --api-listen 相同。不接受主机名。 |
--state-dir DIR |
THRONE_WEB_STATE_DIR |
保存密码哈希和自签名证书的文件夹,网关创建它时只允许自己的用户读取。默认值:systemd 的 $STATE_DIRECTORY,否则为你的配置文件夹中的 thronectl/web:Linux 上为 ~/.config/thronectl/web,macOS 上为 ~/Library/Application Support/thronectl/web,Windows 上为 %AppData%\thronectl\web。 |
--tls auto|on|off |
THRONE_WEB_TLS |
auto(默认):在网络地址上使用 HTTPS,在环回地址上使用普通 HTTP。on:在环回地址上也使用 HTTPS。off:普通 HTTP;在网络地址上只应在带 TLS 的反向代理之后使用。off 与证书同时使用会报错。 |
--tls-cert FILE、--tls-key FILE |
THRONE_WEB_TLS_CERT、THRONE_WEB_TLS_KEY |
你自己的 PEM 格式证书链和私钥,用来代替自签名证书。两者都要提供。在 auto 下,它们在环回地址上也会开启 HTTPS。 |
--allow-host LIST |
THRONE_WEB_ALLOW_HOST |
浏览器还可以用来访问网关的其他主机名,用逗号或空格分隔,例如反向代理的名称。参见 Host 检查。 |
--password-file FILE |
THRONE_WEB_PASSWORD_FILE |
明文密码,例如 Docker secret,用来代替保存的哈希。参见密码。 |
网关查找 Throne 的方式与任何 thronectl 命令相同:地址来自 --addr 或 THRONE_ADDR(默认为 127.0.0.1:9095),密钥来自 --key-file、THRONE_KEY 或 THRONE_KEY_FILE,在 systemd 下也可以来自凭据 api.key(LoadCredential=api.key:/path/to/key)。--timeout 对它不起作用。
thronectl web 的退出码:
| 退出码 | 何时 |
|---|---|
0 |
它被 Ctrl+C 或 SIGTERM 停止。它会在 5 秒内关闭。 |
2 |
选项或变量错误、没有密钥、密码文件为空或无法读取、password 文件已损坏,或者证书无法加载。 |
3 |
它无法监听,例如因为端口被其他程序占用,或者发生了其他错误。 |
Host 检查
网页可以让你的浏览器以该网页自己的主机名向网关发送请求(DNS rebinding)。因此网关会检查每个请求的主机名:
- 在环回地址上,它接受
localhost、127.0.0.1、::1以及--allow-host中的名称。 - 在网络地址上,它接受任何名称,除非设置了
--allow-host。设置后,它接受这些名称、环回名称和 IP 地址。
对于其他名称,它会返回 421 Misdirected Request。
安全
- Web 界面是一个管理客户端。 任何登录的人都能做
thronectl能做的事,包括管理请求:导入和删除配置档、更改设置、下载以明文保存配置档凭据和 OTP 密钥的备份、恢复备份,以及重启或关闭 Throne。只把密码交给允许管理 Throne 的人。 - API 密钥只留在网关中。 浏览器永远不会收到它。浏览器只会得到一个会话 Cookie 和一个会话令牌,每个请求都需要这两者。
- 登录限制。 同一地址(IPv6 为同一个 /64 网络)在 5 分钟内登录失败 5 次后,网关会在 60 秒内拒绝来自该地址的登录,之后每再失败一次,拒绝时间加倍,最长 15 分钟。如果一分钟内总共有超过 30 次登录失败,它会拒绝所有登录,直到这一分钟过去。成功登录会清除其所在地址的失败记录。失败的登录至少需要半秒钟,网关的日志会列出被锁定的地址。
- 首个密码会留在日志中。 systemd 日志可以被 root 以及
adm或systemd-journal等组的成员读取;容器日志可以被任何能运行docker的人读取。首次登录后请更改密码。 - 优先使用环回地址和 SSH 隧道。 在网络上请使用 HTTPS 并核对证书指纹,或者使用浏览器信任的证书。网络地址上的普通 HTTP 会以未加密的方式传输密码和会话;网关在启动时会对此发出警告。
- 在 Throne 所在的设备上运行网关。 这样 Throne 的 API 可以继续留在环回地址上。运行在其他设备上的网关需要 API 在网络上监听,管理请求还需要
--api-admin-lan。
故障排除
| 症状 | 原因与解决方法 |
|---|---|
Throne is not reachable. Retrying…(throne_unreachable) |
Throne 没有运行、它的 API 已关闭,或者网关的地址不对。请检查 --addr 或 THRONE_ADDR,并用网关的设置运行 thronectl status。Web 界面每 2 秒重试一次,Throne 响应后会重新加载。 |
Throne refuses the web UI 或 Throne refused the connection(throne_rejected) |
网关的密钥不对。请把 Throne 当前的密钥(无界面 Throne 数据目录中的 config/api.key)交给网关,然后重启网关。运行在其他设备上的网关,还可能因其地址失败 5 次而被 Throne 拒绝 60 秒,例如同一地址上另一个使用旧密钥的客户端造成的。 |
The web UI's connection to Throne lacks admin rights(forbidden):日志、导入、设置、删除、备份、重启和关闭都会失败 |
网关没有运行在 Throne 所在的设备上,而 Throne 只响应来自该设备的管理请求。请在 Throne 所在的设备上运行网关,或者让 Throne 响应来自网络的管理请求:无界面 Throne 使用 --api-admin-lan(THRONE_API_ADMIN_LAN=1),桌面版使用 允许从局域网进行管理(Allow management from the local network)。System 页面会显示网关是否拥有管理权限。 |
421 Misdirected Request: this host name is not accepted |
Host 检查:你用网关不接受的名称打开了它。请使用 localhost 或 IP 地址,或者用 --allow-host 添加该名称。 |
在反向代理之后无法登录:The web UI server refused the request because it came from another origin(403 csrf) |
代理更改了 Host 头。请原样转发它,例如在 nginx 中使用 proxy_set_header Host $http_host;。 |
Too many failed attempts. Try again in … s.(rate_limited) |
等待倒计时结束。参见安全。 |
| 浏览器对证书发出警告 | 使用自签名证书时这是正常的。请按照局域网中的说明比对指纹。 |
thronectl web 退出:address already in use; --listen picks another address |
其他程序或另一个 thronectl web 占用了 9096 端口。请停止它,或者指定其他 --listen。 |
通过 nginx 恢复备份时失败,返回 413 |
设置 client_max_body_size 512m;。 |
Logs 页面提示 This Throne is too old to share its log with the web UI,或者在 Throne 窗口中所做的更改最多要过 30 秒才显示 |
Throne 比网关旧,没有 GET /v1/logs 或 GET /v1/events。没有事件时,网关每 2 秒读取一次 Throne 的状态,每 30 秒读取一次其他所有内容。请更新 Throne。 |
The password comes from a password file |
修改 --password-file 指定的文件。网关会在 5 秒内发现这一更改。 |