Apereo CAS - Tracking & Auditing Events via Audit Log

Posted by Misagh Moayyed on August 01, 2022 · 10 mins read ·

Apereo CAS provides a facility for auditing authentication activity, allowing them to be recorded to a variety of storage services. Essentially, audited authentication events attempt to provide the who, what, when, how, along with any additional contextual information that might be useful to track activity. Auditable events can be narrowed down and filtered, and may also be stored in a wide range of backend services.

This blog post briefly looks at the Audit Log in Apereo CAS. Our starting position is as follows:

Audit Log

By default, Apereo CAS uses a file-based auditing mechanism to store auditable events. File-based audit logs appear in a cas_audit.log file defined in the Logging configuration and they may look like this:

WHO: casuser
WHAT: supplied credentials: ...
ACTION: AUTHENTICATION_SUCCESS
APPLICATION: CAS
WHEN: Mon Aug 26 12:35:59 IST 2013
CLIENT IP ADDRESS: 172.16.5.181
SERVER IP ADDRESS: 192.168.200.22

Let’s review several essential configuration options that should immediately become useful. First, the auditing facility in Apereo CAS is on by default, and may be tweaked using the following setting:

cas.audit.engine.enabled=false

…and if you wanted to tweak the file-based audit log facility,

cas.audit.slf4j.enabled=false

By default, the structure of the audited record shows up in the log in a multi-line format. You could of course opt for a more compact syntax and switch to a single-line format:

cas.audit.slf4j.use-single-line=true

…or you could also switch to JSON:

cas.audit.engine.audit-format=JSON

You may also note that the audit record includes a special field for Client IP Address, which typically notes the IP address of the end-user attempting to authenticate, etc. Deployments that are behind a proxy or a load balancer often tend to mask the real IP address by default and expose it using a dedicated header, such as X-Forwarded-For. This can be configured with CAS as well, so the correct IP is then recorded into the audit log:

cas.audit.engine.alternate-client-addr-header-name=X-Forwarded-For

A similar trick exists if you are using the embedded Apache Tomcat for the CAS deployment and wish to see the client ip address in the access logs. In this case, the following YAML properties should be effective:

server:
  tomcat:
    basedir: /etc/cas/config/logs 
    remote-ip-header: X-Forwarded-For
    accesslog:
      enabled: true
      pattern: "%{X-Forwarded-For}i %l %u %t %r %s %b"

Storage

It’s often useful to track audit records in a relational database for future monitoring, data mining, and querying features that may be done outside CAS. Here, we try to configure CAS to push audit data into a PostgreSQL database.

First, ensure you have declared the appropriate module/intention in the build:

dependencies {
  implementation "org.apereo.cas:cas-server-support-audit-jdbc"
}

Remember
You should not have to include additional modules or dependencies to provide database drivers. Those will be automatically provided by CAS to the build with the inclusion of the module above.

Then, put specific audit settings in your cas.properties:

cas.audit.jdbc.user=postgres
cas.audit.jdbc.password=password
cas.audit.jdbc.driver-class=org.postgresql.Driver
cas.audit.jdbc.url=jdbc:postgresql://localhost:5432/audit
cas.audit.jdbc.dialect=org.hibernate.dialect.PostgreSQL10Dialect

Note that the schema for the audit records should automatically be generated by CAS:

create table COM_AUDIT_TRAIL (
  id int8 generated by default as identity,
  AUD_ACTION varchar(255),
  APPLIC_CD varchar(255),
  AUD_CLIENT_IP varchar(255),
  AUD_DATE timestamp not null,
  AUD_RESOURCE varchar(2048),
  AUD_SERVER_IP varchar(255),
  AUD_USER varchar(255),
  AUD_USERAGENT varchar(255),
  primary key (id)
)

Retrieval

There are two specific actuator endpoints for fetching audited records from the storage: auditevents that is provided by Spring Boot, and one is auditLog which is owned by CAS.

First, ensure you have declared the appropriate module/intention in the build:

dependencies {
  implementation "org.apereo.cas:cas-server-support-reports"
}

…and then let’s enable both endpoints:

management.endpoint.auditLog.enabled=true
management.endpoint.auditevents.enabled=true

management.endpoints.web.exposure.include=auditLog,auditevents

cas.monitor.endpoints.endpoint.auditevents.access=ANONYMOUS
cas.monitor.endpoints.endpoint.auditLog.access=ANONYMOUS

Working with actuator endpoints requires a 3-step configuration process:

  1. Enable the endpoint
  2. Expose the endpoint over HTTP
  3. Optionally, define security access rules for each endpoint
Note
To make things easy, I am not enforcing any particular access rules to secure these two endpoints. This should ONLY be done during development and testing. In reality, you should make sure access to such endpoints is protected and secured.

The two endpoints essentially return the same data though using different HTTP methods and structures. For example, we could begin by invoking the CAS auditLog endpoint:

curl -k -X POST https://localhost:8443/cas/actuator/auditLog

…where the output would be:

[{
  "principal": "casuser",
  "resourceOperatedUpon": "TGT-1-********zVvV7-M-mmoayyed-2656",
  "actionPerformed": "TICKET_GRANTING_TICKET_CREATED",
  "applicationCode": "CAS",
  "whenActionWasPerformed": "...",
  "clientIpAddress": "...",
  "serverIpAddress": "...",
  "userAgent": "Mozilla/5.0 ..."
}]

The Spring Boot auditevents endpoint is also available using a GET:

curl -k -X GET https://localhost:8443/cas/actuator/auditevents

…and that returns similar output:

{
  "events": [
    {
      "timestamp": "...",
      "principal": "casuser",
      "type": "AUTHENTICATION_EVENT_TRIGGERED",
      "data": { "[UsernamePasswordCredential(username": "casuser, source=null, customFields={})]" }
    }
  ]
}

Note that you can instruct CAS to ignore certain audit activities and actions. For example, if you do not care to see AUTHENTICATION_EVENT_TRIGGERED events in your audit log, you could always skip and exclude those:

# The value here must be a valid regular expression
cas.audit.engine.excluded-actions=AUTHENTICATION_EVENT_TRIGGERED

Need Help?

If you have questions about the contents and the topic of this blog post, or if you need additional guidance and support, feel free to send us a note and ask about consulting and support services.

So…

I hope this review was of some help to you and I am sure that both this post as well as the functionality it attempts to explain can be improved in any number of ways. Please feel free to engage and contribute as best as you can.

Happy Coding,

Misagh Moayyed