Auditing
Cloud CMS provides a built-in audit system that automatically logs the activity between users and content through API service methods. The audit system produces an audit trail consisting of audit records that document the access of every user, content object and service method.
Audit records are created automatically if they are enabled for your tenant and for the repository against which the operation occurs.
Auditing is only available for on-premise, Docker customers.
How to Enable Auditing
To enable audit records, you will need to modify your docker.properties
file to include the following:
org.gitana.platform.services.audit.AuditService.enabled=true
You then need to enable audit records on a per-Tenant basis. If you're not running in multitenancy mode (which is common), then you can choose to enable audit records for all tenants by adding the following to your docker.properties
file as well:
org.gitana.platform.services.audit.AuditAspect.enableForAllTenants=true
By default, auditing is disabled for each tenant. This is because auditing is inherently an expensive operation both in terms of computational time as well as disk space. With auditing enabled, some operations may take as much as twice as long to execute.
To enable auditing on a per-Tenant basis, you can set auditingEnabled
to true
on each Tenant object.
Finally, you must make sure the auditing is enabled on each Repository data store that you want to keep records on. To enable auditing within the Repository, make sure that auditingEnabled
is set to true
. Note that this is the default value - thus, Repositories are automatically set up ready to record their audit trails.
Audit disk space is non-capped meaning that your allocations and total disk usage will continue to grow over time. The rate of growth is dependent on how frequent users interact with content and content services.
For information on configuring your Audit log files, see Auditing Docker Configuration.
Audit Records
An audit record is an entry that describes a particular interaction between a user and a content object or service. It provides the following fields:
- scope
- action
- principal
- args
- method
- handler
- return
If the operation involves a data store, the following fields are also filled in:
- datastoreTypeId
- datastoreId
If the operation is against a repository branch, the following field is filled in:
- branchId
In addition, the audit record contains _system
and _doc
properties that are consistent with the rest of the product. All objects within Cloud CMS support these properties.
Here is an example of what an audit record might look like:
{
"_doc" : "87d93692e40edaa9f1ba",
"action" : "READ",
"principal" : "joesmith",
"scope" : "NODE",
"method" : "read",
"handler" : "nodeserviceimpl",
"args" : [{
"type" : "repository",
"value" : {
"platformId" : "9faf2171429839834811",
"datastoreId" : "21853c33f501377f49e6",
"datastoreTypeId" : "repository",
"enableAuditing" : true,
"enableAuthorities" : true,
"maxsize" : -1,
"size" : 0,
"objectcount" : 0,
"_system" : {
"created_on" : {
"timestamp" : "04-Apr-2014 18:00:02",
"year" : 2014,
"month" : 3,
"day_of_month" : 4,
"hour" : 18,
"minute" : 0,
"second" : 2,
"millisecond" : 246,
"ms" : NumberLong("1396634402246")
},
"created_by" : "system",
"created_by_principal_id" : "system",
"created_by_principal_domain_id" : null,
"modified_on" : {
"timestamp" : "04-Apr-2014 18:00:05",
"year" : 2014,
"month" : 3,
"day_of_month" : 4,
"hour" : 18,
"minute" : 0,
"second" : 5,
"millisecond" : 509,
"ms" : NumberLong("1396634405509")
},
"modified_by" : "system",
"modified_by_principal_id" : "system",
"modified_by_principal_domain_id" : null
},
"_doc" : "12a970f0f79af0b9b1c3",
"statisticsDate" : {
"timestamp" : "01-Jan-1970 00:00:00",
"year" : 1970,
"month" : 0,
"day_of_month" : 1,
"hour" : 0,
"minute" : 0,
"second" : 0,
"millisecond" : 1,
"ms" : 1
},
"statisticsDirty" : true,
"title" : "My Content Repository"
}
}, {
"type" : "string",
"value" : "b67b9df3e739a39cd031"
}, {
"type" : "string",
"value" : "614c4da562daafdd80b6"
}],
"datastoreId" : "21853c33f501377f49e6",
"datastoreTypeId" : "repository",
"branch" : "67b9df3e739a39cd031b",
"_system" : {
"created_on" : {
"timestamp" : "04-Apr-2014 18:00:05",
"year" : 2014,
"month" : 3,
"day_of_month" : 4,
"hour" : 18,
"minute" : 0,
"second" : 5,
"millisecond" : 592,
"ms" : NumberLong("1396634405592")
},
"created_by" : "joesmith",
"created_by_principal_id" : "39a3b67b9df3e79cd031",
"created_by_principal_domain_id" : "9cd031b67b9df3e739a3",
"modified_on" : {
"timestamp" : "04-Apr-2014 18:00:05",
"year" : 2014,
"month" : 3,
"day_of_month" : 4,
"hour" : 18,
"minute" : 0,
"second" : 5,
"millisecond" : 592,
"ms" : NumberLong("1396634405592")
},
"modified_by" : "joesmith",
"modified_by_principal_id" : "39a3b67b9df3e79cd031",
"modified_by_principal_domain_id" : "9cd031b67b9df3e739a3"
}
}
In the audit record above shows that a method called read
was invoked on a service called nodeserviceimpl
by the user joesmith
. It shows the time that the invocation occurred as well as the arguments that were passed into the method.
In this case, the return
field is missing which indicates that the method call returned null
or empty value. Joe tried to read a node and it wasn't found.
You can also see that the audit engine has written down information about the data store (repository in this case) that was trying to be reached as well as the branch. This information is useful in terms of sifting through the audit records to find operations against data stores for a particular project repository or branch.
Audit Services
If enabled, the audit engine automatically generates audit records for you. No additional setup is required. The audit engine generates audit records for operations against nodes and branches. It groups these audit records into the following action categories:
- CREATE
- READ,
- UPDATE
- DELETE
- EXISTS
- COPY
- MOVE
Audit Records Logging
Audit records are logged to disk using a configurable Log4j Appender. The default Audit Records appender is a rolling file appender that writes records to disk.
It is defined like this:
<RollingRandomAccessFile name="AuditRecordsFile" fileName="${baseDir}/cloudcms-auditrecords.log" filePattern="${baseDir}/cloudcms-auditrecords/cloudcms-auditrecords-%d{yyyy-MM-dd-HH-mm}-%i.log" append="false" immediateFlush="false">
<PatternLayout>
<pattern>%msg%n</pattern>
</PatternLayout>
<Policies>
<OnStartupTriggeringPolicy/>
<TimeBasedTriggeringPolicy interval="30"/>
<SizeBasedTriggeringPolicy size="256 MB"/>
</Policies>
<DefaultRolloverStrategy max="20"/>
</RollingRandomAccessFile>
As such, audit records are written to the ./cloudcms-auditrecords
directory (relative to the servlet container root).
The filename for each audit records log file will essentially look like this:
cloudcms-auditrecords-2018-11-15-13-14-1.log
Which is an audit record log file started on Nov 15, 2018 at 1:14pm. 20 rolled over log files are retained before they are cleaned up. The rollover check occurs every 30 minutes or when the current log file reaches 256MB in size (whichever comes first).
S3 Rollover
Cloud CMS supports automatic S3 rollover of audit record files. This generally very desirable since audit record logs tend to grow quite extensively. With S3 Rollover enabled, log files are rolled over on local disk but are also written up to a common S3 bucket. This provides a good way for your third-party tooling to pick up these files and work with them.
To use S3 rollover, you should add a file called log4j2-custom.xml
to your classes
directory.
The log4j2-custom.xml
should look something like this:
<?xml version=“1.0” encoding=“UTF-8"?>
<Configuration status=“info” packages=“org.gitana.platform.services.log”>
<Properties>
<Property name=“AUDITRECORDS_S3_ACCESS_KEY”></Property>
<Property name=“AUDITRECORDS_S3_SECRET_KEY”></Property>
<Property name=“AUDITRECORDS_S3_REGION”></Property>
<Property name=“AUDITRECORDS_S3_BUCKET”></Property>
<Property name=“AUDITRECORDS_S3_PREFIX”></Property>
</Properties>
<Appenders>
<!-- override so that supports S3 rollover -->
<RollingRandomAccessFile name=“AuditRecordsFile” fileName=“${baseDir}/cloudcms-auditrecords.log” filePattern=“${baseDir}/cloudcms-auditrecords/cloudcms-auditrecords-%d{yyyy-MM-dd-HH-mm}-%i.log” append=“false” immediateFlush="false">
<PatternLayout>
<pattern>%msg%n</pattern>
</PatternLayout>
<Policies>
<OnStartupTriggeringPolicy/>
<TimeBasedTriggeringPolicy interval=“30"/>
<SizeBasedTriggeringPolicy size=“256 MB”/>
</Policies>
<DefaultRolloverStrategy max=“20"/>
<S3RolloverStrategy accessKey=“${AUDITRECORDS_S3_ACCESS_KEY}” secretKey=“${AUDITRECORDS_S3_SECRET_KEY}” region=“${AUDITRECORDS_S3_REGION}” bucket=“${AUDITRECORDS_S3_BUCKET}” prefix=“${AUDITRECORDS_S3_PREFIX}” />
</RollingRandomAccessFile>
</Appenders>
<Loggers>
<!-- override “auditrecords” logger to use our RollingRandomAccessFile Appender with support for S3 rollover -->
<Logger name=“auditRecords” additivity=“false” level=“INFO”>
<AppenderRef ref=“AuditRecordsFile”/>
</Logger>
</Loggers>
</Configuration>
You will need to fill in the values for:
- AUDITRECORDS_S3_ACCESS_KEY
- AUDITRECORDS_S3_SECRET_KEY
- AUDITRECORDS_S3_REGION
- AUDITRECORDS_S3_BUCKET
- AUDITRECORDS_S3_PREFIX
And you will further want to adjust the rollover policy to match the needs of your usage. The audit system is intentionally verbose (that is the whole point of it) and so your Audit log files should grow quickly.