☰
SSH防爆破与SELinux策略:Linux服务器安全加固实战解析
2026/10/22 16:40:11 网站建设 项目流程

上个月帮某公司处理一台被爆破的数据库服务器时,我打开登录日志,短短 6 个小时里躺着 4 万多条失败记录。攻击者先是批量扫端口,接着用常见用户名列表跑密码字典,最后还尝试通过畸形请求触发服务漏洞。这种场景在公网环境里几乎每天都会发生,Linux 安全加固不是"可选优化项",而是服务器上线前就该完成的基本工序。

这篇文章我把 SSH 防爆破和 SELinux 策略配置这两块最核心的内容串起来讲,从 sshd 配置、密钥登录、fail2ban 自动封禁,一直讲到 SELinux 的标签体系、布尔值开关和自定义策略生成。中间会穿插我在真实环境里处理过的案例、踩过的坑,以及一些常规文档里不会写的判断依据。适合刚接手服务器的新手,也适合已经会基础操作但想补齐安全细节的运维同学。

1. 为什么企业 Linux 服务器会被“盯上”

1.1 攻击者到底在扫描什么

公网 IP 一旦开放,就会有扫描器不间断地探测。它们不看服务器上跑的是什么业务,只看两个东西:第一,端口是否开放;第二,端口背后的服务是否存在已知弱点。SSH 默认端口 22、MySQL 默认端口 3306、Redis 默认端口 6379,这些都是扫描器最喜欢的光顾对象。

扫描结果里面,只有一个开放端口还不够,攻击者还要判断这台机器值不值得投入成本。他们的判断维度通常包括:是否使用弱密码、是否允许 root 直接登录、系统版本是否偏老、服务是否存在高危漏洞。这些信息通过指纹识别和几次尝试就能摸清。所以一台刚装好系统、没有做任何加固的服务器,本质上就像没锁门的仓库,任何人都能推门看一眼。

很多开发者觉得"我的密码够复杂,爆破不可能成功",但实际上暴力穷举只是最低级的手段。攻击者手里有大量泄露的真实密码组合,还会利用常见用户名、默认口令、键盘序列密码来批量尝试。即使单次爆破成功率很低,放在上千台服务器上跑,总能撞开几台。企业服务器一旦被撞开,轻则被植入挖矿程序,重则成为内网横向渗透的跳板。

1.2 加固前先想清楚:要防的是谁

做安全加固之前,我会先问自己三个问题:这台机器暴露在什么网络环境里?上面跑的是什么数据?被攻破之后会造成什么影响?这三个问题的答案决定了加固的强度。

比如一个公网 WEB 服务器和一台纯内网运维跳板机,面对的风险不一样。公网服务器受到的是持续扫描和自动化攻击,内网机器则更怕员工误操作或已失陷终端的横向移动。再比如数据库服务器,即使内网访问,也不能只靠内网隔离,因为渗透者一旦控制同网段的某一台应用服务器,下一步就会尝试连接数据库。所以内网机器同样需要关闭无关端口、限制来源地址、开启 SELinux,而不是一句"内网很安全"就不管了。

这个阶段还有一个容易被忽略的动作:盘点开放端口和监听服务。用ss -lntp看当前监听端口,用systemctl list-unit-files --type=service --state=enabled看开机自启服务,把不认识、不需要的服务全部停掉并禁用。很多加固方案忽略这一步,结果防火墙规则写得再漂亮,机器上还是开着不必要的端口,等于白做。

2. SSH 防爆破:把第一道门真正锁死

2.1 sshd 配置里最容易被忽略的开关

SSH 防爆破的核心在于/etc/ssh/sshd_config。我刚接触 Linux 的时候,只知道改Port和PermitRootLogin,后来才发现真正决定 SSH 安全等级的细节远不止这两个参数。

先说端口,把 SSH 从 22 改成其他高位端口,能过滤掉绝大多数无差别扫描,这是成本最低的措施。但注意,改端口不是隐藏式安全,真正有目标的攻击者依然能探测到。所以端口只能作为第一层噪声过滤,不能替代密钥认证和 fail2ban。

再说PermitRootLogin,我给出的建议是prohibit-password而不是no。prohibit-password的意思是禁止 root 使用密码登录,但允许通过密钥登录。这样做的好处是运维人员在紧急情况下仍然可以直接用 root 密钥登录,不需要先切到普通用户再su -,同时又堵死了暴力破解 root 密码的路径。

其他容易被忽略的推荐配置:

  • PasswordAuthentication no:关闭密码认证,这是防爆破最关键的一步。
  • PubkeyAuthentication yes:显式开启密钥认证。
  • MaxAuthTries 3:限制单次连接的最大认证尝试次数,防止在同一连接中反复试密码。
  • LoginGraceTime 30:登录超时缩短到 30 秒,避免攻击者占用连接资源。
  • AllowUsers ops admin:只允许指定用户通过 SSH 登录,从账号层面收口。
  • ClientAliveInterval 300配合ClientAliveCountMax 2:每 300 秒探测一次客户端,连续 2 次无响应就断开,避免死连接堆积。

我见过一个问题:管理员修改完sshd_config后直接重启 sshd 服务,结果配置写错了,把自己关在外面。正确的做法是先执行sshd -t做语法检查,再通过另一个终端保持已有会话不断开的情况下重启服务,这样即使新配置有问题,旧连接还能保留,方便回滚。

2.2 密钥登录替代密码登录的落地流程

密钥登录的原理是非对称加密。客户端生成一对密钥,私钥留在本地,公钥放到服务器的~/.ssh/authorized_keys中。登录时服务器用公钥验证客户端的签名,私钥不经过网络传输,天然避免了密码被中间人截获的问题。

生成密钥时,我推荐用 ED25519 算法而不是 RSA。ED25519 密钥更短、生成更快、安全性不输给 RSA,在多数现代系统上都有原生支持。命令是:

ssh-keygen -t ed25519 -a 100 -f ~/.ssh/id_ed25519

-a 100设置 KDF 迭代次数,虽然生成和加载时会稍慢一点,但能显著提高密钥文件被拿到后的暴力破解难度。生成后把公钥上传到服务器:

ssh-copy-id -i ~/.ssh/id_ed25519.pub ops@your-server-ip

如果服务器禁用了密码登录还没上传公钥,那就尴尬了。所以我的操作顺序是:先生成并上传公钥,确认密钥登录已经能正常工作,再去关闭PasswordAuthentication。整个切换过程要留一个"逃生窗口",不要在一次变更里同时改端口、改认证方式、改允许用户列表,任何一个环节出错都会导致登录失败,而且很难判断到底卡在哪一步。

密钥本身也要管理好。私钥文件权限必须是 600,如果权限过松,SSH 会直接拒绝使用该密钥。另外建议给私钥设置 passphrase,这样即使私钥文件泄露,攻击者还要再破解一道口令。对要求比较高的企业环境,可以再把私钥放进硬件令牌或系统钥匙串中,不过这些属于进阶方案,普通团队先把 passphrase 用起来就足够了。

2.3 fail2ban:自动拉黑的最后一道防线

即使关闭了密码认证,公网服务器上仍然会收到大量尝试连接的空连接和无效请求。fail2ban 的作用是读取服务日志,发现异常模式后自动调用防火墙规则封禁来源 IP,相当于给 SSH 加了一个自动防御系统。

安装后在/etc/fail2ban/jail.local中做如下设置:

[sshd] enabled = true port = ssh filter = sshd logpath = /var/log/secure maxretry = 5 findtime = 600 bantime = 3600

这几个参数的含义是:600 秒内同一个 IP 出现 5 次认证失败,就封禁 1 小时。maxretry不要设得太低,否则你自己输错一次密码、换个 IP 再登录,很容易误封;也不建议设得太高,否则等于给攻击者留了容错空间。我在生产环境里通常用 5 到 8 次,配合合理的长封禁时间。

封禁时间bantime也可以做成递增策略,首次封 1 小时、再次触发封 24 小时、第三次封 7 天,这个可以在 jail.local 里用recidive机制实现。具体配置不展开,知道有递增封禁这个思路就行。

fail2ban 运行后,用fail2ban-client status sshd可以查看当前封禁列表。日常维护中要注意:有些系统日志路径不是/var/log/secure,而是/var/log/auth.log,需要根据发行版调整logpath。另外,如果服务器前面还有云平台的负载均衡器,所有 SSH 连接都会显示成同一个入口 IP,fail2ban 就无法正确识别真实来源,这种情况下需要在云平台的安全组层面做访问控制,而不是单纯依赖 fail2ban。

3. 账号权限和最小化:加固的第二层

3.1 用户管理、sudo 规则和 PAM 收敛

SSH 防住了端口,不等于服务器就安全了。真正被攻破的服务器,往往是在合法账号上出了问题。比如员工离职但账号没有禁用、测试环境使用共享账号、普通用户被误加到 sudo 组等。账号权限的收敛必须和 SSH 加固同步进行。

我的原则是最小权限。每个需要登录服务器的人,都应该有独立账号,而不是共用 root 或共用某个业务账号。独立账号的另一个好处是可追溯,出事的时候能根据日志定位到具体的人。

对所有运维账号,日志审计和安全审计环境要求较高的地方,还可以启用jailkit或限制为rbash,不过多数场景并不需要这么重的限制,普通用户配合 sudo 规则就够了。

举一个完整的收敛步骤:创建用户ops,加入wheel组,然后单独配置 sudoers。在/etc/sudoers.d/ops中不直接给完整 root 权限,而是只允许执行运维需要的命令:

ops ALL=(ALL) NOPASSWD: /usr/bin/systemctl restart nginx, /usr/bin/systemctl restart sshd

这样即使账号泄露,攻击者能执行的命令也是有限集合。当然,如果团队确实需要完整权限,那至少要保证 sudo 操作会被日志记录。/etc/sudoers中的Defaults log_output可以把 sudo 会话的所有输入输出记录到日志中,这是排查问题的关键证据。

PAM 层面的配置也值得检查。/etc/security/pwquality.conf可以设定密码复杂度,/etc/pam.d/system-auth中启用pam_pwquality、pam_faillock等模块。pam_faillock可以在连续输错密码后锁定账号一段时间,配合 fail2ban 形成双重保护。注意锁定账号和锁定来源 IP 是两种不同的防护思路,账号锁定能防御针对同一账号的暴力尝试,来源 IP 锁定能防御大量账号的批量扫描。

3.2 文件权限、umask 与不可变属性

文件权限是 Linux 权限体系里最基础的一环,但越是基础越容易出乱子。我接手过一台服务器,根目录下有一个 0777 权限的备份目录,里面放的是数据库全量导出文件。这种权限意味着任何能登录服务器的用户都能读取甚至覆盖备份,一旦被入侵者发现,数据泄露只是时间问题。

先检查系统中权限过宽的文件,尤其是配置文件、脚本和备份文件:

find / -xdev -type f -perm -002 -print

这条命令会扫描出所有"其他用户可写"的文件,-002表示检查文件的 other 写位。扫出来后逐一确认,能去掉写权限就去掉,不能去掉的也要评估原因并记录在案。

umask决定新建文件的默认权限。多数系统的默认 umask 是 022,也就是新建文件权限为 644、目录为 755,这个值对普通环境够用。对高安全环境,可以把 umask 设为 027,让同组用户保持默认读权限、其他用户没有任何权限。修改位置可以放在/etc/profile或/etc/bashrc中:

umask 027

还有一个小众但实用的机制是chattr不可变属性。对关键目录和关键配置,比如/etc/ssh/sshd_config、/etc/shadow,可以用chattr +i加上不可变标记。加了这个标记后,即使是 root 用户也不能修改或删除文件,直到执行chattr -i取消标记。这对防御勒索软件和篡改配置类攻击很有效,因为攻击者就算拿到了权限,改不了文件也白搭。

我踩过最深的一个坑是:用chattr +i锁定了 SSH 配置,后来改配置时忘记先解锁,直接重启 sshd 后发现新版配置没生效,排查了半天才发现是文件被锁定了。所以这个操作只适合企业里运维流程严格的机器,不适合个人偶尔改配置的服务器。

4. SELinux 策略配置:从原理到实战

4.1 三种模式:enforcing、permissive 与 disabled

SELinux 是 Linux 内核的强制访问控制机制,和传统的"用户权限决定文件能否访问"不同,它会进一步检查"进程"和"对象"之间的关系。默认的 DAC 权限只看你是哪个用户、在哪个组;SELinux 在此基础上添加了安全上下文,也就是给进程、文件、端口都打上类型标签,通过策略指定哪些类型之间可以交互。

这个机制让很多人头疼,因为配置不当会导致正常服务无法启动。最常见的例子:把 Nginx 的网页根目录放到/home/nginx/www下,结果 Nginx 启动后返回 403,就是因为/home下的文件没有httpd_sys_content_t标签,SELinux 阻止了 Nginx 进程读取。

SELinux 有三种模式:enforcing、permissive和disabled。disabled表示完全关闭,permissive表示记录违规但放行,enforcing表示拦截违规操作。我强烈不建议直接disabled,因为关闭之后系统就不再产生 AVC 记录,你无法知道业务到底会触碰哪些规则,日后即使想开启,要排查的问题比从permissive开始更多。

正确的新系统部署路径是:先把系统设为permissive,跑一遍正常业务流程,然后查看/var/log/audit/audit.log里的 AVC 拒绝记录。再根据记录调整标签或布尔值,之后切到enforcing观察一段时间。用命令切换运行时模式:

setenforce 1 # 切换到 enforcing setenforce 0 # 切换到 permissive

修改/etc/selinux/config中的SELINUX=enforcing可以让模式在重启后保持。注意setenforce只改变当前运行模式,不改配置文件,两个都要做才能持久化。

4.2 标签、布尔值与端口映射怎么配合

SELinux 的标签规则是核心。查看文件标签用ls -Z,查看进程标签用ps -eZ。例如:

ls -Z /var/www/html/index.html ps -eZ | grep nginx

正常情况下,Nginx 读取网页文件的标签应该是httpd_sys_content_t,Nginx 进程本身的标签是httpd_t。如果文件被放在没有正确标签的目录下,执行restorecon能把这个文件恢复为策略默认的标签:

restorecon -Rv /var/www/html

如果目录本身是自定义路径,比如/data/www,就需要告诉 SELinux 这个目录应该使用哪种类型标签。一种方式是用semanage fcontext添加规则:

semanage fcontext -a -t httpd_sys_content_t "/data/www(/.*)?" restorecon -Rv /data/www

这里我把/data/www及其子文件都映射为httpd_sys_content_t,restorecon让这条映射立即生效。很多资料只告诉你怎么用chcon临时改标签,却不说chcon改的是文件当前属性,一旦后续执行restorecon就会被重置。正确做法是先用semanage fcontext定义默认规则,再用restorecon应用,这样系统能记住这条规则。

布尔值是 SELinux 提供给运维人员的可控开关。查看与 SSH 相关的布尔值:

getsebool -a | grep ssh

比如httpd_can_network_connect这个布尔值,决定了 Apache 或 Nginx 进程能否主动发起网络连接。如果应用需要从后端 API 拉取数据,就需要打开它:

setsebool -P httpd_can_network_connect on

-P表示持久化写入配置,不加则重启后失效。使用布尔值是标准做法,优先于编写自定义策略,因为布尔值是系统维护的,升级后依然有效。

改 SSH 端口也会触发 SELinux 问题。如果把 sshd 的监听端口从 22 改成 2222,SELinux 默认只允许 sshd 监听 22 端口,此时需要添加端口映射:

semanage port -a -t ssh_port_t -p tcp 2222

然后在/etc/ssh/sshd_config中设置Port 2222,重启 sshd。如果漏掉semanage port这一步,sshd 会因为 SELinux 限制而启动失败,日志里通常不会直接写"端口被 SELinux 禁止",而是显示"地址已被使用"或"绑定失败"之类的模糊信息,排查起来很费时间。

4.3 用 audit2allow 生成企业常用自定义策略

布尔值解决不了所有问题。当某个业务程序需要访问不在默认策略允许范围内的资源时,就需要写自定义 SELinux 策略模块。这里最推荐的手段是audit2allow,它能把审计日志中的拒绝记录转换成策略模块,省去手写策略文件的学习成本。

我的操作流程是这样的:

  1. 把相关服务故障复现一次,确保 AVC 拒绝记录被写入审计日志。
  2. 查看最近的拒绝记录:
ausearch -m AVC -ts recent
  1. 用audit2why看拒绝原因,确认是类型冲突还是布尔值问题:
audit2why < /var/log/audit/audit.log
  1. 如果确认需要放行,用audit2allow生成模块:
audit2allow -a -M customwebapp semodule -i customwebapp.pp

命令中的-a表示读取所有 AVC 拒绝记录,-M customwebapp会生成customwebapp.pp和customwebapp.te两个文件,然后semodule把编译好的策略模块加载进内核。整个过程不需要手写一行策略语言,适合大多数开发者。

但这里必须强调:audit2allow是辅助工具,不是无脑放行工具。生成的策略往往比实际需要的权限更宽松,因为日志里面可能包含恶意扫描产生的拒绝记录,如果一股脑全放行,等于把攻击行为也合法化了。我在生产环境中的做法是:先停止业务、清空审计日志、启动业务并复现问题,再只针对这一小段时间的 AVC 记录使用audit2allow,生成的模块还要人工打开.te文件检查,确认放行的类型和权限是预期范围内的。

5. 常见问题与排查实录

5.1 SSH 加固后反而连不上,怎么救

SSH 加固最常见的翻车现场,就是我前面提到的"配置没生效就把自己锁在外面"。有一次某同事把PasswordAuthentication改成no,却忘了把公钥放到服务器上,结果新配置生效之后所有密码登录全部被拒,自己也进不去。当时好在还有云平台的管理终端,可以通过管理终端登录到机器恢复配置。

如果管理终端也没有,那就只能祈祷之前有保留 SSH 连接。这也是为什么我反复强调:修改 SSH 配置时,一定要保持一个已经建立的 SSH 会话不要断开。修改完配置后先sshd -t检查语法,再用systemctl reload sshd而不是restart。reload不会断开现有连接,新连接使用新配置,这样即便配置有问题,你还能通过旧连接修复。

遇到 SSH 连通性问题,按下面的顺序排查:

  • 先确认网络层:ping通不通,端口通不通,可以使用telnet或nc测试端口。
  • 再确认服务层:systemctl status sshd查看服务状态,journalctl -u sshd -n 50查看最近日志。
  • 接着看配置文件:sshd -T会输出实际生效的配置,有时候你改了 A 文件,但系统实际用的是 B 文件,只有-T能看出真实结果。
  • 最后看认证过程:journalctl -u sshd里如果有Failed publickey或Connection closed by authenticating user,基本可以断定是公钥或权限问题。

公钥登录失败时,还有一个容易忽略的坑:~/.ssh/authorized_keys文件如果权限是 644,SELinux 在 enforcing 模式下会拒绝 sshd 读取。必须设置成 600,并且用户主目录不能是 777。这些条件都满足后,建议把 SELinux 先切到 permissive 测试一次,如果能登录,再把问题锁定到 SELinux 标签上。

5.2 SELinux 误杀业务服务的排查流程

SELinux 最让运维头大的问题,是"明明日志里没有报错,服务就是起不来"。排查时我有一套固定的流程,可以大幅缩短定位时间。

第一步,看服务状态和主日志。很多服务报的错和真实原因对不上,比如 Nginx 提示Permission denied,但文件权限却完全正常。这时候不要纠结表象,直接看 SELinux 的审计记录。

第二步,查看 AVC 拒绝记录:

ausearch -m AVC -ts recent --start today | tail -50

或者直接打开/var/log/audit/audit.log搜索denied。审计记录里会有tcontext和scontext,前者是目标资源的 SELinux 类型,后者是发起访问的进程类型。看到这两个字段,基本就能判断是进程类型和资源类型不匹配。

第三步,把记录交给audit2why分析,它会给出三个结论中的一种:规则阻止、布尔值关闭或者类型不匹配。如果是布尔值问题,setsebool -P开启即可;如果是类型不匹配,要么用semanage fcontext调整标签,要么用audit2allow生成模块。

第四步,验证问题是否解决后一次性移除多余权限。我发现很多团队遇到 SELinux 问题就临时setenforce 0,然后忘了切回来,等于把 SELinux 缓存成了摆设。解决完业务问题后,请务必将系统切回 enforcing,用后续监控来确认策略是合适的。

5.3 一套可以长期执行的巡检清单

安全加固不是"做完就完事"的一次性工程。系统升级、用户变更、业务上线都可能导致原有配置失效,所以必须把安全巡检固化到日常流程中。我平时会定期检查这些项目:

  • 登录失败日志:lastb | head -50,看是否有异常 IP 反复尝试。
  • 活跃用户:last -a | head -30,确认最近登录记录是否都是本人操作。
  • 新增用户和权限变更:查看/etc/passwd中最近修改的账号,检查 sudoers 文件内容。
  • 监听端口变化:ss -lntup,对比基线端口清单,发现新开放端口要追问来源。
  • SELinux 审计日志量:audit.log如果暴涨,说明有进程一直在被拒绝访问,优先排查是否被入侵后产生的新行为。
  • 定时任务:检查/etc/crontab、/var/spool/cron/下有没有新增条目,警惕挖矿或持久化后门。
  • 系统更新:为内核和关键安全补丁留出维护窗口,不要把生产环境长期停在旧版本上。

这些检查项不需要全部写成脚本,前期可以先靠人工巡检,积累几周的基线数据后,再写成 cron 脚本或接入监控平台。比如把lastb的结果做数量统计,超过阈值就告警;把ss -lntup的输出和基线文件做 diff,有差异就通知运维确认。有了基线就能尽早发现问题,安全监控的意义不是出事后的追查,而是出事之前的察觉。

我个人在实际操作中的体会是:Linux 安全加固的价值,不在于某一项配置有多强,而在于每层防护都能正常运转。SSH 防爆破挡掉了外部的高频扫描,账号权限收敛减少内部误操作和横向移动的可乘之机,SELinux 兜住那些"想不到"的越权访问。这三层只要都活着,哪怕某一层被绕过了,后面还有东西能拦住。如果你现在手头有一台还没加固的服务器,不妨从修改 sshd 配置和查看 SELinux 状态开始,这两个动作做完,最危险的暴露面就已经收缩了一大半。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询