简单实现linux shell命令审计
2014-01-26 10:32
666 查看
前段时间需要对服务器的所有shell操作做一个审计,上网查了查大概有这么几种实现方式:
修改环境变量“PROMPT_COMMAND”
给bash打补丁‘bash-BOFH’
shell封装‘rootsh / sniffy / ttyrpld / ttysnoop’
修改“PROMPT_COMMAND”最为简单快捷,问题在于使用history命令只能在执行结束以后才能得到审计结果,效果不好还可能丢失审计日志;打补丁维护比较麻烦,如果bash升级需要重新打补丁编译;而几个shell封装的程序打印出太多的日志,和需求不符合。后来查到了一种相对简单(shell脚本实现)又符合需求的方法,参考连接:http://www.pointsoftware.ch/de/howto-bash-audit-command-logger/。
这个方法的原理是通过shell的trap命令在shell命令执行前指定自定义的函数。trap是一个shell内建命令,它用来在脚本中指定信号如何处理。比如,按Ctrl+C会使脚本终止执行,实际上系统发送了SIGINT信号给脚本进程,SIGINT信号的默认处理方式就是退出程序。如果要在Ctrl+C不退出程序,那么就得使用trap命令来指定一下SIGINT的处理方式了。trap命令不仅仅处理Linux信号,还能对脚本退出(EXIT)、调试(DEBUG)、错误(ERR)、返回(RETURN)等情况指定处理方式。这里选用DEBUG来插入审计函数,具体实现参考下面的脚本:
修改环境变量“PROMPT_COMMAND”
给bash打补丁‘bash-BOFH’
shell封装‘rootsh / sniffy / ttyrpld / ttysnoop’
修改“PROMPT_COMMAND”最为简单快捷,问题在于使用history命令只能在执行结束以后才能得到审计结果,效果不好还可能丢失审计日志;打补丁维护比较麻烦,如果bash升级需要重新打补丁编译;而几个shell封装的程序打印出太多的日志,和需求不符合。后来查到了一种相对简单(shell脚本实现)又符合需求的方法,参考连接:http://www.pointsoftware.ch/de/howto-bash-audit-command-logger/。
这个方法的原理是通过shell的trap命令在shell命令执行前指定自定义的函数。trap是一个shell内建命令,它用来在脚本中指定信号如何处理。比如,按Ctrl+C会使脚本终止执行,实际上系统发送了SIGINT信号给脚本进程,SIGINT信号的默认处理方式就是退出程序。如果要在Ctrl+C不退出程序,那么就得使用trap命令来指定一下SIGINT的处理方式了。trap命令不仅仅处理Linux信号,还能对脚本退出(EXIT)、调试(DEBUG)、错误(ERR)、返回(RETURN)等情况指定处理方式。这里选用DEBUG来插入审计函数,具体实现参考下面的脚本:
#! /bin/bash cat >> /etc/bash_audit <<"EOF" #to avoid sourcing this file more than once if [ "${OSTYPE:0:7}" != "solaris" ] #following not working in solaris then #do not source this file twice; also do not source it if we are in forcecommand.sh, source it later from "-bash-li" #if we would source it from forcecommand.sh, the environment would be lost after the call of 'exec -l bash -li' if [ "$AUDIT_INCLUDED" == "$$" ] || { [ -z "$SSH_ORIGINAL_COMMAND" ] && [ "$(cat /proc/$$/cmdline)" == 'bash-c"/etc/forcecommand.sh"' ]; } then return else declare -rx AUDIT_INCLUDED="$$" fi fi #prompt & color _backnone="\e[00m" _backblack="\e[40m" _backblue="\e[44m" _frontred_b="\e[01;31m" _frontgreen_b="\e[01;32m" _frontgrey_b="\e[01;37m" _frontgrey="\e[00;37m" _frontblue_b="\e[01;34m" #PS1="\[${_backblue}${_frontgrey_b}\]\u@\h:\[${_backblack}${_frontblue_b}\]\w\\$\[${_backnone}${_frontgrey_b}\] " #grey PS1="\[${_backblue}${_frontgreen_b}\]\u@\h:\[${_backblack}${_frontblue_b}\]\w\\$\[${_backnone}${_frontgreen_b}\] " #green #PS1="\[${_backblue}${_frontred_b}\]\u@\h:\[${_backblack}${_frontblue_b}\]\w\\$\[${_backnone}${_frontred_b}\] " #red declare -rx PS1 #'history' options declare -rx HISTFILE="$HOME/.bash_history" declare -rx HISTSIZE=500000 #nbr of cmds in memory declare -rx HISTFILESIZE=500000 #nbr of cmds on file declare -rx HISTCONTROL="" #does not ignore spaces or duplicates declare -rx HISTIGNORE="" #does not ignore patterns declare -rx HISTCMD #history line number #following line is commented to avoid following issue: loading the history during the sourcing of this file (non-interactive bash) is also loading history lines that begin with '#', but then during the trap DEBUG calls it reloads the whole history without '#'-lines and produces an double-length history. #history -r #to reload history from file if a prior HISTSIZE has truncated it #following 2 lines commented because 'history -r' was still loading '#'-lines #shopt -s extglob #enable extended pattern matching operators #HISTIGNORE="*([ \t])#*"; history -r #reload history without commented lines; this force non-interactive bash to behave like interactive bash, without this AUDIT_HISTLINE will get a wrong initial value, leading then to a small issue where empty bash sessions are actually logging the last command of history if [ "${OSTYPE:0:7}" != "solaris" ] #following not working in solaris then if groups | grep -q root then declare -x TMOUT=3600 #timeout for root's sessions chattr +a "$HISTFILE" #set append-only fi fi shopt -s histappend shopt -s cmdhist #history substitution ask for a confirmation shopt -s histverify #add timestamps in history - obsoleted with logger/syslog #'http://www.thegeekstuff.com/2008/08/15-examples-to-master-linux-command-line-history/#more-130' #declare -rx HISTTIMEFORMAT='%F %T ' #enable forward search ('ctrl-s') #'http://ruslanspivak.com/2010/11/25/bash-history-incremental-search-forward/' if shopt -q login_shell && [ -t 0 ] then stty -ixon fi #bash audit & traceability # # # declare -rx AUDIT_LOGINUSER="$(who -mu | awk '{print $1}')" declare -rx AUDIT_LOGINPID="$(who -mu | awk '{print $6}')" declare -rx AUDIT_USER="$USER" #defined by pam during su/sudo declare -rx AUDIT_PID="$$" declare -rx AUDIT_TTY="$(who -mu | awk '{print $2}')" declare -rx AUDIT_SSH="$([ -n "$SSH_CONNECTION" ] && echo "$SSH_CONNECTION" | awk '{print $1":"$2"->"$3":"$4}')" declare -rx AUDIT_STR="[audit $AUDIT_LOGINUSER/$AUDIT_LOGINPID as $AUDIT_USER/$AUDIT_PID on $AUDIT_TTY/$AUDIT_SSH]" declare -x AUDIT_LASTHISTLINE="" #to avoid logging the same line twice declare -rx AUDIT_SYSLOG="1" #to use a local syslogd # # # #the logging at each execution of command is performed with a trap DEBUG function #and having set the required history options (HISTCONTROL, HISTIGNORE) #and to disable the trap in functions, command substitutions or subshells. #it turns out that this solution is simple and works well with piped commands, subshells, aborted commands with 'ctrl-c', etc.. set +o functrace #disable trap DEBUG inherited in functions, command substitutions or subshells, normally the default setting already shopt -s extglob #enable extended pattern matching operators function AUDIT_DEBUG() { if [ -z "$AUDIT_LASTHISTLINE" ] #initialization then local AUDIT_CMD="$(fc -l -1 -1)" #previous history command AUDIT_LASTHISTLINE="${AUDIT_CMD%%+([^ 0-9])*}" else AUDIT_LASTHISTLINE="$AUDIT_HISTLINE" fi local AUDIT_CMD="$(history 1)" #current history command AUDIT_HISTLINE="${AUDIT_CMD%%+([^ 0-9])*}" if [ "${AUDIT_HISTLINE:-0}" -ne "${AUDIT_LASTHISTLINE:-0}" ] || [ "${AUDIT_HISTLINE:-0}" -eq "1" ] #avoid logging unexecuted commands after 'ctrl-c', 'empty+enter', or after 'ctrl-d' then echo -ne "${_backnone}${_frontgrey}" #disable prompt colors for the command's output #remove in last history cmd its line number (if any) and send to syslog if [ -n "$AUDIT_SYSLOG" ] then if ! logger -p user.info -t "$AUDIT_STR $PWD" "${AUDIT_CMD##*( )?(+([0-9])?(\*)+( ))}" then echo error "$AUDIT_STR $PWD" "${AUDIT_CMD##*( )?(+([0-9])?(\*)+( ))}" fi else echo $( date +%F_%H:%M:%S ) "$AUDIT_STR $PWD" "${AUDIT_CMD##*( )?(+([0-9])?(\*)+( ))}" >>/var/log/userlog.info fi #echo "===cmd:$BASH_COMMAND/subshell:$BASH_SUBSHELL/fc:$(fc -l -1)/history:$(history 1)/histline:${AUDIT_CMD%%+([^ 0-9])*}/last_histline:${AUDIT_LASTHISTLINE}===" #for debugging return 0 else return 1 fi } # # # #audit the session closing function AUDIT_EXIT() { local AUDIT_STATUS="$?" if [ -n "$AUDIT_SYSLOG" ] then logger -p user.info -t "$AUDIT_STR" "#=== session closed ===" else echo $( date +%F_%H:%M:%S ) "$AUDIT_STR" "#=== session closed ===" >>/var/log/userlog.info fi exit "$AUDIT_STATUS" } # # # #make audit trap functions readonly; disable trap DEBUG inherited (normally the default setting already) declare -frx +t AUDIT_DEBUG declare -frx +t AUDIT_EXIT # # # #audit the session opening if [ -n "$AUDIT_SYSLOG" ] then logger -p user.info -t "$AUDIT_STR" "#=== session opened ===" #audit the session openning else echo $( date +%F_%H:%M:%S ) "$AUDIT_STR" "#=== session opened ===" >>/var/log/userlog.info fi # # # #when a bash command is executed it launches first the AUDIT_DEBUG(), #then the trap DEBUG is disabled to avoid a useless rerun of AUDIT_DEBUG() during the execution of pipes-commands; #at the end, when the prompt is displayed, re-enable the trap DEBUG #declare -rx PROMPT_COMMAND="AUDIT_DONE=; trap 'AUDIT_DEBUG && AUDIT_DONE=1; trap DEBUG' DEBUG; [ -n \"\$AUDIT_DONE\" ] && echo '-----------------------------'" #NOK: declare -rx PROMPT_COMMAND="echo "-----------------------------"; trap 'AUDIT_DEBUG; trap DEBUG' DEBUG; echo '-----------------------------'" #OK: declare -rx PROMPT_COMMAND="echo "-----------------------------"; trap 'AUDIT_DEBUG; trap DEBUG' DEBUG" declare -rx PROMPT_COMMAND="[ -n \"\$AUDIT_DONE\" ] && echo '-----------------------------'; AUDIT_DONE=; trap 'AUDIT_DEBUG && AUDIT_DONE=1; trap DEBUG' DEBUG" declare -rx BASH_COMMAND #current command executed by user or a trap declare -rx SHELLOPT #shell options, like functrace trap AUDIT_EXIT EXIT #audit the session closing #endof EOF chown root:root /etc/bash_audit chmod 644 /etc/bash_audit for i in /etc/profile /etc/skel/.bashrc /root/.bashrc /home/*/.bashrc; do if ! grep -q ". /etc/bash_audit" "$i" then echo "===updating $i===" echo "[ -f /etc/bash_audit ] && . /etc/bash_audit #added by franzi" >>"$i" fi done cat >>/etc/rsyslog.conf <<"EOF" #added by franzi $ActionFileDefaultTem 8d83 plate RSYSLOG_FileFormat #stop avahi if messages are dropped (cf. /var/log/messages with 'net_ratelimit' or 'imuxsock begins to drop') #update-rc.d -f avahi-daemon remove && service avahi-daemon stop #$SystemLogRateLimitInterval 10 #$SystemLogRateLimitBurst 500 $SystemLogRateLimitInterval 0 #endof EOF cat >/etc/rsyslog.d/45-xsserver.conf <<"EOF" #added by franzi # Filter duplicated messages $RepeatedMsgReduction off # Enable high precision timestamps $ActionFileDefaultTemplate RSYSLOG_FileFormat # Log bash audit generated log messages to file if $syslogfacility-text == 'user' and $syslogseverity-text == 'info' and $syslogtag startswith '[audit' then /var/log/userlog.info #then drop them & ~ EOF
相关文章推荐
- Linux Shell - 如何使用sort与uniq命令删除重复的文本行
- 37条常用Linux Shell命令组合
- Linux Shell 编程实战技巧
- linux shell 统计文件夹下的目录
- 《A Practical Guide to Linux Commands, Editors, and Shell Programming》之命令行解析
- linux shell 特殊变量----轻松获取系统信息
- Linux Shell for循环写法总结
- Linux shell脚本编写基础
- Linux下配置eclipse桌面快捷方式
- Manage Audit File Directory Growth with cron (Doc ID 1298957.1)
- Linux shell命令汇总
- linux shell if 参数
- Linux shell 脚本示例
- linux shell要点与技巧
- Linux Make(Makefile)由浅入深的学习与示例剖析
- Linux Shell sort排序常用命令
- linux 脚本编写
- Understand Linux Shell and Basic Shell Scripting Language Tips-Part I
- 5 Shell Scripts for Linux Newbies to Learn Shell Programming – Part II
- Sailing Through The World of Linux BASH Scripting – Part III