首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >【容器安全系列Ⅳ】- 深入理解Linux Cgroup

【容器安全系列Ⅳ】- 深入理解Linux Cgroup

原创
作者头像
星尘安全
发布于 2024-08-25 02:17:04
发布于 2024-08-25 02:17:04
4620
举报
文章被收录于专栏:linuxlinux
图片
图片

     当主机上运行多个进程时,管理系统资源可能是一个挑战。单个行为异常的程序可能会消耗所有可用资源,从而导致整个系统崩溃。为了解决这个问题,Linux 依靠控制组 (cgroups) 来管理每个进程对资源(如 CPU 和内存)的访问。

    Docker 和其他容器化工具使用 cgroups 来限制容器可以使用的资源,这有助于避免相互干扰的问题。这在使用 Kubernetes 时特别有用,因为来自多个应用程序的工作负载经常在同一主机上共享资源。

     在这篇文章中,我们将仔细研究 cgroups,并探讨它们如何确保每个进程都能访问高效运行所需的资源。我们还将介绍 cgroups 的几个安全方面,包括如何使用 cgroups 来降低拒绝服务攻击的风险,并管理容器对主机上特定设备的访问。

cgroups v1 和 v2

     值得注意的是,根据Linux发行版和版本,在给定的主机上可能会使用两个版本的cgroups。与原始实现相比,Cgroup v2 提供了管理优势,并且是某些容器功能(如非root容器)所必需的。

    Cgroup v2 最初是在2016年 4.5 版本的 Linux 内核中引入的,但直到最近才成为某些发行版的默认版本。要确定主机上运行的版本,可以验证挂载的文件系统。例如,如果您在 Ubuntu 20.04 主机上执行该命令 mount | grep cgroup ,您将看到一行展示“cgroup2”,另几行展示“cgroup”,这表示两个系统都已安装。

cgroup 挂载列表 - Ubuntu 20.04
cgroup 挂载列表 - Ubuntu 20.04

     但是,如果您在 Ubuntu 22.04 系统上运行相同的命令,则只会看到 cgroup v2。

cgroup 挂载列表 - Ubuntu 22.04
cgroup 挂载列表 - Ubuntu 22.04

     由于 cgroup v2 是最近 Linux 发行版中使用的版本,因此我们将在示例的其余部分重点介绍 v2。

Cgroups 基础知识

     有几种方法可以检查 Linux 主机上使用的 cgroup。一种选择是使用 /proc 文件系统来查看用于特定进程的 cgroup(例如,正在运行的用户的 bash shell)。

     执行命令 cat /proc/[PID]/cgroup 将显示进程所属的 cgroup “slice” 和 “scope”(slice 和 scopes 用于组织 cgroup 和进程)。在以下示例中,我们首先用 ps -fC bash 获取 shell 的进程 ID。然后,我们使用该进程 ID 来发现它使用的 cgroup 会话。

查找 bash shell 的 cgroup 作用域和切片
查找 bash shell 的 cgroup 作用域和切片

     要查看可以为该进程修改的可用资源,您可以查看 /sys/ 文件系统,它对应于我们从上一个命令中获得的信息(例如:sys/fs/cgroup/user.slice/user-1000.slice/session-4.scope)

显示给定 cgroup 范围的资源
显示给定 cgroup 范围的资源

     此手动过程可能非常耗时,因此您可以利用更便捷的工具,以更有条理的方式显示 cgroup 信息。例如,systemd-cgls 可以显示主机上不同 cgroup 作用域的分层视图。

使用 systemd-cgls 获取 cgroup 信息的分层视图
使用 systemd-cgls 获取 cgroup 信息的分层视图

     此外,cgroup-utils 软件包中的 lscgroup 实用程序可用于检查 cgroup 信息。

使用 lscgroup 显示 cgroup 信息
使用 lscgroup 显示 cgroup 信息

使用 cgroups 限制资源

     现在我们已经了解了如何查看 cgroup 信息,下一步是探索如何使用 cgroups 来限制进程可用的资源,这有助于缓解拒绝服务风险。为了证明这一点,我们将使用 stress 工具来模拟攻击者或行为不端的应用程序消耗我们主机上的所有 CPU。

     在 Docker 容器中,我们可以利用命令 stress -c 2  ,它将启动两个进程,总共消耗 2 个 CPU 内核。然后,通过在另一个窗口中执行命令 top ,我们可以验证对主机 CPU 的影响。

使用压力工具消耗 CPU 资源
使用压力工具消耗 CPU 资源

     Docker 提供了各种选项来限制容器可以使用的 CPU 时间量,但最简单的是--cpus标志,它允许您指定可以使用的 CPU 的十进制数。在幕后,Docker 利用 cgroups 来强制执行此限制。

     例如,执行 docker run --name stress --cpus 0.5 -it stressimage /bin/bash 会将容器限制为 0.5 个 CPU。使用上一个容器中的相同 stress 命令后,我们可以在 top 中观察到此限制的结果。 

CPU 资源使用率受 cgroups 限制
CPU 资源使用率受 cgroups 限制

     现在,stress 进程不再能够利用两个 CPU 内核,而是限制为 0.5 个 CPU(每个进程占内核的 25%)。

     我们还可以通过检查底层文件系统来观察 Docker 实现的 cgroup 限制的细节。为此,我们首先使用 docker inspect -f '{{.State.Pid}}' stress 获取 Docker 容器的进程 ID。然后我们可以查找此过程的 cgroup 信息。容器的 cgroup 目录包含一个 cpu.max 文件,其值为 50000 100000 ,相当于 0.5 个 CPU。默认情况下,Docker 不限制进程的 CPU 使用率,因此文件将显示值 max 100000 。如果攻击者有权访问此容器,则可以使用主机上的所有 CPU 资源(例如,挖掘加密货币)。

使用 cgroups 限制fork炸弹

     Linux 系统上常见的拒绝服务攻击称为frok炸弹,当攻击者生成大量进程,最终耗尽系统资源时,就会发生这种攻击。默认情况下,容器(和其他 Linux 进程)在它们可以生成多少个新进程方面不受限制,这意味着任何进程都可以创建fork炸弹。

    Cgroup 能够限制可以生成的进程数量,从而有效地保护主机免受fork炸弹攻击。我们可以使用 docker run 的--pids-limit 参数来演示这一点,这实质上将设置适当的 cgroup。

     要了解其工作原理,我们可以使用 docker run -it --pids-limit 10 ubuntu:22.04 /bin/bash 命令启动容器,这会将容器限制为最多 10 个进程。然后我们可以使用命令执行 bash fork 炸弹 :(){ :|: & };: 

Docker PID 限制在fork炸弹中生效
Docker PID 限制在fork炸弹中生效

     很快,容器达到 10 个进程的限制,并显示错误。但是,底层主机将保持响应,从而防止拒绝服务攻击。

使用 cgroups 控制设备访问

    cgroups 的另一个与安全相关的方面是,它们可用于控制对设备的访问。容器提供对主机上一系列设备的访问,详见 runc 的允许设备列表,并且可以利用 Docker 的功能(使用 cgroups)将其他设备添加到该列表中。这允许您向特定容器授予对硬件(例如音频设备)的访问权限。

     您可以将 --device 选项添加到命令 docker run 中以授予对设备的访问权限。例如,执行 docker run -d --rm --device /dev/dm-0 --name webdevice nginx 会生成一个有权访问/dev/dm-0设备的容器。

     与 CPU 或内存等其他资源相比,Linux 工具没有提供那么多的功能来检查 cgroup 对设备的访问。在 cgroup v2 中,一般使用 eBPF 程序用于管理对设备的访问,因此标准工具将不起作用。所以, bpftool 是必需的。您可以使用此程序列出与任何给定 cgroup 关联的 eBPF 程序,从而提供容器对主机访问的一些可见性,尽管不是很详细。

结论

     控制共享资源是确保多个容器可以有效地共享单个服务器的关键。在这篇文章中,我们介绍了 cgroup,这是 Linux 系统用于实现此控制的主要机制。我们还演示了如何利用 cgroup 来帮助缓解常见的拒绝服务攻击,并管理对连接到主机的特定设备的访问。

     到目前为止,我们检查的所有安全机制都在系统上的 root 用户的控制之下。但是,有时我们想要限制 root 用户的操作,那又该怎么办呢?在下一篇文章中,我们将探讨 SELinux 和 AppArmor 等强制访问控制(MAC)系统如何实现这一目标。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
Python文件处理入门篇
昨天分享了一个关于文件搜索的小实战项目,其实文件处理是Python里面非常重要的一块内容,知识点很多,Python对本地文件的处理,主要是通过文件的读和写来完成的。
stormwen
2019/08/05
5000
Python 基础(十二):文件基本操作
在编程工作中文件操作还是比较常见的,基本文件操作包括:创建、读、写、关闭等,Python 中内置了一些文件操作函数,我们使用 Python 操作文件还是很方便的。
Python小二
2020/08/18
5460
Python 基础(十二):文件基本操作
【Python】测试造数--文件 I/O操作
Python 提供了必要的函数和方法进行默认情况下的文件基本操作,可以使用 file 对象做大部分的文件操作。
用户9913368
2022/08/13
6760
【Python】测试造数--文件 I/O操作
小朋友学Python(17):文件
Python 提供了必要的函数和方法进行默认情况下的文件基本操作。你可以用 file 对象做大部分的文件操作。 一、打开和关闭文件 例1 (1)创建名为test1.txt的文件,内容为 This is a test file. (2)在同一目录下新建file1.py文件,内容如下 file = open("test1.txt", "wb") print "File name: ", file.name print "Access mode: ", file.mode print "Closed or not
海天一树
2018/04/17
9130
python学习笔记(9)文件 I/O
Python提供了两个内置函数从标准输入读入一行文本,默认的标准输入是键盘。如下:
大数据小禅
2021/08/16
4250
python学习笔记(9)文件 I/O
【Python】Python读写文件操作
http://blog.csdn.net/adupt/article/details/4435615
py3study
2020/01/14
7860
Python file 方法
file object = open(file_name [, access_mode][, buffering])
py3study
2020/01/07
8260
Python 4 种不同的存取文件骚操作
前言:最近开始学习tensorflow框架,选修课让任选一种框架实现mnist手写数字的识别分类。小詹也就随着大流选择了 tf 框架,跟着教程边学边做,小詹用了不同的神经网络实现了识别分类,其中有一个步骤是将训练过程得到的模型进行保存,在之后的测试中加载并使用该模型。想到这种先保存再加载调用的过程,之前很多地方都遇到过呀,最简单常用的就是python中文件的存取哇!于是乎,小詹夜观星象,就着手整理记录各种文件存取的骚操作,具体如下。
小小詹同学
2018/07/24
1.5K0
Python 4 种不同的存取文件骚操作
python教程(八)·文件操作
由于离高考越来越近,博主打算本篇文章过后,暂停本系列教程的更新,等到高考完后再继续本系列教程,请谅解!
py3study
2020/01/16
5450
Python3文件操作
产生输出的最简单方法是使用print语句,可以通过用逗号分隔零个或多个表达式。这个函数传递表达式转换为一个字符串,如下结果写到标准输出 -
py3study
2020/01/09
8090
python读写文件
本文系海特网编程技术斑竹Cute所发表,版权归海特网与Cute所有,转载请保留完整信息
py3study
2020/01/10
1.8K0
【十】python基础之文件处理
给要打开的文件对象指定一个名字,这样可在完成操作之后迅速关闭文件,防止一些无用的文件对象占用内存
菲宇
2019/06/13
7490
【十】python基础之文件处理
关于python文件读写小结
如果文件不存在,open()函数就会抛出一个IOError的错误,并且给出错误码和详细的信息告诉你文件不存在:
Lansonli
2021/10/09
4270
python文件读写(open参数,文件
文件读写调用open函数打开一个文件描述符(描述符的个数在操作系统是定义好的) python3情况下读写文件:
py3study
2020/01/09
1.6K0
Python3 读写文件
如果文件不存在,open()函数就会抛出一个IOError的错误,并且给出错误码和详细的信息告诉你文件不存在:
py3study
2020/01/19
4.6K0
超好懂的 Python 文件读写教程!
使用python读取一个txt文件的时候,相当于把这个文件从硬盘上,读取到了内存中。
用户6543014
2020/07/09
1.2K0
超好懂的 Python 文件读写教程!
不可不知的三种缓冲类型
为什么有时候写入文件的内容却没有?没什么printf打印在终端的内容看不到?这一切背后有着怎样早为人知的秘密?
编程珠玑
2019/12/16
6050
不可不知的三种缓冲类型
[操作系统] 缓冲区的设计与实现
缓冲区是内存中预留的临时存储区域(如char outbuffer[SIZE])。内存空间中预留了一定的存储空间,这些存储空间用来缓冲输入或输出的数据,这部分预留的空间就叫做缓冲区。缓冲区根据其对应的是输入设备还是输出设备,分为输入缓冲区和输出缓冲区。
DevKevin
2025/02/19
2890
[操作系统] 缓冲区的设计与实现
Python文件操作
Python作为一种高效且易于学习的编程语言,提供了一系列强大的文件操作功能,使得用户能够轻松地实现文件的读取、写入和管理。本章将详细讲解文件的编码以及读取、写入和追加操作。
Heaven645
2024/07/25
3361
Python文件操作
系统文件IO/文件描述符/重定向/FILE/缓冲区的理解
本文目标: 认识文件相关系统调用接口 认识文件描述符,理解重定向 对比fd和FILE,理解系统调用和库函数的关系
二肥是只大懒蓝猫
2023/03/30
9260
系统文件IO/文件描述符/重定向/FILE/缓冲区的理解
相关推荐
Python文件处理入门篇
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档