做Linux运维这些年我处理过不少服务器也亲眼见过几次入侵后的现场。说句实在话Linux系统安全这件事太容易被排在“先跑业务”后面了等想起来补的时候往往已经付出了不小的代价。这篇文章我打算把Linux系统安全涉及的核心内容完整拆一遍包括安全架构认知、账户认证管控、文件权限防篡改、SSH加固、日志审计还有入侵排查每块都给出可以直接照做的命令、配置和参数适合刚接手服务器管理的新手也适合已经干了几年运维但想系统梳理安全体系的同学。1. 安全架构认知把Linux安全当成一套纵深体系1.1 纵深防御为什么单点加固救不了场我见过不少这样的加固方式随便改个密码、关掉一个端口就觉得“安全了”。但实际效果很有限因为安全是一个体系不是拼图碎片的堆叠。你想想小区的安保大门保安是第一道单元门禁是第二道房间门锁是第三道监控摄像头则是事后追溯的关键。如果只加固一道门被突破了就全完了。Linux系统的安全模型也是这样一层套一层我习惯把它分成几个逻辑层次来看账户认证层系统怎么确认“你是谁”密码策略、账户锁定、sudo权限都属于这一层。权限授权层确认身份之后你“能做什么”文件权限、ACL、特殊权限位都在这一层。网络接入层外部流量如何进入系统SSH加固、防火墙规则在这一层做拦截。审计追溯层系统记录了什么日志、审计、文件完整性校验在事后发挥作用。很多入侵事件之所以发生并不是某一层做得太差而是几层同时存在短板。比如SSH密码太弱网络接入层失守结果被人登录进来恰好系统里有个普通用户挂在sudo组里没有限制账户认证层失守再一查文件权限全是777授权层失守最后攻击者还能通过修改业务脚本实现长期驻留审计层没有发现。任何一层能把风险拦住结局都会完全不同。这也是为什么我建议做安全时不要上来就改参数而是先画一张“风险从哪里来”的图再按图逐层加固。1.2 一张表看清主要风险与对应防线为了让大家建立整体概念我先列一张自己常用的风险对照表。这张表是我在排查实际问题和设计加固方案时反复用到的列得不算全但覆盖了最常见的攻击路径。风险类型常见表现对应防线暴力破解SSH端口持续收到大量失败登录密码复杂度、账户锁定、fail2ban、限制来源IP弱口令账户密码过于简单或长期不变/etc/login.defs密码策略、PAM密码质量模块提权利用普通用户通过SUID文件或漏洞获得root定期扫描SUID位、及时打补丁、最小化sudo授权权限扩散业务目录777、敏感文件任何账户可读严格文件权限、ACL收口、chattr锁关键文件恶意脚本驻留计划任务、自启动项被植入挖矿或木马定期检查crontab、systemd自启项、rc.local痕迹灭失日志被清空、文件时间被篡改日志异地同步、权限锁定日志目录、auditd审计对照这张表你会发现后面几章的内容基本就是按表里的防线展开的。不是所有风险都能靠某一个工具解决但每一层至少要有对应的措施兜底这就是“纵深防御”落到实际操作里的意义。2. 账户与认证安全把入口看住2.1 密码策略落地改好这两个配置文件Linux下的密码策略绝大多数发行版默认是很宽松的。我在一些服务器上见过密码就六位、还持续用了三年以上的情况这种账户基本等于为攻击者预留了一个后门。做账户安全第一步就是把密码策略收紧。要改的配置文件主要有两个分工完全不一样。第一个是/etc/login.defs它决定密码的有效期。我常用的配置段是这样的PASS_MAX_DAYS 90 PASS_MIN_DAYS 7 PASS_WARN_AGE 7三个参数的含义分别是密码最长使用90天、两次改密至少间隔7天、密码过期前7天开始提醒。这里有个容易踩的坑/etc/login.defs只对新建用户和后续修改密码时生效已存在账户的密码过期时间不会自动更新。要让存量用户也按策略执行需要配合chage命令逐个应用例如chage -M 90 -m 7 -W 7 用户名第二个文件是针对密码复杂度的。现代发行版普遍使用 PAM 的 pwquality 模块配置文件在/etc/security/pwquality.conf。我会把核心参数调成下面这样minlen 12 dcredit -1 ucredit -1 lcredit -1 ocredit -1 minclass 3解释一下minlen 是指密码最短长度dcredit、ucredit、lcredit、ocredit 分别代表数字、大写字母、小写字母、特殊字符必须出现的个数这里的负数含义是“至少包含多少个”而不是“最多”。minclass 则要求密码至少覆盖三类字符。设置完之后任何试图通过passwd修改密码的行为都会被这个模块检查。需要提醒的是这些配置项对 root 自身不一定生效因为 root 修改密码通常在 PAM 层会被跳过。生产环境里建议对 root 之外的账户统一执行策略这也是最小权限原则的一部分。2.2 账户锁定与暴力破解防护配置密码再复杂也架不住一直尝试。暴力破解工具在几十个小时里可以跑上百万次密码组合所以我们必须让“猜密码”这件事的成本无限抬高。账户锁定就是干这个的。Linux 系统自带一个账户锁定模块RHEL 系的发行版叫 pam_faillockDebian 系常用 pam_tally2。以 pam_faillock 为例我通常在/etc/pam.d/system-auth和/etc/pam.d/password-auth里配置核心逻辑是在认证开始前检查是否已被锁定在认证失败后记录失败次数。auth required pam_faillock.so preauth audit silent deny5 unlock_time900 auth sufficient pam_unix.so auth [defaultdie] pam_faillock.so authfail audit deny5 unlock_time900 account required pam_faillock.so这段配置的意思是连续失败5次就锁定900秒也就是15分钟。这里必须强调一下顺序问题pam_faillock 的 authfail 行必须放在 pam_unix.so 之后因为它的作用是“认证失败之后”记录并拒绝。如果顺序放错你会遇到一个经典症状连续输错几次后即使输入完全正确的密码也登录不了因为失败分支直接中断了认证流程。我在实际配置中踩过这个坑当时第一反应是“系统是不是挂了”后来才发现是 PAM 顺序问题。所以配置完后一定要用单独的终端保持一个已登录会话不要断开再开新窗口测试登录避免把自己锁在外面。除了系统自带的锁定生产环境我还会再加一层来源管控。比如只允许办公网段的IP访问SSH端口那么在防火墙层面就做了第一道过滤暴力破解的人连端口都探测不到这个细节放在后文SSH加固部分展开。2.3 特权账户与sudo收口管理账户安全还有一个容易被忽略的点检查哪些账户拥有特权。攻击者拿到一个普通账户后最想做的事情就是提权到root。如果系统里存在多余的UID为0的账户那就相当于直接送了一把钥匙。我建议定期执行下面两个检查。第一个是找出所有UID为0的账户awk -F: ($3 0) {print $1} /etc/passwd正常输出应该只有 root 一个名字如果出现了其他用户名就要立刻排查。第二个是检查哪些账户有空的密码字段awk -F: ($2 ) {print $1} /etc/shadow空密码账户意味着不需要密码就能登录这种账户一旦存在后果不堪设想。另外还要看哪些账户拥有登录Shell、是不是早就该被清理掉awk -F: ($7 ! /sbin/nologin $7 ! /bin/false) {print $1, $7} /etc/passwd普通业务账户如果没有交互登录需求Shell一律改成/sbin/nologin这样即使密码泄露也无法通过SSH进入系统。sudo 的收口管理同样重要。我见过把整个wheel组都加到 sudo 里的配置等于几十个用户全部有root权限。正确的做法是/etc/sudoers里只保留确实需要管理权限的用户并且尽量细化命令范围。如果你喜欢直接编辑/etc/sudoers记得用visudo命令而不是普通编辑器因为它会在保存前做语法检查避免写错格式导致整个sudo瘫痪。3. 文件权限与防篡改守住数据完整性3.1 常规权限模型的三个易错点Linux文件权限看起来简单却是我在给团队做安全培训时发现误区最多的地方。先说最容易忽略的目录的 x 权限和文件的 x 权限含义完全不同。目录的 x 权限代表能否把目录当作路径的一部分进入r 权限代表能否列出目录内容w 权限代表能否在目录里创建或删除文件。很多人给目录配了 r 却没有 x于是 ls 能看到列表cd 却进不去排查问题时非常迷惑。第二个易错点是把所有业务文件一概chmod 777。这种做法在测试环境里确实省事但生产环境里一个777的脚本目录意味着任何账户都可以向其中写入恶意文件再配合目录的执行权限基本等于开放了代码执行能力。我见过的最恶劣案例就是攻击者往777的spool目录里放了一个可执行的脚本再利用计划任务周期触发整个过程完全不需要提权。第三个值得关注的是权限继承问题。很多运维改权限时只改当前目录忘了-R递归或者反过来把不该递归的都递归了。我的习惯是配置好后立刻用ls -ld和find抽查几个关键路径确认权限符合预期而不是相信命令一次性执行成功。3.2 特殊权限位的风险与排查Linux里除了常见的rwx还有三个特殊权限位SUID4、SGID2、Sticky1。其中风险最大的是SUID因为带有SUID的可执行文件在运行时进程的有效身份会变成文件属主。典型例子是/bin/passwd它以root身份运行普通用户才能借此修改自己的密码。攻击者拿到一个普通Shell后如果系统里存在可利用的SUID程序就可能用它完成提权。我曾经在一次安全测试里遇到过系统/usr/bin/find带SUID位的配置这意味着任何用户执行 find 时都临时拥有root身份配合 find 的命令执行参数就能直接拿到高权限。这种问题的修复方式很简单chmod -s /usr/bin/find排查系统中所有带SUID位的文件一条命令就能扫出来find / -perm -4000 -type f 2/dev/nullSGID 的影响要复杂一些。文件带SGID时执行者会临时获得该组成员的身份目录带SGID时里面新建的文件和目录会继承父目录的组。这在协作共享目录里很实用但如果被错误设置可能导致普通用户访问到本不该接触的组资源。Sticky位反而是防护性的常见于/tmp它保证只有文件属主能删除自己的文件避免了共享目录里互相删文件的问题。3.3 用chattr与ACL实现关键文件防篡改权限模型之上Linux还有一些更硬核的保护手段我最常用的两个是 chattr 和 ACL。先讲 chattr它能给文件加上特殊的属性标志。比如chattr i表示不可修改设置之后即使是 root也没办法对文件做删除、重命名、写入等操作。这个特性用来保护关键配置和业务脚本非常合适。举个例子某业务系统里有一个自动发布的脚本一旦被篡改发布流程就可能被注入恶意操作。我给脚本执行chattr i /opt/business/release.sh之后任何人都改不动它。等需要更新脚本时先chattr -i解除锁定改完再锁回去。用之前一定想清楚i是能拦住root的所以操作时不要锁错文件尤其是别把用户的主目录或者数据库配置目录直接锁死否则后续维护会非常麻烦。查看文件是否被加了锁用lsattr。再来看ACLACL允许对单个用户或单个组设置独立权限而不改变文件原有的属主和属组。命令是setfacl和getfacl。比如我需要让某个运维账号能读日志目录但不想把它加进日志属组里可以这样setfacl -m u:tom:r /var/log/app/ getfacl /var/log/app/用-R参数可以对目录递归设置ACL但要注意ACL和普通权限位可能互相覆盖设置后用getfacl检查是必不可少的环节。4. SSH远程管理加固暴露面最小化4.1 SSH为什么总是第一批被打如果有一台Linux服务器直接暴露在公网扫描和暴力破解几乎是24小时不停歇的。SSH服务在默认的22端口上裸奔的密码认证加上root直接登录这三个条件凑齐等于把系统的钥匙挂在门外。我见过最夸张的一次一天之内某个IP段对一个测试机的22端口尝试了上万次密码组合。有些同学觉得“我的密码够复杂不怕”这话有一定道理但暴力破解只是其中一种方式。如果系统存在漏洞、密码被人从别的途径泄露或者内部某台机器被攻破之后横向移动SSH都是进入内网最顺手的通道。所以SSH的加固核心就三件事关闭不需要的登录入口、改用更强的认证方式、限制能连进来的来源。4.2 从密码登录切换到密钥认证密钥认证是目前性价比最高的SSH登录方式。原理不复杂客户端生成一对密钥公钥放到服务器的~/.ssh/authorized_keys里私钥留在本地。登录时服务器用公钥验证客户端的私钥签名不通过任何密码明文交换比密码安全得多。生成密钥我推荐用 ed25519 算法比传统的 RSA 更短更快安全性也足够。命令如下ssh-keygen -t ed25519 -a 100 -C your-comment其中-a 100是增加密钥派生时的计算次数目的是防止私钥文件泄露后被快速暴力破解。生成之后把公钥内容追加到服务器的 authorized_keys 里这一步可以用ssh-copy-id来完成ssh-copy-id 用户名服务器地址确认用密钥能登录之后再修改/etc/ssh/sshd_config把密码认证关掉PermitRootLogin no PubkeyAuthentication yes PasswordAuthentication no MaxAuthTries 3 LoginGraceTime 30 AllowUsers tom jerry这几行的含义分别是禁止root直接登录、启用公钥认证、关闭密码认证、最多尝试3次、登录超时限制在30秒、只允许指定用户通过SSH登录。改完先检查配置文件语法sshd -t确认没有报错后用systemctl reload sshd重载服务。这里我需要反复提醒一句不要直接重启reload 更温和而且远程操作时必须保持一个已登录会话不要断开确认新登录方式可用后再关闭旧连接。我遇到过同事修改SSH配置后重启了服务结果发现自己之前用的密码认证已经关闭新配置又没生效人被锁在机器外面最后只能走物理终端处理非常狼狈。4.3 远程管理白名单与防火墙收敛密钥认证解决的是身份验证问题但SSH服务还是暴露在网络里。更进一步的做法是把SSH的访问来源限制在白名单内也就是只允许特定网段的IP连接。以常见的 firewalld 为例我可以把SSH端口只放给内网管理网段firewall-cmd --permanent --add-rich-rulerule familyipv4 source address192.168.10.0/24 service namessh accept firewall-cmd --reload如果是云环境还需要在安全组层面同步配置来源IP白名单两层都要收敛。如果确实需要从外部网络管理可以再叠加一层跳板机或者堡垒机而不应该直接把生产服务器的SSH端口暴露给所有地址。另外一个实用操作是修改SSH端口。虽然有争议但把默认端口从22改成高位端口确实能大幅减少自动化扫描的噪音。配合防火墙白名单效果更好。这里要注意改端口是在 sshd_config 里改Port参数改完后别忘了同步放行防火墙的新端口然后把旧端口从防火墙规则里移除。端口不是灵丹妙药它只挡那些无脑扫描器真正的攻击者如果盯上你一样能发现新端口但作为纵深防御的一环还是值得做。5. 日志审计与入侵痕迹追踪5.1 Linux日志体系速览与关键文件安全做得再好也难免有万一。一旦发生入侵日志就是还原现场、确定损失范围的第一手证据。很多运维对日志的熟悉程度其实不够出了问题只知道去翻/var/log/messages其实Linux的日志是有分工的。我最常用的几个日志文件和用途如下/var/log/messages系统通用日志很多内核和服务的运行信息都在这里。/var/log/secure认证相关日志SSH登录成功、失败、sudo授权都在记录。/var/log/cron计划任务执行日志。/var/log/btmp和/var/log/wtmp记录登录成功与失败的账户信息配合last、lastb命令使用。journald现代发行版的系统日志服务通过journalctl查询可以精确按时间和服务过滤。查看最近登录记录的命令很简单last -n 20 lastb -n 20lastb能直观看到多少次失败的登录尝试及其来源IP这是判断是否被暴力破解的首选命令。5.2 rsyslog与logrotate的日常配置日志本身也要做两层设置。第一层是确保安全相关日志有足够长的保存周期第二层是防止日志把磁盘占满。rsyslog 负责把客户端发来的日志和本机日志按规则写入对应文件我通常会在/etc/rsyslog.conf或/etc/rsyslog.d/下单独建一个针对安全事件的配置authpriv.* /var/log/secure cron.* /var/log/cron需要注意的是日志目录如果权限太宽攻击者入侵后就能直接清空日志消灭痕迹。我会把/var/log/secure这类关键日志文件设成只有 root 和 adm 组可读必要时用前面提到的 chattra属性设置成只能追加不能修改已有内容。a和i的区别在于前者允许追加适合日志这种持续写入的场景。第二个重点是 logrotate它负责日志的轮转和清理。默认配置在/etc/logrotate.conf和/etc/logrotate.d/下。我可以为安全日志单独配置/var/log/secure { daily rotate 30 compress missingok notifempty }这段配置表示每天轮转、保留30份、压缩旧日志、缺失不报错、空文件也轮转。日志如果永远不轮转磁盘迟早会被写满服务反而会出问题。配完可以用logrotate -d /etc/logrotate.conf做预演确认语法没有问题。5.3 一次入侵排查的完整流程记录日志排查不仅仅是看文件更要形成一套有序的排查流程。我举一个实际处理过的典型场景某台内网测试服务器突然CPU飙高业务虽然没挂但明显不对劲。我登录后的第一件事是看进程状态发现一个名字伪装成系统内核进程的可疑进程占用CPU超过100%进程名和系统里的kworker极其相似但不属于标准的内核线程列表。接着我用下面的命令确认进程的可执行文件路径ls -al /proc/[进程PID]/exe结果发现它的可执行文件指向/tmp/.x/upd这个路径已经足够说明问题。/tmp下的隐藏目录加随机文件名是恶意脚本最常见的驻留方式。第二步检查计划任务执行crontab -l查看当前用户的计划任务同时在/etc/cron.d/、/etc/cron.daily/等目录里翻了一遍发现了一条每天定时执行下载脚本的任务脚本指向外部地址。这就是典型的持久化后门。第三步检查开机自启动项用systemctl list-unit-files --typeservice --stateenabled列出开机启动的服务重点看有没有名字看起来像系统服务但实际上指向非系统目录的单元。同时查看/etc/rc.local和/etc/ld.so.preload这两个文件也是恶意脚本常挂的地方。第四步检查SSH授权公钥执行cat ~/.ssh/authorized_keys发现里头多了几条陌生的公钥说明攻击者已经为自己预留了登录通道即使我修复了密码和进程它仍然可以随时进来。确认完整之后我才动手清理顺序是先断开可疑进程删除恶意脚本文件移除计划任务和自启动项清掉陌生公钥最后修改所有账户密码并补上前面几章的加固措施。整个流程里最重要的一点是先收集证据再动手不要一上来就 kill 进程、删文件因为很多痕迹一旦删除就没有了后面想分析都无从下手。6. 常见问题与排查技巧实录6.1 安全加固后服务异常的典型症状安全加固本身也会引入一些问题而且往往是在你满怀信心地做完配置之后突然冒出来。我这里整理几个最常见的症状和原因基本都是我或同事在环境里亲眼见到的。现象一SSH连不上报错“Connection refused”。常见原因是sshd配置语法错误、服务没启动、防火墙新规则没放行新端口。先检查进程是否在跑再ss -tlnp看端口有没有监听最后看防火墙规则。现象二密码正确但一直提示认证失败。多半是 PAM 配置顺序出问题或者账户被 pam_faillock 锁定了。可以用pam_tally2 --user 用户名查看失败次数或者在/var/log/secure里看具体的拒绝原因。现象三sudo 直接无法使用提示“sudo: parse error”。这是/etc/sudoers语法写坏了需要用 root 身份执行visudo -c排查如果实在修复不了可以用pkexec visudo从图形环境或另一个终端进入修复。现象四服务正常但业务访问很慢。这种有时候是防火墙规则过多、日志量暴增或者 SELinux 策略误伤导致的。建议先dmesg看内核日志再查/var/log/messages里有没有大量的denied记录。任何一次加固改动都建议单点执行、逐步验证不要一次把所有配置全部堆上去否则出了问题根本定位不到原因。6.2 Linux安全自查清单可直接照着做为了方便大家新接手一台服务器时快速摸底我把自己的检查流程整理成一个表格。这个清单覆盖了最容易出问题的几个方向照着跑一遍大概十分钟能筛掉绝大多数低级的风险隐患。检查项参考命令期望结果UID 0账户awk -F: ($30){print $1} /etc/passwd只有root空密码账户awk -F: ($2){print $1} /etc/shadow无输出登录Shell账户awk -F: ($7!/sbin/nologin){print $1,$7} /etc/passwd只有必要账户监听端口ss -tlnp端口列表与业务一致SUID文件find / -perm -4000 -type f 2/dev/null文件均为已知系统程序计划任务crontab -l ls -l /etc/cron.*无异常脚本自启动服务systemctl list-unit-files --typeservice --stateenabled无异常服务SSH配置grep -E PermitRootLoginPasswordAuthentication /etc/ssh/sshd_config登录记录lastb -n 20无大量异常失败记录系统补丁包管理器检查更新列表安装最新安全更新这个清单在每次新系统上线前和定期巡检时都很实用。我自己的习惯是每季度至少跑一遍遇到大版本升级或者安全通告出来之后再额外加一次重点检查。6.3 几个值得记住的踩坑经验最后分享几个藏在细节里的经验都是平时文档里不容易看到、但实操中让我印象深刻的。第一个是关于修改密码策略的生效时机。我在前文已经提过/etc/login.defs只对新用户生效这里再补充一个案例。有一次我在某台机器上修改了 PASS_MAX_DAYS以为所有用户都开始按90天过期了结果三个月后发现一个核心账户还能登录一查才发现它的 shadow 条目里chage字段根本没更新。后来我才养成了一个习惯改完配置文件之后一定要用chage -l 用户名逐个核对实际值不能想当然。第二个是关于用chattr i锁文件的边界。锁文件确实好用但如果锁错了范围会很麻烦。有一次我想保护某个应用的配置目录直接对目录执行了chattr i结果目录本身是挡住了新文件写入但目录里已有的子目录没有全部继承这个锁定属性应用还在正常写文件。这其实不算事故但让我意识到 chattr 的递归效果和预期不同。后来我锁目录时都会先find出所有子项再逐一处理或者明确只锁单个关键文件而不是盲目-R。第三个是我反复强调的远程操作保底原则。每次对线上设备做SSH或防火墙加固我都会遵守一个流程先开一个不打算关闭的备用连接然后执行改动再从新连接验证最后才关闭备用连接。还有一点就是尽量不要在生产高峰期做改动你永远不知道一个看似无害的重载服务会带来什么连锁反应。按我自己处理过的线上问题来看绝大多数是因为准备不足和操作太急安全意识不光是技术问题更是流程问题。