NXLog Documentation

Microsoft Sentinel

Azure Sentinel is Microsoft’s security information event management (SIEM), which is offered as service within Azure. Because of its presence within Azure and close integration with other Azure services, Microsoft refers to Azure Sentinel as "a scalable, cloud-native, and security orchestration automated response (SOAR) solution." NXLog can be configured as an agent for Azure Sentinel, collecting and forwarding logs to its Azure Log Analytics workspaces. For more information about Azure Sentinel, see Microsoft’s Microsoft Sentinel documentation.

NXLog Enterprise Edition can send security logs directly to Microsoft Sentinel using the Microsoft Sentinel (om_azure) module.

Prerequisites

In order to send any type of logs to Azure Sentinel from NXLog, a few prerequisites need to be met.

Microsoft Sentinel authentication

The first step in preparing to configure om_azure is to retrieve the Workspace ID and either the Primary key or the Secondary key. These keys can be found by navigating in the Azure portal to Log Analytics workspace > Settings > Agents management. The same set of keys can be viewed under either the Windows servers or Linux servers tab.

Log analytics > Workspace > Agents management

The following om_azure directives are required in order to authenticate with Azure Sentinel:

Table 1. Mandatory om_azure directives
Directive Description Sample Value

WorkspaceID

Your organization’s Workspace ID

18fb21ab-d8d4-4448-bdf6-3748c9c03135

SharedKey

Either your Primary key or your Secondary key

VfIQqBoz6fxmnI/E4PKVPza2clH/YAdJ20RnCDwzHCqCMnobYdM1/dD1+KJ6cI6AkR4xPJlTIWI/jfwPU6QHmw==

URL

The Azure endpoint

https://<WorkspaceID>.ods.opinsights.azure.com/api/logs?api-version=2016-04-01

When assigning values to directives that are subject to change by a third-party vendor — or that might change from one organizational unit to another — using define directives to establish the constants needed for a module instance is a best practice. In the following partial configuration file, the values for the om_azure directives that will rarely change are defined. The contents of this file will be read by the main nxlog.conf per file inclusion:

azure-defines.conf
define WORKSPACE    DUMMY_WORKSPACE
define SHAREDKEY    DUMMY_SHAREDKEY
define SUBDOMAIN    ods.opinsights.azure.com
define RESOURCE     api/logs
define APIVER       api-version=2016-04-01

Likewise, another partial configuration containing a complete, generic om_azure instance is platform-agnostic — and thus well-suited for automated deployments — will also be read by the main nxlog.conf configuration file per file inclusion:

om-azure.conf
<Output azure>
    Module          om_azure
    URL             https://%WORKSPACE%.%SUBDOMAIN%/%RESOURCE%?%APIVER%
    WorkspaceID     %WORKSPACE%
    SharedKey       %SHAREDKEY%
    TableName       "%TABLE%"
    HTTPSCAFile     %CAFILE%
</Output>

These two partial configuration files together comprise an almost complete configuration for sending events to Azure Sentinel and will be used for all of the following configuration examples. Only two om_azure directives are missing values: %TABLE%, because it will need to change frequently depending on the log source being monitored, and %CAFile%, because it can change depending on the platform where NXLog Enterprise Edition is running. They will be defined within the the main nxlog.conf configuration file immediately before the inclusion of om-azure.conf.

Gathering and forwarding security events to Microsoft Sentinel

The following four examples collect security logs from four different platforms:

Since the output instances for all four examples are almost identical, but their log sources differ greatly, the primary focus will be on configuring their input instances. Once the security events have been captured and prepared for sending to Azure Sentinel, the steps for verifying the ingested data are basically the same, aside from their respective table names and queries:

  1. After logging in to Microsoft Azure, navigate to Microsoft Sentinel and select the Workspace that will receive the forwarded security events.

    Microsoft Sentinel Logs
  2. Under the General heading immediately below Overview click Logs, which will open up the following Azure Queries modal. Close this modal.

    Azure queries modal
  3. Under the Tables tab, expand the Custom Logs heading to reveal the list of table names which should correspond to the value assigned define TABLE in each of the configurations. This value is eventually assigned to the TableName directive in the generic om-azure.conf file.

    Custom log and query input area

    The om_azure module uses the Azure Monitor HTTP Data Collector API to forward events to Azure Sentinel. Azure classifies all log sources using this API as Custom Logs. When Log Analytics ingests JSON data from Custom Logs, it appends _CL to the value of %TABLE%, thus table names will appear with this modification under the Custom Logs heading of table types as seen below.

    Additionally, Log Analytics renames each event record’s fields: “To identify a property’s data type, Azure Monitor adds a suffix to the property name.” For example, datetime fields like $EventTime will have _t appended to their names: EventTime_t. Fields used for numeric data like $EventID will have _d appended: EventID_d. Fields representing string values like $Hostname will have _s appended: Hostname_s.

    For details about this process, see the Azure Monitor Documentation - Custom logs: Record type and properties.

  4. In the input area beneath the Run button, compose the Kusto queries specific to each table for verifying the successful ingestion of the security events.

The Kusto Query Language (KQL) provides a rich set of tools that allow one to manipulate existing tables to create new table views. Some of the following examples provide instructions with KQL sample functions for achieving this. Using this technique, fields can be normalized according to Azure Sentinel specifications so that the ingested logs can be better integrated with Azure Sentinel analytics. Another use case is to undo the effects of Log Analytics' automated renaming of all fields on ingestion. This could prove useful in cases where custom log analytics have been developed that rely on the original field names.

Forwarding Windows DNS Server events to Microsoft Sentinel

In this example NXLog collects DNS Server logs via ETW from the Microsoft-Windows-DNSServer provider using the Event Tracing for Windows (im_etw) module. Although this Azure Sentinel data connector will collect both audit and analytical DNS Server events, analytical events are not logged by default. This example focuses on analytical events. To enable analytical event logging, open Event Viewer, navigate to Applications and Services Logs\Microsoft\Windows\DNS-Server and use View > Show Analytic and Debug Logs to change the properies of the Analytical log. For additional details, see DNS logging via ETW Providers.

In the following configuration, the Exec block contains a call to the core function host_ip() since Azure Sentinel prefers having a non-loopback IP address. Each record is converted to JSON using the to_json() procedure of the xm_json module in order to enrich the event log with the NXLog core fields.

Right after the external azure-defines.conf is included, TABLE and CAFILE are defined since they have been intentionally excluded from the generic, external om-azure.conf output instance. They will be referenced by the azure output instance once it is loaded via the next include.

nxlog.conf
<Extension _json>
    Module          xm_json
</Extension>

<Input dnsserver>
    Module          im_etw
    Provider        Microsoft-Windows-DNSServer
    <Exec>
        $HostIP = host_ip();
        to_json();
    </Exec>
</Input>

include  %INSTALLDIR%\conf\azure-defines.conf

define TABLE        NXLog_DNS_Server
define CAFILE       %CERTDIR%\ca-certificates.crt

include  %INSTALLDIR%\conf\om-azure.conf

<Route r1>
    Path            dnsserver => azure
</Route>

The follow Windows DNS Server analytical event was parsed by the im_etw module and converted to JSON before it was sent to Azure Sentinel.

Sample DNS Server analytical event
{
  "SourceName": "Microsoft-Windows-DNSServer",
  "ProviderGuid": "{EB79061A-A566-4698-9119-3ED2807060E7}",
  "EventID": 261,
  "Version": 0,
  "ChannelID": 16,
  "OpcodeValue": 0,
  "TaskValue": 2,
  "Keywords": "9223372036854775840",
  "EventTime": "2021-10-05T22:19:59.457092-07:00",
  "ExecutionProcessID": 2080,
  "ExecutionThreadID": 2940,
  "EventType": "INFO",
  "SeverityValue": 2,
  "Severity": "INFO",
  "Hostname": "WIN-93NOI1UVL29",
  "Domain": "NT AUTHORITY",
  "AccountName": "SYSTEM",
  "UserID": "S-1-5-18",
  "AccountType": "User",
  "Flags": "32768",
  "TCP": "0",
  "Source": "2603:1061::cd",
  "InterfaceIP": "::",
  "AA": "0",
  "AD": "0",
  "QNAME": "8c70dfcb-83e1-4959-934d-fcb4860bf1de.ods.opinsights.azure.com.",
  "QTYPE": "28",
  "XID": "39719",
  "RecursionDepth": "1",
  "Port": "0",
  "RecursionScope": ".",
  "CacheScope": "Default",
  "BufferSize": "224",
  "PacketData": "0x9B27800000010000000400012438633730646663622D383365312D343935392D393334642D666362343836306266316465036F64730A6F70696E73696768747305617A75726503636F6D00001C0001C031000200010000012C0013066E73312D303109617A7572652D646E73C046C04F000200010000012C0016066E73322D303109617A7572652D646E73036E657400C04F000200010000012C0016066E73332D303109617A7572652D646E73036F726700C04F000200010000012C0017066E73342D303109617A7572652D646E7304696E666F0000002904D0000080000000",
  "AdditionalInfo": ".",
  "GUID": "{18F714C3-99C7-41EA-A194-15C7EA1EF5EB}",
  "EventReceivedTime": "2021-10-05T22:20:00.460347-07:00",
  "SourceModuleName": "dnsserver",
  "SourceModuleType": "im_etw",
  "HostIP": "192.168.1.36"
}

The most basic query in the Azure Sentinel query editor is simply the table name itself, in this case NXLog_DNS_Server_CL. Here is the same sample event after Log Analtyics ingested it and renamed the fields.

NXLog_DNS_Server_CL ingested event
NXLog_DNS_Server_CL ingested event
Figure 1. Basic query with an expanded view of the sample event

The following KQL ASimDnsMicrosoftNXLog() function serves as an additional parser for defining a table view that adheres to the Azure Sentinel DNS normalization schema. This is one of various schemas Azure Sentinel has defined in order "to enable source-agnostic analytics." With this normalized DNS parser, access to an ever-growing number of built-in analytics rules is now avialable. This KQL function uses the same NXLog_DNS_Server_CL as its data source.

ASimDnsMicrosoftNXLog.kql
// Usage Instructions:
// Paste the query below into the Log Analytics query editor.
// Click the "Save" button and select "Save as function".
// Enter "ASimDnsMicrosoftNXLog" in the "Function name" field.
// For "Legacy category:" enter "DNS Server logs".
// "Parameters" are not needed.
// Function usually takes 10-15 minutes to activate.
// You can then use this function from any other queries (e.g. ASimDnsMicrosoftNXLog | take 10).
// Reference: Using functions in Azure monitor log queries : https://docs.microsoft.com/azure/azure-monitor/log-query/functions
let ASimDnsMicrosoftNXLog = view () {
  let EventTypeTable=datatable(EventOriginalType:real,EventType:string)[
      256, 'Query'
    , 257, 'Query'
    , 258, 'Query'
    , 259, 'Query'
    , 260, 'Query'
    , 261, 'Query'
    , 262, 'Query'
    , 263, 'Dynamic update'
    , 264, 'Dynamic update'
    , 265, 'Zone XFR'
    , 266, 'Zone XFR'
    , 267, 'Zone XFR'
    , 268, 'Zone XFR'
    , 269, 'Zone XFR'
    , 270, 'Zone XFR'
    , 271, 'Zone XFR'
    , 272, 'Zone XFR'
    , 273, 'Zone XFR'
    , 274, 'Zone XFR'
    , 275, 'Zone XFR'
    , 276, 'Zone XFR'
    , 277, 'Dynamic update'
    , 278, 'Dynamic update'
    , 279, 'Query'
    , 280, 'Query'
  ];
  let EventSubTypeTable=datatable(EventOriginalType:real,EventSubType:string)[
    256, 'request'
  , 257, 'response'
  , 258, 'response'
  , 259, 'response'
  , 260, 'request'
  , 261, 'response'
  , 262, 'response'
  , 263, 'request'
  , 264, 'response'
  , 265, 'request'
  , 266, 'request'
  , 267, 'response'
  , 268, 'response'
  , 269, 'request'
  , 270, 'request'
  , 271, 'response'
  , 272, 'response'
  , 273, 'request'
  , 274, 'request'
  , 275, 'response'
  , 276, 'response'
  , 277, 'request'
  , 278, 'response'
  , 279, 'NA'
  , 280, 'NA'
  ];
  let EventResultTable=datatable(EventOriginalType:real,EventResult:string)[
      256, 'NA'
    , 257, 'Success'
    , 258, 'Failure'
    , 259, 'Failure'
    , 260, 'NA'
    , 261, 'NA'
    , 262, 'Failure'
    , 263, 'NA'
    , 264, 'Based on RCODE'
    , 265, 'NA'
    , 266, 'NA'
    , 267, 'Based on RCODE'
    , 268, 'Based on RCODE'
    , 269, 'NA'
    , 270, 'NA'
    , 271, 'Based on RCODE'
    , 272, 'Based on RCODE'
    , 273, 'NA'
    , 274, 'NA'
    , 275, 'Success'
    , 276, 'Success'
    , 277, 'NA'
    , 278, 'Based on RCODE'
    , 279, 'NA'
    , 280, 'NA'
  ];
  let RCodeTable=datatable(DnsResponseCode:int,ResponseCodeName:string)[
      0,'NOERROR'
    , 1,'FORMERR'
    , 2,'SERVFAIL'
    , 3,'NXDOMAIN'
    , 4,'NOTIMP'
    , 5,'REFUSED'
    , 6,'YXDOMAIN'
    , 7,'YXRRSET'
    , 8,'NXRRSET'
    , 9,'NOTAUTH'
    , 10,'NOTZONE'
    , 11,'DSOTYPENI'
    , 16,'BADVERS'
    , 16,'BADSIG'
    , 17,'BADKEY'
    , 18,'BADTIME'
    , 19,'BADMODE'
    , 20,'BADNAME'
    , 21,'BADALG'
    , 22,'BADTRUNC'
    , 23,'BADCOOKIE'
  ];
  let QTypeTable=datatable(DnsQueryType:int,QTypeName:string)[
      0, 'Reserved'
    , 1, 'A'
    , 2, 'NS'
    , 3, 'MD'
    , 4, 'MF'
    , 5, 'CNAME'
    , 6, 'SOA'
    , 7, 'MB'
    , 8 ,'MG'
    , 9 ,'MR'
    , 10,'NULL'
    , 11,'WKS'
    , 12,'PTR'
    , 13,'HINFO'
    , 14,'MINFO'
    , 15,'MX'
    , 16,'TXT'
    , 17,'RP'
    , 18,'AFSDB'
    , 19,'X25'
    , 20,'ISDN'
    , 21,'RT'
    , 22,'NSAP'
    , 23,'NSAP-PTR'
    , 24,'SIG'
    , 25,'KEY'
    , 26,'PX'
    , 27,'GPOS'
    , 28,'AAAA'
    , 29,'LOC'
    , 30,'NXT'
    , 31,'EID'
    , 32,'NIMLOC'
    , 33,'SRV'
  ];
  NXLog_DNS_Server_CL
  | where EventID_d < 281
  | project-rename
      DnsFlags=Flags_s,
      DnsQuery=QNAME_s,
      DnsQueryType=QTYPE_s,
      DnsResponseCode=RCODE_s,
      DnsResponseName=PacketData_s,
      Dvc=Hostname_s,
      DvcIpAddr=HostIP_s,
      EventOriginalType=EventID_d,
      EventOriginalUid=GUID_g,
      EventStartTime=EventTime_t,
      SrcPortNumber=Port_s,
      SrcIpAddr=Source_s
  | extend
      DnsQuery=trim_end(".",DnsQuery),
      DnsQueryType=toint(DnsQueryType),
      DnsResponseCode=toint(DnsResponseCode),
      DvcHostname=Dvc,
      EventEndTime=EventStartTime,
      EventProduct="Microsoft DNS Server",
      EventSchemaVersion="0.1.1",
      EventVendor="Microsoft",
      NetworkProtocol=iff(TCP_s == "0","UDP","TCP"),
      TransactionIdHex=tohex(toint(XID_s))
  | lookup EventTypeTable on EventOriginalType
  | lookup EventSubTypeTable on EventOriginalType
  | lookup EventResultTable on EventOriginalType
  | lookup RCodeTable on DnsResponseCode
  | lookup QTypeTable on DnsQueryType
  | extend
      EventResultDetails = case (isnotempty(ResponseCodeName), ResponseCodeName
        , DnsResponseCode between (3841 .. 4095), 'Reserved for Private Use'
        , 'Unassigned')
  | extend
      Domain=DnsQuery,
      DnsResponseCodeName=EventResultDetails,
      DnsQueryTypeName = case (isnotempty(QTypeName), QTypeName
        , DnsQueryType between (66 .. 98), 'Unassigned'
        , DnsQueryType between (110 .. 248), 'Unassigned'
        , DnsQueryType between (261 .. 32767), 'Unassigned'
        , 'Unassigned'),
      EventResult=iff (DnsResponseCode == 0 and EventResult == 'Informational','Success',EventResult)
  | project-away
      AA_s,
      AD_s,
      AdditionalInfo_s,
      BufferSize_s,
      AccountName_s,
      AccountType_s,
      CacheScope_s,
      ChannelID_d,
      Destination_s,
      DNSSEC_s,
      Domain_s,
      ElapsedTime_s,
      EventReceivedTime_t,
      EventType_s,
      ExecutionProcessID_d,
      ExecutionThreadID_d,
      InterfaceIP_s,
      Keywords_s,
      OpcodeValue_d,
      PolicyName_s,
      ProviderGuid_g,
      QXID_s,
      RD_s,
      Reason_s,
      RecursionDepth_s,
      RecursionScope_s,
      ResponseCodeName,
      Scope_s,
      Severity_s,
      SeverityValue_d,
      SourceModuleName_s,
      SourceModuleType_s,
      SourceName_s,
      TaskValue_d,
      TCP_s,
      UserID_s,
      Version_d,
      XID_s,
      Zone_s
};
ASimDnsMicrosoftNXLog();

Here are some simple aggregation queries that use this schema.

DNS Server top 5 Event IDs
Figure 2. Query results of the top 5 Event IDs rendered as a pie chart
DNS Server top 5 host lookus
Figure 3. Query results of the top 5 host lookups rendered as a pie chart
DNS Server EPS time chart
Figure 4. Query results of EPS plotted as a time chart

Forwarding Linux audit events to Microsoft Sentinel

In this example the NXLog Linux Audit System im_linuxaudit module is used as the event source. The xm_resolver module is needed for the ResolveValues directive in the LinuxAudit input instance, where it is used for resolving some of the numeric values to human readable string values.

The LinuxAudit input instance gets its ruleset from an external rules file that includes rules for monitoring changes to DNS zone files. The Exec block contains a call to the core function hostname() since the Linux Audit system does not include a hostname field by default. This creates a new $Hostname field. The to_json() procedure of the xm_json module is also invoked to format the events as JSON records and enrich them with the NXLog core fields.

Right after the external azure-defines.conf is included, TABLE and CAFILE are defined since they have been intentionally excluded from the generic, external om-azure.conf output instance. They will be referenced by the azure output instance once it is loaded via the next include.

nxlog.conf
<Extension _json>
    Module          xm_json
</Extension>

<Extension _resolver>
    Module          xm_resolver
</Extension>

<Input LinuxAudit>
    Module          im_linuxaudit
    FlowControl     FALSE
    LoadRule        /opt/nxlog/etc/im_linuxaudit.rules
    ResolveValues   TRUE
    <Exec>
        $Hostname = hostname();
        to_json();
    </Exec>
</Input>

define WORKSPACE    DUMMY_WORKSPACE
define SHAREDKEY    DUMMY_SHAREDKEY
define SUBDOMAIN    ods.opinsights.azure.com
define RESOURCE     api/logs
define APIVER       api-version=2016-04-01

define TABLE        LinuxAudit
define CAFILE       /etc/ssl/certs/ca-certificates.crt

<Output azure>
    Module          om_azure
    URL             https://%WORKSPACE%.%SUBDOMAIN%/%RESOURCE%?%APIVER%
    WorkspaceID     %WORKSPACE%
    SharedKey       %SHAREDKEY%
    TableName       "%TABLE%"
    HTTPSCAFile     %CAFILE%
</Output>

<Route r1>
    Path            LinuxAudit => azure
</Route>
A sample Linux Audit event before it is sent to Microsoft Sentinel
{
  "type": "PATH",
  "time": "2021-04-28T04:04:04.030000+00:00",
  "seq": 2582,
  "item": 0,
  "name": "/etc/bind/zones/db.example.com",
  "inode": 524363,
  "dev": "fc:02",
  "mode": "file,644",
  "ouid": "root",
  "ogid": "bind",
  "rdev": 0,
  "nametype": "NORMAL",
  "cap_fp": 0,
  "cap_fi": 0,
  "cap_fe": 0,
  "cap_fver": 0,
  "cap_frootid": "0",
  "Hostname": "u20server-1",
  "EventReceivedTime": "2021-04-28T04:04:04.048076+00:00",
  "SourceModuleName": "LinuxAudit",
  "SourceModuleType": "im_linuxaudit"
}
LinuxAudit_CL events ingested
Figure 5. Query to list the Linux Audit events of interest
LinuxAudit_CL event ingested
Figure 6. An expanded view of the fields for the same event shown in the JSON sample above

Forwarding BSM audit events from macOS to Microsoft Sentinel

NXLog supports capturing kernel audit events on macOS with the Basic Security Module Auditing (im_bsm) module. This configuration reads BSM audit events directly from the kernel via the (default) /dev/auditpipe device. Since the hostname is not included in these events, the core function hostname() is called to create a new $Hostname field.

Each record is converted to JSON using the to_json() procedure of the xm_json module in order to enrich the event log with the NXLog core fields.

Right after the external azure-defines.conf is included, TABLE and CAFILE are defined since they have been intentionally excluded from the generic, external om-azure.conf output instance. They will be referenced by the azure output instance once it is loaded via the next include.

nxlog.conf
<Extension _json>
    Module          xm_json
</Extension>

<Input BSMmacOS>
    Module          im_bsm
    DeviceFile      /dev/auditpipe
    <Exec>
        $Hostname = hostname();
        to_json();
    </Exec>
</Input>

define WORKSPACE    DUMMY_WORKSPACE
define SHAREDKEY    DUMMY_SHAREDKEY
define SUBDOMAIN    ods.opinsights.azure.com
define RESOURCE     api/logs
define APIVER       api-version=2016-04-01
define TABLE        BSMmacOS
define CAFILE       /etc/ssl/cert.pem

<Output azure>
    Module          om_azure
    URL             https://%WORKSPACE%.%SUBDOMAIN%/%RESOURCE%?%APIVER%
    WorkspaceID     %WORKSPACE%
    SharedKey       %SHAREDKEY%
    TableName       "%TABLE%"
    HTTPSCAFile     %CAFILE%
</Output>

<Route r1>
    Path            BSMmacOS => azure
</Route>
A sample macOS BSM audit event before it is sent to Azure Sentinel
{
  "TokenVersion": "11",
  "EventType": "AUE_ssauthorize",
  "EventName": "SecSrvr AuthEngine",
  "EventModifier": "",
  "EventTime": "2021-04-27 02:49:21",
  "SubjectAuditID": "4294967295",
  "SubjectUID": "root",
  "SubjectGID": "wheel",
  "SubjectRealUID": "root",
  "SubjectRealGID": "wheel",
  "SubjectPID": "5515",
  "SubjectSID": "100019",
  "SubjectTerminal": "",
  "SubjectTerminal.Port": "1:22136",
  "SubjectTerminal.Host": "0.0.0.0",
  "Text": "com.apple.ServiceManagement.daemons.modify",
  "ReturnErrno": "success",
  "ReturnRetval": "0",
  "Identity": "",
  "Identity.SignerType": "1",
  "Identity.SignerId": "com.apple.authd",
  "Identity.SignerIdTruncated": "0",
  "Identity.TeamId": "",
  "Identity.TeamIdTruncated": "0",
  "Identity.CDHash": "0x46e7a142b069906a06813cc80c6b024e0580a656",
  "TrailerCount": "210",
  "EventReceivedTime": "2021-04-27T02:49:21.210462-05:00",
  "SourceModuleName": "BSMmacOS",
  "SourceModuleType": "im_bsm",
  "Hostname": "mm23a"
}
BSMmacOS_CL events ingested
Figure 7. Query to list the macOS BSM audit events of interest
BSMmacOS_CL event ingested
Figure 8. An expanded view of the fields for the same event shown in the JSON sample above (part 1)
BSMmacOS_CL event ingested
Figure 9. An expanded view of the fields for the same event shown in the JSON sample above (part 2)

Forwarding AIX audit events to Azure Sentinel

NXLog supports capturing AIX audit events directly from the AIX Audit Subsystem using the AIX auditing (im_aixaudit) input module. This configuration uses the default settings for the DeviceFile directive to collect events streaming from /dev/audit. Since Azure Sentinel’s analytics needs each event’s origin, i.e., its hostname and IP address, the core function hostname_fqdn() is called to determine the AIX host’s fully qualified domain name (FQDN), while the host_ip() returns the IP address.

Each record is converted to JSON using the to_json() procedure of the xm_json module in order to enrich the event log with the NXLog core fields.

Right after the external azure-defines.conf is included, TABLE and CAFILE are defined since they have been intentionally excluded from the generic, external om-azure.conf output instance. They will be referenced by the azure output instance once it is loaded via the next include.

nxlog.conf
<Extension _json>
    Module          xm_json
</Extension>

<Input aixaudit>
    Module          im_aixaudit
    DeviceFile      /dev/audit
    <Exec>
        $Hostname = hostname_fqdn();
        $MessageSourceAddress = host_ip();
        to_json();
    </Exec>
</Input>

include  /opt/nxlog/etc/azure-defines.conf

define TABLE        AIX_Audit
define CAFILE       %INSTALLDIR%/etc/cert.pem

include  /opt/nxlog/etc/om-azure.conf
A sample AIX audit event before it is sent to Azure Sentinel
{
  "Verbose": "filename /audit/tempfile.04718774",
  "Status": 0,
  "EventType": "FILE_Unlink",
  "Command": "compress",
  "PID": 9961666,
  "ParentPID": 4718774,
  "Thread": 38862985,
  "EventTime": "2021-09-09T11:33:01.379327-04:00",
  "Login": "root",
  "Real": "builder",
  "LoginUID": 0,
  "RealUID": 206,
  "WPARkey": 0,
  "WPARname": "Global",
  "EventReceivedTime": "2021-09-09T11:33:01.605055-04:00",
  "SourceModuleName": "aixaudit",
  "SourceModuleType": "im_aixaudit",
  "Hostname": "p1220-pvm1.p1220.cecc.ihost.com",
  "MessageSourceAddress": "10.10.0.3"
}
AIX_Audit_CL events ingested
Figure 10. Query to list the AIX audit events after Azure Sentinel has ingested them

The following KQL NXLog_parsed_AIX_Audit_view() function was created to wrap the default AIX_Audit_CL query and use the project-rename operator to restore the fields back to their original names prior to ingestion. Any fields having an Azure Sentinel Information Model (ASIM) equivalent are renamed accordingly so that the ingested logs can be better integrated with Azure Sentinel analytics.

nxlog-parsed-aix-audit-view.kql
let NXLog_parsed_AIX_Audit_view = view () {
  AIX_Audit_CL
  | project-rename
    CommandLine=Command_s,
    EventReceivedTime=EventReceivedTime_t,
    EventEndTime=EventTime_t,
    EventType=EventType_s,
    DvcHostname=Hostname_s,
    Username=Login_s,
    UserId=LoginUID_d,
    MessageSourceAddress=MessageSourceAddress_s,
    ParentProcessId=ParentPID_d,
    ProcessId=PID_d,
    RealUsername=Real_s,
    RealUserId=RealUID_d,
    SourceModuleName=SourceModuleName_s,
    SourceModuleType=SourceModuleType_s,
    EventResultDetails=Status_d,
    Thread=Thread_d,
    Verbose=Verbose_s,
    WPARkey=WPARkey_d,
    WPARname=WPARname_s
};
NXLog_parsed_AIX_Audit_view();

After saving the NXLog_Parsed_AIX_Audit_view() function (shown above) as a user-defined function, it can be used like a regular SQL view:

Using the NXLog_Parsed_AIX_Audit_view() function as a "view"
Figure 11. Calling the NXLog_parsed_AIX_Audit_view() function

When these field names are compared with the original ingested field names, it is clear that the KQL function effectively renamed them back to the original parsed field names.

The following examples illustrate some of the KQL aggregation queries that can be created for analyzing AIX audit logs.

AIX audit EventType distribution
Figure 12. Query results of AIX audit EventType distribution rendered as a pie chart
Running the stored query "Highest EPS by AIX EventType"
Figure 13. Highest EPS by AIX EventType
AIX Audit events per second (EPS) - All event types
Figure 14. Time chart of AIX Audit events per second (EPS) over a specific time span
Disclaimer

While we endeavor to keep the information in this topic up to date and correct, NXLog makes no representations or warranties of any kind, express or implied about the completeness, accuracy, reliability, suitability, or availability of the content represented here. We update our screenshots and instructions on a best-effort basis.

The accurateness of the content was tested and proved to be working in our lab environment at the time of the last revision with the following software versions:

NXLog 5.3.6735
AIX 7.1 TL 5 SP 7
Apple macOS 11.2.3 (Big Sur)
Ubuntu 20.04 LTS
Windows Server 2019

Last revision: 19 August 2021