☰
Linux服务器五维安全巡检:进程、网络、文件、日志、权限实战指南
2026/10/15 15:10:40 网站建设 项目流程

简介:本资源是一份面向IT运维工程师、系统管理员及网络安全初学者的服务器安全巡检标准化操作指南,聚焦生产环境中日常安全加固与风险排查核心需求。文档共5页,以Word格式(.docx)提供,大小163KB,内容完整可编辑,适用于A4打印与现场巡检记录。全文系统梳理了安全巡检的五大模块:重要性说明、关键关注点、九大必查项(含系统服务、账户权限、防火墙、防病毒系统、补丁状态、密码策略、日志分析、数据库及备份任务)、九步标准化巡检流程,以及异常处置原则与配套巡检表示例。结构清晰、步骤具体、术语规范,兼具理论指导性与实操落地性。目前已有165人学习下载,是快速建立巡检意识、掌握基础安全检查方法、完善内部运维SOP的实用型文档资料。

1. 服务器安全巡检不是“打钩清单”,而是对系统生命力的定期听诊

你有没有遇到过:凌晨三点告警说 CPU 突然飙到 99%,登录一看,是某个被遗忘的测试服务在疯狂拉取外部 API;或者某次例行更新后,SSH 登录延迟飙升,排查半天发现是 PAM 模块配置里多了一行已下线的 LDAP 认证源;又或者审计方一纸报告指出“存在未授权 SSH 密钥残留”,而你翻遍~/.ssh/authorized_keys却找不到——最后在/etc/ssh/sshd_config的AuthorizedKeysCommand指向的一个脚本里挖出三年前某次自动化部署留下的硬编码密钥路径?这些都不是故障,是巡检缺位的慢性失血。

《服务器安全巡检内容及方法.docx》这个标题看似平淡,实则指向一套可落地、可回溯、可度量的主动防御节奏:它不依赖漏洞扫描器的单次快照,也不止于“检查 root 密码是否够长”这种静态合规项,而是把服务器当作一个持续呼吸的实体,从进程行为、网络连接、文件完整性、日志脉搏、权限拓扑五个维度做动态切片。适合两类人:一是刚接手一批历史包袱较重的生产服务器的运维工程师,需要快速建立可信基线;二是正在搭建自动化运维体系的团队,要把人工经验沉淀为可触发、可验证、可嵌入 CI/CD 流水线的检查单元。本文不讲 ISO27001 条款映射,只讲你在终端里敲什么命令、看什么输出、改哪行配置、存哪些快照——所有操作均基于 Linux 发行版通用工具链(systemd、auditd、rsyslog、ss、lsof、rpm/deb 验证、AIDE),无需安装商业 Agent。


2. 巡检五维模型:为什么必须同时覆盖进程、网络、文件、日志、权限

2.1 进程维度:识别“活着但不该活”的幽灵进程

巡检不是查ps aux | grep nginx看服务是否在跑,而是确认进程的启动来源、资源占用合理性、以及与预期生命周期的一致性。常见误判是:看到python3 /tmp/.cache/update.py就删,结果发现是某监控 agent 的热更新模块;或看到java -jar app.jar就认为正常,却忽略其-Xmx参数被恶意篡改为16G导致内存耗尽。

正确做法是分三层验证:

  • 启动溯源:用systemctl status <service>查 systemd 单元状态,重点看Loaded:行的 unit 文件路径和Active:后的启动时间戳;对非 systemd 进程,用ps -o pid,ppid,comm,args -p <PID>追溯父进程链,再查cat /proc/<PID>/environ | tr '\0' '\n'看环境变量是否含可疑路径。
  • 资源合理性:top -b -n1 | head -20抓实时快照,但更关键的是比对历史基线——用sar -u 1 5(CPU)、sar -r 1 5(内存)连续采样,若某进程 CPU 占用率波动标准差 >40%,即触发人工复核。
  • 生命周期一致性:对 cron 启动的进程,检查crontab -l和/etc/cron.d/下文件修改时间;对 systemd timer 启动的,运行systemctl list-timers --all确认下次触发时间是否符合业务预期(如备份任务应在凌晨 2 点而非 2:03)。

提示:pstree -p是进程树可视化利器,但生产环境禁用pstree -a(会暴露完整命令行参数,可能含密钥),应改用pstree -p -s <PID>只显示祖先进程 ID。

2.2 网络维度:连接状态比端口开放更重要

“端口开着”不等于“服务健康”,“连接建立”不等于“通信合法”。巡检要穿透到连接级:谁在连、连哪里、连多久、传什么。

核心命令组合:

# 1. 查看所有 ESTABLISHED 连接(排除 LISTEN) ss -tunp state established | awk '{print $5,$6}' | sort | uniq -c | sort -nr | head -10 # 2. 对高频外连目标(如 185.199.108.153:443)反查进程 ss -tunp dst 185.199.108.153:443 | grep -v "users:" # 3. 检查异常监听地址(如 0.0.0.0 而非 127.0.0.1) ss -tuln | awk '$4 !~ /^127\.|^::1|^192\.168\./ {print $0}'

逻辑说明:第一行用ss替代netstat(后者在高并发下易卡死),state established过滤掉监听态,$5是远端地址,$6是进程信息;第二行针对可疑 IP 做精准反查,避免ss -tunp全量输出淹没关键信息;第三行用正则排除本地回环和内网地址,揪出暴露在公网的监听服务。

参数说明:-t(TCP)、-u(UDP)、-n(不解析域名,防 DNS 延迟)、-p(需 root 权限,显示进程);awk中$4是本地地址列,正则^127\.|^::1|^192\.168\.覆盖 IPv4/IPv6 常见私有网段。若发现0.0.0.0:22,需立即检查/etc/ssh/sshd_config的ListenAddress配置是否被注释——这是弱口令爆破的黄金入口。

2.3 文件维度:用 AIDE 构建不可篡改的文件指纹基线

ls -la /etc/passwd看权限只是表象,真正的风险是/etc/shadow被静默追加了新用户哈希,或/usr/bin/curl被替换成带后门的版本。手动校验不现实,必须用文件完整性监控工具。AIDE(Advanced Intrusion Detection Environment)是 Linux 发行版预装率最高、配置最轻量的选择。

初始化基线步骤:

# 1. 安装(CentOS/RHEL) yum install aide -y # Ubuntu/Debian 则用 apt install aide # 2. 生成默认配置(会写入 /etc/aide.conf) aide --init # 此命令生成 /var/lib/aide/aide.db.new.gz,需重命名为 aide.db.gz # 3. 安装数据库(关键!否则后续检查无效) mv /var/lib/aide/aide.db.new.gz /var/lib/aide/aide.db.gz # 4. 手动添加需监控的关键路径(编辑 /etc/aide.conf) echo "/etc/.* p+i+n+u+g+s+b+acl+selinux+xattrs+sha256" >> /etc/aide.conf echo "/usr/bin/.* p+i+n+u+g+s+b+acl+selinux+xattrs+sha256" >> /etc/aide.conf echo "/boot/.* p+i+n+u+g+s+b+acl+selinux+xattrs+sha256" >> /etc/aide.conf

逻辑说明:aide --init会扫描全盘并生成加密哈希库,但默认配置过于宽泛(如监控/root目录会导致每次history更新都告警),因此需在aide.conf中精简规则。p+i+n+u+g+s+b+acl+selinux+xattrs+sha256表示监控:权限(p)、inode(i)、链接数(n)、UID(u)、GID(g)、大小(s)、块数(b)、ACL、SELinux 上下文、扩展属性、SHA256 哈希——这是生产环境推荐的最小完备集。

参数说明:p(permissions)、i(inode)、n(link count)、u(uid)、g(gid)、s(size)、b(block count)是基础元数据;acl和selinux在启用了对应安全模块的系统中必开;xattrs覆盖 extended attributes(如user.xattr);sha256是哈希算法,比默认的md5更抗碰撞。

2.4 日志维度:从 rsyslog 到 journalctl 的双轨审计

日志不是“出了事才看”,而是巡检时判断系统是否“按预期呼吸”的听诊器。单一日志源有致命缺陷:/var/log/messages可能被攻击者清空,journalctl在无持久化配置时重启即丢。必须双轨并行。

双轨配置法:

# 1. 确保 journald 持久化(否则 journalctl 无历史) echo "Storage=persistent" >> /etc/systemd/journald.conf systemctl restart systemd-journald # 2. 配置 rsyslog 将关键日志同步到远程(防本地篡改) # 编辑 /etc/rsyslog.conf,取消注释并修改: $ActionFileDefaultTemplate RSYSLOG_ForwardFormat *.* @@10.0.1.100:514 # 使用 @@ 表示 TCP,@ 表示 UDP # 3. 本地日志轮转加固(防止磁盘占满导致日志丢失) echo "rotate 12" >> /etc/logrotate.d/rsyslog echo "maxage 365" >> /etc/logrotate.d/rsyslog

逻辑说明:Storage=persistent强制 journal 日志写入/var/log/journal/,否则默认存于/run/log/journal/(内存临时目录);@@是 rsyslog 的 TCP 转发模式,比 UDP 可靠(丢包重传),但需确保远程接收端(如 10.0.1.100)的 rsyslog 或 syslog-ng 已监听 514 端口;maxage 365设定日志最大保留 365 天,避免因maxsize触发轮转时旧日志被误删。

参数说明:rotate 12表示最多保留 12 个归档文件;maxage 365是日志文件最后修改时间距今的最大天数;RSYSLOG_ForwardFormat是预定义模板,确保时间戳、主机名等字段不丢失。

2.5 权限维度:超越 chmod 的细粒度访问控制验证

ls -l /etc/shadow显示-rw-r-----并不意味安全——如果shadow所属组shadow中包含了一个已被离职员工使用的账号,风险依然存在。权限巡检必须穿透到用户-组-文件三级关系链。

三步验证法:

# 1. 查找高危文件组成员(如 shadow、disk、wheel 组) getent group shadow disk wheel | awk -F: '{print $1,$4}' # 2. 对每个组成员,检查其是否存在于 /etc/passwd 且未被禁用 while IFS=: read -r user _ _ _ _ _ _; do if ! grep -q "^$user:" /etc/passwd; then echo "警告:组成员 $user 不在 /etc/passwd" elif grep -q "^$user:!!" /etc/shadow; then echo "警告:用户 $user 密码已锁定" fi done <<< "$(getent group shadow | cut -d: -f4 | sed 's/,/ /g')" # 3. 检查 sudoers 中的 NOPASSWD 权限是否过度开放 grep -E '^[^#].*NOPASSWD' /etc/sudoers /etc/sudoers.d/* 2>/dev/null | \ grep -v "ALL=(ALL) NOPASSWD: ALL" | \ awk '{print $1,$2,$3,$4,$5}'

逻辑说明:第一行用getent group统一查询本地和 LDAP 组,$4是组成员列表;第二行将成员列表转为空格分隔,逐个检查是否在/etc/passwd中存在(防 LDAP 用户被移除后组仍残留),并用grep "^$user:!!"检测密码锁定状态(!!表示密码被passwd -l锁定);第三行过滤sudoers中的NOPASSWD条目,排除最宽松的ALL=(ALL) NOPASSWD: ALL,聚焦具体命令授权。

参数说明:getent group比cat /etc/group更可靠,能合并本地和网络目录服务;grep -v排除已知宽松策略,避免噪音;awk '{print $1,$2,$3,$4,$5}'截取前五列,清晰显示用户、主机、执行者、命令、选项。


3. 巡检脚本化:把五维检查封装成可定时、可审计的 shell 工具

3.1 巡检主脚本:server-audit.sh的骨架设计

脚本不是功能堆砌,而是分阶段、可中断、带证据链的执行体。核心设计原则:

  • 阶段化:phase1_process、phase2_network… 每阶段独立函数,失败不影响后续;
  • 证据链:每个检查生成唯一时间戳子目录(如/var/log/audit/20240520_142301/),存入原始命令输出、截屏、哈希值;
  • 可中断:用trap捕获SIGINT,退出前自动打包当前证据目录并标记INCOMPLETE。

脚本骨架(关键部分):

#!/bin/bash AUDIT_DIR="/var/log/audit" TIMESTAMP=$(date +"%Y%m%d_%H%M%S") RUN_DIR="$AUDIT_DIR/$TIMESTAMP" mkdir -p "$RUN_DIR" # 定义各阶段函数 phase1_process() { echo "=== 进程维度检查 ===" > "$RUN_DIR/process.log" ps aux --sort=-%cpu | head -15 >> "$RUN_DIR/process.log" systemctl list-units --type=service --state=running | grep -E "(active|activating)" >> "$RUN_DIR/process.log" } phase2_network() { echo "=== 网络维度检查 ===" > "$RUN_DIR/network.log" ss -tunp state established | awk '{print $5,$6}' | sort | uniq -c | sort -nr | head -10 >> "$RUN_DIR/network.log" ss -tuln | awk '$4 !~ /^127\.|^::1|^192\.168\./ {print $0}' >> "$RUN_DIR/network.log" } # 主执行流 trap 'echo "巡检被中断,证据存于 $RUN_DIR (INCOMPLETE)"; exit 1' INT phase1_process phase2_network # ... 其他阶段 echo "巡检完成,证据存于 $RUN_DIR"

逻辑说明:mkdir -p "$RUN_DIR"确保目录存在;trap捕获Ctrl+C,避免中断后证据散落;ps aux --sort=-%cpu按 CPU 降序排列,head -15取前 15 行,避免日志过大;systemctl list-units加--state=running过滤非活跃服务,grep -E匹配active和activating(避免漏掉刚启动的服务)。

参数说明:--sort=-%cpu中-表示降序;--state=running是 systemd 的精确状态匹配;grep -E启用扩展正则,(active|activating)匹配两种状态。

3.2 定时巡检:用 systemd timer 替代 crontab 的三大优势

crontab -e写0 2 * * * /opt/audit/server-audit.sh很简单,但无法解决:

  • 任务超时未结束,下次又触发,导致进程堆积;
  • 某次执行失败,无日志留存,管理员不知情;
  • 需要依赖网络就绪(如远程日志服务器)才能执行,crontab 无依赖管理。

systemd timer 完美解决:

# 1. 创建 service 单元(/etc/systemd/system/audit.service) [Unit] Description=Server Security Audit After=network.target [Service] Type=oneshot ExecStart=/opt/audit/server-audit.sh User=root StandardOutput=journal StandardError=journal # 2. 创建 timer 单元(/etc/systemd/system/audit.timer) [Unit] Description=Run Server Audit Daily Requires=audit.service [Timer] OnCalendar=*-*-* 02:00:00 Persistent=true RandomizedDelaySec=300 [Install] WantedBy=timers.target

逻辑说明:After=network.target确保网络就绪后再启动;Type=oneshot表示执行完即退出;StandardOutput=journal将 stdout/stderr 写入 journal,便于journalctl -u audit.service查询;OnCalendar=*-*-* 02:00:00是精确到秒的 cron 替代;Persistent=true解决服务器关机导致的错过执行——下次开机立即补跑;RandomizedDelaySec=300防止集群内所有服务器在同一秒发起巡检,造成网络风暴。

参数说明:OnCalendar支持*-*-* 02:00:00(每天 2 点)、Mon,Fri *-*-* 02:00:00(每周一、五)等灵活语法;Persistent=true是 systemd timer 的独有能力;RandomizedDelaySec的单位是秒,300 秒即 5 分钟随机偏移。

3.3 证据归档:自动生成可验证的巡检报告包

每次巡检生成的原始日志是证据,但需封装成带签名、可解压、含摘要的交付物,供审计或交接。

归档脚本(archive-audit.sh):

#!/bin/bash if [ ! -d "$1" ]; then echo "错误:目录 $1 不存在" exit 1 fi # 1. 生成 SHA256 摘要 find "$1" -type f -not -name "SHA256SUMS" -print0 | xargs -0 sha256sum > "$1/SHA256SUMS" # 2. 打包并压缩(排除 .git 等无关目录) tar --exclude='*.git*' -czf "$1.tar.gz" -C "$(dirname "$1")" "$(basename "$1")" # 3. 用 GPG 签名(需提前配置 GPG 密钥) gpg --detach-sign --armor "$1.tar.gz" echo "归档完成:$1.tar.gz 和 $1.tar.gz.asc"

逻辑说明:find ... -print0 | xargs -0处理含空格的文件名;sha256sum > SHA256SUMS生成校验文件,供后续用sha256sum -c SHA256SUMS验证;tar --exclude排除版本控制目录,减小包体积;gpg --detach-sign --armor生成 ASCII 格式签名文件(.asc),验证时用gpg --verify "$1.tar.gz.asc" "$1.tar.gz"。

参数说明:--exclude='*.git*'通配符匹配.git和.gitignore;--detach-sign生成分离签名,不修改原文件;--armor输出 Base64 编码文本,便于邮件传输。


4. 避坑指南:五维巡检中踩过的 4 个真实血泪坑

4.1 现象:AIDE 检查总报/usr/bin/openssl文件变更,但手动 diff 无差异

原因:openssl二进制文件在每次执行时会动态生成.rodata段的内存映射,AIDE 默认监控m(mtime)和c(ctime),而openssl的 ctime 在每次调用时都会更新(因修改了内部缓存时间戳)。这不是篡改,是程序自身行为。
解决:在/etc/aide.conf中为/usr/bin/openssl单独配置规则,去掉c(ctime)监控:

/usr/bin/openssl p+i+n+u+g+s+b+acl+selinux+xattrs+sha256-m-c

-m-c表示排除 mtime 和 ctime,仅监控其他属性。同理适用于/usr/bin/python3、/usr/bin/java等 JIT 编译型程序。

4.2 现象:ss -tunp执行时报错Permission denied,但 root 用户下仍失败

原因:Linux 内核 4.18+ 默认启用net_admincapability 隔离,即使 root 用户,若未显式赋予CAP_NET_ADMIN,ss也无法读取进程信息。常见于容器环境或 SELinux enforcing 模式。
解决:

  • 容器内:启动时加--cap-add=NET_ADMIN;
  • 物理机:用setcap cap_net_admin+ep /usr/sbin/ss赋权(注意:ss通常在/usr/sbin/,非/bin/);
  • SELinux:临时设为 permissive 模式验证setsebool -P nis_enabled 1(不推荐生产环境长期开启)。

提示:用getcap /usr/sbin/ss查看当前 capability,避免重复赋权。

4.3 现象:journalctl --since "2 hours ago"查不到任何日志,但journalctl -n 10能看到最新日志

原因:journald 默认配置SystemMaxUse=100M,当磁盘空间紧张时,旧日志被自动清理,--since查询的时间范围超出保留窗口。
解决:增大日志保留空间,在/etc/systemd/journald.conf中:

SystemMaxUse=1G SystemMaxFileSize=100M MaxRetentionSec=3month

然后执行systemctl restart systemd-journald。注意MaxRetentionSec优先级高于SystemMaxUse,两者配合使用。

4.4 现象:巡检脚本在cron下执行成功,但systemd timer下失败,报错command not found: ss

原因:cron使用/bin/sh,而systemdservice 默认使用/usr/bin/bash,但 PATH 环境变量不同。ss命令在 CentOS 中位于/usr/sbin/,该路径不在 systemd 的默认 PATH 中。
解决:在 service 单元的[Service]段显式声明 PATH:

Environment="PATH=/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin"

或直接在ExecStart中用绝对路径:ExecStart=/usr/sbin/ss -tunp。


5. 巡检结果验证:用三次交叉比对法确认基线可靠性

5.1 时间维度交叉:同一台服务器,三次不同时段的巡检对比

不要只信某一次巡检结果。我一般在一台新接管的服务器上,执行三次间隔 24 小时的巡检(02:00,02:00+1d,02:00+2d),重点比对:

  • 进程维度:ps aux --sort=-%cpu | head -5的 PID 是否稳定(PID 变化正常,但进程名和命令行不应突变);
  • 网络维度:ss -tunp state established | awk '{print $5}' | sort | uniq -c | sort -nr | head -3的远端 IP 排名是否一致(如10.0.1.100:514应始终在前三);
  • 文件维度:aide --check的变更条目数是否 <3(允许/var/log/等动态目录的少量变化)。

若三次结果中某项波动剧烈(如外连 IP 排名从第 1 变到第 10),说明该维度存在未识别的周期性任务或异常行为,需深入journalctl -u <service>追踪。

5.2 空间维度交叉:同构服务器集群的横向一致性检查

对 5 台同用途服务器(如 Web 前端集群),抽取相同巡检项做横向比对:
| 服务器 |ss -tuln | wc -l(监听端口数) |rpm -qa | wc -l(安装包数) |getent group wheel | cut -d: -f4 | wc -w(wheel 组成员数) |
|---------|-------------------------------|---------------------------|-----------------------------------|
| web01 | 12 | 328 | 4 |
| web02 | 12 | 328 | 4 |
| web03 |15|335|7|
| web04 | 12 | 328 | 4 |
| web05 | 12 | 328 | 4 |

web03 的三项指标均异常偏高,立即登录检查:发现其httpd配置中多启用了mod_proxy和mod_ssl,且wheel组被误加入了 3 个临时调试账号。这就是空间交叉的价值——单台服务器的“合理”可能是集群的“异常”。

5.3 工具维度交叉:用不同原理工具验证同一对象

对关键结论,绝不用单一工具背书。例如验证“SSH 是否仅监听 127.0.0.1”:

  • 工具1(ss):ss -tuln | grep ":22"→127.0.0.1:22
  • 工具2(netstat):netstat -tuln | grep ":22"→127.0.0.1:22(验证 ss 结果非工具 Bug)
  • 工具3(配置文件):grep -E "^(ListenAddress|Port)" /etc/ssh/sshd_config→ListenAddress 127.0.0.1(验证监听行为由配置驱动,非临时命令)
    三者一致,结论才可信。若ss显示0.0.0.0:22而配置文件写的是127.0.0.1,说明sshd未重载配置(systemctl reload sshd),此时工具交叉立刻暴露运维疏漏。

5.4 建立你的巡检后悔药:一键回滚可疑变更

巡检发现异常,最怕“改完更糟”。我在所有服务器上预置了rollback-audit.sh:

#!/bin/bash # 回滚最近一次 AIDE 报告的变更(需先运行 aide --check > /tmp/aide-last.log) if [ -f "/tmp/aide-last.log" ]; then # 提取被修改的文件路径(AIDE 输出格式:changed: /etc/passwd) CHANGED_FILES=$(grep "changed:" /tmp/aide-last.log | awk '{print $2}') for file in $CHANGED_FILES; do if [ -f "$file.rpmsave" ]; then cp "$file.rpmsave" "$file" echo "已从 $file.rpmsave 恢复 $file" elif [ -f "$file.rpmnew" ]; then cp "$file.rpmnew" "$file" echo "已从 $file.rpmnew 恢复 $file" else echo "警告:$file 无备份,跳过" fi done else echo "无上次 AIDE 日志,退出" fi

逻辑说明:RPM 包管理器在升级时会自动创建.rpmsave(旧版)和.rpmnew(新版)备份,此脚本利用这一机制实现零成本回滚。grep "changed:"匹配 AIDE 的标准输出格式;cp覆盖原文件,echo记录动作。

参数说明:.rpmsave是 RPM 升级时对已修改配置文件的备份;.rpmnew是 RPM 升级时对新配置文件的保存;二者共存时,管理员需手动合并,但巡检发现异常时,.rpmsave通常是安全的回滚点。

我坚持一个习惯:每次巡检后,花 5 分钟手写一条「本次最意外发现」,比如“web03 的 wheel 组多出 3 个账号,源于上周 Jenkins 自动化脚本 bug”,然后把它钉在团队知识库首页。不是为了追责,而是让下一次巡检的人,一眼就知道这里曾埋过雷。希望帮到你。

本文还有配套的精品资源,点击获取

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

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

立即咨询