Log bash history to syslog on CentOS 6

I think you should notice the new feature of Bash 4.1: log the bash history to syslog.

If you don’t, try to find it yourself in the official change logs here: http://tiswww.case.edu/php/chet/bash/CHANGES

There is a new configuration option (in config-top.h) that forces bash to
forward all history entries to syslog.

There is little information about it, you may not know how to use the fancy feature. This post will show you how to enable it.

There is no configurable option to enable it, and it is disable by default on CentOS 6, so, we must recompile the BASH to make it work. In order to take the less change to the system, we are going to recompile the BASH with the src rpm package.

1) Download the SRPM package, and verify the file.

From here: http://vault.centos.org/6.4/os/Source/SPackages/bash-4.1.2-14.el6.src.rpm

And always keep a good behavior habit to verify the file download from internet, especially this one, it’s your SHELL !

$ sha1sum bash-4.1.2-14.el6.src.rpm
da020835947d7098cf8c07d49b61dd2e6c482f6b bash-4.1.2-14.el6.src.rpm

If the sha1 checksum of the srpm file shows you “da020835947d7098cf8c07d49b61dd2e6c482f6b” , you get the correct file. The strings comes from the file: http://vault.centos.org/6.4/os/Source/repodata/primary.xml.gz 

2) Prepare your rpmbuild environment

Remember this rule [1] :

Building RPMs should NEVER be done with the root user. It should ALWAYS be done with an unprivileged user

$ sudo yum install -y rpm-build make gcc
 
$ mkdir -p ~/rpmbuild/{BUILD,RPMS,SOURCES,SPECS,SRPMS}
 
$ echo '%_topdir %(echo $HOME)/rpmbuild' > ~/.rpmmacros

3) Install the SRPM package

$ rpm -i bash-4.1.2-14.el6.src.rpm

So many warnings like:

warning: user mockbuild does not exist – using root
warning: group mockbuild does not exist – using root
warning: user mockbuild does not exist – using root
warning: group mockbuild does not exist – using root
warning: user mockbuild does not exist – using root
warning: group mockbuild does not exist – using root
warning: user mockbuild does not exist – using root
warning: group mockbuild does not exist – using root
warning: user mockbuild does not exist – using root
warning: group mockbuild does not exist – using root

….

As you did before, just ignore the warnings. :-)

The command will unpacke some files from SRPM and put them into the directory “rpmbuid” you created just now.

4) Rebuild the SRPM

You will follow this instruction [2]:

  1. cd ~/rpmbuild/SPECS/
  2. rpmbuild -bp mypackage.spec
  3. cd ~/rpmbuild/BUILD/
  4. cp existing_directory existing_directory.orig
  5. cd existing_directory
  6. find the file you wish to change, modify it.
  7. cd ~/rpmbuild/BUILD/
  8. diff -Npru existing_directory.orig exiting_directory > name_of_your_patch_file.patch
  9. cp name_of_your_patch_file.patch ~/rpmbuild/SOURCES/
  10. cd ~/rpmbuild/SPECS/
  11. edit the mypackage.spec file to add the definition of name_of_your_patch_file.patch and the application of your_patch_file — please look in the file to see how that is done.
  12. rpmbuild -ba mypackage.spec

 

$ cd ~/rpmbuild/SPECS/
 
$ rpmbuild -bp bash.spec
 
$ cd ~/rpmbuild/BUILD/
 
$ cp -r bash-4.1 bash-4.1.orig
 
$ cd bash-4.1

Now, it’s hack time !

$ vim config-top.h

Find the following comment out line:

/* #define SYSLOG_HISTORY */

Uncomment it:

#define SYSLOG_HISTORY

And make the next few lines:

#if defined (SYSLOG_HISTORY)
# define SYSLOG_FACILITY LOG_USER
# define SYSLOG_LEVEL LOG_INFO
#endif

Change to:

#if defined (SYSLOG_HISTORY)
# define SYSLOG_FACILITY LOG_LOCAL1
# define SYSLOG_LEVEL LOG_DEBUG
#endif

LOG_USER and LOG_INFO means that bash history will use the “user” facility and “info” level. By default, if we don’t change the facility and level of syslog, the bash history will fill with the /var/log/messages …

Available facility: kern, user, info, mail, daemon, auth, syslog, news, uucp, lpr, ftp, cron, local0-7

Available level: emerg, alert, crit, err, notice, info, debug

So I choose the unusual facility “local1″ and level “debug”.

The bash history has the same effect like this command: logger -p local1.debug “helloworld” .

$ vim bashhist.c
void
bash_syslog_history (line)
const char *line;
{
char trunc[SYSLOG_MAXLEN];
 
if (strlen(line) < SYSLOG_MAXLEN)
syslog (SYSLOG_FACILITY|SYSLOG_LEVEL, "HISTORY: PID=%d UID=%d %s", getpid(), current_user.uid, line);
else
{
strncpy (trunc, line, SYSLOG_MAXLEN);
trunc[SYSLOG_MAXLEN - 1] = '\0';
syslog (SYSLOG_FACILITY|SYSLOG_LEVEL, "HISTORY (TRUNCATED): PID=%d UID=%d %s", getpid(), current_user.uid, trunc);
}
}

Change to:

void
bash_syslog_history (line)
const char *line;
{
char trunc[SYSLOG_MAXLEN];
 
if (strlen(line) < SYSLOG_MAXLEN)
syslog (SYSLOG_FACILITY|SYSLOG_LEVEL, "HISTORY: PPID=%d PID=%d SID=%d UID=%d User=%s %s", getppid(), getpid(), getsid(getpid()), current_user.uid, current_user.user_name, line);
else
{
strncpy (trunc, line, SYSLOG_MAXLEN);
trunc[SYSLOG_MAXLEN - 1] = '\0';
syslog (SYSLOG_FACILITY|SYSLOG_LEVEL, "HISTORY (TRUNCATED): PPID=%d PID=%d SID=%d UID=%d User=%s %s", getppid(), getpid(), getsid(getpid()), current_user.uid, current_user.user_name, trunc);
}
}

Because we want to know the current user’s name not hist uid. SID and PPID is for more information about the command.

When you finish the above hacking, let’s continue the recompiling.

diff -Npru bash-4.1.orig bash-4.1 > bash_history_syslog.patch
 
$ cp bash_history_syslog.patch ~/rpmbuild/SOURCES/
 
$ cd ~/rpmbuild/SPECS/
 
$ vim bash.spec

edit the spec file, add these 2 lines to the right place:

Patch117: bash-setlocale.patch
Patch118: bash-tty-tests.patch
Patch119: bash_history_syslog.patch

%patch117 -p1 -b .setlocale
%patch118 -p1 -b .tty_tests
%patch119 -p1 -b .history_syslog
%patch123 -p1 -b .nobits
%patch124 -p1 -b .examples

All done. Let’s rebuild it.

$ rpmbuild -ba bash.spec

After a long wait, the command should end without any error. And it will generate a rpm file named bash-4.1.2-14.el6.x86_64.rpm located in ~/rpmbuild/RPMS/x86_64/ .

5) Install the custom bash rpm. ( force )

$ sudo rpm -Uvh --force bash-4.1.2-14.el6.x86_64.rpm

Let’s see the new “bash” :

$ ll /bin/bash
 
-rwxr-xr-x 1 root root 1030434 Jul 27 02:38 /bin/bash

 

6) Testing 

I use syslog-ng as my syslog server.

Add the following lines to the config file: /etc/syslog-ng/syslog-ng.conf

filter f_bash { facility(local1) and level(debug); };
destination d_bash { file(“/var/log/bash”); };
log { source(s_sys); filter(f_bash); destination(d_bash); };

Restart the syslog-ng:

$ sudo /etc/init.d/syslog-ng restart

Then, you will see your command occurs in /var/log/bash:

Jul 27 02:59:06 alpha -bash: HISTORY: PPID=16253 PID=16254 SID=16254 UID=1001 User=hello sudo cat /var/log/bash

Of course, you can log to the remote syslog server.

 

Enjoy !

 

Reference:

[1] http://wiki.centos.org/HowTos/SetupRpmBuildEnvironment

[2] http://wiki.centos.org/HowTos/RebuildSRPM

 

Comments

  1. jaseywang says:

    没必要这么麻烦,很多脚本都能搞定,不过原生支持了也算不错了~

  2. 也不算麻烦,就是替换一下所有服务器的 bash 。
    如果胆大一点,就直接在 puppet 上指定重装一下 bash 。

  3. Chan Yen says:

    So how would you configure this for rsyslog?

  4. In my example, I use local1 facility and debug level, so just drop this line to the rsyslog config file /etc/rsyslog.conf

    local1.debug @@(remote_syslog_server)

    (remote_syslog_server) stands for the ip or hostname of the remote syslog server

  5. anon says:

    Are you not supposed to escape the syslog special-purpose characters? (Like %) ?

    I’m pretty sure you’ve introduced a buffer overflow exploit here by forgetting to do that?

    You should also use the syslog facility to log to /var/log/secure – so nobody else can read the log files except root!

    Beware that syslog is not encrypyted – you’re giving the whole packet-sniffing world access to all your commands if you send any logging data to remote servers.

    Also remember that even if it is encrypted, it’s going to leak info still (like – the dates and times you run commands, and length of commands – might *sound* innocent, but that’s the kind of data that let the allies win WWII with good cryptanalysis).

  6. Thank you very much.
    You are right, I just introduced a method I found to log every command, I didn’t consider the security issues yet.

    So, if you want to log every command, what’s your plan?
    Thanks again.

  7. Danny says:

    Most modern syslog implementations (syslog-ng and rsyslog, definitely) support TLS encryption over TCP for remote logging. If you log to the local syslog server and forward to remote over an encrypted TCP link, then you just have to worry about people who have admin-level access to the machine. Those people can just as easily turn on system auditing or something along those lines, so there’s not much concern about access to the logged history files at that point.

    At that point, the important thing is to use appropriate filesystem permissions to protect the file, as well as to remember to control access to backups and that sort of thing.

    In either case, rather than reusing the same version number, you should add a suffix to your package’s version and your binary, then either use the alternatives system to allow your bash to coexist with another (man alternatives) or set your package up to conflict with the default bash package.

  8. higkoo says:

    如果希望把历史记录上传到网络上的一台专门收集日志的服务器上,应该怎样处理?

    是不是 define SYSLOG_FACILITY LOG_USER 这个地方就不用改了?然后日志的配置怎么弄呢?

    求指点

  9. 如果要打到其它机器,修改一下本机的 syslog ,打到 remote 的 syslog server 来接收

  10. zhujh2 says:

    echo ‘%_topdir %(echo $HOME)/rpmbuild’ > ~/.rpmmacros
    这一行应该是复制错了。
    应该把>改为>

Submit a Comment