工作中,有时候我们需要知道某个文件是否被修改了, 被哪个程序修改了. Linux 下可以很方便地监控某个文件被修改的记录. 根据目的不同, Linux 下有不同的监视文件或文件夹的方案.

A. Auditd

Auditd 可以很方便监控记录哪些程序和用户对指定文件(即使不存在)做的操作.

安装 auditd

  1. 一般发行版软件仓库里都会有 auditd 安装包. 如果默认没有安装,可以很方便地利用包管理器来安装

    1
    2
    3
    4
    5
    6
    ## Fedora
    $ sudo dnf install -y audit
    ## CentOS
    $ sudo yum install -y audit
    ## Debian / Ubuntu
    $ sudo apt install -y audit
  2. 启动 audit 服务

    1
    $ sudo systemctl enable --now auditd
  3. 检查 audit 服务状态

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    $ sudo systemctl status auditd
    ● auditd.service - Security Auditing Service
    Loaded: loaded (/usr/lib/systemd/system/auditd.service; enabled; vendor preset: enabled)
    Active: active (running) since Fri 2018-09-07 08:47:53 CST; 10min ago
    Docs: man:auditd(8)
    https://github.com/linux-audit/audit-documentation
    Process: 28126 ExecStartPost=/sbin/augenrules --load (code=exited, status=0/SUCCESS)
    Process: 28121 ExecStart=/sbin/auditd (code=exited, status=0/SUCCESS)
    Main PID: 28122 (auditd)
    Tasks: 5 (limit: 4915)
    Memory: 3.3M
    CGroup: /system.slice/auditd.service
    ├─28122 /sbin/auditd
    ├─28124 /sbin/audispd
    └─28127 /usr/sbin/sedispatch

这就表示服务正常.

配置 audit 监视规则

  1. 为了监视文件的访问操作, 需要在/etc/audit/rules.d/audit.rules 添加如下规则, 其中 perm 是要监控的操作, key 是在查寻时指定的关键字, path 是要监控的文件路径.

    1
    2
    $ vi /etc/audit/rules.d/audit.rules
    -a always,exit -F path=/path/to/file -F perm=warx -F key=keyword-for-filter-log
  2. 重启 audit 服务

    1
    2
    ### Note: use service instead of systemctl
    $ sudo service auditd restart
  3. 检查 audit 规则.

    1
    2
    $ sudo auditctl -l
    -a always,exit -F path=/path/to/file -F perm=warx -F key=keyword-for-filter-log

检查文件操作记录

假如我们监控 /root/test.txt 的操作, 规则是

1
-a always,exit -F path=/root/test.txt -F perm=warx -F key=test-filter

然后对其做一些操作

1
2
3
$ ls /root/test.txt
$ cat /root/test.txt
$ touch /root/test.txt

然后通过如下命令查询操作记录.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
$ ausearch -k test-filter

time->Fri Sep 7 09:23:24 2018
type=PROCTITLE msg=audit(1536283404.017:39844): proctitle=63617400746573742E747874
type=PATH msg=audit(1536283404.017:39844): item=0 name="test.txt" inode=1587579 dev=103:08 mode=0100644 ouid=0 ogid=0 rdev=00:00 nametype=NORMAL cap_fp=0000000000000000 cap_fi=0000000000000000 cap_fe=0 cap_fver=0
type=CWD msg=audit(1536283404.017:39844): cwd="/root"
type=SYSCALL msg=audit(1536283404.017:39844): arch=c000003e syscall=257 success=yes exit=3 a0=ffffff9c a1=7fff145999ed a2=0 a3=0 items=1 ppid=32477 pid=10767 auid=1000 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts4 ses=3 comm="cat" exe="/usr/bin/cat" key="test-filter"
----
time->Fri Sep 7 09:27:22 2018
type=PROCTITLE msg=audit(1536283642.307:40008): proctitle=6C73002D2D636F6C6F723D6175746F
type=PATH msg=audit(1536283642.307:40008): item=0 name="test.txt" inode=1587579 dev=103:08 mode=0100644 ouid=0 ogid=0 rdev=00:00 nametype=NORMAL cap_fp=0000000000000000 cap_fi=0000000000000000 cap_fe=0 cap_fver=0
type=CWD msg=audit(1536283642.307:40008): cwd="/root"
type=SYSCALL msg=audit(1536283642.307:40008): arch=c000003e syscall=191 success=no exit=-61 a0=7ffcd0a58c40 a1=7f4b30ece210 a2=7ffcd0a58c00 a3=14 items=1 ppid=1617 pid=12314 auid=1000 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts6 ses=3 comm="ls" exe="/usr/bin/ls" key="test-filter"
----
time->Fri Sep 7 09:27:29 2018
type=PROCTITLE msg=audit(1536283649.775:40009): proctitle=746F75636800746573742E747874
type=PATH msg=audit(1536283649.775:40009): item=1 name="test.txt" inode=1587579 dev=103:08 mode=0100644 ouid=0 ogid=0 rdev=00:00 nametype=NORMAL cap_fp=0000000000000000 cap_fi=0000000000000000 cap_fe=0 cap_fver=0
type=PATH msg=audit(1536283649.775:40009): item=0 name="/root" inode=1569793 dev=103:08 mode=040550 ouid=0 ogid=0 rdev=00:00 nametype=PARENT cap_fp=0000000000000000 cap_fi=0000000000000000 cap_fe=0 cap_fver=0
type=CWD msg=audit(1536283649.775:40009): cwd="/root"
type=SYSCALL msg=audit(1536283649.775:40009): arch=c000003e syscall=257 success=yes exit=3 a0=ffffff9c a1=7fff075909e1 a2=941 a3=1b6 items=2 ppid=1617 pid=12367 auid=1000 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts6 ses=3 comm="touch" exe="/usr/bin/touch" key="test-filter"

我们可以看到都有 cat, ls, touch 在相应的时间对其做了什么操作. aureport 可以给出更直观的报告,

1
2
3
4
$ ausearch -k test_filter | aureport -f -i
33. 09/07/2018 09:56:23 test.txt getxattr no /usr/bin/ls amito 41596
34. 09/07/2018 09:56:26 test.txt openat yes /usr/bin/cat amito 41600
35. 09/07/2018 09:56:28 test.txt openat yes /usr/bin/touch amito 41607

Systemtap

systemtap 功能非常强大,可以监控系统调用, 也可检测文件的修改操作. 但是需要安装系统的 debug 模块. 可能需要在相应的 repo 中 设置 debug 为enabled=1

安装 Systemtap

1
2
3
4
## Fedora
$ sudo dnf install -y kernel-debuginfo kernel-debuginfo-common systemtap systemtap-client systemtap-runtime systemtap-devel
## CentOS
$ sudo yum install -y kernel-debuginfo kernel-debuginfo-common systemtap systemtap-client systemtap-runtime systemtap-devel

获取文件信息

1
2
$ stat -c '%D %i' test.txt
10308 1587579

Systemtap 监控脚本

下面这个脚本可以监控文件的修改记录.将脚本保存为 inodewatch.stp

1
2
3
4
5
6
7
8
9
10
#! /usr/bin/env stap

probe vfs.write, vfs.read
{
# dev and ino are defined by vfs.write and vfs.read
if (dev == MKDEV($1,$2) # major/minor device
&& ino == $3)
printf ("%s(%d) %s 0x%x/%u\n",
execname(), pid(), ppfunc(), dev, ino)
}

监控文件

1
$ sudo stap sudo stap inodewatch.stp 0x103 0x08 1587579

其中连个十六进制数字 0x103 0x08 是上面得到的文件设备ID 10308. 现在对 test.txt 做一些操作.

1
2
vim test.txt
cat test.txt

脚本会给出如下类似的信息

1
2
3
4
5
6
7
8
vim(22230) vfs_read 0x10300008/1587579
vim(22230) vfs_read 0x10300008/1587579
vim(22230) vfs_read 0x10300008/1587579
vim(22230) vfs_read 0x10300008/1587579
vim(22230) vfs_read 0x10300008/1587579
vim(22230) vfs_write 0x10300008/1587579
cat(22303) vfs_read 0x10300008/1587579
cat(22303) vfs_read 0x10300008/1587579

我们可以看到 vim, cattest.txt做了一些读写操作.

inotifywait

inotifywaitinotify-tools 里带的工具可以监控文件的修改建立等等.

安装 inotify-tools

1
2
3
$ sudo dnf install -y inotify-tools
# or
$ sudo apt install -y inotify-tools

监控 文件夹下的文件产生,修改,删除

1
2
3
4
5
$ inotifywait -m /path/to/watch -r -e create -e moved_to -e delete -e modify | while read path action file; do
$ ...
$ your action here
$ ...
$ done

比如, 在特定文件 test.txt 产生时,给特定邮箱发邮件, 并删除它

1
2
3
4
5
6
7
$ inotifywait -m /path -e create -e moved_to | while read path action file; do
$ echo "The file '$file' appeared in directory '$path' via '$action'."
$ if [[ $file == "test.txt" ]]; then
$ echo "The file '$file' appeared in directory '$path' via '$action'. Deleting"|mutt -s "Deleting $file" your_email
$ sudo rm -rf /path/test.txt
$ fi
$ done