Hermes WebUI & Dashboard 部署方案¶
一、架构总览¶
Internet
│
┌──────────────┴──────────────┐
│ │
▼ ▼
┌─────────────────────┐ ┌─────────────────────────┐
│ hermes.wonius.top │ │ db.hermes.wonius.top │
└──────────┬──────────┘ └────────────┬──────────────┘
│ │
Caddy :443 │
┌──────────┴──────────┐ │
│ │ │
▼ ▼ ▼
┌─────────────────┐ ┌─────────────────┐ ┌──────────────────────┐
│ hermes-webui │ │ hermes-auth- │ │ hermes-dashboard │
│ :8787 │ │ proxy :9118 │ │ :9119 │
│ │ │ │ │ │
│ 自带认证模块 │ │ 独立认证层 │ │ 无内置认证 │
│ api/auth.py │ │ + 反向代理 │ │ │
│ 认证状态: 未启用 │ │ 密码: Iron2026! │ │ │
└─────────────────┘ └────────┬─────────┘ └──────────────────────┘
│ 无认证
└──────────→ (直接代理转发)
关键点:所有服务均运行在同一台服务器,监听 127.0.0.1,仅 Caddy(:443)对公网暴露。
二、组件说明¶
2.1 hermes-webui(:8787)¶
进程:
/root/.hermes/hermes-agent/venv/bin/python /root/.hermes/hermes-webui/server.py
作用:Hermes Agent 的主 Web 管理界面,提供会话管理、配置、API Keys 等功能。
认证状态:内置 api/auth.py 认证模块(Cookie Session,PBKDF2 + HMAC),但当前未启用。
未启用原因:
- 环境变量 HERMES_WEBUI_PASSWORD 未设置
- settings.json 中无 password_hash
- is_auth_enabled() 返回 False → check_auth() 直接放行
→ 当前任何人都可以直接访问 https://hermes.wonius.top 完整界面。
Caddy 配置:
hermes.wonius.top {
root * /usr/share/caddy
reverse_proxy localhost:8787
}
注:
root指令在仅有reverse_proxy时不产生实际作用(无file_server),保留仅供将来扩展静态托管。
WebUI 路由说明:
- 静态资源(/static/*):Python server.py 自己处理,不走 Caddy
- 动态路由(/, /login, /api/*):由 routes.py 的 handle_get / handle_post 处理
2.2 hermes-dashboard(:9119)¶
进程:
/root/.hermes/hermes-agent/venv/bin/python -m hermes_cli.main dashboard \
--host 127.0.0.1 --port 9119 --insecure --no-open
作用:Hermes Agent 轻量 Web Dashboard,提供配置、API Keys、会话管理界面。
认证状态:无内置认证(独立服务,无 api/auth.py 模块)。
→ 为保护敏感信息,通过 hermes-auth-proxy 在外层增加独立认证层。
访问方式:https://db.hermes.wonius.top(必须通过认证)
2.3 hermes-auth-proxy(:9118)¶
进程:/usr/bin/python3 /root/.hermes/auth_proxy.py(systemd 管理)
服务文件:/etc/systemd/system/hermes-auth-proxy.service
作用:
1. 为 hermes-dashboard 提供独立的认证层(Cookie Session)
2. 作为反向代理,将认证后的请求转发给 :9119
认证机制:
| 项目 | 值 |
|---|---|
| 密码 | Iron2026! |
| 密码存储 | /root/.hermes/auth_proxy.pass(PBKDF2-SHA256,60万次迭代) |
| 签名密钥 | /root/.hermes/auth_proxy.key(secrets.token_urlsafe(64),自动生成) |
| Cookie 名称 | hermes_auth |
| Cookie 有效期 | 12 小时 |
| Cookie 安全属性 | HttpOnly + Secure + SameSite=Lax |
| 防暴力破解 | 同一 IP 60 秒内最多 5 次尝试 |
Session Cookie 格式:user|timestamp|hmac_sha256_signature
- 签名内容:f"{user}|{ts}",用 SECRET_KEY 生成 HMAC-SHA256,取前 32 字符
Caddy 配置:
db.hermes.wonius.top {
reverse_proxy localhost:9118
}
三、Caddy 配置¶
文件:/etc/caddy/Caddyfile
{
email "admin@wonius.top"
}
hermes.wonius.top {
root * /usr/share/caddy
reverse_proxy localhost:8787
}
db.hermes.wonius.top {
reverse_proxy localhost:9118
}
两个域名共用同一个 Caddy 进程,各自独立反向代理。
四、认证流程详解¶
4.1 WebUI(理论认证流程,当前未启用)¶
用户请求 ──→ check_auth()
│
├─ path 在 PUBLIC_PATHS?
│ PUBLIC_PATHS = {
│ /login, /health, /favicon.ico,
│ /api/auth/login, /api/auth/status, /static/*
│ }
│ → 是:放行
│ → 否:检查 hermes_session cookie
│
├─ cookie 无效或缺失?
│ → API 路径:返回 401 JSON
│ → 页面路径:302 重定向到 /login
│
└─ POST /api/auth/login
→ verify_password() 验证 PBKDF2
→ 有效:create_session() 生成签名 cookie
4.2 Dashboard(auth_proxy 认证流程)¶
用户请求 https://db.hermes.wonius.top/
│
├─ GET / ──────────────────────────────────────────────┐
│ ├─ 有有效 hermes_auth cookie? │
│ │ → 是:proxy GET / 到 dashboard (:9119) │
│ │ 返回 Dashboard HTML │
│ │ → 否:serve_login() 返回自定义登录页 │
│ └─ /assets/*, /_next/* 静态资源? │
│ → 直接 proxy 转发(无需认证) │
│ │
├─ POST /login ───────────────────────────────────────┐
│ ├─ 超过速率限制? │
│ │ → 429 Too Many Requests │
│ │ │
│ ├─ 密码错误? │
│ │ → 401 + {"detail":"Invalid credentials"} │
│ │ │
│ └─ 密码正确? │
│ → PBKDF2 验证通过 │
│ → sign("admin", unix_timestamp) 生成 cookie │
│ → proxy GET / 到 dashboard (:9119) │
│ → 在响应中注入 Set-Cookie: hermes_auth=... │
│ → 浏览器收到 Dashboard HTML + 保存 Cookie │
│ │
└─ 其他请求 ──────────────────────────────────────────┐
├─ 无效 cookie → 401 Unauthorized │
└─ 有效 cookie → proxy 到 :9119 │
五、文件清单¶
| 文件路径 | 说明 |
|---|---|
/etc/caddy/Caddyfile |
Caddy 反向代理配置 |
/root/.hermes/auth_proxy.py |
独立认证代理(含登录页、反向代理) |
/root/.hermes/auth_templates/login.html |
登录页 HTML 模板 |
/root/.hermes/auth_proxy.key |
HMAC 签名密钥 |
/root/.hermes/auth_proxy.pass |
PBKDF2 密码哈希 |
/root/.hermes/start-dashboard.sh |
Dashboard 启动脚本 |
/etc/systemd/system/hermes-auth-proxy.service |
auth_proxy systemd 服务 |
/etc/systemd/system/hermes-dashboard.service |
dashboard systemd 服务 |
/root/.hermes/hermes-webui/server.py |
WebUI 主进程 |
/root/.hermes/hermes-webui/api/auth.py |
WebUI 内置认证模块(未启用) |
/root/.hermes/hermes-webui/api/routes.py |
WebUI 路由处理 |
六、运维命令¶
# 查看 auth_proxy 日志
tail -f /var/log/hermes-auth-proxy.log
# 查看 dashboard 日志
tail -f /var/log/hermes-dashboard.log
# 重启 auth_proxy
sudo systemctl restart hermes-auth-proxy
# 重启 dashboard
sudo systemctl restart hermes-dashboard
# 重载 Caddy 配置
sudo systemctl reload caddy
# 查看各服务运行状态
systemctl status hermes-auth-proxy hermes-dashboard caddy
# 手动测试(绕过 auth_proxy 直接访问 dashboard)
curl http://127.0.0.1:9119/
# 手动测试 auth_proxy
curl http://127.0.0.1:9118/
七、已知问题¶
7.1 curl -sI(HEAD 请求)返回 502¶
现象:curl -sI https://db.hermes.wonius.top/ 返回 502 EOF
原因:hermes-auth-proxy 基于 Python BaseHTTPRequestHandler,urllib 代理 HEAD 请求时,hermes-dashboard 返回空 body,Caddy 无法确定响应结束边界,导致 EOF。
影响范围:仅影响 curl -I / curl --head 命令,不影响真实浏览器。
状态:暂不修复。
7.2 WebUI 无认证保护¶
https://hermes.wonius.top 当前无任何认证,任何人可直接访问完整界面(含会话、配置内容)。
建议后续方案(二选一):
1. 设置 HERMES_WEBUI_PASSWORD 环境变量,启用 webui 内置认证
2. 在 Caddy 层为 hermes.wonius.top 增加 basicauth 指令
八、方案对比¶
| hermes-webui (:8787) | hermes-dashboard (:9119) | |
|---|---|---|
| Caddy 配置 | reverse_proxy :8787 |
reverse_proxy :9118 (auth_proxy) |
| 认证模块 | api/auth.py(存在,但未启用) |
独立 auth_proxy.py(已启用) |
| 认证方式 | Cookie Session(PBKDF2 + HMAC) | Cookie Session(PBKDF2 + HMAC) |
| 登录页面 | WebUI 自带(多语言) | 自定义 login.html |
| 外网访问 | https://hermes.wonius.top |
https://db.hermes.wonius.top |
| 状态 | ⚠️ 无认证保护 | ✅ 有认证保护 |