通过代理访问 SSH
LLM 智能体如何通过 SSH 代理 API(而不是裸 SSH 密钥)安全地在远程机器上执行命令。
通过代理访问 SSH
智能体需要在远程机器上执行命令——部署容器、读日志、修配置。把裸的 SSH 私钥交给 LLM 是个坏主意:密钥会落进上下文窗口、日志和转录文本。SSH 代理解决了这个问题:凭据由它掌管,智能体只说 HTTP。
模型
LLM ──HTTPS──► SSH 代理 ──SSH──► 目标机器
(掌管密钥、
审计日志、
速率限制)代理通过简单的 REST 面提供持久会话:
| 端点 | 用途 |
|---|---|
POST /connect?target=vps1 |
打开会话 → session_id |
POST /<sid>/exec {command, timeout} |
执行命令(异步)→ exec_id |
GET /<sid>/exec/<exec_id> |
轮询输出 |
| SFTP 端点 | 文件上传/下载,ls,mkdir,rm |
认证是 Bearer 令牌——和你用的其他 API 相同的模式。
执行模式
# 1. 连接一次,整个任务复用会话
SID=$(curl -s -H "Authorization: Bearer $KEY" \
"https://ssh-proxy.example/connect?target=vps1" | jq -r .session_id)
# 2. 执行命令
EID=$(curl -s -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
-X POST "https://ssh-proxy.example/$SID/exec" \
-d '{"command": "docker ps --format \"{{.Names}}\"", "timeout": 120}' | jq -r .exec_id)
# 3. 轮询结果
curl -s -H "Authorization: Bearer $KEY" "https://ssh-proxy.example/$SID/exec/$EID"行为准则
- 复用会话。 每条命令都连接一次会浪费资源并触发速率限制。保存
session_id(如/tmp/vps1_sid),复用前先心跳确认。 - 给目标命名,而不是去发现它们。 目标是预先注册的(
vps1、pi……)。代理是唯一的事实来源——永远不要把 IP 地址硬编码进智能体代码。 - 文件优先用 SFTP,而不是 shell 的 cat/heredoc。 通过
exec转义二进制数据会损坏载荷。使用专用的上传/下载端点。 - 给你的命令加引号。 JSON 体会被解析两次(HTTP + shell)。远程命令用单引号包裹,并保持单行。
- 假设审计存在。 每次执行都会落入代理的审计日志——像人类在读一切那样行事(他确实可以)。
什么时候不要用 exec
- 读取拥有 API 的结构化状态(Docker、数据库、Synapse 的 Mind API)——永远优先用 API,而不是解析 CLI 输出。
- 长时间运行的交互式进程。
exec是带超时的即发即忘;改用目标机上的nohup+ 状态文件。 - 没有明确指令的任何破坏性操作。代理给你能力——人类给你许可。
错误恢复
| 症状 | 处理 |
|---|---|
| 连接时 401 | 令牌过期——从 memory/settings 重新读取,绝不从转录文本 |
| 会话 404 | 会话过期(24 小时上限)——重新连接 |
| exec 超时 | 提高 timeout 或拆分命令;检查目标机负载 |
| 文件内容损坏 | 你用了 exec 而不是 SFTP——改用 SFTP 端点 |