安全加固这件事最尴尬的地方在于:它做得好时毫无存在感,做砸一次就可能让前面所有努力归零。很多团队的加固停留在"装了个 fail2ban 就算安全了",但真正出问题时,入口往往是三年前那个没人管的测试账号、某次临时开放的 6379 端口、或者一台从来没打过补丁的边缘机器。本文把服务器加固拆成五个可执行的模块——账号、端口、补丁、权限、日志审计——每个模块都给出具体的检查命令、合格标准和操作步骤,你可以直接拿它当基线检查表,一台一台地过。
一、账号加固:先搞清楚谁有权限进来
账号是权限的入口。加固的目标是三件事:没有多余账号、没有弱口令、没有不该有的高权限。
# 查看所有具备 root 权限(UID 为 0)的账号,正常应只有 root 一个
awk -F: '($3 == 0) {print $1, $3, $7}' /etc/passwd
# 查看空口令账号(有输出即为高危)
awk -F: '($2 == "") {print $1 " 空口令"}' /etc/shadow
# 查看可登录 shell 的账号(/sbin/nologin 与 /bin/false 属正常)
awk -F: '($7 !~ /nologin|false/) {print $1, $3, $7}' /etc/passwd
# 查看所有账号最近登录时间,长期未使用却存在即应清理
lastlog
# 查看近期登录记录
last -n 30 -i
# 锁定不再使用的账号(推荐用 -L 锁定而非直接删除,保留可追溯性) usermod -L olduser passwd -l olduser # 确认需要删除时连同家目录一起清理 userdel -r olduser # 设置口令有效期(90 天过期,提前 7 天提醒) chage -M 90 -W 7 opsuser chage -l opsuser
口令策略建议在 /etc/login.defs 中统一配置,常见的基线值是:PASS_MAX_DAYS 90、PASS_MIN_DAYS 1、PASS_MIN_LEN 12、PASS_WARN_AGE 7、UMASK 027。注意 login.defs 只对新创建或修改口令的账号生效,已有账号需要用 chage 逐个补齐。
登录失败锁定建议用 PAM 模块实现,以 RHEL 系的 pam_faillock 为例:
# /etc/pam.d/system-auth 与 /etc/pam.d/password-auth(Debian 系为 /etc/pam.d/common-auth) auth required pam_faillock.so preauth silent deny=5 unlock_time=900 fail_interval=900 auth required pam_faillock.so authfail deny=5 unlock_time=900 fail_interval=900 account required pam_faillock.so # 查看与解除某账号的失败锁定 faillock --user opsuser faillock --user opsuser --reset
sudo 权限要遵循最小化:给具体命令而不给 ALL。例如只允许某人重启服务,就写成 opsuser ALL=(root) /bin/systemctl restart nginx,而不是 opsuser ALL=(ALL:ALL) ALL。修改 sudoers 务必用 visudo,它会做语法检查,避免写错导致所有 sudo 失效。定期用 sudo -l -U opsuser 复核实际权限。
二、端口加固:把暴露面压到最小
# 列出所有监听端口及进程 ss -lntup # 只看监听中的 TCP ss -lntp # 从外部视角核对(仅对自有资产执行) nmap -Pn -sT -sV -T4 --top-ports 1000 203.0.113.10 # 查看防火墙规则 iptables -L INPUT -n -v --line-numbers ufw status numbered firewall-cmd --list-all
# 建议的默认策略:默认拒绝入站 ufw default deny incoming ufw default allow outgoing ufw allow from 办公出口网段 to any port 22 proto tcp ufw allow 80/tcp ufw allow 443/tcp ufw enable # 或用 iptables 的基础四件套 iptables -P INPUT DROP iptables -A INPUT -i lo -j ACCEPT iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT iptables -A INPUT -p tcp --dport 22 -s 办公出口网段 -j ACCEPT
合格标准很明确:一台公网 Web 服务器对外开放端口应只有 80、443 和限制来源的 22;数据库(3306、5432、27017)、缓存(6379、11211)、容器管理(2375)、etcd(2379)等端口绝不允许出现在公网,只监听内网地址或 127.0.0.1。判断不了某个端口是否必需的,先关掉观察一周。
三、补丁与软件:把已知漏洞补上
绝大多数入侵利用的都是早已公布补丁的漏洞。补丁管理的关键不是"打不打",而是"有没有可持续的机制"。
# Debian / Ubuntu apt update apt list --upgradable apt upgrade -y # 配置无人值守安全更新 apt install -y unattended-upgrades dpkg-reconfigure --priority=low unattended-upgrades cat /etc/apt/apt.conf.d/20auto-upgrades # RHEL / CentOS / Rocky / AlmaLinux dnf check-update dnf update -y dnf install -y dnf-automatic systemctl enable --now dnf-automatic.timer # 判断更新后是否需要重启(RHEL 系) needs-restarting -r # Debian 系可检查是否存在待重启标志 ls /var/run/reboot-required 2>/dev/null && echo '需要重启'
| 补丁策略 | 适用对象 | 建议频率 | 注意事项 |
|---|---|---|---|
| 自动安全更新 | Web 服务器、边缘节点、无状态服务 | 每天自动 | 需配合监控,防止更新引入兼容性问题 |
| 窗口内手动更新 | 数据库、核心业务、有状态服务 | 每月一次维护窗口 | 必须先在测试环境验证 |
| 紧急补丁 | 高危漏洞(如远程代码执行) | 评估后 24 至 72 小时内 | 无法立即修复时先做缓解措施(关端口、加 WAF 规则) |
| 内核更新 | 所有主机 | 每季度或有高危公告时 | 需要重启,需与业务方协调窗口 |
补丁之外的另一半工作是卸载不需要的软件。默认镜像里常带有 Telnet、FTP、rpcbind、avahi 等用不到却自带监听的服务。用 ss -lntup 发现后就地禁用并卸载:systemctl disable --now 服务名,再 apt purge 或 dnf remove。少一个服务就少一个攻击面。
四、权限与文件系统:避免"一处失守、全盘沦陷"
# 关键文件的标准权限 chmod 644 /etc/passwd chmod 600 /etc/shadow chmod 600 /etc/gshadow chmod 644 /etc/group chmod 600 /etc/ssh/sshd_config ls -l /etc/passwd /etc/shadow /etc/ssh/sshd_config # 查找全局可写的文件与目录(正常应极少) find / -xdev -type f -perm -0002 -print 2>/dev/null | head -20 find / -xdev -type d -perm -0002 -print 2>/dev/null | head -20 # 查找 SUID / SGID 程序(权限提升的常见跳板,逐个确认必要性) find / -xdev -type f -perm -4000 -print 2>/dev/null find / -xdev -type f -perm -2000 -print 2>/dev/null # 对关键文件加不可修改属性(注意:会阻止 useradd 等写操作,需评估) chattr +i /etc/passwd /etc/shadow lsattr /etc/passwd # 检查无属主或无属组的文件 find / -xdev -nouser -o -nogroup 2>/dev/null | head -20
服务运行账号同样要收敛原则:绝不用 root 跑业务服务。Nginx 用 nginx 或 www-data,MySQL 用 mysql,Redis 用 redis,各自独立且不可登录(shell 设为 /sbin/nologin)。这样即使某个服务被攻破,攻击者也只拿到一个低权限账号,横向移动成本大幅上升。
Web 目录还要单独处理:代码目录属主应为部署用户而非 root,上传目录必须禁止执行脚本(Nginx 里对上传路径禁止解析 PHP),并且不要给 777 权限——这在很多"图省事"的部署里非常常见,也是后门文件能落地的直接原因。
五、SSH 与登录安全:加固的重中之重
# /etc/ssh/sshd_config 关键项 PermitRootLogin no PasswordAuthentication no PubkeyAuthentication yes PermitEmptyPasswords no MaxAuthTries 3 MaxSessions 5 LoginGraceTime 30 X11Forwarding no AllowTcpForwarding no ClientAliveInterval 300 ClientAliveCountMax 2 # AllowUsers opsuser deploy # 校验与加载 sshd -t systemctl reload sshd
# fail2ban sshd 监狱 [sshd] enabled = true port = ssh filter = sshd logpath = /var/log/auth.log # RHEL 系为 /var/log/secure maxretry = 3 findtime = 600 bantime = 3600 bantime.increment = true bantime.factor = 2 bantime.maxtime = 604800 ignoreip = 127.0.0.1/8 办公出口网段
顺序提醒:先验证密钥能登录、保留一个活动会话,再关闭 PasswordAuthentication;先把办公出口 IP 加进 ignoreip,再启用 fail2ban。这两步做反了,最常见的后果就是把自己锁在门外。
六、日志与审计:让一切有据可查
日志是事后追溯的唯一依据,也是唯一能在"还没出事"时就发现异常的模块。加固目标是:日志要留得住、改不了、看得到。
# 开启 journald 持久化(默认可能只存在内存中,重启即丢) mkdir -p /var/log/journal sed -i 's/#\?Storage=.*/Storage=persistent/' /etc/systemd/journald.conf systemctl restart systemd-journald journalctl --disk-usage # 常用查询 journalctl -u sshd --since '24 hours ago' journalctl -u nginx --since '1 hour ago' -n 100 journalctl -p err -b
# /etc/audit/rules.d/hardening.rules 基础审计规则 -w /etc/passwd -p wa -k identity -w /etc/shadow -p wa -k identity -w /etc/sudoers -p wa -k priv_change -w /etc/ssh/sshd_config -p wa -k sshd_config -w /etc/crontab -p wa -k cron_change -w /root/.ssh/authorized_keys -p wa -k sshkey_change -a always,exit -F arch=b64 -S chmod -S chown -S fchmod -S fchown -F auid>=1000 -F auid!=-1 -k perm_mod -a always,exit -F arch=b64 -S unlink -S unlinkat -S rename -S renameat -F auid>=1000 -F auid!=-1 -k delete # 加载并验证 systemctl restart auditd auditctl -l # 查询审计记录 ausearch -k identity -i | tail -20 ausearch -k sshkey_change -i | tail -20 aureport --summary
日志还要做两件容易被忽略的事:集中化和防篡改。集中化可以简单地在 /etc/rsyslog.d/ 里加一行 *.* @@日志服务器IP:514,把日志实时发到独立的日志服务器——这样即使本机被攻破,攻击者也无法抹掉远程留存的那份。防篡改则依赖 auditd 对 /var/log/ 的监控,以及给日志文件设置合适的权限。
七、加固实施清单
- 资产盘点:列出所有服务器、用途、负责人、对外开放端口,形成清单。没在清单上的机器往往是风险最高的那台。
- 账号清理:删除或锁定无用账号,检查 UID 0 账号与空口令账号,配置口令有效期与失败锁定策略。
- SSH 加固:密钥认证、禁 root 直登、
MaxAuthTries 3、fail2ban、限制来源 IP。 - 端口收敛:
ss -lntup盘点,服务改监听内网地址,防火墙默认拒绝,安全组同步收敛。 - 补丁更新:配置自动安全更新,安排维护窗口处理需要重启的内核更新,卸载无用服务。
- 权限收窄:修正关键文件权限,清理全局可写文件,核查 SUID/SGID,服务用独立非 root 账号运行。
- 日志审计:开启 journald 持久化,配置 auditd 规则,日志集中化,设置日志保留期与轮转。
- 建立周期巡检:把上述检查做成每月一次的例行任务,新机器上线时用同一份清单验收。
- 固化为镜像或配置管理:用 Ansible、cloud-init 或自定义镜像把加固项固化,避免每台机器手工操作产生偏差。
八、加固项速查表
| 模块 | 检查命令 | 合格标准 |
|---|---|---|
| 账号 | awk -F: '($3==0){print}' /etc/passwd | UID 0 账号只有 root;无空口令账号 |
| 账号 | lastlog、last -n 30 | 长期未使用账号已锁定;无异常来源登录 |
| 端口 | ss -lntup | 公网仅 80、443、22(限源);无数据库/缓存端口暴露 |
| 端口 | ufw status numbered | 默认拒绝入站,放行规则均可解释用途 |
| 补丁 | apt list --upgradable / dnf check-update | 无长期未修复的高危更新;已启用自动安全更新 |
| 权限 | find / -xdev -perm -4000 -type f | SUID 程序逐个有明确用途,无多余项 |
| 权限 | ls -l /etc/shadow | 权限 600,属主 root |
| SSH | sshd -T | grep -E 'permitrootlogin|passwordauth' | PermitRootLogin no;PasswordAuthentication no |
| 日志 | journalctl --disk-usage | 持久化已开启,容量与保留期符合预期 |
| 审计 | auditctl -l | 关键文件与权限变更规则已加载且持久化 |
小结
服务器加固不是一次性项目,而是一套需要固化进流程的基线。五个模块各有明确抓手:账号看"有没有多余的、有没有弱口令、有没有过高权限";端口看"公网只剩必需的几个";补丁看"有没有可持续的自动更新机制";权限看"服务是否用非 root 跑、关键文件权限是否正确";日志审计看"留得住、改不了、看得到"。把这五个模块写成清单,上线前验收一台、每月巡检一遍,并用 Ansible 或镜像固化,安全水位就会长期稳定在及格线以上。
避坑建议
- 不要在远程 SSH 会话里直接启用防火墙:务必先放行 22 再加默认拒绝策略,否则会当场断连。
- 不要给
/etc/passwd、/etc/shadow盲目加chattr +i:这会导致useradd、passwd等命令失败,加之前先确认后续是否还要改账号。 - 不要把 sudoers 改成 ALL 了事:权限应当精确到具体命令,否则等同于给了 root。
- 不要只加固系统不加固应用:Web 中间件、CMS、插件、后台管理页面同样是关键攻击面,弱口令后台和未修复的 CMS 漏洞非常常见。
- 不要开了审计就不管日志容量:auditd 规则过宽会迅速写满磁盘,规则要聚焦关键文件,并配置日志轮转与磁盘告警。
- 不要依赖一次性加固:新机器上线、新服务部署、人员变动都会让基线漂移,巡检机制比单次加固更重要。
- 不要把备份和日志放在同一台机器上:本机被攻破时它们会一起失守,集中化和异地留存是基本要求。
企业QQ咨询




