Bảo vệ API key của Hermes Agent bằng Infisical Agent Vault (Telegram)

Hướng dẫn từng bước để Hermes gateway chạy với placeholder, còn token thật nằm ở Infisical. Cập nhật ngày 25/10/2026.

Vì sao cần làm việc này

Mặc định Hermes Agent lưu API key (nhà cung cấp model, bot token Telegram, GitHub…) trong file .env ngay trên máy chạy agent. Agent lại có quyền chạy lệnh shell, đọc file và gọi web, nên chỉ cần một lần bị prompt injection hoặc một lệnh sai là key có thể bị in ra hoặc gửi đi.

Infisical Agent Vault giải quyết bằng cách đưa credential ra khỏi máy agent. Hermes chỉ giữ placeholder. Mọi request đi qua một proxy, proxy gắn credential thật rồi mới chuyển tiếp tới API. Nếu agent bị lộ, kẻ tấn công chỉ lấy được placeholder và một session token mà bạn có thể thu hồi bất cứ lúc nào.

Kiến trúc

Hermes gateway (chỉ có placeholder)
      |  HTTPS_PROXY, qua mạng WireGuard
      v
Agent Vault proxy (máy riêng, giữ token thật qua Infisical)
      |  gắn token thật vào URL
      v
api.telegram.org

Tách proxy ra máy riêng là khuyến nghị chính thức. Nếu chạy chung máy, agent có thể đọc thư mục trạng thái của proxy (chứa token và khóa riêng của CA), lúc đó lợi ích bảo mật giảm đáng kể.

Yêu cầu

  • Một tổ chức Infisical mà bạn là admin của Agent Vault. Gói Free (5 identity, Agent Proxy cho static secret) là đủ cho trường hợp này. Giá có thể thay đổi nên hãy xem infisical.com/pricing.
  • Hai máy cùng mạng riêng (trong bài là WireGuard, ví dụ proxy ở 10.10.38.1).
  • Docker trên máy proxy. Image infisical/cli phải là bản có CLI từ 0.43.133 trở lên, vì bản cũ hơn không áp dụng substitution.
  • Hermes đã cài, gateway Telegram đang chạy bình thường với token thật.
  • Bot token từ BotFather.

Bước 1: Tạo access bundle và service Telegram

Trong Infisical, mở Agent Vault từ product switcher, vào Access Bundles và tạo một bundle (ví dụ hermes-agent). Sau đó chọn Add Service.

Telegram không có template sẵn, nên chọn Custom và cấu hình như sau:

  1. Host: api.telegram.org (không có https://, không thừa dấu cách).
  2. Credential Type: Pass-through, nghĩa là proxy không gắn gì vào header. Telegram đặt token trong URL nên việc thay token do substitution đảm nhiệm.
  3. Trong Advanced Options → Substitutions, bấm Add Substitution:
    • Replace: __TELEGRAM_BOT_TOKEN__ (placeholder do bạn tự đặt).
    • With: token thật từ BotFather, dạng 123456789:AAH.... Không có chữ bot đứng trước, không có khoảng trắng thừa.
    • Replace In: chỉ tick URL Path.
  4. Bấm sang Review rồi Add Service.

Lưu ý quan trọng nhất của cả bài: ô Replace In mặc định có thể chỉ tick Headers. Token Telegram nằm trong đường dẫn (/bot<token>/getMe) nên nếu không tick URL Path, proxy sẽ không thay gì cả và Telegram trả về 404 Not Found.

Bạn có thể thêm service cho các API khác (nhà cung cấp model, GitHub…) vào cùng bundle. Với API dùng header Bearer thì chọn template tương ứng và dán key thật vào ô Token.

Bước 2: Tạo proxy

Vào Proxies, bấm Create Proxy, đặt tên rồi Create. Hộp thoại Proxy Token hiện ra và chỉ hiển thị một lần, vì vậy hãy sao chép ngay. Hộp thoại này cũng cho sẵn lệnh docker run đã điền token và URL.

Về traffic policy: mặc định là Any host, proxy sẽ chuyển tiếp tới mọi host mà máy proxy truy cập được, kể cả hệ thống nội bộ. Nếu không muốn Hermes chạm vào các hệ thống đó, chọn Access bundle hosts only trong Advanced Options và liệt kê các host khác mà Hermes cần ở mục Exceptions.

Bước 3: Chạy proxy bằng Docker Compose

Trên máy proxy, tạo thư mục (ví dụ ~/docker/agent-vault) với hai file.

docker-compose.yml

services:
  agent-vault-proxy:
    image: infisical/cli:latest   # nên ghim phiên bản cụ thể (>= 0.43.133)
    container_name: agent-vault-proxy
    restart: unless-stopped
    command: ["agent-vault", "proxy", "--domain", "${INFISICAL_URL}"]
    environment:
      INFISICAL_AGENT_VAULT_PROXY_TOKEN: ${PROXY_TOKEN}
    volumes:
      - agent-vault-proxy:/etc/infisical/agent-vault
    ports:
      - "${WG_IP}:17323:17323"

volumes:
  agent-vault-proxy:

.env

INFISICAL_URL=https://app.infisical.com   # hoặc URL Infisical tự host / vùng EU của bạn
PROXY_TOKEN=<proxy token từ bước 2>
WG_IP=10.10.38.1                           # IP WireGuard của máy proxy

Chạy:

chmod 600 .env
docker compose up -d
docker compose logs -f agent-vault-proxy

Log phải có dòng báo proxy đang lắng nghe cổng 17323. Volume giữ cache và chứng chỉ của proxy, nên recreate container không làm đổi CA đã cài trên máy Hermes.

Hai điều cần lưu ý:

  • Bind cổng vào IP WireGuard thay vì 0.0.0.0. Cổng publish bằng Docker thường bỏ qua luật ufw, nên bind rộng có thể mở cổng ra ngoài dù bạn đã chặn trong firewall.
  • Khi reboot, nếu Docker khởi động trước khi interface wg0 có IP thì container không bind được. Cho Docker chờ WireGuard bằng systemd drop-in (sudo systemctl edit docker, thêm [email protected] và [email protected] trong mục [Unit]).

Không để bất cứ thứ gì ngoài mạng riêng truy cập cổng 17323. Hermes gửi session token không mã hóa trong mỗi request, nên ai nhìn thấy và truy cập được proxy đều dùng được token đó.

Bước 4: Tạo session cho Hermes

Vào Sessions → Create Session, chọn đúng access bundle ở bước 1, đặt Expires (ví dụ Never cho gateway chạy liên tục), rồi bấm Create Session. Token là giá trị đứng sau --session-token trong lệnh hiện ra. Nó cũng chỉ hiển thị một lần.

Đừng nhầm với proxy token. Proxy token dùng để proxy đăng ký với Infisical, còn session token dùng để Hermes xác thực với proxy. Nên tạo một session riêng cho gateway này để thu hồi không ảnh hưởng agent khác.

Bước 5: Kiểm tra từ máy Hermes (trước khi sửa Hermes)

Chạy trên máy hoặc container Hermes, thay 10.10.38.1 bằng IP proxy của bạn.

Kiểm tra kết nối tới proxy (phải ra 200):

curl -sS -o /dev/null -w "%{http_code}\n" http://10.10.38.1:17323/_agent-vault/ca

Tải CA của proxy. Nếu máy chưa có jq, dùng Python (đã có sẵn cùng Hermes):

mkdir -p /opt/data/agent-vault
curl -fsS http://10.10.38.1:17323/_agent-vault/ca \
  | python3 -c 'import sys,json; print(json.load(sys.stdin)["certificate"])' \
  > /opt/data/agent-vault/ca.pem

Đường dẫn /opt/data là giá trị HERMES_HOME trong ví dụ của bài này. Hãy dùng giá trị của bạn (xem echo $HERMES_HOME; nếu rỗng thì dùng ~/.hermes).

Đối chiếu fingerprint. Chứng chỉ tải qua HTTP thường, nên phải kiểm tra nó đúng của proxy bạn:

head -n 1 /opt/data/agent-vault/ca.pem   # phải là BEGIN CERTIFICATE
openssl x509 -in /opt/data/agent-vault/ca.pem -noout -fingerprint -sha256

So với fingerprint trong log khởi động của proxy hoặc cột Certificate Authority ở trang Proxies trong Infisical. Chỉ tiếp tục nếu hai giá trị khớp.

Test Telegram qua proxy. Dùng placeholder trong URL, đừng dán token thật:

curl -sS \
  -x "http://x-agent-vault:<session-token>@10.10.38.1:17323" \
  --cacert /opt/data/agent-vault/ca.pem \
  https://api.telegram.org/bot__TELEGRAM_BOT_TOKEN__/getMe

Kết quả đúng là JSON có "ok":true và thông tin bot. Để đối chứng, chạy cùng URL nhưng bỏ -x và --cacert: lần này phải lỗi, vì Telegram nhận một token giả. Điều đó cho thấy chính proxy mới là nơi đổi placeholder thành token thật.

Bước 6: Cấu hình Hermes

Hermes đọc key từ file .env trong thư mục home của nó, là $HERMES_HOME/.env nếu bạn đã đặt biến này. Trong ví dụ là /opt/data/.env. Hãy chắc chắn bạn sửa đúng file bằng cách kiểm tra file nào chứa TELEGRAM_BOT_TOKEN.

Sửa các dòng sau:

# Placeholder khớp từng ký tự với ô Replace trong Infisical
TELEGRAM_BOT_TOKEN=__TELEGRAM_BOT_TOKEN__

# Giữ adapter Telegram dùng hostname thay vì địa chỉ IP
HERMES_TELEGRAM_DISABLE_FALLBACK_IPS=true

# Đưa mọi request qua proxy
HTTPS_PROXY=http://x-agent-vault:<session-token>@10.10.38.1:17323
HTTP_PROXY=http://x-agent-vault:<session-token>@10.10.38.1:17323
NO_PROXY=localhost,127.0.0.1,::1,10.10.38.1

# Tin tưởng chứng chỉ của proxy (đường dẫn tuyệt đối)
SSL_CERT_FILE=/opt/data/agent-vault/ca.pem
REQUESTS_CA_BUNDLE=/opt/data/agent-vault/ca.pem
CURL_CA_BUNDLE=/opt/data/agent-vault/ca.pem
NODE_EXTRA_CA_CERTS=/opt/data/agent-vault/ca.pem

Một số điểm cần chú ý:

  • Chỉ đổi sang placeholder những key đã có service trong bundle. Nếu key của nhà cung cấp model chưa có service, hãy để nguyên key thật (request vẫn đi qua proxy và được chuyển tiếp nguyên vẹn) cho đến khi bạn thêm service cho nó.
  • Khi đổi key sang placeholder, tránh các giá trị như dummy, placeholder, changeme, none hay chuỗi toàn chữ x, vì Hermes coi đó là chưa đặt key.
  • Xóa TELEGRAM_PROXY (trong .env) hoặc telegram.proxy_url (trong config.yaml) nếu có, vì chúng được ưu tiên hơn HTTPS_PROXY và khiến Telegram bỏ qua proxy.
  • Nếu terminal.backend khác local, các lệnh shell của Hermes chạy trong môi trường không có cấu hình proxy nên sẽ không được gắn credential.
  • Khi Hermes chạy trong Docker, hãy mount $HERMES_HOME (ở đây là /opt/data) ra một thư mục trên host. Nếu không, .env và ca.pem sẽ biến mất khi container bị tạo lại.

Sau đó khởi động lại gateway:

hermes gateway restart
hermes gateway status

Nếu container không có service manager thì khởi động lại bằng cách restart container từ host (docker restart <tên-container>).

Loại trừ dịch vụ nội bộ khỏi proxy (ví dụ gbrain)

Biến HTTP_PROXY trong .env áp dụng cho toàn bộ tiến trình Hermes, nên mọi kết nối HTTP đi ra, kể cả tới dịch vụ nội bộ, đều bị đẩy qua proxy. Nếu dịch vụ nội bộ nằm cùng địa chỉ với máy proxy (ví dụ MCP server gbrain tại http://10.10.38.1:7333/mcp), request sẽ đi sang proxy rồi gọi ngược lại từ bên trong container của proxy, và có thể treo cho tới khi hết thời gian chờ. Hermes khi đó báo lỗi kiểu Failed to connect to MCP server 'gbrain': CancelledError.

Cách sửa là thêm địa chỉ của dịch vụ đó vào NO_PROXY để Hermes kết nối thẳng:

NO_PROXY=localhost,127.0.0.1,::1,10.10.38.1

Sau đó restart gateway và kiểm tra lại bằng hermes mcp test gbrain. Việc loại trừ chỉ áp dụng cho các địa chỉ được liệt kê: Telegram và các API khác vẫn đi qua proxy như cũ. Liệt kê từng IP hoặc hostname cụ thể là cách chắc nhất, vì việc hỗ trợ dải CIDR (ví dụ 10.10.38.0/24) phụ thuộc vào từng thư viện HTTP.

Các dịch vụ chạy trong mạng WireGuard đã được mã hóa trên đường truyền nên đi thẳng là ổn. Nếu muốn che luôn token của dịch vụ nội bộ (ví dụ GBRAIN_TOKEN) bằng Agent Vault thì cần thêm service riêng cho nó, việc này là tùy chọn.

Bước 7: Xác nhận hoạt động

  1. Nhắn tin cho bot trên Telegram. Nếu Hermes trả lời, cả Telegram lẫn nhà cung cấp model đã nhận được credential thật.
  2. Trên máy proxy, chạy docker logs -f agent-vault-proxy. Các request tới Telegram phải hiện với trạng thái brokered.
  3. Trên Hermes, xem $HERMES_HOME/logs/gateway.log (ở đây là /opt/data/logs/gateway.log) để theo dõi lỗi.

Khi mọi thứ ổn, token thật của bot chỉ còn nằm trong Infisical, và bạn có thể xóa nó khỏi mọi nơi khác trên máy Hermes.

Xử lý sự cố

  • 404 Not Found từ Telegram khi test: proxy chưa thay placeholder. Nguyên nhân phổ biến nhất là ô Replace In chưa tick URL Path. Kiểm tra thêm placeholder trong URL khớp từng ký tự với ô Replace, và ô With là token thật không có chữ bot.
  • Log proxy không có dòng nào cho api.telegram.org: theo mặc định proxy chỉ ghi log các request mà nó brokered, chặn hoặc không hoàn tất được. Request không khớp service nào được chuyển tiếp mà không ghi log. Kiểm tra service nằm đúng bundle, host nhập đúng, session gắn đúng bundle. Thêm --log-level debug vào command của proxy để thấy toàn bộ request.
  • 407: thiếu hoặc sai session token trong HTTPS_PROXY (phải có dạng x-agent-vault:<token>@).
  • 403: session đã bị thu hồi hoặc hết hạn, hoặc service không cho phép method hay path của request.
  • 502 khi gọi Telegram: adapter đang kết nối bằng địa chỉ IP. Đặt HERMES_TELEGRAM_DISABLE_FALLBACK_IPS=true rồi restart gateway.
  • Lỗi chứng chỉ trong log Hermes: kiểm tra đường dẫn CA là tuyệt đối và file ca.pem không rỗng. Nếu bạn dùng jq mà chưa cài, lệnh > vẫn tạo ra file rỗng, hãy tải lại bằng Python như ở bước 5.
  • Hermes báo thiếu key: placeholder bị Hermes coi là chưa đặt. Đổi sang giá trị khác và cập nhật ô Replace nếu cần.
  • Key bị đánh dấu hỏng sau khi sửa lỗi: chạy hermes auth list để tìm tên provider, rồi hermes auth reset <provider> và restart gateway.
  • Gateway tự thoát khi Telegram từ chối token: sửa nguyên nhân rồi chạy hermes gateway start, vì service manager sẽ không tự khởi động lại trong trường hợp này.
  • Container proxy không khởi động sau khi reboot: thường do Docker chạy trước khi wg0 có IP. Xem phần lưu ý ở bước 3.

Lưu ý bảo mật

  • Session token là credential duy nhất còn lại trên máy Hermes. Ai có token và truy cập được proxy thì dùng được mọi API trong bundle, nên đừng dán token vào chat hay commit lên git, và hãy che nó khi chia sẻ log. Nếu lỡ lộ, thu hồi session ngay.
  • Không để cổng 17323 ra ngoài mạng riêng. Bind vào IP WireGuard, không bind 0.0.0.0.
  • Tách máy proxy và máy agent nếu muốn có bảo đảm thật. Chạy chung máy chỉ chống được việc lộ key do vô tình, không chống được agent bị tấn công.
  • Nền tảng nhắn tin dùng WebSocket (ví dụ phần nhận tin của Discord) có thể không hoạt động qua Agent Vault, vì proxy không hỗ trợ kết nối WebSocket. Telegram dùng HTTP long-polling nên không bị ảnh hưởng.
  • Giữ file .env của proxy ở quyền 600 và không đưa proxy token vào repository.

Thu hồi và thay session

Để chặn Hermes dùng credential thật, vào Agent Vault → Sessions, mở menu của session và chọn Revoke Session. Proxy sẽ ngừng gắn credential trong vòng một chu kỳ poll, và Hermes sẽ không trả lời được vì request tới API thất bại.

Để cấp session mới, tạo session mới, thay token trong cả HTTPS_PROXY lẫn HTTP_PROXY trong .env của Hermes rồi restart gateway. Có thể tự động hóa việc này bằng API tạo session của Infisical.

Kết luận

Sau khi hoàn tất, .env của Hermes chỉ còn placeholder và một session token có thể thu hồi, còn token Telegram thật nằm ở Infisical. Mẫu cấu hình này áp dụng được cho bất kỳ API nào: với API nhận key qua header thì dùng template Bearer, với API đặt key trong URL (như Telegram) thì dùng Custom cùng substitution và nhớ tick đúng vị trí cần thay.