很多时候我们为了安全审计或者故障跟踪排错,可能会记录分析主机系统的操作行为。比如在系统中新增了一个用户,修改了一个文件名,或者执行了一些命令等等,理论上记录的越详细, 越有利于审计和排错的目的。不过过剩的记录也会为分析带来不少麻烦, 尤其是将很多主机的记录行为发送到固定的远程主机中,数据越多,分析的成本便越大。
忽略 cron,daemon 产生的记录;
忽略带密码的敏感命令行或脚本操作记录;
忽略监控用户(比如 nagios,zabbix,promethus 等)产生的记录;
忽略频繁产生日志的操作行为;
history 记录方式 定制 bash 记录方式 snoopy 记录方式 auditd 记录方式 eBPF 记录方式
history 记录方式
history 方式 很传统也很简单,本质上是将历史的命令发送到 syslog 日志中,可以用来简单记录用户的命令操作历史。但是这种方式有几个重要的缺点,并不适合审计的目的:
容易被修改,被绕过; 记录太简单,没有上下文信息(比如 pid, uid, sid 等); 无法记录 shell 脚本内的操作; 无法记录非登录的操作; 难以实现过滤规则;
定制 bash 记录方式
容易被绕过,用户可以使用 csh,zsh 等; 无法记录 shell 脚本内的操作; 过滤规则可能单一; 可能需要不停的更新 bash 版本,工作量大,否则容易被发行版替换;
snoopy 记录方式
难以绕过,只要设置了 PRELOAD,就肯定会记录;
无论是否存在 tty 会话,都会记录 execv,execve 相关的命令行操作,包含详细的进程上下文信息;
可以记录 shell 脚本内部的操作行为,脚本内的命令行操作大部分都会调用 execv,execve;
可以记录操作行为的参数, 比如指定了用户名,密码等;
过滤规则丰富,可以忽略指定 daemon,uid,也可以仅记录指定的 uid;
如下日志示例:
Oct 27 11:34:31 cz-t1 snoopy[24814]: [time_ms:778 login:cz uid:0 pid:24814 ppid:24676 sid:24579 tty:/dev/pts/0 cwd:/root filename:/bin/uptime username:root]: uptime -p
上述日志显示 root 用户执行了uptime
命令,参数包含 -p
对应的进程上下文信息都比较全,不过 snoopy 的缺点也比较明显,主要包含以下几点:
仅支持 execv,execve 相关系统调用的操作; 不设置规则可能产生的日志过多,对日志搜集系统造成很大的负担; 暂不支持过滤敏感信息规则;
在实际的使用中,snoopy 记录方式可以很详细的记录所有的命令操作信息,帮助我们定位很多疑难问题。不过我们也需要通过过滤规则来避免产生过多的信息,snoopy 的过滤规则可以满足以下需求:
忽略 cron,daemon 产生的记录;
忽略监控用户(比如 nagios,zabbix,promethus 等) 产生的记录;
比如以下配置,即可忽略 crond,my-daemon 守护进程,忽略 zabbix 用户:
# zabbix uid 为 992
filter_chain = exclude_uid:992;exclude_spawns_of:crond,my-daemon
备注:过滤规则在(filtering.c - snoopy_filtering_check_chain)函数实现,由 log.c - snoopy_log_syscall_exec 函数调用,过滤规则为事后行为,即在打印日志的时候判断是否满足过滤规则,并非事前行为。
另外,我们在 snoopy 的基础上增加了 exclude_comm 过滤规则,我们可以忽略记录指定的命令,比如以下:
filter_chain = exclude_uid:992;exclude_comm:mysql,mongo,redis-cli
auditd 记录方式
type=SYSCALL msg=audit(1603800704.305:5304075): arch=c000003e syscall=59 success=yes exit= a0=1c79fd0 a1=1bf51a0 a2=1bd4450 a3=7ffe7270d320 items=2 ppid=95264 pid=99702 auid= uid= gid= euid= suid= fsuid= egid= sgid= fsgid= tty=pts0 ses=571973 comm="mysql" exe="/usr/bin/mysq
l" key="command"
type=EXECVE msg=audit(1603800704.305:5304075): argc=5 a0="/usr/bin/mysql" a1="-h" a2="127.0.0.1" a3="-P" a4="3301"
auditd 整体上为分离的架构,auditctl 可以控制 kauditd 生成记录的策略,kauditd 生成的记录事件会发送到 auditd 守护程序,audisp 可以消费 auditd 的记录到其它地方。其主要的几个工具包含如下:
### ignore common tools
-a never,exit -F arch=b64 -F exe=/usr/bin/redis-cli
-a never,exit -F arch=b64 -F exe=/usr/bin/mysql
-a never,exit -F arch=b64 -F exe=/usr/bin/mongo
....
## Kernel module loading and unloading
-a always,exit -F perm=x -F auid!=-1 -F path=/sbin/insmod -k modules
....
never 和 always 所能支持的 -F 过滤字段不尽相同, 如果要按照 exe 忽略指定的工具路径, 只能通过 never 实现, exe 为执行工具的路径, 需要设置其值, 这点没有 snoopy 的 exclude_comm 方便.
更多规则设置见: audit-define
eBPF 记录方式
# ./execsnoop
PCOMM PID PPID RET ARGS
bash 32647 32302 /bin/bash
id 32649 32648 /usr/bin/id -un
hostname 32651 32650 /usr/bin/hostname
uptime 410 32744 /bin/uptime
总结
从上述介绍可以看到,审计系统的操作行为其实就是为了更方便的追溯和排查问题,审计所产生的日志记录本身也可以作为取证的材料。一些对安全敏感的企业可以通过 auditd 方式来实现不同级别的审计标准。
在实际的使用中,我们建议通过 snoopy 或 auditd 来实现系统操作的审计需求,一些细致的记录追踪可以通过 eBPF 方式实现。另外也可以将审计的日志发送到 ELK 等日志平台做一些策略方面的告警,不过在具体的实践中,我们需要做好详细的过滤规则避免产生大量重复且收效甚微的数据。
来源:http://blog.arstercz.com/how-to-audit-linux-system-operation/