
“系统安全及应用”这个题目乍一看很像高校Linux课程里的一个章节但真放到生产环境里它几乎是每个运维、开发乃至个人站长都绕不开的生存技能。我见过太多“服务跑起来就行”的项目结果上线没两周就被扫了端口、改了主页、成了矿机。这篇东西不聊虚的就围绕账户安全、权限控制、日志审计、自动化巡检这几大块把我在实际服务器上摸爬滚打出来的经验和能直接抄作业的方案一次性讲透。1. 系统安全的本质先搞清楚你在防谁1.1 安全不是一道锁而是一整个流程很多初学者对“系统安全”的理解就是设个复杂密码、装个杀毒软件这个认知在个人电脑上凑合能用但在服务器上完全不够。攻击者从来不会按套路出牌他们可能通过你忘记关闭的测试端口进来可能利用某个带SUID权限的旧版程序提权也可能只是因为你把SSH暴露在公网且允许root直接登录然后被暴力破解撞库撞开了门。安全的核心思路是“纵深防御”假设任何一层都可能被攻破那么每一层都要有独立的防护手段。密码再复杂如果服务本身有漏洞也没用服务再安全如果日志没人看被入侵了也是事后诸葛亮。我自己的配置习惯是把系统安全拆成四个维度来管理分别是身份验证、权限收敛、行为审计、定期巡检。这四个维度相互独立又彼此补充任何一环失效其他环节还能兜底。1.2 常见的攻击路径与对应的防御切入点攻击者进服务器的路径其实就那么几条摸清楚它们你就知道该在哪儿使劲。攻击路径常见手法对应防御手段远程登录入口SSH暴力破解、弱口令、默认端口扫描密钥登录、禁用root远程登录、修改端口、限制来源IP、fail2ban本地权限提升SUID滥用、sudo配置错误、内核漏洞提权收紧SUID位、最小化sudo授权、及时打补丁应用服务漏洞中间件漏洞、上传漏洞、反序列化攻击最小化安装、服务降权运行、Web层加固敏感信息泄露日志泄露、备份文件外泄、环境变量泄露日志集中管理、备份权限收紧、配置项脱敏供应链风险恶意软件包、带后门的第三方工具校验软件签名、控制安装来源、定期审计包列表我特意把“供应链风险”列进来是因为现在很多入侵事件不是从外部打进来的而是开发者自己装了个带恶意代码的pip包或者npm包。这类问题传统的防火墙很难拦住只能靠平时的安全意识去弥补。2. 账户安全与身份认证守住登录这扇门2.1 账户体系规划从源头控制人与权限的对应关系生产环境最忌讳的就是“人手一个root”。我接手过一台服务器里面居然有七个不同的账号全部能sudopassword文件里的信息乱成一团甚至有两个账号是上一个离职同事遗留的。这种账户管理方式别说安全审计真出了事你都查不出来是谁动的。我的建议是把账号分成三类来管理超级管理员账户仅限极少数人使用主要用于系统初始化和紧急故障恢复。日常运维账户使用sudo授予临时权限且sudo规则必须精确到具体命令。服务运行账户对应各种应用服务例如nginx、mysql、redis没有登录Shell也不能sudo。具体操作上日常运维账户一定要用useradd创建时指定合理参数我常用的命令模板是这样的useradd -m -s /bin/bash -U ops_user这里-U参数会自动创建同名用户组后面配合sudo配置更容易管理。创建完立刻设置高强度初始密码用chage强制用户在第一次登录时修改密码。passwd ops_user chage -d 0 ops_user要注意的是服务运行账户应该用-M选项禁止创建家目录并且Shell设置为/sbin/nologinuseradd -M -s /sbin/nologin -r webapp-r参数表示创建系统账户UID通常小于1000这类账户不会出现在登录界面上也天然没有家目录和登录Shell即使服务被攻破攻击者想通过这个账户登录系统也是徒劳。2.2 密码策略别让密码成为最薄弱的环节密码策略这件事最反直觉的其实是“定期改密”未必比“设置复杂密码”更重要。因为人的习惯是一旦要求三个月换一次密码密码就会变成Spring2024!或者Autumn2024这种带规律的模式。更好的策略是密码要长不要定期强制更换配合多因素认证来兜底。/etc/login.defs里几个关键参数我建议这样配PASS_MAX_DAYS 99999 PASS_MIN_DAYS 7 PASS_WARN_AGE 14PASS_MIN_DAYS 7是有讲究的它防止用户改了密码之后立刻又改回原密码。真正有效的其实是密码复杂度本身CentOS/RHEL系可以通过/etc/security/pwquality.conf来配置minlen 14 dcredit -1 ucredit -1 lcredit -1 ocredit -1这四行分别要求密码至少14位必须包含数字、大写字母、小写字母、特殊符号。-1表示至少有一个这种字符注意不是“最多”和PAM模块里其他参数语义不一样这个很容易搞混。注意配置密码策略之前先用authconfig或者pam-auth-update确保系统使用的是PAM认证否则改了半天不生效。检查方式是cat /etc/pam.d/system-authRHEL系或者cat /etc/pam.d/common-passwordDebian系确认存在pam_pwquality.so这一行。2.3 SSH安全加固密钥、端口、来源限制一个都不能少SSH基本是所有远程服务器唯一的入口这里必须重点照顾。第一件事就是关闭密码登录全面切换到密钥认证。很多人不敢这么做怕密钥丢了进不了系统但正确的做法是配一个带密码保护的私钥再用ssh-agent缓存安全性和便利性兼得。客户端生成密钥时ssh-keygen -t ed25519 -a 64 -f ~/.ssh/my_server_keyed25519是目前平衡安全性和性能的优选算法-a 64把KDF迭代次数拉到64轮暴力破解私钥密码的难度会大幅增加。生成后在服务器端执行mkdir -p ~/.ssh chmod 700 ~/.ssh echo 公钥内容 ~/.ssh/authorized_keys chmod 600 ~/.ssh/authorized_keys权限设置必须严格.ssh目录700authorized_keys文件600。很多SSH不生效的问题都是权限多了一点点导致的这个坑我踩过不止一次。然后是修改/etc/ssh/sshd_config里的关键选项Port 22022 PermitRootLogin no PasswordAuthentication no PubkeyAuthentication yes AllowUsers ops_user dev_user MaxAuthTries 3 LoginGraceTime 30 ClientAliveInterval 300 ClientAliveCountMax 2把端口从22改成高位端口能过滤掉绝大多数无差别扫描。AllowUsers是一个容易被忽略的好选项它从账户维度限制谁能通过SSH登录比单纯改端口更严格——即使某开发人员的密钥泄露了只要他不是AllowUsers列表里的人密钥也用不上。这里要给一个实操提醒修改sshd配置后不要直接重启服务先执行sshd -t这个命令只会做配置语法检查不实际重启服务。确认无误后用systemctl restart sshd重启并且不要关闭当前SSH会话新开一个窗口测试能登录成功后再关老窗口。否则配置写错了你把自己锁在外面就麻烦了。2.4 sudo权限收敛最小化授权的正确写法sudo授权是日常运维效率和安全性的平衡点。我见过最糟糕的写法是直接给开发团队所有人sudo ALL(ALL) ALL这和直接把root密码发群里没区别。合理的sudo授权应该精确到命令、精确到用户。以运维账户为例在/etc/sudoers.d/下创建一个运维组专用的规则文件%ops_user ALL(ALL) /usr/bin/systemctl restart nginx, /usr/bin/systemctl reload nginx这表示ops_user用户组的成员只能执行nginx的restart和reload做不了其他越权操作。如果还需要查看日志再补一行%ops_user ALL(ALL) /usr/bin/tail -f /var/log/nginx/access.log一个比较实际的做法是允许运维组执行systemctl命令中的status和list-units子命令但不允许start、stop、restart任意服务除非你明确列出来。因为systemctl这个命令本身有太多子命令如果授权得太宽等于没限制。注意修改sudoers文件一定要用visudo命令它会做语法检查避免语法错误导致sudo完全无法使用。如果是编辑/etc/sudoers.d/下的文件依然要用visudo -f /etc/sudoers.d/xxx来检查不要直接用vim或sed改。3. 权限与文件系统安全收窄攻击者的活动空间3.1 找出并清理危险的SUID/SGID文件SUID权限是Linux系统里最容易出问题的机制之一。简单说带SUID位的可执行程序运行时身份是文件所有者而不是执行者。经典案例就是/usr/bin/passwd因为普通用户需要临时以root身份修改/etc/shadow所以必须带SUID位。但这种机制如果落在不安全的自定义程序上等于给攻击者留了提权后门。排查方式很直接find / -perm -4000 -type f 2/dev/null这个命令会找出全盘带SUID位的文件。正常情况下系统自带的SUID文件就那么二十几个例如ping、su、mount、umount、passwd等出现任何陌生程序就要立刻查清楚。需要去掉SUID位时chmod -s /path/to/suspicious_file更细的维度还要检查SGID位权限中是2000find / -perm -2000 -type f 2/dev/nullSGID位在目录上还有特殊含义——在该目录下新建的文件会继承目录的属组。这个特性在某些协作场景有用但也容易被利用比如某个共享目录是组可写的如果再带上SGID位攻击者写进去的木马文件就会带着合法组身份运行。3.2 umask与默认文件权限别让新建文件“裸奔”服务器的默认创建文件权限由umask这个掩码决定。很多默认安装的Linux系统umask是022这意味着新文件权限是644rw-r--r--新目录是755。问题在于如果服务器上跑的是多租户应用或者目录需要被多个服务访问644权限可能让不该读的人也能读。我建议根据业务场景设置umask。偏安全的环境可以改成027这样同组用户能读写但其他用户完全没有权限umask 027在/etc/profile或者/etc/bashrc里统一设置echo umask 027 /etc/profile改了之后要注意应用服务的启动方式。很多服务是通过systemd拉起的systemd会继承它自己那边的默认环境不一定读/etc/profile。所以更靠谱的方式是在服务对应的systemd unit文件里写UMask0027。这一步容易被忽略但实际很重要——否则你在shell里设置了027服务实际创建的文件还是022。3.3 利用chattr和ACL做精细化控制对某些非常关键的文件比如/etc/ssh/sshd_config、/etc/shadow、/etc/sudoers光是文件权限收紧还不够我习惯加一层不可变属性chattr i /etc/ssh/sshd_config chattr a /var/log/messagesi表示文件不可修改、不可删除、不可重命名即使是root也没用除非先执行chattr -i解除。a表示只能追加内容不能覆盖和删除非常适合日志文件。这个属性对攻击者来说是个巨大的障碍因为就算他们拿到root权限想把篡改后的sshd配置写进去也会直接报错。不过要提醒一句chattr i虽然好用但也会妨碍正常的配置变更。改配置前要先chattr -i改完再chattr i。我就在自动化脚本里栽过跟头——某个配置管理脚本尝试修改被加锁的文件直接失败排查了半天才意识到是chattr锁住了。ACL访问控制列表也是一种很实用的权限工具它能突破传统的属主/属组/其他人的三组权限模型。举个例子某个目录需要让nginx用户只读但传统权限模式下放给“其他人”读权限就等于所有系统用户都能读。用ACL可以精确指定setfacl -m u:nginx:r /var/www/html查看ACL权限用getfacl。如果未来要批量清除某个用户的ACL权限用setfacl -x u:nginx而不是setfacl -b-b是清空所有ACL范围更广容易误伤。3.4 系统目录与挂载参数的安全自查安全加固里有个冷门但有效的操作对不需要执行文件的文件系统分区挂载时加上noexec参数。比如/home、/tmp、/var/tmp这类用户可写目录加noexec后攻击者即使上传了二进制木马也无法在该分区执行。修改/etc/fstab挂载参数/dev/mapper/vg_home /home xfs defaults,noexec,nosuid 0 0nosuid参数同时加上防止在该分区上运行带SUID位的程序。需要注意的坑是/tmp加noexec不是所有环境都能直接上的。有些软件安装包在解压时需要在/tmp执行临时脚本加上noexec后安装会报错。所以生产环境我是建议先加nodev,nosuid这两项比较安全noexec需要根据实际跑的应用逐一验证后再启用。4. 日志安全与审计让每一次操作都有迹可循4.1 日志取证关键谁能在入侵后毫发无损很多管理员把安全重心全放在防御上日志几乎不看。等到服务器被入侵想溯源时才发现日志早被清空了或者繁杂到根本找不到关键片段。其实日志的核心价值不在于记录了多少数据而在于关键事件能不能被快速检索出来。一个值得坚持的好习惯是在每台服务器的/etc/rsyslog.conf里配置远程日志转发把日志实时同步到独立的日志服务器上。这样做的好处有两点一是本地日志被攻击者清空后远程日志是唯一无法删改的证据来源二是多台服务器日志统一入口日常巡检不必一台一台登录去看。Minimal配置示例# 在 /etc/rsyslog.conf 中追加 *.* 192.168.x.x:514远程端把rsyslog开启$ModLoad imudp和$IMUDP端口监听。注意UDP 514端口存在流量伪造的风险有条件的话用TCP模式或部署日志接收代理安全性更好。这个策略的短板是它依赖网络连通性所以建议配合日志文件的本地加密备份一起做。4.2 systemd-journald与logrotate的配合现代Linux发行版统一使用systemd-journald管理日志它把内核日志、系统服务日志集中存储成二进制journal文件。直接用journalctl查询很方便journalctl -u nginx --since 1 hour ago journalctl -u sshd --since today | grep Failed password但journal文件本身不会无限增长生产环境建议把/etc/systemd/journald.conf里的SystemMaxUse设置成合理的上限比如500M否则长时间不清理会占满/var分区。更合理的做法是让journald把日志转发给rsyslog然后由logrotate按天滚动归档这样既有二进制的时效性又有文本日志的可读性。在/etc/logrotate.d/可以配置什么日志、什么周期、保留多少份。我常用的一个模板/var/log/nginx/access.log /var/log/nginx/error.log { daily rotate 14 compress delaycompress missingok notifempty create 0640 nginx adm }daily表示按天滚动rotate 14保留14份compress压缩旧日志delaycompress让最新一份刚滚出来的日志先不压缩因为nginx还可能继续往里写。实测下来这套配置绝大多数场景都够用。4.3 审计工具的使用姿势auditd与journalctl组合如果需要精确跟踪“谁在什么时间执行了什么命令、改动了哪个文件”单靠系统日志不够这时候要引入auditd。它属于内核级审计框架在红帽系发行版里通常预装但默认没启用systemctl enable auditd --now审计规则分两大类文件监控和系统调用监控。比如我想跟踪/etc/passwd和/etc/sudoers的每一次访问修改行为可以配置auditctl -w /etc/passwd -p wa -k identity_file auditctl -w /etc/sudoers -p wa -k sudo_changes-w指定监控文件-p指定权限类型r读、w写、x执行、a属性变更-k是给这条规则打个便于检索的标签。查询审计记录用ausearch -k identity_file -ts recent注意auditctl设置的规则重启后会失效要持久化必须写到/etc/audit/rules.d/下对应规则文件中或者使用augenrules --load重新加载全部规则。实操心得auditd规则不是越多越好。每条规则都会增加内核开销生产环境监控/etc目录里敏感文件就够了不要全局监控/usr/bin目录否则磁盘很快会被海量审计日志填满。4.4 日志分析发现异常的第一道信号日志的价值最终要落到“发现异常”上。我给自己设了几个固定的检查项你可以直接抄首先登录失败记录。所有发行版都能查SSH日志中的Failed password条目重点核查同一IP多次失败的情况。一条命令就能搞定grep Failed password /var/log/secure | awk {print $(NF-3)} | sort | uniq -c | sort -nr如果看到某个IP重试了几百上千次基本可以断定有人在爆破。其次是sudo提权行为的记录。在/var/log/secure里查询sudo相关的标记定时确认有没有人在非工作时间执行过特权命令。判断一个系统是不是被入侵有个很实用的观察点系统平均负载。如果一台应用负载常年很低某天CPU飙高、网络连接暴涨先不要急着重启机器用top和ss -tnp观察进程和连接来源多半能找到隐藏在系统目录下的挖矿木马。5. 自动化安全巡检不要让安全停留在配置完成那天5.1 Shell脚本实现每日安全扫描安全配置一旦配好最怕的就是“配完就忘”。系统升级、开发人员临时改权限、新部署的服务端口没收敛……任何变动都可能让之前的安全工作付诸东流。我养成了一个习惯每周跑一次巡检脚本把关键指标扫一遍结果落到文件里偶尔翻出来看看。一个轻量级巡检脚本的核心模块包括#!/bin/bash # 检查SUID异常文件 find / -perm -4000 -type f 2/dev/null /tmp/suid_files.txt # 检查可登录账户 awk -F: $7 ! /sbin/nologin $7 ! /bin/false {print $1} /etc/passwd # 检查SSH配置关键项 grep -E PermitRootLogin|PasswordAuthentication /etc/ssh/sshd_config # 检查开放端口 ss -tulnp # 检查最近登录记录 last -10把这些模块跑出来的结果和上一次快照做对比差异部分单独列出来重点分析。我在某次巡检中就是这样发现服务器上多了一个没见过的端口在监听顺藤摸瓜找到一个被开发者偷偷部署的测试服务它竟然带着一个全版本漏洞的web框架。这种服务要是放着不管出是早晚的。5.2 安全基线核查对照基线找偏差有条件的团队可以引入安全基线核查工具没有条件的也能用手动方式制定基线。比如基线里应该强制/etc/shadow文件权限为000或600/etc/passwd文件权限为644/etc/ssh/sshd_config文件权限为600不存在/etc/cron.allow和/etc/cron.deny之外的异常cron任务把这些基线固化成一个checklist每次巡检对照执行。不要觉得手动麻烦在服务器数量不超过二十台的环境里脚本加checklist的方式比引入大型平台工具要灵活得多也不会给服务器带来多余agent负担。5.3 服务端口与进程白名单安全巡检的核心技术点之一是“先建立正常画像再排查异常”。我的做法是先把当前系统中的服务和端口全部列出来挑出真正需要对外开放的其他全部设为目标清单后续巡检只关注清单之外的新增项。ss -tlnp | awk NR1 {print $4} | cut -d: -f2 | sort -u这条命令会列出所有TCP监听端口。如果是数据库服务器通常只该有3306且绑定内网、SSH端口、进程本身需要的端口。多出的端口必须能说清楚是谁、为什么、什么时候开的。这个“说不清楚”就是风险信号应立即排查进程的行为和来源。6. 关键防线实战从配置到验证的完整闭环6.1 验证一条加固策略是否有效的两种方式从经验上讲配置加完了必须做验证一是正常路径能通过二是攻击路径被阻断。以禁用root SSH登录为例验证方式就是用root账号尝试通过SSH登录如果配置正确连接会被拒绝并返回Permission denied。而正常运维账号用密钥可以顺利登录。同理修改SSH端口后用nc -vz IP 新端口验证端口通不通再用旧端口探测应该超时。一些管理员改完端口后只测了新端口忘了确认旧端口是否真的失效结果旧端口仍然响应等于白改。验证方式不应该只是“看一眼配置文件”而是实打实从外部模拟真实访问。没有条件的团队可以直接在服务器本机执行ssh -p 端口 rootlocalhost试试看效果完全一样。6.2 结合软件包更新降低已知漏洞风险系统安全有一个绕不开的环节打补丁。很多入侵事件里攻击者利用的漏洞其实早在几个月前就有官方修复版本了只是管理员一直没更新。服务器上长期不更新软件包等于把已知漏洞挂着招摇过市。更新策略有一个度的问题。完全禁止更新不可取每天自动更新也可能引入兼容性问题。我建议的节奏是安全更新如内核、OpenSSL、OpenSSH每两周检查一次测试环境验证后推向生产。功能版本升级不主动更新等业务有需要时再评估。命令层面apt/yum都有安全更新接口# RHEL系 yum update-minimal --security # Debian系 apt update apt list --upgradable | grep -i security生产环境强推在凌晨低峰期执行并且更新前做快照或备份。更新后至少观察十分钟服务状态确保没有因依赖变更导致服务异常。6.3 攻击面最小化删除不需要的组件和服务最小化安装是系统安全里性价比最高的一条。一台干净的服务器默认只开SSH就足够跑业务了但很多发行版默认会附带一堆你根本用不上的服务打印服务cups、蓝牙栈bluetooth、邮件传输代理postfix/sendmail等。这些服务不仅白白占资源还额外引入攻击面。查看和管理开机启动systemctl list-unit-files --typeservice --stateenabled对不需要的服务直接禁用并停止systemctl disable --now cups systemctl disable --now bluetooth还有一类容易忽略的自动挂载服务autofs、远程桌面协议端口RDP、Telnet等明文远程控制协议。这些老协议如果还开着就属于高危项必须彻底关闭。6.4 利用入侵检测思维fail2ban与异常连接的发现虽然防火墙能限制端口但对企业网络里“合法端口上的持续暴力尝试”防火墙是挡不住的。SSH的暴力破解是公网服务器最常见的安全事件百试不爽的解决方案是fail2ban。fail2ban的原理很朴素监控日志发现同一来源多次失败尝试就动态调用防火墙规则封禁该IP一段时间。安装后配置一个jail以Debian系路径为例[sshd] enabled true port ssh filter sshd logpath /var/log/auth.log maxretry 3 findtime 600 bantime 3600maxretry 3表示10分钟内失败3次就触发封禁bantime 3600表示封禁1小时。如果你把SSH端口改成了22022这里port也要同步修改否则fail2ban解析的还是22端口。实测下来fail2ban能挡住几乎所有无差别扫描式爆破但它不能完全替代密钥认证和AllowUsers限制——攻击者如果拿到合法密钥或者合法用户名fail2ban就失效了。所以fail2ban属于“水桶拼图中比较靠下的那一块”不能因为装了它就觉得万事大吉。7. 常见问题速查与排查技巧7.1 问题场景表我整理了一张高频排查表非常适合放在手边参照典型问题可能原因排查命令解决方式SSH密码登录被拒绝PasswordAuthentication设置为no但客户端未配置密钥grep PasswordAuthentication /etc/ssh/sshd_config客户端生成密钥并加入authorized_keys修改端口后无法连接防火墙/selinux未放行新端口ss -tlnp、getenforcefirewalld添加端口规则或调整SELinux布尔值普通用户执行sudo报错用户不在sudo组或sudoers规则配置错误groups 用户名、visudo -c修改/etc/sudoers.d/添加精准规则文件明明给了777还是无法访问SELinux策略限制ls -Z、ausearch -m avc调整SELinux布尔值或写上正确的上下文系统日志增长过快auditd规则过宽或journald无上限journalctl --disk-usage、auditctl -l缩小审计范围设置journald上限开放端口里多出未知端口服务被植入后门或自带端口ss -tnp、lsof -i:端口定位进程终止并排查启动项关于SELinux这里展开多说一句。很多管理员为了省事实质上是把SELinux直接关掉了这个做法在安全要求不高的个人服务器上勉强能接受但生产环境最好不要关。SELinux的坑在于默认策略会让原本正常的服务访问报错而且报错不一定直接提示“SELinux denied”而是显示权限不足。遇到这类问题先用ausearch -m avc -ts recent查一下最近的拒绝记录再针对性地调整布尔值或文件上下文比直接setenforce 0要科学得多。7.2 配置审核流程每次做安全配置变更我建议严格走“备份、测试、验证、记录”四步。任何配置文件的修改第一步永远是备份cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak.$(date %F)然后在小范围或测试机验证确认正常后再批量推送。推送完成后记录下变更内容和时间这样以后出问题能快速回滚也能回答“这台机器上到底改过什么”这个灵魂拷问。7.3 说一个我踩过的真实心态坑有段时间我觉得自己把服务器安全做得非常完善密钥登录、fail2ban、防火墙、日志转发都上了。然后某天凌晨两点监控告警说数据库CPU跑满登录上去一看rsyslog的日志转发配置因为磁盘写满挂了而挂载/var的分区正好因为日志膨胀被撑到100%。复盘时发现我忽略了logrotate对转发日志目录的轮转导致日志文件无限增长。这类安全问题不是被攻击者攻破而是自己配置不当产生的不可用。从此我养成了一个习惯安全配置的每一项都要问自己一句“如果这项配置失效会不会引发系统不可用”。平衡安全性和可用性才是做系统安全真正难的地方。太激进的最小化可能会让运维寸步难行太保守的配置又等于把门敞开。关键在于持续巡检、持续验证把安全当成一个不断优化的过程而不是某一天一顿操作就一劳永逸的工程。