Linux ulimit 资源限制实战:防资源耗尽攻击配置

适用场景

ulimit 资源限制用于防止单个进程或单个用户耗尽系统资源,是抵御 Fork 炸弹、连接数耗尽型 DoS、文件描述符泄漏等问题的系统层防线。典型适用场景:Nginx 高并发下报 Too many open files、某业务进程失控打开数万连接拖垮整机、恶意或异常脚本无限 fork 子进程导致服务器假死、多用户服务器需要限制每人可创建的进程数量。合理的资源限制能让异常进程被隔离在可控范围内,而不是波及整台服务器。

前置条件

  • root 或 sudo 权限的 Linux 服务器,本文以 CentOS 7+/Ubuntu 20+(systemd 环境)为例
  • 明确业务进程的合理资源水位(可先通过 ulimit -a 与监控数据确认)
  • 若为容器或云主机,确认宿主机未额外覆盖 limits 配置

原理说明

ulimit 是 Linux 对进程资源使用的软硬限制机制:soft limit 是当前生效值,进程可自行调高但不能超过 hard limit;hard limit 是上限,普通进程只能调低不能调高。核心限制项包括 nofile(打开文件描述符数,网络连接也占用该配额)、nproc(可创建进程数)、maxlogins(同账号并发登录数)。配置入口有三层:Shell 会话通过 /etc/security/limits.conf(由 PAM 的 pam_limits 模块读取);systemd 系统服务通过 unit 文件的 LimitNOFILE 等指令;systemd 用户会话通过 /etc/systemd/system.conf 的 DefaultLimitNOFILE。三层互不继承,只配一处是常见踩坑点。

操作步骤

第一步:查看当前限制水位

# 当前 Shell 的全部限制
ulimit -a
# 只看文件描述符
ulimit -n
# 查看某个已运行进程(如 Nginx)的实际限制
cat /proc/$(pidof nginx | awk '{print $1}')/limits

第二步:配置 limits.conf 全局基线

编辑 /etc/security/limits.conf,追加以下内容(按业务调整数值):

# 文件描述符:业务账号放宽,防止高并发报错
*    soft  nofile     65535
*    hard  nofile     65535
root soft  nofile     65535
root hard  nofile     65535
# 进程数:限制普通用户 fork 能力,缓解 Fork 炸弹
*    hard  nproc      4096
# 单账号并发登录限制
*    hard  maxlogins  10

确认 PAM 已启用限制模块,/etc/pam.d/login/etc/pam.d/common-session(Ubuntu)中应存在:

session required pam_limits.so

第三步:配置 systemd 服务级限制

limits.conf 对 systemd 启动的服务不生效,必须单独配置。以 Nginx 为例,创建 override:

systemctl edit nginx
# 在打开的编辑器中写入:
[Service]
LimitNOFILE=65535
LimitNPROC=4096
# 保存后重载并重启
systemctl daemon-reload
systemctl restart nginx

同时调整全局默认值,编辑 /etc/systemd/system.conf

DefaultLimitNOFILE=65535
DefaultLimitNPROC=4096
# 生效需重启系统

第四步:为关键服务联动 Nginx 配置

系统层放宽后,应用层也要跟上,否则 Nginx 仍按旧值工作:

# /etc/nginx/nginx.conf 全局段
worker_rlimit_nofile 65535;
events {
    worker_connections 10240;
}

配置验证

# 重新登录后验证 Shell 限制
ulimit -n
ulimit -nH
# 验证 systemd 服务的实际生效值
cat /proc/$(pidof nginx | awk '{print $1}')/limits | grep "open files"
# 压测连接数是否突破旧上限(示例:ab 压测或业务自测)
ab -n 10000 -c 2000 http://127.0.0.1/
# 检查有无因限制产生的拒绝记录
journalctl -u nginx --since "10 min ago" | grep -i "too many"

常见问题

FAQ 1:改了 limits.conf 后 ulimit -n 仍是 1024?

三个原因依次排查:修改后必须重新登录(或重启服务),已存在会话不会热更新;服务由 systemd 启动时读取的是 unit 内 LimitNOFILE 而非 limits.conf;远程通过 sshd 登录的会话还需确认 /etc/pam.d/sshd 中包含 pam_limits.so 行。

FAQ 2:nproc 限制过严导致业务 fork 失败怎么处理?

先通过 journalctl -k | grep -i fork 或应用日志确认触发限制的具体账号,再为该账号单独放宽,limits.conf 支持按用户精确指定,例如 appuser hard nproc 16384。避免直接把全局 nproc 调到无限(unlimited),否则 Fork 炸弹防线失效。

总结

ulimit 是最轻量的资源隔离手段,不依赖额外软件即可为服务器构建第一道防资源耗尽防线。落地要点:nofile 按业务并发量设定并同步 Nginx 的 worker_rlimit_nofile;nproc 保留全局上限防 Fork 炸弹;systemd 服务必须单独配置 LimitNOFILE。配置完成后用 /proc/<pid>/limits 逐服务验证生效值,并将检查纳入巡检清单。