我做了很多年Linux运维,第一次服务器被入侵时,我连日志在哪看都不知道,只会在网上搜“怎么杀毒”。后来踩过的坑多了才明白,Linux系统安全根本不是一两个工具能解决的,而是一整套贯穿账户、权限、网络、日志、应急响应和备份恢复的工程习惯。很多人觉得“Linux天生安全”,装上防火墙、设个强密码就万事大吉,实际上真正决定一台机器安全性的,是你对基线配置的态度,以及平时有没有反复演练过“机器被搞了之后该怎么办”。
这篇文章我会从攻击面分析、基础加固、工具策略、应急响应到常见坑位,把我实际运维中验证过的方法和命令完整写出来。无论是刚入行的运维、自己玩云主机的开发者,还是需要在公司内部建立安全基线的工程师,都能照着操作。我不会讲特别玄的高阶概念,重点是你今天就能在服务器上跑起来的步骤。
1. Linux系统安全隐患从哪里来
1.1 攻击面可能超出你的想象
很多人的第一反应是:Linux服务器不就是开了22、80、443这些端口吗,攻击面能有多大?实际上攻击面远不止网络端口。
先说账户问题。弱口令、空口令、闲置账户、共享账户,这些都是最常见的入口。我见过不少服务器,运维离职了账户也不删,密码还写在办公桌便利贴上,这类机器基本就是给攻击者递钥匙。还有个容易被忽略的点是UID为0的非root账户,这种账户拥有和root完全相同的权限,很多人建账户时没注意,等于给自己留了一个隐形后门。
再看服务和软件。系统安装时默认带了一堆用不上的服务,每个服务都可能存在历史漏洞。更麻烦的是依赖链,你装了某个应用,它自动拉来几十个依赖包,只要其中一个包被投毒或者版本过旧,整台机器就被牵着走了。第三方软件源也不可靠,如果你把不明来源的源混进生产机器,风险完全不可控。
内部误操作同样不能忽视。权限给得过大、一键执行了别人发来的脚本、错误地修改了配置文件,这些造成的后果不比外部攻击轻。攻击者不一定从网络进来,也可能就是通过一次内部人员的失误拿到了入口。安全不是“防黑客”,是防一切可能导致失控的行为。
1.2 常见攻击路径与真实场景
攻击者的常规思路大致是这样:扫描端口 -> 识别服务 -> 尝试弱口令或已知漏洞 -> 获取执行权限 -> 本地提权 -> 植入持久化后门。听起来很复杂,实际上很多攻击都是自动化完成的,脚本根本不管你机器上跑的是什么,就是一个网段一个网段地扫。
我遇到过一台服务器被拿去挖矿,查了半天发现不是边界被突破,而是Redis服务直接暴露在公网,端口没改、密码没设、还以root权限运行。攻击者用未授权访问连上,通过写入定时任务把挖矿脚本拉下来执行。这类问题在一些开发者自建环境中极其常见,因为大家关注的是业务功能能不能跑,很少从“别人能不能连上我”这个角度审视。
另一种高发路径是Web应用漏洞。上传点没做限制、参数校验不严、使用了带有已知漏洞的旧组件,攻击者通过这些漏洞上传WebShell或者反弹Shell,拿到执行权限后再尝试提权。只要应用层有一个破口,下面系统加固得再好也会被绕过去。所以把系统安全做好,不代表应用可以随意裸奔,两者是互为补充的。
持久化是攻击者站稳脚跟的关键手段。常见的持久化方式包括:写入用户或系统的crontab、修改开机启动项、往authorized_keys里塞公钥、替换常用二进制文件、通过动态链接器预加载恶意库。这些都说明,安全的功夫要在平时做足,等发现问题再补救往往已经迟了。
2. 基础加固:先把入门功课做扎实
2.1 权限与账户管理的关键细节
账户管理是Linux安全的第一道闸门,也是投入产出比最高的加固点。第一步是梳理现有账户,看看系统里到底有哪些能登录的账户:
# 查看所有拥有登录Shell的普通用户 awk -F: '$3 >= 1000 || $7 ~ /(bash|sh|zsh)$/ {print $1, $3, $7}' /etc/passwd我习惯把UID 0用户和具备Shell的用户单独拉出来核对,发现不需要的账户直接用usermod -L 用户名锁定,而不是只删掉密码。有些软件会依赖特定账户存在,直接userdel可能引发连锁问题。
日常运维尽量避免直接用root登录,而是通过普通用户加sudo。建用户时给最小可用的Shell和目录,不要图省事复制root的行为:
# 创建普通用户并加入sudo组 sudo useradd -m -s /bin/bash ops sudo passwd ops sudo usermod -aG sudo opssudo权限不要一刀切给ALL。一个只负责重启Nginx的人,只需要允许他执行对应的systemctl命令就够了。在/etc/sudoers.d/下按用户拆分规则,比直接改/etc/sudoers更清晰,也更安全。修改之前一定用visudo -c校验语法,否则可能把自己锁在root之外。
密码策略同样不能省。在/etc/security/pwquality.conf里可以设置最小长度、必须包含字符种类等要求:
minlen = 14 dcredit = -1 ucredit = -1 lcredit = -1 ocredit = -1再配合PAM模块,让策略真正生效。Debian系在/etc/pam.d/common-password,RHEL系在/etc/pam.d/system-auth,通常加上这一行:
password requisite pam_pwquality.so retry=3文件权限保持发行版默认就好,不要为了“看起来更安全”随意改。比如/etc/shadow默认一般是640 root:shadow,你把权限改成600或者000,反而可能影响某些依赖shadow组读取的工具。真正的保护是定期检查关键文件有没有被动过,而不是把它们锁死。
2.2 SSH远程访问的安全实践
SSH是绝大多数Linux服务器唯一的远程管理入口,也是最常被扫描爆破的对象。我见过只设了8位小写字母的密码,挂到公网不到一小时就被暴力破解日志塞满。别迷信复杂密码能扛多久,密钥认证才是正道。
修改/etc/ssh/sshd_config时,我建议至少包含这几项:
PermitRootLogin no PasswordAuthentication no PubkeyAuthentication yes MaxAuthTries 3 LoginGraceTime 30 AllowUsers ops把端口换到高位能降低一部分脚本扫描的噪声,但不能依赖它作为安全手段,真正挡住爆破的是禁用密码登录。生成密钥时用Ed25519算法,安全性优于RSA且密钥更短:
ssh-keygen -t ed25519 -a 100 -C "ops@myserver" ssh-copy-id ops@server-ip把公钥放到服务器的~/.ssh/authorized_keys后,立刻收紧目录权限:
chmod 700 ~/.ssh chmod 600 ~/.ssh/authorized_keys修改sshd配置后,先校验再重载:
sudo sshd -t sudo systemctl reload sshd这里有个特别重要的教训:远程改SSH配置时,千万不要改完就断开当前连接。我当时犯过的错误是改完端口忘了在防火墙放行新端口,然后退出旧会话,结果把自己锁在机器外面,只能通过云控制台VNC进去救。现在我的习惯是用tmux开一个会话,改完配置后开一条新SSH连接测试,确认新连接能登录,再退出旧会话。如果新连接失败,还能回到旧会话里回滚配置。
Fail2ban可以装,但在密钥认证为主的环境里它更多是减少日志噪声,不是核心防线。如果确实要装,记得把办公网段或管理网段加入ignoreip,不然误封自己人很难看。
2.3 关键文件与目录的权限保护
对关键文件设置不可变属性,是防止被粗心脚本或半吊子恶意程序修改的简单手段:
sudo chattr +i /etc/ssh/sshd_config sudo chattr +i /etc/passwd sudo chattr +i /etc/shadow但要清醒一点:chattr +i挡得住普通用户和很多自动化脚本,挡不住已经拿到root的攻击者,因为root可以先chattr -i再改。它更适合作为一种防误操作机制,比如防止自己不留意执行了覆盖式脚本。
/tmp目录是所有用户都能写入的地方,也是恶意程序最喜欢藏身的位置之一。很多服务器没有单独为 /tmp 分区,直接在根分区上读写,这也为攻击者提供了可乘之机。在/etc/fstab中给 /tmp 加上安全挂载选项:
tmpfs /tmp tmpfs defaults,noexec,nosuid,nodev,mode=1777 0 0需要提醒的是,noexec会阻止在/tmp下直接执行二进制文件,但有些软件安装时需要从临时目录执行脚本,切到生产环境前一定要先验证业务是否受影响。如果 /tmp 放在普通分区而不是tmpfs,可以考虑把它单独划成一个分区,而不是依赖根分区的容量。
其他关键目录,比如/etc,我的建议是配合定期备份和完整性校验工具来保护,而不是全局设置不可变属性。全局不可变会导致日常更新软件包、修改配置时到处碰壁,反而降低维护效率,成本不值当。
3. 安全工具选用与策略配置
3.1 文件完整性校验与主机审计
机器被入侵后,攻击者经常改配置文件、替换二进制、添加账户。如果你有文件完整性校验基线,这些改动可以很快暴露。我常用的工具是AIDE,它会对关键目录的文件属性生成数据库,之后定期比对。
第一次使用前,先确认系统是刚安装完的干净状态,否则数据库里记录的就是“已经被改过的状态”,没有参考价值。安装后初始化:
sudo apt install aide sudo aideinit初始化完成后把生成的数据库放到预期位置,不同发行版路径略有差异,按你系统上的提示操作即可。手动检查时运行:
sudo aide --check有变化时AIDE会输出变化部分的详细信息。第一次跑大概率会有一堆误报,因为编译软件、更新缓存之类的正常操作也会改变文件。你需要人工核对之后更新基线:
sudo aide --update更新前确认这些变更是预期内的,不然等于把脏数据写进黄金标准里。
另一个值得跑的审计工具是Lynis。它会对整个系统做一次全面体检,包括软件包、内核参数、认证方式、网络配置等:
sudo apt install lynis sudo lynis audit system输出里会给出一个Hardening Index和一个建议清单。我第一次跑完只盯着分数看,后来才发现真正有价值的是建议列表,例如“没有设置日志远程备份”“存在无密码账户”这类细节。建议一条条过,确认确实不需要再忽略。
3.2 防火墙规则的正确写法
防火墙是网络层面的基础防线,很多人一上来就把默认策略设成DROP,结果导致DNS解析失败、服务间互通异常,最后又手忙脚乱地全放行。正确做法是按照业务需要的端口和来源逐条放行,并且提前做好规则备份。
以iptables为例,一条典型规则集长这样:
# 备份现有规则 iptables-save > /root/iptables.bak # 设置默认策略 iptables -P INPUT DROP iptables -P FORWARD DROP iptables -P OUTPUT ACCEPT # 回环 iptables -A INPUT -i lo -j ACCEPT # 已建立连接的数据包 iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT # 放行管理网段的SSH iptables -A INPUT -s 192.0.2.0/24 -p tcp --dport 22 -j ACCEPT # 放行80和443 iptables -A INPUT -p tcp --dport 80 -j ACCEPT iptables -A INPUT -p tcp --dport 443 -j ACCEPT # 其余全部记录日志后丢掉 iptables -A INPUT -j LOG --log-prefix "IPT_DROP: " --log-level 4 iptables -A INPUT -j DROP规则顺序很重要,iptables会从上到下逐条匹配,第一条匹配就生效。所以放行规则要放在DROP之前,如果你把DROP放在列表最前面,后面的放行规则全部无效。使用iptables -I INPUT ...可以在最前面插入规则,但插入前更要确认规则顺序没问题。
追求稳定的话,尽量用iptables-persistent或firewalld这类持久化机制,不要只在命令行敲几条临时规则,重启机器就没了。Ubuntu下的做法:
sudo apt install iptables-persistent sudo netfilter-persistent savefirewalld对企业用户更友好,规则可以按zone管理。如果需要放行指定网段访问某个端口,一个rich-rule就够了:
firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="192.0.2.0/24" port port="8080" protocol="tcp" accept' firewall-cmd --reload远程操作防火墙时,我的习惯是先用iptables-save备份,然后把规则变更写进脚本,在脚本里加一条延时回滚逻辑,比如nohup bash -c 'sleep 300; iptables-restore < /root/iptables.bak' &,确认没问题后再取消这个回滚任务。这样即使新规则把自己锁在外面,过几分钟规则也会自动恢复,不会直接失联。
3.3 日志审计与异常发现
日志是最容易被忽视但又最关键的证据源。攻击者在系统中留下的几乎所有痕迹,都能在日志里找到对应线索,前提是你知道去哪里看。
不同发行版日志路径不一样,我列一个速查表:
| 用途 | Debian/Ubuntu | RHEL/CentOS |
|---|---|---|
| 登录认证 | /var/log/auth.log | /var/log/secure |
| 系统常规 | /var/log/syslog | /var/log/messages |
| 定时任务 | /var/log/cron | /var/log/cron |
journalctl在systemd系统上也能查,比如查看今天的SSH登录情况:
journalctl _COMM=sshd --since today为了监控关键文件的变化,建议启用auditd:
sudo apt install auditd sudo systemctl enable --now auditd添加审计规则:
auditctl -w /etc/passwd -p wa -k passwd_changes auditctl -w /etc/shadow -p wa -k shadow_changes auditctl -w /etc/ssh/sshd_config -p wa -k sshd_config auditctl -a always,exit -F arch=b64 -S execve -k shell_cmds其中-w监听的是文件路径,-p wa表示记录写和属性变更,-k是给规则打标签,方便后续检索。规则添加后,用ausearch -k passwd_changes就能查到谁改过/修改了passwd文件。
日志轮转一定要配置好。很多服务器是跑着跑着磁盘突然满了,排查下来就是/var/log下某个日志文件涨到几十个G,因为logrotate没有开启压缩或者轮转周期太长。以rsyslog为例,你可以在/etc/logrotate.d/中设置:
/var/log/auth.log { daily size 100M rotate 7 compress delaycompress missingok notifempty }更稳妥的做法是把日志实时送到远程日志服务器。本地日志一旦被入侵者清理,证据链就断了;集中日志服务器能在攻击者清理之前保留一份独立副本。配置rsyslog客户端只需在/etc/rsyslog.conf或/etc/rsyslog.d/中加一行:
*.* @192.0.2.10:514然后重启rsyslog。远程端开启imudp/imtcp模块接收即可。做这件事之前先确认公司网络环境和日志服务器容量,别把日志集中到一起后反而变成新的瓶颈。
4. 应急响应与入侵排查实战
4.1 快速定位可疑进程与恶意文件
发现服务器异常时,很多人第一反应是“赶紧重启”,这是个错误动作。重启会清掉进程和内存里的线索,等于把现场破坏了。正确做法是先隔离再收集证据。
第一步是尽可能断网隔离,同时保留现场。可以直接挂起公网网卡,或者在防火墙上设置禁止对外连接:
sudo iptables -I OUTPUT -j DROP这一步非常重要,如果是挖矿程序,它需要外联矿池才能继续工作;如果是C2后门,断掉外联能让它处于半瘫痪状态。但断网时间不宜太长,业务中断监控会很快找过来,你需要在这段窗口期内收集完核心证据。
第二步是收集快照。以下命令的输出最好都重定向到文件并保留:
top -b -n 1 | head -30 > /tmp/evidence/top.txt ps -ef --forest > /tmp/evidence/ps.txt ss -anptu > /tmp/evidence/ss.txt看到CPU占用异常的进程,继续深挖它的信息:
ls -l /proc/PID/exe cat /proc/PID/cmdline不要急着kill -9。先观察这个进程有没有子进程、有没有打开特殊文件、网络连接连到哪个地址。/proc/PID/cmdline里如果出现bash -i或者sh -c ...这类字符串,基本可以判断是反弹Shell。
再检查启动项和定时任务,很多恶意程序靠这些小机制实现重启复活:
systemctl list-unit-files --state=enabled ls -la /etc/rc*.d cat /etc/crontab crontab -l ls -la /var/spool/cron/还要留意几个黑客喜欢藏文件的目录:
ls -la /tmp /var/tmp /dev/shm find /tmp /var/tmp /dev/shm -type f -mtime -3 -ls 2>/dev/null正常情况下/dev/shm里应该几乎没有文件,出现一堆看不懂的二进制或脚本就要高度警惕。
4.2 典型入侵痕迹的识别与清理
常见的入侵痕迹集中在几个位置。第一是SSH公钥,攻击者拿到权限后会把公钥写入某个用户的~/.ssh/authorized_keys,这样即使你改了密码,他依然能通过密钥进来。检查每个用户的该文件,看到陌生公钥直接移除:
cat /home/*/.ssh/authorized_keys第二是定时任务。挖矿木马最常见的持久化方式就是写crontab,不断从远程地址下载更新脚本。被篡改的crontab内容往往带wget、curl、base64 -d这类组合,一眼就能看出来。
第三是启动项。systemd目录下出现了名字很像系统服务的恶意unit,例如system-update.service之类,要用systemctl cat unit名查看它的ExecStart,发现异常直接disable并删除文件。
第四是可疑账户。检查系统中是否有新增的高权限账户:
awk -F: '$3 == 0 {print $1}' /etc/passwd所有UID 0的账户都拥有root权限,如果这个输出里除了root还有其他名字,说明系统已经被种下后门账户。
清理时不要直接只删文件,先把证据打包带走:
cd /tmp/evidence tar czf evidence.tar.gz top.txt ps.txt ss.txt /var/log/auth.log然后把恶意进程杀掉、相关文件删除、定时任务清掉、authorized_keys恢复干净、所有账户密码修改、SSH密钥重新生成。再检查有没有异常的开机脚本和预加载库,特别是/etc/ld.so.preload,这个文件正常情况下不存在,一旦存在就必然有问题。
如果攻击者已经拿到了root权限,我对本机清理会持保留态度。因为你无法确定它是否替换了ps、lsof、ss等核心工具,甚至内核层面有没有被动手脚。这种情况下,最稳妥的方案是备份业务数据和必要配置,然后重装系统,恢复服务时确保备份来源是干净的。
4.3 修复后的复盘与长效防护
处理完入侵,不等于事情结束。我每次都会花时间做复盘,否则下次很可能在同一个坑里再摔一次。
复盘的第一步是重新确认系统暴露面。从外部用nmap扫描一下公网端口,看是有没有多余的服务在监听:
nmap -sT -p- <服务器IP>然后检查系统级配置:
sudo sshd -T iptables-save systemctl list-unit-files --state=enabled再检查最近几天哪些关键文件发生了变更:
find /etc -type f -mtime -7 -printf '%TY-%Tm-%Td %p\n' 2>/dev/null | sort把发现的问题和入侵事件串成一条时间线:攻击者从哪个IP进来、通过哪个端口、利用了什么漏洞、什么时候种植了后门、做了什么操作。这些信息写进事件报告,不是为了交差,而是为了指导后续改进。
改进的方向通常包括:对公网开放的服务进行收敛,只暴露必要端口;对管理网段和白名单做更严格限制;把所有密码、密钥、数据库口令全部重置;引入文件完整性校验和集中日志;制定每周补丁更新流程,至少做一次更新评估。最后,还要把这个应急流程写成一份清单,下次再遇到类似事件,团队成员照着清单做,不慌也不会漏。
5. 常见问题与踩坑记录
5.1 加固后服务不可用的排查思路
远程改配置最怕的就是把自己锁在机器外面。我之前改SSH端口,忘了在新端口上放行防火墙,退出旧连接后瞬间失联,最后只能通过云厂商的VNC入口把配置改回来。从那次以后,我所有远程加固操作都坚持一个原则:保留一个可用的带外会话,或者用tmux/screen开着当前终端,不要在一棵树上吊死。
防火墙默认策略改DROP后,最先出问题的一般是DNS解析,因为系统需要向外部DNS服务器转发UDP 53的请求,默认策略把它一起丢了。如果出现“服务看着是好的,但机器上不了网、解析不了域名”的情况,先检查防火墙有没有放行UDP 53:
iptables -L -n | grep 53Fail2ban误封自己的IP也是高频事故。办公网IP是动态变化时,哪天上不了服务器一看,原来是自己被ban了。解决办法是在开始使用Fail2ban之前就把自己的固定IP加入ignoreip,并且waline bantime设置为合理时长,不要一上来就封24小时。
PAM模块配置错了也很致命。有一次我在测试环境加了一条pam_pwquality规则,没有确认模块已安装就直接改配置,结果系统上所有用户包括root都没法登录。幸好这台机器有物理控制台,否则就等着救援模式吧。所以配置PAM之前,先确认模块在,且保留一个root会话,然后立刻测试能否正常登录。
5.2 日志爆炸与磁盘占满
日志爆炸的典型症状是服务突然拒绝写入文件,MySQL、Nginx这些进程开始报“No space left on device”。排查时先看磁盘占用和inode占用:
df -h df -i du -sh /var/log/* /tmp /var/tmp 2>/dev/null | sort -hr | head -20df -h正常但df -i100%的情况很容易被忽略,系统里创建任何新文件都会失败,即使磁盘还有空间。
日志文件爆炸的原因往往有三个:一是auditd规则写得太宽,监听全盘文件导致日志滚雪球;二是应用日志死循环输出,一行接一行刷到日志文件;三是logrotate配置不对,没有按大小轮转也没有压缩。对症下药:细化audit规则,对应用日志增加size和rotate限制,并且定期检查日志目录大小。
更值得做的是把日志集中到远端,这样即使本机磁盘被日志塞满,也可以快速清理而不影响历史日志的保留。日志不是越多越好,而是该记的要记、能删的要定期删、关键的必须扔到远端。
5.3 被忽略的小细节
最后说几个平时不容易注意、一旦出事很难发现的细节。
第一是内核参数。有些系统默认开启了ICMP重定向、IP转发等特性,对网关类服务器可能是必须的,但对普通Web服务器就是多余且危险的。我一般会在/etc/sysctl.conf中设置这些常用参数:
net.ipv4.conf.all.accept_redirects = 0 net.ipv4.conf.all.send_redirects = 0 net.ipv4.conf.all.rp_filter = 1改完后sysctl -p生效,但别在需要依赖这些功能的集群上一刀切,先弄清楚业务是否需要。
第二是动态链接器预加载后门。攻击者可以在/etc/ld.so.preload或环境变量LD_PRELOAD中植入恶意库,从而监控你输入的命令甚至篡改程序行为。检查方式:
cat /etc/ld.so.preload 2>/dev/null echo $LD_PRELOAD正常情况下这两个地方都应该是空的,有任何输出都值得怀疑。
第三是旧系统版本。很多机器从上线以来就没做过大版本升级,软件包版本老到连官方安全源都不再更新。这种情况下系统安全加固做得再多,也经不住已知漏洞被批量利用。尽快制定迁移计划,把核心业务迁到仍维护的版本上,比什么防御工具都重要。
第四是备份。安全工具再强,也不能保证100%不被突破。真正能让你从灾难中快速恢复的,只有平时勤快且校验过的备份。备份不是“拷一份文件放着就行了”,要定期做恢复演练,否则到关键时候你才发现备份数据有问题,比入侵本身更让人崩溃。
我个人在实际运维中的体会是:安全不是某个单点工具的强度,而是你每天是否坚持看日志、是否定期检查账户和定时任务、是否对每一次配置变更心存敬畏。Linux系统安全里最值钱的部分,往往不是那些复杂的漏洞利用技术,而是“把基础配置做对、把问题响应流程跑通”这种看似笨拙的习惯。你如果刚开始接触这块,别急着上一堆复杂平台,先把账户、SSH、防火墙、日志这四件事做成基线,已经能挡住绝大多数自动化和半自动化的攻击。之后再逐步加入完整性校验、集中日志和应急演练,让安全能力跟着业务一起长,效果会比一次性堆砌工具好得多。