No space left on device 的根因是 /var/log/secure 被 SSH 爆破日志写到了 4.2G、/var/log/btmp 涨到 2.8G,把根分区撑满。急救动作是截断日志文件,止血动作是关掉 SSH 密码登录。
要点速览:
- 根因不是业务数据,是 SSH 爆破日志:secure 4.2G + btmp 2.8G
- 排查顺序:
df -h/df -i→du -sh /var/log/*→ 统计来源 IP - 清日志必须用截断(
> /var/log/secure),不能用rm - 治本动作:
PasswordAuthentication no,封 IP 只是打地鼠 - 验证标准:
df -h回到正常水位,btmp不再增长
2026 年 10 月上旬,一台业务机的 sshd 起不来,登录直接报 No space left on device。本来以为是日志写爆磁盘这种老问题,结果一查,根因是被人爆破了一整周。
故障现场是什么表现
这台机器的表现是典型的磁盘写满,但不是完全不可用:
- 远程 ssh 登录失败,偶尔能连上,但一执行命令就报错;
- nginx、cron 这些服务写入失败;
- 客户端提示
Connection closed by remote host。
看到 No space left on device 先别急着删文件,按下面两步定位。
怎么区分是磁盘满还是 inode 满
两者报错一模一样,处理方式完全不同,所以第一步必须把这两条命令都跑一遍:
bash
df -h
df -i
df -i 别跳过,inode 被打满一样会报 No space left on device。这次是根分区使用率 100%,inode 正常,属于前者。
怎么定位是哪个目录吃掉了空间
bash
du -sh /var/log/* 2>/dev/null | sort -rh | head -10
结果很整齐:
4.2G /var/log/secure
2.8G /var/log/btmp
secure 4.2G、btmp 2.8G,这就不用猜了。btmp 记录失败登录,正常服务器放几个月也就几百 K 的量级,涨到 G 级只有一种解释。顺手看下爆破规模:
bash
lastb | head -20
wc -l /var/log/btmp
怎么查是谁在爆破
bash
grep "Failed password" /var/log/secure | awk '{print $(NF-3)}' | sort | uniq -c | sort -rn | head -20
Failed password for root from 1.2.3.4 port 51234 ssh2 这种格式里,从后往前数第 4 个字段就是源 IP。跑完就能看到 top 攻击源,通常前几名是同一批 IP 在几十万次地试。再看下总共有多少个来源:
bash
grep "Failed password" /var/log/secure | awk '{print $(NF-3)}' | sort -u | wc -l
为什么清日志不能用 rm
这是整个过程里最容易踩的坑。日志文件被 rsyslog 打开着,rm 掉之后空间不会立刻释放,df -h 还是满的,非得重启服务才行。正确做法是截断:
bash
> /var/log/secure
> /var/log/btmp
df -h
如果打算留取证记录(我这次就留了一份),清之前先把关键片段抠出来:
bash
grep "Failed password" /var/log/secure | awk '{print $(NF-3)}' \
| sort | uniq -c | sort -rn > /root/attack_ips_$(date +%F).txt
怎么止血:四步,优先级从低到高
先把四步的作用和优先级说清楚,再展开具体命令:
| 步骤 | 作用 | 优先级 | 是否治本 |
|---|---|---|---|
| 限制日志体积(logrotate) | 防止再次撑满磁盘 | 高 | 只治症状 |
| 封禁攻击源 IP | 减少噪音 | 低 | 否,换 IP 即可绕过 |
| 关闭 SSH 密码登录 | 爆破彻底失效 | 最高 | 是 |
| 部署 fail2ban | 自动封禁 | 中 | 辅助 |
第一步:限制日志体积
给 secure 加大轮转力度,改 /etc/logrotate.d/syslog:
/var/log/secure {
weekly
rotate 4
maxsize 200M
compress
missingok
notifempty
sharedscripts
postrotate
/bin/kill -HUP `cat /var/run/syslogd.pid 2>/dev/null` 2>/dev/null || true
endscript
}
journald 也顺手压一下,改 /etc/systemd/journald.conf:
SystemMaxUse=500M
MaxRetentionSec=2week
改完 systemctl restart systemd-journald。
第二步:封禁攻击源 IP
少量 IP 直接 iptables,数量多就上 ipset,别一条条写:
bash
ipset create blacklist hash:ip timeout 86400
while read ip; do ipset add blacklist $ip; done < <(awk '{print $2}' /root/attack_ips_$(date +%F).txt)
iptables -I INPUT -m set --match-set blacklist src -j DROP
不过说句实话,光封 IP 是打地鼠,攻击源换 IP 是秒级的。真正有效的是下面两条。
第三步:关闭密码登录(治本)
bash
# 确认密钥能正常登录之后再执行
sed -i 's/^#\?PasswordAuthentication.*/PasswordAuthentication no/' /etc/ssh/sshd_config
sed -i 's/^#\?PermitRootLogin.*/PermitRootLogin prohibit-password/' /etc/ssh/sshd_config
sshd -t && systemctl restart sshd
密码登录一关,爆破就彻底失去了意义,btmp 基本不会再涨。这一步是整件事的转折点。
第四步:部署 fail2ban
bash
yum install -y fail2ban
cat > /etc/fail2ban/jail.local <<'EOF'
[sshd]
enabled = true
port = ssh
maxretry = 5
findtime = 600
bantime = 86400
EOF
systemctl enable --now fail2ban
fail2ban-client status sshd
怎么验证恢复成功
bash
df -h # 根分区回到正常水位
lastb | head # 失败登录不再疯涨
fail2ban-client status sshd # 能看到 Banned IP list
三条都符合预期才算收工,只看到 df -h 降下来是不够的。
常见问题
报 No space left on device 一定要先删文件吗
不一定,先分清楚是磁盘满还是 inode 满。df -h 看空间,df -i 看 inode,两者的处理方式完全不同。这次是磁盘满,inode 正常。
为什么 rm 删掉大日志后 df -h 还是满的
因为文件还被 rsyslog 这类进程打开着。Linux 下删除的只是目录项,进程持有的文件描述符仍然指向数据块,空间不会释放,直到进程关闭句柄或重启。正确的清理方式是截断文件内容。
关掉密码登录之后,我自己怎么登录
提前配置好 SSH 密钥登录,确认密钥能正常登录之后再执行 PasswordAuthentication no。顺序反了就会把自己关在门外。
封 IP 能彻底解决爆破问题吗
不能。攻击源换 IP 是秒级的,封禁只降低日志噪音,不降低风险。真正的解法是关闭密码登录,让爆破在原理上失效。
btmp 和 secure 有什么区别,为什么要看两个
btmp 记录失败登录(lastb 读取),secure 记录认证过程详情。btmp 看体量和趋势,secure 看源 IP 和尝试方式,两个配合才能还原爆破规模和来源。
日志该不该直接纳入监控
建议纳入。把 secure 和 btmp 的体积纳入监控,涨幅异常基本就等于有人在敲门,比等磁盘满更早拿到信号。
日志被写满,表面是磁盘问题,根因基本都指向"机器暴露在公网 + 还开着密码登录"。清日志只是急救,PasswordAuthentication no 才是止血。遇到类似问题的,把 df -h 和 du 的结果贴评论区,我帮你看看。