Code Monkey home page Code Monkey logo

rfanotify's Introduction

Rust fanotify example โ€ƒ Travis Badge


About

This repository is a Rust port of the Linux man-pages project's example C program for the Linux fanotify API. It offers a simple program called rfanotify that responds to and logs permission requests to open a file on a monitored filesystem or mount. It also logs when a process writes a file on a monitored filesystem/mount.

The fanotify API is a linux-specific API for notification and interception of filesystem events. In some ways it is similar to inotify, but more powerful. Unlike inotify based approaches to filesystem monitoring using fanotify has several advantages:

  • An entire filesystem/mount can be monitored with very low overhead.
  • The pid of the program causing the filesystem event is known.
  • The event handling program can deny file opens for the monitored filesystem.

For more information, see:

NOTE: This is my first attempt at writing Rust and it's likely not especially idomatic/clean. Kind PRs/suggestions welcome.

Building

Since this project is written in Rust, you'll need to grab a Rust installation in order to compile it.

rfanotify was developed with Rust 1.34.0 (stable). It may work with other versions, but this isn't guaranteed.

To build rfanotify:

git clone https://github.com/cpu/rfanotify && cd rfanotify
cargo build --release

Usage

After building from source, run:

sudo ./target/release/rfanotify <directory>

If no explicit directory argument is provided the filesystem/mount of the current working directory is monitored.

You can avoid running rfanotify as root by instead giving the binary the CAP_SYS_ADMIN capability, and then running it as a normal user:

sudo setcap cap_sys_admin+eip target/release/rfanotify
./target/release/rfanotify <directory>

The rfanotify program adds a fanotify watch on the entire filesystem/mount backing <directory>. When a process tries to open a file on the monitored filesystem/mount a FAN_OPEN_PERM event is received and logged by rfanotify and a FAN_ALLOW response is returned, allowing the open to complete. When a process closes a file a FAN_CLOSE_WRITE event is received and logged.

Events are logged to stdout including the absolute path of the accessed file and the program executable performing the access. Both of these values are retrieved by fd using the Linux procfs. If the accessed file has been deleted since the time the event was generated then the filename will have "(deleted)" appended by readlink and the procfs. If the pid that generated the access has terminated since the time the event was generated then the gone pid is printed instead.

Example

Here's an example of running rfanotify on a fresh Ubuntu 19.04 VM with a Linux 5.0.0-38-generic kernel.

First rfanotify is started inside of a screen session:

sudo rfanotify

Next, a separate window is created with ctrl-a c and a file is edited with vim:

vim /tmp/test.txt

After exiting vim and switching back to the first screen window running rfanotify you should see output like:

FAN_OPEN_PERM: File /usr/lib/x86_64-linux-gnu/utempter/utempter Exe /usr/bin/screen
FAN_OPEN_PERM: File /usr/lib/x86_64-linux-gnu/ld-2.29.so Exe /usr/lib/x86_64-linux-gnu/utempter/utempter
FAN_OPEN_PERM: File /etc/ld.so.cache Exe /usr/lib/x86_64-linux-gnu/utempter/utempter
<snipped>
FAN_CLOSE_WRITE: File /tmp/.test.txt.swx Exe /usr/bin/vim.basic
FAN_CLOSE_WRITE: File /tmp/.test.txt.swp Exe /usr/bin/vim.basic
FAN_OPEN_PERM: File /tmp/.test.txt.swp Exe /usr/bin/vim.basic
FAN_OPEN_PERM: File /usr/share/vim/vim81/scripts.vim Exe /usr/bin/vim.basic
FAN_OPEN_PERM: File /usr/share/vim/vim81/ftplugin/text.vim Exe /usr/bin/vim.basic
FAN_OPEN_PERM: File /tmp/test.txt Exe /usr/bin/vim.basic
FAN_CLOSE_WRITE: File /tmp/test.txt Exe /usr/bin/vim.basic
FAN_CLOSE_WRITE: File /tmp/.test.txt.swp Exe /usr/bin/vim.basic

The full program output can be viewed here.

Filesystem vs Mount

On Linux kernel versions >= 4.2.0 rfanotify uses fanotify_mark with the FAN_MARK_FILESYSTEM flag. On older versions FAN_MARK_MOUNT is used instead. When marking a filesystem mount instead of a filesystem it is possible events will be missed.

For example, consider if the device /dev/sdb1 is mounted to /mnt/example as well as having a bind mount to /mnt/example-b (e.g. mount --bind /mnt/example /mnt/example-b).

If rfanotify /mnt/example is run on a Linux kernel version >= 4.2.0 then events will be logged regardless of which mount is used. (e.g editing /mnt/example/foo.txt or /mnt/example-b/foo.txt will both log rfanotify events). The filesystem for /mnt/example is monitored.

If rfanotify /mnt/example is run on a Linux kernel version < 4.2.0 then events will be logged only for the /mnt/example mount. (e.g editing /mnt/example/foo.txt will log rfanotify events but editing /mnt/example-b/foo.txt will not. Only the mount of /mnt/example is monitored.

Nix and Libc versions

The fanotify system calls have been available since Linux 2.6.37 but FFI bindings for Rust were not available in the nix and libc crates as of March 2020.

The required FFI bindings were added to the libc crate in April 2020 and released as part of v0.2.69. The nix wrappers are a work in progress and require using a fork of the nix crate that has rough initial fanotify support.

Updates to the fanotify API

The fanotify API has been updated several times since it was enabled in Linux 2.6.37. The rfanotify code was written using the man page content from release 4.04 of the Linux man-pages project and tested on Linux kernel version 4.4.0 and 5.0.0.

Linux 4.20 added FAN_MARK_FILESYSTEM to "enable monitoring of filesystem events on all filesystem objects regardless of the mount where event was generated". It also added FAN_REPORT_TID to get thread IDs that triggered events.

Linux 5.0 added the ability to watch for when a file is opened with the intent to execute (FAN_OPEN_EXEC).

Linux 5.1 added many of inotify's directory based events to fanotify (e.g. FAN_ATTRIB, FAN_CREATE, etc). It also added a new fanotify_init flag FAN_REPORT_FID that allows receiving an additional structure beyond the base fanotify_event_metadata structure that contains information about the filesystem object correlated with an event (e.g. a monitored directory or mount).

The forked libc and nix crates used by this project do not (yet) implement any of these updates (except for FAN_MARK_FILESYSTEM). This project does not (yet) port the fanotify_fid.c example C program included in release 5.05 of the Linux man-pages project.

See Also

Martin Pitt cleverly used the fanotify API to develop fatrace, a program for reporting system wide file access events tailored towards reducing power consumption.

rfanotify's People

Contributors

cpu avatar

Stargazers

 avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

Forkers

icodein

rfanotify's Issues

Add port of Linux 5.1 `fanotify_fid.c` example program.

Linux 5.1 added many of inotify's directory based events to fanotify (e.g. FAN_ATTRIB, FAN_CREATE, etc). It also added a new fanotify_init flag FAN_REPORT_FID that allows receiving an additional structure beyond the base fanotify_event_metadata structure that contains information about the filesystem object correlated with an event (e.g. a monitored directory or mount).

Since my forked libc and nix crates used by this project do not yet implement any of these updates that work will need to be completed first. Afterwards it will be possible to port the fanotify_fid.c example C program included in release 5.05 of the Linux man-pages project.

Switch to nonblocking I/O, read from stdin.

The original fanotify example program that serves the basis of the rfanotify port uses poll on the fanotify descriptor in nonblocking mode. It also polls stdin to allow the user to quit gracefully by entering any input to stdin.

Presently rfanotify reads from the fanotify descriptor in a blocking fashion. It also doesn't read from stdin or offer any graceful exit, requiring the user to send a signal instead.

I should switch rfanotify to use InitFlags::FAN_NONBLOCK with Fanotify::init and implement an async approach to reading events and monitoring stdin. Since I'm super new to rust I punted on non-blocking I/O in the first version.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.