Linux Ulimit 小记
Ulimit
- limit的设定值是 per-process 的
- 在 Linux 中,每个普通进程可以调用 getrlimit() 来查看自己的 limits,也可以调用 setrlimit() 来改变自身的 soft limits
- 要改变 hard limit, 则需要进程有 CAP_SYS_RESOURCE 权限
- 进程 fork() 出来的子进程,会继承父进程的 limits 设定
ulimit
是 shell 的内置命令。在执行ulimit
命令时,其实是 shell 自身调用 getrlimit()/setrlimit() 来获取/改变自身的 limits. 当我们在 shell 中执行应用程序时,相应的进程就会继承当前 shell 的 limits 设定- shell 的初始 limits 是谁设定的: 通常是 pam_limits 设定的。顾名思义,pam_limits 是一个 PAM 模块,用户登录后,pam_limits 会给用户的 shell 设定在 limits.conf 定义的值
ulimit, limits.conf 和 pam_limits 的关系,大致是这样的:
- 用户进行登录,触发 pam_limits;
- pam_limits 读取 limits.conf,相应地设定用户所获得的 shell 的 limits;
- 用户在 shell 中,可以通过 ulimit 命令,查看或者修改当前 shell 的 limits;
- 当用户在 shell 中执行程序时,该程序进程会继承 shell 的 limits 值。于是,limits 在进程中生效了
/etc/security/limits.conf
格式:
# cat /etc/security/limits.conf
(省略若干....)
# End of file
apps soft nofile 65535
apps hard nofile 65535
apps soft nproc 10240
apps hard nproc 10240
第一列表示域(domain),可以使用用户名(root等),组名(以@开头),通配置*和%,%可以用于%group参数。
第二列表示类型(type),值可以是soft或者hard
第三列表示项目(item),值可以是core, data, fsize, memlock, nofile, rss, stack, cpu, nproc, as, maxlogins, maxsyslogins, priority, locks, msgqueue, nie, rtprio。其中nofile(Number of Open File)就是文件打开数。
第四列表示值
- 软限制可以在程序的进程中自行改变(突破限制)
- 硬限制则不行(除非程序进程有root权限)
linux capability
- Linux内核从2.1版开始提供了”能力”机制,替代传统UNIX模型中”特权/非特权”的简单划分,而是通过具体的能力要求,来判定某一个操作是否可以执行。通过这样的限制,可以确保系统中不存在”完全不被限制的”进程
- Linux系统中的能力分为两部分,一部分是进程能力,一部分是文件能力,而Linux内核最终检查的是进程能力中的Effective
每个进程有三个和能力有关的位图:
- permitted(P): 它是effective capabilities和Inheritable capability的超集, 表示进程能够使用的能力, 这些能力是被进程自己临时放弃的, 进程放弃没有必要的能力对于提高安全性大有助益
- effective(E): Linux内核真正检查的能力集
- inheritable(I): 表明该进程可以通过execve继承给新进程的能力
可执行文件能力:
TODO
进程能力调试:
cat /proc/<pid>/status
CapInh: 0000000000000000
CapPrm: 0000003fffffffff
CapEff: 0000003fffffffff
CapBnd: 0000003fffffffff 系统的边界能力
能力边界集:
- 能力边界集(capability bounding set)是系统中所有进程允许保留的能力。如果在能力边界集中不存在某个能力,那么系统中的所有进程都没有这个能力,即使以超级用户权限执行的进程也一样
- root用户可以向能力边界集中写入新的值来修改系统保留的能力。但是要注意,root用户能够从能力边界集中删除能力,却不能再恢复被删除的能力,只有init进程能够添加能力。通常,一个能力如果从能力边界集中被删除,只有系统重新启动才能恢复
- 删除系统中多余的能力对提高系统的安全性是很有好处的
能力可视化:
/sbin/capsh --decode=0000003fffffffff
获取和设置capabilities:
- 系统调用capget(2)和capset(2),可被用于获取和设置线程自身的capabilities。此外,也可以使用libcap中提供的接口cap_get_proc(3)和cap_set_proc(3)。当然,Permitted集合默认是不能增加新的capabilities的,除非CAP_SETPCAP在Effective集合中
Linux能力机制的继承:
P'(permitted) = (P(inheritable) & F(inheritable)) |
(F(permitted) & cap_bset) //新进程的permitted有老进程的和新进程的inheritable和可执行文件的permitted及cap_bset运算得到.
P'(effective) = F(effective) ? P'(permitted) : 0 //新进程的effective依赖可执行文件的effective位,使能:和新进程的permitted一样,负责为空
P'(inheritable) = P(inheritable) [i.e., unchanged] //新进程的inheritable直接继承老进程的Inheritable
说明:
P 在执行execve函数前,进程的能力
P' 在执行execve函数后,进程的能力
F 可执行文件的能力
cap_bset 系统能力的边界值,在此处默认全为1