1. 从一次真实入侵复盘聊起
去年帮某朋友公司排查一台被入侵的Linux服务器,拿下root权限的方式相当朴实无华:SSH端口直接暴露在公网,root账号密码是Admin@123,Fail2ban没装,密码策略形同虚设。扫描器一天之内就能搞定这种目标,根本不需要什么高级漏洞。当时我做的第一件事不是杀进程、清后门,而是把整个系统的账号、登录方式、口令策略和端口监听全部过了一遍,重新加固后才开始处理入侵痕迹。那次之后我得到一个结论:Linux系统安全的核心不是装了多少安全软件,而是账号、登录、口令和端口这四个基础维度是否做到足够扎实。
这篇文章就围绕这四个维度展开,全部是实际运维中一定会用到的操作和思路。不管你是刚接手服务器的新人,还是已经跑了好几年生产环境的老手,按照这套方法来一次系统性的自检和加固,都能实打实降低被攻破的概率。内容偏实操,每一段命令都可以直接复制运行,但在生产环境执行之前请务必先在测试机验证。
2. 账号安全:从源头管好每一把钥匙
2.1 用户权限分配的第一原则
Linux系统的账号体系本质上是身份的标识与权限的边界。当多个用户或服务共用一个服务器时,如果权限分配混乱,攻击者拿到任何一个低权限账号后就有机会横向提权。我见过很多团队为了省事,直接给所有开发者开sudo权限,这等于把整栋大楼的万能钥匙复制了几十份。
正确的做法是遵循最小权限原则:每个用户只拥有完成其工作所需的最小权限集。比如普通的应用部署账号只需要对指定目录有读写权限,不需要系统管理权限;备份账号只需要能读取需要备份的数据并写入备份存储,不需要登录Shell。可以针对性地创建nginx-deploy这类专用账号,用sudo细粒度授权甚至sudo -l限制其可执行的命令集合,而不是简单地把人加到wheel组里。
2.2 排查隐藏账号与空口令账号
账号清理是加固的第一步,借助几个关键检查项快速识别风险点。
# 查看所有本地账号(UID从1000开始的基本都是普通用户) awk -F: '$3>=1000 {print $1, $3, $7}' /etc/passwd # 查找UID为0的账号(root权限账号,正常只有一个root) awk -F: '$3==0 {print $1}' /etc/passwd # 检查是否有用户在/etc/passwd中无口令占位(正常应该是x或*) awk -F: '($2=="") {print $1" 空口令"}' /etc/shadow重点看三方面:UID为0的账号是否只有root;是否存在不认识的账号;是否有空闲账号长期未登录。awk命令配合/etc/passwd和/etc/shadow就能快速筛出异常情况。普通用户被改成UID 0是后门常用手法,检查时我习惯用find / -user root -type f -perm -4000再查一遍SUID文件,双保险。
处理空闲账号时先和对应负责人确认是否还需要保留,确认无用后:
# 锁定账号而非简单删除,保留审计痕迹 usermod -L username # 如果需要彻底删除 userdel -r username2.3 sudo权限收敛实操
查看当前sudo授权情况:
grep -E "^sudo|^wheel" /etc/group cat /etc/sudoers.d/* 2>/dev/null more /etc/sudoerssudoers文件建议细粒度到命令级别。典型的收紧思路是:用Cmnd_Alias定义一组可执行命令,例如只允许应用管理员用systemctl管理特定服务,不允许直接修改root密码或编辑/etc/shadow。这样即便应用账号被攻破,攻击者的操作半径也被限制在极小范围内。顺便提醒,绝对不要用sudo ALL=(ALL) ALL这种毫无节制的授权方式,一旦账号失守牵连面太大了。
3. 登录安全:守住系统的每一道门
3.1 SSH服务加固要点
绝大多数Linux服务器的远程管理都依赖SSH,但SSH本身如果配置不当,基本等于把家门钥匙挂在门口。先看几个常用配置项:
# 编辑 /etc/ssh/sshd_config PermitRootLogin no # 禁止root直接登录 PasswordAuthentication no # 禁用密码登录,只用密钥 PubkeyAuthentication yes # 开启公钥认证 MaxAuthTries 3 # 单次连接最大认证尝试次数 LoginGraceTime 30 # 登录超时时间 Protocol 2 # 禁用老旧的SSHv1协议 X11Forwarding no # 关闭X11转发 AllowUsers alice bob # 白名单模式,只允许指定用户登录关于PermitRootLogin no,很多新手会担心:禁了root那我怎么管理?先建一个有sudo权限的普通用户,用普通用户登录后再通过sudo提权,完全覆盖了日常管理需求。而且root直接登录的最大问题是:攻击者只需要暴力破解一个用户名(root)和一个密码,成功率远高于需要先猜测用户名的场景。禁用之后暴力破解的起点就提高了。
PasswordAuthentication no是另一项争议操作。很多维护者担心密钥丢了进不去,但这个担心可以用多把备用密钥、带外管理(如云厂商控制台VNC)来解决。密码登录被禁掉后,暴力破解的入口被彻底切断,这是我做过那么多次加固后认为性价比最高的一项配置。如果团队规模小、暂时做不到纯密钥登录,退一步用PasswordAuthentication yes也至少要配合Fail2ban和强密码策略。
3.2 密钥认证的规范化管理
公钥认证开启后,密钥的生成、分发和保护必须有章法。建议每人生成独立的密钥对,不要几个人共用同一把私钥;私钥一定要设置passphrase,设置后即使私钥文件泄露,攻击者也无法直接使用。
# 生成ed25519类型密钥,比RSA更安全且长度短 ssh-keygen -t ed25519 -a 64 -C "your_email@example.com" # 分发公钥到目标服务器 ssh-copy-id -i ~/.ssh/id_ed25519.pub user@servered25519是我目前推荐的默认选择,性能好、安全性强,绝大多数现代系统都支持。如果遇到老系统不支持,退而求其次用RSA 3072位以上。同时建议在服务器上开启-o IdentitiesOnly yes这类客户端配置,避免密钥尝试顺序混乱的问题。
3.3 Fail2ban与暴力破解防护
密钥认证普及之前(甚至之后),Fail2ban都是防暴力破解的常规主力。它通过动态封禁恶意IP的方式,把频繁失败的来源地址直接挡在门外。
apt install fail2ban -y # Debian系 yum install fail2ban -y # RHEL系核心配置在/etc/fail2ban/jail.local:
[DEFAULT] bantime = 3600 findtime = 600 maxretry = 3 [sshd] enabled = true backend = systemd这套配置含义是10分钟内同一个IP失败3次就封禁1小时。实际生产环境可以根据暴露程度调整,公网直连的建议maxretry=3加上bantime=86400;内网环境可以放宽一些,避免误封。Fail2ban部署后要测试一下:故意输错几次密码,然后观察iptables -L -n里是否出现了封禁条目。
注意:Fail2ban不是万能的,它只针对暴力破解,对基于合法凭证的撞库、钓鱼攻击无能为力。它的作用是显著提高攻击门槛,而不是替代账号口令本身的安全性。
3.4 PAM认证与登录限制
PAM(可插拔认证模块)在SSH之外提供更底层的认证能力。限制登录来源和并发会话数,可以在PAM层面直接配置:
# /etc/security/access.conf # 只允许特定来源访问指定账号(示例) + : alice : 192.168.1.0/24 - : ALL : ALL再配合/etc/security/limits.conf限制用户最大登录数:
alice hard maxlogins 2这种限制能有效防止账号在多个终端被同时使用,某些离职人员如果还残留账号权限,也能通过并发数限制间接约束其行为。PAM是个威力很大但容易配错的地方,每次修改前记得备份原配置,并且开一个新会话测试登录无误后再关掉当前会话,防止把自己锁在外面。
4. 口令安全:密码策略不只是复杂度
4.1 密码复杂度与老化周期配置
Linux系统的密码策略由PAM控制,重点在/etc/pam.d/system-auth(RHEL系)或/etc/pam.d/common-password(Debian系)。经典配置如下:
password requisite pam_pwquality.so retry=3 minlen=14 dcredit=-1 ucredit=-1 lcredit=-1 ocredit=-1 password required pam_unix.so use_authtok sha512 shadow解释一下参数含义:minlen=14强制密码最短14位;dcredit=-1要求至少包含1位数字;ucredit=-1要求至少1位大写字母;lcredit=-1要求至少1位小写字母;ocredit=-1要求至少1个特殊字符。retry=3是允许用户重试的次数。
老化周期在/etc/login.defs中配置:
PASS_MAX_DAYS 90 PASS_MIN_DAYS 7 PASS_WARN_AGE 14含义分别是密码最长使用90天、最短7天(防止用户立刻改回旧密码)、过期前14天提醒。这里有个容易被忽略的点:修改/etc/login.defs后不会立即对已有用户生效,需要执行:
chage -M 90 username或者触发一次密码变更,否则老账号的策略保持不变。
4.2 密码哈希算法的演进
Linux存储密码的/etc/shadow字段格式中,$6$代表SHA-512,$y$代表yescrypt(较新发行版的默认值),$1$则是老旧的MD5。检查一下:
awk -F: '$2 ~ /^\$1\$/ {print $1" 使用MD5"} ' /etc/shadow如果还在用MD5哈希,建议尽快升级认证算法。现代发行版大多使用yescrypt或SHA-512,强度足够。密码哈希的原理简单说就是不可逆的数学变换,攻击者拿到/etc/shadow后只能通过暴力猜测比对哈希值。算法越强,单次猜测的耗时越长,破解成本成倍上升。
值得一提的还有盐值(salt)的概念。每个用户的盐值不同,意味着同样的密码产生完全不同的哈希值,彩虹表攻击基本失效。这也是为什么/etc/shadow不能被随意读取的底层原因——拿到哈希只是第一步,破解成本依然是天文数字。
4.3 历史密码与重用控制
用户频繁改密后最常做的事就是改回老密码。通过PAM模块限制密码重用:
password sufficient pam_unix.so remember=5remember=5会记住用户最近5次的历史密码,换新密码时不允许重复。对于合规要求严格的环境可以设置到10,但对用户体验的影响也要有预期。
4.4 实际案例:密码策略落地的正确顺序
某次给客户的100多台服务器统一制定密码策略,我没有直接批量改PAM配置,而是分了三步:先在几台测试机验证策略配置、确认不影响正常登录流程和服务启动;再推送到非核心环境观察一周,收集误报和用户反馈;最后才逐步覆盖到生产环境。结果依然有台老服务因为硬编码了短密码而遭殃。所以口令策略的落地除了改配置,还要做一轮口令强度审计,把不符合要求的已有账号密码强制重置或通知到人。
Linux下可以用john或hydra等工具对/etc/shadow做一次弱密码扫描,但要注意合规和授权。内部审计没问题,千万别对未授权的系统做这类操作。
5. 端口防护:收敛攻击面
5.1 端口监听状态检查
端口是服务暴露给网络的入口,每个开放的端口都是一条潜在的入侵路径。检查系统的端口监听情况:
ss -lntup重点关注监听在0.0.0.0或::的端口,这代表对所有网络接口开放。对比业务需求,把非必要监听的端口全部关掉。比如服务器上如果跑着测试用的Node服务监听在3000端口、MySQL监听在3306端口,赶紧确认是否真的需要公网访问。
ss -lntup的输出会显示进程名和PID,进一步定位某个端口是哪个服务启动的:
lsof -i :3306 fuser -v 3306/tcp定位后根据业务决定是绑内网地址、从防火墙规则中移除还是直接停掉服务。
5.2 防火墙规则的层次设计
防火墙的正确姿势是白名单模式,默认拒绝一切入站流量,只放行明确需要的服务和来源。iptables的规则链设计可以简单从三层考虑:第一层是放行明确允许的入站端口(如SSH、HTTPS);第二层是允许已建立连接及相关返回流量;第三层是默认DROP。
nftables在较新的发行版上逐渐取代iptables成为主流前端,但底层概念一致。云服务器用户除了操作系统内部防火墙,还要检查安全组规则。这两层不是二选一,而是两层都要配置。我处理过一个案例:服务器上iptables规则是空的,攻击者直接探测到了Redis端口未授权访问,而云安全组居然也放行了6379。双重防线任何一边失效都可能导致灾难,两层一起做才靠谱。
下面给一份防火墙规则的参考:
# 清空规则(谨慎使用,先确认不是远程连接) iptables -F # 默认策略 iptables -P INPUT DROP iptables -P FORWARD DROP iptables -P OUTPUT ACCEPT # 放行已建立的连接 iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT # 放行loopback iptables -A INPUT -i lo -j ACCEPT # 放行SSH(假设端口为22,建议改为非默认端口) iptables -A INPUT -p tcp --dport 22 -j ACCEPT # 放行Web服务 iptables -A INPUT -p tcp --dport 80 -j ACCEPT iptables -A INPUT -p tcp --dport 443 -j ACCEPT这里有一个高频事故点:修改防火墙规则时丢了当前的SSH连接。执行iptables -F之前务必确认有一条明确的放行规则,或者用at定时任务做规则恢复,防止自己把自己关在门外。
5.3 端口扫描与自检
自检系统最直接的方式是外部扫描自己的端口。可以使用nmap从另一台机器扫描:
nmap -sS -sV -p 1-65535 your_server_ip看到开放的端口列表后逐一确认是否业务需要。如果发现预期之外的端口开着,立刻定位进程、确认是否异常,该关就关。这个习惯建议每隔一段时间做一次,尤其在大量变更之后。
由于某些系统自带的防护机制(如Fail2ban)可能会对扫描限流,扫描结果中显示filtered的端口也要手动复查,确认是防火墙有规则还是别的机制在起作用。
5.4 服务最小化与端口清理
端口背后是进程,进程背后是服务。清理端口的前提是清理无用的服务:
# 查看系统开启了哪些服务 systemctl list-units --type=service --state=running # 批量禁用不需要的 systemctl disable --now cups systemctl disable --now avahi-daemon systemctl disable --now postfix每种服务都有对应的暴露面,停掉一个不必要的服务,比任何防火墙规则都更彻底。防火墙挡的是外部流量,但挡不住本地进程发起的连接;进程本身有漏洞时,防火墙也起不了作用。所以端口防护更本质的夯实方式,是减少不必要的监听进程。原则是:不需要开的服务不装、不需要开的功能不启、不需要的端口不放行。
6. 自动化审计:让安全加固持续有效
6.1 定期检查清单
手工做一次加固很简单,难的是持续保持。维护一台服务器不是搞完就完事,建议把下列检查项固化成一个定时任务,或用脚本定期生成审计报告:
# 检查空口令账号 awk -F: '($2=="") {print}' /etc/shadow # 检查UID 0账号 awk -F: '($3=="0") {print}' /etc/passwd # 检查最近登录记录 last -20 # 检查失败登录日志 grep "Failed password" /var/log/auth.log | tail -20 # 检查监听端口 ss -lntup这些命令组合成bash脚本加到crontab里,每天跑一次并把结果发到指定邮箱或集中日志平台,能有专人跟进异常当然更好,没有的话至少留个痕迹。
6.2 日志监控与分析
Linux安全审计绕不开日志。核心日志文件包括:
| 日志文件 | 作用 |
|---|---|
/var/log/auth.log | Debian系认证日志,记录登录、sudo等事件 |
/var/log/secure | RHEL系认证日志 |
/var/log/wtmp | 登录记录,用last读取 |
/var/log/btmp | 失败登录记录,用lastb读取 |
/var/log/syslog | 系统日志 |
重点看auth.log里反复出现的Failed password、Invalid user、Connection closed等记录。正常环境下这些日志的量会随时间线性增长,如果某个时间段突然爆发,大概率正在被扫描或暴力破解。lastb能看失败的登录尝试,同时配合last确认是否存在非本人操作的登录成功记录。
6.3 基线模板与自动修复
对于多台服务器的场景,基线配置一定要做成模板。把sshd_config、pam配置、防火墙规则、用户策略全部纳入配置管理,用工具统一分发。我见过有些团队30台服务器,每台的SSH配置都不一样,这是非常危险的状态。用Ansible这类工具做统一批量修改,能避免一台台改配置时漏改、改错的问题。模板化的好处是标准化、可回溯、可对比,和手工逐台维护相比完全不在一个维度上。
7. 常见问题与排查技巧实录
实操过程中最容易踩的坑集中在下面几个地方。
7.1 改了SSH配置后登录失败
改sshd_config最合理的姿势是先跑一下:
sshd -t检查语法正确性之后,不要退出当前会话,另开一个窗口测试新配置能正常登录,再关闭旧会话。生产服务器如果只有你一个人,先确保有带外管理渠道(云控制台VNC或物理机IPMI),不然一次配置错误可能让你连服务器都进不去。这是我反复强调的一点,但每年还是会有人因为省这一步而需要去机房插显示器。
7.2 Fail2ban误封自己
误封通常有两种原因:一种是findtime窗口内多次输错密码触发封禁;另一种是自己的跳板机IP被代理链路上的其他节点连累。排查时用:
fail2ban-client status sshd fail2ban-client set sshd unbanip your_ip如果觉得自己会用错IP登录,先把自己常用的几个IP加入ignoreip白名单,再配置封禁策略。加入白名单的意义在于避免运维人员因为切换网络环境导致身份验证失败而被封禁。
7.3 防火墙规则重启后失效
这是iptables老用户经常遇到的问题:手工用iptables命令添加的规则,重启后统统消失。解决办法是把规则持久化。Debian系:
apt install iptables-persistent netfilter-persistent saveRHEL系:
yum install iptables-services systemctl enable iptables service iptables save云服务器用户更好用的方案是直接用安全组规则,把规则保存在云平台侧,既能持久化又能统一管理。但云安全组和系统防火墙两层的规则需要保持同步,别只配一层。
7.4 密码策略修改后不生效
/etc/login.defs里改了密码老化策略,但已有用户不生效,这是理解层面的经典误区。login.defs只在用户修改密码或管理员显式设置时作为默认值起作用,要立即应用到已有用户需要用chage:
chage -M 90 -m 7 -W 14 username批量应用时可以这样循环:
for user in $(awk -F: '$3>=1000 && $3<65534 {print $1}' /etc/passwd); do chage -M 90 -m 7 -W 14 $user done注意这条命令特意排除了系统账号(UID小于1000和大于等于65534),以免影响系统服务使用的专用账号。
7.5 SSH登录慢
第一次连接服务器要卡好几秒才出密码提示,这通常是DNS反向解析的问题。在sshd_config中设置:
UseDNS no GSSAPIAuthentication no之后重启sshd服务,速度立刻见效。这条算是最常见的体验优化了,和安全排查无关,但运维时总是碰上。
8. 写在最后的几点体会
账号、登录、口令、端口这四个维度看似基础,但真正能在生产环境贯彻到底的团队并不多。我做过多次服务器应急响应,十次里有八次都是这四个基础环节中的某一个失守。安全没有银弹,把基础做得滴水不漏,比安装任何一款昂贵的安全产品都管用。
在实际操作中,我的建议是不要追求一次性做完所有加固。先跑审计脚本,把当前风险点列出来,按照风险等级从高到低逐项整改,每改一项验证一项,避免一次改动过多造成服务异常。另一件重要的事是保留操作记录,无论是一行命令的变更还是整个配置文件的替换,都记录下来。不只是为了审计合规,更是出于排障的刚需——出了问题才能知道是改了哪里引起的。
最后再分享一个小技巧:任何涉及认证和网络的关键改动(SSH配置、防火墙、PAM),都可以先用tmux开一个会话,在会话里执行操作,如果配置错误导致连接中断,通过重新连接后tmux attach还能找回会话上下文,不至于卡在一个半死不活的状态里。这是我踩过不少坑之后形成的习惯,希望你能用得着。