IBM Z Day on Nov. 21: Discover the ideal environment for modern, mission-critical workloads. Learn more

Archived | Generating AIX audit reports

Archived content

Archived date: 2019-06-24

This content is no longer being updated or maintained. The content is provided “as is.” Given the rapid evolution of technology, some content, steps, or illustrations may have changed.


In the IBM Systems magazine (August 2009) I wrote an article about ‘Monitoring Events with AIX Audit’ (see Resource). What I did not cover in that article was the generating of audit reports. AIX provides the auditselect utility to select event records from the audit log. However, when running audit in stream mode, you can use the sed and the awk utilities to generate formated reports. In this article, I discuss auditing events and demonstrate how to produce daily audit reports.

Audit overview

AIX audit can be configured to operate in three modes: stream, bin, or stream and bin. The stream mode is my personal choice, because it offers real-time viewing of audit events due to the audit log file being written to in text mode. Stream mode writes to a circular buffering file, called stream.out. Thus, if the filesystem that contains the audit log files fills up, it still continues to write events to the beginning of the log file.

When audit is used in bin mode data, it is written in binary format and is generally considered the preferred way if you want to collect and keep the audit event records over a long period. However, with good administration practice, the stream mode can be used to keep a history of your audit events.

Audit configuration files

When you start configuring auditing, it is pretty much a case of trial and error when you initially start collecting audit events, which, no doubt, system administrators whom have audit running will verify. To get the right events monitored requires periodic changes to the /etc/security/config file, to monitor the correct events for your system. For a full list of events that can be monitored, see the Resource section for the Redbook on ‘Accounting and auditing’. Once you are satisfied with the events being monitored, you can then do bespoke monitoring on other files, for example:


The adhoc or system specific files to be monitored are placed in the /etc/security/objects file.

In the objects file, you specify which filename (called an object) should be monitored for either a read, write, or execute operation. A typical layout to monitor the files (sudoers, sshd_config, syslog.conf) for any write operations could be:




The format for the object file output in the previous example is:

full path name of file:
 < access mode> = "tag name"

Where access mode is r for read access, w for write access, and x for execute access. The tag name is a unique name for the file that is being monitored. You should ensure that the tag name is descriptive of the access mode being given. Also, note that you can only have one entry on each line for each access mode. So, to monitor /etc/ssh/sshd_config for both read and write access, you could have the following entry:


For write access, my tag name starts with a “W” and for the read access my tag name starts with a “R“. This helps me to identify straight away, when viewing the audit log or the audit report, if there has been a read or write access on the file.

For audit to understand how to print each object as a record to the audit log, it needs a corresponding entry in the /etc/security/events file.

A simple printf statement will suffice. For example, to print /etc/ssh/sshd_config with the tag name of: W_SSHD_CONFIG_IBM, as identified in the /etc/security/audit/objects file, previously demonstrated, you could have:

* /etc/ssh/sshd_config
W_SSHD_CONFIG_IBM = printf "%s"

In this example, the line starts with a ‘*’, also the full-path name to the file to be monitored is given followed by the tag name, as given in the objects file, with a printf statement that prints a string of the command that is parsed.

To be able to get audit to print any options parsed, as part of the command being monitored, you need to ensure audit prints the trail part of the record. So, in the /etc/security/streamcmds file, make sure you have the ‘-v’ as part of the auditpr command. Here’s an example:

/usr/sbin/auditstream | auditpr -v > /audit/stream.out &

The audit events to monitor are placed in the /etc/security/config file. Here the classes are created that contain the event(s) to monitor. The contents of the config file is shown below. A class of events is then typically assigned to users. The default class, that is the class that is assigned to each user on the system, is also given. A user can belong to one or many classes. Notice in the class called “general” I am only monitoring for user attribute changes, for example, password change, group changes, chuser, rmuser, adduser, and su attempts. Your configuration may well be different.

# cat /etc/security/audit/config

        streammode = on
        binmode = off

  trail = /audit/trail
  bin1 = /audit/bin1
  bin2 = /audit/bin2
  binsize = 25000
  cmds = /etc/security/audit/bincmds

        cmds = /etc/security/audit/streamcmds

        general = File_Write,PASSWORD_Change,USER_Change,USER_Remove,USER_Create

        default = general

Using the lsuser command to view the current audit classes assigned to users, we can see that all users are assigned to the audit class general. This is the default class assigned to everybody as denoted in the users stanza in the config file. However, as previously mentioned, a user can belong to more than one class.

# lsuser -a auditclasses ALL
root auditclasses=general
daemon auditclasses=general
bin auditclasses=general
sys auditclasses=general
adm auditclasses=general
lpd auditclasses=general
lp auditclasses=general
invscout auditclasses=general
snapp auditclasses=general
ipsec auditclasses=general
ftp auditclasses=general
dxtans auditclasses=general
ukazap auditclasses=general
euazap auditclasses=general
auazap auditclasses=general
beazap auditclasses=general
laazap auditclasses=general

To start the audit subsystem, type:

# audit start

To stop the audit subsystem, type:

# audit stop

To view your current settings, including the defined classes and their events, plus the available events on the system and the objects being monitored (check that audit is currently running first) use:

# audit query

If the server is rebooted, or the audit service is stopped, the stream.out audit log will be over written when the system is restarted. So, be sure to have a mechanism in place that will save and copy the current stream.out contents to a new filename (this issue will be discussed more shortly). To make sure audit is restarted on a reboot, be sure to have the following line in your /etc/rc.tcpip file:

# start audit
/usr/sbin/audit start 1>&- 2>&-

If the system is rebooted, ensure that you have a command or script that copies the stream.out log file. These command(s) should go before the audit start command in the /etc/rc.tcpip file so the log file does not get overwritten when audit starts.

Audit logging

When an audit reports an event, it reports it on the initial login-id of the user and not the current su of that user. This is true for local connections only. With user remote connections, like SSH, the login-id used on the connection is reported. AIX audit does not know about su’s done prior to the SSH connection.

A typical audit log file on my system is shown in Listing 1; it shows the trail record, as well:

Listing 1. stream.out

event           login    status      time                     command

--------------- -------- ----------- ------------------------ ------------------
USER_SU         dxtans   OK          Mon Jan 10 19:05:13 2011 su

PASSWORD_Change dxtans   OK          Mon Jan 10 19:06:27 2011 passwd

PASSWORD_Flags  dxtans   OK          Mon Jan 10 19:06:33 2011 pwdadm

        alpha -c
USER_SU         bravo    FAIL        Mon Jan 10 19:07:01 2011 su

GROUP_Change    dxtans   OK          Mon Jan 10 19:07:33 2011 chgroup

        admin users=dxtans,charlie
PASSWORD_Change root     OK          Mon Jan 10 19:08:31 2011 tsm

USER_SU         charlie  OK          Mon Jan 10 19:08:36 2011 su
USER_Change     charlie  OK          Mon Jan 10 19:08:54 2011 chuser

        zulu rlogin=true

Looking more closely at Listing 1 we see from the first few entries, that user dxtans has su to root. Then he changed the password of user alpha and the pwdadm flags of that user. Because we have informed audit to print the trail part of the record, we can now see that the command options parsed to pwdadm was the ‘alpha -c’. If no trail record was printed, this information would not be present. User bravo has also attempted to su to root, but has failed. This could be because, either user bravo does not know the password or is not authorized, due to not belonging to root’s sugroup. In this demonstration user bravo is not authorized to access root, thus this is a violation.

Please note that when the trail part of the record is printed it is printed on a separate line. When generating a report, both parts of the records lines will have to be joined side-by-side, to make it presentable as a report. This will also bring the report into columns where awk can then be used to extract details.

Preparing to report

To generate a report, the first task is to take a copy of stream.out to work on. As you will discover when viewing the stream.out file, there could be more than one entry for a write on a file. This is especially true when recording writes to the /etc/sudoers file, so be sure to parse the file through uniq to strip duplicate entries out. Then, using sed to pull the paired records together on the same line, we would also need to produce a header for the report.

The following piece of code will achieve this, assuming the stream.out file is parsed as the first ($1) parameter, and the resulting output is redirected to the holdf file:

cat $1 | uniq > holdf
mv holdf $1
sed '1i\
'$host' P-Series Audit Report on User Account Changes
$!N;s/\n/ /
# pull in the last column
s/                 //g' $1 >holdf

The contents of the log have the following format in columns:

  • Column 1 is the actual event, for example a USER_Change.
  • Column 2 is the login-id of the user.
  • Column 3 is the exit status of the command issued, either OK or FAIL.
  • Column 4 – 8, is the date/time of the event, for example, Tue Jan 11 11:42:02 2011.
  • Column 9 is the command executed, for example, chuser.
  • Column 10 is any other information parsed to the command, that is the information contained, if any from the trail record, which generally is the parameters parsed.

Now we can get down to extracting what information is required for a daily audit report, extracting the information that is relevant to your own audit security policy. For instance, monitoring unauthorised account switches or changing user account attributes that have not been authorised via an incident ticket or change request on the system.

For consideration, you may not care about the system administrator su-ing to certain accounts, as this maybe due to normal administrator work. You may not care about users changing their own password, but one would certainly want passwords that are changed by root, no matter what user-id it is. Once this extraction is completed the generated report can be sent via email to the administration manager or system administrator for review and justification, via incident tickets or change requests.

I find it more manageable to have separate awk statements for each condition I want met. Thus making it easier to change the rules or patterns on what gets extracted, and indeed for other system administrators to change the rules if you are not around. By using the NOT operator with awk, you can specify what records should NOT be extracted from the report. Then, each awk statement is AND-ed through out each awk statement contained in the script. For example, to exclude an event if a users tries to su’s to themselves by mistake, we could have:

!($1 =="USER_SU" && $2 ==$10)

Or not to include user root who su’s to the user-id poppy, we could have:

!($1=="USER_SU" && $2=="root" && $9=="su" && $10=="poppy")

Ignoring users whom change their own password we could have:

!($1=="PASSWORD_Change" && $2 == $10)

By AND-ing these statement (and other awk statements ), the results will only produce what we want excluded. For example:

awk '
!($1 =="USER_SU" && $2 ==$10 &&
!($1=="USER_SU" && $2=="root" && $9=="su" && $10=="poppy") &&
!($1=="PASSWORD_Change" && $2 == $10) &&
...more awk statements...

If you do not care about any user su-ing to a particular user, these can then be excluded. For example assume the user genrep1, which is a generic user-id, and you do not require these events in your report, you could use:

!($1=="USER_SU" && $10=="genrep1")

If you wish to ignore a whole record based on one pattern search, use the search statement:

/ <pattern>/

To ignore all records that contain the string ‘xnpd’, one could use the following awk statement:


Once you are satisfied on the records or events you do not want published on a daily basis, use the awk END block statement to print an “end of report” statement. To filter out events from the holdf file, you could use:

awk '
!($1 =="USER_SU" && $2 ==$10) &&
!($1=="USER_SU" && $2=="root" && $9=="su" && $10=="poppy") &&
!($1=="PASSWORD_Change" && $2 == $10)
END {print "\t\t\t\t--- end of report ---"}' holdf

Once the report has been generated, the script could then email the report to the system administrators or manager for review.

Listing 2 shows a script with events that could be ignored from the audit log stream.out file. The pattern rules contained in the listing are the ones just demonstrated. The awk statements contained in Listing 2 gives you some idea on how to apply the awk patterns against the stream.out file, thus allowing you to take it further and personalize the report to your needs. Once the report is generated, it is emailed to the email list:rs6admins.

Listing 2. auditfilter

# auditfilter

if [ $# != 1 ]
  echo "`basename $0` <audit_log>"
  exit 1
 if [ ! -f $1 ]
   echo "file does not exist"
   exit 1

# extract the date part of the filename for the report
report=$(basename $1)
# filename ie: audit0118.log
date_rep=$(echo $report | sed -e 's/audit//g' -e 's/.log//g')
mth=$(echo $date_rep | cut -c 1,2)
day=$(echo $date_rep | cut -c 3,4)
datex="$day / $mth"
# email list here
list="rs6admins "

sendmail -t <<mayday
Subject:audit report on $host
Content-Type: text/html
Content-Transfer-Encoding: 7bit
<body bgcolor="#C0C0C0">
Generated: `date`
Date of Audit : $datex
$(cat $1)

cat $1 | uniq > $holdf
mv $holdf $1
# do header and join lines
sed '1i\
                   '$host' P-Series Audit Report on User Account Changes
$!N;s/\n/ /
# get rid of the ** line
# pull in the last column, these are spaces - adjust as required in sed
s/                   //g' $1 >$holdf
# filter out
awk '
!($1 =="USER_SU" && $2 ==$10) &&
!($1=="USER_SU" && $2=="root" && $9=="su" && $10=="poppy") &&
!($1=="PASSWORD_Change" && $2 == $10) &&
!($1=="USER_SU" && $10=="operator") &&
!($1=="USER_SU" && $10=="genrep1") &&
END {print "\t\t\t\t--- end of report ---"}' $holdf >$1.rep
# cat $1.rep
mailit2 $1.rep

Listing 3 calls the auditfilter script to run. Once the auditfilter script completes, two files are generated. One contains the formatted audit report, the filename being in the format:


The other is a saved copy of the current stream.out file, the filename being in the format:


Listing 3. auditroll

# auditroll
OUTFILE=/audit/audit`date +%m%d`.log
# Shut down auditing
/usr/sbin/audit shutdown
sleep 3
# Move contents of capture file
mv /audit/stream.out $OUTFILE
# Restart auditing
/usr/sbin/audit start
# Kill off processes using the audit file
fuser -k $OUTFILE
# Filter contents of audit file
/audit/auditfilter $OUTFILE

Looking more closely at Listing 3, a new filename is specified using the date command as part of its name for the copied stream.out file. Audit is then shutdown. The stream.out file is then moved to the new (OUTFILE) filename in readiness to generate a new audit report file. Audit is then restarted; this will then create a new stream.out file for the that days audit activity. To err on the safe side and to make sure no processes are running against the OUTFILE post audit shutdown, fuser is used to kill any processes.

When the auditfilter script is executed, it produces a formatted report contained within an email similar to Figure 1.

Figure 1. Audit report

Screen shot showing an audit report example

The auditroll script could be executed every weekday to gather a report on the previous days audit activities.


In my opinion, using audit on AIX is a must. It allows you to monitor the security-related events on your system. When collecting audit reports within an enterprise environment, I suggest it is best done by collating all the reports into one email for review.

David Tansley