当前位置: 首页 > news >正文

【安全】Linux Fanotify使用入门

1 Fanotify vs Inotify

在实现某些功能时,可能需要获取某个文件执行的操作,一种可能的方案是用Audit的路径监控,但是Audit存在性能和内核稳定性问题,这个时候就可以其他的文件变更检测机制。

inotify可以监控文件被创建、修改和访问的事件,当文件或者目录发生变化时,可以得到变更的事件,但是inotify有个比较大的不足,就是无法得到执行操作的进程Pid。fanotify是另一种文件监控机制,在使用上两者类似,都是先调用init函数初始化句柄,然后调用类似watch的函数添加监控路径,再使用select+read的方式读取出变化的事件,根据事件中给出的参数获取文件的路径、事件类型以及其他需要的数据。与inotify相比,fanotify的优势在于:

  • 访问控制:inotify只能监控文件的变化,而fanotify可以在用户操作之前决定是否可以继续操作,相当于可以用于实现阻断的功能
  • 监控范围:inotify用于监控文件或者目录的变化,如果要监控目录下所有文件(包括子目录),就需要自行递归添加路径;fanotify则提供directed、per-mount和global三种监控模式,其中global模式可以用于监控整个文件系统
  • 进程信息:inotify只能得到某个文件发生了什么事件;fanotify可以得到对文件操作的进程pid,程序可以从/proc/pid读取进程信息

2 Fanotify Demo

#include <sys/fanotify.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#include <string>
#include <string.h>int main(int argc, char *argv[]) {int fan_fd, fd;if(argc < 2) {printf("usage: ./main path\n");return -1;}// 初始化fanotify的描述符,后续的操作都是通过该描述符fan_fd = fanotify_init(FAN_CLASS_NOTIF, O_RDONLY);if (fan_fd < 0) {perror("fanotify_init");return -1;}// 增加监控路径if (fanotify_mark(fan_fd, FAN_MARK_ADD, FAN_ACCESS|FAN_MODIFY|FAN_EVENT_ON_CHILD, AT_FDCWD, argv[1]) < 0) {perror("fanotify_mark");return -1;}fd_set rfds;while (1) {// 使用select+read读取变更的事件FD_ZERO(&rfds);FD_SET(fan_fd, &rfds);struct timeval timeo = {.tv_sec = 0, .tv_usec = 500000};char buf[4096] = {0};int ret = select(fan_fd + 1, &rfds, NULL, NULL, &timeo);if (ret < 0) {close(fan_fd);fan_fd = -1;break;} else if (ret == 0) {continue;}struct fanotify_event_metadata* metadata =reinterpret_cast<struct fanotify_event_metadata*>(buf);ssize_t len = read(fan_fd, buf, sizeof(buf));if (len == -1 && errno == EAGAIN) {continue;}if (len == -1) {perror("read");exit(EXIT_FAILURE);}// 遍历返回的变更事件for (; FAN_EVENT_OK(metadata, len);metadata = FAN_EVENT_NEXT(metadata, len)) {printf("fd=%d pid=%d ", metadata->fd, metadata->pid);// 根据返回的事件中的mask判断事件类型if (metadata->mask & FAN_ACCESS) {printf("event=read ");} else if (metadata->mask & FAN_MODIFY) {printf("event=write ");} else {printf("event=other ");}// 根据返回事件中的fd读取操作的文件路径char path[1024] = {0};char flink[1024] = {0};sprintf(flink, "/proc/self/fd/%d", metadata->fd);if (readlink(flink, path, sizeof(path)) < 0) {printf("readlink %s failed: %s\n", flink, strerror(errno));close(metadata->fd);printf("\n");continue;}printf("path=%s\n", path);// 此处不能少,由于该fd是在当前进程中,需要在处理完该事件后关闭close(metadata->fd);}}close(fan_fd);return 0;
}

3 Fanotify API

通过上述示例代码发现,使用fanotify监控路径的基本套路是:

  • 调用fanotify_init初始化文件描述符
  • 调用fanotify_mark对路径进行监控
  • 使用select+read读取变更事件
  • 使用FAN_EVENT_OK和FAN_EVENT_NEXT宏遍历变更事件
  • 读取到变更事件后,根据事件中的mask得到事件类型,通过事件中的pid得到进程信息,通过事件中的fd得到文件路径,处理完成后关闭fd
  • 关闭fanotify的文件描述符

因此,使用过程中重点是需要了解fanotify_init和fanotify_mark两个API的参数和含义。

3.1 fanotify_init
#include <fcntl.h>
#include <sys/fanotify.h>int fanotify_init(unsigned int flags, unsigned int event_f_flags);

fanotify_init函数用于初始化文件描述符,flags参数可以使用两类标志,一类是通知类型(notification classes),另一类是对fanotify的文件描述符的一些属性设置。

通知类型有三类:

  • FAN_CLASS_PRE_CONTENT:当文件被访问和文件内容被修改之前收到事件,适用于在文件被修改之前需要执行操作的场景
  • FAN_CLASS_CONTENT:当文件被访问和文件内容被修改之后收到事件,适用于在文件被修改之后需要执行操作的场景
  • FAN_CLASS_NOTIF:默认值,当文件被访问时会收到事件,不能进行访问控制

如果程序只需要获取事件,而不需要进行访问控制,可以用默认值,否则就需要根据使用场景进行选择。另外,根据含义,三类事件也是按照从上到下的顺序接收的。

设置fanotify的文件描述符的属性的标志:

  • FAN_CLOEXEC:设置fanotify的文件描述符的close-on-exec属性,类似open中的O_CLOEXEC
  • FAN_NONBLOCK:将文件描述符设置为非阻塞
  • FAN_UNLIMITED_QUEUE:移除事件队列的长度限制,默认限制是16384个事件,该标志位需要CAP_SYS_ADMIN权限
  • FAN_UNLIMITED_MARKS:移除监控点数量的限制,默认限制是8192,该标志位需要CAP_SYS_ADMIN权限

除了上述标志位,后续的内核还提供其他的标志位,可以在事件上上报更多数据:

  • FAN_REPORT_TID:kernel>=4.20,事件包含线程ID
  • FAN_REPORT_FID:kernel>=5.1,事件文件系统信息,通知类型必须是FAN_CLASS_NOTIF
  • FAN_REPORT_DIR_FID:kernel>=5.9,事件包含目录信息
  • FAN_REPORT_NAME:kernel>=5.9,事件包含目录名,该标志必须和FAN_REPORT_DIR_FID一起使用
  • FAN_REPORT_DFID_NAME:FAN_REPORT_DIR_FID|FAN_REPORT_NAME

flags可以设置通知类型中的一种和属性设置中的若干标志。

event_f_flags用于设置收到的事件中的文件描述符的属性,常用的标志位有:

  • O_RDONLY:只读
  • O_LARGEFILE:对超过2GB文件的支持
  • O_CLOEXEC:设置close-on-exec属性

当然,也可以设置其他标志位:O_WRONLY、O_RDWR、O_APPEND、O_DSYNC、O_NOATIME、O_NONBLOCK、O_SYNC。

3.2 fanotify_mark
#include <sys/fanotify.h>int fanotify_mark(int fanotify_fd, unsigned int flags,uint64_t mask, int dirfd, const char *pathname);

fanotify_mark函数用于操作监控路径,可以增加监控路径,也可以删除监控路径。

fanotify_mark有5个参数:

  • fanotify_fd:fanotify_init返回的fanotify的描述符
  • flags:对监控点的操作
  • mask:要监控的操作
  • dirfd:监控路径的fd
  • pathname:监控路径

flags是表明需要对监控点进行何种操作,是增还是减:

  • FAN_MARK_ADD:增加监控点
  • FAN_MARK_REMOVE:移除监控点
  • FAN_MARK_FLUSH:删除所有监控点

flags可以从上述三个操作中选择一个,然后再结合以下标志位设置:

  • FAN_MARK_DONT_FOLLOW:如果pathname是软链接,只监控软链接本身,而不是软链接指向的文件
  • FAN_MARK_ONLYDIR:只监控目录,如果监控的不是目录,则报错
  • FAN_MARK_MOUNT:监控pathname指定的挂载点路径,如果pathname不是挂载点,则监控pathname所在的挂载点,挂载点的所有目录和文件都会监控

除了上述标志位,kernel>=4.20还提供FAN_MARK_FILESYSTEM,表示监控pathname所在的整个文件系统。

mask表明要监控的事件类型:

  • FAN_ACCESS:读文件或者目录
  • FAN_MODIFY:写文件
  • FAN_CLOSE_WRITE:写关闭
  • FAN_CLOSE_NOWRITE:读关闭
  • FAN_OPEN:打开文件或者目录
  • FAN_OPEN_PERM:在请求打开文件或者目录时,创建fanotify文件描述符时需要使用FAN_CLASS_PRE_CONTENT或者FAN_CLASS_CONTENT
  • FAN_ACCESS_PERM:在请求读取文件或者目录时,创建fanotify文件描述符时需要使用FAN_CLASS_PRE_CONTENT或者FAN_CLASS_CONTENT
  • FAN_ONDIR:目录操作的事件,如果没有该标志位,只有操作文件的事件
  • FAN_EVENT_ON_CHILD:为监控目录的一级路径添加事件
  • FAN_CLOSE:FAN_CLOSE_WRITE|FAN_CLOSE_NOWRITE,关闭文件

以上是从内核版本2.6.37开始支持的事件类型,也有更高版本支持的事件类型:

  • FAN_OPEN_EXEC:kernel>=5.0,执行可以执行文件
  • FAN_ATTRIB:kernel>=5.1,属性变更
  • FAN_CREATE:kernel>=5.1,在监控目录中创建文件或者目录
  • FAN_DELETE:kernel>=5.1,在监控目录中删除文件或者目录
  • FAN_DELETE_SELF:kernel>=5.1,删除监控路径
  • FAN_MOVED_FROM:kernel>=5.1,从监控目录中移走
  • FAN_MOVED_TO:kernel>=5.1,移入监控目录
  • FAN_MOVE_SELF:kernel>=5.1,移动监控文件或者目录本身
  • FAN_OPEN_EXEC_PERM:kernel>=5.0,在请求执行可执行文件时,创建fanotify文件描述符时需要使用FAN_CLASS_PRE_CONTENT或者FAN_CLASS_CONTENT
  • FAN_MOVE:FAN_MOVED_FROM|FAN_MOVED_TO

监控路径则依赖dirfd和pathname两个参数:

  • 如果pathname为NULL,则监控dirfd指定的文件系统
  • 如果pathname为NULL,dirfd设置为AT_FDCWD,则监控当前工作目录
  • 如果pathname为绝对路径,则监控该路径,此时忽略dirfd参数
  • 如果pathname为相对路径,dirfd不是AT_FDCWD,则监控dirfd目录下的pathname
  • 如果pathname为相对路径,dirfd是AT_FDCWD,则监控当前工作目录下的pathname
3.3 三种监控模式

前面说过,fanotify支持三种监控模式:

  • directed:只监控需要监控的文件或者目录,如果是目录,可以添加FAN_EVENT_ON_CHILD标志监控目录下的所有文件,但是不会监控子目录下的文件,per-mount和global模式下,FAN_EVENT_ON_CHILD标志无效
  • per-mount:监控挂载点中的所有文件或者目录,增加监控路径时设置FAN_MARK_MOUNT标志
  • global:监控整个文件系统,增加监控路径时设置FAN_MARK_FILESYSTEM标志,不过该选项是在4.20内核版本才支持

4 容器场景使用的问题

从上述可以看出,fanotify在5.1内核增加了很多能力,因此,在使用时需要考虑内核的版本问题,另外,在容器场景也存在一些问题。

如果是监控主机上的路径,directed结合FAN_EVENT_ON_CHILD标志就只能监控目录以及目录下的文件,per-mount可以监控pathname所在的挂载点中所有文件或者目录的变化,而global模式可以监控整个文件系统。

从功能实现角度来看,如果需要监控某个路径下的文件变化,希望可以只设置该路径就行,也就是,当监控/etc目录时,也可以监控到/etc/xxx/目录下的文件变化。

如果是监控容器中的路径,如果使用directed和global模式,就跟主机上的一样,监控merged路径;如果使用per-mount,由于不能跨命名空间,需要使用setns进入到容器所在的命名空间,再添加监控路径,实现起来会麻烦很多。

在处理文件的变更事件时,通过metadata->fd读取到的文件路径是容器中的路径,metadata->pid是宿主机上的进程Pid,通过/proc/pid/cgroup可以得到容器ID,虽然可以根据容器ID调用docker或者CRI的接口获取merged路径,但是也比较麻烦。

5 参考文档

  • 监听容器中的文件系统事件

相关文章:

【安全】Linux Fanotify使用入门

1 Fanotify vs Inotify 在实现某些功能时&#xff0c;可能需要获取某个文件执行的操作&#xff0c;一种可能的方案是用Audit的路径监控&#xff0c;但是Audit存在性能和内核稳定性问题&#xff0c;这个时候就可以其他的文件变更检测机制。 inotify可以监控文件被创建、修改和…...

java的输出流File OutputStream

一、字节输出流FileOutput Stream 1、定义 使用OutputStream类的FileOutput Stream子类向文本文件写入的数据。 2.常用构造方法 3.创建文件输出流对象的常用方式 二、输出流FileOutputStream类的应用示例 1.示例 2、实现步骤 今天的总结就到此结束啦&#xff0c;拜拜&#x…...

32 - 判断三角形(高频 SQL 50 题基础版)

32 - 判断三角形 select *,if(xy>z and xz>y and zy > x,Yes,No) triangle fromTriangle;...

QT 中ListView和ListWidget有什么区别

ListView和ListWidget在Qt框架中都是用于显示列表数据的控件&#xff0c;但它们在使用方法和特性上存在一些明显的差异。以下是关于它们用法不一样的地方的详细分析&#xff1a; 数据管理方式&#xff1a; ListView&#xff1a;使用QAbstractItemModel数据模型来管理和显示列表…...

Python酷库之旅-第三方库openpyxl(07)

目录 一、 openpyxl库的由来 1、背景 2、起源 3、发展 4、特点 4-1、支持.xlsx格式 4-2、读写Excel文件 4-3、操作单元格 4-4、创建和修改工作表 4-5、样式设置 4-6、图表和公式 4-7、支持数字和日期格式 二、openpyxl库的优缺点 1、优点 1-1、支持现代Excel格式…...

使用Python进行Web开发:从基础到实战

使用Python进行Web开发:从基础到实战 Python不仅是一门数据科学的利器,也是一门强大的Web开发语言。得益于其简洁的语法和丰富的生态系统,Python在Web开发领域同样大放异彩。本文将介绍Python在Web开发中的基础知识、常用框架以及一个完整的实战项目,帮助读者从基础入门到…...

打包体积分析和优化

webpack分析工具&#xff1a;webpack-bundle-analyzer 1. 通过<script src"./vue.js"></script>方式引入vue、vuex、vue-router等包&#xff08;CDN&#xff09; // webpack.config.js if(process.env.NODE_ENVproduction) {module.exports {devtool:…...

numpy的array/asarray/asanyarray的格式转化错误问题解决

关于numpy的array()、asarray()、asanyarray() 当前numpy版本&#xff1a;1.26.3 有时一些依赖numpy的旧项目&#xff0c;在运行时&#xff0c;会出现如下错误 ValueError: setting an array element with a sequence. The requested array has an inhomogeneous shape after 1…...

C++:STL容器-map

C:STL容器-map 1. map构造和赋值2. map大小和交换3. map插入和删除4. map查找和统计5. map容器排序 map中所有元素都是pair&#xff08;对组&#xff09; pair中第一个元素为key&#xff08;键&#xff09;&#xff0c;起到索引作用&#xff0c;第二个元素为value&#xff08;实…...

你好,复变函数2.0

第一行&#xff1a;0 或 1 第二行&#xff1a;&#xff08;空格&#xff09;函数&#xff08;后缀&#xff09; #pragma warning(disable:4996) #include <easyx.h> #include <stdio.h> #include <math.h> #define PI 3.141592653589793 #define E 2.71828…...

汉语拼音字母表 (声母表和韵母表)

汉语拼音字母表 [声母表和韵母表] 1. 汉语拼音声母表2. 汉语拼音韵母表References 1. 汉语拼音声母表 声母是韵母前的辅音&#xff0c;与韵母一起构成一个完整的音节。 辅音是发声时&#xff0c;气流在口腔中受到各种阻碍所产生的声音&#xff0c;发音的过程即是气流受阻和克…...

C++20中的Feature Test Mocros

C20定义了一组预处理器宏&#xff0c;用于测试各种语言和库的feature。 Feature Test Mocros(特性测试宏)是C20中引入的一种强大机制&#xff0c;用于应对兼容性问题。Feature Test Mocros作为预处理器指令(preprocessor directives)出现&#xff0c;它使你能够在编译过程中仔细…...

运维iptables与firewalld详解

iptables与firewalld 一、iptables 1.1 iptables简介 iptables 是一个在 Linux 系统上用来配置 IPv4 数据包过滤规则的工具。它允许系统管理员控制数据包的流向&#xff0c;实现网络安全、网络地址转换&#xff08;NAT&#xff09;和端口转发等功能。 具体来说&#xff0c;…...

适用于 Android 的 几种短信恢复应用程序

Android 设备上的短信丢失可能由于多种原因而丢失&#xff0c;例如意外删除、恢复出厂设置、系统崩溃或病毒攻击。是否有应用程序可以恢复 Android 上已删除的短信&#xff1f;幸运的是&#xff0c;有几款短信恢复应用程序可以扫描您的 Android 手机并从内存或 SIM 卡中检索已删…...

Lodash-js工具库

1. Lodash 简介 Lodash 是一个现代 实用工具库&#xff0c;提供了许多有用的函数&#xff0c;帮助开发者处理常见的编程任务&#xff0c;如数组操作、对象处理、字符串处理等。Lodash 使得代码更简洁、更高效&#xff0c;极大地提高了开发效率。Lodash 的设计灵感来自于 Under…...

Makefile实战论(一)

为什么写这个呢&#xff0c;其实我有系统学过Makefile和CMake。但是因为用的不是很多或者说没有深入的使用场景&#xff0c;导致我不是很熟练&#xff0c;或者说没法优雅地使用。刚好最近对Linux的嵌入式编程比较感兴趣&#xff0c;借着demo来分析一下资深工程师写的Makefile&a…...

Hi3861 OpenHarmony嵌入式应用入门--PWM 三色灯

这篇文章是讲解的pwm控制三色灯的部分&#xff0c;这部分也是后续全彩智能灯的基础。 硬件原理如下 IO管脚定义在hi-12f_v1.1.2-规格书-20211202.pdf文档中 GPIO API API名称 说明 unsigned int IoTGpioInit(unsigned int id); GPIO模块初始化 hi_u32 hi_io_set_func(hi_i…...

CH5xx USB下载工具

文章目录 CH5xx USB下载工具1.前言2.介绍3. USB下载4. 串口免按键下载4.SWD下载 CH5xx USB下载工具 1.前言 CH5xx USB下载工具是一款专为沁恒 CH5xx系列 BLE SOC设计的程序的下载工具。这款工具与串口下载相比较&#xff0c;不仅提供了稳定的数据传输能力&#xff0c;而且提高…...

问题1.用PGP解密出keybox.xml,过程中报“Can‘t check signature: No public key”如图,这个正常吗?如何解决?

问题1.我要写Google attenstation key到设备。就需要keybox.xml生成keybox.kdb文件。而测试机构给我们的是加密的文件&#xff0c;需要用PGP解密出keybox.xml&#xff0c;过程中报“Can’t check signature: No public key”如图&#xff0c;这个正常吗&#xff1f;如何解决&am…...

网络物理隔离后 可以用保密U盘进行数据安全交换吗?

企业用的保密U盘通常被设计用于存储和传输敏感信息&#xff0c;以确保数据的安全和保密性。 在网络之间实现了物理隔离后&#xff0c;使用保密U盘进行数据安全交换是一种常见的做法。物理隔离确保了两个网络之间的完全分离&#xff0c;因此使用保密U盘可以作为一种安全的手段来…...

后进先出(LIFO)详解

LIFO 是 Last In, First Out 的缩写&#xff0c;中文译为后进先出。这是一种数据结构的工作原则&#xff0c;类似于一摞盘子或一叠书本&#xff1a; 最后放进去的元素最先出来 -想象往筒状容器里放盘子&#xff1a; &#xff08;1&#xff09;你放进的最后一个盘子&#xff08…...

Redis相关知识总结(缓存雪崩,缓存穿透,缓存击穿,Redis实现分布式锁,如何保持数据库和缓存一致)

文章目录 1.什么是Redis&#xff1f;2.为什么要使用redis作为mysql的缓存&#xff1f;3.什么是缓存雪崩、缓存穿透、缓存击穿&#xff1f;3.1缓存雪崩3.1.1 大量缓存同时过期3.1.2 Redis宕机 3.2 缓存击穿3.3 缓存穿透3.4 总结 4. 数据库和缓存如何保持一致性5. Redis实现分布式…...

Java-41 深入浅出 Spring - 声明式事务的支持 事务配置 XML模式 XML+注解模式

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; &#x1f680; AI篇持续更新中&#xff01;&#xff08;长期更新&#xff09; 目前2025年06月05日更新到&#xff1a; AI炼丹日志-28 - Aud…...

ServerTrust 并非唯一

NSURLAuthenticationMethodServerTrust 只是 authenticationMethod 的冰山一角 要理解 NSURLAuthenticationMethodServerTrust, 首先要明白它只是 authenticationMethod 的选项之一, 并非唯一 1 先厘清概念 点说明authenticationMethodURLAuthenticationChallenge.protectionS…...

佰力博科技与您探讨热释电测量的几种方法

热释电的测量主要涉及热释电系数的测定&#xff0c;这是表征热释电材料性能的重要参数。热释电系数的测量方法主要包括静态法、动态法和积分电荷法。其中&#xff0c;积分电荷法最为常用&#xff0c;其原理是通过测量在电容器上积累的热释电电荷&#xff0c;从而确定热释电系数…...

Leetcode33( 搜索旋转排序数组)

题目表述 整数数组 nums 按升序排列&#xff0c;数组中的值 互不相同 。 在传递给函数之前&#xff0c;nums 在预先未知的某个下标 k&#xff08;0 < k < nums.length&#xff09;上进行了 旋转&#xff0c;使数组变为 [nums[k], nums[k1], …, nums[n-1], nums[0], nu…...

消防一体化安全管控平台:构建消防“一张图”和APP统一管理

在城市的某个角落&#xff0c;一场突如其来的火灾打破了平静。熊熊烈火迅速蔓延&#xff0c;滚滚浓烟弥漫开来&#xff0c;周围群众的生命财产安全受到严重威胁。就在这千钧一发之际&#xff0c;消防救援队伍迅速行动&#xff0c;而豪越科技消防一体化安全管控平台构建的消防“…...

实战设计模式之模板方法模式

概述 模板方法模式定义了一个操作中的算法骨架&#xff0c;并将某些步骤延迟到子类中实现。模板方法使得子类可以在不改变算法结构的前提下&#xff0c;重新定义算法中的某些步骤。简单来说&#xff0c;就是在一个方法中定义了要执行的步骤顺序或算法框架&#xff0c;但允许子类…...

在golang中如何将已安装的依赖降级处理,比如:将 go-ansible/v2@v2.2.0 更换为 go-ansible/@v1.1.7

在 Go 项目中降级 go-ansible 从 v2.2.0 到 v1.1.7 具体步骤&#xff1a; 第一步&#xff1a; 修改 go.mod 文件 // 原 v2 版本声明 require github.com/apenella/go-ansible/v2 v2.2.0 替换为&#xff1a; // 改为 v…...

Linux安全加固:从攻防视角构建系统免疫

Linux安全加固:从攻防视角构建系统免疫 构建坚不可摧的数字堡垒 引言:攻防对抗的新纪元 在日益复杂的网络威胁环境中,Linux系统安全已从被动防御转向主动免疫。2023年全球网络安全报告显示,高级持续性威胁(APT)攻击同比增长65%,平均入侵停留时间缩短至48小时。本章将从…...