Ubuntu服务器暴露在公网后,SSH服务几乎是第一时间被扫描和爆破的目标。默认的22端口配合密码登录,短则几分钟内就会出现大量失败尝试。真正有效的加固不是安装某个单一工具,而是从认证方式、账户策略、网络层访问控制到内核参数与审计联动,形成一套可落地的限制登录体系。下面直接拆解每一步的具体操作和判断标准。

先确认SSH服务状态与版本基线

在修改任何配置前,先检查当前OpenSSH版本和运行状态。执行 systemctl status ssh 查看服务是否正常,再执行 ssh -V 确认版本。Ubuntu 22.04和24.04默认的OpenSSH版本已经支持较为完善的安全参数,但部分旧配置可能仍允许不安全的密钥交换算法。建议先备份配置文件:cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak.$(date +%F)。后续所有修改都应保留回退能力。

立即关闭密码登录,强制使用密钥认证

密码登录是SSH爆破的主要入口。编辑 /etc/ssh/sshd_config,将 PasswordAuthentication 设置为 no,同时确保 PubkeyAuthentication yes 处于启用状态。修改后执行 systemctl restart ssh 生效。注意在关闭密码登录前,必须确认当前已有可用的公钥已写入目标用户的 ~/.ssh/authorized_keys,并且权限正确:目录权限700,文件权限600。否则一旦断开当前会话,可能无法再次登录。对于必须保留密码登录的临时场景,至少应配合fail2ban或限制来源IP,但长期暴露的主机不建议保留。

禁用root直接登录并限制可登录用户

PermitRootLogin 应设置为 no,强制管理员通过普通用户提权。进一步使用 AllowUsers 或 AllowGroups 限制仅特定账户可建立SSH会话。例如 AllowUsers deploy ops 或 AllowGroups sshusers。这两个指令优先级高,能直接阻断其他系统账户的SSH访问,即使对方拿到了有效密码或密钥也无法登录。配置后重启SSH服务,并用被限制的账户测试确认拒绝效果。

更换默认端口并调整监听地址

将 Port 22 改为非标准端口,例如 22022 或更高位端口,能过滤掉大量自动化扫描。同时如果服务器有多块网卡,建议用 ListenAddress 指定仅监听内网或管理网段的IP,避免公网所有接口直接暴露SSH。修改端口后,防火墙规则必须同步放行新端口,并确认云安全组或本机iptables/nftables规则已更新。测试新端口可用后,再关闭22端口访问。

配置防火墙实现来源IP白名单

Ubuntu默认使用ufw作为iptables前端。启用ufw后,仅放行指定来源IP或网段访问SSH端口:ufw allow from 203.0.113.0/24 to any port 22022 proto tcp。如果管理IP不固定,至少应限制到运营商网段或公司出口IP段。对于需要多地访问的场景,可维护一个IP集合,使用脚本定期更新ufw规则。网络层白名单是限制登录最有效的一环,能直接让非授权来源无法建立TCP连接,减少SSH服务本身被探测和攻击的机会。

设置登录超时与会话存活参数

在sshd_config中调整以下参数:LoginGraceTime 30 限制认证过程最多30秒,超过则断开;MaxAuthTries 3 限制单次连接最多尝试3次认证;ClientAliveInterval 300 和 ClientAliveCountMax 2 用于检测僵死会话,10分钟无响应即断开。这些参数能显著降低慢速暴力尝试和空闲会话占用的资源。对于自动化运维场景,可适当调整ClientAliveInterval避免长任务被中断。

限制认证方式与密钥交换算法

在sshd_config中显式指定强算法,避免使用过时的ssh-rsa和CBC模式加密。推荐配置:

KexAlgorithms curve25519-sha256,curve25519-sha256@libssh.org,diffie-hellman-group-exchange-sha256
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com
MACs hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com
HostKeyAlgorithms ssh-ed25519,rsa-sha2-512,rsa-sha2-256

这些算法列表排除了已知弱算法,能防止降级攻击。修改后重启SSH服务,并用 ssh -Q 参数验证客户端是否支持对应算法。对于老旧客户端,可能需要升级或单独配置兼容策略,但公网服务器应以安全优先。

部署fail2ban实现动态封禁

即使关闭密码登录,fail2ban仍能封禁反复尝试无效用户名的来源IP,降低日志噪音和资源消耗。安装后创建本地配置 /etc/fail2ban/jail.local,针对sshd设置:

[sshd]
enabled = true
port = 22022
filter = sshd
logpath = /var/log/auth.log
maxretry = 3
bantime = 3600
findtime = 600

重启fail2ban后,连续3次失败即封禁1小时。对于使用密钥认证的环境,可将filter调整为匹配无效用户或协议错误,避免只监控密码失败。fail2ban的封禁动作默认通过iptables实现,与ufw规则可叠加,但需注意规则顺序和重启后的持久化。

启用SSH日志审计与实时告警

仅靠登录限制还不够,需要持续观察登录行为。Ubuntu的SSH日志默认记录在 /var/log/auth.log。建议配置rsyslog将sshd日志单独输出,或使用journalctl -u ssh 查看服务日志。对于关键服务器,可编写脚本监控auth.log中“Accepted”和“Failed”事件,通过邮件或Webhook发送告警。例如使用logwatch或自建脚本定时分析登录来源、用户和频率。审计日志不仅是事后追溯的依据,也能帮助发现绕过限制的异常行为。

利用TCP Wrappers或systemd沙箱加固

虽然TCP Wrappers在部分发行版中逐渐被弃用,但Ubuntu仍可通过 /etc/hosts.allow 和 /etc/hosts.deny 实现简单的来源控制。更现代的做法是使用systemd的沙箱能力为SSH服务添加额外限制。创建 /etc/systemd/system/ssh.service.d/hardening.conf:

[Service]
PrivateTmp=true
ProtectSystem=strict
ProtectHome=read-only
NoNewPrivileges=true
RestrictAddressFamilies=AF_INET AF_INET6

这些参数限制SSH服务进程的文件系统访问和权限提升能力,即使SSH服务本身出现漏洞,攻击者也难以横向移动或持久化。修改后执行 systemctl daemon-reload 和 systemctl restart ssh 生效。

定期轮换密钥并清理历史授权

SSH密钥长期不换同样存在风险。建议每季度或半年轮换一次访问密钥,并清理不再使用的公钥条目。检查所有用户的 ~/.ssh/authorized_keys,删除无法对应到实际人员或设备的密钥。对于团队协作,可使用集中式密钥管理或证书签名方式,避免密钥散落在多台服务器上难以回收。同时检查 ~/.ssh/known_hosts 中是否存在异常主机记录,防止中间人攻击后遗留的错误信任。

结合内核参数限制连接速率

在 /etc/sysctl.conf 或 /etc/sysctl.d/ 下增加网络层限制:

net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_max_syn_backlog = 4096
net.ipv4.tcp_synack_retries = 2
net.ipv4.tcp_abort_on_overflow = 1

这些参数能缓解SYN Flood和连接队列耗尽攻击,间接保护SSH服务的可用性。对于高并发管理场景,可适当调大backlog值,但不应完全关闭syncookies。执行 sysctl -p 使配置生效。

测试与验证加固效果

完成上述配置后,必须从多个来源进行验证:使用被限制的IP测试是否被防火墙拒绝;使用错误密钥测试是否无法登录且日志有记录;使用允许的账户和密钥确认能正常建立会话。同时检查 sshd -t 确认配置文件语法正确,避免因配置错误导致服务无法启动。最后重启服务器测试所有规则在重启后是否依然生效,确保防火墙、fail2ban和systemd沙箱配置均已持久化。

SSH安全加固的核心不是堆砌配置,而是让每个限制条件都能在实际攻击路径上生效。关闭密码登录和root登录是基础,来源IP白名单和动态封禁是主要防线,算法限制和沙箱机制则提供纵深防御。定期审计和轮换密钥才能让这套体系长期有效。