Contents

使用auditd监控Linux文件访问

1.简介

Linux 系统通常有多个用户。审计是这种多用户环境的重要组成部分。特别是,作为文件系统的原子部分,文件通常是被监控的单元。

**在本教程中,我们将探讨如何在 Linux 下执行文件访问监控。**首先,我们回顾一下文件访问权限。接下来,我们跳到审计的一般主题。之后,我们深入研究 Linux 下的审计。具体来说,我们讨论了使用广泛使用的审计组件来安装、配置和监视文件访问。

我们使用 GNU Bash 5.1.4 在 Debian 11 (Bullseye) 上测试了本教程中的代码。它应该在大多数符合 POSIX 的环境中工作。

2. 寻找

找出谁在任何系统中做了什么并非易事。 例如,让我们假设一个简单的情况:多用户 Linux 环境中的文件丢失。通常,管理员可以通过ls 检查谁拥有或有权访问该文件:

$ ls -l /dir/file
-rwxr--r-- 1 user1 user1 666 May 6 16:56 file

阅读该行,我们可以破译所有权和权限 。 特别是,用户user1是唯一的所有者,他还具有对常规(第一个*-)文件的读取*( r ) 和写入( w ) 访问权限。此外,用户的私有组(也称为user1)和全局权限是只读的。

在实践中,删除需要包含目录的执行权限:

$ ls -l /dir
drwxrwxrwx 1 root root 4096 May 6 16:25 .
drwxr-xr-x 1 root root 4096 May 6 06:25 ..

请注意,我们添加了*-a*标志来获取特殊的. /dir目录 ,它告诉我们它的属性。

现在,我们知道user1可能是删除*/dir/file*的那个人。未来,我们如何确保?

3. 审计

审计的总体思路是帮助控制用户操作。它提供了一种将活动映射到某些帐户的方法,使管理员能够跟踪:

  • 执行了什么操作
  • 哪个用户采取了行动
  • 涉及哪些对象或对象
  • 事件发生的时间

结合强大的安全概念,如加密保护的身份验证和授权 ,审计可以确保几乎完全的问责制。

通过这种方式,存在一系列记录,从而可以重建事件。实际上,该机制在数据方面与用于跟踪数据库(例如mysql )的日志或多或少相同:

$ cat /var/lib/mysql/audit.log
[...]
<AUDIT_RECORD>
  <TIMESTAMP>2022-10-05T16:06:56 UTC</TIMESTAMP>
  <RECORD_ID>6_2022-10-05T16:03:33</RECORD_ID>
  <NAME>Query</NAME>
  <CONNECTION_ID>5</CONNECTION_ID>
  <STATUS>0</STATUS>
  <STATUS_CODE>0</STATUS_CODE>
  <USER>root[root] @ localhost [127.0.0.1]</USER>
  <OS_LOGIN/>
  <HOST>localhost</HOST>
  <IP>127.0.0.1</IP>
  <COMMAND_CLASS>drop_table</COMMAND_CLASS>
  <SQLTEXT>DROP TABLE IF EXISTS t</SQLTEXT>
 </AUDIT_RECORD>
 [...]

当然,操作系统审计并不像某些数据库那样直接允许恢复——我们需要数据手段 。通过这种方式,审计数据位于备份和简单历史日志之间:

$ cat /home/user1/.mysql_history
create database blogdemo
create user [[email protected]](/cdn_cgi/l/email_protection) IDENTIFIED BY 'password'
grant select,insert,update,delete,create,drop,index,alter,create temporary tables,lock tables on blogdemo.* to [[email protected]](/cdn_cgi/l/email_protection)
flush privileges
exit

然而,有了这些知识,我们仍然比求助于人工取证要好得多。让我们看看这在操作系统级别是如何工作的。

4. 使用auditd进行 Linux 审计

在 Linux 下,我们可以通过多种方式执行和自动化审计。在非常简单的情况下,我们可以使用inotifywaitinotifywatch 。但是,综合auditd 包可能是更好的选择。

4.1. 安装及主要配置

由于默认情况下它不是所有 Linux 发行版的一部分,我们可能需要自己安装auditd

$ apt-get install auditd
[...]

接下来,我们在*/etc/audit/auditd.conf*中建立守护进程的默认配置

$ cat /etc/audit/auditd.conf
#
# This file controls the configuration of the audit daemon
#
local_events = yes
write_logs = yes
log_file = /var/log/audit/audit.log
log_group = adm
log_format = ENRICHED
flush = INCREMENTAL_ASYNC
freq = 50
max_log_file = 8
num_logs = 5
priority_boost = 4
name_format = NONE
##name = mydomain
... long output omited
transport = TCP
krb5_principal = auditd
##krb5_key_file = /etc/audit/audit.key
distribute_network = no
q_depth = 400
overflow_action = SYSLOG
max_restarts = 10
plugin_dir = /etc/audit/plugins.d

文件中的大多数关键设置都是不言自明的,并且具有合理的默认值。其余的,我们可以使用配置参考

至关重要的是,我们应该记下log_file的路径:/var/log/audit/audit.log。当然,write_logs必须是yes才重要。

为了使auditd满足我们的需求,我们可能还需要设置一些规则,基于哪些审计将被执行。

4.2. 规则结构

基本上,规则是管理审计事件记录的触发器。与防火墙类似,当事件与规则匹配时,它会被记录下来。

规则存储在/etc/audit/audit.rules*文件*中:

$ cat /etc/audit/audit.rules
## This file is automatically generated from /etc/audit/rules.d
-D
-b 8192
-f 1
--backlog_wait_time 60000

但是,此文件只是*/etc/audit/rules.d/目录中所有.rules*文件的合并版本:

$ ls /etc/audit/rules.d/
audit.rules

因此,要正确更改或添加规则,我们应该首先在那里修改或创建文件。

4.3. 了解规则

让我们首先检查默认值:

$ cat /etc/audit/rules.d/audit.rules
## First rule - delete all
-D
## Increase the buffers to survive stress events.
## Make this bigger for busy systems
-b 8192
## This determine how long to wait in burst of events
--backlog_wait_time 60000
## Set failure mode to syslog
-f 1

在大多数情况下,这些选项在评论中进行了解释。值得注意的是,缓冲区大小以条目为单位,而不是字节的倍数。

以下是规则文件行的所有选项

  • # <评论>
  • -w <文件路径> -p <权限> -k <密钥名>
  • -a , -S -F <field=value> -k
  • <标志> <值>

请注意,该包对规则语法非常严格。例如,不允许与规则在同一行的注释。此外,重复规则是有风险的。因此,为避免出现问题,应严格遵循语法。考虑到这一点,我们可以继续。

4.4. 设置规则

现在,让我们创建一个非常简单的规则文件*/etc/audit/rules.d/blogdemo.rules*:

## Enable ruleset
-e 1
## Limit the rate to 120 audit entries per second
-r 120
## Monitor /blogdemo for rwxa events
-w /blogdemo -p rwxa -k toplevel_blogdemo
## Monitor /file for read events
-w /file     -p r    -k root_file
## Monitor for name change syscall
-S sethostname -a always,exit -k hostname_change

*在这里,-w /blogdemo -p rwxa -k toplevel_blogdemo监视/blogdemo*目录中涉及读取 ( r )、写入 ( w )、执行 ( x ) 或属性更改 ( a )**的任何事件。

另一方面,-a always,exit -S sethostname -k hostname_change 监视系统调用(-S with sethostname)。需要always操作来记录事件,而exit决定何时以及如何发生。

本质上,规则文件的所有行都传递给用户空间审计控制组件auditctl 。因此,我们可以通过工具手册解释任何规则。

4.5. 启动守护进程

完成初步步骤后,我们可以通过augenrules 加载配置:

$ augenrules --check
/sbin/augenrules: Rules have changed and should be updated
$ augenrules --load
No rules
enabled 1
failure 2
[...]
$ augenrules --check
/sbin/augenrules: No change

在这里,–check开关测试任何新规则添加,而*–load从**/etc/audit/rules.d/重新生成所有规则。 重要的是,augenrules –check可能会显示假阴性,在语法出现问题时声称没有变化。*我们可以通过检查规则并确保它们正确来诊断这里的任何问题。此外,我们可以确认什么是有效的:

$ auditctl -l
-w /blogdemo -p rwxa -k toplevel_blogdemo
-w /file -p r -k root_file
-a always,exit -S sethostname -F key=hostname_change

切换到auditctl的*-l会列出所有当前活动的规则。 最后,我们可以使用auditd*运行守护进程:

$ auditd

通常,我们可以使用journalctl -u auditd在启动期间查找问题。

4.6. 阅读日志

现在让我们使用我们在上面的配置中看到的路径检查日志:

$ cat /var/log/audit/audit.log
type=DAEMON_START msg=audit(1644796660.200:666): op=start ver=3.0.7
  format=enriched kernel=4.9.0-8-amd64 auid=4294967295 pid=1666000 =0 ses=4294967295
  subj=unconfined  res=success AUID="unset" UID="root"
[...]
type=SERVICE_START msg=audit(1644796660.216:669): pid=1 uid=0 auid=4294967295
  ses=4294967295 subj==unconfined msg='unit=auditd comm="systemd"
  exe="/usr/lib/systemd/systemd" hostname=? addr=? terminal=? res=success'
  UID="root" AUID="unset"

显然,服务已经启动。此外,从type=DAEMON_START行,我们可以看到它的PID 为 1666000此外,我们可以从msg部分(1644796660.200 )中提取时间戳,这也是其他事件的一部分。在它之后,用冒号分隔的是事件标识号(666669)。 现在,我们可以手动浏览日志或使用提供的ausearch 进行过滤搜索

## $ ausearch --event 666
time->Tue Feb 15 20:11:06 2022
type=DAEMON_START msg=audit(1644948666.200:666): op=start ver=3.0.7
  format=enriched kernel=4.9.0-8-amd64 auid=4294967295 pid=1666000 uid=0
  ses=4294967295 subj=unconfined  res=success AUID="unset" UID="root"

值得注意的是,ausearch的一大优势是–interpret ( -i ) 标志,它对消息中的数据进行解码,因为它们的一部分是十六进制编码的**:

## $ ausearch --event 666 --interpret
time->Tue Feb 15 20:11:06 2022
type=DAEMON_START msg=audit(02/15/2022 20:11:06.200:666): op=start ver=3.0.7
  format=enriched kernel=4.9.0-8-amd64 auid=unset pid=1666000 uid=0 ses=unset
  subj=unconfined  res=success

比较输出,我们看到记录中auid参数的值从一串十六进制数字转换为unset。此外,日期戳被转换为人类可读的格式。 在上面的两个示例中,我们直接使用了事件编号,但也有许多其他条件,例如:

  • -m ( –message ) 按消息类型查询
  • -c ( –comm ) 按命令名查询
  • -ui ( -uid ) 按用户 ID 查询 此外,我们可以将输出格式化为rawdefaultinterpretcsvtext。这可以通过*–format*标志实现。

5. 使用auditd生成事件和检查记录

最后,我们可以通过一些简单的示例来查看auditd的运行情况。让我们先探索阅读和写作,然后再进行删除。

5.1.读写

首先,让我们写入*/file*:

$ echo Input. > /file

接下来,我们使用*–file*标志检查日志中的任何操作:

$ ausearch --file /file
<no matches>

在这里,我们看不到匹配项,因为根据我们的规则,我们只审核读取事件。考虑到这一点,让我们通过cat 执行一个简单的读取 :

$ cat /file
Input.
## $ ausearch --file /file --interpret
type=PROCTITLE msg=audit(02/15/2022 20:12:22.240:11666) : proctitle=cat /file
type=PATH msg=audit(02/15/2022 20:12:22.240:11666) : item=0 name=/file inode=22
  dev=08:11 mode=file,644 ouid=root ogid=root rdev=00:00 nametype=NORMAL
  cap_fp=none cap_fi=none cap_fe=0 cap_fver=0 cap_frootid=0
type=CWD msg=audit(02/15/2022 20:12:22.240:11666) : cwd=/
type=SYSCALL msg=audit(02/15/2022 20:12:22.240:11666) : arch=x86_64
  syscall=openat success=yes exit=3 a0=AT_FDCWD a1=0x7fffd21697d9 a2=O_RDONLY
  a3=0x0 items=1 ppid=1732318 pid=1733641 auid=user1 uid=user1 gid=user1
  euid=user1 suid=user1 fsuid=user1 egid=user1 sgid=user1 fsgid=user1
  tty=pts1 ses=3873 comm=cat exe=/usr/bin/cat subj==unconfined key=root_file

所有这些行也称为记录,是同一事件11666的一部分。在该事件的type=PATH行中,我们可以看到有关文件本身的详细信息,例如inode编号、设备、模式等。 此外,以type=SYSCALL开头的行是我们的主要兴趣,也由规则的key=root_file 值标识。事实上,它包含完整的用户名 ( uid=user1 )。当不解释结果时,用户会按他们的数字记录。在这种情况下,我们可以通过id 获取用户名:

$ id -nu 1000
user1

type=SYSCALL记录还为我们提供了用于访问文件的命令( comm=catexe=/usr/bin/cat )。

5.2. 删除

回到前面的文件删除示例,我们可以检测谁从*/blogdemo*的子目录中删除了文件。首先,让我们创建一个其中包含单个文件的文件:

$ mkdir --parents /blogdemo/x/
$ echo Input. > /blogdemo/x/file.ext

接下来,我们检查日志:

$ ausearch --file /blogdemo --interpret
type=PROCTITLE msg=audit(02/15/2022 20:30:40.504:12666) : proctitle=mkdir --parents /blogdemo/x/
type=PATH msg=audit(02/15/2022 20:30:40.504:12666) : item=4 name=(null) inode=1966583
  dev=08:11 mode=dir,755 ouid=root ogid=root rdev=00:00 nametype=CREATE cap_fp=none
  cap_fi=none cap_fe=0 cap_fver=0 cap_frootid=0
type=PATH msg=audit(02/15/2022 20:30:40.504:12666) : item=3 name=(null) inode=1966582
  dev=08:11 mode=dir,755 ouid=root ogid=root rdev=00:00 nametype=PARENT cap_fp=none
  cap_fi=none cap_fe=0 cap_fver=0 cap_frootid=0
type=PATH msg=audit(02/15/2022 20:30:40.504:12666) : item=2 name=(null) nametype=CREATE
  cap_fp=none cap_fi=none cap_fe=0 cap_fver=0 cap_frootid=0
type=PATH msg=audit(02/15/2022 20:30:40.504:12666) : item=1 name=(null) inode=1966582
  dev=08:11 mode=dir,755 ouid=root ogid=root rdev=00:00 nametype=PARENT cap_fp=none
  cap_fi=none cap_fe=0 cap_fver=0 cap_frootid=0
type=PATH msg=audit(02/15/2022 20:30:40.504:12666) : item=0 name=/blogdemo inode=1966582
  dev=08:11 mode=dir,755 ouid=root ogid=root rdev=00:00 nametype=PARENT cap_fp=none
  cap_fi=none cap_fe=0 cap_fver=0 cap_frootid=0
type=CWD msg=audit(02/15/2022 20:30:40.504:12666) : cwd=/blogdemo
type=SYSCALL msg=audit(02/15/2022 20:30:40.504:12666) : arch=x86_64 syscall=mkdir
  success=yes exit=0 a0=0x7ffcf6eb17d8 a1=0777 a2=0x0 a3=0xfffffffffffffb8d items=5
  ppid=1732318 pid=1734036 auid=noot uid=root gid=root euid=root suid=root fsuid=root
  egid=root sgid=root fsgid=root tty=pts1 ses=3873 comm=mkdir exe=/usr/bin/mkdir
  subj==unconfined key=toplevel_blogdemo
[...]

目录*/blogdemo/x* 和文件*/blogdemo/x/file.ext*以及负责用户 ( root )的创建和填充都有事件。现在,让我们更改整个目录的权限 以允许任何用户删除文件:

$ ls -la /blogdemo
total 12
drwxr-xr-x  3 root root 4096 Feb 18 00:06 .
drwxr-xr-x 29 root root 4096 Feb 18 00:01 ..
drwxr-xr-x  2 root root 4096 Feb 15 00:06 x
$ chmod --recursive 777 /blogdemo
$ ls -la /blogdemo/.
total 12
drwxrwxrwx  3 root root 4096 Feb 15 00:06 .
drwxr-xr-x 29 root root 4096 Feb 15 00:01 ..
drwxrwxrwx  2 root root 4096 Feb 15 00:06 x

甚至这些更改也有相关的记录,我们可以通过*–comm*标志看到:

## $ ausearch --file /blogdemo --comm chmod --interpret
type=PROCTITLE msg=audit(02/15/2022 20:56:56.248:12696) : proctitle=chmod --recursive 777 /blogdemo
type=PATH msg=audit(02/15/2022 20:56:56.248:12696) : item=0 name=/blogdemo inode=1966582
  dev=08:11 mode=dir,755 ouid=root ogid=root rdev=00:00 nametype=NORMAL cap_fp=none
  cap_fi=none cap_fe=0 cap_fver=0 cap_frootid=0
type=CWD msg=audit(02/15/2022 20:56:56.248:12696) : cwd=/
type=SYSCALL msg=audit(02/15/2022 20:56:56.248:12696) : arch=x86_64 syscall=fchmodat
  success=yes exit=0 a0=AT_FDCWD a1=0x55b15f13e500 a2=0777 a3=0xfffffffffffffd2c
  items=1 ppid=1732318 pid=1734049 auid=noot uid=root gid=root euid=root suid=root
  fsuid=root egid=root sgid=root fsgid=root tty=pts1 ses=3873 comm=chmod exe=/usr/bin/chmod
##   subj==unconfined key=toplevel_blogdemo
type=PROCTITLE msg=audit(02/15/2022 20:56:56.248:12697) : proctitle=chmod --recursive 777 /blogdemo
type=PATH msg=audit(02/15/2022 20:56:56.248:12697) : item=0 name=/blogdemo inode=1966582
  dev=08:11 mode=dir,777 ouid=root ogid=root rdev=00:00 nametype=NORMAL cap_fp=none
  cap_fi=none cap_fe=0 cap_fver=0 cap_frootid=0
type=CWD msg=audit(02/15/2022 20:56:56.248:12697) : cwd=/
type=SYSCALL msg=audit(02/15/2022 20:56:56.248:12697) : arch=x86_64 syscall=openat
  success=yes exit=3 a0=AT_FDCWD a1=0x55b15f13e500
  a2=O_RDONLY|O_NOCTTY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC a3=0x0 items=1 ppid=1732318
  pid=1734049 auid=noot uid=root gid=root euid=root suid=root fsuid=root egid=root
  sgid=root fsgid=root tty=pts1 ses=3873 comm=chmod exe=/usr/bin/chmod subj==unconfined
  key=toplevel_blogdemo

最后,让我们删除file.ext作为user1

$ whoami
user1
$ rm --force /blogdemo/x/file.ext

事件记录包含我们需要的所有数据:

## $ ausearch --file /blogdemo/x/file.ext --comm rm --interpret
type=PROCTITLE msg=audit(02/15/2022 23:59:59.159:16660) : proctitle=rm --force /blogdemo/x/file.ext
type=PATH msg=audit(02/15/2022 23:59:59.159:16660) : item=1 name=/blogdemo/x/file.ext
  inode=1966584 dev=08:11 mode=file,777 ouid=user1 ogid=user1 rdev=00:00 nametype=DELETE
  cap_fp=none cap_fi=none cap_fe=0 cap_fver=0 cap_frootid=0
type=PATH msg=audit(02/15/2022 23:59:59.159:16660) : item=0 name=/blogdemo/x/
  inode=1966583 dev=08:11 mode=dir,777 ouid=user1 ogid=user1 rdev=00:00
  nametype=PARENT cap_fp=none cap_fi=none cap_fe=0 cap_fver=0 cap_frootid=0
type=CWD msg=audit(02/15/2022 23:59:59.159:16660) : cwd=/
type=SYSCALL msg=audit(02/15/2022 23:59:59.159:16660) : arch=x86_64 syscall=unlinkat
  success=yes exit=0 a0=AT_FDCWD a1=0x56227e23e4d0 a2=0x0 a3=0xfffffffffffffbca items=2
  ppid=1732318 pid=1734073 auid=noot uid=user1 gid=user1 euid=user1 suid=user1 fsuid=user1
 egid=user1 sgid=user1 fsgid=user1 tty=pts1 ses=3873 comm=rm exe=/usr/bin/rm
  subj==unconfined key=toplevel_blogdemo

请注意,由于我们更改了其内容,因此审核条目也包含目录的数据。

现在,我们已经证明了导致删除给定目录中任何数据的所有事件。让我们无可争辩。

6. 安全与整合

保护任何类型的日志都很重要,但审计信息本质上是敏感的。因此,让我们确保只有root可以访问审计日志:

$ ls -la /var/log/audit/audit.log
-rw-r----- 1 root root 66600 May 16 12:15 /var/log/audit/audit.log

重要的是,当存储来自多个节点的信息时,这可能变得更加关键。由于auditd包使我们能够将服务器配置为来自其他机器的事件的整合节点,因此可以出现这种情况。

当然,在各个方面加强中央节点的安全性至关重要。这包括SSH 访问防火墙 等领域。