使用rlimit(以及为什么要这样做)

我一直在浏览一些旧笔记,并碰到setrlimit(2)的提醒。

这是一个C系统调用,它允许应用程序在许多重要参数上指定资源限制:

  • RLIMIT_AS –进程的虚拟内存的最大大小(地址空间),以字节为单位。
  • RLIMIT_CORE –核心文件的最大大小。
  • RLIMIT_CPU – CPU时间限制(以秒为单位)。
  • RLIMIT_DATA –进程的数据段(已初始化数据,未初始化数据和堆)的最大大小。
  • RLIMIT_FSIZE –进程可以创建的最大文件大小。
  • RLIMIT_MEMLOCK –可以锁定到RAM的最大内存字节数。
  • RLIMIT_MSGQUEUE –指定可以为调用进程的真实用户ID为POSIX消息队列分配的字节数限制。
  • RLIMIT_NICE –指定可以使用setpriority(2)或nice(2)将进程的nice值提高到的上限。
  • RLIMIT_NOFILE –指定一个比该进程可以打开的最大文件描述符数目大一的值。
  • RLIMIT_NPROC –可以为调用进程的真实用户ID创建的最大进程数(或更确切地说,在Linux中为线程)。
  • RLIMIT_RSS –指定进程的驻留集的限制(以页为单位)(RAM中驻留的虚拟页数)。
  • RLIMIT_RTPRIO –指定可以使用sched_setscheduler(2)和sched_setparam(2)为此进程设置的实时优先级的上限。
  • RLIMIT_RTTIME –指定在不进行阻塞系统调用的情况下,根据实时调度策略调度的进程可能消耗的CPU时间的限制(以微秒为单位)。
  • RLIMIT_SIGPENDING –指定可以排队等待呼叫过程的真实用户ID的信号数量的限制。
  • RLIMIT_STACK –进程堆栈的最大大小,以字节为单位。

所有程序的限制都在配置文件(/etc/security/limits.conf和/etc/security/limits.d)中指定,或者可以通过'ulimit'shell函数在单个shell及其进程中设置。 在Linux下,当前进程的资源限制在/ proc / [pid] / limits中可见。

也可以通过setrlimit(2)以编程方式设置限制。 任何过程都可以赋予自己更多的限制。 任何特权进程(以root身份运行或具有正确的功能运行)都可以赋予自己更多的限制。

我相信大多数系统默认为无限制或非常高的限制,应用程序有责任指定更严格的限制。 安全性更高的系统将起到相反的作用-它们将具有更严格的限制,并使用特权加载程序将更多资源分配给特定程序。

我们为什么在乎?

深度安全。

首先,人们会犯错误。 设置合理的限制可以防止失控的系统崩溃。

其次,攻击者将利用他们发现的任何机会。 缓冲区溢出不是抽象问题,它们是真实存在的,通常允许攻击者执行任意代码。 合理的限制可能足以大大减少漏洞利用所造成的损害。

以下是一些具体示例:

首先,将RLIMIT_NPROC设置为零意味着该进程无法派生/执行新进程-攻击者无法以当前用户身份执行任意代码。 (注意:手册页建议这可能会限制用户的进程总数,而不仅限于此进程及其子进程。应仔细检查。)这还可以防止在进程被反复分叉直到获得期望的PID。 PID应该是唯一的,但是显然,某些内核现在比传统的pid_t支持更大的PID空间。 这意味着遗留系统调用可能是模棱两可的。

其次,将RLIMIT_ASRLIMIT_DATARLIMIT_MEMLOCK设置为合理的值可防止进程通过限制可用内存来迫使系统崩溃

第三,将RLIMIT_CORE设置为合理的值(或完全禁用核心转储)在过去一直被用来通过在磁盘上填充核心转储来防止拒绝服务攻击。 如今,通常会禁用核心转储,以确保不会将诸如加密密钥之类的敏感信息无意间写入磁盘,从而使攻击者以后可以检索它们。 还应该对敏感信息进行memlock()锁定,以防止将其写入交换磁盘。

Java呢?

这会影响Java吗?

是。

标准的类加载器为每个加载的类维护一个打开的“文件句柄”。 这可以是应用程序服务器的数千个打开文件句柄。 我已经看到现实世界中的失败最终被追查到达到RLIMIT_NOFILE限制。

有三种解决方案。 第一个是通过limits.conf文件增加每个人允许的打开文件数。 这是不希望的–我们希望应用程序和用户拥有足够的资源来完成其工作,但不要更多。

第二个是增加仅用于开发人员和应用程序服务器的允许打开文件的数量。 这比第一种方法要好,但仍会使恶意程序造成很多损害。

第三是编写一个简单的启动器应用程序,该应用程序在执行exec()来启动应用程序服务器或开发人员的IDE之前设置了更高的限制。 这样可以确保只有授权的应用程序才能获得其他资源。

(Java的SecurityManager也可以用于限制资源使用,但这超出了本讨论的范围。)

样例代码

最后,从prlimit手册页中获取一些示例代码。 setrlimit版本与此类似。

#define _GNU_SOURCE
#define _FILE_OFFSET_BITS 64
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/resource.h>

#define errExit(msg)	do { perror(msg);  exit(EXIT_FAILURE); } while (0)

int
main(int argc, char *argv[])
{
    struct rlimit old, new;
    struct rlimit *newp;
    pid_t pid;

    if (!(argc == 2 || argc == 4)) {
        fprintf(stderr, "Usage: %s  [<new-soft-limit> <new-hard-limit>]\n", argv[0]);
        exit(EXIT_FAILURE);
     }

     pid = atoi(argv[1]);        /* PID of target process */

     newp = NULL;
     if (argc == 4) {
         new.rlim_cur = atoi(argv[2]);
         new.rlim_max = atoi(argv[3]);
         newp = ≠w;
     }

     /* Set CPU time limit of target process; retrieve and display previous limit */
     if (prlimit(pid, RLIMIT_CPU, newp, &old) == -1)
         errExit("prlimit-1");
         printf("Previous limits: soft=%lld; hard=%lld\n",
             (long long) old.rlim_cur, (long long) old.rlim_max);

    /* Retrieve and display new CPU time limit */
    if (prlimit(pid, RLIMIT_CPU, NULL, &old) == -1)
           errExit("prlimit-2");
           printf("New limits: soft=%lld; hard=%lld\n",
              (long long) old.rlim_cur, (long long) old.rlim_max);

    exit(EXIT_FAILURE);
}

实际使用

在程序启动时编写一个设置限制的函数应该不难,也许是在程序初始化的最后一步,但要在读取用户提供的任何内容之前。 在许多情况下,我们可以利用现有资源的使用量,并添加足够的资源来满足支持用户请求所需的资源。 例如,可能还有两个其他文件句柄,一个用于输入,一个用于输出。

在其他情况下,很难确定好的界限,但是有三种方法。

首先是专注于关键。 例如,许多应用程序都知道它们永远不应启动子进程,因此RLIMIT_NPROC可以设置为零。 (同样,在确认这是当前进程的限制之后,不是用户的所有进程都可以使用。)他们知道,不必再打开少数几个其他文件,因此可以将RLIMIT_NOFILE设置为允许几个更多打开的文件,但没有更多。 即使这些适度的限制也可以大大减少损害。

第二个是简单地选择一些较大的值,您确定这些值足以限制内存或处理器的使用。 也许100 MB的数量级太大了-但是比以前小了一个数量级。 这种方法对于老板/工人体系结构中的子过程尤其有用,在该过程中,可以很好地估计任何单个工人所需的资源量。

最终的方法需要更多的工作,但是会给您最好的数字。 在开发过程中,您将添加一些额外的脚手架:

  • 以setuid root身份运行程序,但立即将有效用户更改为非特权用户。
  • 设置高硬限制和低软限制。
  • 检查是否在每个系统调用上都达到了软限制。 (您应该已经检查了错误。)
  • 在软限制命中时,将有效用户更改为root,提高软限制,恢复原始有效用户,然后重试该操作。
  • 每次必须突破软限制时都要对其进行记录。 变体–由外部进程轮询/ proc / [pid] / limits文件。

通过良好的功能和验收测试,您应该对程序所需的资源有扎实的了解。 您仍然希望对最终的资源限制大方,但是它可以为您所需的资源提供良好的“数量级”估计,例如10 MB与2 GB。

最后一点:磁盘配额

我们一直在讨论单个流程的资源限制,但是有时问题是随着时间的推移资源枯竭。 特别是磁盘使用情况–应用程序可能会由于填充磁盘而无意中导致拒绝服务攻击。

有一个简单的解决方案–启用磁盘配额。 我们通常认为磁盘配额是用来确保多用户系统上的用户能够很好地协作,但是它们也可以用作安全措施来约束受感染的服务器。

翻译自: https://www.javacodegeeks.com/2014/09/using-rlimit-and-why-you-should.html

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值