给本地模型 SSH 访问权的安全做法,是永远不要给它密钥。让它去调用一个工具——一个充当密钥托管方(替你保管密钥、代为签名的中间层)的 MCP 服务器——由它保管密钥(或签发短期证书),只连到你列出的主机,按主机强制执行命令允许列表,并把每一条都记录下来。这样一来,一个被劫持的提示词最多只能通过托管方去请求某个操作;它拿不走你的身份,也到不了你没列出的主机。人们最先搞错的一点是:在你自己的硬件上运行模型,保护的是你的提示词,而不是你的服务器——“它是本地的,所以安全”并不成立,对那个模型拿到一个 shell 之后会发生什么也毫无帮助。
本地模型正当其时。工作站上的 Ollama、自建端点后面的开源权重模型、自托管的编码代理——你运行它们有充分的理由:隐私、成本、没有速率限制、在飞机上也能用。而一旦一个模型已经在处理你的提示词,下一步自然就是把它指向你的基础设施:让它跟踪日志、重启服务、跑一次迁移——而真正的问题正是在这里落地。
为什么“本地”并不会让服务器访问变得安全
无论模型是跑在数据中心里,还是跑在你桌子底下那台机器上,有两件事的危险程度分毫不变。
密钥仍然是一种持有即生效的凭据。把 id_ed25519 丢进代理的环境、好让它自己去跑 ssh,那么无论是谁——或什么东西——握着那串字节,在每一台信任该密钥的主机上,它就是你。在本地运行模型并不改变这一点;它只是把副本挪到了一台恰好归你所有的机器上。而且你照样收不回来:密钥一旦经过模型的上下文——一个环境变量、一个挂载的文件、一次工具调用、一份对话记录——你就再也无法证明它没流落到不该去的地方。对“模型看到我的密钥了吗?”这个问题,唯一诚实的回答,就是在每一台主机上把它轮换掉。
本地模型照样会犯糊涂。提示词注入并不需要一个云端模型。一行被投毒的日志、一个恶意的文件名、一个设了陷阱的工具返回结果、一个过于急切的计划——本地模型对这些的照单全收,和托管模型一样痛快。真要说的话,风险更大,因为人们更信任本地搭建的东西,给它们配的护栏更少,放给它们的权限更宽,恰恰就因为“它在我自己的机器上”。这种直觉是错的:模型的判断力并不会因为权重在本地就变得更好。
所以目标和对待任何代理都一样——让模型去做那份活儿,却从不持有凭据,并让每一个操作都有范围限定、可见、可撤销。
三种做砸的方式
**直接粘贴原始密钥。**最直接的路子,也是最糟的:没有范围、没有有效期,还带着前面说的那种不可逆。那把用来部署预发布环境的密钥,通常隔两跳也能够到生产数据库。
**签发一个长期令牌。**一个永不过期的部署令牌或“自动化”凭据。它感觉上比密钥更整洁,但本质还是那个持有即生效的问题,只是多绕了几步。如果撤销某样东西的前提是记得它还存在,那它就会比当初造出它的那个实验活得更久。
**给它一个自己的 root 登录,然后撒手不管。**对的直觉——一个独立身份——却用错了方式。往一个机器人用户的 authorized_keys 里塞一把公钥,你就建起了一条没有审计轨迹的访问通道:没有任何东西能把模型的命令和别人的区分开,而当凌晨两点出了岔子,你只能靠 bash 历史和感觉去还原它的会话。
共同的毛病是:模型持有一份常驻凭据,而可观测性是事后才想起来的。
行之有效的模式:一个托管方,而不是一份副本
反过来。模型根本就不该持有凭据。
- 模型发起请求,托管方负责签名。一个中间方保管密钥(或签发短期证书),并代表模型完成认证。攻破了模型的上下文,你拿到的是通过这个中间方去请求操作的能力——而不是从任何地方冒充你的能力。
- **范围是明确的。**模型只能连到你列出的主机。一台新主机是一次新的决定,而不是默认就有。
- **命令受到管束。**逐台主机地决定模型拿到的是一个完整的 shell、一份命令允许列表,还是什么都没有——于是“重启 nginx”在 Web 层是允许的,而“任何操作都行”永远不被允许。
- **人可以盯着看——实时地。**不只是在事后复盘的时候,而且记录会把“这是模型干的”和“这是我干的”区分开。
- **撤销是一个开关,而不是一次轮换。**因为从来没有共享过任何东西,关掉访问权没有任何代价。
这一切都不稀奇——在正经的基础设施团队里,基于证书的 SSH 大体上就是这么运作的。一直以来的麻烦在于,自己把这套东西接起来是一个项目,于是人们就跳过它,去选了上面那些糟糕的办法之一。
把它接到本地模型上
MCP——模型上下文协议(Model Context Protocol)——是干这件事的利落办法,而且它与客户端无关:任何支持 MCP 的客户端都能驱动它,所以你的模型是 Claude、Ollama 后面的开源权重模型,还是你自己托管的某个东西,都无所谓。
- 让你的本地模型通过一个支持 MCP 的客户端运行(一个会说 MCP 的编码代理或聊天客户端)。
- 把它指向一个代理 SSH 的 MCP 服务器——一个替模型完成认证、而不是把密钥交到它手上的服务器。
- **只打开你想要的那些主机,**并设好每台主机的命令策略(完全 / 允许列表 / 禁止)。
- 让会话保持在视野里,并让它写入审计日志,这样模型运行的每一条命令都可追溯、可回放。
模型拿到的是一组工具——打开一个会话、执行一条命令、读写一个文件——而认证由托管方来做。你的密钥绝不会进入模型的环境,无论是在你的机器上还是别处。而且因为持有凭据的是托管方而不是模型,所以当你转而去用一个托管代理时,同一套接法照样成立——通过 SSH 使用 Claude Code和通过 SSH 使用 OpenAI Codex,在另一端换成云端模型,撑起的正是这同一个模式。
Termalin 是怎么做的
Termalin 是一款带内置 MCP 服务器的 SSH 客户端,所以这个模式是默认就有的,而不是一个需要你自己搭的项目。
- **在你自己的机器上,**把内置的本地服务器注册到你的代理(
claude mcp add termalin -- <path>/termalin-mcp,或任何 MCP 客户端的等价写法)。模型只能连到你启用的主机——代理访问默认关闭——而认证走的是 Termalin 的密钥托管方:你把密钥解锁一次,Termalin 代表模型签名,自始至终都没有任何密钥文件可供模型读取。 - 逐台主机地设定策略——完全访问、命令允许列表或禁止——这样一来,一个犯糊涂的本地模型就无法在那台机器上执行你从未授权过的操作。
- **你看着它发生。**代理会话以实时终端标签页运行;监控网格会镜像它们,模型正在驱动的窗格会发光。每一条命令都会在会话录制里被标记出来——只记录输出,绝不记录你的按键——并写入审计日志,连同它来自的设备和 IP。
- **没有应用在运行?**用一个 API 密钥把模型指向 Termalin 的托管 MCP 端点,这个密钥限定到特定服务器、带有有效期、受命令策略约束,并用一张短期证书为每次运行做认证——所以即便密钥泄露,也到不了未列出的主机,也跑不出它被允许的命令之外。
无论哪种方式,你想要的那个性质都成立:你的本地模型可以操作你的服务器,而它从未见过一把密钥。
从一台无关紧要的主机开始
别从生产环境开始。接入一台低风险的机器——一台预发布服务器、一台玩具级 VPS——把它设成允许列表,然后给模型一件真正的杂活:跟踪日志直到错误出现、修好配置、重启服务。它干活时,让监控网格一直开着。头一个小时里你学到的东西——它怎么表现、在哪里犹豫、遇到模棱两可时怎么处理——会告诉你第二台主机要不要接入,以及该给多大的自由度。
这就是托管方模式悄然带来的回报:你一次只扩展一台主机,因为你迈出的每一步都不是收不回来的——无论模型跑在哪里。
Termalin 是一款免费的跨平台 SSH 客户端,内置 MCP 服务器、密钥托管方和按主机的代理策略——下载它,或读一读它是如何安全处理密钥的。