夜雨聆风学习资料网

ARTICLE · 1280479

用密钥让AI控制云服务器

用密钥让AI控制云服务器
架构师学习笔记

用密钥让 AI 控制云服务器

SSH 免密 · 五步落地 · 安全收尾

密码登录是给人在终端前准备的:它要求有一个会看屏幕、会敲键盘的操作者。而 AI 在脚本里没法完成「提示输入密码 → 等待 → 输入」这一步,真把密码写进脚本,又等于把钥匙钉在门上。

密钥登录解决的正是这件事——一个文件证明身份,一条命令完成登录,全程不需要人工干预。本文从原理讲到落地,再补齐安全收尾和高频排错。

先看速查:整件事就五步

核心矛盾只有一句话:怎么在不暴露密码的前提下,让一个非人类操作者反复登录服务器。下面五步按执行顺序排列,全程只在本地机器上操作,服务器端只需要一次密码。

第一步 · 本地生成密钥对
生成一对数学上配对的字符串。私钥留在本地给 AI 用,公钥装到服务器。
▸ 产出:id_ed25519(私钥)+ id_ed25519.pub(公钥)
第二步 · 把公钥装到服务器
一条管道命令,把本地公钥追加进服务器的授权列表。这是全流程唯一一次需要输密码。
▸ 关键点:用 >> 追加,不能用 > 覆盖
第三步 · 验证免密登录
用 IP 直接登录,确认不再提示密码。不验证就往下走,后面全是瞎猜。
▸ 判断标准:直接进命令行 = 成功
第四步 · 写入别名配置
在 config 里定义 server-a,长 IP 变成短别名。也让 AI 的调用指令更短、更好维护。
▸ 好处:换服务器只改一行,AI 侧脚本不动
第五步 · 安全收尾
收紧私钥权限、确认撤销路径、再考虑关闭密码登录。这一步省了,前面四步等于开了后门还加了把锁在门上。
▸ 顺序很重要:先验证,再收紧
动手前的约定
本文出现的 A_USER、A_IP 都是占位符,执行时必须替换成真实值。server-a 是自定义别名,可以改成任何你喜欢的名字,但改了之后后面所有命令要跟着一起改。

一、先搞清楚原理:密钥对到底在做什么

不理解原理直接复制命令,一旦失败就只能靠猜。花两分钟把这一节看完,后面每一步你都知道自己在干什么。

▎两个文件的角色分工

私钥 id_ed25519 · 证明自己是谁
留在本地机器上,从不通过网络传输。谁拿到它,谁就是你。
▸ 类比:印章,握在手里,只在纸上盖一下
公钥 id_ed25519.pub · 验证对方是谁
装到服务器的 authorized_keys 里,可以随意分发,泄露也不致命。
▸ 类比:印鉴样本,公开在册,用于比对

▎握手过程其实就三句话

①
客户端把公钥的指纹报给服务器
②
服务器在 authorized_keys 里找到这条公钥,用它对一段随机数加密后发回去
③
客户端用私钥解密并回传,服务器比对成功即放行

整个过程中,私钥既没有出现在网络上,也没有交给服务器。服务器只验证了「对方持有对应私钥」这一件事。

▎和密码登录的差别,不止是省了敲键盘

维度
密码登录
密钥登录
凭据形态
一段可被记住、抄走的字符串
一个本地文件,不参与传输
能否自动化
需交互输入,脚本化就得明文存密码
命令直接带上,天然可脚本化
泄露风险
被撞库、被暴力破解、被日志记录
私钥不出本机则风险极低
撤销粒度
改密码,影响所有使用者
删一行公钥,只影响这一把
可追溯性
共用密码时无法区分操作者
一钥一人,日志可定位到具体密钥
一条不能破的红线
私钥只存在于发起连接的那台机器上。不上传服务器、不提交代码仓库、不发聊天工具。一旦私钥离开本机又落入他人之手,对方就拥有和你完全相同的服务器权限,而且日志里看不出任何异常。

二、先把代号对齐,再看命令

后面的命令里会反复出现这几个占位符。先把它们记住,读命令时就不会被字母绕晕。

B
本地电脑,运行 AI 的那台机器。私钥放在这里
A
云服务器,被控制的那一端。公钥装在这里
A_USER
A 的登录用户名,常见是 root,也可是普通账号
A_IP
A 的公网 IP,注意是公网不是内网
server-a
给 A 起的 SSH 别名,纯自定义,改了要全局同步
建议给 AI 单独开一个账号
不要直接用 root 给 AI 用。建一个专用账号(如 aiops),需要提权时走 sudo 白名单。这样 AI 的权限边界清晰,出问题影响面可控,审计日志也能落到具体人身上。

三、第一步:在本地生成密钥对

在 B 上打开 Windows PowerShell,执行:

ssh-keygen -t ed25519 -C "ai-controller-for-A"

▎三个参数分别管什么

-t ed25519
指定算法。ed25519 是现代默认选择,密钥短、速度快、安全性高
-C "备注"
写进公钥末尾的注释。建议标明用途,服务器上有一堆公钥时才分得清哪把是哪把
保存路径
直接回车,用默认的 ~/.ssh/id_ed25519
passphrase
直接回车留空,理由见下

▎为什么选 ed25519 而不是 RSA

对比项
ed25519
RSA
密钥长度
256 位,公钥约 80 字符
2048 到 4096 位,公钥很长
安全性
约等于 3000 位 RSA
2048 位偏弱,4096 位才够用
性能
签名验证快,握手开销小
计算量大,高频连接时明显
兼容性
OpenSSH 6.5 以上均支持
全平台通吃,老系统唯一选择

只有一种情况该退回 RSA:目标服务器系统极老(CentOS 6 时代的 OpenSSH 5.x),不认 ed25519。那时用 ssh-keygen -t rsa -b 4096。

▎passphrase 留空,是一个有代价的选择

留空的代价
私钥文件一旦被人拷走,可直接使用,没有任何二次验证
设口令的代价
AI 每次调用都要人工输入,免密就失去了意义(除非配合 ssh-agent 预加载)
留空可以,但必须用别的办法补回来
三条补偿措施:① 私钥文件权限只给当前用户;② 这对密钥专用于 AI 控制,不和个人密钥混用;③ 在服务器端给这把公钥加来源 IP 和命令限制。三条都做了,留空的风险才可控。

▎生成后得到两个文件

~/.ssh/id_ed25519
私钥,留在 B,给 AI 用,绝不上传
~/.ssh/id_ed25519.pub
公钥,装到 A,可以公开

四、第二步:把公钥装到服务器上

这一步在本地 PowerShell 里完成,是全流程唯一需要输入密码的环节。下面是一条命令,因排版折行。

type $env:USERPROFILE\.ssh\id_ed25519.pub | ssh A_USER@A_IP "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 700 ~/.ssh && chmod 600 ~/.ssh/authorized_keys"

执行后提示 A_USER@A_IP's password,此时输入服务器登录密码,输入时屏幕不显示字符属正常。

若提示 type 不是可识别的命令,换成等价写法:

Get-Content "$env:USERPROFILE\.ssh\id_ed25519.pub" | ssh A_USER@A_IP "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 700 ~/.ssh && chmod 600 ~/.ssh/authorized_keys"

▎命令拆开看,每一段都不能省

mkdir -p ~/.ssh
创建 .ssh 目录,-p 保证目录已存在时也不报错
cat >> authorized_keys
把标准输入接到授权文件末尾。两个 > 是追加,一个 > 是覆盖
chmod 700 ~/.ssh
目录仅属主可读写进入。权限过宽 sshd 会直接拒绝使用该密钥
chmod 600 authorized_keys
文件仅属主可读写,道理同上
最容易犯的错:把 >> 写成 >
单箭头会把 authorized_keys 清空重写。如果服务器上原有其他公钥(可能包括云厂商注入的、你自己另一台电脑的),一次覆盖就全部失效——轻则别人登不上,重则连你自己都进不去,只能去控制台用 VNC 救场。

▎装完可以核一下

ssh A_USER@A_IP "cat ~/.ssh/authorized_keys"

输出里应该能看到你的注释(例如 ai-controller-for-A)以及 ssh-ed25519 开头的整行。若只看到一个空行或报错,说明管道没生效,重来一次。

五、第三步:验证免密是否生效

ssh A_USER@A_IP
成功表现
不提示密码,直接进入 A 的命令行提示符
失败表现
仍提示 password,或报 Permission denied (publickey)

进入后输入 exit 退回本地。这一步必须成功才能继续——在没确认免密可用的前提下直接去关密码登录,等于把自己锁在门外。

六、第四步:配置别名,把长命令变短

到这一步免密已经能用了,别名是锦上添花,但对 AI 场景很关键:它把「IP + 用户名 + 密钥路径 + 端口」四个变量收敛成一个词,以后换服务器只改配置文件,AI 侧的调用方式一个字都不用动。

$configPath = "$env:USERPROFILE\.ssh\config"
$entry = @"

Host server-a
HostName A_IP
User A_USER
IdentityFile ~/.ssh/id_ed25519
Port 22
"@

New-Item -ItemType Directory -Force -Path "$env:USERPROFILE\.ssh" | Out-Null

if (Test-Path $configPath) {
Add-Content -Path $configPath -Value $entry -Encoding ascii
} else {
Set-Content -Path $configPath -Value $entry.TrimStart() -Encoding ascii
}

Get-Content $configPath

▎五个字段的含义

Host
你以后敲的别名,可随意命名,此处为 server-a
HostName
真实公网 IP,别名解析的目标
User
登录用户名,省去每次写 A_USER@
IdentityFile
显式指定用哪把私钥,多密钥环境下避免选错
Port
SSH 端口,改过默认端口的要在这里同步

▎这段脚本做了三件安全的事

①
不覆盖已有配置
:先判断文件存在,存在则 Add-Content 追加
②
强制 ASCII 编码
:SSH 配置文件不支持 BOM,用 UTF-8 带 BOM 会让整段配置失效
③
末尾打印结果
:直接确认 Host server-a 段落已写入
两个容易忽略的细节
文件名就是 config,没有扩展名,不要存成 config.txt;
重复执行会重复追加同名 Host 段落,SSH 取第一个匹配项,此时需要手动打开文件删掉多余的段。

七、第五步:验证别名,然后交给 AI

ssh server-a

不提示密码、直接进命令行即配置成功。加个 -v 可以看到详细的握手过程,卡在哪一步一目了然,排错时非常有用。

▎AI 侧要满足什么条件

运行位置
AI 运行在 B 上,或它的沙箱 / 容器能够访问到 B 的 ~/.ssh/id_ed25519
能读配置
能读取 ~/.ssh/config,否则别名解析不了
非交互
私钥不能带 passphrase,否则会卡在输入提示上

三个条件都满足后,AI 只需要执行一条命令就能控制服务器:

ssh server-a
ssh server-a "df -h"
ssh server-a "systemctl status nginx"

八、安全收尾:这一步省了,前面全白做

免密打通的那一刻,服务器的访问门槛就从「知道密码」变成了「持有文件」。门槛变低的同时,责任也转移到了文件管理上。

▎私钥的三不原则

不上传
不拷到服务器上「方便另一台机器用」,那等于把钥匙留在锁孔里
不进库
不提交到 Git。一旦推上去,历史记录里永久留存
不外发
不发微信、不发邮件、不贴到任何在线工具里

▎Windows 下收紧私钥权限

如果 SSH 报 UNPROTECTED PRIVATE KEY FILE 或警告权限过宽,在管理员 PowerShell 中执行:

icacls "$env:USERPROFILE\.ssh\id_ed25519" /inheritance:r
icacls "$env:USERPROFILE\.ssh\id_ed25519" /grant:r "${env:USERNAME}:R"
/inheritance:r
断开从上级目录继承的权限,去掉 Administrators、Users 等继承来的条目
/grant:r 用户名:R
只给当前用户只读权限,R 即 Read

▎一对密钥,一个用途

不要拿日常登录用的私钥给 AI 用。专钥专用的价值在于撤销干净:哪天不想让 AI 再碰这台机器,只需在服务器上删掉对应那一行公钥,其他访问一律不受影响。这也是密钥登录相较共享密码最实际的好处。

# 服务器上查看并按注释定位要删的行
grep -n "ai-controller-for-A" ~/.ssh/authorized_keys
# 确认无误后编辑删除
vi ~/.ssh/authorized_keys

▎关闭密码登录的正确顺序

密码登录开着,暴力破解就一直有人在试。但关它有严格顺序,顺序错了就是把自己锁在门外。

①
先确认 ssh server-a 免密登录已成功,且能正常执行 sudo
②
另外保留一个已经登录成功的 SSH 会话不要退出,作为退路
③
修改 /etc/ssh/sshd_config,把 PasswordAuthentication 改为 no
④
用 reload 不用 restart:reload 不中断现有连接,restart 会踢掉所有会话,包括你的退路
⑤
新开窗口再验证一次免密登录,成功后再关掉退路会话
改配置前先备份,改完先测试语法
cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak
sshd -t
sshd -t 通过再 reload。配置有语法错误时 reload 会让 sshd 拒绝加载,运气不好就直接失去所有远程入口,只能去云控制台开 VNC。

▎进阶:给这把公钥本身加上限制

authorized_keys 的每一行不只是公钥,行首还可以带选项。把限制写在这里,比在服务器全局做限制更精细——只约束这把密钥,不影响别人。

选项
作用
from="IP"
只接受来自指定 IP 的连接,私钥泄露到别处也用不了
command="..."
登录后只允许执行指定命令,无法获得交互式 shell
no-pty
禁止分配终端,进一步限制交互操作
restrict
一键关掉端口转发、X11、agent 转发等所有扩展能力,最省事的加固起点
# 一行完整的授权记录示例
restrict,from="203.0.113.10" ssh-ed25519 AAAAC3Nza... ai-controller-for-A
加限制之前先想清楚一件事
写了 from 之后,如果本地是动态公网 IP(家宽常见),IP 一变 AI 就登不上了。动态 IP 场景要么跳过 from 只做 restrict,要么留一条备用路径。

九、排错清单:六种高频失败

免密配置失败,九成以上是下面六种情况。按「现象 → 原因 → 处理」对号入座即可。

① 仍然提示输入密码
原因
:公钥没真正写进 authorized_keys(管道被 PowerShell 编码影响)、服务器 sshd 关闭了公钥认证、或家目录权限过宽被 sshd 拒绝。
处理:先 cat ~/.ssh/authorized_keys 确认内容;再查 grep PubkeyAuthentication /etc/ssh/sshd_config 是否为 yes;最后看家目录权限是否不超过 755。
② Permission denied (publickey)
原因
:用户名写错(root 与普通用户是不同的 authorized_keys)、选错了私钥、或服务器禁用了该用户登录。
处理:用 ssh -v 看日志里实际尝试的是哪把密钥、期望哪个用户;确认 authorized_keys 写在目标用户的家目录下。
③ 用 IP 能登,用别名不行
原因
:config 文件名带扩展名、编码不是 ASCII、或 Host 段的缩进 / 拼写有误。
处理:确认文件名就是 config 无后缀;用 ssh -G server-a 打印最终解析结果,能看清实际生效的参数。
④ 公钥装进去但被识别成乱码
原因
:Windows 的换行符是 CRLF,追加进 authorized_keys 后每行末尾多一个不可见字符,sshd 无法解析。
处理:在服务器上执行 sed -i 's/\r$//' ~/.ssh/authorized_keys 清理一遍。
⑤ 连接超时,连密码提示都不出现
原因
:网络层不通,与密钥无关。安全组没放行、端口填错、或 IP 已变更。
处理:先用 telnet 或 Test-NetConnection A_IP -Port 22 确认端口可达,再去云控制台检查安全组规则。
⑥ 本地报 UNPROTECTED PRIVATE KEY FILE
原因
:Windows 私钥文件继承了目录权限,其他用户组可读。
处理:按第八节的两条 icacls 命令收紧权限即可。

十、总结:三条容易做反的原则

① 先验证,再收紧
关密码登录、加 IP 限制、禁 root 登录,全都是收紧动作。每一个动作之前都要先确认新路径可用,并且留一条已登录的会话做退路。顺序反了,代价是跑去机房或开 VNC。
② 权限给到刚好够用,不多一分
给 AI 专用账号而不是 root,给 sudo 白名单而不是全权,给 from 限制而不是任意来源。治理的本质是缩小每次操作的授权范围,而不是相信对方不会犯错。
③ 撤销路径必须提前想好
一钥一人、一钥一用途,注释写清归属。哪天要收回权限,删一行就行。做不到单点撤销的授权方式,都不适合长期交给自动化系统使用。
最后一句
整个过程真正的难点不在命令,而在边界:这把密钥能给谁、能从哪里连、能做什么事、怎么收回来。命令十分钟就能跑完,这四个问题的答案,决定了这套配置是提效工具还是安全缺口。

架构师学习笔记 · SSH 密钥与自动化运维

相关学习资料