diff --git a/filebeat/docs/fields.asciidoc b/filebeat/docs/fields.asciidoc index 055cf62d30b4..a2ccac638535 100644 --- a/filebeat/docs/fields.asciidoc +++ b/filebeat/docs/fields.asciidoc @@ -3911,6 +3911,17 @@ elasticsearch Module +*`elasticsearch.node.id`*:: ++ +-- +type: keyword + +example: DSiWcTyeThWtUXLB9J0BMw + +ID of the node + +-- + *`elasticsearch.node.name`*:: + -- @@ -3972,7 +3983,7 @@ The layer from which this event originated: rest, transport or ip_filter -- -*`elasticsearch.audit.origin_type`*:: +*`elasticsearch.audit.origin.type`*:: + -- type: keyword @@ -3983,7 +3994,7 @@ Where the request originated: rest (request originated from a REST API request), -- -*`elasticsearch.audit.realm`*:: +*`elasticsearch.audit.user.realm`*:: + -- type: keyword @@ -3992,7 +4003,7 @@ The authentication realm -- -*`elasticsearch.audit.roles`*:: +*`elasticsearch.audit.user.roles`*:: + -- type: keyword @@ -4036,33 +4047,6 @@ The type of request that was executed -- -*`elasticsearch.audit.event_type`*:: -+ --- -type: alias - -alias to: event.type - --- - -*`elasticsearch.audit.origin_address`*:: -+ --- -type: alias - -alias to: source.ip - --- - -*`elasticsearch.audit.uri`*:: -+ --- -type: alias - -alias to: url.original - --- - *`elasticsearch.audit.request_body`*:: + -- @@ -4072,15 +4056,6 @@ alias to: http.request.body.content -- -*`elasticsearch.audit.principal`*:: -+ --- -type: alias - -alias to: user.name - --- - [float] == deprecation fields diff --git a/filebeat/module/elasticsearch/_meta/fields.yml b/filebeat/module/elasticsearch/_meta/fields.yml index a22b241f68d9..3bfea37be8e0 100644 --- a/filebeat/module/elasticsearch/_meta/fields.yml +++ b/filebeat/module/elasticsearch/_meta/fields.yml @@ -7,6 +7,10 @@ type: group description: > fields: + - name: node.id + description: "ID of the node" + example: "DSiWcTyeThWtUXLB9J0BMw" + type: keyword - name: node.name description: "Name of the node" example: "vWNJsZ3" diff --git a/filebeat/module/elasticsearch/audit/_meta/fields.yml b/filebeat/module/elasticsearch/audit/_meta/fields.yml index 46e0b0530a02..4012e8e78a56 100644 --- a/filebeat/module/elasticsearch/audit/_meta/fields.yml +++ b/filebeat/module/elasticsearch/audit/_meta/fields.yml @@ -6,15 +6,15 @@ description: "The layer from which this event originated: rest, transport or ip_filter" example: "rest" type: keyword - - name: origin_type + - name: origin.type description: "Where the request originated: rest (request originated from a REST API request), transport (request was received on the transport channel), local_node (the local node issued the request)" example: "local_node" type: keyword - - name: realm + - name: user.realm description: "The authentication realm" example": "active_directory" type: keyword - - name: roles + - name: user.roles description: "Roles to which the principal belongs" example: [ "kibana_user", "beats_admin" ] type: keyword @@ -30,23 +30,7 @@ description: "The type of request that was executed" example: "ClearScrollRequest" type: keyword - - name: event_type - type: alias - path: event.type - migration: true - - name: origin_address - type: alias - path: source.ip - migration: true - - name: uri - type: alias - path: url.original - migration: true - name: request_body type: alias path: http.request.body.content migration: true - - name: principal - type: alias - path: user.name - migration: true diff --git a/filebeat/module/elasticsearch/audit/ingest/pipeline-json.json b/filebeat/module/elasticsearch/audit/ingest/pipeline-json.json new file mode 100644 index 000000000000..1764a45cf9e4 --- /dev/null +++ b/filebeat/module/elasticsearch/audit/ingest/pipeline-json.json @@ -0,0 +1,137 @@ +{ + "description": "Pipeline for parsing elasticsearch audit logs in JSON format", + "processors": [ + { + "json": { + "field": "message", + "target_field": "elasticsearch.audit" + } + }, + { + "dot_expander": { + "field": "event.action", + "path": "elasticsearch.audit" + } + }, + { + "rename": { + "field": "elasticsearch.audit.event.action", + "target_field": "event.action" + } + }, + { + "dot_expander": { + "field": "event.type", + "path": "elasticsearch.audit" + } + }, + { + "rename": { + "field": "elasticsearch.audit.event.type", + "target_field": "elasticsearch.audit.layer" + } + }, + { + "dot_expander": { + "field": "origin.address", + "path": "elasticsearch.audit" + } + }, + { + "grok": { + "field": "elasticsearch.audit.origin.address", + "patterns": [ + "\\[%{IPORHOST:source.ip}\\]:%{INT:source.port:int}", + "%{IPORHOST:source.ip}:%{INT:source.port:int}" + ] + } + }, + { + "remove": { + "field": "elasticsearch.audit.origin.address" + } + }, + { + "dot_expander": { + "field": "url.path", + "path": "elasticsearch.audit" + } + }, + { + "dot_expander": { + "field": "url.query", + "path": "elasticsearch.audit" + } + }, + { + "set": { + "if": "ctx.elasticsearch.audit?.url?.path != null && ctx.elasticsearch.audit?.url?.query == null", + "field": "url.original", + "value": "{{elasticsearch.audit.url.path}}" + } + }, + { + "set": { + "if": "ctx.elasticsearch.audit?.url?.path != null && ctx.elasticsearch.audit?.url?.query != null", + "field": "url.original", + "value": "{{elasticsearch.audit.url.path}}?{{elasticsearch.audit.url.query}}" + } + }, + { + "remove": { + "if": "ctx.elasticsearch.audit?.url?.path != null", + "field": "elasticsearch.audit.url.path" + } + }, + { + "remove": { + "if": "ctx.elasticsearch.audit?.url?.query != null", + "field": "elasticsearch.audit.url.query" + } + }, + { + "dot_expander": { + "field": "node.id", + "path": "elasticsearch.audit" + } + }, + { + "dot_expander": { + "field": "node.name", + "path": "elasticsearch.audit" + } + }, + { + "rename": { + "field": "elasticsearch.audit.node", + "target_field": "elasticsearch.node" + } + }, + { + "dot_expander": { + "field": "user.name", + "path": "elasticsearch.audit" + } + }, + { + "rename": { + "field": "elasticsearch.audit.user.name", + "target_field": "user.name" + } + }, + { + "dot_expander": { + "field": "audit.@timestamp", + "path": "elasticsearch" + } + } + ], + "on_failure": [ + { + "set": { + "field": "error.message", + "value": "{{ _ingest.on_failure_message }}" + } + } + ] +} diff --git a/filebeat/module/elasticsearch/audit/ingest/pipeline-plaintext.json b/filebeat/module/elasticsearch/audit/ingest/pipeline-plaintext.json new file mode 100644 index 000000000000..8e12a69a4381 --- /dev/null +++ b/filebeat/module/elasticsearch/audit/ingest/pipeline-plaintext.json @@ -0,0 +1,63 @@ +{ + "description": "Pipeline for parsing elasticsearch audit logs in plaintext format", + "processors": [ + { + "grok": { + "field": "message", + "pattern_definitions": { + "ES_TIMESTAMP": "\\[%{TIMESTAMP_ISO8601:elasticsearch.audit.@timestamp}\\]", + "ES_NODE_NAME": "(\\[%{DATA:elasticsearch.node.name}\\])?", + "ES_AUDIT_LAYER": "\\[%{WORD:elasticsearch.audit.layer}\\]", + "ES_AUDIT_EVENT_TYPE": "\\[%{WORD:event.type}\\]", + "ES_AUDIT_ORIGIN_TYPE": "(origin_type\\=\\[%{WORD:elasticsearch.audit.origin.type}\\])?", + "ES_AUDIT_ORIGIN_ADDRESS": "(origin_address\\=\\[%{IPORHOST:source.ip}\\])?", + "ES_AUDIT_PRINCIPAL": "(principal\\=\\[%{WORD:user.name}\\])?", + "ES_AUDIT_REALM": "(realm\\=\\[%{WORD:elasticsearch.audit.user.realm}\\])?", + "ES_AUDIT_ROLES": "(roles\\=\\[%{DATA:elasticsearch.audit.user.roles}\\])?", + "ES_AUDIT_ACTION": "(action\\=\\[%{DATA:elasticsearch.audit.action}(\\[%{DATA:elasticsearch.audit.sub_action}\\])?\\])?", + "ES_AUDIT_URI": "(uri=\\[%{DATA:url.original}\\])?", + "ES_AUDIT_INDICES": "(indices\\=\\[%{DATA:elasticsearch.audit.indices}\\])?", + "ES_AUDIT_REQUEST": "(request\\=\\[%{WORD:elasticsearch.audit.request}\\])?", + "ES_AUDIT_REQUEST_BODY": "(request_body\\=\\[%{DATA:http.request.body.content}\\])?" + }, + "patterns": [ + "%{ES_TIMESTAMP}\\s*%{ES_NODE_NAME}\\s*%{ES_AUDIT_LAYER}\\s*%{ES_AUDIT_EVENT_TYPE}\\s*%{ES_AUDIT_ORIGIN_TYPE},?\\s*%{ES_AUDIT_ORIGIN_ADDRESS},?\\s*%{ES_AUDIT_PRINCIPAL},?\\s*%{ES_AUDIT_REALM},?\\s*%{ES_AUDIT_ROLES},?\\s*%{ES_AUDIT_ACTION},?\\s*%{ES_AUDIT_INDICES},?\\s*%{ES_AUDIT_URI},?\\s*%{ES_AUDIT_REQUEST},?\\s*%{ES_AUDIT_REQUEST_BODY},?" + ] + } + }, + { + "split": { + "field": "elasticsearch.audit.user.roles", + "separator": ",", + "ignore_missing": true + } + }, + { + "split": { + "field": "elasticsearch.audit.indices", + "separator": ",", + "ignore_missing": true + } + }, + { + "script": { + "lang": "painless", + "source": "if (ctx.elasticsearch.audit.sub_action != null) { ctx.elasticsearch.audit.action += '[' + ctx.elasticsearch.audit.sub_action + ']' }" + } + }, + { + "remove": { + "field": "elasticsearch.audit.sub_action", + "ignore_missing": true + } + } + ], + "on_failure": [ + { + "set": { + "field": "error.message", + "value": "{{ _ingest.on_failure_message }}" + } + } + ] +} diff --git a/filebeat/module/elasticsearch/audit/ingest/pipeline.json b/filebeat/module/elasticsearch/audit/ingest/pipeline.json index 9c97beb45741..25d5c464d7dc 100644 --- a/filebeat/module/elasticsearch/audit/ingest/pipeline.json +++ b/filebeat/module/elasticsearch/audit/ingest/pipeline.json @@ -10,56 +10,29 @@ { "grok": { "field": "message", - "pattern_definitions": { - "ES_TIMESTAMP": "\\[%{TIMESTAMP_ISO8601:elasticsearch.audit.timestamp}\\]", - "ES_NODE_NAME": "(\\[%{DATA:elasticsearch.node.name}\\])?", - "ES_AUDIT_LAYER": "\\[%{WORD:elasticsearch.audit.layer}\\]", - "ES_AUDIT_EVENT_TYPE": "\\[%{WORD:event.type}\\]", - "ES_AUDIT_ORIGIN_TYPE": "(origin_type\\=\\[%{WORD:elasticsearch.audit.origin_type}\\])?", - "ES_AUDIT_ORIGIN_ADDRESS": "(origin_address\\=\\[%{IPORHOST:source.ip}\\])?", - "ES_AUDIT_PRINCIPAL": "(principal\\=\\[%{WORD:user.name}\\])?", - "ES_AUDIT_REALM": "(realm\\=\\[%{WORD:elasticsearch.audit.realm}\\])?", - "ES_AUDIT_ROLES": "(roles\\=\\[%{DATA:elasticsearch.audit.roles}\\])?", - "ES_AUDIT_ACTION": "(action\\=\\[%{DATA:elasticsearch.audit.action}(\\[%{DATA:elasticsearch.audit.sub_action}\\])?\\])?", - "ES_AUDIT_URI": "(uri=\\[%{DATA:url.original}\\])?", - "ES_AUDIT_INDICES": "(indices\\=\\[%{DATA:elasticsearch.audit.indices}\\])?", - "ES_AUDIT_REQUEST": "(request\\=\\[%{WORD:elasticsearch.audit.request}\\])?", - "ES_AUDIT_REQUEST_BODY": "(request_body\\=\\[%{DATA:http.request.body.content}\\])?" - }, "patterns": [ - "%{ES_TIMESTAMP}\\s*%{ES_NODE_NAME}\\s*%{ES_AUDIT_LAYER}\\s*%{ES_AUDIT_EVENT_TYPE}\\s*%{ES_AUDIT_ORIGIN_TYPE},?\\s*%{ES_AUDIT_ORIGIN_ADDRESS},?\\s*%{ES_AUDIT_PRINCIPAL},?\\s*%{ES_AUDIT_REALM},?\\s*%{ES_AUDIT_ROLES},?\\s*%{ES_AUDIT_ACTION},?\\s*%{ES_AUDIT_INDICES},?\\s*%{ES_AUDIT_URI},?\\s*%{ES_AUDIT_REQUEST},?\\s*%{ES_AUDIT_REQUEST_BODY},?" - ] - } - }, - { - "split": { - "field": "elasticsearch.audit.roles", - "separator": ",", - "ignore_missing": true - } - }, - { - "split": { - "field": "elasticsearch.audit.indices", - "separator": ",", - "ignore_missing": true + "^%{CHAR:first_char}" + ], + "pattern_definitions": { + "CHAR": "." + } } }, { - "script": { - "lang": "painless", - "source": "if (ctx.elasticsearch.audit.sub_action != null) { ctx.elasticsearch.audit.action += '[' + ctx.elasticsearch.audit.sub_action + ']' }" + "pipeline": { + "if": "ctx.first_char != '{'", + "name": "{< IngestPipeline "pipeline-plaintext" >}" } }, { - "remove": { - "field": "elasticsearch.audit.sub_action", - "ignore_missing": true + "pipeline": { + "if": "ctx.first_char == '{'", + "name": "{< IngestPipeline "pipeline-json" >}" } }, { "date": { - "field": "elasticsearch.audit.timestamp", + "field": "elasticsearch.audit.@timestamp", "target_field": "@timestamp", "formats": [ "ISO8601" @@ -70,7 +43,14 @@ }, { "remove": { - "field": "elasticsearch.audit.timestamp" + "field": "elasticsearch.audit.@timestamp" + } + }, + { + "remove": { + "field": [ + "first_char" + ] } } ], diff --git a/filebeat/module/elasticsearch/audit/manifest.yml b/filebeat/module/elasticsearch/audit/manifest.yml index 7ccb68c7e434..677b47eb590f 100644 --- a/filebeat/module/elasticsearch/audit/manifest.yml +++ b/filebeat/module/elasticsearch/audit/manifest.yml @@ -4,10 +4,13 @@ var: - name: paths default: - /var/log/elasticsearch/*_access.log + - /var/log/elasticsearch/*_audit.log os.darwin: - /usr/local/var/lib/elasticsearch/*_access.log + - /usr/local/var/lib/elasticsearch/*_audit.log os.windows: - c:/ProgramData/Elastic/Elasticsearch/logs/*_access.log + - c:/ProgramData/Elastic/Elasticsearch/logs/*_audit.log - name: convert_timezone default: false # if ES < 6.1.0, this flag switches to false automatically when evaluating the @@ -16,5 +19,9 @@ var: version: 6.1.0 value: false -ingest_pipeline: ingest/pipeline.json +ingest_pipeline: + - ingest/pipeline.json + - ingest/pipeline-json.json + - ingest/pipeline-plaintext.json + input: config/audit.yml diff --git a/filebeat/module/elasticsearch/audit/test/test.log b/filebeat/module/elasticsearch/audit/test/test-access.log similarity index 100% rename from filebeat/module/elasticsearch/audit/test/test.log rename to filebeat/module/elasticsearch/audit/test/test-access.log diff --git a/filebeat/module/elasticsearch/audit/test/test.log-expected.json b/filebeat/module/elasticsearch/audit/test/test-access.log-expected.json similarity index 96% rename from filebeat/module/elasticsearch/audit/test/test.log-expected.json rename to filebeat/module/elasticsearch/audit/test/test-access.log-expected.json index 5511a09e38c3..76a91fdd4445 100644 --- a/filebeat/module/elasticsearch/audit/test/test.log-expected.json +++ b/filebeat/module/elasticsearch/audit/test/test-access.log-expected.json @@ -37,7 +37,7 @@ "ecs.version": "1.0.0-beta2", "elasticsearch.audit.action": "indices:data/read/scroll/clear", "elasticsearch.audit.layer": "transport", - "elasticsearch.audit.origin_type": "local_node", + "elasticsearch.audit.origin.type": "local_node", "elasticsearch.audit.request": "ClearScrollRequest", "event.dataset": "elasticsearch.audit", "event.module": "elasticsearch", @@ -87,7 +87,7 @@ "ecs.version": "1.0.0-beta2", "elasticsearch.audit.action": "cluster:monitor/main", "elasticsearch.audit.layer": "transport", - "elasticsearch.audit.origin_type": "rest", + "elasticsearch.audit.origin.type": "rest", "elasticsearch.audit.request": "MainRequest", "event.dataset": "elasticsearch.audit", "event.module": "elasticsearch", @@ -131,10 +131,10 @@ "servicelog-2019.01.07" ], "elasticsearch.audit.layer": "transport", - "elasticsearch.audit.origin_type": "transport", - "elasticsearch.audit.realm": "active_directory", + "elasticsearch.audit.origin.type": "transport", "elasticsearch.audit.request": "SearchFreeContextRequest", - "elasticsearch.audit.roles": [ + "elasticsearch.audit.user.realm": "active_directory", + "elasticsearch.audit.user.roles": [ "kibana_user", "my_custom_role_1", "foo_reader" diff --git a/filebeat/module/elasticsearch/audit/test/test-audit.log b/filebeat/module/elasticsearch/audit/test/test-audit.log new file mode 100644 index 000000000000..2c74b5cbee95 --- /dev/null +++ b/filebeat/module/elasticsearch/audit/test/test-audit.log @@ -0,0 +1,6 @@ +{"@timestamp":"2018-10-31T09:34:25,109", "node.id":"DSiWcTyeThWtUXLB9J0BMw", "event.type":"rest", "event.action":"authentication_failed", "user.name":"elastic", "origin.type":"rest", "origin.address":"[::1]:61598", "url.path":"/_xpack/security/user/beats_system/_password"} +{"@timestamp":"2018-10-31T09:34:25,207", "node.id":"DSiWcTyeThWtUXLB9J0BMw", "event.type":"rest", "event.action":"authentication_failed", "user.name":"elastic", "origin.type":"rest", "origin.address":"[::1]:61599", "url.path":"/_xpack/security/user/remote_monitoring_user/_password"} +{"@timestamp":"2018-10-31T09:35:11,428", "node.id":"DSiWcTyeThWtUXLB9J0BMw", "event.type":"transport", "event.action":"access_granted", "user.name":"_xpack_security", "user.realm":"__attach", "user.roles":["superuser"], "origin.type":"local_node", "origin.address":"127.0.0.1:9300", "action":"cluster:admin/xpack/security/realm/cache/clear", "request.name":"ClearRealmCacheRequest"} +{"@timestamp":"2018-10-31T09:35:11,430", "node.id":"DSiWcTyeThWtUXLB9J0BMw", "event.type":"transport", "event.action":"access_granted", "user.name":"_xpack_security", "user.realm":"__attach", "user.roles":["superuser"], "origin.type":"local_node", "origin.address":"127.0.0.1:9300", "action":"cluster:admin/xpack/security/realm/cache/clear[n]", "request.name":"Node"} +{"@timestamp":"2018-10-31T09:35:12,303", "node.id":"DSiWcTyeThWtUXLB9J0BMw", "event.type":"transport", "event.action":"access_granted", "user.name":"elastic", "user.realm":"reserved", "user.roles":["superuser"], "origin.type":"rest","origin.address":"[::1]:61711", "action":"cluster:admin/xpack/security/user/change_password", "request.name":"ChangePasswordRequest"} +{"@timestamp":"2018-10-31T09:35:12,314", "node.id":"DSiWcTyeThWtUXLB9J0BMw", "event.type":"transport", "event.action":"access_granted", "user.name":"_xpack_security", "user.realm":"__attach", "user.roles":["superuser"], "origin.type":"local_node", "origin.address":"127.0.0.1:9300", "action":"indices:admin/create", "request.name":"CreateIndexRequest", "indices":[".security-6"]} diff --git a/filebeat/module/elasticsearch/audit/test/test-audit.log-expected.json b/filebeat/module/elasticsearch/audit/test/test-audit.log-expected.json new file mode 100644 index 000000000000..cec4333c1016 --- /dev/null +++ b/filebeat/module/elasticsearch/audit/test/test-audit.log-expected.json @@ -0,0 +1,40 @@ +[ + { + "@timestamp": "2018-10-31T09:34:25.109Z", + "ecs.version": "1.0.0-beta2", + "elasticsearch.audit.layer": "rest", + "elasticsearch.audit.origin.type": "rest", + "elasticsearch.node.id": "DSiWcTyeThWtUXLB9J0BMw", + "event.action": "authentication_failed", + "event.dataset": "elasticsearch.audit", + "event.module": "elasticsearch", + "fileset.name": "audit", + "input.type": "log", + "log.offset": 0, + "message": "{\"@timestamp\":\"2018-10-31T09:34:25,109\", \"node.id\":\"DSiWcTyeThWtUXLB9J0BMw\", \"event.type\":\"rest\", \"event.action\":\"authentication_failed\", \"user.name\":\"elastic\", \"origin.type\":\"rest\", \"origin.address\":\"[::1]:61598\", \"url.path\":\"/_xpack/security/user/beats_system/_password\"}", + "service.type": "elasticsearch", + "source.ip": "::1", + "source.port": 61598, + "url.original": "/_xpack/security/user/beats_system/_password", + "user.name": "elastic" + }, + { + "@timestamp": "2018-10-31T09:34:25.207Z", + "ecs.version": "1.0.0-beta2", + "elasticsearch.audit.layer": "rest", + "elasticsearch.audit.origin.type": "rest", + "elasticsearch.node.id": "DSiWcTyeThWtUXLB9J0BMw", + "event.action": "authentication_failed", + "event.dataset": "elasticsearch.audit", + "event.module": "elasticsearch", + "fileset.name": "audit", + "input.type": "log", + "log.offset": 274, + "message": "{\"@timestamp\":\"2018-10-31T09:34:25,207\", \"node.id\":\"DSiWcTyeThWtUXLB9J0BMw\", \"event.type\":\"rest\", \"event.action\":\"authentication_failed\", \"user.name\":\"elastic\", \"origin.type\":\"rest\", \"origin.address\":\"[::1]:61599\", \"url.path\":\"/_xpack/security/user/remote_monitoring_user/_password\"}", + "service.type": "elasticsearch", + "source.ip": "::1", + "source.port": 61599, + "url.original": "/_xpack/security/user/remote_monitoring_user/_password", + "user.name": "elastic" + } +] \ No newline at end of file diff --git a/filebeat/module/elasticsearch/fields.go b/filebeat/module/elasticsearch/fields.go index 013167c476eb..a0ccb2cd6306 100644 --- a/filebeat/module/elasticsearch/fields.go +++ b/filebeat/module/elasticsearch/fields.go @@ -32,5 +32,5 @@ func init() { // AssetElasticsearch returns asset data. // This is the base64 encoded gzipped contents of module/elasticsearch. func AssetElasticsearch() string { - return "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" + return "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" }