我最早干这行的时候,所有服务器登录都靠密码硬扛。那时候觉得也没什么问题,直到有一回在一台机器上连续输错几次密码,直接把IP给封了,远程同事那边系统直接进不去,最后只能找机房的人帮忙重启网络。那事之后我才老老实实把xshell公钥登录配起来,前后花了不到十分钟搞定,后来再没被密码折腾过。
xshell配公钥登录这件事,本质上就是让你在连接远程服务器的时候,不再输入密码,而是用一个本地生成的密钥对来证明“我是我”。操作本身不复杂,但里面涉及几个关键环节——密钥怎么生成、公钥怎么放上服务器、权限怎么设对、会话怎么关联密钥。每一步都不难,但漏掉任何一步都会让整个配置卡壳。
这篇东西适合正在给Linux服务器做安全加固的同学,也适合刚接触xshell、搞不懂密钥对和authorized_keys文件到底是什么的新手。我会把从零开始的完整过程拆开讲,包括每种操作背后的原因,以及我在实际环境里踩过的坑和排查思路。
1. 为什么非要配公钥登录,而不是继续用密码
1.1 密码登录的三个致命短板
如果你只是在一台临时测试机上用root密码登录,问题不大。但只要你手上有三四台以上、需要长期维护的服务器,纯密码登录的体验就会瞬间转向灾难。
第一是安全问题。密码是会被猜的、被扫的、被暴力撞的。任何一台暴露在公网上的Linux服务器,只要你开了22端口,几乎每天都会收到来自各个角落的自动扫描连接。这些扫描脚本专门针对常见的弱口令词典尝试登录,即使你自认为密码设得够复杂,也经不住长时间被人撞库。日志里那些反复出现的"Failed password"记录就是证据。
第二是管理问题。团队里每人一套密码,就意味着密码在不同人之间流转,离职、换岗、共享账号,每个环节都可能成为突破口。改密码又得挨个服务器通知,批量修改服务器密码这个动作本身就很容易出错。
第三是体验问题。密码输入多了你自然会偷懒,比如保存会话密码、用同一个密码走天下。一旦有了这些习惯,安全防线就形同虚设。
1.2 公钥登录到底是怎么回事
公钥登录基于非对称加密。本地会生成一对密钥:一个私钥,一个公钥。私钥保存在你的电脑上,公钥则部署到服务器上。连接服务器时,服务器会生成一段随机数据,用你放上去的公钥加密,发回给客户端。你的xshell收到这段密文后,用本地私钥解密,把解密结果再发回给服务器。服务器验证一致,就说明私钥的所有者确实是你。
整个过程密码根本不出现在网络传输里,所以不存在密码被窃听的问题。更重要的一点是,私钥永远只放在你自己的电脑里,别人拿不到,这人就冒充不了你登录。如果私钥本身还设置了口令(Passphrase),那即使电脑被人摸走,对方不知道口令也解不开私钥,相当于多了一层保险。
1.3 xshell在密钥管理上的设计思路
xshell本身是一个Windows平台上的SSH客户端,它在密钥管理上做了一套图形化界面,把OpenSSH命令行的那套逻辑封装成了几个按钮。理论上你可以完全不懂ssh-keygen命令,在界面里点几下就能生成密钥对。但我强烈建议你至少理解背后发生了什么,因为后期排查问题的时候,你总归要回到命令行手动操作服务器文件的。
xshell的密钥管理在"工具"菜单下,里面可以生成、导入、导出密钥。它生成的私钥有两种格式:一种是OpenSSH格式,方便跨平台使用;另一种是xshell私有的格式(.pem或者纯文本),仅限xshell自己用。如果你以后要在其他工具里使用同一对密钥,建议导出成OpenSSH格式,兼容性最好。
2. 配置前的环境盘点
2.1 确认你的服务器和客户端状态
开始操作前,先花两分钟确认几件事:服务器的操作系统是什么(常见的就是CentOS系、Ubuntu系、Debian系,核心逻辑都一样),是否开启了SSH服务,以及本机xshell版本。
检查SSH服务状态很简单:
sudo systemctl status sshd如果没启动,先启动并设为开机自启:
sudo systemctl start sshd sudo systemctl enable sshdUbuntu系服务名可能是ssh而不是sshd,记不住的时候可以执行systemctl status ssh试试。
还要确认远程服务器上~/.ssh目录是否存在。如果是新装的系统,这个目录往往不存在,需要先创建:
mkdir -p ~/.ssh chmod 700 ~/.ssh我自己见过太多人忽略这一步。公钥文件放进去之后,如果home目录权限不对或者.ssh目录带了奇怪的权限组,登录就会直接报错。
2.2 xshell版本差异问题
xshell 5、6、7的界面略有不同,但整体逻辑一致。新版xshell在“工具”菜单里能找到“新建用户密钥生成向导”,旧版叫“用户密钥生成向导”。如果你用的是英文版,菜单名对应的是“User Key Generation Wizard”。
另外特别提醒一句:xshell下载页面经常附带全家桶提示,注意装的时候把不想要的附加组件勾掉,这不影响本教程的密钥功能。我在帮同事处理问题时碰到过几次,装完xshell连ssh命令的默认行为都被干扰的,最后都是重装干净版本解决的。
2.3 准备一张白纸记录关键信息
配置过程中你会需要几个信息:服务器IP、登录用户名(建议别直接用root,后面我会说)、目标服务器上是否已有其他密钥。把这些写在纸上,避免操作到一半忘了当初用的哪个账号。
3. 生成密钥对:图形化操作和命令行操作哪个更适合你
3.1 xshell图形化生成密钥对
打开xshell,点击顶部菜单“工具”->“新建用户密钥生成向导”。第一步选择密钥类型,默认是RSA,建议直接选RSA,加密长度下拉框里选2048或者4096。我自己的习惯是4096,虽然生成的密钥文件稍大,但安全性更好。在目前的硬件条件下,生成密钥的耗时几乎可以忽略。
点击“下一步”,xshell会提示你移动鼠标或者敲键盘来收集随机信息,这个动作实际上是给你的密钥生成过程注入随机熵。随便在键盘上敲一会儿,它会自动进入下一步。这一步不要直接跳过,我试过连点太快的,生成的密钥校验时提示强度不足的,虽然几率很低,但没必要去赌。
然后你会看到生成的公钥预览,也就是以ssh-rsa开头的一大串字符。这个界面可以给密钥起一个名称,这个名称会直接用作私钥文件名。比如你起名mycloud,那生成的私钥文件就叫mycloud,公钥文件名就是mycloud.pub。
最后一步是设置私钥口令(Passphrase)。这个口令跟服务器密码是两码事,它只加密你本地的私钥文件。每次用私钥登录时,如果设置了口令,xshell会要求输入这个口令,如果没设置就可以直接免密登录。我建议在自己的个人电脑上可以不为私钥设口令,方便日常操作;但在公用电脑上必须设口令,否则私钥跟裸奔没区别。
生成完成后,xshell会在工具菜单的“用户密钥管理者”里显示这个密钥。
3.2 命令行生成密钥对让兼容性更好
如果你手头还有其他SSH工具,或者需要在Linux主机间跳转,那么用OpenSSH的标准方式生成密钥会更通用,xshell也能直接导入使用。
在本地任一终端里执行:
ssh-keygen -t rsa -b 4096 -C "your_comment" -f ~/.ssh/mykey-C参数是对这个密钥的注释,方便你日后区分是给哪台机器用的。会提示设置passphrase,不想设置就直接回车继续。
生成后目录下会出现两个文件:
mykey # 私钥 mykey.pub # 公钥公钥文件内容是纯文本,可以放心查看、复制、传输。私钥文件就千万别外传了,任何聊天工具、网盘、公司共享盘都别放。
3.3 两种生成方式怎么选
我个人的结论是这样的:你在Windows上只打算用xshell,那图形化向导足够用了;你可能有跳板机登录、计划用终端工具连服务器、或者想把这套密钥配置自动化,那就用命令行生成,然后到xshell里“导入”这个私钥文件。
xshell导入私钥的路径是“工具”->“用户密钥管理者”->“导入”,选择私钥文件后输入你设置过的口令,即导入完成。这个方法也适合你在服务器A上生成了密钥,想把同一对密钥用到服务器B上的场景,私钥文件拷到本机后导入即可。
4. 把公钥放到服务器上:三种思路各有优劣
4.1 方法一:自动分发命令最省事
如果服务器允许你登录后直接执行命令,最标准的方式就是用ssh-copy-id。前提是你当前能通过密码登录这台服务器。
在本地终端执行:
ssh-copy-id -i ~/.ssh/mykey.pub user@server_ip命令会提示你输入user在服务器上的登录密码,验证通过后,它会自动把公钥内容追加到服务器的~/.ssh/authorized_keys文件里,并且把该文件的权限调整为600。
这个命令的好处是安全且自动化。它不会销毁服务器上已有的公共密钥,只是在现有authorized_keys文件末尾追加一条。它还会自动处理.ssh目录权限等细节。如果服务器上没有~/.ssh目录,它也会帮你创建。
但是有一个限制:服务器必须支持你用该用户密码登录。有些刚做完安全加固的机器,root密码登录本身就被禁掉,普通用户密码登录也加了一堆限制。这时候ssh-copy-id会直接失败,那就得用下面的手动方式。
4.2 方法二:手动复制粘贴最稳
先用当前方式登录服务器(密码登录或者已有的密钥登录),然后打开你的本地公钥文件,把里面的内容整行复制。
在服务器上执行:
mkdir -p ~/.ssh chmod 700 ~/.ssh echo "ssh-rsa AAAAB3NzaC1yc2EAAA... your_comment" >> ~/.ssh/authorized_keys chmod 600 ~/.ssh/authorized_keys注意用的是>>追加而不是>覆盖,特别是在服务器上已经有别人公钥的情况下,覆盖会导致别人无法登录。
如果你的公钥文件内容很长,复制粘贴容易断行,更稳妥的做法是用scp把公钥文件传到服务器上,再追加进去:
scp ~/.ssh/mykey.pub user@server_ip:/tmp/在服务器上执行:
cat /tmp/mykey.pub >> ~/.ssh/authorized_keys && rm -f /tmp/mykey.pub直接把公钥文件放在/tmp下有点风险,但如果只是临时放一下然后立即清理,问题不大。
4.3 方法三:xshell登录脚本自动执行
xshell的登录脚本功能其实还能帮你做公钥分发。在会话属性里,找到“登录脚本”标签,设置一个发送命令的规则,登录成功后自动执行:
mkdir -p ~/.ssh && chmod 700 ~/.ssh && echo "ssh-rsa AAA..." >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys这个方法看上去很炫,但我不推荐给新手。因为如果服务器配置有特殊要求,比如需要先经过跳板机或者sudo模式,脚本很容易出错。还是老老实实手动执行更清晰。
4.4 三种方式的权衡表
为了方便直接对比,我把三种方式整理成表格:
| 分发方式 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| ssh-copy-id | 自动处理权限和追加,不易出错 | 需要当前能密码登录 | 大多数常规场景,首选 |
| 手动复制追加 | 思路简单,全程可控 | 容易在粘贴时断行,权限要自己设 | 服务器加固后无法用密码登录 |
| xshell登录脚本 | 可视化,点几下就行 | 配置逻辑比较复杂,出错难排查 | 已经熟悉xshell的高级用法 |
5. 权限设置是公钥登录的第一大坑
5.1 权限不对直接登录失败
无论用哪种方式完成公钥分发,最终都必须检查服务器端的权限配置。很多教程到这里就结束了,但我可以负责任地说,公钥登录失败原因里,权限错误至少占了一半。
OpenSSH对权限是比较挑剔的。它的原则是:私钥、公钥、配置目录都不能让其他用户可写,否则会认为存在安全隐患,直接拒绝使用你的公钥登录。
进入服务器后执行:
ls -la ~/.ssh预期看到的列表大致是这样:
drwx------ 2 user user 4096 Jun 10 10:00 . drwx------ 6 user user 4096 Jun 10 09:58 .. -rw------- 1 user user 751 Jun 10 10:00 authorized_keys如果发现任何差异,执行修复:
chmod 700 ~/.ssh chmod 600 ~/.ssh/authorized_keys chmod 644 ~/.ssh/*.pub还有home目录本身,权限也不能太开放。Shorthand规则是home目录不能是775或者777,最好保持755或者700。如果home目录是777,有些版本的openssh同样会拒绝密钥验证。
5.2 SELinux不是只有安全专家才要关心
CentOS和RHEL系服务器默认开着SELinux,它可能会拦截sshd对home目录的读取。如果权限都设对了,连接还是失败,查看SELinux状态:
getenforce输出如果是Enforcing,检查sshd相关的SELinux布尔值:
getsebool -a | grep ssh通常需要保证use_ssh_keys布尔值是on。执行:
setsebool -P use_ssh_keys 1对于大多数云镜像里的CentOS默认配置,这一步不是必须,但遇到权限没问题却依然登录失败的情况时,它就是排查的下一个方向。
5.3 authorized_keys文件已存在时的合并策略
当你不是第一次配置密钥,服务器上已经有人部署过公钥时,绝对不要贸然覆盖authorized_keys。我之前在帮一个团队整理跳板机的时候,就因为顺手按了回车把整份文件清空了,导致几个同事全被拒之门外,后来挨个找他们重新添加公钥。
正确做法是先用cat确认现有内容:
cat ~/.ssh/authorized_keys每一行是一条公钥记录。新的公钥追加到文件末尾即可。如果这条新公钥和其他人公钥恰好有断行拼接的情况,登录可能被识别为无效公钥。保险的做法是追加后查看一次文件最后两行,确认完整。
6. 在xshell里创建会话并绑定密钥
6.1 新建会话的正确配置方法
前面几节把密钥对生成和公钥分发讲完了,现在回到xshell客户端,把最后一步连接配置完成。
新建会话,在弹出的“新建会话属性”窗口里,先填写主机地址和端口号。然后点击左侧“用户身份验证”标签页。在“方法”中选择“Public Key”。在“用户密钥”下拉框里选中你刚才生成或导入的私钥名称。如果私钥设置了口令,xshell会在连接时提示输入口令,勾选“用户密钥”下方的“记住口令”可以省去重复输入。
把“用户名”填写成你在服务器上的实际登录用户。注意这里不是root就填root,而是你生成公钥时用的那个用户。如果你把公钥配置到了普通用户user的.ssh/authorized_keys里,登录名就必须填user,否则回调到root的authorized_keys去验证,自然找不到你的公钥。
6.2 使用跳板机时的多级配置
如果你是通过跳板机再访问目标服务器,xshell的“连接”->“代理”标签里可以填跳板机信息。密钥验证在每一级都可能生效。最常见的情况是:你本机到跳板机用密钥A,跳板机再到目标机用密钥B。
这种情况下xshell里只能为一个会话配置一个用户密钥,但你可以创建两个会话:一个连接到跳板机,一个直连目标机但走跳板机的代理。第二个会话的“用户密钥”就是目标机的密钥B,代理认证如果还需要密钥A,xshell会弹窗让你选。这功能我用过多次,算是xshell比较顺手的地方。
6.3 保存会话并测试连接
配置完之后,点击“确定”保存会话。建议立即测试一次。
连接时会遇到三种情况:
- 直接登录成功,没有再要求输入任何密码
- 弹出提示要输入私钥口令,输入后登录成功
- 登录失败,提示Permission denied
第三种情况说明前面的某个环节出了问题,不要在那里干瞪眼,直接跳到下一章查问题。
我个人会建议在刚开始测试时,不要先禁用密码登录,否则密钥没配对好,连密码兜底通道也没了,服务器直接进不去了。等密钥登录确认稳定之后,再决定是否关闭密码认证。
7. 常见问题与排查技巧实录
7.1 Permission denied (publickey) 到底是谁拒绝了谁
这个错误是公钥登录失败的“标准名片”,但它的成因可能至少有五六种。你要做的第一步是搞清楚是服务器端拒绝了密钥,还是客户端没有把密钥发出去。
在服务器上查看ssh服务日志:
sudo tail -f /var/log/secureCentOS系的日志在/var/log/secure,Ubuntu系在/var/log/auth.log。连接失败时会看到类似这样的信息:
Failed publickey for user from x.x.x.x port 52672这就意味着服务器确实收到了公钥验证请求,但验证失败。
接着做两件事:
- 检查服务器端authorized_keys文件里到底有没有你发的公钥
- 检查文件权限,是否600
- 检查用户名是否匹配
大多数情况下,要么公钥粘贴时少了几个字符,要么权限又变成644甚至777了。把权限和内容重新确认一遍,问题通常就解开了。
7.2 权限没错但依然被拒,两个隐藏原因
第一个是StrictModes配置。sshd_config里如果设置了StrictModes yes,sshd登录时不仅检查authorized_keys权限,还会检查home目录和.ssh目录的属主与权限。只要home目录是别属主,或者.ssh目录允许组写入,就会拒绝登录。临时放宽检查的方法是:
sudo vi /etc/ssh/sshd_config # 找到 StrictModes,改为 StrictModes no # 然后重启 sshd,但仅用于排查,不建议长期开着第二个是authorized_keys文件本身的属主不对。如果文件属主是root而不是你当前登录用户,即使权限是600,sshd也可能不认。修复方式:
sudo chown user:user ~/.ssh/authorized_keys7.3 私钥格式不对的经典报错
在xshell里导入在Linux上生成的OpenSSH私钥时,有时会提示格式不支持。老版本xshell对OpenSSH新格式私钥的支持不够好。这种情况不要急着重新生成密钥,换个思路:在服务器上重新生成一次OpenSSH兼容格式的私钥,或者用ssh-keygen做格式转换。
在Linux执行:
ssh-keygen -p -f mykey它会引导你输入旧口令、设新口令,转换格式。在交互提示里选择保存为OpenSSH格式即可。转换完的私钥拿到xshell里导入,绝大多数情况就能用了。
7.4 xshell提示“没有可用密钥”
有时候你会看到连会话配置里的“用户密钥”下拉框都是空的。这种情况说明密钥还没有导入到xshell中。回到“工具”->“用户密钥管理者”里确认密钥是否存在。生成向导如果中途没点完,密钥不会保存。所以生成完毕之后,留意窗口是否提示“完成”。
如果你是从命令行生成的密钥对,用公钥分发后发现自己手里只有.ssh文件夹里的两个文件,想导入xshell,记得导入的是私钥文件(mykey),而不是公钥(mykey.pub)。
7.5 root登录被单独禁掉的情况
很多服务器默认配置禁止root通过密码登录,甚至禁止root通过SSH登录。你如果始终用root去测密钥,思路就被锁死了。正确的做法是先创建一个带sudo权限的普通用户。
以root身份登录服务器(通过控制台或者其他方式):
useradd -m -G wheel deploy passwd deploy然后把公钥放到deploy用户下,用deploy测试密钥登录。等密钥确认没问题了,再用这个普通用户切root。我自己的习惯是日常所有操作都先用普通用户登录,再sudo提权,root的SSH登录权限直接禁用掉,安全性会高很多。
7.6 常见问题速查表
| 检查项 | 正确值 | 错误示例 |
|---|---|---|
| ~/.ssh目录 | 700,属主为当前用户 | 755或属主为root |
| ~/.ssh/authorized_keys | 600,属主为当前用户 | 644或组可写 |
| home目录 | 755或700 | 777或组可写 |
| 登录用户 | 与公钥所在用户一致 | 用root测普通用户公钥 |
| 私钥格式 | OpenSSH格式(xshell可识别) | 纯Text乱码 |
| 公钥内容 | 以ssh-rsa开头,单行 | 多行断行、缺少前缀 |
| SELinux布尔值 | use_ssh_keys为1 | Enforcing且布尔值为0 |
8. 密钥配置完成后,顺手做的安全加固
8.1 关闭密码登录的时机和操作
新密钥确认能登录之后,可以从服务器端关闭密码认证。修改/etc/ssh/sshd_config:
PasswordAuthentication no PubkeyAuthentication yes ChallengeResponseAuthentication no保存后重启sshd服务:
sudo systemctl restart sshd在这之后,所有人登录这台服务器都必须持有私钥。服务器暴露在公网上的密码爆破攻击面就等于关掉了。
这里建议操作前再三确认本配置修改不会锁死自己。修改之后不要立即关闭当前已连接的会话,先另开一个新会话,用密钥登录一次,验证没问题,然后再关闭旧会话。如果新连接出问题,旧窗口还在,还能回退配置。这几秒的谨慎能帮你避免一次远程失联的惨剧。
8.2 私钥进一步加密:用passphrase兜底
私钥文件本身落盘在你的电脑上,一旦电脑失窃或被植入恶意软件读取文件,攻击者就能在无意识间偷走你的密钥。为了避免这种事情,强烈建议给私钥设置passphrase。
xshell的图形向导在生成时会让你设置口令。命令行生成时如果当初跳过了,可以用:
ssh-keygen -p -f mykey这条命令会触发重新设置私钥口令的过程。以后连接服务器时,xshell会弹出小窗让你输入私钥口令。虽然每天多了一次输入,但安全性提升是实打实的。
8.3 建立密钥轮换与审计的习惯
技术配置完,工作只完成了一半。密钥是长期性的资产,一样会过期、泄露、被误用。
我建议在团队里做一个简单的记录表,记录每对密钥用在哪台服务器、给谁使用、创建时间、联系人。每半年对密钥做一次盘点,把不再使用的公钥从authorized_keys里删掉。必要时可以定期更替,重新生成密钥、重新分发。
另外还有一个隐藏问题:xshell里如果保存过旧密钥,你重新生成了密钥后依旧会碰到旧密钥被自动尝试的情况。这是因为xshell在连接时会自动筛选匹配的“用户密钥”,你如果在密钥管理者里没有删除旧密钥,可能一连就报错。遇到这种情况,删掉旧的私有密钥,重新添加新的即可。
9. 我踩过的一次完整的坑,复盘给你看
前阵子帮同事配置一群测试服务器。同事自己用xshell的向导生成了一对密钥,把公钥内容复制粘贴到服务器的authorized_keys,然后测试连接,结果一直弹Permission denied。
我过去看了一下,服务器端权限全是好的,用户也是对的,authorized_keys内容在界面上看起来也没问题。我就纳闷了,打开文本逐字符比对才发现,粘贴的时候把开头的ssh-rsa AAAA字段里的空格吞掉了,公钥前缀断成了ssh-rsaAAAA。这种情况OpenSSH解析时直接判定非法公钥。
像这类肉眼几乎无法发现的问题,解决方式很粗暴:把他本地公钥文件直接scp传上去,再追加进authorized_keys,文件内容不会有半点缺失。所以我一度很抵触纯手动复制粘贴公钥,能传文件就尽量传文件,省心。
另一次是在某实验室服务器上,密钥配好了,第一次免密登录成功了,结果重启机器之后突然又不行了。查了半天发现系统的/etc/ssh/sshd_config里有一行AuthorizedKeysFile /etc/ssh/%u.keys,ssh服务被改成从系统级路径读取公钥,而不是默认的~/.ssh/authorized_keys。如果你的系统也做过类似的定制,改回默认值,问题自然消失。
这两次经历让我养成了一个习惯:每次配完密钥后,立刻断开重连测试三次,其中一次要在服务器重启后测试,才能真正证明这套方案是稳的。毕竟公钥登录这事,配好比配对更重要,配置的细节一旦错了,后续排查的成本高得惊人。
10. 一点个人总结
xshell配置公钥登录的整体流程,说穿了就是“生成密钥—部署公钥—对权限—绑会话”四个动作。很多人卡住,不是因为哪个操作难,而是因为不知道每一步背后的意义。当你理解了authorized_keys是公钥的收纳箱、.ssh目录权限是打开收纳箱的钥匙,各种报错在你眼里就自动拆解了。
如果在看了这篇文章之后还遇到问题,我建议你按这样的顺序排查:先确认本地私钥确实存在于xshell密钥管理者里,然后确认服务器上authorized_keys内容完整且无断行,接着核对权限三件套,最后看SELinux状态。按这个清单走下来,十次里至少有九次能直接定位到原因。
我的建议是,不要跳过公钥登录这一步,不要觉得“暂时够用”就忽视它。密码登录在任何一台长期运行的服务器上,都像一扇随时可以被人试着砸开的门。十几分钟配置一次,换来的安全收益是一劳永逸的。
最后再分享一个小技巧:配置完公钥登录后,把服务器上的密码登录关掉之前,先确认你已经可以用密钥登录,然后再顺手测一下用“密钥+代理”方式从另一台机器登录也正常。两边都通了,才算完整。我自己现在管理每一台服务器,登录的第一步一定是密钥验证,密码登录这个选项在我的服务器上早就是关闭状态了。