[{"content":" 本系列：SSH 安全加固实战（共 5 篇） ① 为什么你的 SSH 正在裸奔 → ② 先读懂配置文件再动手 → ③ 用密钥认证告别密码 → ④ 限制谁能登录、从哪里登录 → ⑤ 防暴力破解与安全审计（本篇，终篇）\n前四篇把\u0026quot;门\u0026quot;层层收窄：密钥认证堵死了密码爆破，白名单与防火墙限制谁能进、从哪进。最后一篇做主动拦截和持续运维——用 fail2ban 自动封禁恶意来源、看懂日志做审计，最后给出一份贯穿全系列的自测清单，让你能一键检查自己到底加固到几成。\n用 fail2ban 自动封禁爆破来源 fail2ban 会监控登录日志，一旦某个 IP 短时间内连续失败多次，就自动调用防火墙把它封掉一段时间。这是对\u0026quot;扫描爆破\u0026quot;最直接的反制。\n安装 # Debian / Ubuntu sudo apt update \u0026amp;\u0026amp; sudo apt install -y fail2ban # RHEL / CentOS / Rocky sudo yum install -y fail2ban # 或用 dnf 配置 fail2ban 的主配置是 /etc/fail2ban/jail.conf，但我们不要改它，而是新建本地覆盖文件，避免升级被覆盖：\nsudo cp /etc/fail2ban/jail.local /etc/fail2ban/jail.local 2\u0026gt;/dev/null # 若上面报错说明文件不存在，直接新建： sudo vim /etc/fail2ban/jail.local 写入最小可用配置，为 SSH 开启默认 jail：\n[DEFAULT] bantime = 1h # 封禁时长，可写成 10m / 1h / 1d findtime = 10m # 统计窗口 maxretry = 5 # 窗口内失败 5 次即封禁 [sshd] enabled = true backend = systemd # 用 systemd 日志；传统 syslog 发行版可省去或改 auto 提示框：backend = systemd 适用于使用 journald 的较新发行版（Ubuntu 18.04+、RHEL 7+）。老系统（纯 syslog）通常不写这行或设为 auto 让 fail2ban 自动判断。拿不准就删掉该行用默认。\n启用并查看状态：\nsudo systemctl enable --now fail2ban sudo fail2ban-client status sshd status 输出的 Banned IP list 若出现 IP，说明 fail2ban 已开始工作。可以用下面命令手动解除误封的 IP：\nsudo fail2ban-client set sshd unbanip 你的IP 提示框：别把自己封了。 若你在用 fail2ban 的同时又频繁输错密码、或防火墙白名单没覆盖自己，容易误封。封了也别慌：多数云控制台可\u0026quot;重开实例\u0026quot;或通过带外 VNC 登录后 fail2ban-client set sshd unbanip 解封。生产上建议先把自己的固定 IP 加入白名单（fail2ban 的 ignoreip）再启用。\n在 jail.local 的 [DEFAULT] 段加：\nignoreip = 127.0.0.1/8 ::1 你的固定IP 日志审计：看清攻击长什么样 加固不是设完就完，要会\u0026quot;看日志\u0026quot;判断是否仍在被攻击、有没有异常登录。\n查看失败登录（爆破痕迹）：\n# Debian / Ubuntu sudo grep \u0026#39;Failed password\u0026#39; /var/log/auth.log | tail -30 # RHEL / CentOS / Rocky sudo grep \u0026#39;Failed password\u0026#39; /var/log/secure | tail -30 # 使用 systemd 的发行版通用写法 sudo journalctl -u ssh -u sshd --since \u0026#34;1 hour ago\u0026#34; | grep -iE \u0026#39;failed|invalid|break-in\u0026#39; 看成功登录（谁在登、从哪登）：\nsudo grep \u0026#39;Accepted\u0026#39; /var/log/auth.log | tail -30 # Debian/Ubuntu sudo grep \u0026#39;Accepted\u0026#39; /var/log/secure | tail -30 # RHEL 系 提示框：日志路径与格式因发行版而异——auth.log（Debian/Ubuntu）vs secure（RHEL），见第 1 篇提示。用 journalctl 是较新发行版的统一入口。\n想一眼看到\u0026quot;最频繁的来源 IP\u0026quot;，可统计：\nsudo grep \u0026#39;Failed password\u0026#39; /var/log/auth.log | awk \u0026#39;{print $(NF-3)}\u0026#39; | sort | uniq -c | sort -nr | head -20 提示框：字段位置会随日志格式和发行版变化，awk 取列未必通用，仅作演示思路。生产更稳的做法是看 fail2ban 的 banned 列表、或用 lastb（若启用 btmp）看爆破来源。\n查看最近登录记录：last（看 /var/log/wtmp）和 lastb（看失败的 /var/log/btmp）：\nlast | head -20 # 最近成功登录 sudo lastb | head # 最近失败尝试（爆破来源） 审计要点：\n有大量来自陌生 IP 的 Failed password → 正常，公网常态；fail2ban 会拦截。若持续暴涨再排查是否配置未生效。 出现你不认识的 Accepted → 立刻核查是否有人入侵（改密、踢掉会话、查进程）。可 w 查看当前在线用户。 关注异常时段的登录、非白名单账号的 Accepted、可疑的反弹 shell 进程。 长期维护：安全不是一劳永逸 及时升级 OpenSSH：sudo apt upgrade openssh-server 或 sudo yum update openssh-server，修复 CVE。 关注安全公告：留意发行版安全通告中的 openssh 条目，重大漏洞时第一时间升级。 定期轮换密钥：长期使用的密钥可定期换新（删旧公钥、生成新 key、清理 authorized_keys 里的陈旧公钥）。 只开最小端口、最小账号：随时间推移新增的服务/账号记得及时清理，别让入口悄悄变宽。 做好备份与逃生通道：无论怎么加固，都保留一个带外管理入口（云控制台/VNC/串口）作为兜底。 全系列最终自测清单 把五篇的成果汇总成一份可复检清单，建议定期跑一遍：\n认证与账号\n仅使用密钥登录，PasswordAuthentication no（第 3 篇） 私钥设了 passphrase 且保管妥当，公钥权限正确（700 / 600）（第 3 篇） PermitRootLogin no（或 prohibit-password），日常用普通账号 + sudo（第 2、4 篇） 配置了 AllowUsers / AllowGroups 白名单（第 4 篇） 会话与资源\nLoginGraceTime、MaxAuthTries、MaxStartups 已限制（第 4 篇） ClientAliveInterval / ClientAliveCountMax 已清理空闲会话（第 4 篇） 网络层\n防火墙仅放行可信来源 IP 到 SSH 端口（ufw / firewalld）（第 4 篇） （可选）已改非默认端口并写入 ~/.ssh/config（第 4 篇） 主动防御与维护\nfail2ban 已启用，ssh-agent jail 生效，ignoreip 含自己（本篇） 会看认证日志判断异常（auth.log / secure / journalctl）（本篇） OpenSSH 保持最新，密钥定期轮换，保留带外逃生通道（本篇） 全系列回顾 至此，从\u0026quot;为什么 SSH 会裸奔\u0026quot;到\u0026quot;如何系统加固\u0026quot;，五篇层层推进：\n认知：SSH 是唯一远程入口，默认不设防，公网被爆破是常态。 基础：读懂 sshd_config，学会安全改配置不锁死自己。 核心：ed25519 密钥认证，彻底告别密码登录。 纵深：账号白名单 + 会话限制 + 防火墙来源白名单 + 禁用 root 直登。 收尾：fail2ban 主动封禁 + 日志审计 + 长期维护自测。 安全没有终点，是一个持续收紧、验证、维护的过程。愿你的服务器大门从此清净。\n我们下个系列再见。\n","permalink":"https://www.lovezsh.com/posts/ssh-fail2ban-audit/","summary":"\u003cblockquote\u003e\n\u003cp\u003e\u003cstrong\u003e本系列：SSH 安全加固实战（共 5 篇）\u003c/strong\u003e\n① 为什么你的 SSH 正在裸奔 → ② 先读懂配置文件再动手 → ③ 用密钥认证告别密码 → ④ 限制谁能登录、从哪里登录 → ⑤ 防暴力破解与安全审计（本篇，终篇）\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e前四篇把\u0026quot;门\u0026quot;层层收窄：密钥认证堵死了密码爆破，白名单与防火墙限制谁能进、从哪进。最后一篇做\u003cstrong\u003e主动拦截\u003c/strong\u003e和\u003cstrong\u003e持续运维\u003c/strong\u003e——用 fail2ban 自动封禁恶意来源、看懂日志做审计，最后给出一份贯穿全系列的\u003cstrong\u003e自测清单\u003c/strong\u003e，让你能一键检查自己到底加固到几成。\u003c/p\u003e\n\u003ch2 id=\"用-fail2ban-自动封禁爆破来源\"\u003e用 fail2ban 自动封禁爆破来源\u003c/h2\u003e\n\u003cp\u003efail2ban 会监控登录日志，一旦某个 IP 短时间内连续失败多次，就自动调用防火墙把它封掉一段时间。这是对\u0026quot;扫描爆破\u0026quot;最直接的反制。\u003c/p\u003e\n\u003ch3 id=\"安装\"\u003e安装\u003c/h3\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Debian / Ubuntu\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo apt update \u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u0026amp;\u003c/span\u003e sudo apt install -y fail2ban\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# RHEL / CentOS / Rocky\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo yum install -y fail2ban     \u003cspan style=\"color:#75715e\"\u003e# 或用 dnf\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3 id=\"配置\"\u003e配置\u003c/h3\u003e\n\u003cp\u003efail2ban 的主配置是 \u003ccode\u003e/etc/fail2ban/jail.conf\u003c/code\u003e，但我们\u003cstrong\u003e不要改它\u003c/strong\u003e，而是新建本地覆盖文件，避免升级被覆盖：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo cp /etc/fail2ban/jail.local /etc/fail2ban/jail.local 2\u0026gt;/dev/null\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 若上面报错说明文件不存在，直接新建：\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo vim /etc/fail2ban/jail.local\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e写入最小可用配置，为 SSH 开启默认 jail：\u003c/p\u003e","title":"SSH 安全加固（五）：防暴力破解与安全审计"},{"content":" 本系列：SSH 安全加固实战（共 5 篇） ① 为什么你的 SSH 正在裸奔 → ② 先读懂配置文件再动手 → ③ 用密钥认证告别密码 → ④ 限制谁能登录、从哪里登录（本篇） → ⑤ 防暴力破解与安全审计\n第 3 篇关掉了密码登录，爆破的最大威胁已堵上。但攻击面还能继续收窄：账号层面限制谁能登进来、网络层面只放行可信来源，并砍掉用不上的资源占用。这一篇把这些做细，让你的入口更\u0026quot;隐蔽、干净、难接近\u0026quot;。\n账号层面：只让该登录的人能登录 在 sshd_config 里可用白名单机制，只允许指定账号或用户组登录：\n# 只允许 user1、user2 两个账号登录 AllowUsers user1 user2 # 或者只允许 admin 组里的用户登录 AllowGroups admin 提示框：AllowUsers 与 AllowGroups 是白名单（命中才放行），DenyUsers / DenyGroups 是黑名单。生产环境建议用白名单更稳。二者可同时存在，规则叠加生效。\n设置后同样 sshd -t + reload（注意服务名），并用另一个账号测试确认被拒、目标账号正常。这样即使某个账号被暴力破解尝试，只要不在白名单内也进不来。\n会话层面：限制并发与重试，避免资源被耗死 攻击者会不断建立半开连接、反复试探，拖慢甚至拖垮服务。限制几个防拖垮参数：\nLoginGraceTime 30 # 认证超时秒数，太久容易堆积半开连接 MaxAuthTries 3 # 单次连接最大认证尝试次数 MaxSessions 10 # 单连接最多打开的会话数 MaxStartups 10:30:100 # 未认证并发连接上限，防 DoS 解释：\nLoginGraceTime：一次连接若在 30 秒内没完成认证就断开，减少挂着的僵尸连接。 MaxAuthTries：限制单次连接里猜密码的次数，配合第 3 篇关密码更安全。 MaxStartups：并发未完成认证的连接数达到 10 后，新连接以 30% 概率被拒，超过 100 全部拒绝，是经典防爆破/防拖垮配置。 提示框：不要为了省事把这些设得极苛刻导致误伤自己（比如自己在慢网络上 MaxAuthTries 1 容易把自己挡外面）。保留逃生会话、一次改一项的原则依然适用。\n保活与空闲断开：避免会话永远挂着 长期空闲的连接既占资源，也可能被劫持复用。可加：\nClientAliveInterval 300 # 每 300 秒向客户端发一次探活包 ClientAliveCountMax 0 # 连续 0 次未响应即断开（即探活失败就断） 这样 5 分钟没响应的空闲连接会被主动断开，清理僵尸会话。若你经常挂着长时间任务，可适当放宽或使用 tmux/screen 保持任务（那是另一个话题）。\n网络层面：用防火墙只放行可信来源 账号限制管的是\u0026quot;谁能登录\u0026quot;，网络层还要管\u0026quot;从哪来能到达 22 端口\u0026quot;。这是比改端口更本质的收窄。\nDebian / Ubuntu —— ufw sudo ufw allow from 你的家/办公IP to any port 22 proto tcp sudo ufw enable 把\u0026quot;你的家/办公IP\u0026quot;换成你固定的出口 IP。家里宽带若无固定公网 IP，可改为只允许公司的 IP 段，或用 VPN/跳板机（见文末）。\n查看与确认：\nsudo ufw status verbose RHEL / CentOS / Rocky —— firewalld sudo firewall-cmd --permanent --add-rich-rule=\u0026#39;rule family=\u0026#34;ipv4\u0026#34; source address=\u0026#34;你的家/办公IP\u0026#34; port protocol=\u0026#34;tcp\u0026#34; port=\u0026#34;22\u0026#34; accept\u0026#39; sudo firewall-cmd --reload sudo firewall-cmd --list-all 提示框：原理相同，只是命令体系不同。ufw 用 allow from X，firewalld 用 rich-rule 指定 source address。都能实现\u0026quot;只放行某个来源 IP 到 22\u0026quot;。两个体系不要混用。\n关键纪律：加白名单规则时，先确保你自己的连接来源 IP 在放行列表内，且当前会话保持登录，再 reload/生效，否则可能把自己挡在防火墙外。若 IP 会变，考虑 VPN 进入内网后再连 SSH（更安全也更省心）。\n要不要改监听端口？—— 权衡 常见做法是把 Port 22 改成如 2222。作用与代价：\n作用：让地毯式扫描器\u0026quot;撞不到\u0026quot;默认端口，显著降低噪音（无脑扫描 22 的数量远多于随机端口）。 代价：不等于安全。真正有针对性的扫描仍能发现；且客户端连接都要带 -p 端口 或写进 ~/.ssh/config，稍有不便。 建议：改端口作为降低噪音的辅助手段，但不要指望它替代防火墙白名单与密钥认证。改法（记得逃生会话）：\nPort 2222 reload 后用客户端指定新端口验证：ssh -p 2222 user@server_ip。\n安全提示：开放公网服务前务必清楚\u0026quot;最低权限 + 最小暴露\u0026quot;原则——只开必需端口、只放行可信来源、只允许必要账号。SSH 是同理。\n禁用 root 直登并改用 sudo 即便有密钥，最佳实践是禁止 root 直接用 SSH 登录，日常用普通账号 + sudo：\nPermitRootLogin no 这样即使某把 root 私钥泄露或 root 账号被探测，也难以直接登入最高权限账号。日常操作用普通用户提权即可（sudo 命令）。\n提示框：云厂商（阿里云/腾讯云/AWS 等）镜像的默认账号常叫 root、ubuntu、ec2-user 等。禁用 root 前，先确认你有一个能 sudo 的普通账号，否则会失去管理能力。\n组合起来：一个更完整的入口策略 把这四层叠加，就是\u0026quot;纵深防御\u0026quot;的样子：\n密钥认证（第 3 篇）—— 防密码爆破 白名单账号（本篇）—— 只允许必要账号 防火墙来源限制（本篇）—— 只放行可信 IP 会话/并发参数 + 禁用 root 直登（本篇）—— 防拖垮、降风险 （可选）改端口 —— 降扫描噪音 本篇小结 账号白名单 AllowUsers / AllowGroups 收窄\u0026quot;谁能登录\u0026quot;。 LoginGraceTime / MaxAuthTries / MaxStartups 防止资源被爆破拖垮。 ClientAlive* 清理空闲会话。 防火墙 ufw / firewalld 只放行可信来源 IP，比改端口更本质。 改端口只降噪音；禁用 root 直登 + sudo 是更稳妥的长期做法。 下一篇预告 入口已经收得很窄了。最后一篇做主动拦截与持续维护：用 fail2ban 自动封禁爆破 IP、看日志做安全审计，并给出一份贯穿全系列的最终自测清单。\n建议先在服务器上开启防火墙并配置账号白名单，逐项验证后再进入终篇。我们下一篇见。\n","permalink":"https://www.lovezsh.com/posts/ssh-access-lockdown/","summary":"\u003cblockquote\u003e\n\u003cp\u003e\u003cstrong\u003e本系列：SSH 安全加固实战（共 5 篇）\u003c/strong\u003e\n① 为什么你的 SSH 正在裸奔 → ② 先读懂配置文件再动手 → ③ 用密钥认证告别密码 → ④ 限制谁能登录、从哪里登录（本篇） → ⑤ 防暴力破解与安全审计\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e第 3 篇关掉了密码登录，爆破的最大威胁已堵上。但攻击面还能继续收窄：\u003cstrong\u003e账号层面\u003c/strong\u003e限制谁能登进来、\u003cstrong\u003e网络层面\u003c/strong\u003e只放行可信来源，并砍掉用不上的资源占用。这一篇把这些做细，让你的入口更\u0026quot;隐蔽、干净、难接近\u0026quot;。\u003c/p\u003e\n\u003ch2 id=\"账号层面只让该登录的人能登录\"\u003e账号层面：只让该登录的人能登录\u003c/h2\u003e\n\u003cp\u003e在 \u003ccode\u003esshd_config\u003c/code\u003e 里可用白名单机制，只允许指定账号或用户组登录：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-text\" data-lang=\"text\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e# 只允许 user1、user2 两个账号登录\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eAllowUsers user1 user2\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e# 或者只允许 admin 组里的用户登录\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eAllowGroups admin\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cblockquote\u003e\n\u003cp\u003e提示框：\u003ccode\u003eAllowUsers\u003c/code\u003e 与 \u003ccode\u003eAllowGroups\u003c/code\u003e 是\u003cstrong\u003e白名单\u003c/strong\u003e（命中才放行），\u003ccode\u003eDenyUsers\u003c/code\u003e / \u003ccode\u003eDenyGroups\u003c/code\u003e 是黑名单。生产环境建议用白名单更稳。二者可同时存在，规则叠加生效。\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e设置后同样 \u003ccode\u003esshd -t\u003c/code\u003e + reload（注意服务名），并用另一个账号测试确认被拒、目标账号正常。这样即使某个账号被暴力破解尝试，只要不在白名单内也进不来。\u003c/p\u003e\n\u003ch2 id=\"会话层面限制并发与重试避免资源被耗死\"\u003e会话层面：限制并发与重试，避免资源被耗死\u003c/h2\u003e\n\u003cp\u003e攻击者会不断建立半开连接、反复试探，拖慢甚至拖垮服务。限制几个防拖垮参数：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-text\" data-lang=\"text\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eLoginGraceTime 30          # 认证超时秒数，太久容易堆积半开连接\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eMaxAuthTries 3             # 单次连接最大认证尝试次数\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eMaxSessions 10             # 单连接最多打开的会话数\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eMaxStartups 10:30:100      # 未认证并发连接上限，防 DoS\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e解释：\u003c/p\u003e","title":"SSH 安全加固（四）：限制谁能登录、从哪里登录"},{"content":" 本系列：SSH 安全加固实战（共 5 篇） ① 为什么你的 SSH 正在裸奔 → ② 先读懂配置文件再动手 → ③ 用密钥认证告别密码（本篇） → ④ 限制谁能登录、从哪里登录 → ⑤ 防暴力破解与安全审计\n前两篇铺垫完毕，这一篇是全系列最关键的一步：用公钥密钥认证替换密码登录。原理上，密钥是一个几乎无法被\u0026quot;猜中\u0026quot;的数字签名，比任何密码都难破解得多。配好它之后，你就能彻底关闭 PasswordAuthentication，从根上断掉爆破这条路。\n为什么选 ed25519 现代 OpenSSH 支持多种密钥算法，最常用的两种：\ned25519：曲线加密，密钥极短（公钥约几十字节）、生成快、安全性强，是当前推荐首选。 RSA 4096 位：兼容老设备，但密钥长、生成慢，安全性也不优于 ed25519。 结论：新环境一律用 ed25519。只有当你的旧服务器、网络设备或运维脚本只认 RSA 时才退回去用 RSA 4096。\n第 1 步：在你自己的电脑上生成密钥对 密钥要在**客户端（你自己的电脑）**生成，不是在服务器上生成。先看你电脑上有没有：\nls -l ~/.ssh/id_ed25519* 2\u0026gt;/dev/null 没有则生成（一路回车即可，-t 指定算法，-C 只是给个备注便于识别）：\nssh-keygen -t ed25519 -C \u0026#34;your_email@example.com\u0026#34; -f ~/.ssh/id_ed25519 提示框：要不要 passphrase？ 建议设一个口令保护私钥，即使私钥文件泄露攻击者也无法直接使用；搭配 ssh-agent（见第 4 步）只需输入一次。若嫌麻烦可留空，但私钥文件务必只有你能读。\n生成后会得到两个文件：\n~/.ssh/id_ed25519 —— 私钥，绝不能外泄 ~/.ssh/id_ed25519.pub —— 公钥，可以放心上传到服务器 私钥就是你\u0026quot;钥匙\u0026quot;，公钥是配给服务器这把锁的\u0026quot;齿模\u0026quot;。私钥泄露 = 家门钥匙掉了，务必保管好。\n第 2 步：把公钥安装到服务器上 推荐用 ssh-copy-id 一键完成（它会自动把公钥追加到服务器的 ~/.ssh/authorized_keys 并修好权限）：\nssh-copy-id -i ~/.ssh/id_ed25519.pub user@server_ip 按提示输入当前仍生效的密码，完成后即安装成功。\n手动安装（备选） 若没有 ssh-copy-id，可以手动做。在服务器上创建必要目录并修正权限（这一步的权限是常见坑）：\nmkdir -p ~/.ssh chmod 700 ~/.ssh touch ~/.ssh/authorized_keys chmod 600 ~/.ssh/authorized_keys 然后把公钥内容追加进 authorized_keys：\n# 在你电脑上把公钥内容复制出来 cat ~/.ssh/id_ed25519.pub # 在服务器上把那段内容追加进去 echo \u0026#34;ssh-ed25519 AAAA...你的公钥...\u0026#34; \u0026gt;\u0026gt; ~/.ssh/authorized_keys 权限要点（必须满足） authorized_keys 及其所在目录权限不对，OpenSSH 会直接忽略这个文件：\n~（家目录）不可被 group/other 写 ~/.ssh 应为 700 ~/.ssh/authorized_keys 应为 600 第 3 步：验证能否用密钥登录 先用密钥试试能否登录，不要立刻关密码：\nssh -i ~/.ssh/id_ed25519 user@server_ip 能免密登进（或只需输 passphrase），说明密钥已生效。可以再加 -v 查看详细握手过程确认用的是 publickey 而非 password：\nssh -v user@server_ip 2\u0026gt;\u0026amp;1 | grep -i \u0026#34;authentications that can continue\u0026#34; 第 4 步：用 ssh-agent 告别反复输 passphrase 给私钥设了 passphrase 的话，可以启动 ssh-agent 只输入一次，之后自动使用：\neval \u0026#34;$(ssh-agent -s)\u0026#34; # 启动 agent ssh-add ~/.ssh/id_ed25519 # 添加私钥（输入一次 passphrase） 提示框：macOS 用钥匙串管理、Linux 桌面端常配 GNOME Keyring，机制各有差异，但概念一致——agent 在你登录期间替你保管已解锁的私钥。重启后需要重新 ssh-add（或配置开机加载）。\n第 5 步：给常用主机配别名 在 ~/.ssh/config（客户端配置，还记得吗？）里给常用服务器配置别名，省去记 IP 和端口：\nHost myserver HostName 你的服务器IP或域名 User 你的用户名 Port 22 IdentityFile ~/.ssh/id_ed25519 之后直接 ssh myserver 即可。注意这里的文件是你电脑上的 ~/.ssh/config（不是服务器的 /etc/ssh/sshd_config）。\n第 6 步：彻底关闭密码登录（安全地） 确认上面都能用密钥登录后，再回到服务器改 sshd_config。先开一个新终端窗口作为逃生会话保持登录，再：\nsudo vim /etc/ssh/sshd_config 把密码登录关掉：\nPasswordAuthentication no 语法校验 + 重载（不改服务名，参考第 2 篇，Debian 用 ssh、RHEL 用 sshd）：\nsudo sshd -t \u0026amp;\u0026amp; sudo systemctl reload ssh # Debian/Ubuntu sudo sshd -t \u0026amp;\u0026amp; sudo systemctl reload sshd # RHEL/CentOS/Rocky 再次强调顺序：先确认密钥能登录 → 保留逃生会话 → 才改 no → 另开终端用密钥连一次验证 → 确认无误后再考虑关闭逃生会话。\n禁用密码前的自检清单 准备关掉密码前，逐条打勾：\n能从客户端用密钥登录成功（ssh -v 确认走的是 publickey） 公钥、~/.ssh、authorized_keys 权限正确（700 / 600） 保留了一条已登录的逃生会话 服务器仍可访问（IP/端口没写错） 若私钥设了 passphrase，确认已记住或已配 ssh-agent 应急预案：如果不慎把自己锁在门外，而你有云服务商控制台或服务器机房，可走VNC / 控制台/单用户模式进入系统把 PasswordAuthentication 改回 yes，或临时用 root 登录恢复。这也是为什么生产环境建议保留带外（out-of-band）管理入口。\n本篇小结 首选 ed25519 密钥，私钥私钥绝不能外泄，建议设 passphrase。 用 ssh-copy-id 上传公钥，手动上传要特别注意 700 / 600 权限。 先验证密钥登录成功，再开逃生会话，最后才关 PasswordAuthentication。 ssh-agent + ~/.ssh/config 别名能大幅提升日常体验。 下一篇预告 密码已关闭，爆破这个最大的威胁基本堵死。第 4 篇继续收窄攻击面：限制谁能登录、从哪些来源登录，以及用防火墙只放行可信 IP。\n现在就去生成你的 ed25519 密钥并完成上面的自检清单。我们下一篇见。\n","permalink":"https://www.lovezsh.com/posts/ssh-key-auth/","summary":"\u003cblockquote\u003e\n\u003cp\u003e\u003cstrong\u003e本系列：SSH 安全加固实战（共 5 篇）\u003c/strong\u003e\n① 为什么你的 SSH 正在裸奔 → ② 先读懂配置文件再动手 → ③ 用密钥认证告别密码（本篇） → ④ 限制谁能登录、从哪里登录 → ⑤ 防暴力破解与安全审计\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e前两篇铺垫完毕，这一篇是\u003cstrong\u003e全系列最关键的一步\u003c/strong\u003e：用公钥密钥认证替换密码登录。原理上，密钥是一个几乎无法被\u0026quot;猜中\u0026quot;的数字签名，比任何密码都难破解得多。配好它之后，你就能彻底关闭 \u003ccode\u003ePasswordAuthentication\u003c/code\u003e，从根上断掉爆破这条路。\u003c/p\u003e\n\u003ch2 id=\"为什么选-ed25519\"\u003e为什么选 ed25519\u003c/h2\u003e\n\u003cp\u003e现代 OpenSSH 支持多种密钥算法，最常用的两种：\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003eed25519\u003c/strong\u003e：曲线加密，密钥极短（公钥约几十字节）、生成快、安全性强，是\u003cstrong\u003e当前推荐首选\u003c/strong\u003e。\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eRSA 4096 位\u003c/strong\u003e：兼容老设备，但密钥长、生成慢，安全性也不优于 ed25519。\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e\u003cstrong\u003e结论：新环境一律用 ed25519\u003c/strong\u003e。只有当你的旧服务器、网络设备或运维脚本\u003cstrong\u003e只认 RSA\u003c/strong\u003e 时才退回去用 RSA 4096。\u003c/p\u003e\n\u003ch2 id=\"第-1-步在你自己的电脑上生成密钥对\"\u003e第 1 步：在你自己的电脑上生成密钥对\u003c/h2\u003e\n\u003cp\u003e密钥要在**客户端（你自己的电脑）**生成，不是在服务器上生成。先看你电脑上有没有：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003els -l ~/.ssh/id_ed25519* 2\u0026gt;/dev/null\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e没有则生成（一路回车即可，\u003ccode\u003e-t\u003c/code\u003e 指定算法，\u003ccode\u003e-C\u003c/code\u003e 只是给个备注便于识别）：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003essh-keygen -t ed25519 -C \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;your_email@example.com\u0026#34;\u003c/span\u003e -f ~/.ssh/id_ed25519\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cblockquote\u003e\n\u003cp\u003e提示框：\u003cstrong\u003e要不要 passphrase？\u003c/strong\u003e 建议设一个口令保护私钥，即使私钥文件泄露攻击者也无法直接使用；搭配 \u003ccode\u003essh-agent\u003c/code\u003e（见第 4 步）只需输入一次。若嫌麻烦可留空，但私钥文件务必只有你能读。\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e生成后会得到两个文件：\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ccode\u003e~/.ssh/id_ed25519\u003c/code\u003e —— \u003cstrong\u003e私钥\u003c/strong\u003e，绝不能外泄\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003e~/.ssh/id_ed25519.pub\u003c/code\u003e —— \u003cstrong\u003e公钥\u003c/strong\u003e，可以放心上传到服务器\u003c/li\u003e\n\u003c/ul\u003e\n\u003cblockquote\u003e\n\u003cp\u003e私钥就是你\u0026quot;钥匙\u0026quot;，公钥是配给服务器这把锁的\u0026quot;齿模\u0026quot;。\u003cstrong\u003e私钥泄露 = 家门钥匙掉了\u003c/strong\u003e，务必保管好。\u003c/p\u003e","title":"SSH 安全加固（三）：用密钥认证告别密码"},{"content":" 本系列：SSH 安全加固实战（共 5 篇） ① 为什么你的 SSH 正在裸奔 → ② 先读懂配置文件再动手（本篇） → ③ 用密钥认证告别密码 → ④ 限制谁能登录、从哪里登录 → ⑤ 防暴力破解与安全审计\n第 1 篇我们确认了\u0026quot;门是裸的\u0026quot;。很多人这时候会急着改配置，结果改错直接把自己锁在门外。这篇先打地基：配置文件在哪、每个关键开关什么意思、以及怎么改才能万无一失地不锁死自己。最后给一份可直接套用的安全基线。\n两套配置别搞混 Linux 上有两套 SSH 配置，新手极易混淆：\n文件 作用对象 服务器路径示例 ssh_config 控制本机作为客户端出去连接别人 /etc/ssh/ssh_config sshd_config 控制本机作为服务端接受别人连进来 /etc/ssh/sshd_config 我们要加固的是被连进来的那一侧，也就是服务端，所以一切改动都对着 sshd_config。\n提示框：sshd_config 不一定在 /etc/ssh/，可先定位。Debian/Ubuntu 通常就在 /etc/ssh/sshd_config；想确认服务实际用的配置文件，可执行 sshd -T（下方会讲）。\n读懂关键指令 用编辑器打开配置文件（这里用 sudo + 编辑器，Ubuntu 可用 nano，RHEL 系常配 vim）：\nsudo vim /etc/ssh/sshd_config # RHEL/CentOS/Rocky 常用 vim sudo nano /etc/ssh/sshd_config # Debian/Ubuntu 若偏好 nano 下面逐条看这些\u0026quot;命门\u0026quot;开关：\nPort 22 # 监听端口，默认 22（改端口利弊见第 4 篇） PermitRootLogin prohibit-password # 是否允许 root 直接登录 PasswordAuthentication yes # 是否允许密码登录 PubkeyAuthentication yes # 是否允许公钥登录 逐一解释：\nPort：监听端口。默认 22，是扫描器的头号目标。 PermitRootLogin：控制 root 能否 SSH 登录。yes=允许任意方式、prohibit-password=仅允许密钥（禁止密码）、no=禁止 root 直登（推荐，改用 sudo）。永远别开成 yes。 PasswordAuthentication：是否允许密码登录。破解就是冲它来的，用密钥后应改为 no（第 3 篇详解）。 PubkeyAuthentication：是否接受公钥登录，建议保持 yes。 提示框：版本差异——prohibit-password 是新版 OpenSSH 写法，老版本用 without-password，含义相同（仅密钥）。写 no 或 prohibit-password 通常都安全。\n关键一步：别\u0026quot;锁死\u0026quot;自己 SSH 是服务器唯一入口，改配置时最怕的是：systemctl restart ssh 后连不上，人又不在机房。正确的安全改法分四步：\n第 1 步：先开一个\u0026quot;逃生会话\u0026quot;并保持登录 改动前，先保留一个已登录的终端窗口不要关。一旦配置出问题，至少还有一个活着的会话能救命。\n第 2 步：只改你懂的，一次只改一点 不要一次堆十处改动。每改一处、验证一处、回退容易。\n第 3 步：改完先校验语法，再重载而不是重启 sshd 自带语法检查，改完先跑：\nsudo sshd -t # 语法正确无输出；有错会打印具体行号 提示框：Debian 系 sshd -t 可能需要 sudo /usr/sbin/sshd -t；RHEL 系通常直接 sshd -t。若提示找不到 sshd，用 which sshd 或发行版的完整路径。\n语法 OK 后，用重载（reload）而非重启（restart）。重载不中断已建立的会话，只是让新连接用新配置：\n# Debian / Ubuntu sudo systemctl reload ssh # RHEL / CentOS / Rocky sudo systemctl reload sshd # 老系统（无 systemd）可尝试 sudo service ssh reload # 或 service sshd reload 提示框：服务名差异——Debian/Ubuntu 服务名是 ssh，RHEL/CentOS/Rocky 是 sshd。不确定时 systemctl list-units | grep -i ssh 看一眼真实名字。若没有 systemd（很老的发行版），用 service sshd reload。\n第 4 步：别关逃生门，另开一个窗口测试新配置 保持逃生会话，另开一个终端用新配置连一次。确认能连上、且用的是你想要的方式（比如密钥）后，才考虑是否关闭逃生会话。\n黄金法则：改动某开关并成功验证前，永远不要关闭你手里最后一条已登录会话。\n直接可套用的安全基线 把下面这段追加或合并到 sshd_config 尾部（可先注释掉 PasswordAuthentication，第 3 篇配好密钥再放开 no）：\n# ---- 安全基线（结合后续文章逐步启用）---- PermitRootLogin prohibit-password # 禁止 root 用密码直登，后续可改 no PubkeyAuthentication yes PasswordAuthentication no # 先配好密钥再启用，见第 3 篇 ChallengeResponseAuthentication no # 禁用额外的密码挑战 提示框：务必配合第 3 篇——在你确认能用密钥登录之前，别把 PasswordAuthentication 设成 no，否则会把自己关在门外。这也是为什么前面强调\u0026quot;逃生门 + 另开会话验证\u0026quot;。\n本篇小结 加固改的是 /etc/ssh/sshd_config（服务端），不是 ssh_config（客户端）。 关键开关：Port、PermitRootLogin、PasswordAuthentication、PubkeyAuthentication。 安全改配置四步：留逃生会话 → 一次改一点 → sshd -t 校验 → systemctl reload + 另开会话测试。 prohibit-password 新旧版本写法有差异，systemd reload vs service、服务名 ssh vs sshd 因发行版而异。 下一篇预告 你已经能安全地改配置了。第 3 篇进入全系列最关键的一步：生成密钥对、用密钥登录、彻底关掉密码登录。这是告别爆破最有效的手段。\n现在可以先按本篇基线配置里可安全启用的项（root 禁止密码、保持密钥开启）改起来，记得留好逃生会话。我们下一篇见。\n","permalink":"https://www.lovezsh.com/posts/ssh-config-basics/","summary":"\u003cblockquote\u003e\n\u003cp\u003e\u003cstrong\u003e本系列：SSH 安全加固实战（共 5 篇）\u003c/strong\u003e\n① 为什么你的 SSH 正在裸奔 → ② 先读懂配置文件再动手（本篇） → ③ 用密钥认证告别密码 → ④ 限制谁能登录、从哪里登录 → ⑤ 防暴力破解与安全审计\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e第 1 篇我们确认了\u0026quot;门是裸的\u0026quot;。很多人这时候会急着改配置，结果改错直接把自己锁在门外。这篇先打地基：\u003cstrong\u003e配置文件在哪、每个关键开关什么意思、以及怎么改才能万无一失地不锁死自己\u003c/strong\u003e。最后给一份可直接套用的安全基线。\u003c/p\u003e\n\u003ch2 id=\"两套配置别搞混\"\u003e两套配置别搞混\u003c/h2\u003e\n\u003cp\u003eLinux 上有两套 SSH 配置，新手极易混淆：\u003c/p\u003e\n\u003ctable\u003e\n\t\u003cthead\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003cth\u003e文件\u003c/th\u003e\n\t\t\t\t\t\u003cth\u003e作用对象\u003c/th\u003e\n\t\t\t\t\t\u003cth\u003e服务器路径示例\u003c/th\u003e\n\t\t\t\u003c/tr\u003e\n\t\u003c/thead\u003e\n\t\u003ctbody\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003essh_config\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e控制\u003cstrong\u003e本机作为客户端\u003c/strong\u003e出去连接别人\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003e/etc/ssh/ssh_config\u003c/code\u003e\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003esshd_config\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e控制\u003cstrong\u003e本机作为服务端\u003c/strong\u003e接受别人连进来\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003e/etc/ssh/sshd_config\u003c/code\u003e\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\u003c/tbody\u003e\n\u003c/table\u003e\n\u003cp\u003e我们要加固的是被连进来的那一侧，也就是\u003cstrong\u003e服务端\u003c/strong\u003e，所以一切改动都对着 \u003ccode\u003esshd_config\u003c/code\u003e。\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e提示框：\u003ccode\u003esshd_config\u003c/code\u003e 不一定在 \u003ccode\u003e/etc/ssh/\u003c/code\u003e，可先定位。Debian/Ubuntu 通常就在 \u003ccode\u003e/etc/ssh/sshd_config\u003c/code\u003e；想确认服务实际用的配置文件，可执行 \u003ccode\u003esshd -T\u003c/code\u003e（下方会讲）。\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch2 id=\"读懂关键指令\"\u003e读懂关键指令\u003c/h2\u003e\n\u003cp\u003e用编辑器打开配置文件（这里用 \u003ccode\u003esudo\u003c/code\u003e + 编辑器，Ubuntu 可用 \u003ccode\u003enano\u003c/code\u003e，RHEL 系常配 \u003ccode\u003evim\u003c/code\u003e）：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo vim /etc/ssh/sshd_config    \u003cspan style=\"color:#75715e\"\u003e# RHEL/CentOS/Rocky 常用 vim\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo nano /etc/ssh/sshd_config   \u003cspan style=\"color:#75715e\"\u003e# Debian/Ubuntu 若偏好 nano\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e下面逐条看这些\u0026quot;命门\u0026quot;开关：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-text\" data-lang=\"text\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ePort 22                                  # 监听端口，默认 22（改端口利弊见第 4 篇）\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ePermitRootLogin prohibit-password        # 是否允许 root 直接登录\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ePasswordAuthentication yes               # 是否允许密码登录\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ePubkeyAuthentication yes                 # 是否允许公钥登录\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e逐一解释：\u003c/p\u003e","title":"SSH 安全加固（二）：先读懂配置文件再动手"},{"content":" 本系列：SSH 安全加固实战（共 5 篇） ① 为什么你的 SSH 正在裸奔（本篇） → ② 先读懂配置文件再动手 → ③ 用密钥认证告别密码 → ④ 限制谁能登录、从哪里登录 → ⑤ 防暴力破解与安全审计\n如果你有一台公网服务器，大概率会遇到这种时刻：apt upgrade 时刷屏一堆 Failed password for root from x.x.x.x，或者某天醒来发现机器被装了挖矿程序。SSH 是绝大多数 Linux 服务器唯一的远程入口，而默认配置下它就像把家门钥匙挂在门锁旁边。\n这篇先不讲怎么做，讲清楚三件事：SSH 到底在保护什么、攻击者是怎么盯上你的、以及如何确认你已经处于暴露之中。想明白\u0026quot;为什么\u0026quot;再动手，后面的加固才不会变形。\nSSH 在服务器上扮演什么角色 SSH（Secure Shell）是加密的远程登录协议。它做三件基本的事：\n身份认证——确认\u0026quot;你是谁\u0026quot;（密码 / 密钥 / 证书等） 加密传输——保证你敲的每条命令、传的每个文件都不被窃听 安全通道——基于它还能转发端口、开隧道、传文件（scp / sftp） 可以简单理解为：服务器只有一个门，这门就是 SSH，默认配置下任何人都能来试着撬一下。\n攻击者是怎么盯上你的 大多数攻击者并不\u0026quot;认识\u0026quot;你。他们用自动扫描器在公网上地毯式扫 IP，发现有 22 端口开着就记录在案，然后挂着字典用常见账号（root、admin、ubuntu）和弱口令（123456、password、root）一轮轮爆破。\n几个容易忽略的真相：\n你的 IP 被扫描是常态，不是针对你个人。只要暴露在公网，通常几分钟内就会被扫描器发现。 SSH 默认允许密码登录，而弱密码在字典里几乎必中。 默认允许 root 直接登录，等于给了攻击者一个\u0026quot;命中即最高权限\u0026quot;的目标账号。 爆破是一夜之间就能完成的，日志里成千上万条 Failed password 就是证据。 先确认自己是否已经被盯上 动手加固之前，先看看暴露面。请在你自己的服务器上执行（非 root 用户需 sudo）。\n# 查看当前所有 TCP 监听端口，确认哪些对外暴露 ss -tuln # 查看已建立的 SSH 连接（外部 IP 来源） ss -tnp | grep \u0026#39;:22 \u0026#39; || true 再看认证日志里有没有爆破痕迹：\n# Debian / Ubuntu（传统路径） sudo grep \u0026#39;Failed password\u0026#39; /var/log/auth.log | tail -20 # RHEL / CentOS / Rocky（有些版本也用 journalctl，见下） sudo grep \u0026#39;Failed password\u0026#39; /var/log/secure | tail -20 # 使用 systemd 的发行版通用写法（Ubuntu 新版本、RHEL 8+ 都支持） sudo journalctl -u ssh -u sshd --since today | grep \u0026#39;Failed password\u0026#39; | tail -20 提示框：发行版差异——Debian/Ubuntu 的服务名与日志常写 ssh，RHEL/CentOS/Rocky 常写 sshd，对应 /var/log/auth.log vs /var/log/secure。后面每篇我会同步标注这类差异，命令都给出双版本。\n如果输出里出现大量不同 IP 的 Failed password，说明你的服务器早就被盯上了。别慌——这正是我们要解决的事。\n一个值得先做的心理准备 SSH 加固要牢记一个原则：任何操作都必须给自己留一条能回来的路。锁死配置之前，务必保留一个已登录的终端会话作为\u0026quot;逃生门\u0026quot;，并且先测试再断开。这个\u0026quot;正确改配置而不锁死自己\u0026quot;的方法，正是第 2 篇要手把手教你的。\n本篇小结 SSH 是服务器唯一的远程门，默认配置对爆破几乎不设防。 公网 IP 被自动扫描爆破是常态，弱密码 + root 直登是最大隐患。 用 ss 看端口、用认证日志看爆破记录，可以快速评估自己暴露程度。 后续一切加固的前提是：先留逃生门，再动手。 下一篇预告 你已经知道\u0026quot;门是裸的\u0026quot;。下一篇进入实操第一步：读懂 /etc/ssh/sshd_config——弄清每个关键指令含义、学会安全地修改并热重载而不锁死服务器，最后给出一份可直接套用的安全基线配置。\n在动手前，先在自己服务器上跑一遍上面查看端口和日志的命令，把结果记录下来。我们下一篇见。\n","permalink":"https://www.lovezsh.com/posts/ssh-security-why/","summary":"\u003cblockquote\u003e\n\u003cp\u003e\u003cstrong\u003e本系列：SSH 安全加固实战（共 5 篇）\u003c/strong\u003e\n① 为什么你的 SSH 正在裸奔（本篇） → ② 先读懂配置文件再动手 → ③ 用密钥认证告别密码 → ④ 限制谁能登录、从哪里登录 → ⑤ 防暴力破解与安全审计\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e如果你有一台公网服务器，大概率会遇到这种时刻：\u003ccode\u003eapt upgrade\u003c/code\u003e 时刷屏一堆 \u003ccode\u003eFailed password for root from x.x.x.x\u003c/code\u003e，或者某天醒来发现机器被装了挖矿程序。SSH 是绝大多数 Linux 服务器唯一的远程入口，而默认配置下它就像把家门钥匙挂在门锁旁边。\u003c/p\u003e\n\u003cp\u003e这篇先不讲怎么做，讲清楚三件事：SSH 到底在保护什么、攻击者是怎么盯上你的、以及\u003cstrong\u003e如何确认你已经处于暴露之中\u003c/strong\u003e。想明白\u0026quot;为什么\u0026quot;再动手，后面的加固才不会变形。\u003c/p\u003e\n\u003ch2 id=\"ssh-在服务器上扮演什么角色\"\u003eSSH 在服务器上扮演什么角色\u003c/h2\u003e\n\u003cp\u003eSSH（Secure Shell）是加密的远程登录协议。它做三件基本的事：\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e\u003cstrong\u003e身份认证\u003c/strong\u003e——确认\u0026quot;你是谁\u0026quot;（密码 / 密钥 / 证书等）\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e加密传输\u003c/strong\u003e——保证你敲的每条命令、传的每个文件都不被窃听\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e安全通道\u003c/strong\u003e——基于它还能转发端口、开隧道、传文件（\u003ccode\u003escp\u003c/code\u003e / \u003ccode\u003esftp\u003c/code\u003e）\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003e可以简单理解为：\u003cstrong\u003e服务器只有一个门，这门就是 SSH，默认配置下任何人都能来试着撬一下。\u003c/strong\u003e\u003c/p\u003e\n\u003ch2 id=\"攻击者是怎么盯上你的\"\u003e攻击者是怎么盯上你的\u003c/h2\u003e\n\u003cp\u003e大多数攻击者并不\u0026quot;认识\u0026quot;你。他们用自动扫描器在公网上地毯式扫 IP，发现有 22 端口开着就记录在案，然后挂着字典用常见账号（\u003ccode\u003eroot\u003c/code\u003e、\u003ccode\u003eadmin\u003c/code\u003e、\u003ccode\u003eubuntu\u003c/code\u003e）和弱口令（\u003ccode\u003e123456\u003c/code\u003e、\u003ccode\u003epassword\u003c/code\u003e、\u003ccode\u003eroot\u003c/code\u003e）一轮轮爆破。\u003c/p\u003e\n\u003cp\u003e几个容易忽略的真相：\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003e你的 IP 被扫描是常态\u003c/strong\u003e，不是针对你个人。只要暴露在公网，通常几分钟内就会被扫描器发现。\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eSSH 默认允许密码登录\u003c/strong\u003e，而弱密码在字典里几乎必中。\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e默认允许 \u003ccode\u003eroot\u003c/code\u003e 直接登录\u003c/strong\u003e，等于给了攻击者一个\u0026quot;命中即最高权限\u0026quot;的目标账号。\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e爆破是一夜之间就能完成的\u003c/strong\u003e，日志里成千上万条 \u003ccode\u003eFailed password\u003c/code\u003e 就是证据。\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"先确认自己是否已经被盯上\"\u003e先确认自己是否已经被盯上\u003c/h2\u003e\n\u003cp\u003e动手加固之前，先看看暴露面。请在你自己的服务器上执行（非 root 用户需 \u003ccode\u003esudo\u003c/code\u003e）。\u003c/p\u003e","title":"SSH 安全加固（一）：为什么你的 SSH 正在裸奔"},{"content":"欢迎来到 lovezsh 的博客。这里记录技术与生活的点滴。\n","permalink":"https://www.lovezsh.com/about/","summary":"\u003cp\u003e欢迎来到 lovezsh 的博客。这里记录技术与生活的点滴。\u003c/p\u003e","title":"关于"},{"content":"第一篇博客，正在建设中。\necho \u0026#34;hello hugo\u0026#34; PaperMod 主题已接入，支持目录、代码高亮、站内搜索。\n","permalink":"https://www.lovezsh.com/posts/first-post/","summary":"\u003cp\u003e第一篇博客，正在建设中。\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eecho \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;hello hugo\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003ePaperMod 主题已接入，支持目录、代码高亮、站内搜索。\u003c/p\u003e","title":"你好，世界"},{"content":"df -h 警告磁盘 90%，但你不知道是哪个目录、哪个文件在涨。先给结论：\n从根开始逐层 du 找大目录，再用 find/ncdu 定位超大文件。这套\u0026quot;自上而下\u0026quot;的套路几分钟就能把大头锁定。\n方法一：du 逐层下钻（最通用，不依赖额外工具） 先看根下各目录谁最大：\ndu -xhd1 / 2\u0026gt;/dev/null | sort -h | tail -20 参数：-x 不跨文件系统（排除 /proc、独立分区），-h 人性化大小，-d1 只列一层目录，sort -h 按人类可读大小排序，tail 看最大的几个。\n进入最大的那个目录（比如 /var），继续下钻：\ndu -xhd1 /var 2\u0026gt;/dev/null | sort -h | tail 逐步 du -xhd1 \u0026lt;最大的目录\u0026gt;，一路追到具体文件/子目录。看到文件级后：\nls -lhS /var/log # 按大小列出该目录文件 提示：追到某层出现\u0026quot;许多小文件的总和巨大\u0026quot;，那可能是 inode/海量小文件问题，见 inode 篇。\n方法二：直接找超大单文件 # 全盘找 \u0026gt;1G 的文件（限制到根文件系统） find / -xdev -type f -size +1G -exec ls -lh {} \\; 2\u0026gt;/dev/null | sort -k5 -h # 只找指定目录下超过 500M 的 find /var -type f -size +500M -exec ls -lh {} \\; 2\u0026gt;/dev/null 配合把结果按大小排序：\nfind / -xdev -type f -size +100M -printf \u0026#39;%s %p\\n\u0026#39; 2\u0026gt;/dev/null | sort -rn | head -20 方法三：ncdu 交互查看（最直观） ncdu 提供可上下翻、按大小排的交互界面，是磁盘分析利器：\n# Debian / Ubuntu sudo apt install -y ncdu # RHEL / CentOS / Rocky sudo yum install -y ncdu # 扫描根文件系统（不进其他挂载点） sudo ncdu -x / 用方向键浏览、按目录展开，d 可直接删除，退出时按 q。只扫某个目录则 ncdu /var。\n提示：ncdu 扫描较大分区会花些时间，但直观好用；日常命令行巡检用上面的 du/find 即可。\n定位到之后的处理建议 超大日志：见《磁盘满了怎么办》用 truncate/轮转清理。 被删仍占用的文件：用 lsof +L1 定位进程（见\u0026quot;删不掉\u0026quot;篇）。 缓存目录（/var/cache、~/.cache）：清理前确认用途。 长期：定期巡检 + 告警 手工排查只能救急，长期要监控磁盘增长：\n# 简易巡检脚本思路：每天记录根分区用量到日志 df -h / | tee -a /var/log/disk-usage.log 生产环境建议纳入监控告警（阈值如 80% 提醒、90% 告警），并对已知会增长的目录（日志、数据库、上传目录）做好轮转与配额。\nFAQ 问：最快找出磁盘大户的命令是什么？ du -xhd1 / | sort -h | tail 逐层找大目录；想找超大单文件用 find / -xdev -type f -size +1G。交互式用 ncdu -x /。\n问：为什么 du 显示的和 df 差很多？ 统计口径不同，见同系列《df 和 du 不一致》。\n问：如何知道是哪个文件系统/分区满了？ df -h 会列出每个挂载点；先确认是根还是 /var、/home 等独立分区满，再去对应分区里定位。\n问：有没有更省事的可视化工具？ ncdu 终端交互界面最省事；图形桌面可用 baobab/Filelight（仅本机 GUI 环境）。\n完整的磁盘占用排查可串起同系列：df/du 不一致、磁盘满了删不掉、inode 耗尽、磁盘清理实战。\n","permalink":"https://www.lovezsh.com/posts/find-large-files-disk/","summary":"\u003cp\u003e\u003ccode\u003edf -h\u003c/code\u003e 警告磁盘 90%，但你不知道是哪个目录、哪个文件在涨。先给结论：\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e\u003cstrong\u003e从根开始逐层 \u003ccode\u003edu\u003c/code\u003e 找大目录，再用 \u003ccode\u003efind\u003c/code\u003e/\u003ccode\u003encdu\u003c/code\u003e 定位超大文件\u003c/strong\u003e。这套\u0026quot;自上而下\u0026quot;的套路几分钟就能把大头锁定。\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch2 id=\"方法一du-逐层下钻最通用不依赖额外工具\"\u003e方法一：du 逐层下钻（最通用，不依赖额外工具）\u003c/h2\u003e\n\u003cp\u003e先看根下各目录谁最大：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003edu -xhd1 / 2\u0026gt;/dev/null | sort -h | tail -20\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cblockquote\u003e\n\u003cp\u003e参数：\u003ccode\u003e-x\u003c/code\u003e 不跨文件系统（排除 /proc、独立分区），\u003ccode\u003e-h\u003c/code\u003e 人性化大小，\u003ccode\u003e-d1\u003c/code\u003e 只列一层目录，\u003ccode\u003esort -h\u003c/code\u003e 按人类可读大小排序，\u003ccode\u003etail\u003c/code\u003e 看最大的几个。\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e进入最大的那个目录（比如 \u003ccode\u003e/var\u003c/code\u003e），继续下钻：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003edu -xhd1 /var 2\u0026gt;/dev/null | sort -h | tail\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e逐步 \u003ccode\u003edu -xhd1 \u0026lt;最大的目录\u0026gt;\u003c/code\u003e，一路追到具体文件/子目录。看到文件级后：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003els -lhS /var/log   \u003cspan style=\"color:#75715e\"\u003e# 按大小列出该目录文件\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cblockquote\u003e\n\u003cp\u003e提示：追到某层出现\u0026quot;许多小文件的总和巨大\u0026quot;，那可能是 inode/海量小文件问题，见 inode 篇。\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch2 id=\"方法二直接找超大单文件\"\u003e方法二：直接找超大单文件\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 全盘找 \u0026gt;1G 的文件（限制到根文件系统）\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003efind / -xdev -type f -size +1G -exec ls -lh \u003cspan style=\"color:#f92672\"\u003e{}\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e\\;\u003c/span\u003e 2\u0026gt;/dev/null | sort -k5 -h\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 只找指定目录下超过 500M 的\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003efind /var -type f -size +500M -exec ls -lh \u003cspan style=\"color:#f92672\"\u003e{}\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e\\;\u003c/span\u003e 2\u0026gt;/dev/null\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e配合把结果按大小排序：\u003c/p\u003e","title":"找出 Linux 上最占空间的文件/目录：磁盘增长定位"},{"content":"df -h 显示根分区 100%，服务器开始出问题。先给结论——清理要按\u0026quot;定位 → 按类型清\u0026quot;两步走：\n先用 df/du 找到空间到底被谁占了； 再按下面的清单逐类清理（日志、包缓存、journald、docker、临时文件），每清一类就用 df -h 确认效果，避免删错。 第 1 步：定位大头 df -h / du -xhd1 / 2\u0026gt;/dev/null | sort -h | tail -20 从占用最大的目录逐层进入，找到真正的大户再动手。详细定位方法见同系列《找出最占空间的文件》。\n第 2 步：按类型清理 1) 日志文件（最常背锅） 先清空正在被写的大日志内容（比 rm 更安全，能即时释放且不影响写进程）：\nsudo truncate -s 0 /var/log/syslog sudo truncate -s 0 /var/log/messages # RHEL 系 系统日志轮转配置（控制轮转大小/份数），Debian/Ubuntu 用 logrotate：\n# 查看轮转配置与状态 ls /etc/logrotate.d/ # 手动强制执行一次轮转 sudo logrotate -f /etc/logrotate.conf 2) journald 系统日志 journalctl 日志可能占数百 MB 到数 G。设置大小上限（推荐）并清理：\n# 查看当前占用 sudo journalctl --disk-usage # 清理到保留最近 3 天 sudo journalctl --vacuum-time=3d # 或只保留 100M sudo journalctl --vacuum-size=100M 要持久限制大小，编辑 /etc/systemd/journald.conf：\nSystemMaxUse=200M 然后重启服务生效：\nsudo systemctl restart systemd-journald 提示：journald.conf 位置 Debian 与 RHEL 均在 /etc/systemd/journald.conf；改完 SystemMaxUse 记得重启 systemd-journald。\n3) 软件包缓存 # Debian / Ubuntu：清理 apt 下载缓存 sudo apt clean # RHEL / CentOS / Rocky：清理 yum/dnf 缓存 sudo yum clean all 4) Docker 残留（装了 Docker 时） # 查看可回收 docker system df # 清理悬空镜像、停止的容器、无用网络/构建缓存（会提示确认） docker system prune # 更激进（含未使用的镜像，谨慎） docker system prune -a 5) 临时与缓存目录 # 清空 /tmp（注意是否有进程正在用的文件，生产谨慎） sudo find /tmp -type f -atime +3 -delete 2\u0026gt;/dev/null 清理有风险，原则：先看再清、逐类确认、只删确定无用。不确定的目录不要 rm -rf。\n第 3 步：确认清理效果 df -h / 若空间仍满，可能是被删文件仍被进程占用（lsof +L1，见同系列\u0026quot;删不掉\u0026quot;篇）或 inode 问题（df -i，见 inode 篇）。\n一屏速查表 # 一套顺手的检查 df -h / \u0026amp;\u0026amp; df -i / du -xhd1 / | sort -h | tail sudo journalctl --disk-usage docker system df # 有 docker 时 lsof +L1 2\u0026gt;/dev/null | grep -i deleted # 被删仍占用 FAQ 问：删了大日志空间为什么没释放？ 文件被进程占用未释放，用 \u0026gt; 文件 或 truncate -s 0 文件 清空内容可即时释放；被删未释放的用 lsof +L1 定位进程。\n问：journald 日志怎么设上限？ 改 /etc/systemd/journald.conf 的 SystemMaxUse，重启 systemd-journald；即时清理用 journalctl --vacuum-size=100M。\n问：apt/yum 缓存可以放心清吗？ 可以，只是删掉下载过的安装包缓存，不影响已装软件。apt clean / yum clean all 即可。\n问：磁盘满了还能正常启动服务吗？ 可能无法写入导致服务异常；尽快清出空间。若彻底无法写，先看能否用 journalctl --vacuum 或删临时文件腾出最小空间。\n想系统搞懂磁盘空间去哪，可看同系列：df/du 不一致、磁盘满了删不掉、inode 耗尽、找出最占空间的文件。\n","permalink":"https://www.lovezsh.com/posts/linux-disk-cleanup-guide/","summary":"\u003cp\u003e\u003ccode\u003edf -h\u003c/code\u003e 显示根分区 100%，服务器开始出问题。先给结论——\u003cstrong\u003e清理要按\u0026quot;定位 → 按类型清\u0026quot;两步走\u003c/strong\u003e：\u003c/p\u003e\n\u003cblockquote\u003e\n\u003col\u003e\n\u003cli\u003e先用 \u003ccode\u003edf\u003c/code\u003e/\u003ccode\u003edu\u003c/code\u003e 找到空间到底被谁占了；\u003c/li\u003e\n\u003cli\u003e再按下面的清单逐类清理（日志、包缓存、journald、docker、临时文件），每清一类就用 \u003ccode\u003edf -h\u003c/code\u003e 确认效果，避免删错。\u003c/li\u003e\n\u003c/ol\u003e\n\u003c/blockquote\u003e\n\u003ch2 id=\"第-1-步定位大头\"\u003e第 1 步：定位大头\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003edf -h /\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003edu -xhd1 / 2\u0026gt;/dev/null | sort -h | tail -20\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e从占用最大的目录逐层进入，找到真正的大户再动手。详细定位方法见同系列《找出最占空间的文件》。\u003c/p\u003e\n\u003ch2 id=\"第-2-步按类型清理\"\u003e第 2 步：按类型清理\u003c/h2\u003e\n\u003ch3 id=\"1-日志文件最常背锅\"\u003e1) 日志文件（最常背锅）\u003c/h3\u003e\n\u003cp\u003e先清空正在被写的大日志内容（比 \u003ccode\u003erm\u003c/code\u003e 更安全，能即时释放且不影响写进程）：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo truncate -s \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e /var/log/syslog\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo truncate -s \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e /var/log/messages   \u003cspan style=\"color:#75715e\"\u003e# RHEL 系\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e系统日志轮转配置（控制轮转大小/份数），Debian/Ubuntu 用 \u003ccode\u003elogrotate\u003c/code\u003e：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 查看轮转配置与状态\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003els /etc/logrotate.d/\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 手动强制执行一次轮转\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo logrotate -f /etc/logrotate.conf\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3 id=\"2-journald-系统日志\"\u003e2) journald 系统日志\u003c/h3\u003e\n\u003cp\u003e\u003ccode\u003ejournalctl\u003c/code\u003e 日志可能占数百 MB 到数 G。设置大小上限（推荐）并清理：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 查看当前占用\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo journalctl --disk-usage\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 清理到保留最近 3 天\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo journalctl --vacuum-time\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e3d\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 或只保留 100M\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo journalctl --vacuum-size\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e100M\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e要\u003cstrong\u003e持久\u003c/strong\u003e限制大小，编辑 \u003ccode\u003e/etc/systemd/journald.conf\u003c/code\u003e：\u003c/p\u003e","title":"Linux 磁盘满了怎么办：日志/缓存/journald 清理实战"},{"content":"磁盘明明还剩很多空间，写文件、甚至 touch 一个空文件都报 No space left on device？先给结论：\n磁盘没满，是 inode 用尽了。 Linux 每个文件/目录都要占用一个 inode（索引节点），一个分区能创建的 inode 总数有限。当小文件多到把 inode 吃光，即便块空间还有富余，也无法再新建任何文件。\n第 1 步：确认是不是 inode 耗尽 df -i 看 inode 使用率，而不是 df -h 看空间：\ndf -i / 看 IUse% 一列。如果接近 100%，基本可以确定是 inode 耗尽。\n# 只看根分区 df -i / | head # Filesystem Inodes IUsed IFree IUse% Mounted on # /dev/sda1 655360 655360 0 100% / 提示：df -h 看的是块（Block）空间，df -i 看的是inode（索引节点），两者都满都会导致\u0026quot;无法写入\u0026quot;，需分别检查。\n第 2 步：找出小文件堆在哪 最常见来源：邮件队列、/tmp、缓存目录、程序产生的海量小日志或 session 文件、journald 高频写入等。逐层找 inode 占用大的目录：\n# 找到 inode 数最多的顶层目录 find / -xdev -printf \u0026#39;%h\\n\u0026#39; 2\u0026gt;/dev/null | sort | uniq -c | sort -rn | head # 更直观：逐层统计，进入可疑目录继续 for d in /var /tmp /home; do echo \u0026#34;$d: $(find $d -xdev -type f 2\u0026gt;/dev/null | wc -l)\u0026#34;; done 或按目录树从上到下找 inode 大户：\ndu --inodes -x --max-depth=1 / 2\u0026gt;/dev/null | sort -rn | head -20 提示：du --inodes 需要 GNU coreutils 较新版本（Debian 较新 / RHEL 8+ 一般可用）。若提示不支持，就用上面的 find ... | uniq -c 思路。\n第 3 步：针对海量小文件清理 定位到目录后，查看里面是什么再清理。清空目录下大量小文件示例（注意先确认目录用途，别误删数据）：\n# 例：清空 session/临时文件目录下的内容（先看，再清） ls /tmp/sess_* | head # 先看看是什么 find /tmp/sess_* -type f -delete # 删除（确认无误后） # 大日志先清空内容（省 inode 不释放也没关系） sudo truncate -s 0 /var/log/app.log 清理后确认 inode 使用率下降：\ndf -i / 如果确实需要更多 inode 重新 mkfs 时加大 inode：只有分区未用/可重新格式化时才可行（会丢数据，谨慎），mkfs.ext4 -N 数量 或指定 -i bytes-per-inode 预留更多 inode。 把海量小文件目录放到 inode 充足的分区，或改用能承载海量小文件的存储（如换到 inode 预算更高的文件系统）。 多数场景靠清理就能解决，扩容 inode 是在清理也满足不了时的下策。 日常预防 对会堆积小文件的目录做定期清理 + 日志轮转，别让小文件无限增长。 用 df -i 纳入日常巡检；结合同系列\u0026quot;磁盘增长定位\u0026quot;识别异常目录。 FAQ 问：df -h 明明有空间，为什么还报 no space？ 可能是 inode 耗尽。用 df -i 看 inode 使用率即可区分\u0026quot;块满\u0026quot;与\u0026quot;inode 满\u0026quot;。\n问：inode 满了怎么快速定位小文件在哪？ du --inodes -x --max-depth=1 / | sort -rn | head 逐层找目录；再进到可疑目录缩小范围。\n问：不删数据能给分区扩容 inode 吗？ 运行中的分区一般不能直接扩 inode；通常靠清理小文件解决，或把堆积目录迁到 inode 更充足的分区/文件系统。\n问：inode 数怎么看总共多少、还能用多久？ df -i / 的 Inodes 与 IFree 两列即总量与剩余；IUse% 越接近 100% 越危险。\n想区分磁盘空间去向，可看同系列：df/du 不一致、磁盘满了删不掉、磁盘清理实战。\n","permalink":"https://www.lovezsh.com/posts/inode-exhausted-nospace/","summary":"\u003cp\u003e磁盘明明还剩很多空间，写文件、甚至 \u003ccode\u003etouch\u003c/code\u003e 一个空文件都报 \u003ccode\u003eNo space left on device\u003c/code\u003e？先给结论：\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e\u003cstrong\u003e磁盘没满，是 inode 用尽了。\u003c/strong\u003e Linux 每个文件/目录都要占用一个 inode（索引节点），一个分区能创建的 inode 总数有限。当小文件多到把 inode 吃光，即便块空间还有富余，也无法再新建任何文件。\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch2 id=\"第-1-步确认是不是-inode-耗尽\"\u003e第 1 步：确认是不是 inode 耗尽\u003c/h2\u003e\n\u003cp\u003e\u003ccode\u003edf -i\u003c/code\u003e 看 inode 使用率，而不是 \u003ccode\u003edf -h\u003c/code\u003e 看空间：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003edf -i /\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e看 \u003ccode\u003eIUse%\u003c/code\u003e 一列。如果接近 100%，基本可以确定是 inode 耗尽。\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 只看根分区\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003edf -i / | head\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Filesystem     Inodes IUsed IFree IUse% Mounted on\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# /dev/sda1    655360  655360 0     100%  /\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cblockquote\u003e\n\u003cp\u003e提示：\u003ccode\u003edf -h\u003c/code\u003e 看的是\u003cstrong\u003e块（Block）空间\u003c/strong\u003e，\u003ccode\u003edf -i\u003c/code\u003e 看的是\u003cstrong\u003einode（索引节点）\u003c/strong\u003e，两者都满都会导致\u0026quot;无法写入\u0026quot;，需分别检查。\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch2 id=\"第-2-步找出小文件堆在哪\"\u003e第 2 步：找出小文件堆在哪\u003c/h2\u003e\n\u003cp\u003e最常见来源：邮件队列、\u003ccode\u003e/tmp\u003c/code\u003e、缓存目录、程序产生的海量小日志或 session 文件、\u003ccode\u003ejournald\u003c/code\u003e 高频写入等。逐层找 inode 占用大的目录：\u003c/p\u003e","title":"Linux inode 耗尽：磁盘没满却报 no space left"},{"content":"df -h 已经 100%，于是你 rm 删了一个几十 G 的日志，再一看——还是 100%。先给结论：\n文件被运行中的进程打开着。 rm 只是把文件从目录里\u0026quot;摘掉\u0026quot;（解除链接），但只要还有进程持有它的文件句柄，占用的磁盘块就不会释放。要找到是谁，用 lsof +L1。\n很多日志/程序（tail、java、数据库、nginx、某些写日志的后台服务）都会持续持有文件句柄，删了文件也一样占着空间，直到进程退出或服务重启。\n第 1 步：确认空间确实没释放 df -h / 如果删文件前后都接近 100%，基本可以判断是被占用未释放。\n第 2 步：找出被删除但仍被占用的文件 核心命令，列出所有 link count 为 0（已被删除）却仍被打开的文件：\nlsof +L1 输出里 COMMAND 是进程名，PID 是进程号，NAME 会显示类似 (deleted) 的删除标记：\nlsof +L1 2\u0026gt;/dev/null | grep -i deleted # 只看有没有删除文件；通常长这样： # COMMAND PID USER FD TYPE DEVICE ... NAME # java 12345 root 1w REG 8,1 ... /var/log/app.log (deleted) 提示：lsof 若未安装，Debian/Ubuntu 用 sudo apt install lsof，RHEL/CentOS/Rocky 用 sudo yum install -y lsof。\n第 3 步：确认是哪个进程、能否处理 # 用 PID 看进程详情 ps -fp \u0026lt;PID\u0026gt; # 只想看某个目录下的删除占用 lsof +L1 2\u0026gt;/dev/null | grep \u0026#39;/var/log\u0026#39; 第 4 步：释放空间 最干净的方式是让持有句柄的服务正常重启，句柄关闭后块即归还：\n# 以 nginx / systemd 服务为例 sudo systemctl restart nginx # 确认空间释放 df -h / 如果你不确定是哪个服务，先 lsof +L1 看 COMMAND 与路径，再针对性重启对应服务（如 java 进程一般是应用，nginx/httpd 是 Web 服务）。\n谨慎：不要直接 kill 关键生产进程。能优雅重启就优雅重启；确需 kill 时先确认该进程可被拉起。\n如果一直查不到\u0026quot;删除文件\u0026quot;的进程 空间仍满但 lsof +L1 为空，可能是：\ndf 与 du 口径问题——见同系列《df -h 和 du 不一致》； inode 耗尽导致看似\u0026quot;没空间\u0026quot;——见《inode 耗尽》； 文件在别的挂载点/快照里，用 df -h 确认到底是哪个分区满。 一个更彻底的小技巧：清空而非删除日志 对于正在被写的日志，推荐用\u0026quot;清空文件内容\u0026quot;代替\u0026quot;删除文件\u0026quot;，这样不需要重启进程也能立刻释放空间，且进程写入不中断：\n# 用 truncate 或直接 \u0026gt; 清空，而非 rm sudo truncate -s 0 /var/log/bigapp.log # 或 sudo sh -c \u0026#39;\u0026gt; /var/log/bigapp.log\u0026#39; df -h / 提示：清空要用重定向把内容置 0 而不是 rm，才能即时释放且不影响持有句柄的进程。\nFAQ 问：rm 删了大文件空间立刻就该释放吗？ 只有当没有进程持有该文件句柄时才立刻释放；被进程占用的文件要等进程退出/重启。\n问：怎么快速找到\u0026quot;删除文件但没释放\u0026quot;的进程？ lsof +L1 | grep deleted，看 COMMAND 和 PID。\n问：不能重启服务怎么释放空间？ 用 \u0026gt; 文件 或 truncate -s 0 文件 把文件清空（针对正在写的日志），或视情况处理占用进程。\n问：为什么 tail -f 也会导致空间不释放？ tail -f 会持续打开文件句柄；若先 rm 再 tail 某文件的旧路径，句柄仍指向被删文件，空间就占着。\n想系统看懂磁盘占用，可看同系列：df/du 不一致的原因、inode 耗尽、磁盘清理实战。\n","permalink":"https://www.lovezsh.com/posts/disk-full-file-deleted-still-used/","summary":"\u003cp\u003e\u003ccode\u003edf -h\u003c/code\u003e 已经 100%，于是你 \u003ccode\u003erm\u003c/code\u003e 删了一个几十 G 的日志，再一看——还是 100%。先给结论：\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e\u003cstrong\u003e文件被运行中的进程打开着。\u003c/strong\u003e \u003ccode\u003erm\u003c/code\u003e 只是把文件从目录里\u0026quot;摘掉\u0026quot;（解除链接），但只要还有进程持有它的文件句柄，占用的磁盘块就\u003cstrong\u003e不会释放\u003c/strong\u003e。要找到是谁，用 \u003ccode\u003elsof +L1\u003c/code\u003e。\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e很多日志/程序（\u003ccode\u003etail\u003c/code\u003e、\u003ccode\u003ejava\u003c/code\u003e、数据库、nginx、某些写日志的后台服务）都会持续持有文件句柄，删了文件也一样占着空间，直到进程退出或服务重启。\u003c/p\u003e\n\u003ch2 id=\"第-1-步确认空间确实没释放\"\u003e第 1 步：确认空间确实没释放\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003edf -h /\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e如果删文件前后都接近 100%，基本可以判断是被占用未释放。\u003c/p\u003e\n\u003ch2 id=\"第-2-步找出被删除但仍被占用的文件\"\u003e第 2 步：找出被删除但仍被占用的文件\u003c/h2\u003e\n\u003cp\u003e核心命令，列出所有 link count 为 0（已被删除）却仍被打开的文件：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003elsof +L1\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e输出里 \u003ccode\u003eCOMMAND\u003c/code\u003e 是进程名，\u003ccode\u003ePID\u003c/code\u003e 是进程号，\u003ccode\u003eNAME\u003c/code\u003e 会显示类似 \u003ccode\u003e(deleted)\u003c/code\u003e 的删除标记：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003elsof +L1 2\u0026gt;/dev/null | grep -i deleted\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 只看有没有删除文件；通常长这样：\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# COMMAND   PID   USER  FD  TYPE DEVICE ...  NAME\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# java    12345  root  1w   REG  8,1 ...  /var/log/app.log (deleted)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cblockquote\u003e\n\u003cp\u003e提示：\u003ccode\u003elsof\u003c/code\u003e 若未安装，Debian/Ubuntu 用 \u003ccode\u003esudo apt install lsof\u003c/code\u003e，RHEL/CentOS/Rocky 用 \u003ccode\u003esudo yum install -y lsof\u003c/code\u003e。\u003c/p\u003e","title":"Linux 磁盘空间满了却删不掉/没释放？定位被占用文件"},{"content":"用 df -h 看到根分区 90%，用 du -sh /* 累加起来却只有一半？这几乎是每个运维都遇到过的困惑。先说结论：\ndf 和 du 统计口径不同，对不上是正常现象。 df 看的是文件系统层已使用的块；du 是把当前能看到的每个文件实际大小累加。两者只在\u0026quot;一切文件都可见、无被删占用、无隐藏挂载\u0026quot;的理想状态下才相等。\n这篇文章讲清楚差异来源，并给出一套逐步定位\u0026quot;空间到底去哪了\u0026quot;的方法。\n先分清两个命令的口径 命令 统计对象 特点 df 整个文件系统/分区的块使用情况 包含所有占用的块，含元数据、被删但仍被进程占用、被隐藏挂载挡住的 du 从当前目录遍历到的可见文件/目录 只算能遍历到的，mount 挂载点下的内容默认也算它自己一份 举一个极端例子：某进程正在写一个大文件，你把它删了但进程没退出——磁盘块并未释放，df 显示还是满的，而 du 已经\u0026quot;看不到\u0026quot;那个文件了。这是最常见的\u0026quot;对不上\u0026quot;来源之一。\n用这个顺序排查，找出空间去哪了 第 1 步：确认是 df 大还是 du 大 df -h / du -xh --max-depth=1 / 2\u0026gt;/dev/null | sort -h | tail -20 提示：du -x 表示不要跨文件系统去统计（避免把 /proc、挂载的独立分区也算进来），--max-depth=1 只列一层。\n第 2 步：被删除但仍被进程占用的文件（最常见） 如果 df 明显大于 du，优先查\u0026quot;删了但没释放\u0026quot;的文件：\nlsof +L1 2\u0026gt;/dev/null | grep -iE \u0026#39;deleted|unlinked\u0026#39; +L1 列出所有 link count 为 0（已被删除）却仍被打开的文件。找到后定位进程：\nlsof +L1 2\u0026gt;/dev/null | grep -i deleted # 记下第 2 列的 PID，再用 ps 看是谁 ps -fp \u0026lt;PID\u0026gt; 处理方式：让对应服务正常重启（释放句柄）即可归还空间；如果无法重启，只能视情况处理该进程。确认归还：\ndf -h / 第 3 步：有没有隐藏/重复的挂载点 同一目录被二次挂载（或某个分区挂到了 du 遍历的目录下），du 会重复计算或漏算。查看所有挂载与占用：\nmount | grep -E \u0026#39;^/dev\u0026#39; | sort findmnt # 想看清每个挂载点占了多少，用 -x 从上往下看 df -h 第 4 步：稀疏文件与预分配 用 truncate 或数据库预分配的文件，du 显示的是\u0026quot;实际占用\u0026quot;，而 ls -lh 显示的是\u0026quot;逻辑大小\u0026quot;，两者也能差很大。检查有无大逻辑文件：\nls -lhS /path/to/check 2\u0026gt;/dev/null | head 如果确认是大文件且不再需要，用 truncate 收缩即可（对正在写的文件慎用）。\n快速对比示例 # df 角度 df -h / # du 逐目录角度（最多 20 行、按大小排序） du -xhd1 / 2\u0026gt;/dev/null | sort -h | tail -20 # 谁占用被删文件 lsof +L1 2\u0026gt;/dev/null | grep deleted 常见结论速记 df 大、du 小 → 多半是被删未释放（lsof +L1）或隐藏挂载。 du 大、df 小（不太常见）→ 目录下有另一个独立挂载点被 du 重复计入，或跨文件系统统计没加 -x。 两个都大 → 确实有海量小文件（占 inode）或文件系统元数据占用，见 inode 篇。 FAQ 问：为什么删了文件 df 空间没变？ 因为文件仍被某进程打开（句柄未释放），只有关闭该进程/重启服务后块才会归还。用 lsof +L1 找出来。\n问：du 为什么比 df 看到的少很多？ du 只统计当前能遍历到的可见文件，被删占用、/proc 之类虚拟文件系统、或隐藏挂载的内容都不会算进去。\n问：怎样最直观地看每个目录占用？ du -xhd1 / | sort -h | tail 从根往下逐层找大目录，逐层进入即可定位大头。\n想进一步清理空间或看懂 inode，可看同系列：磁盘空间满了却删不掉 / inode 耗尽 / 磁盘满了怎么办清理实战。\n","permalink":"https://www.lovezsh.com/posts/df-du-disk-space/","summary":"\u003cp\u003e用 \u003ccode\u003edf -h\u003c/code\u003e 看到根分区 90%，用 \u003ccode\u003edu -sh /*\u003c/code\u003e 累加起来却只有一半？这几乎是每个运维都遇到过的困惑。先说结论：\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e\u003cstrong\u003e\u003ccode\u003edf\u003c/code\u003e 和 \u003ccode\u003edu\u003c/code\u003e 统计口径不同，对不上是正常现象。\u003c/strong\u003e \u003ccode\u003edf\u003c/code\u003e 看的是\u003cstrong\u003e文件系统层已使用的块\u003c/strong\u003e；\u003ccode\u003edu\u003c/code\u003e 是把\u003cstrong\u003e当前能看到的每个文件实际大小\u003c/strong\u003e累加。两者只在\u0026quot;一切文件都可见、无被删占用、无隐藏挂载\u0026quot;的理想状态下才相等。\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e这篇文章讲清楚差异来源，并给出一套逐步定位\u0026quot;空间到底去哪了\u0026quot;的方法。\u003c/p\u003e\n\u003ch2 id=\"先分清两个命令的口径\"\u003e先分清两个命令的口径\u003c/h2\u003e\n\u003ctable\u003e\n\t\u003cthead\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003cth\u003e命令\u003c/th\u003e\n\t\t\t\t\t\u003cth\u003e统计对象\u003c/th\u003e\n\t\t\t\t\t\u003cth\u003e特点\u003c/th\u003e\n\t\t\t\u003c/tr\u003e\n\t\u003c/thead\u003e\n\t\u003ctbody\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003edf\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e整个\u003cstrong\u003e文件系统/分区\u003c/strong\u003e的块使用情况\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e包含所有占用的块，含元数据、被删但仍被进程占用、被隐藏挂载挡住的\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003edu\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e从当前目录遍历到的\u003cstrong\u003e可见文件/目录\u003c/strong\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e只算能遍历到的，\u003ccode\u003emount\u003c/code\u003e 挂载点下的内容默认也算它自己一份\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\u003c/tbody\u003e\n\u003c/table\u003e\n\u003cp\u003e举一个极端例子：某进程正在写一个大文件，你把它删了但进程没退出——磁盘块并未释放，\u003ccode\u003edf\u003c/code\u003e 显示还是满的，而 \u003ccode\u003edu\u003c/code\u003e 已经\u0026quot;看不到\u0026quot;那个文件了。这是最常见的\u0026quot;对不上\u0026quot;来源之一。\u003c/p\u003e\n\u003ch2 id=\"用这个顺序排查找出空间去哪了\"\u003e用这个顺序排查，找出空间去哪了\u003c/h2\u003e\n\u003ch3 id=\"第-1-步确认是-df-大还是-du-大\"\u003e第 1 步：确认是 df 大还是 du 大\u003c/h3\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003edf -h /\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003edu -xh --max-depth\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e / 2\u0026gt;/dev/null | sort -h | tail -20\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cblockquote\u003e\n\u003cp\u003e提示：\u003ccode\u003edu -x\u003c/code\u003e 表示\u003cstrong\u003e不要跨文件系统\u003c/strong\u003e去统计（避免把 \u003ccode\u003e/proc\u003c/code\u003e、挂载的独立分区也算进来），\u003ccode\u003e--max-depth=1\u003c/code\u003e 只列一层。\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch3 id=\"第-2-步被删除但仍被进程占用的文件最常见\"\u003e第 2 步：被删除但仍被进程占用的文件（最常见）\u003c/h3\u003e\n\u003cp\u003e如果 \u003ccode\u003edf\u003c/code\u003e 明显大于 \u003ccode\u003edu\u003c/code\u003e，优先查\u0026quot;删了但没释放\u0026quot;的文件：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003elsof +L1 2\u0026gt;/dev/null | grep -iE \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;deleted|unlinked\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003ccode\u003e+L1\u003c/code\u003e 列出所有 link count 为 0（已被删除）却仍被打开的文件。找到后定位进程：\u003c/p\u003e","title":"df -h 和 du 磁盘占用不一致：原因与排查"}]