description: Pipeline for parsing system authorisation/secure logs
processors:
- set:
    field: event.ingested
    value: '{{_ingest.timestamp}}'
- grok:
    field: message
    ignore_missing: true
    pattern_definitions:
      GREEDYMULTILINE: |-
        (.|
        )*
      TIMESTAMP: (?:%{TIMESTAMP_ISO8601}|%{SYSLOGTIMESTAMP})
    patterns:
    - '%{TIMESTAMP:system.auth.timestamp} %{SYSLOGHOST:host.hostname} %{DATA:process.name}(?:\[%{POSINT:process.pid:long}\])?:
      %{DATA:system.auth.ssh.event} %{DATA:system.auth.ssh.method} for (invalid user
      )?%{DATA:user.name} from %{IPORHOST:source.ip} port %{NUMBER:source.port:long}
      ssh2(: %{GREEDYDATA:system.auth.ssh.signature})?'
    - '%{TIMESTAMP:system.auth.timestamp} %{SYSLOGHOST:host.hostname} %{DATA:process.name}(?:\[%{POSINT:process.pid:long}\])?:
      %{DATA:system.auth.ssh.event} user %{DATA:user.name} from %{IPORHOST:source.ip}'
    - '%{TIMESTAMP:system.auth.timestamp} %{SYSLOGHOST:host.hostname} %{DATA:process.name}(?:\[%{POSINT:process.pid:long}\])?:
      Did not receive identification string from %{IPORHOST:system.auth.ssh.dropped_ip}'
    - '%{TIMESTAMP:system.auth.timestamp} %{SYSLOGHOST:host.hostname} %{DATA:process.name}(?:\[%{POSINT:process.pid:long}\])?:
      \s*%{DATA:user.name} :( %{DATA:system.auth.sudo.error} ;)? TTY=%{DATA:system.auth.sudo.tty}
      ; PWD=%{DATA:system.auth.sudo.pwd} ; USER=%{DATA:system.auth.sudo.user} ; COMMAND=%{GREEDYDATA:system.auth.sudo.command}'
    - '%{TIMESTAMP:system.auth.timestamp} %{SYSLOGHOST:host.hostname} %{DATA:process.name}(?:\[%{POSINT:process.pid:long}\])?:
      new group: name=%{DATA:group.name}, GID=%{NUMBER:group.id}'
    - '%{TIMESTAMP:system.auth.timestamp} %{SYSLOGHOST:host.hostname} %{DATA:process.name}(?:\[%{POSINT:process.pid:long}\])?:
      new user: name=%{DATA:user.name}, UID=%{NUMBER:user.id}, GID=%{NUMBER:group.id},
      home=%{DATA:system.auth.useradd.home}, shell=%{DATA:system.auth.useradd.shell}$'
    - '%{TIMESTAMP:system.auth.timestamp} %{SYSLOGHOST:host.hostname}? %{DATA:process.name}(?:\[%{POSINT:process.pid:long}\])?:
      %{GREEDYMULTILINE:system.auth.message}'
- remove:
    field: message
- rename:
    field: system.auth.message
    target_field: message
    ignore_missing: true
    if: ctx?.system?.auth?.message != null && ctx?.system?.auth?.message != ""
- grok:
    field: message
    ignore_missing: true
    ignore_failure: true
    patterns:
    - 'for user \"?%{DATA:_temp.foruser}\"? by \"?%{DATA:_temp.byuser}\"?(?:\(uid=%{NUMBER:_temp.byuid}\))?$'
    - 'for user \"?%{DATA:_temp.foruser}\"?$'
    - 'by user \"?%{DATA:_temp.byuser}\"?$'
    if: ctx?.message != null && ctx?.message != ""
- rename:
    field: _temp.byuser
    target_field: user.name
    ignore_missing: true
    ignore_failure: true
- rename:
    field: _temp.byuid
    target_field: user.id
    ignore_missing: true
    ignore_failure: true
- rename:
    field: _temp.foruser
    target_field: user.name
    ignore_missing: true
    ignore_failure: true
    if: ctx?.user?.name == null || ctx?.user?.name == ""
- rename:
    field: _temp.foruser
    target_field: user.effective.name
    ignore_missing: true
    ignore_failure: true
    if: ctx?.user?.name != null
- convert:
    field: system.auth.sudo.user
    target_field: user.effective.name
    type: string
    ignore_failure: true
    if: ctx?.system?.auth?.sudo?.user != null
- set:
    field: source.ip
    value: '{{system.auth.ssh.dropped_ip}}'
    ignore_empty_value: true
- date:
    if: ctx.event.timezone == null
    field: system.auth.timestamp
    target_field: '@timestamp'
    formats:
    - MMM  d HH:mm:ss
    - MMM dd HH:mm:ss
    - ISO8601
    on_failure:
    - append:
        field: error.message
        value: '{{ _ingest.on_failure_message }}'
- date:
    if: ctx.event.timezone != null
    field: system.auth.timestamp
    target_field: '@timestamp'
    formats:
    - MMM  d HH:mm:ss
    - MMM dd HH:mm:ss
    - ISO8601
    timezone: '{{ event.timezone }}'
    on_failure:
    - append:
        field: error.message
        value: '{{ _ingest.on_failure_message }}'
- remove:
    field: system.auth.timestamp
- geoip:
    field: source.ip
    target_field: source.geo
    ignore_failure: true
- geoip:
    database_file: GeoLite2-ASN.mmdb
    field: source.ip
    target_field: source.as
    properties:
    - asn
    - organization_name
    ignore_missing: true
- rename:
    field: source.as.asn
    target_field: source.as.number
    ignore_missing: true
- rename:
    field: source.as.organization_name
    target_field: source.as.organization.name
    ignore_missing: true
- set:
    field: event.kind
    value: event
- script:
    lang: painless
    ignore_failure: true
    source: >-
      if (ctx.system.auth.ssh.event == "Accepted") {
        ctx.event.type = ["authentication_success", "info"];
        ctx.event.category = ["authentication","session"];
        ctx.event.action = "ssh_login";
        ctx.event.outcome = "success";
      } else if (ctx.system.auth.ssh.event == "Invalid" || ctx.system.auth.ssh.event == "Failed") {
        ctx.event.type = ["authentication_failure", "info"];
        ctx.event.category = ["authentication"];
        ctx.event.action = "ssh_login";
        ctx.event.outcome = "failure";
      }

- append:
    field: event.category
    value: iam
    if: "ctx?.process?.name != null && ['groupadd', 'groupdel', 'groupmod', 'useradd', 'userdel', 'usermod'].contains(ctx.process.name)"
- set:
    field: event.outcome
    value: success
    if: "ctx?.process?.name != null && ['groupadd', 'groupdel', 'groupmod', 'useradd', 'userdel', 'usermod'].contains(ctx.process.name)"
- append:
    field: event.type
    value: user
    if: "ctx?.process?.name != null && ['useradd', 'userdel', 'usermod'].contains(ctx.process.name)"
- append:
    field: event.type
    value: group
    if: "ctx?.process?.name != null && ['groupadd', 'groupdel', 'groupmod'].contains(ctx.process.name)"
- append:
    field: event.type
    value: creation
    if: "ctx?.process?.name != null && ['useradd', 'groupadd'].contains(ctx.process.name)"
- append:
    field: event.type
    value: deletion
    if: "ctx?.process?.name != null && ['userdel', 'groupdel'].contains(ctx.process.name)"
- append:
    field: event.type
    value: change
    if: "ctx?.process?.name != null && ['usermod', 'groupmod'].contains(ctx.process.name)"
- append:
    field: related.user
    value: "{{user.name}}"
    allow_duplicates: false
    if: "ctx?.user?.name != null && ctx.user?.name != ''"
- append:
    field: related.user
    value: "{{user.effective.name}}"
    allow_duplicates: false
    if: "ctx?.user?.effective?.name != null && ctx.user?.effective?.name != ''"
- append:
    field: related.ip
    value: "{{source.ip}}"
    allow_duplicates: false
    if: "ctx?.source?.ip != null && ctx.source?.ip != ''"
- append:
    field: related.hosts
    value: "{{host.hostname}}"
    allow_duplicates: false
    if: "ctx.host?.hostname != null && ctx.host?.hostname != ''"
on_failure:
- set:
    field: error.message
    value: '{{ _ingest.on_failure_message }}'
