☰
Linux系统安全加固实战:从弱口令爆破到挖矿木马应急的完整防御链
2026/10/18 13:07:46 网站建设 项目流程

开篇:一次事故让我决定重新审视 Linux 系统安全

前阵子帮朋友排查一台服务器的“卡顿问题”,登录上去看了一眼负载,直接吓一跳——load average飙到 20 多,top里一个陌生进程占着 300% 的 CPU。再细查网络连接,发现这台机器正疯狂向外发包,标准的挖矿木马特征。问了下情况,服务器部署了一个很老的应用,开放了 22 端口,密码设置还是“Admin@123”这种级别。从日志里看,对方用国外 IP 爆破了整整两天,最后成功登录,然后下木马、提权、清理痕迹,一套流程行云流水。

这台设备最终拆掉重装,但留下的教训很深刻:Linux 系统安全不是某一个单独的动作,而是从账号、权限、网络、日志到应急响应的一整条防御链。任何一环的疏漏,都可能让前面的努力全部白费。

这篇文章我想从一线运维的视角,把 Linux 系统安全这件事完整地拆一遍。内容覆盖威胁模型、账号加固、最小权限、网络防线、日志审计、应急响应和日常运维,适合三种人看:刚接触 Linux 运维的新人、被安全问题困扰过的开发者、以及准备给现有服务器做一遍安全体检的同行。每一段都是实操经验,命令直接能给,思路讲透,你也好判断哪些方案真正适合自己的环境。

1. 先想清楚对手是谁:安全加固的优先级排序

1.1 常见的攻击路径与威胁模型

很多人一提安全就紧张,恨不得把所有加固手段全怼上去。但实际做安全的第一步,不是装各种工具,而是先想清楚一个问题:我的机器正面临哪些威胁?

从实际入侵案例来看,Linux 服务器被攻破的路径,绝大多数绕不开这几类。

第一类是弱口令爆破。这占了所有入侵事件的很大比例,尤其是开放 SSH 端口、root 密码简单、或者用了常见弱密码的机器。攻击者用自动化工具,对常用用户名加字典密码做批量尝试,24 小时不间断。一台防御空白的主机,被爆破成功只是时间问题。

第二类是未修复的已知漏洞。比如老版本的服务程序存在远程代码执行漏洞,攻击者扫描到开放端口后,用公开的 PoC(概念验证代码)直接打进来,甚至不需要任何凭据。这类攻击对老旧环境杀伤力最大,因为补丁更新往往是最容易被忽略的环节。

第三类是配置不当导致的暴露。比如把数据库端口裸奔到公网,Redis 服务未设密码且以 root 权限运行,Web 目录被写入权限放得太宽——这些都不是“入侵”,而是大门自己敞开着,被扫描器像是逛街一样看见,顺手就进来了。

第四类是供应链与第三方风险。上游镜像被污染、安装包被篡改,或者随便从不明渠道拉了一个二进制脚本扔到服务器上执行,都可能直接给攻击者铺好了路。

1.2 按成本收益给加固动作排个序

理清威胁模型之后,加固的优先级就很清晰了。我给自己的原则是八个字:先堵口子,再补细节。

最高优先级是阻断常见攻击入口:禁用 SSH 密码登录、关闭不必要的端口、修复高危漏洞、收敛对外开放的服务面。这些动作成本低、见效快,能挡住 90% 以上的自动化攻击。

第二优先级是做好权限控制和隔离:非 root 运行服务、分权管理、最小化授权文件系统访问。即使攻击者拿到了某个账户,也走不远、提不了权。

第三优先级才是日志、审计、入侵检测这些“看得见”的能力。它们是纵深防御的下一层,用于发现已经绕过外层防护的入侵者,并及时止损。

有人一上来就研究内核加固、容器逃逸防护,但自己的 SSH 还是密码登录,这等于先把后门焊死,却把前门敞开。安全建设讲究性价比,先把基础工作做完,再往深处走。

2. 账户与认证:堵住最容易被攻破的大门

2.1 密码策略的硬性配置

SSH 的弱口令爆破是我见过最频繁的攻击方式。很多机器被拿下的第一步,就是 root 或某个普通用户的密码被猜中。密码策略虽然看似老生常谈,但落地时依然有很多机器漏配。

先看密码老化策略,核心配置文件是/etc/login.defs。几个关键参数需要确认:

PASS_MAX_DAYS 90 PASS_MIN_DAYS 10 PASS_WARN_AGE 7

PASS_MAX_DAYS控制密码最长使用天数,PASS_MIN_DAYS限制最短修改间隔(防止用户改了立刻改回原密码),PASS_WARN_AGE是到期前提醒天数。修改文件只对后续新建用户生效,老用户需要再执行一次chage -M 90 用户名单独调整。

再配合密码复杂度,可以用 PAM 的pwquality模块。在/etc/pam.d/system-auth(CentOS/RHEL 系)或/etc/pam.d/common-password(Debian/Ubuntu 系)中加入:

password requisite pam_pwquality.so try_first_pass local_users_only retry=3 minlen=12 dcredit=-1 ucredit=-1 lcredit=-1 ocredit=-1

这段配置要求密码最短 12 位,并且必须同时包含数字、大写字母、小写字母和特殊字符。这里的dcredit、ucredit、lcredit、ocredit是负值,意思是“至少需要多少个对应类型字符”,负号表示强制要求,正号则相反。

2.2 SSH 服务加固的完整清单

对公网服务器来说,SSH 配置加固是安全投入产出比最高的一项工作。下面是/etc/ssh/sshd_config里我实际在用的关键配置项,每一条都能讲出原因:

# 禁用 root 直接登录,日常运维用普通用户 + sudo 提权 PermitRootLogin no # 只允许密钥认证,关闭密码认证 PasswordAuthentication no PubkeyAuthentication yes # 修改默认端口,能过滤掉大部分扫描噪音 Port 22222 # 严格限制允许登录的用户 AllowUsers admin ops # 控制连接尝试次数和登录超时 MaxAuthTries 3 LoginGraceTime 30

禁用 root 登录,切换到普通用户加 sudo,这是我最推荐的做法。这样即便某个用户密码泄露,攻击者拿到的也不是最高权限,后续 sudo 的密码验证还能再拦一道。修改 SSH 端口的效果我实测下来很明显——扫描日志里针对 22 端的随机爆破瞬间少了九成,属于“低成本、降噪音”的神器。

生成密钥对时,我习惯用ed25519算法代替rsa:

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

这个密钥更短、更快,安全性也足够。把公钥放到服务器的~/.ssh/authorized_keys,注意这个文件的权限必须严格控制:

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

如果权限设置过宽,sshd 会直接拒绝使用这个密钥文件,这是很多人配置密钥登录失败的原因。改完sshd_config之后,务必先用一条测试命令确认配置正确再重载服务:

sshd -t && systemctl reload sshd

2.3 用 Fail2ban 做自动化防线

即使关闭了密码登录,只要服务器有对外开放的服务,依然会收到无脑爆破请求。Fail2ban 的作用是监控日志中的失败尝试,达到阈值后直接封锁来源 IP。我用它来防护 SSH 和 Web 服务,效果非常稳定。

以 Ubuntu 下阻止 SSH 爆破为例,在/etc/fail2ban/jail.local里配置:

[sshd] enabled = true port = ssh filter = sshd logpath = /var/log/auth.log maxretry = 5 bantime = 3600

maxretry 5表示半小时内 5 次失败即触发,bantime 3600表示封禁一小时。如果你改了 SSH 端口,port那里要填实际的端口号,logpath也要根据系统日志路径调整(RHEL 系是/var/log/secure)。

一个值得注意的坑:Fail2ban 的封禁默认走 iptables,如果服务器本身用的防火墙规则管理工具是 nftables,两者可能冲突,导致封禁规则不生效。建议在部署前确认防火墙体系,统一用同一套规则管理方案,别让 Fail2ban 和自己的防火墙打架。

3. 最小权限原则:账号、文件和 sudo 的控制之道

3.1 文件权限与特殊位的基础检查

Linux 系统安全里有一条铁律:给每个主体最小够用的权限。这个原则落到文件系统层,就是权限位和特殊位的合理配置。

我接手一台新服务器,通常会先用两条命令探查权限异常。第一条是找出所有 SUID 程序:

find / -perm -4000 -type f 2>/dev/null

SUID 位意味着普通用户执行这个文件时,能以文件属主的身份运行。如果某个不知名程序带上了 SUID,一旦存在漏洞,就可能被用来提权。系统自带的sudo、passwd这类程序带 SUID 是正常的,但凡是出现在奇奇怪怪路径下的,就要立刻查清来源。

第二条是检查全局可写的目录和文件:

find / -perm -0002 -type d 2>/dev/null find / -perm -0002 -type f 2>/dev/null

全局可写目录配合sticky bit才是安全的组合。/tmp目录默认带 Sticky Bit(目录权限显示为drwxrwxrwt),意味着每个用户只能删除自己的文件。但其他目录如果也全局可写且没有 sticky bit,就相当于允许任何人删别人的文件,必须引起重视。

3.2 sudo:颗粒化授权比给 root 密码更靠谱

团队协作时,我不建议共享 root 密码。更可控的方式是给运维人员分配普通账号,把需要的管理命令放进 sudo 白名单。

用visudo打开/etc/sudoers,配置一段规则:

ops ALL=(ALL) /usr/bin/systemctl restart nginx, /usr/bin/systemctl reload nginx

这条规则赋予ops用户重启和重载 nginx 的权限,其他管理命令一概没有。对需要完整管理权限的同事,也不要直接给NOPASSWD选项。我见过一个生产事故:某员工配置了 sudo NOPASSWD,结果脚本里一个命令写错,直接把错误的运行参数传给了服务进程,因为免密,没有任何二次确认的环节,误操作在几秒内就完成了。这类风险防不胜防,所以我的习惯是要求 sudo 必须带密码验证,哪怕麻烦点,也值得。

visudo本身也是保命工具——它在保存时会校验语法,写错了不会让你存进去,避免搞坏 sudoers 导致所有提权失效的尴尬处境。

3.3 用系统能力和服务隔离收窄权限边界

现代 Linux 还提供了一个比普通文件权限更细的权限控制层:Capabilities。它能把 root 的超级权限拆分成几十个小能力,按需赋予进程。

举个实际例子:nginx 的master进程如果需要监听 80 端口,完全不需要整个 root 身份,只需要CAP_NET_BIND_SERVICE这一项能力。可以通过配置systemd单元文件的AmbientCapabilities=参数来赋予,或者在容器场景的配置文件中设置。

对于常驻服务,我更推荐用 systemd 的沙箱参数做一层防护:

[Service] NoNewPrivileges=true PrivateTmp=true ProtectSystem=strict ProtectHome=true ReadOnlyPaths=/

NoNewPrivileges=true是关键——它禁止该服务进程及其子进程通过exec获得任何新的特权,即使二进制文件设置了 SUID 位也没用。这能有效阻断一部分提权攻击链。加这些参数前要仔细测试,比如ProtectSystem=strict下服务无法写/usr和/etc,如果应用需要写这些目录,服务会直接启动失败。

4. 网络与服务:把暴露面收敛到最小

4.1 服务的精简与端口收敛

攻击者想打进来,必须先找到一个通向你服务的网络入口。所以服务越少、开放端口越少,攻击面就越小。我从多年前就养成了一个习惯:新服务器安装系统时选择最小化安装,不装图形界面、不装用不到的软件包。系统跑起来后,再查一遍当前监听的端口:

ss -tulnp

看到的结果里,只保留业务必需的服务。任何不清楚是什么、谁在用、为什么监听的端口,一律查清楚然后停掉。有些应用安装时自动开启额外服务,这是最常见的“隐形暴露面”来源。

对于必须在公网提供服务的应用,也要细化访问控制。比如 MySQL 这类数据库通常只需要内网访问,直接在监听地址上做限制,让它只绑内网 IP 就足够:

bind-address = 192.168.1.10

把服务绑定到内网 IP 而不是0.0.0.0,这一步很多人会忽略,但它往往是防住数据库被拖走的最后一道锁。

4.2 防火墙策略:从默认放行到默认拒绝

防火墙的原则可以浓缩成一句话:默认拒绝,显式放行。我见过不少配置,防火墙开着,但规则是开放 1-65535 端口,这等同于没开。

以 nftables 为例,一套简洁的策略框架可以这么搭:

nft add table inet filter nft add chain inet filter input { type filter hook input priority 0 ; policy drop ; } nft add chain inet filter forward { type filter hook forward priority 0 ; policy drop ; } nft add rule inet filter input ct state established,related accept nft add rule inet filter input iif lo accept nft add rule inet filter input tcp dport 22222 accept nft add rule inet filter input tcp dport 80 accept nft add rule inet filter input tcp dport 443 accept

这段规则的含义是:默认丢弃所有入站和转发流量,只放行进站口的回程连接、本机回环、SSH(22222 端口)、HTTP(80)和 HTTPS(443)。established,related这条很重要,否则服务器主动访问外网时回包会被拦截,业务直接受影响。

Debian/Ubuntu 用户用ufw会更简单直观:

ufw default deny incoming ufw default allow outgoing ufw allow 22222/tcp ufw allow 80/tcp ufw allow 443/tcp ufw enable

在修改生产环境防火墙规则前,我会给自己留一条后路:设置一个临时 cron 任务,5 分钟后关掉防火墙,或者打印当前规则保存好。万一写错规则把自己锁在外面,还有一个补救窗口。这个习惯救过我两次,强烈推荐。

4.3 内核级防护:SELinux 与 AppArmor

SELinux 和 AppArmor 是 Linux 的强制访问控制机制,它们是文件权限之上的另一层防护:即使进程以 root 运行,也只能访问预定义的系统资源和文件。

很多运维朋友对 SELinux 的第一反应是“关了它”,因为开启后经常遇到权限莫名其妙的报错。但我要说,SELinux 并不是洪水猛兽,它的报错恰恰是在提示你,某个进程的行为异常了或是上下文配置错了。对于标准服务,用对了上下文,或者设置合适的布尔值,运行毫无问题。

以 nginx 为例,在 RHEL 系系统上,如果 SELinux 处于 enforcing 模式,可能会阻止 nginx 访问自定义的日志目录或非标准目录。这类问题多数可以通过设置布尔值或修改上下文解决:

setsebool -P httpd_can_network_connect 1 semanage fcontext -a -t httpd_sys_content_t "/var/www/html(/.*)?" restorecon -Rv /var/www/html

Ubuntu 等 Debian 系默认用 AppArmor,配置侧重点略有差异,但思路一致:让每个服务跑在受限的 profile 里。

从安全收益角度说,强制访问控制拦截的是“即使被攻破也拿不到关键数据”的第二层防线。我倾向于在条件允许时开启,不要嫌麻烦。刚开始切换时可以先放宽松模式(SELinux 的 permissive)观察日志,确认无误后再切到 enforcing,这样既安全又平滑。

5. 日志、审计与文件完整性:看得见的防护才可靠

5.1 日志的集中收集与日常阅读

没有日志的安全是无从谈起的。Linux 系统的认证和权限日志,在 RHEL/CentOS 系存于/var/log/secure,Debian/Ubuntu 系存于/var/log/auth.log。后者记录了所有 SSH 登录尝试,包括成功和失败的 IP、用户、时间。

每天花两分钟扫一眼认证日志,是成本最低的安全巡检方式。比如用一条命令统计今天被尝试爆破的次数:

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

多台服务器的情况下,逐个登录查看不现实,建议搭建集中日志服务。用 rsyslog 把各主机的认证日志推送到一台日志收集机上,配合简单的grep脚本就能做到异常告警。日志集中之后,还要注意两点:一是收集机的日志目录权限必须收紧,二是日志要定期归档,别让日志文件无限膨胀。

5.2 logrotate:防止日志撑爆磁盘

这个问题太常见了:默认配置下系统日志会不断增长,如果没人管,某天一个晚上就能把磁盘写满。而磁盘满在 Linux 上带来的后果,比想象中严重得多——服务可能挂掉,而且你能看到的报错信息少得可怜。

好在主流发行版默认带 logrotate。关键是要检查配置文件里的保留周期和压缩选项。以 RHEL 系/etc/logrotate.conf为例:

weekly rotate 4 create dateext compress include /etc/logrotate.d

含义是每周轮转一次、保留四周、轮转后压缩。如果你有日志增长速度特别快的服务,比如 nginx 访问日志,在/etc/logrotate.d/nginx里单独设置:

/var/log/nginx/*.log { daily rotate 14 compress missingok notifempty sharedscripts postrotate /bin/kill -USR1 `cat /var/run/nginx.pid 2>/dev/null` 2>/dev/null || true endscript }

daily和rotate 14表示按天轮转、保留两周。轮转后向 nginx 发信号让重新打开日志文件,否则 nginx 还会往已删掉的文件里写内容。

5.3 auditd:关键操作不留死角

如果想对文件访问、系统调用做更细致的审计,可以用 auditd。它能在谁、什么时间、访问了哪个文件这些层面留下记录。在安全事件追溯时,这是决定性的证据。

常用的审计规则配置示例:

auditctl -w /etc/passwd -p wa -k user-file auditctl -w /etc/sudoers -p wa -k sudoers-change auditctl -a always,exit -F arch=b64 -S execve -k command-log

前两条监控/etc/passwd和/etc/sudoers的写入和属性变更,第三条记录所有命令执行。执行后日志在/var/log/audit/audit.log,可以通过ausearch -k user-file按规则标识查询。

注意,写审计规则要控制范围,别把execve全量记录开在繁忙的生产机器上,日志增长速度会让你怀疑人生。最好只针对敏感可执行目录和安全关键操作做审计。

5.4 文件完整性校验:AIDE

攻击者在拿下系统后,通常会篡改系统二进制文件,植入后门。靠人工很难发现这类异常,这时需要一个文件完整性校验工具,最常用的是 AIDE。

安装后先初始化数据库:

aide --init mv /var/lib/aide/aide.db.new.gz /var/lib/aide/aide.db.gz

之后每次做基线检查,对比当前状态和数据库是否有变化:

aide --check

有变更会输出差异报告,正常合理更新产生的差异我们确认后重新初始化数据库即可。部署 AIDE 要注意一点:数据库本身要存储在安全位置,最好只读挂载或者定时备份到异机。否则攻击者改了文件之后也改数据库,完整性校验就形同虚设。

我建议把 AIDE 检查放到 cron 里每周跑一次,输出结果定时发到日志中心。虽然有些繁琐,但一旦服务器被篡改,这是第一个能提醒你的设备。

6. 应急响应:发现问题后的黄金半小时

6.1 先判断:系统到底有没有被入侵

事前防护做得再好,也不能保证 100% 不出事。应急响应是安全体系里兜底的部分,它的启动时机是“发现可疑迹象”。最常见的迹象包括:

  • 负载突然飙升,top里出现名字古怪的进程,CPU 占用极高
  • 外联连接异常增多,尤其高频连接境外或可疑 IP
  • 系统出现未授权的新用户,或 root 用户被克隆
  • 登录日志显示大量失败尝试之后出现成功的记录
  • 定时任务(cron)出现看不懂的条目
  • 关键文件被修改,比如/.ssh/authorized_keys中出现陌生公钥

发现可疑迹象,第一反应不要慌着重启或者杀进程。先保现场,命令一条条查。

6.2 常用的排查命令与检查清单

我建议按下面这个顺序开展排查,每一条命令都有明确的意图:

先看用户和登录记录:

# 列出所有用户及最近登录 awk -F: '$3==0{print $1}' /etc/passwd last | head -20

root(uid 0)账户如果出现多个,说明被做了手脚。last看谁的登录记录异常,比如凌晨三点来自陌生 IP。

看进程和网络:

# 按 CPU 排序,找陌生进程 top -b -n 1 | head -30 # 查看所有外连连接 ss -tunap | grep ESTABLISHED

重点排查ESTABLISHED状态且目标端口非业务端口的外连,比如主动连接 443、常见的挖矿矿池端口等。

查定时任务和自启动项:

crontab -l cat /etc/crontab ls -la /etc/cron.d/ systemctl list-unit-files --type=service --state=enabled

木马常驻的方式通常是定时任务或 systemd 服务。把所有可疑条目列出来,一行一行核对是否是自己部署过的任务。

检查 LD_PRELOAD 和后门文件:

echo $LD_PRELOAD find / -name "*.so" -newer /boot 2>/dev/null

偷懒又有效的做法是用chkrootkit和rkhunter自动扫描一遍已知的后门特征。虽然这类工具不能发现所有新型后门,但对常见样本检出率很高,应急开局用它快速筛查是合理的。

在处理方式上有几个原则要遵守。如果确认被入侵,先从网络层面把机器隔离——拔掉公网网卡或直接更新防火墙规则丢弃所有入站流量。这个动作比在机器上杀毒更优先,可以阻止攻击者继续外传数据或横向渗透。然后保存好当前系统状态,包括内存镜像、进程列表、网络连接等证据,再考虑重装或恢复。

6.3 一个实用的紧急封禁命令

应急时最常用的动作是封掉攻击来源 IP。用 firewalld 的话:

firewall-cmd --permanent --add-rich-rule='rule family=ipv4 source address=1.2.3.4 reject' firewall-cmd --reload

nftables 的话:

nft add rule inet filter input ip saddr 1.2.3.4 drop

这类规则需要持久化保存,否则重启后失效。nftables 执行nft list ruleset > /etc/nftables.conf保存规则集即可。

7. 常见问题速查表与避坑心得

做安全加固时踩过的坑,我整理成了一张速查表,基本覆盖了常见生产事故的起点:

问题现象可能原因解决方案
修改 SSH 配置后无法登录sshd_config 语法错误或密钥权限不对本地执行sshd -t验证;确认 authorized_keys 为 600 权限
开启 SELinux 后服务报权限错误文件上下文或布尔值设置不当先用ausearch查拒绝记录,再setsebool或semanage调整
Fail2ban 封禁不生效与系统防火墙体系冲突,或日志路径错误确认使用同套防火墙;ls 检查 logpath 是否正确
服务日志增长过快撑爆磁盘未配置 logrotate 或轮转周期过长为服务单独配置 logrotate,缩短轮转周期并开启压缩
改完 sudoers 后 sudo 全部失效sudoers 语法错误用 root 的另一个登录会话执行pkexec visudo修复
登录日志显示大量爆破但未成功密码策略已生效,但攻击仍在尝试启用 Fail2ban 封禁来源 IP,或考虑改用密钥认证
服务器被植入挖矿木马,进程杀掉后反复出现存在守护进程或定时任务在拉取按 6.2 清单排查 cron、systemd、LD_PRELOAD 后彻底清理

治本的建议仍然是:加固前做好备份,加固后做完整功能回归测试。安全配置改动引发可用性问题,这在生产环境并不少见。我自己比较喜欢的一套节奏是:先在测试环境验证配置,再在生产环境分批灰度,每个改动都能回滚。

另外,听说有些公司会在安全加固后直接跑一轮压测和故障演练,专门验证“加固是否影响业务、应急脚本是否可用”。这种思路我非常认同——安全配置不该是写在那里的摆设,它必须被验证过,才算真正生效。

8. 安全加固的最后一公里:持续维护才是关键

有人说 Linux 安全是“一锤子买卖”,配好了就能一劳永逸。这个想法非常危险。安全加固是持续过程,资产的更新、业务的变化、新漏洞的公开,都会打破之前好不容易建立的平衡。

我的日常做法是三类动作并行:每周检查系统更新并评估是否需要打补丁;每月做一次日志和文件完整性检查;每季度对全部开放端口、账号权限、sudo 规则做一遍整体审计。同时关注主流发行版的漏洞公告邮件,让安全信息保持新鲜。

在最后,分享一个我从实战中坚持下来的小习惯:每天早晨到工位后,花一分钟快速翻一下认证日志和关键服务日志,用固定的脚本输出前一天的异常摘要。看得久了,你会对这台服务器的“正常”画像形成直觉,而直觉在安全运维里,往往是最开始发现异常的信号。

Linux 系统安全没有终点,更多的是“防御、发现、响应、恢复”循环里不断变强。希望这篇从实战里长出来的文章,能帮你把这个循环跑起来,让每一台机器都少一点裸奔的风险。

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

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

立即咨询