技术分享

Hermes Agent 安装与飞书打通复盘

约 9,371 字 阅读约 47 分钟
目录

这篇是一次完整的实操记录:从一台腾讯云 Ubuntu 服务器开始,完成 Hermes Agent 安装、Dashboard 域名映射、HTTPS、基础安全加固,再到飞书机器人接入、Gateway 常驻运行,以及群聊里通过 @ 机器人触发回复。

为方便后续分享或发布,本文做了脱敏处理:

  • 真实域名统一写成 hermes.example.com
  • 真实服务器 IP 统一写成 203.0.113.10
  • 本地 SSH Host alias 统一写成 tx_hermes
  • 真实主机名统一写成 cloud-ubuntu
  • App Secret、密码、Token 等敏感值只保留占位符

整个过程最后形成的结果是:

  • Hermes Dashboard 可通过 https://hermes.example.com 访问
  • Dashboard 走 HTTPS,并由 Nginx Basic Auth 保护
  • Hermes 后端只监听本机 127.0.0.1:9119,不直接暴露公网
  • Nginx 负责反向代理和 WebSocket 转发
  • fail2ban 已开启,防止 Basic Auth 爆破
  • Hermes Gateway 已作为 systemd 服务常驻运行
  • 飞书/Lark 长连接已建立
  • 私聊机器人可回复
  • 群聊中 @ 机器人可回复

1. SSH 登录服务器

本地已经有 SSH key,服务器是腾讯云 Ubuntu,IP 为:

203.0.113.10

本地 ~/.ssh/config 使用了类似配置:

Host tx_hermes
  HostName 203.0.113.10
  User root
  Port 22
  IdentityFile ~/.ssh/id_rsa

因此可以直接:

ssh tx_hermes

或者显式指定:

ssh -i ~/.ssh/id_rsa -p 22 root@203.0.113.10

服务器环境确认:

Ubuntu 24.04.4 LTS
user: root
hostname: cloud-ubuntu

2. 安装 Hermes Agent

安装来自官方仓库:

NousResearch/hermes-agent

安装命令:

curl -fsSL https://hermes-agent.nousresearch.com/install.sh | bash -s -- --skip-setup

安装完成后的关键路径:

Code:     /usr/local/lib/hermes-agent
Command:  /usr/local/bin/hermes
Config:   /root/.hermes/config.yaml
Env:      /root/.hermes/.env
Data:     /root/.hermes/cron, sessions, logs

随后补了一些基础依赖,并跑了一次 doctor:

apt-get update
apt-get install -y ripgrep ffmpeg
hermes doctor --fix
hermes doctor

3. 初始化 Hermes 配置

通过:

hermes setup

完成基本配置。

这次大体选择是:

  • Terminal backend:保持当前 local
  • Messaging platforms:后续配置飞书
  • Browser provider:Local Browser
  • Image generation:Skip
  • TTS:Microsoft Edge TTS
  • Vision backend:Auto
  • Search provider:可先用默认或免费方案

配置完成后,Hermes 提示主要文件在:

/root/.hermes/config.yaml
/root/.hermes/.env

启动本地 Dashboard 的基础命令是:

hermes dashboard --host 127.0.0.1 --port 9119 --no-open --skip-build

4. Dashboard systemd 常驻

为了稳定运行,创建了 systemd 服务:

/etc/systemd/system/hermes-dashboard.service

核心内容:

[Unit]
Description=Hermes Agent Dashboard
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=root
WorkingDirectory=/root
Environment=HOME=/root
ExecStart=/usr/local/bin/hermes dashboard --host 127.0.0.1 --port 9119 --no-open --skip-build
Restart=always
RestartSec=5
TimeoutStopSec=15
KillMode=mixed

[Install]
WantedBy=multi-user.target

启动:

systemctl daemon-reload
systemctl enable hermes-dashboard
systemctl start hermes-dashboard

确认:

systemctl status hermes-dashboard
ss -ltnp | grep 9119

期望看到:

127.0.0.1:9119

也就是说,Hermes Dashboard 后端只监听本机端口,不直接暴露公网。

5. 绑定域名与 HTTPS

域名:

hermes.example.com

DNS 指向服务器:

203.0.113.10

安装 Nginx 和 Certbot:

apt-get update
apt-get install -y nginx apache2-utils certbot python3-certbot-nginx

申请证书:

certbot --nginx -d hermes.example.com

证书路径:

/etc/letsencrypt/live/hermes.example.com/fullchain.pem
/etc/letsencrypt/live/hermes.example.com/privkey.pem

Certbot 会自动启用续期 timer。

6. Nginx 反向代理

Nginx 站点配置位于:

/etc/nginx/sites-available/hermes.example.com

核心逻辑是:

server {
    server_name hermes.example.com;

    client_max_body_size 50m;

    auth_basic "Hermes Dashboard";
    auth_basic_user_file /etc/nginx/.htpasswd-hermes;

    location / {
        proxy_pass http://127.0.0.1:9119;
        proxy_http_version 1.1;

        proxy_set_header Host 127.0.0.1:9119;
        proxy_set_header Origin http://127.0.0.1:9119;

        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_set_header Authorization "";
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";

        proxy_read_timeout 3600s;
        proxy_send_timeout 3600s;
    }

    listen 443 ssl;
    ssl_certificate /etc/letsencrypt/live/hermes.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/hermes.example.com/privkey.pem;
    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
}

server {
    if ($host = hermes.example.com) {
        return 301 https://$host$request_uri;
    }

    listen 80;
    server_name hermes.example.com;
    return 404;
}

这里有一个关键点:

proxy_set_header Origin http://127.0.0.1:9119;

最初 Dashboard 页面能打开,但 Chat 区域显示:

Chat connection interrupted (code 1006)
events feed disconnected

Nginx 日志里 /api/events/api/pty 返回 403。Hermes 日志显示:

origin_mismatch origin=https://hermes.example.com bound=127.0.0.1

原因是 Hermes Dashboard 以 127.0.0.1 绑定时,会校验 WebSocket 的 Host/Origin。浏览器从公网域名发起 WebSocket,Origin 是 https://hermes.example.com,后端不认。

解决方式是在 Nginx 反代层把 Origin 改成后端绑定地址。这样后端仍只监听本地端口,公网入口仍只有 Nginx。

验证 WebSocket 正常时,应该能看到:

101 Switching Protocols

7. Dashboard Basic Auth

使用 Nginx Basic Auth 保护入口:

htpasswd -c /etc/nginx/.htpasswd-hermes <username>

如果遇到 Nginx 500,并且 error log 里有:

open() "/etc/nginx/.htpasswd-hermes" failed (13: Permission denied)

修复权限:

chgrp www-data /etc/nginx/.htpasswd-hermes
chmod 640 /etc/nginx/.htpasswd-hermes
systemctl reload nginx

注意:密码不要写进文档、代码仓库或聊天记录里。已经明文出现过的密码,应该视为已泄露并尽快更换。

8. 防爆破:fail2ban

为了防止 Basic Auth 被爆破,安装 fail2ban:

apt-get install -y fail2ban

新增:

/etc/fail2ban/jail.d/nginx-hermes-auth.conf

内容:

[nginx-http-auth]
enabled = true
port = http,https
filter = nginx-http-auth
logpath = /var/log/nginx/error.log
maxretry = 5
findtime = 10m
bantime = 1h
backend = auto

启动:

systemctl enable fail2ban
systemctl restart fail2ban

查看状态:

fail2ban-client status
fail2ban-client status nginx-http-auth

当前策略是:

10 分钟内错误 5 次,封禁 1 小时

常用命令:

fail2ban-client status nginx-http-auth
fail2ban-client banned
fail2ban-client set nginx-http-auth unbanip 1.2.3.4

9. 安全性判断

当前架构是:

Internet
  -> HTTPS / Nginx / Basic Auth / fail2ban
  -> 127.0.0.1:9119 Hermes Dashboard

优点:

  • 只有 80/443 暴露公网
  • Dashboard 后端不直接暴露公网
  • 有 HTTPS
  • 有 Basic Auth
  • 有 fail2ban 防爆破
  • WebSocket 已正常转发

主要风险:

  • Hermes 是 Agent 控制台,不是普通后台,权限很大
  • 当前服务以 root 运行,安全边界较薄
  • Basic Auth 没有 MFA
  • 如果密码泄露,攻击者可能获得服务器级操作能力

更安全的长期方案:

  • 最安全:不开放公网,只走 SSH Tunnel 或 Tailscale
  • 更适合公网:Cloudflare Access / Zero Trust + Nginx
  • 当前方案:适合个人低频使用,但建议强密码、限流、定期看日志

10. 配置飞书 / Lark

Hermes 支持 Feishu / Lark 作为消息入口。

飞书配置文件主要是:

/root/.hermes/.env
/root/.hermes/config.yaml

当前关键配置:

FEISHU_APP_ID=cli_xxx
FEISHU_APP_SECRET=***
FEISHU_DOMAIN=feishu
FEISHU_ALLOW_ALL_USERS=true
FEISHU_GROUP_POLICY=open
FEISHU_REQUIRE_MENTION=true

config.yaml 中有:

platforms:
  feishu:
    enabled: true

飞书后台需要做的事:

  1. 创建自建应用
  2. 启用 Bot 能力
  3. 配置 App ID / App Secret
  4. 事件订阅启用 im.message.receive_v1
  5. 连接方式使用长连接 WebSocket
  6. 发布应用版本
  7. 把机器人添加到私聊或群聊

推荐权限:

im:message
im:message:send_as_bot
im:resource
im:chat
im:chat:readonly

更完整功能可加:

im:message.reactions:readonly
admin:app.info:readonly
contact:user.id:readonly

11. Gateway 常驻运行

最初执行:

hermes gateway restart

但日志提示:

Cannot restart gateway as a service — linger is not enabled.
The gateway user service requires linger to function on headless servers.

因此改为安装 system-level service:

hermes gateway install --system --run-as-user root --start-now --force

生成服务:

/etc/systemd/system/hermes-gateway.service

确认:

systemctl status hermes-gateway
hermes gateway status --system

正常状态:

hermes-gateway.service active running
enabled

日志里看到:

[Lark] connected to wss://msg-frontier.feishu.cn/ws/v2 ...

这说明 Hermes Gateway 已经通过飞书开放平台长连接在线。

常用命令:

systemctl restart hermes-gateway
systemctl status hermes-gateway
journalctl -u hermes-gateway -f
hermes gateway status --system

12. 私聊与群聊响应策略

Hermes 飞书集成的默认行为:

  • 私聊:收到消息就回复
  • 群聊:默认只有 @ 机器人时才回复

这次最终设置为:

FEISHU_ALLOW_ALL_USERS=true
FEISHU_GROUP_POLICY=open
FEISHU_REQUIRE_MENTION=true

含义:

  • 任何人都可以和机器人交互
  • 群聊里必须 @ 机器人
  • 不 @ 的群消息不会主动处理

如果想更严格,只允许指定用户:

FEISHU_GROUP_POLICY=allowlist
FEISHU_ALLOWED_USERS=ou_xxx,ou_yyy
FEISHU_REQUIRE_MENTION=true

如果想彻底关闭群聊:

FEISHU_GROUP_POLICY=disabled

当前个人使用场景下,先全开是可以的。后续如果机器人进入多人群,建议改成 allowlist。

13. /set-home 是什么

/set-home 的作用是把当前飞书聊天设为 Hermes 的默认通知群。

它主要用于:

  • 定时任务结果发送
  • 后台任务完成通知
  • cron job 输出
  • 没有明确会话时的默认回传位置

它不是私聊或群聊 @ 回复的必要条件。

如果只是:

@机器人 你好

不需要 /set-home

如果希望某个群作为 Hermes 的默认通知群,可以在那个群里发:

/set-home

设置后类似保存:

FEISHU_HOME_CHANNEL=oc_xxx

14. 最终检查清单

Dashboard:

systemctl status hermes-dashboard
ss -ltnp | grep 9119
curl -I https://hermes.example.com

Nginx:

nginx -t
systemctl status nginx
tail -f /var/log/nginx/access.log
tail -f /var/log/nginx/error.log

fail2ban:

fail2ban-client status nginx-http-auth

Gateway:

systemctl status hermes-gateway
hermes gateway status --system
journalctl -u hermes-gateway -f

飞书:

私聊机器人:发送“你好”
群聊中:@机器人 你好

如果日志出现:

[Lark] connected to wss://msg-frontier.feishu.cn/ws/v2 ...

说明飞书长连接已经在线。

15. 这次踩过的坑

WebSocket 403

现象:

Chat connection interrupted (code 1006)
events feed disconnected

原因:

origin_mismatch origin=https://hermes.example.com bound=127.0.0.1

解决:

proxy_set_header Origin http://127.0.0.1:9119;

Nginx Basic Auth 权限错误

现象:

open() "/etc/nginx/.htpasswd-hermes" failed (13: Permission denied)

解决:

chgrp www-data /etc/nginx/.htpasswd-hermes
chmod 640 /etc/nginx/.htpasswd-hermes
systemctl reload nginx

Gateway restart 不生效

现象:

Cannot restart gateway as a service — linger is not enabled.

解决:

hermes gateway install --system --run-as-user root --start-now --force

群聊 @ 不回复

可能原因:

  • 飞书应用没有发布新版
  • 没订阅 im.message.receive_v1
  • 机器人没进群
  • 群聊策略还是 allowlist
  • 没有 @ 机器人

当前推荐配置:

FEISHU_GROUP_POLICY=open
FEISHU_REQUIRE_MENTION=true
FEISHU_ALLOW_ALL_USERS=true

16. 后续建议

短期:

  • 换一个新的强密码
  • 定期查看 Nginx 和 fail2ban 日志
  • 保持 9119 只监听 127.0.0.1

中期:

  • 把 Hermes 从 root 用户迁移到普通用户
  • 给 Dashboard 加 Cloudflare Access 或 Tailscale
  • 如果多人使用飞书机器人,改成 allowlist

长期:

  • 把这套安装流程沉淀成脚本
  • 定期备份 /root/.hermes/config.yaml/root/.hermes/.env
  • 把飞书 App 权限保持在最小可用范围

这次最终的可用状态是:Dashboard 能稳定访问,飞书 Gateway 已常驻,飞书私聊和群聊 @ 都具备回复条件。