collecting bash historical cmds for all users
Edit me
Bash History Collector
=====
bash history collector
script
# cat /etc/bash_history_collector
if [ "${SHELL##*/}" != "bash" ]; then
return
fi
# to avoid sourcing this file more than once
if [ "$AUDIT_INCLUDED" == "$$" ] || { [ -z "$SSH_ORIGINAL_COMMAND" ] && [ "$(cat /proc/$$/cmdline)" == 'bash-c"/etc/forcecommand.sh"' ]; }; then
return
else
declare -rx AUDIT_INCLUDED="$$"
fi
# '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
if groups | grep -q root; then
declare -x TMOUT=43200 # timeout for root's sessions
chattr +a "$HISTFILE" # set append-only
fi
shopt -s histappend
shopt -s cmdhist
# history substitution ask for a confirmation
shopt -s histverify
## add timestamps in history - obsoleted with logger/syslog
# declare -rx HISTTIMEFORMAT='%F %T '
# enable forward search ('ctrl-s')
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_TAG="bash_history"
declare -rx AUDIT_STR="[$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" ]; then # initialization
if [ -f "$HISTFILE" ] && [ "$(wc -l .bash_history | awk '{print $1}')" -ne 0 ]; then
local AUDIT_CMD="$(fc -l -1 -1)" # previous history command
AUDIT_LASTHISTLINE="${AUDIT_CMD%%+([^ 0-9])*}"
else
local AUDIT_CMD="" # handle a newly created home dir
AUDIT_LASTHISTLINE="${AUDIT_CMD%%+([^ 0-9])*}"
fi
else
AUDIT_LASTHISTLINE="$AUDIT_HISTLINE"
fi
local AUDIT_CMD="$(history 1)" # current history command
if [ -z "${HISTTIMEFORMAT}" ]; then
AUDIT_HISTLINE="$(echo ${AUDIT_CMD%%+([^ 0-9])*})"
else
AUDIT_HISTLINE="$(echo ${AUDIT_CMD%%+([^ 0-9])*} | cut -d' ' -f1)"
fi
if [ "${AUDIT_HISTLINE:-0}" -ne "${AUDIT_LASTHISTLINE:-0}" ] || [ "${AUDIT_HISTLINE:-0}" -eq "1" ]; then #AUDIT_HISTLINE avoid logging unexecuted commands after 'ctrl-c', 'empty+enter', or after 'ctrl-d'
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_TAG" "$AUDIT_STR $PWD" "${AUDIT_CMD##*( )?(+([0-9])?(\*)+( ))}"; then
echo error "AUDIT_TAG" "$AUDIT_STR $PWD" "${AUDIT_CMD##*( )?(+([0-9])?(\*)+( ))}"
fi
else
echo $( date +%F_%H:%M:%S ) "AUDIT_TAG" "$AUDIT_STR $PWD" "${AUDIT_CMD##*( )?(+([0-9])?(\*)+( ))}" >>/var/log/cmd.log
fi
# debug
# 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
}
# aut the session closing
function AUDIT_EXIT() {
local AUDIT_STATUS="$?"
if [ -n "$AUDIT_SYSLOG" ]; then
logger -p user.info -t "$AUDIT_TAG" "$AUDIT_STR" "#=== session closed $AUDIT_LOGINUSER/$AUDIT_LOGINPID ==="
else
echo $( date +%F_%H:%M:%S ) "$AUDIT_TAG" "$AUDIT_STR" "#=== session closed $AUDIT_LOGINUSER/$AUDIT_LOGINPID ===" >>/var/log/cmd.log
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_TAG" "$AUDIT_STR" "#=== session opened $AUDIT_LOGINUSER/$AUDIT_LOGINPID ===" #audit the session openning
else
echo $( date +%F_%H:%M:%S ) "$AUDIT_TAG" "$AUDIT_STR" "#=== session opened $AUDIT_LOGINUSER/$AUDIT_LOGINPID ===" >>/var/log/cmd.log
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 -x PROMPT_COMMAND="[ -n \"\$AUDIT_DONE\" ]; 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
# terminal/window's size:
shopt -s checkwinsize
Hook it in /etc/bashrc
# echo "[ -f /etc/bash_history_collector ] && . /etc/bash_history_collector # added for bash history collector" >> /etc/bashrc