先说个现实:我自己的服务器从用密码登录切换到SSH密钥登录,已经好几年了。现在让我回到纯密码登录,我肯定不愿意。原因很简单——密码登录又麻烦又不安全,而密钥登录在安全性和便利性之间找到了最平衡的方案。项目标题里的"客户机与服务器ssh秘钥登录",本质就是一件事:让客户机可以不用密码、安全地登录远程服务器。
这篇文章我会从原理讲到实操,再到日常高频场景的配置,最后把常见问题一次说透。适合谁看?刚接触Linux服务器的小白、管理多台云服务器的运维、经常用VSCode远程开发的前后端,都能在这里找到自己需要的部分。我会尽量保持说人话,保证你看完能自己搞定密钥认证,而不是只拿到一堆命令。
1. SSH密钥登录的核心思路拆解
1.1 为什么密码登录越来越不够用
先聊我自己的体验。早年间管理服务器,用的是密码登录,每次ssh user@ip然后输密码。一开始觉得没啥,但服务器数量多起来之后,问题就暴露了。
第一是记忆负担。每台服务器的密码如果都不同,你得记一长串;如果都相同,一台机器被攻破就全部沦陷。第二是暴力破解风险。哪怕设置了复杂密码,只要服务器暴露在公网上,每天都会看到大量的Failed password日志,这个我不止一次被吓到。第三是团队协作的麻烦。几个人共用一个密码,有人离职了,要不要改?怎么通知所有人?效率极低。
密钥登录正好把这几个痛点全部解决:登录不需要输入密码,密码本身不存在于认证流程中;私钥不会传输到服务器,破解难度完全不在一个量级;团队成员各自持有自己的私钥,人员变动时只需要在服务器上增删对应的公钥一行配置,几秒钟搞定。
1.2 非对称加密:密钥认证的工作原理
SSH密钥认证基于非对称加密算法,常见的是RSA和Ed25519。它生成一对密钥:私钥和公钥。公钥可以公开分发,私钥必须自己保存好。
怎么理解这对密钥的关系?我用一个生活化类比:公钥就像一把挂锁,你可以把挂锁发给任何人;私钥是你手里的钥匙。别人用挂锁锁上一个箱子,只有你的钥匙能打开。对应到SSH场景,服务器手上的authorized_keys就是它信任的"挂锁"集合,客户机用私钥来证明"我就是那个拥有钥匙的人"。
实际认证流程分三步:
- 客户机发起SSH连接,向服务器声明要用公钥认证。
- 服务器从对应用户的authorized_keys文件中找到该客户机的公钥,生成一个随机挑战并加密,发给客户机。
- 客户机用私钥解密挑战,把结果回传给服务器。服务器验证通过,允许登录。
整个过程私钥始终留在客户机本地,不会出现在网络传输中,也不会上传到服务器。这就是密钥认证比密码认证安全的根本原因——密码每次登录都在传输,存在被截获的风险;私钥不参与双向传输,只要本地不被攻破,外部很难破解。
1.3 客户机与服务器的"角色分工"
在SSH密钥认证中,双方的角色非常清晰。
客户机是发起连接的一方,负责生成密钥对、保管私钥、向服务器提交公钥。服务器是被连接的一方,负责存放公钥,在客户机连接时发起挑战并验证身份。
公钥存放在服务器的哪个位置?对应用户家目录下的~/.ssh/authorized_keys文件。比如root用户登录,公钥就在/root/.ssh/authorized_keys;用ubuntu用户登录,就在/home/ubuntu/.ssh/authorized_keys。
这里有个概念要和虚拟机里的"客户机"区分开。在VMware这类虚拟化平台中,"客户机"通常指虚拟机Guest OS,和宿主机Host对应。但在SSH语境下,客户机指的是发起连接的那台机器,角色由连接方向决定,和机器类型无关。比如你用宿主机SSH连接虚拟机里的Linux,那宿主机就是客户机,虚拟机就是服务器端。我在后面会提到虚拟机场景的注意事项,因为热词里也有"客户机操作系统已禁用cpu"这类报错,这说明不少人确实在用虚拟机做服务器端测试,一旦虚拟化层出问题,SSH服务自然也起不来。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 环境准备与工具选型
2.1 客户机端:系统自带还是第三方工具
先说客户机的工具选择。在Windows上,Win10 1809之后的系统内置了OpenSSH客户端,打开PowerShell或CMD直接输入ssh命令就能用。如果提示"ssh不是内部或外部命令",可以在系统设置的"可选功能"里把OpenSSH客户端装上。
如果你觉得纯命令行不够方便,常用的第三方工具有三个:
- Xshell:免费版够用,界面友好,支持密钥管理和端口转发,我早期在Windows上管理服务器基本都用它。
- SecureCRT:老牌商业工具,功能全面,企业环境用得多,支持密钥对管理。
- MobaXterm:多标签页、自带文件传输界面,会话管理很舒服,我在管理多台服务器时很喜欢用它。
在Linux和macOS上就不用纠结了,系统自带的OpenSSH客户端就是标准方案,直接用终端里的ssh命令。
工具选型的核心建议是:日常连一两台服务器,用系统自带就够;要管理多台、频繁传文件、做端口转发,选一个顺手的图形化终端工具能省很多事。工具本身不影响密钥认证的逻辑,配置好的密钥在任何工具里都能复用。
2.2 服务器端:确认sshd服务状态
服务器端必须运行SSH服务sshd。绝大多数Linux发行版默认安装了OpenSSH Server,但部分精简版系统和某些云镜像可能没有。
用以下命令确认:
bash复制sudo systemctl status sshd
如果显示active (running),说明服务正常。如果没有运行:
bash复制sudo systemctl start sshd
sudo systemctl enable sshd
在云服务器上还有一个关键点:安全组规则必须放行22端口或你自定义的SSH端口。这个非常常见——本地一切配置正常,但连接就是超时,最后发现是云平台的安全组没放行端口。我建议你在配置密钥之前,先确保能用密码登录,这样至少说明网络链路和服务本身是通的。
2.3 虚拟机场景下的环境检查
如果你是在VMware这类虚拟化环境里搭服务器端,有几个额外的检查点。
第一个是网络模式。虚拟机作为SSH服务器时,宿主机要能访问到虚拟机IP。通常用桥接模式或仅主机模式,NAT模式下宿主机一般也能访问,但外部机器不一定能进来。先用ping验证宿主机到虚拟机IP通不通。
第二个是虚拟机系统里有没有启动SSH服务。有的系统镜像安装完默认不启动sshd,需要手动安装并启动。
第三个是虚拟化层的干扰。热词里有一句"客户机操作系统已禁用cpu。请关闭或重置虚拟机",这其实是VMware的CPU虚拟化报错,和SSH本身无关,但虚拟机系统运行异常,sshd自然起不来。遇到这类问题,先关机重置虚拟机、检查VMware Tools和虚拟化设置,再排查SSH。热词里还提到"vmware tools不再随旧版客户机操作系统的vmware workstation一起提供",这个也是VMware Tools的安装策略变化,和SSH无直接关系,但虚拟机里没有VMware Tools时网卡驱动可能异常,影响网络连通性。简单说,虚拟机做的服务器端连不上SSH时,先把目光放到虚拟化环境本身。
3. 密钥生成与配置全流程
3.1 在客户机生成密钥对:选算法和参数
生成密钥对的命令在Windows、Linux、macOS上完全一样:
bash复制ssh-keygen -t ed25519 -C "your_email@example.com"
我个人强烈推荐Ed25519算法。它的密钥更短、生成速度快、安全性高,现代OpenSSH版本都支持。有些老旧的服务器只支持RSA,那就用:
bash复制ssh-keygen -t rsa -b 4096 -C "your_email@example.com"
-b 4096指定RSA密钥长度,低于2048不建议使用。-C后面的注释一般写你的邮箱或机器名,作用是方便区分多个公钥,不影响认证逻辑。
执行后会问保存位置,默认是~/.ssh/id_ed25519,直接回车。接着会问passphrase,也就是私钥的保护口令。我建议设置一个,因为私钥文件如果被拷走,没有passphrase也无法使用。代价是每次SSH连接时多输一次口令,如果你嫌麻烦也可以直接回车留空,相当于私钥裸奔,安全性取决于客户机本身的安全程度。
生成完成后,~/.ssh目录下会多出两个文件:
id_ed25519:私钥,绝对不能外传。id_ed25519.pub:公钥,可以随意分发。
在Linux/macOS上,私钥文件权限必须是600,公钥是644。如果权限过宽,SSH会直接拒绝使用该密钥,报错类似"Permissions too open"。修复命令:
bash复制chmod 600 ~/.ssh/id_ed25519
chmod 644 ~/.ssh/id_ed25519.pub
3.2 把公钥分发到服务器:两种方式
方式一,最省事的方式,用ssh-copy-id:
bash复制ssh-copy-id user@server_ip
命令会提示输入一次密码,然后自动把本地的~/.ssh/id_ed25519.pub追加到服务器上对应账号的~/.ssh/authorized_keys中,同时自动创建目录和修正权限。这条命令几乎不会出错,是我推荐的首选方式。
方式二,手写命令,适合ssh-copy-id不可用的Windows环境:
bash复制cat ~/.ssh/id_ed25519.pub | ssh user@server_ip "mkdir -p ~/.ssh && chmod 700 ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys"
这条命令做的事很完整:在服务器上创建.ssh目录、设置目录权限为700、追加公钥内容、设置authorized_keys文件权限为600。每一步都不能少,因为SSH对权限很敏感。
Windows用户在没有ssh-copy-id工具时,也可以用文本方式手动完成:用记事本打开公钥文件(.pub后缀),把整段内容复制下来,登录服务器后用编辑器把内容粘贴到~/.ssh/authorized_keys文件末尾。特别要注意的是,Windows记事本保存文本时默认带CRLF换行符,直接粘贴到Linux的vim里一般没问题,但如果你是在Windows上编辑好再上传,就有可能出现换行符导致认证失败的问题。后面排查部分我会给出解决方案。
3.3 服务器端sshd_config关键参数
公钥配置好后,建议检查一下服务器的/etc/ssh/sshd_config文件,重点确认这几个参数:
code复制PubkeyAuthentication yes
PasswordAuthentication no
PermitRootLogin prohibit-password
AuthorizedKeysFile .ssh/authorized_keys
PubkeyAuthentication yes是开启公钥认证。PasswordAuthentication no是关闭密码认证,这个要慎用——必须在密钥连接确认无问题之后再关。PermitRootLogin prohibit-password允许root用户登录,但只允许密钥方式,禁止密码方式。如果你平时用root管理机器,这个配置很合适。
修改完配置必须重启sshd才生效:
bash复制sudo systemctl restart sshd
这里分享一个我自己保持多年的习惯:在修改sshd_config之前,先开一个新的终端连接服务器并保持在登录状态,然后再去改配置、重启服务。如果新的连接验证密钥正常,再关闭旧连接。如果验证失败,旧连接还能帮你把配置回滚。否则你很可能把自己锁在门外,只能去云平台的控制台VNC登录救援,非常折腾。
4. 高频场景下的SSH密钥应用
4.1 VSCode远程开发:最常用的密钥登录场景
热词里"vscode连接ssh远程服务器"出现频率很高,现在很多前后端和运维人员都在用VSCode远程开发。VSCode的Remote-SSH插件本质是调用本地SSH客户端连接远程服务器,然后在服务器端运行一个VS Code Server,本地只负责显示界面。
配置密钥认证后,连接体验会非常顺畅。关键操作是编辑客户机的~/.ssh/config文件,添加如下内容:
code复制Host my-server
HostName 192.168.1.100
User root
IdentityFile ~/.ssh/id_ed25519
Port 22
IdentityFile指定了本次连接使用的私钥路径。这一步很关键,因为VSCode Remote-SSH默认使用~/.ssh/id_rsa,如果你生成的是Ed25519密钥,不指定就找不到。
配置完成后,VSCode的Remote-SSH扩展列表里会出现my-server这一项,点击就能连接,完全不需要输入密码。配合密钥登录,整个远程开发体验和本地几乎无异,非常流畅。
如果连接时报"Permission denied",先确认IdentityFile路径是否正确,再确认公钥是否已经加到服务器上,用ssh -v my-server看详细日志是最快的排查方法。
4.2 Git仓库SSH配置
另一个高频场景是Git。很多人clone仓库时选择SSH协议,结果被Permission denied整到怀疑人生,通常就是密钥没配置好。
Git的SSH认证和远程服务器登录原理一样,区别只是目标换成了GitHub、GitLab或Gitee等代码托管平台,不再是一台真实的服务器。
配置步骤:
- 生成密钥(如果还没有):
ssh-keygen -t ed25519 -C "你的邮箱" - 把公钥添加到Git平台的SSH Keys设置中。
- 测试连接:
ssh -T git@github.com,如果显示成功认证的用户名,就说明配置完毕。
如果你同时使用多个Git平台,比如GitHub和公司GitLab,建议在~/.ssh/config中分别指定不同host的密钥:
code复制Host github.com
HostName github.com
User git
IdentityFile ~/.ssh/id_ed25519_github
Host gitlab.company.com
HostName gitlab.company.com
User git
IdentityFile ~/.ssh/id_ed25519_gitlab
这种方式的优势是不同平台互不干扰,删掉某个平台的密钥也不会影响其他平台。
4.3 批量主机登录管理:config文件与脚本循环
管理多台服务器时,~/.ssh/config文件的价值会被放大到几乎不可或缺。它相当于你所有SSH连接配置的通讯录,不用再每次记IP、用户名和端口。
示例配置:
code复制Host prod-web-1
HostName 10.0.0.11
User deploy
IdentityFile ~/.ssh/id_ed25519
Port 22
ServerAliveInterval 60
Host prod-db-1
HostName 10.0.0.12
User dba
IdentityFile ~/.ssh/id_ed25519_dba
Port 22
配置后直接ssh prod-web-1就能连接,省去了记忆IP和用户名的时间。
热词里还有"ssh批量登录",这个可以通过Shell脚本配合密钥登录实现。举个例子,要给多台服务器执行同样的运维命令:
bash复制for host in prod-web-1 prod-web-2 prod-db-1; do
ssh "$host" "uptime && df -h"
done
前提是这些机器都配置好了密钥,整个过程不需要输密码。我在管理一批测试服务器时经常这么干,效果很稳定。如果你需要更复杂的批量操作,可以再配合Ansible这类自动化工具,但本质上它们底层的认证逻辑还是密钥认证。
5. 常见问题与排查技巧实录
5.1 权限问题:九成故障的来源
SSH对文件权限的检查极为严格,官方文档里明确说了权限不对就拒绝认证。我看到的案例中,八成以上"密钥配好却登不上"都是权限问题。
服务器端需要检查:
- 家目录权限不能是777,推荐755或700。
~/.ssh目录权限必须是700。~/.ssh/authorized_keys文件权限必须是600。- 文件所有者必须是对应的登录用户,不能是root。root所有会导致其他用户认证失败。
客户机端需要检查:
~/.ssh目录权限建议是700。- 私钥文件权限必须是600,不要大于644,否则SSH会报"Permissions too open"。
在Linux上快速修复:
bash复制chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
另外一个容易被忽视的是回车换行符问题。Windows上复制公钥内容时,文本可能带有Windows风格的CRLF换行符(\r\n),在Linux下会被识别为公钥的一部分,从而认证失败。排查时如果发现公钥内容看起来正常但就是登不上,可以在服务器上重写一下authorized_keys:
bash复制sed -i 's/\r$//' ~/.ssh/authorized_keys
这种细节问题曾经让我折腾过整整一个下午,写出来希望你能少走弯路。
5.2 连接失败:用-v参数抓出真凶
连接失败时,最有效的排查手段是开启SSH的详细日志输出:
bash复制ssh -v user@server_ip
加上-v后,SSH会打印出整个连接过程,包括尝试使用哪个密钥文件、服务器如何回应、最终在哪个环节失败。-vv和-vvv会更详细,适合深度调试。
我把常见问题整理成一张速查表:
| 现象 | 可能原因 | 首选排查方法 |
|---|---|---|
| Connection refused | sshd服务未启动 | systemctl status sshd |
| Connection timed out | 防火墙/安全组未放行端口 | 检查云平台安全组规则 |
| Permission denied (publickey) | 公钥未正确添加或权限不对 | ssh -v查看详细日志 |
| 能连但一直要密码 | 私钥未被正确加载 | 检查config中IdentityFile |
| Bad owner or permissions | 文件权限不正确 | 执行chmod修复 |
| 认证成功但马上断开 | 服务器端shell配置异常 | 检查用户的默认shell |
排查过程中最忌瞎猜。先看现象,再对表,最后用ssh -v确认,绝大部分问题都能快速定位。
5.3 密钥维护与安全加固建议
密钥配好不代表一劳永逸,日常维护有几个重点。
第一,私钥泄露处理。一旦发现私钥可能泄露,立刻在服务器上从authorized_keys中删除对应公钥,重新生成密钥对并分发。不要心存侥幸,私钥一旦泄露就相当于家门钥匙丢了,必须换锁。
第二,定期审计服务器上的authorized_keys。我每隔一段时间会检查所有服务器上各用户的authorized_keys,看有没有多余的公钥。特别是那些离职人员曾经用过的账号,确认公钥已被移除。
第三,端口安全加固。建议把SSH端口从默认的22改到高位端口,比如22022。虽然这不能完全杜绝扫描,但可以显著减少自动化攻击的噪音。修改端口后,记得同步修改防火墙规则、云安全组以及客户端config里的Port参数,否则你会连不上。
第四,关闭密码认证。确认密钥连接稳定后,把PasswordAuthentication设为no,能从根上杜绝暴力破解。这一步我在前面说过,一定要先确认密钥登录没问题再操作,避免把自己锁在外面。
最后再分享一个我在实际使用中的体会:密钥登录真正解决的问题不是"少输一次密码",而是让你对服务器的访问可控、可审计、可快速回收。团队里每个人都用自己的密钥,每个人都有自己的身份标识;人员变动时,增删公钥就是几秒钟的事情。配合ssh config文件,管理几十台服务器也游刃有余。如果你现在还在用密码登录,真心建议抽个时间把密钥认证配好——这件事的投入产出比,远超你想象。
