Event accounting service

Requires EVA ICS Enterprise.

Functionality

The service provides node-local or cloud-wide event accounting. Accounting measures the resources used (modified) by a user during access, such as unit actions, lmacro executions, file management operations etc.

The audit trail contains also failed authentication attempts, unauthorized API usage, critical system messages sent by the core or services themselves.

Cloud-wide accounting

For cloud-wide accounting the local accounting lvar must be replicated between nodes.

Supported storages

A system can have multiple event storages using multiple service instances. In such configuration only one service instance should have accounting_lvar_oid set in its configuration. Other services MUST NOT handle the lvar and work as data processors only.

Note

As ELK is commonly used to perform analysis using internal and 3rd party-compatible tools, no querying is supported for Elasticsearch storage kind.

Sending events from custom services

See Accounting.

Querying events from command-line

“accounting query” command of eva-shell provides command-line interface to view event audit trail. Use command arguments to apply required filters.

eva accounting query

Querying events using web UI

Event audit trail can be viewed in Node system dashboard, section “Events”:

SDash Event Audit Trail

Setup

Use the template EVA_DIR/share/svc-tpl/svc-tpl-aaa-accounting.yml:

# Accounting service
command: svc/eva-aaa-accounting
bus:
  path: var/bus.ipc
config:
  # lvar OID to store accounting info, super-group name MUST match node name,
  # ${system_name} is automatically parsed
  #
  # There should be only once accounting service instance deployed with the same
  # accounting_lvar_oid
  #
  # If lvar OID is not specified, the service does not accept bus accounting
  # frames but can be used to store them e.g. in a different storage
  #
  # The lvar is created by the service automatically
  #
  # Important: make sure no regular users have access to accounting lvar
  accounting_lvar_oid: "lvar:${system_name}/accounting"
  # Subscription mask, must match chosen lvar OID
  # If not specified, the service does not write any events into the database
  subscribe_accounting_lvar_oids: lvar:+/accounting
  # Accounting database, default: runtime/svc_data/<SVC_ID>/accounting.db
  #db: sqlite:///tmp/accounting.db
  #db: postgres://USER:PASSWORD@HOST/DB
  #db: elasticsearch
  #
  # Elasticsearch requires configuration section:
  #elasticsearch:
    #index: eva-accounting
    # for cloud set url to cloud id (cluster_name:...)
    #url: http://localhost:9200
    #
    # authentication (optional, required for cloud), use a single section only
    #auth:
      #basic:
        #login: username
        #password: secret
    #auth:
      #bearer
        #token: secret
    #auth:
      #api_key:
        #id: key_id
        #key: secret
  # Event buffer size, events are DROPPED if overflown
  event_buf_size: 16384
  # keep events (seconds), if not set: keep forever, ignored for Elasticsearch
  keep: 604800
# for PostgreSQL and Elasticsearch the service can work under "nobody"
user: eva

Create the service using eva-shell:

eva svc create eva.aaa.accounting /opt/eva4/share/svc-tpl/svc-tpl-aaa-accounting.yml

or using the bus CLI client:

cd /opt/eva4
cat DEPLOY.yml | ./bin/yml2mp | \
    ./sbin/bus ./var/bus.ipc rpc call eva.core svc.deploy -

(see eva.core::svc.deploy for more info)

EAPI methods

See EAPI commons for the common information about the bus, types, errors and RPC calls.

query

Description

Query accounting events

Parameters

required

Returns

Events matching the filter

Parameters

Name

Type

Description

Required

t_start

Time

start time (default: last 24 hours)

no

t_end

Time

end time (default: now)

no

node

String/Vec<String>

event node

no

u

String

user account name

no

src

String

source

no

svc

String

service id (substring)

no

subj

String

event subject

no

oid

String

OID affected

no

note

String

a note or its part (substring)

no

data

String

substring of data payload casted as text

no

code

i16

event error code (0 = success)

no

err

String

event error message

no

limit

i64

maximum number of events to return

no

offset

i64

number of events to skip

no

Return payload example:

[
  {
    "code": 0,
    "data": null,
    "err": null,
    "id": [
        207,
        214,
        106,
        1,
        143,
        123,
        76,
        205,
        159,
        165,
        103,
        26,
        81,
        30,
        20,
        201
    ],
    "node": "mws1",
    "note": null,
    "oid": null,
    "src": "127.0.0.1",
    "subj": "login",
    "svc": "eva.hmi.default",
    "t": 1706309851.0001848,
    "u": "admin"
  },
  {
    "code": 0,
    "data": null,
    "err": null,
    "id": [
        170,
        82,
        141,
        89,
        160,
        254,
        66,
        37,
        150,
        19,
        130,
        241,
        140,
        246,
        140,
        214
    ],
    "node": "mws1",
    "note": null,
    "oid": null,
    "src": "127.0.0.1",
    "subj": "login",
    "svc": "eva.hmi.default",
    "t": 1706309888.0001206,
    "u": "opx"
  }
]

query.count

Description

Count accounting events

Parameters

required

Returns

Number of events matching the filter, limit and offset are ignored

Parameters

Name

Type

Description

Required

t_start

Time

start time (default: last 24 hours)

no

t_end

Time

end time (default: now)

no

node

String/Vec<String>

event node

no

u

String

user account name

no

src

String

source

no

svc

String

service id (substring)

no

subj

String

event subject

no

oid

String

OID affected

no

note

String

a note or its part (substring)

no

data

String

substring of data payload casted as text

no

code

i16

event error code (0 = success)

no

err

String

event error message

no

limit

i64

maximum number of events to return

no

offset

i64

number of events to skip

no

Return payload example:

{
  "count": 2
}

query.field_aggregated

Description

Query aggregated field values

Parameters

required

Returns

Aggregated field values

Parameters

Name

Type

Description

Required

t_start

Time

start time (default: last 24 hours)

no

t_end

Time

end time (default: now)

no

field

String

Database field (node/u/src/svc/subj/oid/note/code)

yes

Return payload example:

[
    "started",
    "terminating"
]

report

Description

Send accounting event via RPC call

Parameters

required

Returns

nothing

Parameters

Name

Type

Description

Required

u

String

user account name

no

src

String

source (e.g. user’s IP address)

no

svc

String

service id (default: caller)

no

subj

String

event subject

no

oid

String

OID affected

no

data

Object

any additional information

no

note

String

a custom note

no

code

i16

event error code (0 = success)

no

err

String

event error message

no

HTTP API

The service provides certain methods via extra calls (the methods must be called e.g. as x::eva.aaa.accounting::query)

To use HTTP API methods, a user must be either a node administrator or have “log” operation permission.

query

Description

Query accunting events

Parameters

required

Returns

Events matching the filter

Parameters

Name

Type

Description

Required

k

String

valid API key/token

yes

filter

Filter structure (see query)

Record filter

no

http

POST /jrpc HTTP/1.1
accept: application/json
content-type: application/json
host: localhost:7727

{
  "id": 1,
  "jsonrpc": "2.0",
  "method": "query",
  "params": {
    "filter": {
      "node": "mws1",
      "subj": "login"
    },
    "k": "secretkey"
  }
}

curl

curl -i -X POST http://localhost:7727/jrpc -H "Accept: application/json" -H "Content-Type: application/json" --data-raw '{"id": 1, "jsonrpc": "2.0", "method": "query", "params": {"filter": {"node": "mws1", "subj": "login"}, "k": "secretkey"}}'

wget

wget -S -O- http://localhost:7727/jrpc --header="Accept: application/json" --header="Content-Type: application/json" --post-data='{"id": 1, "jsonrpc": "2.0", "method": "query", "params": {"filter": {"node": "mws1", "subj": "login"}, "k": "secretkey"}}'

httpie

echo '{
  "id": 1,
  "jsonrpc": "2.0",
  "method": "query",
  "params": {
    "filter": {
      "node": "mws1",
      "subj": "login"
    },
    "k": "secretkey"
  }
}' | http POST http://localhost:7727/jrpc Accept:application/json Content-Type:application/json

python-requests

requests.post('http://localhost:7727/jrpc', headers={'Accept': 'application/json', 'Content-Type': 'application/json'}, json={'id': 1, 'jsonrpc': '2.0', 'method': 'query', 'params': {'filter': {'node': 'mws1', 'subj': 'login'}, 'k': 'secretkey'}})

response

HTTP/1.1 200 OK
cache-control: no-cache, no-store
content-length: 1216
content-type: application/json
date: Thu, 13 Aug 2021 00:00:00 GMT
expires: 0
pragma: no-cache

{
    "id": 1,
    "jsonrpc": "2.0",
    "result": [
      {
        "code": 0,
        "data": null,
        "err": null,
        "id": [
            207,
            214,
            106,
            1,
            143,
            123,
            76,
            205,
            159,
            165,
            103,
            26,
            81,
            30,
            20,
            201
        ],
        "node": "mws1",
        "note": null,
        "oid": null,
        "src": "127.0.0.1",
        "subj": "login",
        "svc": "eva.hmi.default",
        "t": 1706309851.0001848,
        "u": "admin"
      },
      {
        "code": 0,
        "data": null,
        "err": null,
        "id": [
            170,
            82,
            141,
            89,
            160,
            254,
            66,
            37,
            150,
            19,
            130,
            241,
            140,
            246,
            140,
            214
        ],
        "node": "mws1",
        "note": null,
        "oid": null,
        "src": "127.0.0.1",
        "subj": "login",
        "svc": "eva.hmi.default",
        "t": 1706309888.0001206,
        "u": "opx"
      }
    ]
}

query.count

Description

Count accunting events

Parameters

required

Returns

Number of events matching the filter

Parameters

Name

Type

Description

Required

k

String

valid API key/token

yes

filter

Filter structure (see query)

Record filter

no

http

POST /jrpc HTTP/1.1
accept: application/json
content-type: application/json
host: localhost:7727

{
  "id": 1,
  "jsonrpc": "2.0",
  "method": "query.count",
  "params": {
    "filter": {
      "node": "mws1",
      "subj": "login"
    },
    "k": "secretkey"
  }
}

curl

curl -i -X POST http://localhost:7727/jrpc -H "Accept: application/json" -H "Content-Type: application/json" --data-raw '{"id": 1, "jsonrpc": "2.0", "method": "query.count", "params": {"filter": {"node": "mws1", "subj": "login"}, "k": "secretkey"}}'

wget

wget -S -O- http://localhost:7727/jrpc --header="Accept: application/json" --header="Content-Type: application/json" --post-data='{"id": 1, "jsonrpc": "2.0", "method": "query.count", "params": {"filter": {"node": "mws1", "subj": "login"}, "k": "secretkey"}}'

httpie

echo '{
  "id": 1,
  "jsonrpc": "2.0",
  "method": "query.count",
  "params": {
    "filter": {
      "node": "mws1",
      "subj": "login"
    },
    "k": "secretkey"
  }
}' | http POST http://localhost:7727/jrpc Accept:application/json Content-Type:application/json

python-requests

requests.post('http://localhost:7727/jrpc', headers={'Accept': 'application/json', 'Content-Type': 'application/json'}, json={'id': 1, 'jsonrpc': '2.0', 'method': 'query.count', 'params': {'filter': {'node': 'mws1', 'subj': 'login'}, 'k': 'secretkey'}})

response

HTTP/1.1 200 OK
cache-control: no-cache, no-store
content-length: 78
content-type: application/json
date: Thu, 13 Aug 2021 00:00:00 GMT
expires: 0
pragma: no-cache

{
    "id": 1,
    "jsonrpc": "2.0",
    "result": {
      "count": 2
    }
}

query.field_aggregated

Description

Query aggregated field values

Parameters

required

Returns

Aggregated field values matching the filter

Parameters

Name

Type

Description

Required

k

String

valid API key/token

yes

filter

Filter structure (see query.field_aggregated)

Record filter

yes

http

POST /jrpc HTTP/1.1
accept: application/json
content-type: application/json
host: localhost:7727

{
  "id": 1,
  "jsonrpc": "2.0",
  "method": "query.field_aggregated",
  "params": {
    "filter": {
      "field": "subj"
    },
    "k": "secretkey"
  }
}

curl

curl -i -X POST http://localhost:7727/jrpc -H "Accept: application/json" -H "Content-Type: application/json" --data-raw '{"id": 1, "jsonrpc": "2.0", "method": "query.field_aggregated", "params": {"filter": {"field": "subj"}, "k": "secretkey"}}'

wget

wget -S -O- http://localhost:7727/jrpc --header="Accept: application/json" --header="Content-Type: application/json" --post-data='{"id": 1, "jsonrpc": "2.0", "method": "query.field_aggregated", "params": {"filter": {"field": "subj"}, "k": "secretkey"}}'

httpie

echo '{
  "id": 1,
  "jsonrpc": "2.0",
  "method": "query.field_aggregated",
  "params": {
    "filter": {
      "field": "subj"
    },
    "k": "secretkey"
  }
}' | http POST http://localhost:7727/jrpc Accept:application/json Content-Type:application/json

python-requests

requests.post('http://localhost:7727/jrpc', headers={'Accept': 'application/json', 'Content-Type': 'application/json'}, json={'id': 1, 'jsonrpc': '2.0', 'method': 'query.field_aggregated', 'params': {'filter': {'field': 'subj'}, 'k': 'secretkey'}})

response

HTTP/1.1 200 OK
cache-control: no-cache, no-store
content-length: 98
content-type: application/json
date: Thu, 13 Aug 2021 00:00:00 GMT
expires: 0
pragma: no-cache

{
    "id": 1,
    "jsonrpc": "2.0",
    "result": [
      "started",
      "terminating"
    ]
}