实现功能:将每台服务器上用户执行的每条命令及当时的相关环境如登录IP、执行时所在目录、执行时间、登录时间、主机IP、用户名等信息收集到某台中心服务器。
用到的工具或服务:rsyslog、logger、logrotate。

先简单介绍相关的服务和用到的环境变量再整合实现:

1、logger

logger 是一个shell 命令接口,可以通过该接口使用rsyslog(rsyslog是syslog的加强版,如果系统是syslog也可以)的系统日志模块,还可以从命令行直接向系统日志文件(或者自定义的文件)写入一行信息。
logger的用法,具体的参数可以用man查看。

logger [-is] [-f file] [-p pri] [-t tag] [-u socket] [ message ... ]

常用的参数如下:

-i     逐行记录每一次logger的进程ID。
-s     记录消息到标准错误, 包括系统日志。
-f     file 记录特定的文件(将file的内容作为message)。
-p     pri 输入消息的特定优先级。 优先级可以是自定义的数值或者诸如"facility.level"的格式. 举例: "-p local3.info",local3 facility这个设备的消息级别为info。默认是"user.notice"
-t     tag 为每行信息打上特定的标签.
-u     sock 以特定的socket代替内嵌系统常规工作
-d     使用一个数据进程代替一个流连接到这个socket.
--     结束参数列表, 这个允许消息以一个“-”开始

其中"facility.level"与rsyslog中的一样。

2、rsysylog

Linux系统日志信息分为两个部分内核信息和设备信息。共用配置文件/etc/rsyslog.conf
内核信息 -> klogd -> syslogd -> /var/log/messages等文件
设备信息 -> syslogd -> /var/log/messages等文件
其中设备可以使用自定义的设备local0-local7,使用自定义的设备照样可以将设备信息(日志)送给rsyslog。

3、PROMPT_COMMAND

Linux系统的环境变量PROMPT_COMMAND的内容会在bash提示符显示之前被执行。该环境变量的默认值是 history -a 功能是将目前新增的history追加到histfiles 中,默认写入隐藏文件~/.bash_history中。

[root@test01 ~]# echo $PROMPT_COMMAND
history -a

在文件/etc/profile追加一行,更改该环境变量的内容,让其执行我们指定的审计语句(审计语句中有自定义的设备local0,其日志等级info)。

[root@test01 ~]# tail -n 1 /etc/profile
export PROMPT_COMMAND='logger -p local0.info "$(ifconfig | grep -E "eth|em" -A 1 | grep "10.0" | grep -oP "(?<=addr:)[\d\.]+") $(who am i |awk "{print \$1\" \"\$2\" \"\$3\" \"\$4\" \"\$5}") [`pwd`] $(history 1 | { read x cmd; echo "$cmd"; })"'
[root@test01 ~]#source /etc/profile

为了防止用户覆盖PROMPT_COMMAND,可以设置这个环境变量为readonly。
审计语句的功能:bash环境下每次执行一条命令后,logger会将执行该命令和该命令的相关环境如登录IP、执行目录、执行时间、主机IP、执行用户等信息(可以看成是自定义设备local0,设备的日志等级自定义为info级别)送给rsyslog服务器。

4、整合实现

为了保证日志的完整性,除了将日志发送到远程的rsyslog日志集中服务器外,还落地存储一份到本地。本地和远程的rsyslog服务配置如下:

*.info;mail.none;authpriv.none;cron.none;local0.none    /var/log/messages

该行新增加内容local0.none,意思是所有local0设备的全部等级日志信息都不记录到文件/var/log/messages中。

local0.info                                                /var/log/audit.log

这是新增加的一行内容,意思是所有local0设备的info等级的日志信息记录到文件/var/log/audit.log中。
远程集中接收日志的rsyslog服务器的配置文件也要改成和上面的两行一样,修改配置文件后需要重启rsyslogd服务使其生效。

*.* @192.168.192.168

这行还是不变,意思是所有日志的所有等级都发送一份到远程(IP为192.168.192.168)的rsyslog日志集中服务器。@表示使用UDP协议发送,@@表示使用TCP协议发送。

由于audit.log文件会不断地增大,需要类似像message、cron等日志一样的切割,要在logrotate的配置文件/etc/logrotate.d/syslog增加一行就行了。

/var/log/cron
/var/log/maillog
/var/log/messages
/var/log/secure
/var/log/spooler
#以下为新增的一行
/var/log/audit.log
{
    sharedscripts
    postrotate
    /bin/kill -HUP `cat /var/run/syslogd.pid 2> /dev/null` 2> /dev/null || true
    endscript
}

最后看看结果:
本地文件/var/log/audit.log

[root@test01 ~]# tail /var/log/audit.log
Nov  6 10:08:15 test01 root: xxx.xxx.xxx.xxx root pts/0 2014-11-06 09:30 (xxx.xxx.xxx.xxx) [/root] logger
Nov  6 10:08:38 test01 root: xxx.xxx.xxx.xxx root pts/0 2014-11-06 09:30 (xxx.xxx.xxx.xxx) [/root] vim /etc/profile
Nov  6 10:36:19 test01 root: xxx.xxx.xxx.xxx root pts/0 2014-11-06 09:30 (xxx.xxx.xxx.xxx) [/root] man logger
Nov  6 10:36:19 test01 root: xxx.xxx.xxx.xxx root pts/0 2014-11-06 09:30 (xxx.xxx.xxx.xxx) [/root] man logger
Nov  6 10:36:27 test01 root: xxx.xxx.xxx.xxx root pts/0 2014-11-06 09:30 (xxx.xxx.xxx.xxx) [/root] man rsyslog
Nov  6 10:36:51 test01 root: xxx.xxx.xxx.xxx root pts/0 2014-11-06 09:30 (xxx.xxx.xxx.xxx) [/root] man rsyslogd
Nov  6 10:41:40 test01 root: xxx.xxx.xxx.xxx root pts/0 2014-11-06 09:30 (xxx.xxx.xxx.xxx) [/root] tail /etc/profile
Nov  6 10:56:17 test01 root: xxx.xxx.xxx.xxx root pts/0 2014-11-06 09:30 (xxx.xxx.xxx.xxx) [/root] tail -n 1 /etc/profile
Nov  6 11:24:49 test01 root: xxx.xxx.xxx.xxx root pts/0 2014-11-06 09:30 (xxx.xxx.xxx.xxx) [/root] vim /etc/rsyslog.conf
Nov  6 11:28:20 test01 root: xxx.xxx.xxx.xxx root pts/0 2014-11-06 09:30 (xxx.xxx.xxx.xxx) [/root] cat /etc/logrotate.d/syslog
[root@test01 ~]#

远程集中收集日志的服务器文件/var/log/audit.log

[root@xxxxxx log]# tail /var/log/audit.log
Nov  6 11:02:56 xx-xx-06 root: xxx.xxx.xxx.xxx root pts/0 2014-11-06 10:59 (xxx.xxx.xxx.xxx) [/root] vim /etc/crontab
Nov  6 11:03:11 xx-xx-01-WEB06 root: xxx.xxx.xxx.xxx root pts/1 2014-11-06 11:02 (xxx.xxx.xxx.xxx) [/root] more /opt/xxx/xxxxxxx/xxx/xxx/settings_local.php
Nov  6 11:03:28 xx-xx-06 root: xxx.xxx.xxx.xxx root pts/0 2014-11-06 10:59 (xxx.xxx.xxx.xxx) [/root] vim /etc/crontab
Nov  6 11:03:34 xx-xx-06 root: xxx.xxx.xxx.xxx root pts/0 2014-11-06 10:59 (xxx.xxx.xxx.xxx) [/root] ps aux | grep sphinx
Nov  6 11:03:50 xx-xx-06 root: xxx.xxx.xxx.xxx root pts/0 2014-11-06 11:03 (xxx.xxx.xxx.xxx) [/root] ps aux | grep sphinx
Nov  6 11:04:06 xx-xx-06 root: xxx.xxx.xxx.xxx root pts/0 2014-11-06 11:03 (xxx.xxx.xxx.xxx) [/root] /usr/local/mongodb/mongostat
Nov  6 11:24:49 xxxx root: xxx.xxx.xxx.xxx root pts/0 2014-11-06 09:30 (xxx.xxx.xxx.xxx) [/root] vim /etc/rsyslog.conf
Nov  6 11:28:20 xxxx root: xxx.xxx.xxx.xxx root pts/0 2014-11-06 09:30 (xxx.xxx.xxx.xxx) [/root] cat /etc/logrotate.d/syslog
Nov  6 11:38:18 xxxx root: xxx.xxx.xxx.xxx root pts/0 2014-11-06 09:30 (xxx.xxx.xxx.xxx) [/root] tail /var/log/audit.log
Nov  6 11:42:52 xxxxxxxxx root:  root pts/0 2014-11-06 09:28 (xxx.xxx.xxx.xxx) [/var/log] tailf /var/log/audit.log
[root@xxxxxx log]#

存在的小的瑕疵,就是敲空命令的时候会将最近一次执行的命令的信息再重复送一份给rsyslog。

目前已知问题:

  • 更换bash 和 sh 以外的shell操作不会被记录
  • 使用shell脚本只会记录执行的脚本名称,内容不会被记录
  • 被ssh远程执行的命令不会被记录