服务器SSH安全加固:禁用Root、密钥认证与端口修改实战指南
1. 项目概述:为什么你的服务器需要一次SSH“体检”?
最近帮朋友处理了一台被暴力破解的服务器,登录日志里密密麻麻全是来自全球各地的失败尝试,目标直指root账户和默认的22端口。这让我意识到,很多朋友在拿到一台云服务器或VPS后,往往只做了基础的环境部署,却忽略了最外层的“大门”——SSH服务的安全加固。今天要聊的,就是一次标准的服务器SSH登录优化“三板斧”:禁用root直接登录、强制使用密钥认证、修改默认SSH端口。这不仅仅是安全最佳实践,更是运维工程师的“肌肉记忆”。
你可能觉得,我的服务器没什么重要数据,或者有云厂商的安全组挡着,没必要这么麻烦。但安全从来不是“亡羊补牢”,而是“未雨绸缪”。一个弱密码的root账户加上默认端口,就像把家门钥匙藏在脚垫下面,攻击者用自动化脚本扫一遍,你的服务器就可能沦为“肉鸡”。这次优化操作,不涉及复杂的网络架构或昂贵的硬件,只需要你有一台Linux服务器(CentOS、Ubuntu等主流发行版均可)和基本的命令行操作知识。整个过程大约30分钟,但带来的安全提升是指数级的。无论你是个人开发者、学生,还是初创公司的运维,这套组合拳都值得你花时间配置一次。
2. 核心思路与方案选型:理解每一招背后的安全逻辑
在动手之前,我们先拆解一下这三个措施各自解决了什么问题,以及为什么它们组合起来效果最佳。单独使用任何一项都有其局限性,但三者叠加,能构建一个坚固的纵深防御体系。
2.1 禁用Root直接登录:切断最高权限的“直通车”
Root是Linux系统的超级管理员,拥有至高无上的权限。允许直接以root身份通过SSH登录,意味着攻击者一旦破解了root密码,就能长驱直入,为所欲为。禁用直接登录,并不是抛弃root用户,而是强制所有管理员先以一个普通用户身份登录,然后再通过su或sudo命令切换或提权到root。这样做有两个核心好处:
- 增加攻击门槛:攻击者需要连续破解两个账户(普通用户和root)的密码,难度大大增加。普通用户的用户名还可以自定义,避免了被针对“root”这个固定用户名进行爆破。
- 留下审计痕迹:通过
sudo执行的命令会被详细记录在日志中(通常是/var/log/auth.log或/var/log/secure),谁在什么时间执行了什么命令一目了然,便于事后追溯和审计。如果直接root登录,很多操作日志的关联性会变弱。
注意:在禁用前,你必须确保已经创建好了一个具有
sudo权限的替代管理账户,并且测试过能用该账户正常登录和提权。否则,你可能会把自己锁在服务器外面。
2.2 密钥认证替代密码认证:从“你知道什么”到“你拥有什么”
密码认证(Password Authentication)依赖于“你知道什么”(Something you know),即密码。密码可能被猜解、被撞库、被键盘记录。而密钥认证(Public Key Authentication)依赖于“你拥有什么”(Something you have),即一对非对称加密的密钥(私钥和公钥)。
工作原理简述:你在本地生成一对密钥。将公钥(好比一把公开的锁)上传到服务器的~/.ssh/authorized_keys文件中。当你尝试连接时,服务器用这把“锁”加密一个随机挑战字符串发送给你的客户端。你的客户端用本地保存的私钥(唯一的钥匙)解密并回应这个挑战。服务器验证回应正确,即认证通过。私钥从不离开你的本地机器。
优势:
- 极高的安全性:私钥通常是一长串随机字符,暴力破解在现有算力下几乎不可能。
- 避免密码泄露风险:网络上传输的只有加密的挑战和回应,没有密码明文。
- 支持免密登录:可以为私钥设置一个“通行短语”(passphrase)来加密私钥文件本身,提供二次保护。也可以不设置,实现真正的免密登录(但建议设置,以防私钥文件被盗)。
2.3 修改默认SSH端口:躲开自动化扫描的“第一波浪潮”
SSH默认监听22端口,这是尽人皆知的事实。全球有无数的僵尸网络和扫描器,24小时不间断地对公网IP的22端口进行密码爆破尝试。修改为一个非标准的高位端口(如 2022, 3522等),可以立即过滤掉99%的自动化、无差别的扫描流量。这就像把你的家门从热闹的主街1号搬到了小巷子里的10086号,那些挨家挨户试门锁的小偷可能根本不会走到这里来。
需要注意:
- 这不是银弹。定向攻击者或全端口扫描依然能找到你的服务。因此,它必须与前两项措施结合使用。
- 修改端口后,你每次连接都需要显式指定端口号,例如
ssh -p 3522 user@server_ip。 - 务必在防火墙(如
firewalld、iptables或云服务商的安全组)中同时放行新端口,并考虑关闭旧端口的访问。
方案选型总结:我们选择按“创建新用户 → 配置密钥 → 修改配置 → 重启服务 → 测试验证”的顺序进行。这个顺序是安全的:先准备好“备用钥匙”(新用户和密钥),再更换“门锁”(修改配置),最后测试新钥匙能否开门,并确保旧钥匙(密码登录和root直接登录)已经失效。绝对不要反过来,先禁用密码或root,结果发现自己没配置好密钥,那就真的“家”门难入了。
3. 实操前的核心准备:打造你的“备用钥匙”
在修改任何SSH服务器配置之前,我们必须先做好万全准备,确保有一条可靠的备用登录路径。这步错了,后续操作就可能变成“自杀”。
3.1 创建具备sudo权限的替代管理用户
首先,我们需要登录到你的服务器(目前还是用root或者已有账户通过密码登录)。然后执行以下步骤:
添加新用户:我们创建一个名为
admin的用户(你可以用任何喜欢的名字,但避免使用admin、test等常见名,增加猜解难度)。adduser admin系统会提示你设置密码及一些个人信息,密码请设置一个强密码(即使后续用密钥,密码作为备用手段也应足够强)。
赋予sudo权限:这是关键一步,让
admin用户能够执行管理员命令。- 在Ubuntu/Debian系统:将用户加入
sudo组。usermod -aG sudo admin - 在CentOS/RHEL系统:将用户加入
wheel组(通常wheel组默认有sudo权限)。
你可以检查usermod -aG wheel admin/etc/sudoers文件,确认%wheel组是否有ALL=(ALL) ALL这样的配置。
- 在Ubuntu/Debian系统:将用户加入
验证新用户:
- 打开一个新的终端窗口或标签页,尝试用新用户登录:
ssh admin@你的服务器IP - 登录成功后,尝试切换为root或执行需要特权的命令:
系统会提示你输入sudo su - # 或者执行一个命令 sudo ls /rootadmin用户的密码(即刚才设置的密码)。输入正确后,命令应该成功执行。这一步至关重要,它证明了你的新用户具备管理权限,是可靠的“备用钥匙”。
- 打开一个新的终端窗口或标签页,尝试用新用户登录:
3.2 在本地生成并部署SSH密钥对
现在,我们在你本地的电脑上(比如你的Windows/Mac/Linux开发机)生成密钥对。
生成密钥对:打开你本地的终端(Windows可用PowerShell或Git Bash)。
ssh-keygen -t rsa -b 4096 -C "your_email@example.com" -f ~/.ssh/id_rsa_admin_server-t rsa: 指定密钥类型为RSA(Ed25519也是很好的选择,更安全更快,但某些老旧系统可能不支持)。-b 4096: 指定密钥长度为4096位,安全性更高。-C: 添加一个注释,通常用邮箱,便于识别这把钥匙是给哪台服务器用的。-f: 指定密钥文件的保存路径和名称。这里我们指定了一个自定义名称id_rsa_admin_server,避免覆盖可能已存在的默认密钥id_rsa。
执行命令后,会提示你输入“通行短语”(passphrase)。强烈建议设置一个!这相当于给你的私钥文件本身加了一把密码锁。即使私钥文件不慎泄露,没有通行短语也无法使用。当然,如果你追求极致的便利性(例如用于CI/CD自动化),可以不设(直接回车两次)。
将公钥上传到服务器:生成后,你会得到两个文件:私钥
id_rsa_admin_server和公钥id_rsa_admin_server.pub。我们需要把公钥上传到服务器上admin用户的家目录下。- 方法一:使用
ssh-copy-id(最方便,但需要当前支持密码登录):
输入ssh-copy-id -i ~/.ssh/id_rsa_admin_server.pub -p 22 admin@你的服务器IPadmin用户的密码,即可自动完成部署。 - 方法二:手动复制(通用方法):
- 先查看你的公钥内容:
cat ~/.ssh/id_rsa_admin_server.pub,全选复制。 - 登录服务器(用
admin用户密码登录):ssh admin@你的服务器IP。 - 在服务器上,确保
~/.ssh目录存在且权限正确:mkdir -p ~/.ssh chmod 700 ~/.ssh - 将复制的公钥内容追加到
authorized_keys文件:echo “你复制的公钥内容” >> ~/.ssh/authorized_keys - 设置
authorized_keys文件的权限(权限不对会导致认证失败):chmod 600 ~/.ssh/authorized_keys
- 先查看你的公钥内容:
- 方法一:使用
测试密钥登录:在本地新开一个终端,尝试使用密钥登录,注意此时要指定我们刚生成的私钥文件:
ssh -i ~/.ssh/id_rsa_admin_server -p 22 admin@你的服务器IP- 如果你设置了通行短语,此时会提示你输入。
- 如果成功登录,且没有要求输入密码,恭喜你,密钥认证配置成功!这是你未来最主要的登录方式。
实操心得:在测试密钥登录时,我强烈建议保持原来的密码登录会话窗口不要关闭。在新窗口测试密钥登录成功并确认一切正常后,再回到旧窗口进行后续的危险配置修改。这相当于系上了“安全绳”。
4. 深入SSH服务端配置:动刀前的谨慎与精确
完成上述准备后,我们终于可以开始修改SSH服务端(sshd)的配置文件了。这个文件通常位于/etc/ssh/sshd_config。修改前,务必先备份!
sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak现在,用你喜欢的文本编辑器(如vim、nano)打开配置文件:
sudo vim /etc/ssh/sshd_config我们将找到并修改以下几个关键参数。注意,配置文件中以#开头的行是注释,要修改的是去掉#后的实际参数行。
4.1 禁用Root直接登录
找到关于PermitRootLogin的配置行。它可能有几种状态:被注释掉(默认行为因发行版而异)、设为yes、prohibit-password等。我们将其修改为:
PermitRootLogin no这明确禁止了任何形式的root直接登录(包括密码和密钥)。
4.2 启用并强制密钥认证,禁用密码认证
找到PubkeyAuthentication和PasswordAuthentication参数。
- 确保公钥认证开启(通常默认是开启的):
PubkeyAuthentication yes - 将密码认证关闭:
重要提醒:在确认密钥登录100%工作正常之前,不要先将此选项设为PasswordAuthentication nono。一个安全的操作顺序是:先设为yes,测试密钥登录成功后,再回来改为no。或者,更稳妥的做法是,在本次修改中先保持yes,等所有修改完成并重启服务后,用密钥登录测试,确认无误,再在同一个会话中第二次修改配置文件,将其改为no并再次重启。这能最大程度避免被锁在外面。
4.3 修改SSH服务监听端口
找到#Port 22这一行。默认是被注释的,SSD服务实际监听22端口。我们做两件事:
- 取消
Port 22的注释(即保留22端口作为一个监听选项,但这不是必须的,有些教程建议直接删除22,我更倾向于先保留作为备份)。 - 在它下面新增一行,指定一个新端口。端口号选择1024到65535之间,且未被系统其他服务占用的数字。例如:
这样配置表示Port 22 Port 3522sshd会同时监听22和3522两个端口。在我们测试新端口成功之前,旧端口是重要的逃生通道。
4.4 其他推荐的安全加固选项
你可以顺便调整这些参数,进一步提升安全性:
ChallengeResponseAuthentication no:禁用挑战应答认证(通常与密码认证相关)。UsePAM yes:保持PAM启用,但因为我们关了密码认证,它的相关模块影响不大。有些系统需要它为yes才能正常使用某些功能(如AllowUsers)。AllowUsers admin:这是一个白名单机制,只允许列出的用户(如admin)通过SSH登录。即使有其他用户账户存在,也无法SSH登录。配置此项要格外小心,确保你列出的用户(admin)绝对正确,并且该用户已经配置好密钥。语法是AllowUsers user1 user2。ClientAliveInterval 300和ClientAliveCountMax 2:设置客户端活跃检测,如果300秒内没有收到客户端的任何数据,服务器会发送一条加密的“保活”消息,最多发送2次。如果客户端均无响应,则断开连接。这可以防止连接因网络问题长时间挂起。
修改完成后,保存并退出编辑器。
5. 防火墙配置与服务重启:让新规则生效
修改了sshd_config只是改变了服务的配置,还需要让服务重新加载配置,并且确保防火墙允许新的端口通过。
5.1 配置系统防火墙
这里以最常见的firewalld(CentOS 7/8, RHEL, Fedora)和ufw(Ubuntu, Debian)为例。
对于使用 firewalld 的系统:
# 添加新端口到永久规则,并重载防火墙 sudo firewall-cmd --permanent --add-port=3522/tcp sudo firewall-cmd --reload # 查看端口是否添加成功 sudo firewall-cmd --list-ports对于使用 ufw 的系统:
# 允许新端口 sudo ufw allow 3522/tcp # 启用UFW(如果尚未启用) sudo ufw enable # 查看规则 sudo ufw status numbered对于云服务器(重要!):你必须登录云服务商的管理控制台(如阿里云、腾讯云、AWS的安全组),找到你服务器实例关联的安全组规则,添加入站规则,允许TCP协议访问你新设置的端口(如3522)。云服务器的安全组是独立于系统防火墙的另一道关卡,两者都需要配置。
5.2 谨慎重启SSH服务并测试
这是最紧张的一步。我们分阶段测试,确保退路。
首次重启,双端口运行:因为我们配置了
Port 22和Port 3522,所以先以此配置重启。sudo systemctl restart sshd # 或者对于某些系统 sudo systemctl restart ssh检查服务状态,确保没有报错:
sudo systemctl status sshd测试新端口密钥登录:不要关闭当前的服务器连接窗口!在你本地机器上打开一个新的终端,尝试使用新端口和密钥登录。
ssh -i ~/.ssh/id_rsa_admin_server -p 3522 admin@你的服务器IP- 如果成功登录,说明新端口和密钥认证工作正常。太好了!
- 如果失败,检查错误信息。常见问题:防火墙没开、安全组没设、
sshd_config中端口号写错、密钥路径或权限不对。根据错误信息,在当前保持的连接窗口里进行修复。
测试旧端口密码登录应失败:在本地再开一个终端,尝试用密码登录旧端口,此时应该被拒绝(因为我们还没关密码,但用了密钥,所以可能提示密钥方式被拒,然后才问密码?实际上,如果客户端提供了密钥,服务器会优先尝试密钥。我们可以强制不用密钥来测试密码):
ssh -o PreferredAuthentications=password -o PubkeyAuthentication=no -p 22 admin@你的服务器IP此时应该提示“Permission denied”。这验证了密钥认证优先。
最终加固:禁用密码并移除旧端口:在通过新端口登录的会话中(或者你一直没断开的那个原始会话),再次编辑
/etc/ssh/sshd_config:- 将
PasswordAuthentication的值从yes改为no。 - 将
Port 22这一行注释掉(行首加#)或直接删除。只保留Port 3522。 - 保存退出。
- 将
第二次重启SSH服务:
sudo systemctl restart sshd最终验证:
- 验证1:尝试用新端口+密钥登录,应该成功。
- 验证2:尝试用新端口+密码登录(强制密码方式),应该立即被拒绝,提示“Permission denied (publickey)”,这表明密码认证已关闭。
- 验证3:尝试连接旧端口22,应该连接超时或被拒绝(因为服务不再监听22端口,且防火墙可能也没开22,云安全组也可以考虑关闭22端口入站)。
至此,核心的“三板斧”加固已经完成。你的服务器SSH入口已经从“不设防的广场”变成了“需要特定钥匙、走特定小巷后门”的隐蔽通道。
6. 客户端连接优化与高级配置
服务端配置好了,客户端用起来也别太别扭。每次连接都要输入-i -p太麻烦,我们可以通过SSH客户端配置文件来简化。
在你本地电脑的~/.ssh/config文件(如果没有就创建一个)中,添加如下配置:
Host myserver # 一个别名,方便记忆 HostName 你的服务器IP地址 Port 3522 # 你修改后的端口 User admin # 登录用户名 IdentityFile ~/.ssh/id_rsa_admin_server # 私钥路径 # 可选配置 ServerAliveInterval 60 # 每60秒发送一次保活包,防止连接断开 TCPKeepAlive yes # 启用TCP保活保存后,以后连接服务器只需要一句命令:
ssh myserver所有参数都会自动应用,极大提升了日常使用的便利性。
7. 常见问题与故障排查实录
即使按照步骤操作,也可能会遇到问题。这里记录几个我踩过的坑和解决方法。
7.1 连接被拒绝 (Connection refused)
现象:ssh -p 3522 admin@ip后立刻返回Connection refused。排查思路:
- 服务未监听端口:在服务器上执行
sudo netstat -tlnp | grep sshd或sudo ss -tlnp | grep sshd,查看sshd进程是否在监听你设置的端口(如3522)。如果没有,说明sshd配置未生效或重启失败。检查sshd_config语法:sudo sshd -t。如果有错误,根据提示修正。 - 防火墙/安全组阻拦:这是最常见的原因。确保系统防火墙和云平台安全组都正确放行了新端口。在服务器上可以临时关闭防火墙测试(
sudo systemctl stop firewalld或sudo ufw disable),但测试后务必重新开启并配置好规则。 - IP地址或端口号写错:再检查一遍命令。
7.2 权限太开放 (Permissions are too open)
现象:密钥登录失败,客户端提示WARNING: UNPROTECTED PRIVATE KEY FILE!或Permissions 0644 for ‘.ssh/id_rsa‘ are too open.原因:你的私钥文件(id_rsa_admin_server)的权限过于宽松,SSH出于安全考虑拒绝使用。解决:在本地电脑上执行:
chmod 600 ~/.ssh/id_rsa_admin_server同样,服务器上的~/.ssh目录权限应为700,authorized_keys文件权限应为600。
7.3 认证失败 (Permission denied, please try again)
现象:提示输入密码,但输入正确密码后仍被拒绝(在禁用密码前),或直接提示Permission denied (publickey)。排查思路:
- 服务器端
authorized_keys问题:确认公钥内容已正确无误地追加到服务器上admin用户的~/.ssh/authorized_keys文件末尾。检查文件权限(必须为600)。 sshd_config配置:确认PubkeyAuthentication yes和AuthorizedKeysFile .ssh/authorized_keys(默认)已设置。- SELinux(仅限RHEL/CentOS):SELinux可能会阻止非标准家目录或
.ssh目录的访问。可以尝试暂时将SELinux设为宽容模式测试:sudo setenforce 0。如果问题解决,需要为SSH设置正确的SELinux上下文:restorecon -Rv ~/.ssh。生产环境请谨慎操作SELinux。 - 用户家目录权限:
admin用户的家目录权限不能对组或其他人有写权限(如755是可以的,但777就不安全,可能导致SSH拒绝使用该目录下的密钥)。建议设置为750或755。
7.4 修改配置后重启sshd失败
现象:执行sudo systemctl restart sshd后,服务状态为failed。解决:立即查看日志获取错误详情:
sudo journalctl -xe -u sshd --no-pager | tail -50最常见的错误是sshd_config文件中有语法错误,比如参数拼写错误、值格式不对。根据日志提示的行号去修正配置文件。在修正前,如果你还有活跃的SSH连接,千万不要断开!如果所有连接都断了,而服务又起不来,你可能需要通过云服务商的VNC控制台(或物理服务器的本地控制台)登录进行修复。
8. 维护与监控:安全是一个持续的过程
配置完成并非一劳永逸。你需要定期:
- 查看认证日志:经常检查
/var/log/secure(RHEL系)或/var/log/auth.log(Debian系),关注失败的登录尝试。命令如sudo grep “Failed password” /var/log/secure或使用fail2ban这类工具自动封禁暴力破解IP。 - 更新密钥:定期(如每半年或一年)更换一次密钥对,就像更换密码一样。
- 管理授权密钥:定期检查服务器上
~/.ssh/authorized_keys文件,移除不再需要的公钥。 - 关注SSH漏洞:关注CVE公告,及时更新系统和OpenSSH软件包。
我个人在管理多台服务器时,会为每台服务器生成独立的密钥对,并使用一个本地的密码管理器来管理这些私钥的通行短语和对应的服务器信息。同时,所有sshd_config的修改都会通过Ansible等自动化工具进行,并纳入版本控制,确保配置的一致性和可追溯性。这套“禁用root+密钥登录+改端口”的组合,是我给每一台新服务器上线时必做的“标准动作”,它用极低的成本,构筑了应对自动化攻击的第一道,也是极其有效的防线。