SSH密钥认证实战:从原理到配置,彻底禁用密码登录提升服务器安全

1. 项目概述

最近在帮几个朋友处理服务器安全问题时,发现一个普遍现象:很多人还在用简单的用户名密码登录SSH。这就像你家大门用的还是那种老式挂锁,稍微懂点技术的小偷拿根铁丝就能捅开。暴力破解、密码泄露、中间人攻击,这些风险每天都在发生。我自己管理的几十台服务器,从几年前开始就全面切换到了SSH密钥认证,并且彻底禁用了密码登录。今天我就把这个从“密码锁”升级到“指纹锁+虹膜识别”的完整实战过程拆开揉碎了讲给你听,无论你是刚接触Linux的新手,还是想优化现有工作流的老手,这篇内容都能让你彻底搞懂并安全落地。

简单来说,SSH密钥认证就是用一对数学上关联的“钥匙”来替代传统的密码。你本地电脑上保管一把绝对私密的“私钥”,服务器上放一把可以公开的“公钥”。登录时,服务器用你的公钥出一道数学题,只有持有对应私钥的你能解出来,从而证明“你就是你”。整个过程密码压根不在网络上传输,安全性是质的飞跃。更棒的是,配置好后登录体验丝滑,再也不用记和输入复杂的密码了。接下来,我会带你从原理到实操,从生成密钥到彻底关闭密码大门,一步步走完整个流程,并分享我踩过的坑和总结的最佳实践。

2. 密钥认证的核心原理与优势解析

2.1 非对称加密:一切安全的基础

要理解SSH密钥认证,必须先搞懂非对称加密。你可以把它想象成一把特殊的“锁和钥匙套装”。这套装里有两把钥匙:一把叫“公钥”,可以复制无数份,发给任何人,它的作用就像一把打开的锁,谁都能用它来“锁上”一个盒子;另一把叫“私钥”,全世界只有你一个人有,它的作用就是唯一能“打开”那把锁的钥匙。

具体到SSH登录过程:

  1. 挑战生成 :当你的客户端(比如你的笔记本电脑)尝试连接服务器时,服务器会随机生成一段毫无规律的“挑战字符串”。
  2. 签名应答 :你的客户端用本地保管的私钥,对这段挑战字符串进行加密运算,生成一个“数字签名”。这个签名就像是你的私钥在这段特定挑战上盖的一个独一无二的印章。
  3. 验证签名 :服务器收到签名后,会用事先存储在你家目录下的那个公钥,去尝试“解开”这个签名。如果能成功解开,并且解出来的内容正好是它当初发出的那个挑战字符串,服务器就确信:“哦,来的人确实拥有对应的私钥,是合法用户。”
  4. 建立连接 :验证通过,安全通道建立。

整个过程,你的私钥从未离开过你的电脑,网络上传输的只有挑战和签名,即使被截获,攻击者也无法反向推导出你的私钥。这和密码登录有本质区别,密码登录时,你的密码(或它的哈希)是要通过网络传给服务器验证的,存在被嗅探或中间人攻击的风险。

2.2 为什么必须禁用密码登录?

仅仅配置了密钥登录,而不禁用密码登录,就像你给家里装了高级指纹锁,却把旧钥匙还插在门上。攻击者依然可以尝试用“撞库”(用常见密码字典)或“暴力破解”(穷举密码)的方式来攻击你那道脆弱的“密码门”。

禁用密码登录( PasswordAuthentication no )意味着彻底封死了这条攻击路径。服务器将只接受公钥认证这一种方式。这带来了几个核心优势:

  • 根绝暴力破解 :攻击者无法再通过尝试成千上万个密码来入侵。
  • 免疫密码泄露 :你再也不用担心因为其他网站被“拖库”而导致服务器密码泄露(很多人习惯用同一个密码)。
  • 便于自动化 :很多自动化工具(如Ansible、CI/CD流水线)需要无密码登录,使用密钥是最安全、最标准的方式。
  • 符合安全合规 :几乎所有安全审计和最佳实践(如CIS基准)都强烈建议禁用SSH密码认证。

注意 :这是一个“破釜沉舟”的操作。一旦禁用,如果你丢失了私钥或配置错误,你将无法再通过密码登录。因此, 在确认密钥登录100%工作正常之前,绝对不要执行这一步 。我个人的习惯是,在修改服务器配置前,一定会开两个终端窗口,一个保持着一个有效的SSH连接作为“逃生通道”,另一个用来测试新配置。

3. 完整配置流程与实操详解

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

生成密钥是第一步,但选对算法和参数很重要。过去大家常用RSA,但现在更推荐使用 Ed25519 算法,它更安全、更快,并且生成的密钥更短。

打开你的本地终端(Linux/macOS的终端,或Windows上的WSL/PowerShell),执行以下命令:

ssh-keygen -t ed25519 -C “your_comment@example.com” -f ~/.ssh/id_ed25519_myserver

让我拆解一下这条命令:

  • -t ed25519 :指定密钥类型为Ed25519。如果你有特殊兼容性要求(比如一些老设备),可以用 -t rsa -b 4096 来生成4096位的RSA密钥。
  • -C “注释” :这是给密钥加个标签,方便你以后识别这个密钥是用于哪台服务器或哪个用途的。通常写邮箱或用途描述。
  • -f ~/.ssh/id_ed25519_myserver :指定密钥文件的存放路径和名称。我习惯用 id_算法_服务器名 的格式,一目了然。文件会生成在 ~/.ssh/ 目录下。

执行命令后,你会被问到两个问题:

  1. Enter passphrase (empty for no passphrase): 是否给私钥设置一个“密码短语”。 我强烈建议设置一个 。这相当于给你的私钥又加了一道密码锁。即使私钥文件不慎泄露,没有这个短语也无法使用。虽然每次使用密钥时需要输入(可通过ssh-agent代理缓存来避免每次输入),但安全性提升巨大。直接回车则表示不设置。
  2. Enter same passphrase again: 确认上一步输入的密码短语。

生成成功后,你会看到类似输出,并生成两个文件:

  • ~/.ssh/id_ed25519_myserver :这是 私钥 ,权限会自动设为 600 (仅所有者可读写)。 这个文件必须像保护银行卡密码一样保护,绝不能泄露或发送给任何人。
  • ~/.ssh/id_ed25519_myserver.pub :这是 公钥 ,内容是一长串以算法名开头的文本。这个文件可以任意分发,它的作用就是被放到服务器上。

3.2 第二步:将公钥部署到目标服务器

现在需要把公钥“安装”到服务器上。最优雅的方式是使用 ssh-copy-id 命令,它帮你处理了目录创建、文件追加和权限设置等一系列琐事。

ssh-copy-id -i ~/.ssh/id_ed25519_myserver.pub username@your_server_ip

执行这条命令,它会提示你输入一次服务器用户的密码(这是你最后一次使用密码登录!)。输入正确后,它会自动将你的公钥内容追加到服务器上对应用户家目录下的 ~/.ssh/authorized_keys 文件中。

如果系统没有 ssh-copy-id 命令(比如一些精简的Docker镜像或老系统),可以手动操作,这也是理解背后原理的好机会:

# 1. 首先,在本地查看并复制你的公钥内容(整行)
cat ~/.ssh/id_ed25519_myserver.pub

# 2. SSH登录到服务器(使用密码)
ssh username@your_server_ip

# 3. 确保.ssh目录存在且权限正确(非常重要!)
mkdir -p ~/.ssh
chmod 700 ~/.ssh

# 4. 将你刚才复制的公钥内容,粘贴并追加到authorized_keys文件末尾
# 注意:下面这条命令中的引号是英文的,且公钥内容要替换成你自己的
echo “ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJx… your_comment@example.com” >> ~/.ssh/authorized_keys

# 5. 设置authorized_keys文件的权限(同样非常重要!)
chmod 600 ~/.ssh/authorized_keys

# 6. 退出当前会话
exit

这里有个巨坑,我踩过无数次 :SSH服务对权限极其敏感。如果 .ssh 目录权限不是 700 ,或者 authorized_keys 文件权限不是 600 ,即使公钥内容正确,SSH也会出于安全考虑拒绝密钥登录。错误信息可能很模糊,只说“Permission denied (publickey)”。所以手动操作时,第3步和第5步的权限设置一定不能省。

3.3 第三步:彻底测试密钥登录

在关闭密码大门前,必须反复测试确认密钥登录畅通无阻。这是你的“消防演习”。

测试1:基础连接测试 在新开的终端窗口,使用 -i 参数指定你的私钥进行连接:

ssh -i ~/.ssh/id_ed25519_myserver username@your_server_ip

如果配置正确,你应该能直接登录,或者提示你输入私钥的密码短语(如果你设置了的话)。输入正确后即可进入。

测试2:详细日志测试(用于排查问题) 如果上一步失败了,别慌。加上 -v (verbose)参数,SSH会输出详细的连接过程,就像给你一份“诊断报告”。

ssh -v -i ~/.ssh/id_ed25519_myserver username@your_server_ip

仔细看输出,它会告诉你:有没有找到私钥、尝试了哪种认证方式、服务器是否提供了公钥认证、你的公钥是否被服务器接受等等。通常错误信息就在最后几行。

测试3:服务器端日志监控 如果本地日志还不够清晰,可以登录服务器(用你之前还保持着的那个连接),实时查看认证日志。在Ubuntu/Debian上通常是 /var/log/auth.log ,在CentOS/RHEL上是 /var/log/secure

# 在服务器上执行
sudo tail -f /var/log/auth.log

然后,在另一个窗口再次尝试失败的SSH连接。你会在服务器日志中看到实时的、更具体的错误信息,比如“Authentication refused: bad ownership or modes for directory /home/username/.ssh”。

测试4:模拟自动化场景测试 很多情况下,密钥是给脚本用的。你可以用 ssh -T 来测试连接而不执行远程命令,或者测试一个简单的命令:

ssh -i ~/.ssh/id_ed25519_myserver username@your_server_ip “whoami”

这条命令会连接服务器,执行 whoami 命令(输出当前用户名),然后立刻断开。如果返回了你的用户名,说明密钥认证在非交互式场景下也能完美工作。

只有当你用多种方式测试,都确认密钥登录万无一失后,才能进行下一步。 我通常会测试2-3次,并且从不同的网络环境(比如用手机热点)也测试一次,确保不是本地网络或缓存的巧合。

3.4 第四步:配置SSH客户端简化登录

每次登录都要输入一长串 -i 和IP地址太麻烦了。我们可以通过配置本地的 ~/.ssh/config 文件来创建别名。

编辑或创建这个文件:

nano ~/.ssh/config

添加如下配置段:

Host myserver # 你给服务器起的别名,以后就用这个
    HostName your_server_ip # 服务器的真实IP或域名
    User username # 登录用户名
    IdentityFile ~/.ssh/id_ed25519_myserver # 指定私钥路径
    Port 22 # SSH端口,如果是默认的22可以省略
    IdentitiesOnly yes # 只使用指定的密钥,避免SSH尝试其他密钥

保存退出后,设置一下这个配置文件的权限(SSH对权限有要求):

chmod 600 ~/.ssh/config

现在,登录就简化成了:

ssh myserver

这个文件功能非常强大,你还可以为不同的服务器设置不同的配置,管理多台服务器时尤其方便。

3.5 第五步:修改服务器配置,禁用密码登录

这是最关键也最需要谨慎的一步。请再次确认:你至少有一个活跃的SSH连接到服务器(作为备份),并且已经用新配置的密钥成功登录过。

  1. 登录服务器 ,使用你保持的那个连接,或者用刚刚测试成功的密钥登录。

  2. 备份原始配置文件 (一个好习惯):

    sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.backup.$(date +%Y%m%d)
    
  3. 编辑SSH服务端配置文件

    sudo nano /etc/ssh/sshd_config
    
  4. 找到并修改关键参数 。你需要找到以下行(可能被注释 # ):

    • #PasswordAuthentication yes PasswordAuthentication yes 将其改为:
    PasswordAuthentication no
    

    同时,确保公钥认证是开启的(默认通常是开启的):

    PubkeyAuthentication yes
    
  5. (可选但强烈推荐)进行其他安全加固 。趁这次修改,可以一并设置,让你的服务器更坚固:

    # 禁止root用户直接SSH登录(先用普通用户登录,再su或sudo)
    PermitRootLogin no
    
    # 禁止使用空密码登录
    PermitEmptyPasswords no
    
    # 禁用不安全的键盘交互式认证(它通常也是密码认证)
    KbdInteractiveAuthentication no
    
    # 限制登录尝试次数
    MaxAuthTries 3
    
    # 设置登录宽限期(秒),超过时间未成功认证则断开
    LoginGraceTime 30
    
    # 只允许特定用户登录(按需设置)
    # AllowUsers username1 username2
    
    # 使用非默认端口(比如2222),可以有效减少自动化扫描攻击
    # Port 2222
    

    注意 :修改 Port 后,防火墙规则和客户端配置( ~/.ssh/config )中的端口号也要相应修改。初次操作可以先不改,熟悉后再调整。

  6. 在重启服务前,先检查配置文件语法 ,避免因配置错误导致SSH服务无法启动,那样你就真的被关在外面了。

    sudo sshd -t
    

    如果没有任何输出,表示语法正确。如果有错误,它会明确指出哪一行有问题。

  7. 重启SSH服务 ,使配置生效:

    sudo systemctl restart sshd
    # 或者使用旧式的service命令
    # sudo service ssh restart
    
  8. 立刻进行最终验证 不要关闭当前的SSH会话! 打开一个新的终端窗口,进行两项测试:

    • 测试密钥登录(必须成功)
      ssh myserver # 或用完整命令
      
    • 测试密码登录(必须失败)
      ssh username@your_server_ip
      
      这时,系统应该会直接返回 Permission denied (publickey). ,而不会给你输入密码的机会。看到这个提示,恭喜你,密码登录的大门已经成功关闭!

4. 多设备管理与密钥轮换策略

4.1 如何从多台电脑登录同一台服务器?

你很可能需要在办公室电脑、家里笔记本甚至手机终端上登录同一台服务器。有两种策略:

策略一:一机一钥(强烈推荐) 这是最安全的方式。在每台你需要登录的设备上,重复本文的 3.1 步骤,生成独立的密钥对。然后,将每台设备生成的 公钥 ,都追加到服务器的同一个 ~/.ssh/authorized_keys 文件里。这个文件可以包含无数个公钥,每行一个。

操作起来很简单,在每台新设备上生成密钥后,用 ssh-copy-id 命令上传公钥即可。服务器端的 authorized_keys 文件看起来会像这样:

ssh-ed25519 AAAAC3... alice@office-pc
ssh-ed25519 BBBBD3... alice@home-laptop
ssh-ed25519 CCCCE3... alice@phone-termux

好处 :哪台设备丢了或不用了,只需从 authorized_keys 文件中删除对应那一行,该设备立即失效,不影响其他设备。安全粒度细,便于审计。

策略二:共享私钥(不推荐但方便) 将同一把私钥文件复制到所有设备上使用。虽然只需配置一次,但风险极高:任何一台设备被入侵,都意味着所有服务器的安全防线失守。除非你对所有设备的安全性和物理控制有绝对信心,否则应避免这样做。

4.2 私钥的日常管理与安全实践

私钥是你的数字身份,必须妥善管理:

  • 权限永远是600 chmod 600 ~/.ssh/id_* 。系统通常会自动设置,但移动或复制后要检查。
  • 绝不网络传输 :不要通过邮件、微信、网盘等方式发送私钥文件。如果需要跨设备,使用加密U盘物理传递,或使用上述“一机一钥”策略。
  • 使用ssh-agent管理密码短语 :如果你为私钥设置了密码短语,每次使用都要输入会很烦。 ssh-agent 是一个密钥管理器,可以帮你在一段时间内记住解密后的私钥。
    • 启动并添加私钥: eval “$(ssh-agent -s)” 然后 ssh-add ~/.ssh/id_ed25519_myserver ,输入一次密码短语。
    • 之后本次终端会话中再使用SSH就不需要输入密码短语了。退出终端或重启后失效。
  • 备份 :将你的私钥(尤其是没有密码短语的)备份到加密的离线存储中,如加密的U盘或密码管理器。防止本地硬盘损坏导致无法登录。

4.3 密钥轮换:定期更换你的“数字门锁”

没有永远安全的密钥。出于最佳安全实践,应该像定期更换密码一样,定期轮换SSH密钥。建议每6-12个月进行一次。

轮换步骤:

  1. 生成新密钥对 :在本地用 ssh-keygen 生成一套新的。
  2. 部署新公钥 :用 ssh-copy-id 将新公钥上传到服务器。此时服务器的 authorized_keys 里会有新旧两个公钥。
  3. 测试新密钥 :使用新私钥登录服务器,确保工作正常。
  4. 移除旧公钥 :登录服务器,编辑 ~/.ssh/authorized_keys 文件,删除旧公钥对应的那一行。
  5. 更新所有客户端配置 :如果你在所有设备上都使用 ~/.ssh/config 文件,记得更新其中的 IdentityFile 路径指向新私钥。
  6. 安全删除旧私钥 :确认新密钥在所有场景下工作正常后,安全地擦除本地的旧私钥文件(例如使用 shred 命令)。

5. 高级加固与故障排查实录

5.1 结合防火墙与fail2ban构建纵深防御

禁用密码登录是首要防线,但结合其他工具可以构建更立体的防御体系。

使用UFW防火墙限制SSH源IP 如果你有固定的办公IP或家庭IP,可以限制只允许这些IP访问服务器的SSH端口。

# 安装UFW(如果未安装)
sudo apt update && sudo apt install ufw -y

# 设置默认策略(拒绝所有入站,允许所有出站)
sudo ufw default deny incoming
sudo ufw default allow outgoing

# 允许你的固定IP(例如1.2.3.4)访问22端口
sudo ufw allow from 1.2.3.4 to any port 22

# 如果你改了SSH端口(比如2222),则允许该端口
# sudo ufw allow from 1.2.3.4 to any port 2222

# 启用UFW
sudo ufw enable

警告 :在远程服务器上操作防火墙时,务必先添加允许自己IP的规则,再启用。否则可能立即把自己踢下线。

安装fail2ban防御扫描与试探 即使禁用了密码,服务器依然会收到大量的SSH连接尝试。fail2ban可以监控日志,如果一个IP在短时间内多次认证失败,就自动将其IP临时加入防火墙黑名单。

# 安装fail2ban
sudo apt update && sudo apt install fail2ban -y

# fail2ban安装后会自带一个针对sshd的jail配置,通常无需额外配置即可工作
sudo systemctl enable fail2ban --now

# 查看状态
sudo fail2ban-client status
sudo fail2ban-client status sshd # 查看sshd监狱的具体状态

5.2 常见故障排查与解决方案

即使按照步骤操作,也可能遇到问题。下面是我总结的常见问题速查表:

问题现象 可能原因 排查命令与解决方案
Permission denied (publickey). 1. 服务器 authorized_keys 文件权限错误
2. 服务器 .ssh 目录权限错误
3. 公钥未正确添加或格式错误
4. sshd_config PubkeyAuthentication 被设为 no
1. 在服务器检查: ls -la ~/.ssh/
2. 修正权限: chmod 700 ~/.ssh; chmod 600 ~/.ssh/authorized_keys
3. 检查公钥内容: cat ~/.ssh/authorized_keys ,确保是一整行且无多余字符
4. 检查配置: sudo grep PubkeyAuthentication /etc/ssh/sshd_config
连接超时或 Connection refused 1. SSH服务未运行
2. 防火墙阻止了连接
3. 修改了端口但客户端未指定
1. 检查服务状态: sudo systemctl status sshd
2. 检查防火墙: sudo ufw status (如果用了UFW)
3. 指定端口连接: ssh -p 2222 user@host
登录仍需输入密码(非密钥短语) 1. sshd_config PasswordAuthentication 仍为 yes
2. 配置文件未重载
1. 确认配置: sudo grep PasswordAuthentication /etc/ssh/sshd_config
2. 重载服务: sudo systemctl reload sshd (注意不是restart)
Agent admitted failure to sign using the key. ssh-agent未加载或未添加该私钥 1. 启动agent: eval “$(ssh-agent -s)”
2. 添加密钥: ssh-add ~/.ssh/你的私钥
修改配置后SSH服务无法启动 sshd_config 文件存在语法错误 1. 通过云平台控制台/VNC登录服务器
2. 检查语法: sudo sshd -t ,根据错误信息修正
3. 或用备份文件恢复: sudo cp /etc/ssh/sshd_config.backup* /etc/ssh/sshd_config

最坏情况:把自己锁在门外了怎么办? 如果你已经关闭了当前所有连接,并且新连接无法建立(密钥配置错误+密码已禁用),不要惊慌。几乎所有主流云服务商(阿里云、腾讯云、AWS、Google Cloud等)都提供了“VNC连接”或“串行控制台”功能。

  1. 登录云服务商的管理控制台。
  2. 找到你的虚拟机实例。
  3. 寻找“连接”、“VNC”、“控制台”或“串行端口”之类的选项。
  4. 通过这个方式登录到服务器的本地终端。
  5. 此时你可以直接编辑 /etc/ssh/sshd_config 文件,将 PasswordAuthentication 临时改回 yes
  6. 重启SSH服务: sudo systemctl restart sshd
  7. 现在你可以用密码登录了,然后仔细排查之前的密钥配置问题。

5.3 安全审计与持续监控

配置完成后,工作并未结束。定期检查是保持安全的关键。

  • 检查当前登录用户 who w 命令可以查看当前谁登录在系统上。
  • 查看授权密钥文件 :定期 cat ~/.ssh/authorized_keys ,确认里面没有陌生的公钥。
  • 查看认证日志 sudo tail -n 50 /var/log/auth.log | grep sshd ,关注失败的登录尝试(尽管密码已禁用,但扫描依然存在),看看是否有异常IP。
  • 检查fail2ban封禁列表 sudo fail2ban-client status sshd ,了解攻击情况。

我个人习惯在重要的服务器上配置日志监控告警,如果短时间内出现大量来自同一IP的SSH失败尝试,即使它无法成功,也会通过邮件或钉钉通知我,让我知晓服务器正在被重点关注。

走到这一步,你的服务器SSH入口已经从一个容易被撞开的“木门”,升级成了需要特定钥匙才能开启的“保险库大门”。整个过程的核心在于理解原理、谨慎操作、充分测试。记住那个黄金法则: 在切断退路(禁用密码)之前,永远确保新路(密钥登录)是畅通的。 这套方法不仅适用于个人服务器,也是企业级运维的基础安全要求。花一个小时完成这个配置,换来的是长期的安全与安心,这笔时间投资绝对划算。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值