跳到主要内容

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 │ │ + 反向代理 │ │ │
│ 认证状态: 未启用 │ │ 密码: xxxxx │ │ │
└─────────────────┘ └────────┬─────────┘ └──────────────────────┘
│ 无认证
└──────────→ (直接代理转发)

关键点:所有服务均运行在同一台服务器,监听 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() 返回 Falsecheck_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.pyhandle_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/CaddyfileCaddy 反向代理配置
/root/.hermes/auth_proxy.py独立认证代理(含登录页、反向代理)
/root/.hermes/auth_templates/login.html登录页 HTML 模板
/root/.hermes/auth_proxy.keyHMAC 签名密钥
/root/.hermes/auth_proxy.passPBKDF2 密码哈希
/root/.hermes/start-dashboard.shDashboard 启动脚本
/etc/systemd/system/hermes-auth-proxy.serviceauth_proxy systemd 服务
/etc/systemd/system/hermes-dashboard.servicedashboard systemd 服务
/root/.hermes/hermes-webui/server.pyWebUI 主进程
/root/.hermes/hermes-webui/api/auth.pyWebUI 内置认证模块(未启用)
/root/.hermes/hermes-webui/api/routes.pyWebUI 路由处理

六、运维命令

# 查看 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 :8787reverse_proxy :9118 (auth_proxy)
认证模块api/auth.py(存在,但未启用)独立 auth_proxy.py(已启用)
认证方式Cookie Session(PBKDF2 + HMAC)Cookie Session(PBKDF2 + HMAC)
登录页面WebUI 自带(多语言)自定义 login.html
外网访问https://hermes.wonius.tophttps://db.hermes.wonius.top
状态⚠️ 无认证保护✅ 有认证保护