news 2026/10/15 5:05:29

Ubuntu服务器SSH安全加固:密钥登录与fail2ban防爆破实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Ubuntu服务器SSH安全加固:密钥登录与fail2ban防爆破实战

接手一台新服务器,第一件事绝对不止是装环境、跑服务。如果 SSH 还开着密码登录,那这台机器在公网上就跟没锁门差不多,扫描脚本和爆破工具无时无刻不在盯着 22 端口。我见过太多人装好 Ubuntu 之后直接 root+密码裸奔,过几天来看,/var/log/auth.log 里全是登录失败的记录,轻则被塞进肉鸡挖矿,重则直接被挂马。这篇东西我不打算写成文档搬运,就按我实际加固一台 Ubuntu 服务器的操作顺序来讲,把密钥登录、禁用密码、防爆破这套组合拳拆开揉碎,每一步都告诉你为什么这么做,以及我踩过的坑。

1. 先搞清楚威胁从哪来,再动手加固

1.1 公网服务器面临的真实爆破压力

只要是公网 IP,22 端口几乎从装好系统那一刻起就被扫描器盯上了。这类扫描不针对人,纯粹是全网段遍历,脚本拿到 IP 段就跑常见用户名和弱密码字典,root、admin、ubuntu、test 这些账号是重灾区。哪怕密码设成大小写字母加数字,只要还在用密码登录,理论上都存在被字典撞破的可能,区别只是时间问题。

我之前在一台低配服务器上做过统计,系统刚装好、还没做任何防护的情况下,24 小时内的认证失败次数能到几千次,来源 IP 分散在世界各地。这些扫描用的都是自动化工具,攻击成本低到可以忽略,但一旦撞上一个弱密码,就能直接拿到机器权限。更麻烦的是,很多扫描器在拿到密码之后不会马上操作,而是先留着,等到夜深人静的时候再悄悄登录,把挖矿程序或者代理工具拖进去。等你自己发现机器卡了、流量不对了,通常已经跑了好几天了。

所以加固的核心思路就是一条:让密码登录这条路彻底走不通。不是说把密码设得足够复杂就安全了,而是要从机制上不依赖密码。密钥登录用的是非对称加密,私钥只在你自己手里,远程爆破完全没有用武之地。只要私钥不泄露、权限设置正确,公网上的扫描器拿你一点办法都没有。

1.2 加固方案的总体目标

这次加固要达到的效果有四个层级。首先是关闭密码登录,这是最根本的一步,线上服务器的 SSH 全部切到密钥认证。其次是把 root 直接登录的入口堵死,日常操作一律用普通用户加 sudo,这样即使账号被入侵,也没法一步登天拿到 root shell。再次是给 SSH 服务加上自动防御,比如限制登录尝试次数、封禁频繁失败的来源 IP,也就是 fail2ban 这类工具的活。最后是隐藏和收敛攻击面,比如修改默认端口、禁止空密码、限制可登录的用户白名单,这些措施单独看不那么起眼,组合起来就能让攻击者连碰撞的机会都少很多。

这个方案不是单纯靠某一个工具,而是把几层防御叠在一起。即便某一层被绕过,后面还有别的机制兜底。密钥登录挡住爆破,fail2ban 挡住穷举尝试,非默认端口挡住无差别扫描,这一套下来,机器在公网上的暴露面就非常小了。我自己的服务器做了这套之后,auth.log 里的失败记录基本归零,效果非常明显。

2. 密钥登录的完整配置流程

2.1 本地生成密钥对:选对算法和参数

密钥登录的第一步是在本地电脑上生成一对密钥,公钥放到服务器上,私钥留在本地。生成工具直接用 OpenSSH 自带的 ssh-keygen,Windows、macOS、Linux 都有,不用额外装东西。

算法选择上有讲究。传统做法是 RSA,但位数要求现在越来越高了,2048 位勉强能用,4096 位才比较稳妥。RSA 的问题在于密钥长度长,生成和验证的耗时也相对高一些。我更推荐用 ed25519,这是目前兼顾安全和性能的选择。它基于 EdDSA 算法,密钥长度固定,安全性高,验签速度还快,OpenSSH 6.5 以上的版本都原生支持。要是你还得兼容非常老的客户端或者遗留设备,再考虑 RSA 4096,但新项目直接用 ed25519 就好。

生成命令如下:

ssh-keygen -t ed25519 -C "your_email_or_comment" -f ~/.ssh/prod_server

-C 参数是注释,方便你区分这把钥匙是干嘛用的,填什么无所谓,但建议别留空。-f 指定生成的文件名,我这里写的是 prod_server,你也可以按实际用途命名,比如 aliyun_web、tencent_db 这样。执行过程中会提示设置 passphrase(口令),这一步我建议不要跳过,给私钥加一层口令保护。

2.2 私钥 passphrase 到底要不要设

很多人嫌麻烦,passphrase 直接留空。这样确实用起来方便,但私钥文件一旦泄露,比如电脑中马、备份文件被拖走,对方拿到私钥就能直接登录服务器。设了 passphrase 之后,就算私钥泄露,对方还得再破一道口令,等于多了一层保险。

有人担心 passphrase 影响自动化脚本,这个也好解决。系统自带 ssh-agent 可以把私钥加载进内存,第一次用的时候输入一次 passphrase,之后一段时间内都不用再输。macOS 的钥匙串和 Windows 的 OpenSSH 服务都能管理这个,体验已经很顺了。

实测下来的建议是:个人电脑上使用的私钥,passphrase 一定设;服务器之间互相调度的密钥,可以用专用的、权限严格限制的密钥文件,不带 passphrase,但绝对不能复用个人密钥。这是两条线,别混。

2.3 将公钥部署到服务器

密钥生成之后,~/.ssh 目录下会有两个文件,prod_server 是私钥,prod_server.pub 是公钥。把公钥内容追加到服务器的 authorized_keys 文件里就可以完成部署。

最简单的方式是 ssh-copy-id,它在多数 Linux 发行版里都预装了:

ssh-copy-id -i ~/.ssh/prod_server.pub user@your_server_ip

这条命令会让你输入一次服务器密码,之后它会把公钥自动追加到服务器的 ~/.ssh/authorized_keys 并设置好权限。如果服务器不是默认 22 端口,需要加 -p 参数:

ssh-copy-id -i ~/.ssh/prod_server.pub -p 2222 user@your_server_ip

没有 ssh-copy-id 的时候,手工操作也不难。先登录服务器,把公钥内容粘贴到 ~/.ssh/authorized_keys,然后确认权限:

chmod 700 ~/.ssh chmod 600 ~/.ssh/authorized_keys

权限这个细节特别关键。OpenSSH 对 authorized_keys 的权限非常敏感,如果文件对其他用户可写,sshd 会直接忽略这个文件,拒绝用里面的公钥认证。我见过有人折腾半天登不上,最后发现是 authorized_keys 权限是 644,改成 600 立刻就好。

2.4 客户端连接配置与免密验证

公钥部署好之后,先别急着关密码登录,第一步是验证密钥登录能不能正常工作:

ssh -i ~/.ssh/prod_server user@your_server_ip

如果设置了 passphrase,这里会提示输入。能登进去说明公钥认证已经通了。为了方便以后使用,可以在本地 ~/.ssh/config 里写一段配置:

Host prod HostName your_server_ip User your_username Port 2222 IdentityFile ~/.ssh/prod_server

保存之后,直接执行 ssh prod 就能连上,不需要每次指定 IP 和密钥文件。这是一个让日常操作变舒服的小习惯,也避免了记错 IP、用错密钥的低级问题。

3. 关闭密码登录前的关键准备工作

3.1 务必先确认 sudo 权限和逃生通道

直接修改 sshd_config 把 PasswordAuthentication 设为 no,然后重启 sshd,这个操作看起来简单,但有一个巨大的风险:如果密钥登录还没配好,或者公钥权限有问题,重启之后你就再也登不上机器了,只能去控制台走 VNC 或者网页终端救急。

所以我会在修改配置之前做三件事:第一,确认当前用户能正常使用密钥登录;第二,确认当前用户有 sudo 权限,别改完配置发现自己连提权都做不到;第三,开一个新的终端窗口,保持一个已登录的会话不要断开。

保持一个在线会话是很多老手都会做的保险。因为 sshd 重启不会把已经建立的连接踢掉,你可以在新会话里测试配置是否正确,万一登录失败,旧的会话还在,可以马上回滚配置。说到底,做服务器安全加固最重要的原则不是一次到位,而是随时留退路。

3.2 修改 sshd_config 的核心参数

SSH 服务端配置在 /etc/ssh/sshd_config,不同发行版路径基本一致。修改之前先备份一份:

sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak.$(date +%F)

这一步习惯很重要,改配置前备份,出问题可以秒回滚,不用凭记忆恢复。

接下来是本次加固的核心参数调整:

# 禁用密码登录 PasswordAuthentication no # 禁用空密码 PermitEmptyPasswords no # 禁止 root 直接登录 PermitRootLogin prohibit-password # 指定允许登录的用户白名单,多个用户用空格分隔 AllowUsers your_username # 登录超时时间(秒) LoginGraceTime 30 # 最大认证尝试次数 MaxAuthTries 3 # 客户端保活 ClientAliveInterval 300 ClientAliveCountMax 2

逐个解释一下。PasswordAuthentication no 是核心开关,关掉之后密码登录直接从机制上被禁用,爆破工具不管怎么撞都不可能成功。PermitRootLogin 不要直接设成 no,最好用 prohibit-password,意思是 root 不能通过密码登录,但允许使用密钥登录。这样系统管理员在紧急情况下仍然可以用 root 的密钥登录,而不是彻底锁死 root 通道。AllowUsers 这行尤其重要,它限制了能通过 SSH 登录的用户名,从根上跳过不存在的用户。

MaxAuthTries 3 限制的是单次连接里最多允许的认证尝试次数,超过直接断开。这个值设得太小会影响一些自动化工具,比如某些客户端会在同一连接里尝试多个密钥,所以 3 是一个比较平衡的取值。LoginGraceTime 30 限定了登录过程中必须完成的时限,超过 30 秒没认证成功就断开,可以防慢速爆破。

3.3 验证配置与平滑重启

改完配置之后,先检查语法,OpenSSH 提供了自带的检查命令:

sudo sshd -t

没有任何输出就代表语法正确。如果输出 config line XX: Bad yes/no argument 之类的信息,说明哪一行写错了,按提示去改就行。

确认无误后重启服务:

sudo systemctl restart sshd

Ubuntu 上服务名是 ssh,部分系统是 sshd,直接用 ssh 就行。重启之后再开一个新终端测试密钥登录是否正常。这里我强烈建议你在确认新会话能正常登录之前,不要关闭之前那个旧会话。等新终端能登进去了,再把这个旧会话断掉,整个切换过程才算真正完成。

4. 部署防爆破工具与攻击面收敛

4.1 fail2ban 的安装与基础配置

密钥登录关闭之后,密码爆破这条路已经堵死了。但 SSH 服务本身还有别的风险点,比如不断尝试不存在的用户名、反复握手、扫描服务版本等。这类行为需要另一个工具来管,就是 fail2ban。

fail2ban 的原理很简单:监控日志文件里的认证失败记录,在单位时间内失败次数超过阈值,就用防火墙规则把来源 IP 封掉一段时间。Ubuntu 上的安装就一条命令:

sudo apt update && sudo apt install fail2ban -y

安装之后不要直接改默认的 /etc/fail2ban/jail.conf,那个文件在升级时会被覆盖,正确做法是在 /etc/fail2ban/jail.local 里写自己的配置。这个文件默认不存在,需要新建:

sudo nano /etc/fail2ban/jail.local

写入如下内容:

[DEFAULT] ignoreip = 127.0.0.1/8 ::1 你的办公IP bantime = 3600 findtime = 600 maxretry = 3 backend = systemd [sshd] enabled = true port = ssh logpath = %(sshd_log)s

参数的含义分别是:ignoreip 是白名单,里面的 IP 永远不会被封,建议至少把你当前办公网络的出口 IP 加进去,免得自己把自己封了;bantime 是封禁时长,单位秒,3600 就是 1 小时;findtime 是统计窗口,600 秒;maxretry 是窗口内最大失败次数,3 次就触发封禁。这几个参数配置逻辑是:10 分钟内失败 3 次,就封 1 小时。

4.2 fail2ban 的实际效果与封禁处理

配置好之后重启服务:

sudo systemctl restart fail2ban

用下面的命令查看 SSH 狱的封禁状态:

sudo fail2ban-client status sshd

输出里可以看到 Currently banned 的 IP 列表和封禁总次数。有攻击的时候这个列表会持续增长,看着还挺解气的。

被误封的时候也不用慌,手动解封一条命令:

sudo fail2ban-client set sshd unbanip 1.2.3.4

这个命令在日常维护里很常用,特别是你自己 IP 忘记加白名单、被 ban 得连不上机器的时候。遇到这种情况,先通过云控制台或者另一台跳板机登录,然后执行上面的命令把 IP 放出来。

4.3 Windows 客户端关闭密码登录后的报错坑

切到密钥登录之后,Windows 用户可能会遇到一个很典型的问题:ssh 客户端提示

Permission denied (publickey)

服务器端日志显示

Failed publickey for user xxx from ...

明明密钥已经放进 authorized_keys 了,为什么还报这个错?最常见的原因是 Windows 下保存公钥文件的时候,编辑器自动加了一个换行,或者文件编码变成了 UTF-8 with BOM。OpenSSH 解析公钥的时候遇到 BOM 会直接认不出来。

另外一个坑是 Windows 命令行的换行符是 CRLF,而 Linux 下 authorized_keys 只要 CRLF 就可能出问题。解决方案是确认公钥文件里只有一行,并且保存的时候选 Unix 换行符。在 Windows 上如果用的是记事本,建议换成 VSCode 或者 PowerShell 里的 Set-Content,能更好地控制编码。

4.4 修改 SSH 默认端口是否正确

把 SSH 从 22 端口改到高位端口,比如 2222 或者 10222,确实能把一大批无差别扫描挡住。扫描器主要撞 22 端口,改成别的端口之后,大量自动化攻击连门都找不到,auth.log 里的噪音立刻少一大截。

但这个操作有两个代价要考虑。首先是自己要记住端口,每次连接都要加 -p 参数,虽然有 ssh config 可以缓解,但心智负担还是增加了。其次是某些安全设备或者机房策略会对非标准端口敏感,比如你要在服务器上做等保测评,端口变了还要额外写说明。

我的建议是:如果服务器是个人使用或者团队内部使用,端口一定要改;如果服务是面向客户的,22 端口不变,靠 fail2ban 和密钥登录来兜底。这不是对错问题,是风险偏好和运维成本的权衡。

4.5 补充策略:禁用无用的认证方式和使用 PAM 限制

现代 OpenSSH 支持的认证方式不止密码和公钥,有时候编译的时候还开了键盘交互认证,跟 PasswordAuthentication 是两条线。所以光关 PasswordAuthentication 还不够,稳妥起见把这两个也明确关掉:

KbdInteractiveAuthentication no ChallengeResponseAuthentication no

这两项在某些老版本配置里会以别的名字出现,保持一致即可。关闭之后,所有基于密码的认证路径都被堵住,只剩公钥这一条,攻击面缩到最小。

另外可以通过 PAM 限制 SSH 登录用户。比如在 /etc/ssh/sshd_config 里配了 AllowUsers 的前提下,也可以在 /etc/security/access.conf 里做更细粒度的来源 IP 控制。不过这些属于进阶玩法,对多数场景来说 AllowUsers 已经足够,我提一嘴,知道有这回事就行。

5. 加固后验证与日常检查清单

5.1 从攻击者视角扫描自己的服务器

配置全部完成之后,我习惯从外部做一次自检。最简单的方式是在另一台机器上用 nmap 扫一下:

nmap -sV -Pn your_server_ip -p 22

如果端口改过了,就扫对应的新端口。重点关注输出的 SSH 版本信息,如果 banner 暴露的是旧版本,说明 OpenSSH 需要升级,这是攻击者可以利用的指纹信息。

更完整的做法是模拟一次爆破,试试看密码登录是否真的被禁了。比如拿一个错误的密码去登录,立刻就会收到 Permission denied (publickey) 的提示,连密码输入的机会都没有,就说明关闭生效了。再用错误的用户名重复尝试几次,触发 fail2ban 阈值,然后用 fail2ban-client status sshd 看该 IP 是否被 ban。验证完记得把自己 IP 从封禁列表里解出来。

5.2 检查日志与常用运维命令

日常运维中,几个命令能帮我们快速掌握服务器 SSH 状态。查看最近认证失败的来源 IP:

sudo grep "Failed password" /var/log/auth.log | awk '{print $(NF-3)}' | sort | uniq -c | sort -nr | head -20

这条命令把 auth.log 里的失败记录按来源 IP 统计并排序,一眼就能看出哪些 IP 在持续轰炸。另一个有用的命令是查看当前所有登录会话:

sudo ss -tnp | grep :22

可以实时看到哪些 IP 正连着 SSH,配合 who 命令看登录用户,能发现异常登录。

5.3 密钥泄露后的紧急处置流程

万一私钥泄露了,比如电脑中马、代码仓库误提交了密钥文件,处理流程要果断。第一步当然是在服务器上把对应的公钥从 authorized_keys 里删掉,让旧私钥作废。第二步是本地重新生成一对新密钥,按前面的步骤重新部署。第三步是检查服务器上有没有被留下后门,重点看 authorized_keys 里是否多出了陌生的公钥、/root/.ssh 下有没有可疑文件、系统 crontab 里有没有不明任务。

这个流程听着繁琐,但真到泄露的时候,宁可多花时间全面排查,也不要只换个密钥就了事。攻击者拿到私钥之后可能已经做了不少操作,不留干净后面会更麻烦。

5.4 定时维护与升级策略

服务器安全不是做完一次加固就一劳永逸,操作系统和软件包要定期更新。Ubuntu 上可以启用自动安全更新:

sudo apt install unattended-upgrades sudo dpkg-reconfigure unattended-upgrades

它会自动安装安全补丁,减少因为已知漏洞被利用的风险。OpenSSH 本身也会定期发布更新,跟上节奏别停在老版本。另外一个建议是每隔一段时间检查一下服务器上的用户和密钥文件,看有没有多出不该存在的账号或者公钥,确认没有异常的登录行为。

6. 常见问题与排查经验速查

整理一下实际操作中容易踩的坑,方便大家直接对照排查。

6.1 密钥登录失败的排查顺序

密钥登录失败时,最常见的原因按出现概率排序如下:

现象可能原因排查方向
Permission denied (publickey)公钥没进 authorized_keys检查文件内容是否正确
同上authorized_keys 权限不对检查是否有其他用户可写权限
同上客户端使用错误私钥确认 ssh -i 指定的文件
连接被断开sshd_config 语法错误sshd -t 检查配置
登录成功但很快掉线ClientAlive 设置过短增大 ClientAliveInterval

具体排查的时候,先看服务器端日志 /var/log/auth.log,里面会有明确的失败原因。比如

Authentication refused: bad ownership or modes for directory /home/user/.ssh

说明 .ssh 目录权限不对。又比如

no matching key exchange method found

说明客户端和服务端加密算法不匹配,老客户端连接新服务器经常遇到,建议升级 OpenSSH 客户端。

6.2 杀毒/防火墙软件拦截 SSH 的问题

有些安全软件会拦截 SSH 连接。比如 Windows 自带的防火墙,某些企业版杀毒软件,可能把 SSH 的连接当成可疑行为拦掉。排查方法很简单:先临时关掉防火墙试试能不能连,能连就是防火墙的规则问题,把 22 端口(或自定义端口)加入放行列表即可。

Ubuntu 服务器本身如果开了 ufw,记得放行对应端口:

sudo ufw allow 2222/tcp sudo ufw enable

这个坑我遇到过,配置全对但连不上,最后发现是 ufw 默认策略拒绝所有入站,把端口放行之后立刻就好。

6.3 禁用密码登录后自动化脚本怎么办

如果你在服务器上跑了自动化脚本,之前用的是密码登录,禁用密码之后脚本立刻会挂。解决方案是给脚本单独配置部署密钥,把公钥加入服务器的 authorized_keys,同时用 ssh config 或者 ssh-agent 管理密钥加载。密钥权限一定要锁死,建议部署密钥只用在自己的 CI/CD 或备份场景,不要跟个人主密钥混用。

6.4 多用户场景下的权限隔离

如果在服务器上还有别的同事需要登录,不要共享同一把私钥。每个人都有独立的密钥对,公钥分别加入自己的 authorized_keys。这样某个人私钥泄露,只影响他自己的账号,回收权限也方便。AllowUsers 列表里把离职人员的账号移除就能生效,不用大动干戈改全局配置。

综合来看,这套 SSH 加固方案做完之后,服务器在公网上的安全水位会有一个明显的提升。我自己的体验是,以前每次巡检都要在一堆爆破日志里翻找异常,现在 auth.log 干净得几乎不用看。密钥登录虽然多了一步设置,但换来的是长期的安心。配置这东西,不怕麻烦,就怕出事之后再后悔。按照上面的步骤走一遍,确保每一步都验证通过再进入下一步,你的 Ubuntu 服务器就算是在公网上站稳了。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/15 5:05:03

Elastic-Job-Lite分片策略深度解析:原理、选型与避坑指南

1. 为什么作业分片策略值得单独拎出来讲做分布式定时任务的人,绕不开 Elastic-Job-Lite 这个框架。它最核心的价值就两个词:弹性调度和分片执行。弹性调度解决的是任务该在哪个节点跑的问题,而分片执行解决的是——一个任务怎么拆成多份、并行…

作者头像 李华
网站建设 2026/10/15 5:04:49

STM32+RS485实现DMX512发送:协议时序、代码实现与调试避坑

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/15 5:04:48

Miniconda + VS Code:从安装到环境管理的 Python 开发完整指南

说实话,这些年我给身边同事和朋友推荐过太多次 Python 环境方案,最后留下长期用的基本都是 Miniconda 加 VS Code。原因不复杂:官方 Python 装完以后想换个版本得手动折腾环境变量,Anaconda 又实在臃肿,装完一堆用不上…

作者头像 李华
网站建设 2026/10/15 5:04:47

基于用户行为的美食推荐系统:Python与混合推荐算法实践

1. 这个选题到底在做什么1.1 先从真实痛点入手做美食信息推荐系统,最开始是因为我自己点外卖和刷菜谱时的一个直观感受:平台上的美食信息实在太多了,翻来翻去反而不知道吃什么。对一个毕业生来说,如果能做一个基于用户行为数据的推…

作者头像 李华
网站建设 2026/10/15 5:04:44

C++代码规范化:clang-format与clang-tidy的工程实践

接手一个迭代过七八年的 C 服务端代码库是什么体验?文件数两千出头,参与过提交的少说四十个人,代码能跑,业务稳定,但每次 code review 都像考古。有人用两空格缩进,有人用四空格,还有人看心情&a…

作者头像 李华