diff --git a/.travis.yml b/.travis.yml index 7ea410b0bfe..3bd86777f5e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -183,6 +183,7 @@ addons: - xsltproc - libxml2-utils - libsystemd-journal-dev + - librpm-dev before_install: - python --version diff --git a/CHANGELOG-developer.next.asciidoc b/CHANGELOG-developer.next.asciidoc index 0317f68c503..eca8bfdc6a1 100644 --- a/CHANGELOG-developer.next.asciidoc +++ b/CHANGELOG-developer.next.asciidoc @@ -19,6 +19,8 @@ https://github.com/elastic/beats/compare/v7.0.0-alpha2..master[Check the HEAD di The list below covers the major changes between 7.0.0-alpha2 and master only. ==== Breaking changes +- Outputs receive Index Manager as additional parameter. The index manager can + be used to create an index selector. {pull}10347[10347] ==== Bugfixes @@ -26,3 +28,6 @@ The list below covers the major changes between 7.0.0-alpha2 and master only. - Allow multiple object type configurations per field. {pull}9772[9772] - Move agent metadata addition to a processor. {pull}9952[9952] +- Add (*common.Config).Has and (*common.Config).Remove. {pull}10363[10363] +- Introduce ILM and IndexManagment support to beat.Settings. {pull}10347[10347] +- Introduce ILM and IndexManagement support to beat.Settings. {pull}10347[10347] diff --git a/CHANGELOG.next.asciidoc b/CHANGELOG.next.asciidoc index b9b2f5965b6..03dbc18888c 100644 --- a/CHANGELOG.next.asciidoc +++ b/CHANGELOG.next.asciidoc @@ -21,6 +21,13 @@ https://github.com/elastic/beats/compare/v7.0.0-alpha2...master[Check the HEAD d - Rename `process.exe` to `process.executable` in add_process_metadata to align with ECS. {pull}9949[9949] - Import ECS change https://github.com/elastic/ecs/pull/308[ecs#308]: leaf field `user.group` is now the `group` field set. {pull}10275[10275] +- Update the code of Central Management to align with the new returned format. {pull}10019[10019] +- Docker and Kubernetes labels/annotations will be "dedoted" by default. {pull}10338[10338] +- Remove --setup command line flag. {pull}10138[10138] +- Remove --version command line flag. {pull}10138[10138] +- Remove --configtest command line flag. {pull}10138[10138] +- Move output.elasticsearch.ilm settings to setup.ilm. {pull}10347[10347] +- ILM will be available by default if Elasticsearch > 7.0 is used. {pull}10347[10347] *Auditbeat* @@ -45,6 +52,7 @@ https://github.com/elastic/beats/compare/v7.0.0-alpha2...master[Check the HEAD d - Rename a few `mongodb.*` fields to map to ECS. {pull}10009[10009] - Rename a few `mysql.*` fields to map to ECS. {pull}10008[10008] - Rename a few `nginx.error.*` fields to map to ECS. {pull}10007[10007] +- Rename many `auditd.log.*` fields to map to ECS. {pull}10192[10192] - Filesets with multiple ingest pipelines added in {pull}8914[8914] only work with Elasticsearch >= 6.5.0 {pull}10001[10001] - Remove service.name from Elastcsearch module. Replace by service.type. {pull}10042[10042] - Remove numeric coercions for `user.id` and `group.id`. IDs should be `keyword`. {pull}10233[10233] @@ -58,6 +66,14 @@ https://github.com/elastic/beats/compare/v7.0.0-alpha2...master[Check the HEAD d including `http.response.elapsed_time` (ECS). {pull}10188[10188], {pull}10274[10274] - Rename multiple fields to `http.response.body.bytes`, from modules "apache", "iis", "kibana", "nginx" and "traefik", including `http.response.content_length` (ECS). {pull}10188[10188] +- Change type from haproxy.log fileset fields from text to keyword: response.captured_headers, request.captured_headers, `raw_request_line`, `mode`. {pull}10397[10397] +- Change type of field backend_url and frontend_name in traefik.access metricset to type keyword. {pull}10401[10401] +- Ingesting Elasticsearch audit logs is only supported with Elasticsearch 6.5.0 and above {pull}10352[10352] +- Migrate Elasticsearch audit logs fields to ECS {pull}10352[10352] +- Several text fields in the Logstash module are now indexed as `keyword` fields with `text` multi-fields (ECS). {pull}10417[10417] +- Several text fields in the Elasticsearch module are now indexed as `keyword` fields with `text` multi-fields (ECS). {pull}10414[10414] +- Move dissect pattern for traefik.access fileset from Filbeat to Elasticsearch. {pull}10442[10442] +- The `elasticsearch/deprecation` fileset now indexes the `component` field under `elasticsearch` instead of `elasticsearch.server`. {pull}10445[10445] *Heartbeat* @@ -73,13 +89,30 @@ https://github.com/elastic/beats/compare/v7.0.0-alpha2...master[Check the HEAD d *Metricbeat* +- Migrate system process metricset fields to ECS. {pull}10332[10332] - Refactor Prometheus metric mappings {pull}9948[9948] - Removed Prometheus stats metricset in favor of just using Prometheus collector {pull}9948[9948] - Migrate system socket metricset fields to ECS. {pull}10339[10339] - Renamed direction values in sockets to ECS recommendations, from incoming/outcoming to inbound/outbound. {pull}10339[10339] - Adjust Redis.info metricset fields to ECS. {pull}10319[10319] -- Change type of field docker.container.ip_addresses to `ip` instead of `keyword. {pull}10364[10364] +- Change type of field docker.container.ip_addresses to `ip` instead of `keyword`. {pull}10364[10364] - Rename http.request.body field to http.request.body.content. {pull}10315[10315] +- Adjust php_fpm.process metricset fields to ECS. {pull}10366[10366] +- Adjust mongodb.status metricset to to ECS. {pull}10368[10368] +- Refactor munin module to collect an event per plugin and to have more strict field mappings. `namespace` option has been removed, and will be replaced by `service.name`. {pull}10322[10322] +- Change the following fields from type text to keyword: {pull}10318[10318] + - ceph.osd_df.name + - ceph.osd_tree.name + - ceph.osd_tree.children + - kafka.consumergroup.meta + - kibana.stats.name + - mongodb.metrics.replication.executor.network_interface + - php_fpm.process.request_uri + - php_fpm.process.script +- Add `service.name` option to all modules to explicitly set `service.name` if it is unset. {pull}10427[10427] +- Update a few elasticsearch.* fields to map to ECS. {pull}10350[10350] +- Update a few logstash.* fields to map to ECS. {pull}10350[10350] +- Update a few kibana.* fields to map to ECS. {pull}10350[10350] *Packetbeat* @@ -95,6 +128,7 @@ https://github.com/elastic/beats/compare/v7.0.0-alpha2...master[Check the HEAD d - Correctly normalize Cloudformation resource name. {issue}10087[10087] - Functionbeat can now deploy a function for Kinesis. {10116}10116[10116] +- Allow functionbeat to use the keystore. {issue}9009[9009] ==== Bugfixes @@ -120,6 +154,7 @@ https://github.com/elastic/beats/compare/v7.0.0-alpha2...master[Check the HEAD d - Make elasticsearch/audit fileset be more lenient in parsing node name. {issue}10035[10035] {pull}10135[10135] - Fix bad bytes count in `docker` input when filtering by stream. {pull}10211[10211] - Fixed data types for roles and indices fields in `elasticsearch/audit` fileset {pull}10307[10307] +- Ensure `source.address` is always populated by the nginx module (ECS). {pull}10418[10418] *Heartbeat* @@ -165,6 +200,11 @@ https://github.com/elastic/beats/compare/v7.0.0-alpha2...master[Check the HEAD d - Add alias field support in Kibana index pattern. {pull}10075[10075] - Add `add_fields` processor. {pull}10119[10119] - Add Kibana field formatter to bytes fields. {pull}10184[10184] +- Document a few more `auditd.log.*` fields. {pull}10192[10192] +- Support Kafka 2.1.0. {pull}10440[10440] +- Add ILM mode `auto` to setup.ilm.enabled setting. This new default value detects if ILM is available {pull}10347[10347] +- Add support to read ILM policy from external JSON file. {pull}10347[10347] +- Add `overwrite` and `check_exists` settings to ILM support. {pull}10347[10347] *Auditbeat* @@ -172,6 +212,8 @@ https://github.com/elastic/beats/compare/v7.0.0-alpha2...master[Check the HEAD d - Add `user.id` (UID) and `user.name` for ECS. {pull}10195[10195] - Add `group.id` (GID) and `group.name` for ECS. {pull}10195[10195] - System module `process` dataset: Add user information to processes. {pull}9963[9963] +- Add system `package` dataset. {pull}10225[10225] +- Add system module `login` dataset. {pull}9327[9327] *Filebeat* @@ -190,6 +232,9 @@ https://github.com/elastic/beats/compare/v7.0.0-alpha2...master[Check the HEAD d - Teach elasticsearch/audit fileset to parse out some more fields. {issue}10134[10134] {pull}10137[10137] - Add convert_timezone to nginx module. {issue}9839[9839] {pull}10148[10148] - Add support for Percona in the `slowlog` fileset of `mysql` module. {issue}6665[6665] {pull}10227[10227] +- Added support for ingesting structured Elasticsearch audit logs {pull}10352[10352] +- Added support for ingesting structured Elasticsearch slow logs {pull}10445[10445] +- Added support for ingesting structured Elasticsearch deprecation logs {pull}10445[10445] *Heartbeat* @@ -231,11 +276,14 @@ https://github.com/elastic/beats/compare/v7.0.0-alpha2...master[Check the HEAD d - Release kvm module as beta. {pull}10279[10279] - Release http.server metricset as GA. {pull}10240[10240] - Release Nats module as GA. {pull}10281[10281] +- Release munin module as GA. {pull}10311[10311] - Release use of xpack.enabled: true flag in Elasticsearch and Kibana modules as GA. {pull}10222[10222] - Add support for MySQL 8.0 and tests also for Percona and MariaDB. {pull}10261[10261] - Rename 'db' Metricset to 'transaction_log' in MSSQL Metricbeat module {pull}10109[10109] +- Add process arguments and the path to its executable file in the system process metricset {pull}10332[10332] - Added 'server' Metricset to Zookeeper Metricbeat module {issue}8938[8938] {pull}10341[10341] - Release AWS module as GA. {pull}10345[10345] +- Add overview dashboard to Zookeeper Metricbeat module {pull}10379[10379] *Packetbeat* diff --git a/Makefile b/Makefile index 9d7122784cb..257c92ed904 100644 --- a/Makefile +++ b/Makefile @@ -16,12 +16,12 @@ XPACK_SUFFIX=x-pack/ # PROJECTS_XPACK_PKG is a list of Beats that have independent packaging support # in the x-pack directory (rather than having the OSS build produce both sets # of artifacts). This will be removed once we complete the transition. -PROJECTS_XPACK_PKG=x-pack/auditbeat x-pack/filebeat +PROJECTS_XPACK_PKG=x-pack/auditbeat x-pack/filebeat x-pack/metricbeat # PROJECTS_XPACK_MAGE is a list of Beats whose primary build logic is based in # Mage. For compatibility with CI testing these projects support a subset of the # makefile targets. After all Beats converge to primarily using Mage we can # remove this and treat all sub-projects the same. -PROJECTS_XPACK_MAGE=x-pack/metricbeat $(PROJECTS_XPACK_PKG) +PROJECTS_XPACK_MAGE=$(PROJECTS_XPACK_PKG) # # Includes diff --git a/NOTICE.txt b/NOTICE.txt index 31605f262ec..f9b2bb77267 100644 --- a/NOTICE.txt +++ b/NOTICE.txt @@ -667,8 +667,8 @@ Apache License 2.0 -------------------------------------------------------------------- Dependency: github.com/elastic/go-ucfg -Version: v0.6.5 -Revision: 92d43887f91851c9936621665af7f796f4d03412 +Version: v0.7.0 +Revision: 0539807037ce820e147797f051ff32b05f4f9288 License type (autodetected): Apache-2.0 ./vendor/github.com/elastic/go-ucfg/LICENSE: -------------------------------------------------------------------- diff --git a/auditbeat/Dockerfile b/auditbeat/Dockerfile index 25d13f15e11..905e23748d1 100644 --- a/auditbeat/Dockerfile +++ b/auditbeat/Dockerfile @@ -5,6 +5,7 @@ RUN \ && apt-get install -y --no-install-recommends \ python-pip \ virtualenv \ + librpm-dev \ && rm -rf /var/lib/apt/lists/* RUN pip install --upgrade pip diff --git a/auditbeat/_meta/common.p2.yml b/auditbeat/_meta/common.p2.yml index df462bf5873..468cc1d45a9 100644 --- a/auditbeat/_meta/common.p2.yml +++ b/auditbeat/_meta/common.p2.yml @@ -1,6 +1,6 @@ #==================== Elasticsearch template setting ========================== setup.template.settings: - index.number_of_shards: 3 + index.number_of_shards: 1 #index.codec: best_compression #_source.enabled: false diff --git a/auditbeat/auditbeat.reference.yml b/auditbeat/auditbeat.reference.yml index b042b2bb6ea..534778657a1 100644 --- a/auditbeat/auditbeat.reference.yml +++ b/auditbeat/auditbeat.reference.yml @@ -358,11 +358,6 @@ output.elasticsearch: # IPv6 addresses should always be defined as: https://[2001:db8::1]:9200 hosts: ["localhost:9200"] - # Enabled ilm (beta) to use index lifecycle management instead daily indices. - #ilm.enabled: false - #ilm.rollover_alias: "auditbeat" - #ilm.pattern: "{now/d}-000001" - # Set gzip compression level. #compression_level: 0 @@ -1009,6 +1004,25 @@ setup.template.settings: #_source: #enabled: false +#============================== Setup ILM ===================================== + +# Configure Index Lifecycle Management Index Lifecycle Management creates a +# write alias and adds additional settings to the template. +# The elasticsearch.output.index setting will be replaced with the write alias +# if ILM is enabled. + +# Enabled ILM support. Valid values are true, false, and auto. The beat will +# detect availabilty of Index Lifecycle Management in Elasticsearch and enable +# or disable ILM support. +#setup.ilm.enabled: auto + +# Configure the ILM write alias name. +#setup.ilm.rollover_alias: "auditbeat" + +# Configure rollover index pattern. +#setup.ilm.pattern: "{now/d}-000001" + + #============================== Kibana ===================================== # Starting with Beats version 6.0.0, the dashboards are loaded via the Kibana API. diff --git a/auditbeat/auditbeat.yml b/auditbeat/auditbeat.yml index 3522827d7a9..65abdbfda3e 100644 --- a/auditbeat/auditbeat.yml +++ b/auditbeat/auditbeat.yml @@ -50,7 +50,7 @@ auditbeat.modules: #==================== Elasticsearch template setting ========================== setup.template.settings: - index.number_of_shards: 3 + index.number_of_shards: 1 #index.codec: best_compression #_source.enabled: false @@ -73,7 +73,7 @@ setup.template.settings: #============================== Dashboards ===================================== # These settings control loading the sample dashboards to the Kibana index. Loading # the dashboards is disabled by default and can be enabled either by setting the -# options here, or by using the `-setup` CLI flag or the `setup` command. +# options here or by using the `setup` command. #setup.dashboards.enabled: false # The URL from where to download the dashboards archive. By default this URL @@ -121,9 +121,6 @@ output.elasticsearch: # Array of hosts to connect to. hosts: ["localhost:9200"] - # Enabled ilm (beta) to use index lifecycle management instead daily indices. - #ilm.enabled: false - # Optional protocol and basic auth credentials. #protocol: "https" #username: "elastic" diff --git a/auditbeat/docs/fields.asciidoc b/auditbeat/docs/fields.asciidoc index 0e5326065cf..0daa577744d 100644 --- a/auditbeat/docs/fields.asciidoc +++ b/auditbeat/docs/fields.asciidoc @@ -5858,16 +5858,6 @@ type: keyword Major version of the user agent. --- - -*`user_agent.device`*:: -+ --- -type: keyword - -Name of the physical device. - - -- *`user_agent.os.major`*:: @@ -5888,16 +5878,6 @@ type: long Minor version of the operating system. --- - -*`url.hostname`*:: -+ --- -type: keyword - -Hostname of the request, such as "elastic.co". - - -- [[exported-fields-file_integrity]] @@ -6175,6 +6155,28 @@ These are the fields generated by the system module. + +*`event.origin`*:: ++ +-- +type: keyword + +Origin of the event. This can be a file path (e.g. `/var/log/log.1`), or the name of the system component that supplied the data (e.g. `netlink`). + + +-- + + +*`user.terminal`*:: ++ +-- +type: keyword + +Terminal of the user. + + +-- + [float] == system.audit fields @@ -6343,6 +6345,101 @@ type: keyword The operating system's kernel version. +-- + +[float] +== package fields + +`package` contains information about an installed or removed package. + + + +*`system.audit.package.name`*:: ++ +-- +type: keyword + +Package name. + + +-- + +*`system.audit.package.version`*:: ++ +-- +type: keyword + +Package version. + + +-- + +*`system.audit.package.release`*:: ++ +-- +type: keyword + +Package release. + + +-- + +*`system.audit.package.arch`*:: ++ +-- +type: keyword + +Package architecture. + + +-- + +*`system.audit.package.license`*:: ++ +-- +type: keyword + +Package license. + + +-- + +*`system.audit.package.installtime`*:: ++ +-- +type: date + +Package install time. + + +-- + +*`system.audit.package.size`*:: ++ +-- +type: long + +Package size. + + +-- + +*`system.audit.package.summary`*:: ++ +-- +Package summary. + + +-- + +*`system.audit.package.url`*:: ++ +-- +type: keyword + +Package URL. + + -- [float] diff --git a/auditbeat/include/fields.go b/auditbeat/include/fields.go index 57d21664a6a..0e852293dae 100644 --- a/auditbeat/include/fields.go +++ b/auditbeat/include/fields.go @@ -32,5 +32,5 @@ func init() { // AssetFieldsYml returns asset data. // This is the base64 encoded gzipped contents of fields.yml. func AssetFieldsYml() string { - return "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" + return "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" } diff --git a/auditbeat/magefile.go b/auditbeat/magefile.go index 0eb532f1e72..6eb32fd91df 100644 --- a/auditbeat/magefile.go +++ b/auditbeat/magefile.go @@ -175,6 +175,7 @@ func UnitTest() { // Use TEST_COVERAGE=true to enable code coverage profiling. // Use RACE_DETECTOR=true to enable the race detector. func GoUnitTest(ctx context.Context) error { + mg.Deps(Fields) return mage.GoTest(ctx, mage.DefaultGoTestUnitArgs()) } @@ -182,6 +183,7 @@ func GoUnitTest(ctx context.Context) error { // Use TEST_COVERAGE=true to enable code coverage profiling. // Use RACE_DETECTOR=true to enable the race detector. func GoIntegTest(ctx context.Context) error { + mg.Deps(Fields) return mage.RunIntegTest("goIntegTest", func() error { return mage.GoTest(ctx, mage.DefaultGoTestIntegrationArgs()) }) diff --git a/auditbeat/module/auditd/audit_linux_test.go b/auditbeat/module/auditd/audit_linux_test.go index 65b3aaf189f..3b01c1c5122 100644 --- a/auditbeat/module/auditd/audit_linux_test.go +++ b/auditbeat/module/auditd/audit_linux_test.go @@ -24,6 +24,7 @@ import ( "io/ioutil" "os" "os/exec" + "strings" "testing" "time" @@ -32,6 +33,7 @@ import ( "github.com/prometheus/procfs" "github.com/elastic/beats/auditbeat/core" + "github.com/elastic/beats/libbeat/common" "github.com/elastic/beats/libbeat/logp" "github.com/elastic/beats/metricbeat/mb" mbtest "github.com/elastic/beats/metricbeat/mb/testing" @@ -76,7 +78,9 @@ func TestData(t *testing.T) { // Send expected ACKs for initialization returnACK().returnACK().returnACK().returnACK().returnACK(). // Send a single audit message from the kernel. - returnMessage(userLoginMsg) + returnMessage(userLoginMsg). + returnMessage(execveMsgs...). + returnMessage(acceptMsgs...) // Replace the default AuditClient with a mock. ms := mbtest.NewPushMetricSetV2(t, getConfig()) @@ -84,16 +88,45 @@ func TestData(t *testing.T) { auditMetricSet.client.Close() auditMetricSet.client = &libaudit.AuditClient{Netlink: mock} - events := mbtest.RunPushMetricSetV2(10*time.Second, 1, ms) - if len(events) == 0 { - t.Fatal("received no events") + events := mbtest.RunPushMetricSetV2(10*time.Second, 3, ms) + if len(events) != 3 { + t.Fatalf("expected 3 events, but received %d", len(events)) } assertNoErrors(t, events) + assertFieldsAreDocumented(t, events) + beatEvent := mbtest.StandardizeEvent(ms, events[0], core.AddDatasetToEvent) mbtest.WriteEventToDataJSON(t, beatEvent, "") } +// assertFieldsAreDocumented mimics assert_fields_are_documented in Python system tests. +func assertFieldsAreDocumented(t *testing.T, events []mb.Event) { + fieldsYml, err := common.LoadFieldsYaml("../../fields.yml") + if err != nil { + t.Fatal(err) + } + documentedFields := fieldsYml.GetKeys() + + for _, e := range events { + beatEvent := e.BeatEvent(moduleName, metricsetName, core.AddDatasetToEvent) + for eventFieldName := range beatEvent.Fields.Flatten() { + found := false + for _, documentedFieldName := range documentedFields { + // Have to use HasPrefix and not "==" since fields in auditd.paths.* get flattened + // to auditd.paths which does not exist in fields.yml. + if strings.HasPrefix(documentedFieldName, eventFieldName) { + found = true + break + } + } + if !found { + assert.Fail(t, "Field not documented", "Key '%v' found in event is not documented.", eventFieldName) + } + } + } +} + func getConfig() map[string]interface{} { return map[string]interface{}{ "module": "auditd", diff --git a/auditbeat/module/file_integrity/monitor/monitor_test.go b/auditbeat/module/file_integrity/monitor/monitor_test.go index 0b496c8e604..9b028bae83a 100644 --- a/auditbeat/module/file_integrity/monitor/monitor_test.go +++ b/auditbeat/module/file_integrity/monitor/monitor_test.go @@ -299,12 +299,14 @@ func testDirOps(t *testing.T, dir string, watcher Watcher) { ev, err = readTimeout(t, watcher) } - // Helper to read events ignoring a write to the parent dir, which seems - // to trigger sometimes under Windows when moving files around in a dir. - readIgnoreParent := func(t *testing.T, w Watcher) (fsnotify.Event, error) { + // Helper to read events ignoring writes. These have been observed + // under Windows in two cases: + // - Writes to the parent dir (metadata updates after update loop above?) + // - Delayed writes to "fpath" file, not discarded by above consumer loop. + readIgnoreWrites := func(t *testing.T, w Watcher) (fsnotify.Event, error) { for { ev, err := readTimeout(t, w) - if err != nil || ev.Name != dir || ev.Op != fsnotify.Write { + if err != nil || ev.Op != fsnotify.Write { return ev, err } } @@ -314,10 +316,10 @@ func testDirOps(t *testing.T, dir string, watcher Watcher) { err = os.Rename(fpath, fpath2) assertNoError(t, err) - evRename, err := readIgnoreParent(t, watcher) + evRename, err := readIgnoreWrites(t, watcher) assertNoError(t, err) - evCreate, err := readIgnoreParent(t, watcher) + evCreate, err := readIgnoreWrites(t, watcher) assertNoError(t, err) if evRename.Op != fsnotify.Rename { @@ -334,7 +336,7 @@ func testDirOps(t *testing.T, dir string, watcher Watcher) { err = os.Remove(fpath2) assertNoError(t, err) - ev, err = readIgnoreParent(t, watcher) + ev, err = readIgnoreWrites(t, watcher) assertNoError(t, err) assert.Equal(t, fpath2, ev.Name) diff --git a/dev-tools/ecs-migration.yml b/dev-tools/ecs-migration.yml index 0b78a631f3e..2fb6c2f574a 100644 --- a/dev-tools/ecs-migration.yml +++ b/dev-tools/ecs-migration.yml @@ -129,6 +129,159 @@ # Filebeat modules +# Auditd module + +- from: auditd.log.acct + to: user.name + alias: true + beat: filebeat + +- from: auditd.log.pid + to: process.pid + alias: true + beat: filebeat + +- from: auditd.log.ppid + to: process.ppid + alias: true + beat: filebeat + +- from: auditd.log.res + to: event.outcome + alias: true + beat: filebeat + +- from: auditd.log.record_type + to: event.action + alias: true + beat: filebeat + +- from: auditd.log.arch + to: host.architecture + alias: true + beat: filebeat + +- from: auditd.log.gid + to: user.group.id + alias: true + beat: filebeat + +- from: auditd.log.uid + to: user.id + alias: true + beat: filebeat + +- from: auditd.log.agid + to: user.audit.group.id + alias: true + beat: filebeat + +- from: auditd.log.auid + to: user.audit.id + alias: true + beat: filebeat + +- from: auditd.log.fsgid + to: user.filesystem.group.id + alias: true + beat: filebeat + +- from: auditd.log.egid + to: user.effective.group.id + alias: true + beat: filebeat + +- from: auditd.log.euid + to: user.effective.id + alias: true + beat: filebeat + +- from: auditd.log.sgid + to: user.saved.group.id + alias: true + beat: filebeat + +- from: auditd.log.suid + to: user.saved.id + alias: true + beat: filebeat + +- from: auditd.log.ogid + to: user.owner.group.id + alias: true + beat: filebeat + +- from: auditd.log.ouid + to: user.owner.id + alias: true + beat: filebeat + +- from: auditd.log.terminal + to: user.terminal + alias: true + beat: filebeat + +- from: auditd.log.comm + to: process.name + alias: true + beat: filebeat + +- from: auditd.log.cmd + to: process.args + alias: false + beat: filebeat + comment: Was a cmdline string, whereas args is an array of keywords. + +- from: auditd.log.exe + to: process.executable + alias: true + beat: filebeat + +- from: auditd.log.msg + to: message + alias: true + beat: filebeat + +- from: auditd.log.src + to: source.address + alias: true + beat: filebeat + +- from: auditd.log.dst + to: destination.address + alias: true + beat: filebeat + +- from: auditd.log.geoip.continent_name + to: source.geo.continent_name + alias: true + beat: filebeat + +- from: auditd.log.geoip.country_iso_code + to: source.geo.country_iso_code + alias: true + beat: filebeat + +- from: auditd.log.geoip.location + to: source.geo.location + alias: true + beat: filebeat + +- from: auditd.log.geoip.region_name + to: source.geo.region_name + alias: true + beat: filebeat + +- from: auditd.log.geoip.city_name + to: source.geo.city_name + alias: true + beat: filebeat + +- from: auditd.log.geoip.region_iso_code + to: source.geo.region_iso_code + alias: true + beat: filebeat + # Suricata module - from: source_ecs.ip @@ -171,6 +324,126 @@ alias: true beat: filebeat +- from: suricata.eve.alert.action + to: event.outcome + alias: true + beat: filebeat + +- from: suricata.eve.alert.severity + to: event.severity + alias: true + beat: filebeat + +- from: suricata.eve.app_proto + to: network.protocol + alias: true + beat: filebeat + +- from: suricata.eve.dest_ip + to: destination.ip + alias: true + beat: filebeat + +- from: suricata.eve.dest_port + to: destination.port + alias: true + beat: filebeat + +- from: suricata.eve.event_type + to: event.type + alias: true + beat: filebeat + +- from: suricata.eve.fileinfo.filename + to: file.path + alias: true + beat: filebeat + +- from: suricata.eve.fileinfo.size + to: file.size + alias: true + beat: filebeat + +- from: suricata.eve.flow.start + to: event.start + alias: true + beat: filebeat + +- from: suricata.eve.flow.bytes_toclient + to: destination.bytes + alias: true + beat: filebeat + +- from: suricata.eve.flow.bytes_toserver + to: source.bytes + alias: true + beat: filebeat + +- from: suricata.eve.flow.pkts_toclient + to: destination.packets + alias: true + beat: filebeat + +- from: suricata.eve.flow.pkts_toserver + to: source.packets + alias: true + beat: filebeat + +- from: suricata.eve.http.hostname + to: url.domain + alias: true + beat: filebeat + +- from: suricata.eve.http.http_method + to: http.request.method + alias: true + beat: filebeat + +- from: suricata.eve.http.http_refer + to: http.request.referrer + alias: true + beat: filebeat + +- from: suricata.eve.http.http_user_agent + to: user_agent.original + alias: true + beat: filebeat + +- from: suricata.eve.http.length + to: http.response.body.bytes + alias: true + beat: filebeat + +- from: suricata.eve.http.status + to: http.response.status_code + alias: true + beat: filebeat + +- from: suricata.eve.http.url + to: url.original + alias: true + beat: filebeat + +- from: suricata.eve.proto + to: network.transport + alias: true + beat: filebeat + +- from: suricata.eve.src_ip + to: source.ip + alias: true + beat: filebeat + +- from: suricata.eve.src_port + to: source.port + alias: true + beat: filebeat + +- from: suricata.eve.timestamp + to: '@timestamp' + alias: true + beat: filebeat + ## System module - from: system.syslog.hostname @@ -1206,6 +1479,45 @@ ## Modules +### Mongodb + +- from: mongodb.status.version + to: service.version + alias: true + beat: metricbeat + +- from: mongodb.status.process + to: process.name + alias: true + beat: metricbeat + +### Redis + +- from: php_fpm.status.pid + to: process.pid + alias: true + beat: metricbeat + +- from: php_fpm.status.request_method + to: http.request.method + alias: true + beat: metricbeat + +- from: php_fpm.status.request_uri + to: url.original + alias: true + beat: metricbeat + +- from: php_fpm.status.content_length + to: http.response.body.bytes + alias: true + beat: metricbeat + +- from: php_fpm.status.user + to: http.response.user.name + alias: true + beat: metricbeat + ### Redis - from: redis.info.server.version @@ -1231,6 +1543,36 @@ ### System +- from: system.process.name + to: process.name + alias: true + beat: metricbeat + +- from: system.process.pid + to: process.pid + alias: true + beat: metricbeat + +- from: system.process.ppid + to: process.ppid + alias: true + beat: metricbeat + +- from: system.process.pgid + to: process.pgid + alias: true + beat: metricbeat + +- from: system.process.cwd + to: process.working_directory + alias: true + beat: metricbeat + +- from: system.process.username + to: user.name + alias: true + beat: metricbeat + - from: system.socket.direction to: network.direction alias: true @@ -1266,6 +1608,50 @@ alias: true beat: metricbeat +### Kibana + +- from: kibana.stats.uuid + to: service.id + alias: true + beat: metricbeat + +- from: kibana.stats.transport_address + to: service.address + alias: true + beat: metricbeat + +- from: kibana.stats.version + to: service.version + alias: true + beat: metricbeat + +- from: kibana.status.uuid + to: service.id + alias: true + beat: metricbeat + +- from: kibana.status.version.number + to: service.version + alias: true + beat: metricbeat + +### Logstash + +- from: logstash.node.host + to: service.hostname + alias: true + beat: metricbeat + +- from: logstash.node.version + to: service.version + alias: true + beat: metricbeat + +- from: logstash.node.jvm.pid + to: process.pid + alias: true + beat: metricbeat + ### Zookeeper - from: zookeeper.mntr.version diff --git a/docs/devguide/newbeat.asciidoc b/docs/devguide/newbeat.asciidoc index c722d7297ce..02dc6a022a2 100644 --- a/docs/devguide/newbeat.asciidoc +++ b/docs/devguide/newbeat.asciidoc @@ -148,10 +148,10 @@ For the `github_name`, enter your github id. The `beat` and `beat_path` are set [source,shell] --------- -project_name [Examplebeat]: Countbeat -github_name [your-github-name]: {username} -beat_path [github.com/{github id}]: -full_name [Firstname Lastname]: {Full Name} +Beat Name [Examplebeat]: Countbeat +Your Github Name [your-github-name]: {username} +Beat Path [github.com/{github id}/{beat name}]: +Firstname Lastname: {Full Name} --------- The Beat generator creates a directory called `countbeat` inside of your project folder (e.g. {project folder}/github.com/{github id}/countbeat). diff --git a/filebeat/_meta/common.p2.yml b/filebeat/_meta/common.p2.yml index 34fe91f2b0e..b423f0b0494 100644 --- a/filebeat/_meta/common.p2.yml +++ b/filebeat/_meta/common.p2.yml @@ -69,6 +69,6 @@ filebeat.config.modules: #==================== Elasticsearch template setting ========================== setup.template.settings: - index.number_of_shards: 3 + index.number_of_shards: 1 #index.codec: best_compression #_source.enabled: false diff --git a/filebeat/docs/fields.asciidoc b/filebeat/docs/fields.asciidoc index 055cf62d30b..752d9ffa424 100644 --- a/filebeat/docs/fields.asciidoc +++ b/filebeat/docs/fields.asciidoc @@ -23,6 +23,7 @@ grouped in the following categories: * <> * <> * <> +* <> * <> * <> * <> @@ -163,7 +164,7 @@ alias to: user_agent.original -- type: alias -alias to: user_agent.device +alias to: user_agent.device.name -- @@ -407,394 +408,819 @@ Module for parsing auditd logs. -[float] -== auditd fields - -Fields from the auditd logs. - +*`user.terminal`*:: ++ +-- +type: keyword -[float] -== log fields +Terminal or tty device on which the user is performing the observed activity. -Fields from the Linux audit log. Not all fields are documented here because they are dynamic and vary by audit event type. +-- -*`auditd.log.record_type`*:: +*`user.audit.id`*:: + -- -The audit event type. +type: keyword + +One or multiple unique identifiers of the user. -- -*`auditd.log.old_auid`*:: +*`user.audit.name`*:: + -- -For login events this is the old audit ID used for the user prior to this login. +type: keyword + +example: albert + +Short name or login of the user. -- -*`auditd.log.new_auid`*:: +*`user.audit.group.id`*:: + -- -For login events this is the new audit ID. The audit ID can be used to trace future events to the user even if their identity changes (like becoming root). +type: keyword + +Unique identifier for the group on the system/platform. -- -*`auditd.log.old_ses`*:: +*`user.audit.group.name`*:: + -- -For login events this is the old session ID used for the user prior to this login. +type: keyword + +Name of the group. -- -*`auditd.log.new_ses`*:: + +*`user.effective.id`*:: + -- -For login events this is the new session ID. It can be used to tie a user to future events by session ID. +type: keyword + +One or multiple unique identifiers of the user. -- -*`auditd.log.sequence`*:: +*`user.effective.name`*:: + -- -type: long +type: keyword -The audit event sequence number. +example: albert + +Short name or login of the user. -- -*`auditd.log.acct`*:: +*`user.effective.group.id`*:: + -- -The user account name associated with the event. +type: keyword + +Unique identifier for the group on the system/platform. -- -*`auditd.log.pid`*:: +*`user.effective.group.name`*:: + -- -The ID of the process. +type: keyword + +Name of the group. -- -*`auditd.log.ppid`*:: + +*`user.filesystem.id`*:: + -- -The ID of the process. +type: keyword + +One or multiple unique identifiers of the user. -- -*`auditd.log.items`*:: +*`user.filesystem.name`*:: + -- -The number of items in an event. +type: keyword + +example: albert + +Short name or login of the user. -- -*`auditd.log.item`*:: +*`user.filesystem.group.id`*:: + -- -The item field indicates which item out of the total number of items. This number is zero-based; a value of 0 means it is the first item. +type: keyword + +Unique identifier for the group on the system/platform. -- -*`auditd.log.a0`*:: +*`user.filesystem.group.name`*:: + -- -The first argument to the system call. +type: keyword + +Name of the group. -- -*`auditd.log.res`*:: + +*`user.owner.id`*:: + -- -The result of the system call (success or failure). +type: keyword + +One or multiple unique identifiers of the user. -- -[float] -== geoip fields +*`user.owner.name`*:: ++ +-- +type: keyword + +example: albert -Contains GeoIP information gathered based on the `auditd.log.addr` field. Only present if the GeoIP Elasticsearch plugin is available and used. +Short name or login of the user. +-- -*`auditd.log.geoip.continent_name`*:: +*`user.owner.group.id`*:: + -- type: keyword -The name of the continent. +Unique identifier for the group on the system/platform. -- -*`auditd.log.geoip.city_name`*:: +*`user.owner.group.name`*:: + -- type: keyword -The name of the city. +Name of the group. -- -*`auditd.log.geoip.region_name`*:: + +*`user.saved.id`*:: + -- type: keyword -The name of the region. +One or multiple unique identifiers of the user. -- -*`auditd.log.geoip.country_iso_code`*:: +*`user.saved.name`*:: + -- type: keyword -Country ISO code. +example: albert + +Short name or login of the user. -- -*`auditd.log.geoip.location`*:: +*`user.saved.group.id`*:: + -- -type: geo_point +type: keyword -The longitude and latitude. +Unique identifier for the group on the system/platform. -- -*`auditd.log.geoip.region_iso_code`*:: +*`user.saved.group.name`*:: + -- type: keyword -Region ISO code. +Name of the group. -- -[[exported-fields-beat]] -== Beat fields +[float] +== auditd fields -Contains common beat fields available in all event types. +Fields from the auditd logs. -*`beat.timezone`*:: +[float] +== log fields + +Fields from the Linux audit log. Not all fields are documented here because they are dynamic and vary by audit event type. + + + +*`auditd.log.old_auid`*:: + -- -type: alias +For login events this is the old audit ID used for the user prior to this login. -alias to: event.timezone -- -*`fields`*:: +*`auditd.log.new_auid`*:: + -- -type: object +For login events this is the new audit ID. The audit ID can be used to trace future events to the user even if their identity changes (like becoming root). -Contains user configurable fields. +-- +*`auditd.log.old_ses`*:: ++ -- +For login events this is the old session ID used for the user prior to this login. -[float] -== error fields -Error fields containing additional info in case of errors. +-- + +*`auditd.log.new_ses`*:: ++ +-- +For login events this is the new session ID. It can be used to tie a user to future events by session ID. +-- -*`error.type`*:: +*`auditd.log.sequence`*:: + -- -type: keyword +type: long -Error type. +The audit event sequence number. -- -*`beat.name`*:: +*`auditd.log.items`*:: + -- -type: alias +The number of items in an event. -alias to: host.name -- -*`beat.hostname`*:: +*`auditd.log.item`*:: + -- -type: alias +The item field indicates which item out of the total number of items. This number is zero-based; a value of 0 means it is the first item. -alias to: agent.hostname -- -[[exported-fields-cloud]] -== Cloud provider metadata fields +*`auditd.log.tty`*:: ++ +-- +type: keyword -Metadata from cloud providers added by the add_cloud_metadata processor. +-- + +*`auditd.log.a0`*:: ++ +-- +The first argument to the system call. +-- -*`cloud.project.id`*:: +*`auditd.log.addr`*:: + -- -example: project-x +type: ip -Name of the project in Google Cloud. +-- +*`auditd.log.rport`*:: ++ +-- +type: long -- -*`meta.cloud.provider`*:: +*`auditd.log.laddr`*:: + -- -type: alias - -alias to: cloud.provider +type: ip -- -*`meta.cloud.instance_id`*:: +*`auditd.log.lport`*:: + -- -type: alias - -alias to: cloud.instance.id +type: long -- -*`meta.cloud.instance_name`*:: +*`auditd.log.acct`*:: + -- type: alias -alias to: cloud.instance.name +alias to: user.name -- -*`meta.cloud.machine_type`*:: +*`auditd.log.pid`*:: + -- type: alias -alias to: cloud.machine.type +alias to: process.pid -- -*`meta.cloud.availability_zone`*:: +*`auditd.log.ppid`*:: + -- type: alias -alias to: cloud.availability_zone +alias to: process.ppid -- -*`meta.cloud.project_id`*:: +*`auditd.log.res`*:: + -- type: alias -alias to: cloud.project.id +alias to: event.outcome -- -*`meta.cloud.region`*:: +*`auditd.log.record_type`*:: + -- type: alias -alias to: cloud.region +alias to: event.action -- -[[exported-fields-docker-processor]] -== Docker fields - -Docker stats collected from Docker. +*`auditd.log.geoip.continent_name`*:: ++ +-- +type: alias +alias to: source.geo.continent_name +-- -*`docker.container.id`*:: +*`auditd.log.geoip.country_iso_code`*:: + -- type: alias -alias to: container.id +alias to: source.geo.country_iso_code -- -*`docker.container.image`*:: +*`auditd.log.geoip.location`*:: + -- type: alias -alias to: container.image.name +alias to: source.geo.location -- -*`docker.container.name`*:: +*`auditd.log.geoip.region_name`*:: + -- type: alias -alias to: container.name +alias to: source.geo.region_name -- -*`docker.container.labels`*:: +*`auditd.log.geoip.city_name`*:: + -- -type: object - -Image labels. +type: alias +alias to: source.geo.city_name -- -[[exported-fields-ecs]] -== ECS fields - -ECS fields. +*`auditd.log.geoip.region_iso_code`*:: ++ +-- +type: alias +alias to: source.geo.region_iso_code +-- -*`@timestamp`*:: +*`auditd.log.arch`*:: + -- -type: date +type: alias -example: 2016-05-23T08:05:34.853Z +alias to: host.architecture -required: True +-- + +*`auditd.log.gid`*:: ++ +-- +type: alias + +alias to: user.group.id + +-- + +*`auditd.log.uid`*:: ++ +-- +type: alias + +alias to: user.id + +-- + +*`auditd.log.agid`*:: ++ +-- +type: alias + +alias to: user.audit.group.id + +-- + +*`auditd.log.auid`*:: ++ +-- +type: alias + +alias to: user.audit.id + +-- + +*`auditd.log.fsgid`*:: ++ +-- +type: alias + +alias to: user.filesystem.group.id + +-- + +*`auditd.log.fsuid`*:: ++ +-- +type: alias + +alias to: user.filesystem.id + +-- + +*`auditd.log.egid`*:: ++ +-- +type: alias + +alias to: user.effective.group.id + +-- + +*`auditd.log.euid`*:: ++ +-- +type: alias + +alias to: user.effective.id + +-- + +*`auditd.log.sgid`*:: ++ +-- +type: alias + +alias to: user.saved.group.id + +-- + +*`auditd.log.suid`*:: ++ +-- +type: alias + +alias to: user.saved.id + +-- + +*`auditd.log.ogid`*:: ++ +-- +type: alias + +alias to: user.owner.group.id + +-- + +*`auditd.log.ouid`*:: ++ +-- +type: alias + +alias to: user.owner.id + +-- + +*`auditd.log.comm`*:: ++ +-- +type: alias + +alias to: process.name + +-- + +*`auditd.log.exe`*:: ++ +-- +type: alias + +alias to: process.executable + +-- + +*`auditd.log.terminal`*:: ++ +-- +type: alias + +alias to: user.terminal + +-- + +*`auditd.log.msg`*:: ++ +-- +type: alias + +alias to: message + +-- + +*`auditd.log.src`*:: ++ +-- +type: alias + +alias to: source.address + +-- + +*`auditd.log.dst`*:: ++ +-- +type: alias + +alias to: destination.address + +-- + +[[exported-fields-beat]] +== Beat fields + +Contains common beat fields available in all event types. + + + +*`beat.timezone`*:: ++ +-- +type: alias + +alias to: event.timezone + +-- + +*`fields`*:: ++ +-- +type: object + +Contains user configurable fields. + + +-- + +[float] +== error fields + +Error fields containing additional info in case of errors. + + + +*`error.type`*:: ++ +-- +type: keyword + +Error type. + + +-- + +*`beat.name`*:: ++ +-- +type: alias + +alias to: host.name + +-- + +*`beat.hostname`*:: ++ +-- +type: alias + +alias to: agent.hostname + +-- + +[[exported-fields-cloud]] +== Cloud provider metadata fields + +Metadata from cloud providers added by the add_cloud_metadata processor. + + + +*`cloud.project.id`*:: ++ +-- +example: project-x + +Name of the project in Google Cloud. + + +-- + +*`meta.cloud.provider`*:: ++ +-- +type: alias + +alias to: cloud.provider + +-- + +*`meta.cloud.instance_id`*:: ++ +-- +type: alias + +alias to: cloud.instance.id + +-- + +*`meta.cloud.instance_name`*:: ++ +-- +type: alias + +alias to: cloud.instance.name + +-- + +*`meta.cloud.machine_type`*:: ++ +-- +type: alias + +alias to: cloud.machine.type + +-- + +*`meta.cloud.availability_zone`*:: ++ +-- +type: alias + +alias to: cloud.availability_zone + +-- + +*`meta.cloud.project_id`*:: ++ +-- +type: alias + +alias to: cloud.project.id + +-- + +*`meta.cloud.region`*:: ++ +-- +type: alias + +alias to: cloud.region + +-- + +[[exported-fields-docker-processor]] +== Docker fields + +Docker stats collected from Docker. + + + + +*`docker.container.id`*:: ++ +-- +type: alias + +alias to: container.id + +-- + +*`docker.container.image`*:: ++ +-- +type: alias + +alias to: container.image.name + +-- + +*`docker.container.name`*:: ++ +-- +type: alias + +alias to: container.name + +-- + +*`docker.container.labels`*:: ++ +-- +type: object + +Image labels. + + +-- + +[[exported-fields-ecs]] +== ECS fields + +ECS fields. + + + +*`@timestamp`*:: ++ +-- +type: date + +example: 2016-05-23T08:05:34.853Z + +required: True Date/time when the event originated. For log events this is the date/time when the event was generated, and not when it was read. @@ -3858,58 +4284,82 @@ Major version of the user agent. -- -*`user_agent.device`*:: +*`user_agent.os.major`*:: + -- -type: keyword +type: long -Name of the physical device. +Major version of the operating system. -- -*`user_agent.os.major`*:: +*`user_agent.os.minor`*:: + -- type: long -Major version of the operating system. +Minor version of the operating system. -- -*`user_agent.os.minor`*:: +[[exported-fields-elasticsearch]] +== elasticsearch fields + +elasticsearch Module + + + +[float] +== elasticsearch fields + + + + +*`elasticsearch.component`*:: + -- -type: long +type: keyword -Minor version of the operating system. +example: o.e.c.m.MetaDataCreateIndexService +Elasticsearch component from where the log event originated -- -*`url.hostname`*:: +*`elasticsearch.cluster.uuid`*:: + -- type: keyword -Hostname of the request, such as "elastic.co". +example: GmvrbHlNTiSVYiPf8kxg9g +UUID of the cluster -- -[[exported-fields-elasticsearch]] -== elasticsearch fields +*`elasticsearch.cluster.name`*:: ++ +-- +type: keyword -elasticsearch Module +example: docker-cluster +Name of the cluster +-- -[float] -== elasticsearch fields +*`elasticsearch.node.id`*:: ++ +-- +type: keyword +example: DSiWcTyeThWtUXLB9J0BMw +ID of the node +-- *`elasticsearch.node.name`*:: + @@ -3972,7 +4422,7 @@ The layer from which this event originated: rest, transport or ip_filter -- -*`elasticsearch.audit.origin_type`*:: +*`elasticsearch.audit.origin.type`*:: + -- type: keyword @@ -3988,11 +4438,20 @@ Where the request originated: rest (request originated from a REST API request), -- type: keyword -The authentication realm +The authentication realm the authentication was validated against + +-- + +*`elasticsearch.audit.user.realm`*:: ++ +-- +type: keyword + +The user's authentication realm, if authenticated -- -*`elasticsearch.audit.roles`*:: +*`elasticsearch.audit.user.roles`*:: + -- type: keyword @@ -4014,6 +4473,24 @@ The name of the action that was executed -- +*`elasticsearch.audit.url.params`*:: ++ +-- +type: keyword + +example: {username=jacknich2} + +REST URI parameters + +*`elasticsearch.audit.url.params.text`*:: ++ +-- +type: text + +-- + +-- + *`elasticsearch.audit.indices`*:: + -- @@ -4025,7 +4502,18 @@ Indices accessed by action -- -*`elasticsearch.audit.request`*:: +*`elasticsearch.audit.request.id`*:: ++ +-- +type: keyword + +example: WzL_kb6VSvOhAq0twPvHOQ + +Unique ID of request + +-- + +*`elasticsearch.audit.request.name`*:: + -- type: keyword @@ -4036,39 +4524,39 @@ The type of request that was executed -- -*`elasticsearch.audit.event_type`*:: +*`elasticsearch.audit.request_body`*:: + -- type: alias -alias to: event.type +alias to: http.request.body.content -- -*`elasticsearch.audit.origin_address`*:: +*`elasticsearch.audit.event_type`*:: + -- type: alias -alias to: source.ip +alias to: event.type -- -*`elasticsearch.audit.uri`*:: +*`elasticsearch.audit.origin_address`*:: + -- type: alias -alias to: url.original +alias to: source.ip -- -*`elasticsearch.audit.request_body`*:: +*`elasticsearch.audit.uri`*:: + -- type: alias -alias to: http.request.body.content +alias to: url.original -- @@ -4432,7 +4920,7 @@ Logger name *`elasticsearch.slowlog.took`*:: + -- -type: text +type: keyword example: 300ms @@ -4454,11 +4942,11 @@ Types *`elasticsearch.slowlog.stats`*:: + -- -type: text +type: keyword -example: +example: group1 -Statistics +Stats groups -- @@ -4476,7 +4964,7 @@ Search type *`elasticsearch.slowlog.source_query`*:: + -- -type: text +type: keyword example: {"query":{"match_all":{"boost":1.0}}} @@ -4487,7 +4975,7 @@ Slow query *`elasticsearch.slowlog.extra_source`*:: + -- -type: text +type: keyword example: @@ -4682,7 +5170,7 @@ Condition the session was in when the session ended. *`haproxy.mode`*:: + -- -type: text +type: keyword mode that the frontend is operating (TCP or HTTP) @@ -4889,7 +5377,7 @@ Optional "name=value" entry indicating that the client had this cookie in the re *`haproxy.http.response.captured_headers`*:: + -- -type: text +type: keyword List of headers captured in the response due to the presence of the "capture response header" statement in the frontend. @@ -4922,7 +5410,7 @@ Optional "name=value" entry indicating that the server has returned a cookie wit *`haproxy.http.request.captured_headers`*:: + -- -type: text +type: keyword List of headers captured in the request due to the presence of the "capture request header" statement in the frontend. @@ -4932,7 +5420,7 @@ List of headers captured in the request due to the presence of the "capture requ *`haproxy.http.request.raw_request_line`*:: + -- -type: text +type: keyword Complete HTTP request line, including the method, request and HTTP version string. @@ -4990,237 +5478,500 @@ Icinga Module -[float] -== debug fields +[float] +== debug fields + +Contains fields for the Icinga debug logs. + + + +*`icinga.debug.facility`*:: ++ +-- +type: keyword + +Specifies what component of Icinga logged the message. + + +-- + +*`icinga.debug.severity`*:: ++ +-- +type: alias + +alias to: log.level + +-- + +*`icinga.debug.message`*:: ++ +-- +type: alias + +alias to: message + +-- + +[float] +== main fields + +Contains fields for the Icinga main logs. + + + +*`icinga.main.facility`*:: ++ +-- +type: keyword + +Specifies what component of Icinga logged the message. + + +-- + +*`icinga.main.severity`*:: ++ +-- +type: alias + +alias to: log.level + +-- + +*`icinga.main.message`*:: ++ +-- +type: alias + +alias to: message + +-- + +[float] +== startup fields + +Contains fields for the Icinga startup logs. + + + +*`icinga.startup.facility`*:: ++ +-- +type: keyword + +Specifies what component of Icinga logged the message. + + +-- + +*`icinga.startup.severity`*:: ++ +-- +type: alias + +alias to: log.level + +-- + +*`icinga.startup.message`*:: ++ +-- +type: alias + +alias to: message + +-- + +[[exported-fields-iis]] +== IIS fields + +Module for parsing IIS log files. + + + +[float] +== iis fields + +Fields from IIS log files. + + + +[float] +== access fields + +Contains fields for IIS access logs. + + + +*`iis.access.sub_status`*:: ++ +-- +type: long + +The HTTP substatus code. + + +-- + +*`iis.access.win32_status`*:: ++ +-- +type: long + +The Windows status code. + + +-- + +*`iis.access.site_name`*:: ++ +-- +type: keyword + +The site name and instance number. + + +-- + +*`iis.access.server_name`*:: ++ +-- +type: keyword + +The name of the server on which the log file entry was generated. + + +-- + +*`iis.access.cookie`*:: ++ +-- +type: keyword + +The content of the cookie sent or received, if any. + + +-- + +*`iis.access.body_received.bytes`*:: ++ +-- +type: alias + +alias to: http.request.body.bytes + +-- + +*`iis.access.body_sent.bytes`*:: ++ +-- +type: alias + +alias to: http.response.body.bytes + +-- + +*`iis.access.server_ip`*:: ++ +-- +type: alias + +alias to: destination.address + +-- + +*`iis.access.method`*:: ++ +-- +type: alias + +alias to: http.request.method + +-- + +*`iis.access.url`*:: ++ +-- +type: alias + +alias to: url.path + +-- + +*`iis.access.query_string`*:: ++ +-- +type: alias + +alias to: url.query + +-- + +*`iis.access.port`*:: ++ +-- +type: alias + +alias to: destination.port + +-- + +*`iis.access.user_name`*:: ++ +-- +type: alias + +alias to: user.name + +-- -Contains fields for the Icinga debug logs. +*`iis.access.remote_ip`*:: ++ +-- +type: alias +alias to: source.address +-- -*`icinga.debug.facility`*:: +*`iis.access.referrer`*:: + -- -type: keyword - -Specifies what component of Icinga logged the message. +type: alias +alias to: http.request.referrer -- -*`icinga.debug.severity`*:: +*`iis.access.response_code`*:: + -- type: alias -alias to: log.level +alias to: http.response.status_code -- -*`icinga.debug.message`*:: +*`iis.access.http_version`*:: + -- type: alias -alias to: message +alias to: http.version -- -[float] -== main fields +*`iis.access.hostname`*:: ++ +-- +type: alias -Contains fields for the Icinga main logs. +alias to: host.hostname +-- -*`icinga.main.facility`*:: +*`iis.access.user_agent.device`*:: + -- -type: keyword - -Specifies what component of Icinga logged the message. +type: alias +alias to: user_agent.device.name -- -*`icinga.main.severity`*:: +*`iis.access.user_agent.major`*:: + -- type: alias -alias to: log.level +alias to: user_agent.major -- -*`icinga.main.message`*:: +*`iis.access.user_agent.minor`*:: + -- type: alias -alias to: message +alias to: user_agent.minor -- -[float] -== startup fields - -Contains fields for the Icinga startup logs. +*`iis.access.user_agent.patch`*:: ++ +-- +type: alias +alias to: user_agent.patch +-- -*`icinga.startup.facility`*:: +*`iis.access.user_agent.name`*:: + -- -type: keyword - -Specifies what component of Icinga logged the message. +type: alias +alias to: user_agent.name -- -*`icinga.startup.severity`*:: +*`iis.access.user_agent.os`*:: + -- type: alias -alias to: log.level +alias to: user_agent.os.full_name -- -*`icinga.startup.message`*:: +*`iis.access.user_agent.os_major`*:: + -- type: alias -alias to: message +alias to: user_agent.os.major -- -[[exported-fields-iis]] -== IIS fields - -Module for parsing IIS log files. - +*`iis.access.user_agent.os_minor`*:: ++ +-- +type: alias +alias to: user_agent.os.minor -[float] -== iis fields +-- -Fields from IIS log files. +*`iis.access.user_agent.os_name`*:: ++ +-- +type: alias +alias to: user_agent.os.name +-- -[float] -== access fields +*`iis.access.user_agent.original`*:: ++ +-- +type: alias -Contains fields for IIS access logs. +alias to: user_agent.original +-- -*`iis.access.sub_status`*:: +*`iis.access.geoip.continent_name`*:: + -- -type: long - -The HTTP substatus code. +type: alias +alias to: source.geo.continent_name -- -*`iis.access.win32_status`*:: +*`iis.access.geoip.country_iso_code`*:: + -- -type: long - -The Windows status code. +type: alias +alias to: source.geo.country_iso_code -- -*`iis.access.site_name`*:: +*`iis.access.geoip.location`*:: + -- -type: keyword - -The site name and instance number. +type: alias +alias to: source.geo.location -- -*`iis.access.server_name`*:: +*`iis.access.geoip.region_name`*:: + -- -type: keyword - -The name of the server on which the log file entry was generated. +type: alias +alias to: source.geo.region_name -- -*`iis.access.cookie`*:: +*`iis.access.geoip.city_name`*:: + -- -type: keyword - -The content of the cookie sent or received, if any. +type: alias +alias to: source.geo.city_name -- -*`iis.access.body_received.bytes`*:: +*`iis.access.geoip.region_iso_code`*:: + -- type: alias -alias to: http.request.body.bytes +alias to: source.geo.region_iso_code -- -*`iis.access.body_sent.bytes`*:: +[float] +== error fields + +Contains fields for IIS error logs. + + + +*`iis.error.reason_phrase`*:: + -- -type: alias +type: keyword + +The HTTP reason phrase. -alias to: http.response.body.bytes -- -*`iis.access.server_ip`*:: +*`iis.error.queue_name`*:: + -- -type: alias +type: keyword + +The IIS application pool name. -alias to: destination.address -- -*`iis.access.method`*:: +*`iis.error.remote_ip`*:: + -- type: alias -alias to: http.request.method +alias to: source.address -- -*`iis.access.url`*:: +*`iis.error.remote_port`*:: + -- type: alias -alias to: url.path +alias to: source.port -- -*`iis.access.query_string`*:: +*`iis.error.server_ip`*:: + -- type: alias -alias to: url.query +alias to: destination.address -- -*`iis.access.port`*:: +*`iis.error.server_port`*:: + -- type: alias @@ -5229,34 +5980,34 @@ alias to: destination.port -- -*`iis.access.user_name`*:: +*`iis.error.http_version`*:: + -- type: alias -alias to: user.name +alias to: http.version -- -*`iis.access.remote_ip`*:: +*`iis.error.method`*:: + -- type: alias -alias to: source.address +alias to: http.request.method -- -*`iis.access.referrer`*:: +*`iis.error.url`*:: + -- type: alias -alias to: http.request.referrer +alias to: url.original -- -*`iis.access.response_code`*:: +*`iis.error.response_code`*:: + -- type: alias @@ -5265,321 +6016,378 @@ alias to: http.response.status_code -- -*`iis.access.http_version`*:: + +*`iis.error.geoip.continent_name`*:: + -- type: alias -alias to: http.version +alias to: source.geo.continent_name -- -*`iis.access.hostname`*:: +*`iis.error.geoip.country_iso_code`*:: + -- type: alias -alias to: host.hostname +alias to: source.geo.country_iso_code -- - -*`iis.access.user_agent.device`*:: +*`iis.error.geoip.location`*:: + -- type: alias -alias to: user_agent.device +alias to: source.geo.location -- -*`iis.access.user_agent.major`*:: +*`iis.error.geoip.region_name`*:: + -- type: alias -alias to: user_agent.major +alias to: source.geo.region_name -- -*`iis.access.user_agent.minor`*:: +*`iis.error.geoip.city_name`*:: + -- type: alias -alias to: user_agent.minor +alias to: source.geo.city_name -- -*`iis.access.user_agent.patch`*:: +*`iis.error.geoip.region_iso_code`*:: + -- type: alias -alias to: user_agent.patch +alias to: source.geo.region_iso_code -- -*`iis.access.user_agent.name`*:: -+ --- -type: alias +[[exported-fields-iptables]] +== iptables fields -alias to: user_agent.name +Module for handling the iptables logs. --- -*`iis.access.user_agent.os`*:: + +[float] +== iptables fields + +Fields from the iptables logs. + + + +*`iptables.ether_type`*:: + -- -type: alias +type: long + +Value of the ethernet type field identifying the network layer protocol. -alias to: user_agent.os.full_name -- -*`iis.access.user_agent.os_major`*:: +*`iptables.flow_label`*:: + -- -type: alias +type: integer + +IPv6 flow label. -alias to: user_agent.os.major -- -*`iis.access.user_agent.os_minor`*:: +*`iptables.fragment_flags`*:: + -- -type: alias +type: keyword + +IP fragment flags. A combination of CE, DF and MF. -alias to: user_agent.os.minor -- -*`iis.access.user_agent.os_name`*:: +*`iptables.fragment_offset`*:: + -- -type: alias +type: long + +Offset of the current IP fragment. -alias to: user_agent.os.name -- -*`iis.access.user_agent.original`*:: +[float] +== icmp fields + +ICMP fields. + + + +*`iptables.icmp.code`*:: + -- -type: alias +type: long + +ICMP code. -alias to: user_agent.original -- - -*`iis.access.geoip.continent_name`*:: +*`iptables.icmp.id`*:: + -- -type: alias +type: long + +ICMP ID. -alias to: source.geo.continent_name -- -*`iis.access.geoip.country_iso_code`*:: +*`iptables.icmp.parameter`*:: + -- -type: alias +type: long + +ICMP parameter. -alias to: source.geo.country_iso_code -- -*`iis.access.geoip.location`*:: +*`iptables.icmp.redirect`*:: + -- -type: alias +type: ip + +ICMP redirect address. -alias to: source.geo.location -- -*`iis.access.geoip.region_name`*:: +*`iptables.icmp.seq`*:: + -- -type: alias +type: long + +ICMP sequence number. -alias to: source.geo.region_name -- -*`iis.access.geoip.city_name`*:: +*`iptables.icmp.type`*:: + -- -type: alias +type: long + +ICMP type. -alias to: source.geo.city_name -- -*`iis.access.geoip.region_iso_code`*:: +*`iptables.id`*:: + -- -type: alias +type: long + +Packet identifier. -alias to: source.geo.region_iso_code -- -[float] -== error fields +*`iptables.incomplete_bytes`*:: ++ +-- +type: long -Contains fields for IIS error logs. +Number of incomplete bytes. +-- -*`iis.error.reason_phrase`*:: +*`iptables.input_device`*:: + -- type: keyword -The HTTP reason phrase. +Device that received the packet. -- -*`iis.error.queue_name`*:: +*`iptables.precedence_bits`*:: + -- -type: keyword +type: short -The IIS application pool name. +IP precedence bits. -- -*`iis.error.remote_ip`*:: +*`iptables.tos`*:: + -- -type: alias +type: long + +IP Type of Service field. -alias to: source.address -- -*`iis.error.remote_port`*:: +*`iptables.length`*:: + -- -type: alias +type: long + +Packet length. -alias to: source.port -- -*`iis.error.server_ip`*:: +*`iptables.output_device`*:: + -- -type: alias +type: keyword + +Device that output the packet. -alias to: destination.address -- -*`iis.error.server_port`*:: +[float] +== tcp fields + +TCP fields. + + + +*`iptables.tcp.flags`*:: + -- -type: alias +type: keyword + +TCP flags. -alias to: destination.port -- -*`iis.error.http_version`*:: +*`iptables.tcp.reserved_bits`*:: + -- -type: alias +type: short + +TCP reserved bits. -alias to: http.version -- -*`iis.error.method`*:: +*`iptables.tcp.seq`*:: + -- -type: alias +type: long + +TCP sequence number. -alias to: http.request.method -- -*`iis.error.url`*:: +*`iptables.tcp.ack`*:: + -- -type: alias +type: long + +TCP Acknowledgment number. -alias to: url.original -- -*`iis.error.response_code`*:: +*`iptables.tcp.window`*:: + -- -type: alias +type: long -alias to: http.response.status_code +Advertised TCP window size. --- +-- -*`iis.error.geoip.continent_name`*:: +*`iptables.ttl`*:: + -- -type: alias +type: integer + +Time To Live field. -alias to: source.geo.continent_name -- -*`iis.error.geoip.country_iso_code`*:: +[float] +== udp fields + +UDP fields. + + + +*`iptables.udp.length`*:: + -- -type: alias +type: long + +Length of the UDP header and payload. -alias to: source.geo.country_iso_code -- -*`iis.error.geoip.location`*:: +[float] +== ubiquiti fields + +Fields for Ubiquiti network devices. + + + +*`iptables.ubiquiti.input_zone`*:: + -- -type: alias +type: keyword + +Input zone. -alias to: source.geo.location -- -*`iis.error.geoip.region_name`*:: +*`iptables.ubiquiti.output_zone`*:: + -- -type: alias +type: keyword + +Output zone. -alias to: source.geo.region_name -- -*`iis.error.geoip.city_name`*:: +*`iptables.ubiquiti.rule_number`*:: + -- -type: alias +type: keyword -alias to: source.geo.city_name +The rule number within the rule set. -- -*`iis.error.geoip.region_iso_code`*:: +*`iptables.ubiquiti.rule_set`*:: + -- -type: alias +type: keyword -alias to: source.geo.region_iso_code +The rule set name. -- @@ -6088,11 +6896,18 @@ The module or class where the event originate. *`logstash.log.thread`*:: + -- -type: text +type: keyword Information about the running thread where the log originate. +*`logstash.log.thread.text`*:: ++ +-- +type: text + +-- + -- *`logstash.log.log_event`*:: @@ -6130,44 +6945,48 @@ slowlog -*`logstash.slowlog.message`*:: +*`logstash.slowlog.module`*:: + -- -type: text +type: keyword -Contains the un-parsed log message +The module or class where the event originate. -- -*`logstash.slowlog.module`*:: +*`logstash.slowlog.thread`*:: + -- type: keyword -The module or class where the event originate. - +Information about the running thread where the log originate. --- -*`logstash.slowlog.thread`*:: +*`logstash.slowlog.thread.text`*:: + -- type: text -Information about the running thread where the log originate. - +-- -- *`logstash.slowlog.event`*:: + -- -type: text +type: keyword Raw dump of the original event +*`logstash.slowlog.event.text`*:: ++ +-- +type: text + +-- + -- *`logstash.slowlog.plugin_name`*:: @@ -6203,11 +7022,18 @@ Execution time for the plugin in milliseconds. *`logstash.slowlog.plugin_params`*:: + -- -type: text +type: keyword String value of the plugin configuration +*`logstash.slowlog.plugin_params.text`*:: ++ +-- +type: text + +-- + -- *`logstash.slowlog.plugin_params_object`*:: @@ -10043,7 +10869,7 @@ alias to: user_agent.original -- type: alias -alias to: user_agent.device +alias to: user_agent.device.name -- @@ -10706,7 +11532,9 @@ Fields exported by the EVE JSON logs *`suricata.eve.event_type`*:: + -- -type: keyword +type: alias + +alias to: event.type -- @@ -10792,7 +11620,9 @@ type: keyword *`suricata.eve.fileinfo.filename`*:: + -- -type: keyword +type: alias + +alias to: file.path -- @@ -10841,7 +11671,9 @@ type: keyword *`suricata.eve.fileinfo.size`*:: + -- -type: long +type: alias + +alias to: file.size -- @@ -10855,21 +11687,27 @@ type: long *`suricata.eve.dest_port`*:: + -- -type: long +type: alias + +alias to: destination.port -- *`suricata.eve.src_port`*:: + -- -type: long +type: alias + +alias to: source.port -- *`suricata.eve.proto`*:: + -- -type: keyword +type: alias + +alias to: network.transport -- @@ -10883,7 +11721,9 @@ type: long *`suricata.eve.src_ip`*:: + -- -type: ip +type: alias + +alias to: source.ip -- @@ -10962,7 +11802,9 @@ type: keyword *`suricata.eve.dest_ip`*:: + -- -type: ip +type: alias + +alias to: destination.ip -- @@ -10977,7 +11819,9 @@ type: long *`suricata.eve.http.status`*:: + -- -type: long +type: alias + +alias to: http.response.status_code -- @@ -10991,7 +11835,9 @@ type: keyword *`suricata.eve.http.http_user_agent`*:: + -- -type: keyword +type: alias + +alias to: user_agent.original -- @@ -11005,35 +11851,45 @@ type: keyword *`suricata.eve.http.http_refer`*:: + -- -type: keyword +type: alias + +alias to: http.request.referrer -- *`suricata.eve.http.url`*:: + -- -type: keyword +type: alias + +alias to: url.original -- *`suricata.eve.http.hostname`*:: + -- -type: keyword +type: alias + +alias to: url.domain -- *`suricata.eve.http.length`*:: + -- -type: long +type: alias + +alias to: http.response.body.bytes -- *`suricata.eve.http.http_method`*:: + -- -type: keyword +type: alias + +alias to: http.request.method -- @@ -11047,7 +11903,9 @@ type: keyword *`suricata.eve.timestamp`*:: + -- -type: date +type: alias + +alias to: @timestamp -- @@ -11069,7 +11927,9 @@ type: keyword *`suricata.eve.alert.severity`*:: + -- -type: long +type: alias + +alias to: event.severity -- @@ -11097,7 +11957,9 @@ type: keyword *`suricata.eve.alert.action`*:: + -- -type: keyword +type: alias + +alias to: event.outcome -- @@ -12045,21 +12907,27 @@ type: keyword *`suricata.eve.flow.bytes_toclient`*:: + -- -type: long +type: alias + +alias to: destination.bytes -- *`suricata.eve.flow.start`*:: + -- -type: date +type: alias + +alias to: event.start -- *`suricata.eve.flow.pkts_toclient`*:: + -- -type: long +type: alias + +alias to: destination.packets -- @@ -12080,7 +12948,9 @@ type: keyword *`suricata.eve.flow.bytes_toserver`*:: + -- -type: long +type: alias + +alias to: source.bytes -- @@ -12094,7 +12964,9 @@ type: keyword *`suricata.eve.flow.pkts_toserver`*:: + -- -type: long +type: alias + +alias to: source.packets -- @@ -12115,7 +12987,9 @@ type: boolean *`suricata.eve.app_proto`*:: + -- -type: keyword +type: alias + +alias to: network.protocol -- @@ -12566,7 +13440,7 @@ The number of requests *`traefik.access.frontend_name`*:: + -- -type: text +type: keyword The name of the frontend used @@ -12576,7 +13450,7 @@ The name of the frontend used *`traefik.access.backend_url`*:: + -- -type: text +type: keyword The url of the backend where request is forwarded @@ -12669,7 +13543,7 @@ alias to: user_agent.original -- type: alias -alias to: user_agent.device +alias to: user_agent.device.name -- diff --git a/filebeat/docs/images/kibana-iptables-ubiquiti.png b/filebeat/docs/images/kibana-iptables-ubiquiti.png new file mode 100644 index 00000000000..246f2ace263 Binary files /dev/null and b/filebeat/docs/images/kibana-iptables-ubiquiti.png differ diff --git a/filebeat/docs/images/kibana-iptables.png b/filebeat/docs/images/kibana-iptables.png new file mode 100644 index 00000000000..a12929d11d5 Binary files /dev/null and b/filebeat/docs/images/kibana-iptables.png differ diff --git a/filebeat/docs/modules/iptables.asciidoc b/filebeat/docs/modules/iptables.asciidoc new file mode 100644 index 00000000000..4edcae95a07 --- /dev/null +++ b/filebeat/docs/modules/iptables.asciidoc @@ -0,0 +1,85 @@ +//// +This file is generated! See scripts/docs_collector.py +//// + +[[filebeat-module-iptables]] +[role="xpack"] + +:modulename: iptables +:has-dashboards: true + +== Iptables module + +This is a module for iptables and ip6tables logs. It parses logs received +over the network via syslog or from a file. Also, it understands the prefix added +by some Ubiquiti firewalls, which includes the rule set name, rule number and +the action performed on the traffic (allow/deny). + +When you run the module, it performs a few tasks under the hood: + +* Sets the default input to `syslog` and binds to `localhost` port `9001` + (but don’t worry, you can override the defaults). + +* Uses ingest node to parse and process the log lines, shaping the data into + a structure suitable for visualizing in Kibana. + +* Deploys dashboards for visualizing the log data. + +[float] +=== Compatibility + +This module requires the {elasticsearch-plugins}/ingest-geoip.html[ingest-geoip] +Elasticsearch plugins. + +include::../include/running-modules.asciidoc[] + +[float] +=== Example dashboard + +This module comes with sample dashboards showing geolocation and network +protocols used. One for all iptables logs: + +[role="screenshot"] +image::./images/kibana-iptables.png[] + +and one specific for Ubiquiti Firewall logs: + +[role="screenshot"] +image::./images/kibana-iptables-ubiquiti.png[] + +include::../include/configuring-intro.asciidoc[] + +The module is by default configured to run via syslog on port 9001. However +it can also be configured to read from a file path. See the following example. + +["source","yaml",subs="attributes"] +----- +- module: iptables + log: + enabled: true + var.paths: ["/var/log/iptables.log"] + var.input: "file" +----- + +:fileset_ex: log + +include::../include/config-option-intro.asciidoc[] + +[float] +==== `log` log fileset settings + +include::../include/var-paths.asciidoc[] + +:has-dashboards!: + +:fileset_ex!: + +:modulename!: + + +[float] +=== Fields + +For a description of each field in the module, see the +<> section. + diff --git a/filebeat/docs/modules_list.asciidoc b/filebeat/docs/modules_list.asciidoc index b8712ccdb55..8af5a7858b0 100644 --- a/filebeat/docs/modules_list.asciidoc +++ b/filebeat/docs/modules_list.asciidoc @@ -9,6 +9,7 @@ This file is generated! See scripts/docs_collector.py * <> * <> * <> + * <> * <> * <> * <> @@ -34,6 +35,7 @@ include::modules/elasticsearch.asciidoc[] include::modules/haproxy.asciidoc[] include::modules/icinga.asciidoc[] include::modules/iis.asciidoc[] +include::modules/iptables.asciidoc[] include::modules/kafka.asciidoc[] include::modules/kibana.asciidoc[] include::modules/logstash.asciidoc[] diff --git a/filebeat/filebeat.reference.yml b/filebeat/filebeat.reference.yml index e4f68c1df5a..cdfa2b0ca11 100644 --- a/filebeat/filebeat.reference.yml +++ b/filebeat/filebeat.reference.yml @@ -1059,11 +1059,6 @@ output.elasticsearch: # IPv6 addresses should always be defined as: https://[2001:db8::1]:9200 hosts: ["localhost:9200"] - # Enabled ilm (beta) to use index lifecycle management instead daily indices. - #ilm.enabled: false - #ilm.rollover_alias: "filebeat" - #ilm.pattern: "{now/d}-000001" - # Set gzip compression level. #compression_level: 0 @@ -1710,6 +1705,25 @@ setup.template.settings: #_source: #enabled: false +#============================== Setup ILM ===================================== + +# Configure Index Lifecycle Management Index Lifecycle Management creates a +# write alias and adds additional settings to the template. +# The elasticsearch.output.index setting will be replaced with the write alias +# if ILM is enabled. + +# Enabled ILM support. Valid values are true, false, and auto. The beat will +# detect availabilty of Index Lifecycle Management in Elasticsearch and enable +# or disable ILM support. +#setup.ilm.enabled: auto + +# Configure the ILM write alias name. +#setup.ilm.rollover_alias: "filebeat" + +# Configure rollover index pattern. +#setup.ilm.pattern: "{now/d}-000001" + + #============================== Kibana ===================================== # Starting with Beats version 6.0.0, the dashboards are loaded via the Kibana API. diff --git a/filebeat/filebeat.yml b/filebeat/filebeat.yml index 2664206c560..c06827965ff 100644 --- a/filebeat/filebeat.yml +++ b/filebeat/filebeat.yml @@ -78,7 +78,7 @@ filebeat.config.modules: #==================== Elasticsearch template setting ========================== setup.template.settings: - index.number_of_shards: 3 + index.number_of_shards: 1 #index.codec: best_compression #_source.enabled: false @@ -101,7 +101,7 @@ setup.template.settings: #============================== Dashboards ===================================== # These settings control loading the sample dashboards to the Kibana index. Loading # the dashboards is disabled by default and can be enabled either by setting the -# options here, or by using the `-setup` CLI flag or the `setup` command. +# options here or by using the `setup` command. #setup.dashboards.enabled: false # The URL from where to download the dashboards archive. By default this URL @@ -149,9 +149,6 @@ output.elasticsearch: # Array of hosts to connect to. hosts: ["localhost:9200"] - # Enabled ilm (beta) to use index lifecycle management instead daily indices. - #ilm.enabled: false - # Optional protocol and basic auth credentials. #protocol: "https" #username: "elastic" diff --git a/filebeat/include/fields.go b/filebeat/include/fields.go index 7721c5ac1d0..bc0cbe3fb1c 100644 --- a/filebeat/include/fields.go +++ b/filebeat/include/fields.go @@ -32,5 +32,5 @@ func init() { // AssetFieldsYml returns asset data. // This is the base64 encoded gzipped contents of fields.yml. func AssetFieldsYml() string { - return "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" + return "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" } diff --git a/filebeat/module/apache/_meta/fields.yml b/filebeat/module/apache/_meta/fields.yml index ed6de2fa81b..2da3f340f3f 100644 --- a/filebeat/module/apache/_meta/fields.yml +++ b/filebeat/module/apache/_meta/fields.yml @@ -62,7 +62,7 @@ fields: - name: device type: alias - path: user_agent.device + path: user_agent.device.name migration: true - name: major type: alias diff --git a/filebeat/module/apache/access/ingest/default.json b/filebeat/module/apache/access/ingest/default.json index 3c25b9c1746..f3df2bbddfe 100644 --- a/filebeat/module/apache/access/ingest/default.json +++ b/filebeat/module/apache/access/ingest/default.json @@ -31,7 +31,7 @@ "date": { "field": "apache.access.time", "target_field": "@timestamp", - "formats": ["dd/MMM/YYYY:H:m:s Z"] + "formats": ["dd/MMM/yyyy:H:m:s Z"] } }, { "remove": { @@ -42,6 +42,12 @@ "field": "apache.access.agent", "ignore_failure": true } + }, { + "rename": { + "field": "user_agent.device", + "target_field": "user_agent.device.name", + "ignore_missing": true + } }, { "rename": { "field": "user_agent.os", diff --git a/filebeat/module/apache/access/test/test.log-expected.json b/filebeat/module/apache/access/test/test.log-expected.json index 074b448c097..bb6ca0490ae 100644 --- a/filebeat/module/apache/access/test/test.log-expected.json +++ b/filebeat/module/apache/access/test/test.log-expected.json @@ -35,7 +35,7 @@ "source.ip": "192.168.33.1", "url.original": "/hello", "user.name": "-", - "user_agent.device": "Other", + "user_agent.device.name": "Other", "user_agent.major": "50", "user_agent.minor": "0", "user_agent.name": "Firefox", @@ -77,7 +77,7 @@ "source.ip": "172.17.0.1", "url.original": "/stringpatch", "user.name": "-", - "user_agent.device": "Other", + "user_agent.device.name": "Other", "user_agent.major": "15", "user_agent.minor": "0", "user_agent.name": "Firefox Alpha", @@ -104,7 +104,7 @@ "source.domain": "monitoring-server", "url.original": "/status", "user.name": "-", - "user_agent.device": "Other", + "user_agent.device.name": "Other", "user_agent.major": "15", "user_agent.minor": "0", "user_agent.name": "Firefox Alpha", diff --git a/filebeat/module/apache/error/ingest/pipeline.json b/filebeat/module/apache/error/ingest/pipeline.json index 3e841303368..07cb14ef16d 100644 --- a/filebeat/module/apache/error/ingest/pipeline.json +++ b/filebeat/module/apache/error/ingest/pipeline.json @@ -18,7 +18,7 @@ "date": { "field": "apache.error.timestamp", "target_field": "@timestamp", - "formats": ["EEE MMM dd H:m:s YYYY", "EEE MMM dd H:m:s.SSSSSS YYYY"], + "formats": ["EEE MMM dd H:m:s yyyy", "EEE MMM dd H:m:s.SSSSSS yyyy"], "ignore_failure": true } }, diff --git a/filebeat/module/apache/fields.go b/filebeat/module/apache/fields.go index bc2ad9b2cd5..08c88521198 100644 --- a/filebeat/module/apache/fields.go +++ b/filebeat/module/apache/fields.go @@ -32,5 +32,5 @@ func init() { // AssetApache returns asset data. // This is the base64 encoded gzipped contents of module/apache. func AssetApache() string { - return "eJysmM+OozgQxu95ilLf24c95rDSaqXVHmakkbrvyDEV8DS4mHKRFm8/gkBP/hhiQzi1TNf3/eyqGLte4QO7PehGmxJ3AGKlwj28/DMMvOwAcvSGbSOW3B7+3gEAnF/Cd8rbqg/yJbFkhtzRFnsQbvvBo8Uq9/sh4BWcrnGy+WsYA5CuwT0UTG0zjgS8Br/Kao8ejsRw0ObjU3MOhupGiz3YykoHn1ZKoCqfLEb7UeKS5YrHGPT+aziEFAq/lGCsSTCzzdXbSUn35DdvGi3lHjy1bFDpPOdrhP6pbcH6vArjat4be1+phknIUJXofV4jdZ69WhCK5TC2KZE3UwRlYhgOlHeZRyfq0Ane2j0CKUUaxegbch5VrxWUiQFpPXLW/5mI0MepQFyMZ41SUr5uzr9a9KKCClHT5dTSa7lSxLawTq8pth47OyF7S27NjMOhMc5TfWSG8tTsXheYFy2tD+nEcRyROfnHdpXvGY0Ye12gkxXFnQ2Bsamf/23N+99u2jCzcV9K5niy5jYLy1MKTmtWZ2lJL0Fq/ZNu07GCY04mGsO6p2DMyMRiNFpMuR1jTiYWI7Afr6CYUYmFoJBJIgJ5dWyrKvRhSkPJnlSn5DeXag/znGrtYTYWLPm5tU1n2Zqk8A67hmVeKeZLUSDNHIrX7NSGnFiHTrYs9HjmLpDUQ73Y9TbUOuEus55Cn/RVaA8VY+EqMsO/bYdaUIqFYSwsuSflb1ksOnlWumcV1IJU4go9r5QeC86hTUjIfLUzpl+QKzxh6i2hokKF4uKuQ97rIvWIHo6K8Wts6t2rYRquvPeRMX6y2k9KRp2rVa711OtJMR5v+EMNqaDC3cF/B3ftot2lWUy36NydOtekGoc3doBmrPrnX3KirfOjxdCkkhInjP/f33/AG/IJeTTrq/uLK8QGKV2eD+w+iW9TusDbP29v32BShfFO/IdoFmShzbMa46w5mKiVu8+Cy39jTpjqy6QMukl5WKj/VVN/L3HU7BORt8a6YiCsqCgwn/YjtfsdAAD//9uTc4g=" + return "eJysmE2PszYQx+/5FKPnvj70mEOlqlLVQytV2r0jx0zAXfDQ8ZAV377ibZ+8GGJDOEUm8///7BkMnjf4xO4IutGmxAOAWKnwCD9+GwZ+HABy9IZtI5bcEX49AACMN+FvytuqD/IlsWSG3NkWRxBu+8GzxSr3xyHgDZyucbb5ZRgDkK7BIxRMbTONBLwGv8pqjx7OxHDS5vNLcw6G6kaLPdnKSgdfVkqgKp8tJvtJ4prlhscY9P57OIQUCr+WYKxJMLPNzd1ZSffkd3caLeURPLVsUOk851uE/qptwXpchWk1H429r1TDJGSoSvQe10iNs1crQrEcxjYl8m6KoEwMw4nyLvPoRJ06wXu7ZyClSKMYfUPOo+q1gjIxIK1HzvqfiQh9nArExXjWKCXl2+b8X4teVFAharqcWnotV4rYFtbpLcXWY2cXZG/JbZlxODTGea6PzFCemt3bAvOipfUhnTiOMzInP2w3+V7QiLHXBTrZUNzZEBib+uVna9n/ftOGhY37WjLHizX3WVifUnBao07oAQ7Nbomm1v/SfU42wCzJRGNY9xKMBZlYjEaLKfdjLMnEYizkNJFiZ2VQyCQRgbw6t1UVejuloWQvqlPyu0u1h3lNtfYwOwuW/NLaprPsTVJ4m93CsqwU87ookBa+jLds14acWIdO9iz09OFdIKmnerHrbah1wl1mPYXe65vQnirGwlVkhr/th1pRioVhLCy5F+VvXSw6eVa6VxXUilTiCr2ulJ4LLqHNSMh8szOmn5IrvGDqUaGiQoXi4s5E3usi9Ts9HBXj19jUA1jDNJx7HyNj/GSzn5SMOlebXOu54ZNiPB3zhxpSQYWHr/8DPPSMDtdmMS2jsUU11qSahne2gRas+ut3cqKt85PF0KmSEmeMPz8+/oF35AvyZNZX9zdXiA1SWj2f2H0R36d0hbe/3t//glkVpoPxT6JFkJVez2aMUXMwURt3nxWXP6acMNXXSRl0k/KwUv+bpv5R4qTZJyJvjXXFQFhRUWA+70fq8H8AAAD//18WdVc=" } diff --git a/filebeat/module/auditd/_meta/fields.yml b/filebeat/module/auditd/_meta/fields.yml index c3d79ce0594..f2625872097 100644 --- a/filebeat/module/auditd/_meta/fields.yml +++ b/filebeat/module/auditd/_meta/fields.yml @@ -4,6 +4,122 @@ Module for parsing auditd logs. short_config: true fields: + + - name: user + type: group + fields: + + - name: terminal + type: keyword + description: > + Terminal or tty device on which the user is performing the observed activity. + + - name: audit + type: group + fields: + - name: id + type: keyword + description: > + One or multiple unique identifiers of the user. + - name: name + type: keyword + example: albert + description: > + Short name or login of the user. + + - name: group.id + type: keyword + description: > + Unique identifier for the group on the system/platform. + - name: group.name + type: keyword + description: > + Name of the group. + + - name: effective + type: group + fields: + - name: id + type: keyword + description: > + One or multiple unique identifiers of the user. + - name: name + type: keyword + example: albert + description: > + Short name or login of the user. + - name: group.id + type: keyword + description: > + Unique identifier for the group on the system/platform. + - name: group.name + type: keyword + description: > + Name of the group. + + - name: filesystem + type: group + fields: + - name: id + type: keyword + description: > + One or multiple unique identifiers of the user. + - name: name + type: keyword + example: albert + description: > + Short name or login of the user. + - name: group.id + type: keyword + description: > + Unique identifier for the group on the system/platform. + - name: group.name + type: keyword + description: > + Name of the group. + + - name: owner + type: group + fields: + - name: id + type: keyword + description: > + One or multiple unique identifiers of the user. + - name: name + type: keyword + example: albert + description: > + Short name or login of the user. + - name: group.id + type: keyword + description: > + Unique identifier for the group on the system/platform. + - name: group.name + type: keyword + description: > + Name of the group. + + - name: saved + type: group + fields: + - name: id + type: keyword + description: > + One or multiple unique identifiers of the user. + - name: name + type: keyword + example: albert + description: > + Short name or login of the user. + - name: group.id + type: keyword + description: > + Unique identifier for the group on the system/platform. + - name: group.name + type: keyword + description: > + Name of the group. + - name: auditd type: group description: > diff --git a/filebeat/module/auditd/fields.go b/filebeat/module/auditd/fields.go index 75a006b5d2f..41ffa6beb71 100644 --- a/filebeat/module/auditd/fields.go +++ b/filebeat/module/auditd/fields.go @@ -32,5 +32,5 @@ func init() { // AssetAuditd returns asset data. // This is the base64 encoded gzipped contents of module/auditd. func AssetAuditd() string { - return "eJy8VsGO2zYQvfsrHnJKD2vk7AIFimxTGGibou3docmxNFiao5LDddWvL0jZXkWWNzWyG95Eke+9eTMc8g4P1K9gsmN1C0BZPa3w5sc68WYBOEo2cqcsYYUfFgDwq7jsCTuJ6ExMHJojALw0abkAUitRN1bCjpsVNGZaADsm79KqQtwhmD2NiMvQvqMVmii5O87MkJfxoSJhF2UPbWnKXsaYbEzopTnPzTE+wzrH/AuH/M/AX6CX+E0UxvsjP0wkOLF5T0HJoaVI2JI1OdFnuNpSPyzug9mzhQkOjyb22PZHeHqkoFXxcrR1Guc41khWotuULZ/9fzbCMv46eXqF9IlCvNuYzO42/A8Si1scBvwEbTmBU7VUvDuSr++RE7laaOVPThTRRS6fcgFaMSrqvNRAh5eWGuhwlrocuba+hzUBWxr0z4mNxhJ2WXOkM7I8RVmmwLsywRHsKChrD9ua0FDCW88P05yiFJbsy2mMIvrd9YQlSi+cr0QpsYRXyNjLai0Je9K6xFoniYIywVyA1jhUJgnb9mOw2RAS/Z0p2Gmuhr7jJTRfdzBP8Ah5v6U4r8FYq7fT1JCNtZKDViCYlMSyKX3swNpWQ6uMedru1qNWWNf3kFr16KJYSukK9muCs9L+xqIr6EMKCkMFAAeY8JxBZdntNGXX0PXBwbE1SgmHlm07/JKspyhV1PipruUMKqfTKk74l6LcbU0i9z0MHo3PVDa/w55MSGA9HaYdx6QV9Erdvbs9ugHTxKbemKeWmPpUQrPG+3mqeGuXKFyRUvZns0YceJuyLfUBidgZ9jnSlXbakHA3e7inj4r/Ieq9BDUcEn4mWf8ODjuJe1NWozFa3g4ONTGQUDV/Gh4+y/LyMM7FTxeQtVCW+Bh8jy5SKqYO18qR5CdvkrJNZKJt0flcOicnmEfD3mw9lZfIXD9000Kae4mMrbISlAMF3ZTvi2Un3x6oP0i8pPyiezidw9Krjkk9c15W/VkWa/8tFbH218VEaljCN5QzED7jTmn/sd9wko0V90qq3g8sWP/5EYXluh4vth6Iqzoakk0nHKY33g3+lGuZNbta+vBG68cXk/a6Fv1RSUYO/RcAAP//EHqm1w==" + return "eJzsWk2P2zYQve+vGKSX9hA1px5coECBIkCANAXa9NDTgiZHMrEURyGH9qq/viAl+VOyLdpBerAPAVYW5703fCRn6LyFF2wXIILSrJ4AWLPBBbz5NT148wSg0EunG9ZkF/DLEwDA76SCQSjJQSOc17bqA4ChyhdPAH5Fjp8l2VJXC2AX8Amg1GiUXzylGG/BihoXEDy69ACA2wYXUDkKTf/kYMT+KEZXayvM9oth9Au2G3Jq7/kI/+HzuY8C5IC5BYVrLRHIwmal5Qp4hYkfaA8NupLi61V6TEuPbo0KhGS91twWpyRTTk4Y7uvb17hPbIig1cHjaZEXhMbPHxajzjoY1o1BCFZ/CQhaoWVdanQeqNxKLkb5xH9nMMJXUTfRTsIs0fE8vn9FCyXESNtQpe0hwVGGKbvFPfP293Geku8jjYQV3RL/8K1nrH9sjODok/H8dexmZvECv/j5lLJU7liNuBHLEqNX8eHIr+XIMYIPQ04ZstQGO5IPRz4c+X9wJG3sthiBhxkfZvyWZvRijephxocZv5UZD9oYNdmjTSC8T8aE0lGdIh81iDDi3QHQUHXR92d0HSN/1Da8dvgxdAGfiEEY0+ODcAiKZKjRMipYoUNYohTBHyaaV9h2L7dW1FqCsArWwrWwbPvwuEbLiXFx5Rolo55FOLHchWl7v/V0QvTAK+1jl5paU6N6Oh9+i2ZXW8+lXrZxOv5JJ0FTjBR1Yvni5t5ULW62VAv4PNgkEpfCwhI7/mNknZAIZeDgcBuZdirjI9DJ1Nr1649bkCthK/TwvdEvx8sI4qxT6vAdEf8wnoU4YR79nefLo/ea7FeYsftyjRO241rABz6aKGCNIE6CJh1MRxO2bPeDjUrw+CWgleNbniFbzRO3s1i3VIfwYEO9nDoWNGM9M4kRpwsZd9YUALQF0Sd1Gmc+TBzVbTGgrdJSMPr+/ip9RYGH3Z2JhTnmVYxE1X54S3v4Fx29XQqP6mcQsBYmpOPiHdQorAfNgzlK7TynoOP6mNtZJ1eprZ5U/vkfCP193f41nQvWxhXcOKqcqD3QxNoQ7+anuhMoXJXOimG/6Q5vkMKYCSil3Khw3czS/CfWxJjCoY8pF3ygXZK1KDnKj4ffOBnX0EktdWY1XcGnSZXWmfVj7pWAjySjf5E35F6GPExg3k9mhzqqcny6pRxHFkaL432kEbzqLsHHKrtaV050vPo79FO4ZqJenUZrHMmYuNORV+HdAJiH6E6OsEuA3SZLgSVl5dShJKeeI0wWspARIAO4QjpZE+MFMEwUlvvRJFnWFi0/j3jrvJqdIk/BSSwqpOJivHMSD4kFy6591p6eJam7ULsY8VpyhqQYmb4MUmciXUvGYaXJ3mn+zge7evI0t/cy1JlQMzN0PytdDjhF7eDV74ZGdImGNrCJbaUl7g4bjMUa/FS8wjJw951nbQwsMVUv1AQjGNVEQeHkaua+tCLPRRynGWWswXM2p9kbfzrXJm5VrkE87TavQszCEpnyUjtxi8iRnnoGcBZk6TPF7n6zuUVx6TMl78FnAWOm7O1vp7eoxkzRO/As2NypTvfPt+jNneQOOAuSMrWmH35u0UqZWjvgLEhJ9fFtwbV1eGanga9zq+EBEF9RBhZLkwM78n99rs7vxNhrYGt/3CZeQqzRe1HlSPROzsTq65W+B86AVH5uk6rQs7Yp7pW4/wUAAP//yO3oSA==" } diff --git a/filebeat/module/auditd/log/_meta/fields.yml b/filebeat/module/auditd/log/_meta/fields.yml index 55607c9fa44..d21f138c682 100644 --- a/filebeat/module/auditd/log/_meta/fields.yml +++ b/filebeat/module/auditd/log/_meta/fields.yml @@ -4,9 +4,6 @@ Fields from the Linux audit log. Not all fields are documented here because they are dynamic and vary by audit event type. fields: - - name: record_type - description: > - The audit event type. - name: old_auid description: > For login events this is the old audit ID used for the user prior to @@ -28,15 +25,6 @@ type: long description: > The audit event sequence number. - - name: acct - description: > - The user account name associated with the event. - - name: pid - description: > - The ID of the process. - - name: ppid - description: > - The ID of the process. - name: items description: > The number of items in an event. @@ -44,40 +32,152 @@ description: > The item field indicates which item out of the total number of items. This number is zero-based; a value of 0 means it is the first item. + - name: tty + type: keyword + definition: > + TTY udevice the user is running programs on. - name: a0 description: > The first argument to the system call. + - name: addr + type: ip + definition: > + Remote address that the user is connecting from. + - name: rport + type: long + definition: > + Remote port number. + - name: laddr + type: ip + definition: > + Local network address. + - name: lport + type: long + definition: > + Local port number. + + - name: acct + type: alias + path: user.name + migration: true + - name: pid + type: alias + path: process.pid + migration: true + - name: ppid + type: alias + path: process.ppid + migration: true - name: res - description: > - The result of the system call (success or failure). + type: alias + path: event.outcome + migration: true + - name: record_type + type: alias + path: event.action + migration: true - name: geoip type: group - description: > - Contains GeoIP information gathered based on the `auditd.log.addr` - field. Only present if the GeoIP Elasticsearch plugin is available and - used. fields: - name: continent_name - type: keyword - description: > - The name of the continent. - - name: city_name - type: keyword - description: > - The name of the city. - - name: region_name - type: keyword - description: > - The name of the region. + type: alias + path: source.geo.continent_name + migration: true - name: country_iso_code - type: keyword - description: > - Country ISO code. + type: alias + path: source.geo.country_iso_code + migration: true - name: location - type: geo_point - description: > - The longitude and latitude. + type: alias + path: source.geo.location + migration: true + - name: region_name + type: alias + path: source.geo.region_name + migration: true + - name: city_name + type: alias + path: source.geo.city_name + migration: true - name: region_iso_code - type: keyword - description: > - Region ISO code. + type: alias + path: source.geo.region_iso_code + migration: true + + # Fields below were not defined in 6.x but were still being populated. + - name: arch + type: alias + path: host.architecture + migration: true + - name: gid + type: alias + path: user.group.id + migration: true + - name: uid + type: alias + path: user.id + migration: true + - name: agid + type: alias + path: user.audit.group.id + migration: true + - name: auid + type: alias + path: user.audit.id + migration: true + - name: fsgid + type: alias + path: user.filesystem.group.id + migration: true + - name: fsuid + type: alias + path: user.filesystem.id + migration: true + - name: egid + type: alias + path: user.effective.group.id + migration: true + - name: euid + type: alias + path: user.effective.id + migration: true + - name: sgid + type: alias + path: user.saved.group.id + migration: true + - name: suid + type: alias + path: user.saved.id + migration: true + - name: ogid + type: alias + path: user.owner.group.id + migration: true + - name: ouid + type: alias + path: user.owner.id + migration: true + - name: comm + type: alias + path: process.name + migration: true + - name: exe + type: alias + path: process.executable + migration: true + - name: terminal + type: alias + path: user.terminal + migration: true + - name: msg + type: alias + path: message + migration: true + - name: src + type: alias + path: source.address + migration: true + - name: dst + type: alias + path: destination.address + migration: true diff --git a/filebeat/module/auditd/log/ingest/pipeline.json b/filebeat/module/auditd/log/ingest/pipeline.json index f4911f86335..dfdeef3731b 100644 --- a/filebeat/module/auditd/log/ingest/pipeline.json +++ b/filebeat/module/auditd/log/ingest/pipeline.json @@ -53,6 +53,7 @@ "ignore_failure": true } }, + { "date": { "field": "auditd.log.epoch", @@ -69,6 +70,7 @@ "ignore_failure": true } }, + { "convert": { "field": "auditd.log.sequence", @@ -86,10 +88,56 @@ } } }, + + { "rename": { "ignore_failure": true, "field": "auditd.log.arch", "target_field": "host.architecture" } }, + + { "rename": { "ignore_failure": true, "field": "auditd.log.acct", "target_field": "user.name" } }, + + { "rename": { "ignore_failure": true, "field": "auditd.log.uid", "target_field": "user.id" } }, + { "rename": { "ignore_failure": true, "field": "auditd.log.gid", "target_field": "user.group.id" } }, + { "rename": { "ignore_failure": true, "field": "auditd.log.agid", "target_field": "user.audit.group.id" } }, + { "rename": { "ignore_failure": true, "field": "auditd.log.auid", "target_field": "user.audit.id" } }, + { "rename": { "ignore_failure": true, "field": "auditd.log.fsgid", "target_field": "user.filesystem.group.id" } }, + { "rename": { "ignore_failure": true, "field": "auditd.log.fsuid", "target_field": "user.filesystem.id" } }, + { "rename": { "ignore_failure": true, "field": "auditd.log.egid", "target_field": "user.effective.group.id" } }, + { "rename": { "ignore_failure": true, "field": "auditd.log.euid", "target_field": "user.effective.id" } }, + { "rename": { "ignore_failure": true, "field": "auditd.log.sgid", "target_field": "user.saved.group.id" } }, + { "rename": { "ignore_failure": true, "field": "auditd.log.suid", "target_field": "user.saved.id" } }, + { "rename": { "ignore_failure": true, "field": "auditd.log.ogid", "target_field": "user.owner.group.id" } }, + { "rename": { "ignore_failure": true, "field": "auditd.log.ouid", "target_field": "user.owner.id" } }, + + { "rename": { "ignore_failure": true, "field": "auditd.log.comm", "target_field": "process.name" } }, + { "rename": { "ignore_failure": true, "field": "auditd.log.exe", "target_field": "process.executable" } }, + { "rename": { "ignore_failure": true, "field": "auditd.log.pid", "target_field": "process.pid" } }, + { "rename": { "ignore_failure": true, "field": "auditd.log.ppid", "target_field": "process.ppid" } }, + { "convert": { "ignore_missing": true, "field": "process.pid", "type": "long" } }, + { "convert": { "ignore_missing": true, "field": "process.ppid", "type": "long" } }, + { "rename": { "ignore_failure": true, "field": "auditd.log.cmd", "target_field": "process.args" } }, + { "split": { "ignore_failure": true, "field": "process.args", "separator": "\\s+" } }, + + { "rename": { "ignore_failure": true, "field": "auditd.log.terminal", "target_field": "user.terminal" } }, + + { "rename": { "ignore_failure": true, "field": "auditd.log.msg", "target_field": "message" } }, + { "rename": { "ignore_failure": true, "field": "auditd.log.res", "target_field": "event.outcome" } }, + { "rename": { "ignore_failure": true, "field": "auditd.log.record_type", "target_field": "event.action" } }, + { "lowercase": { "ignore_failure": true, "field": "event.action" } }, + + + { "rename": { "ignore_failure": true, "field": "auditd.log.src", "target_field": "source.address" } }, + + { "rename": { "ignore_failure": true, "field": "auditd.log.dst", "target_field": "destination.address" } }, + + { + "grok": { + "field": "source.address", + "patterns": [ "^%{IP:source.ip}$" ], + "ignore_failure": true + } + }, { "geoip": { - "field": "auditd.log.addr", - "target_field": "auditd.log.geoip", + "field": "source.ip", + "target_field": "source.geo", "ignore_failure": true } } diff --git a/filebeat/module/auditd/log/test/audit-rhel6.log-expected.json b/filebeat/module/auditd/log/test/audit-rhel6.log-expected.json new file mode 100644 index 00000000000..6ae5934e328 --- /dev/null +++ b/filebeat/module/auditd/log/test/audit-rhel6.log-expected.json @@ -0,0 +1,271 @@ +[ + { + "@timestamp": "2017-03-14T19:20:30.178Z", + "auditd.log.sequence": 19600327, + "auditd.log.ses": "11988", + "ecs.version": "1.0.0-beta2", + "event.action": "user_end", + "event.dataset": "auditd.log", + "event.module": "auditd", + "event.outcome": "success", + "fileset.name": "log", + "input.type": "log", + "log.offset": 0, + "message": "op=PAM:session_close", + "process.executable": "/usr/bin/sudo", + "process.pid": 4121, + "service.type": "auditd", + "user.audit.id": "700", + "user.id": "0", + "user.name": "root" + }, + { + "@timestamp": "2017-03-14T19:20:30.178Z", + "auditd.log.sequence": 19600328, + "auditd.log.ses": "11988", + "ecs.version": "1.0.0-beta2", + "event.action": "cred_disp", + "event.dataset": "auditd.log", + "event.module": "auditd", + "event.outcome": "success", + "fileset.name": "log", + "input.type": "log", + "log.offset": 189, + "message": "op=PAM:setcred", + "process.executable": "/usr/bin/sudo", + "process.pid": 4121, + "service.type": "auditd", + "user.audit.id": "700", + "user.id": "0", + "user.name": "root" + }, + { + "@timestamp": "2017-03-14T19:20:56.192Z", + "auditd.log.sequence": 19600329, + "auditd.log.ses": "11988", + "ecs.version": "1.0.0-beta2", + "event.action": "user_cmd", + "event.dataset": "auditd.log", + "event.module": "auditd", + "event.outcome": "success", + "fileset.name": "log", + "input.type": "log", + "log.offset": 373, + "message": "cwd=\"/", + "process.args": [ + "/usr/lib64/nagios/plugins/check_asterisk_sip_peers", + "-p", + "202" + ], + "process.pid": 4151, + "service.type": "auditd", + "user.audit.id": "700", + "user.id": "497" + }, + { + "@timestamp": "2017-03-14T19:20:56.193Z", + "auditd.log.sequence": 19600330, + "auditd.log.ses": "11988", + "ecs.version": "1.0.0-beta2", + "event.action": "cred_acq", + "event.dataset": "auditd.log", + "event.module": "auditd", + "event.outcome": "success", + "fileset.name": "log", + "input.type": "log", + "log.offset": 620, + "message": "op=PAM:setcred", + "process.executable": "/usr/bin/sudo", + "process.pid": 4151, + "service.type": "auditd", + "user.audit.id": "700", + "user.id": "0", + "user.name": "root" + }, + { + "@timestamp": "2017-03-14T19:20:56.193Z", + "auditd.log.sequence": 19600331, + "auditd.log.ses": "11988", + "ecs.version": "1.0.0-beta2", + "event.action": "user_start", + "event.dataset": "auditd.log", + "event.module": "auditd", + "event.outcome": "success", + "fileset.name": "log", + "input.type": "log", + "log.offset": 803, + "message": "op=PAM:session_open", + "process.executable": "/usr/bin/sudo", + "process.pid": 4151, + "service.type": "auditd", + "user.audit.id": "700", + "user.id": "0", + "user.name": "root" + }, + { + "@timestamp": "2017-03-14T19:23:02.529Z", + "auditd.log.dst_prefixlen": "22", + "auditd.log.op": "SPD-add", + "auditd.log.sequence": 19600354, + "auditd.log.ses": "4294967295", + "auditd.log.src_prefixlen": "16", + "destination.address": "10.100.4.0", + "ecs.version": "1.0.0-beta2", + "event.action": "mac_ipsec_event", + "event.dataset": "auditd.log", + "event.module": "auditd", + "event.outcome": "1", + "fileset.name": "log", + "input.type": "log", + "log.offset": 993, + "service.type": "auditd", + "source.address": "10.100.0.0", + "source.ip": "10.100.0.0", + "user.audit.id": "4294967295" + }, + { + "@timestamp": "2017-03-14T19:23:02.529Z", + "auditd.log.a0": "9", + "auditd.log.a1": "7f564ee6d2a0", + "auditd.log.a2": "b8", + "auditd.log.a3": "0", + "auditd.log.exit": "184", + "auditd.log.items": "0", + "auditd.log.sequence": 19600354, + "auditd.log.ses": "4294967295", + "auditd.log.success": "yes", + "auditd.log.syscall": "44", + "auditd.log.tty": "(none)", + "ecs.version": "1.0.0-beta2", + "event.action": "syscall", + "event.dataset": "auditd.log", + "event.module": "auditd", + "fileset.name": "log", + "host.architecture": "x86_64", + "input.type": "log", + "log.offset": 1162, + "process.executable": "/usr/libexec/strongswan/charon (deleted)", + "process.name": "charon", + "process.pid": 1275, + "process.ppid": 1240, + "service.type": "auditd", + "user.audit.id": "4294967295", + "user.effective.group.id": "0", + "user.effective.id": "0", + "user.filesystem.group.id": "0", + "user.filesystem.id": "0", + "user.group.id": "0", + "user.id": "0", + "user.saved.group.id": "0", + "user.saved.id": "0" + }, + { + "@timestamp": "2017-03-16T04:02:40.072Z", + "auditd.log.new_auid": "700", + "auditd.log.new_ses": "12286", + "auditd.log.old_auid": "700", + "auditd.log.old_ses": "6793", + "auditd.log.sequence": 19623791, + "ecs.version": "1.0.0-beta2", + "event.action": "login", + "event.dataset": "auditd.log", + "event.module": "auditd", + "fileset.name": "log", + "input.type": "log", + "log.offset": 1524, + "process.pid": 28281, + "service.type": "auditd", + "user.id": "0" + }, + { + "@timestamp": "2017-03-16T04:02:40.070Z", + "auditd.log.addr": "96.241.146.97", + "auditd.log.direction": "both", + "auditd.log.kind": "session", + "auditd.log.laddr": "107.170.139.210", + "auditd.log.lport": "50022", + "auditd.log.rport": "58994", + "auditd.log.sequence": 19623788, + "auditd.log.ses": "6793", + "auditd.log.spid": "28282", + "ecs.version": "1.0.0-beta2", + "event.action": "crypto_key_user", + "event.dataset": "auditd.log", + "event.module": "auditd", + "event.outcome": "success", + "fileset.name": "log", + "input.type": "log", + "log.offset": 1640, + "message": "op=destroy", + "process.executable": "/usr/sbin/sshd", + "process.pid": 28281, + "service.type": "auditd", + "user.audit.id": "700", + "user.id": "0", + "user.saved.id": "74" + }, + { + "@timestamp": "2017-03-16T04:02:40.072Z", + "auditd.log.addr": "96.241.146.97", + "auditd.log.sequence": 19623789, + "auditd.log.ses": "6793", + "ecs.version": "1.0.0-beta2", + "event.action": "user_auth", + "event.dataset": "auditd.log", + "event.module": "auditd", + "event.outcome": "success", + "fileset.name": "log", + "input.type": "log", + "log.offset": 1926, + "message": "op=success", + "process.executable": "/usr/sbin/sshd", + "process.pid": 28281, + "service.type": "auditd", + "user.audit.id": "700", + "user.id": "0", + "user.name": "admin", + "user.terminal": "ssh" + }, + { + "@timestamp": "2017-03-16T04:02:57.804Z", + "auditd.log.sequence": 19623807, + "auditd.log.ses": "12286", + "ecs.version": "1.0.0-beta2", + "event.action": "user_auth", + "event.dataset": "auditd.log", + "event.module": "auditd", + "event.outcome": "success", + "fileset.name": "log", + "input.type": "log", + "log.offset": 2122, + "message": "op=PAM:authentication", + "process.executable": "/bin/su", + "process.pid": 28395, + "service.type": "auditd", + "user.audit.id": "700", + "user.id": "0", + "user.name": "root", + "user.terminal": "pts/0" + }, + { + "@timestamp": "2017-03-16T04:02:57.805Z", + "auditd.log.sequence": 19623808, + "auditd.log.ses": "12286", + "ecs.version": "1.0.0-beta2", + "event.action": "user_acct", + "event.dataset": "auditd.log", + "event.module": "auditd", + "event.outcome": "success", + "fileset.name": "log", + "input.type": "log", + "log.offset": 2312, + "message": "op=PAM:accounting", + "process.executable": "/bin/su", + "process.pid": 28395, + "service.type": "auditd", + "user.audit.id": "700", + "user.id": "0", + "user.name": "root", + "user.terminal": "pts/0" + } +] \ No newline at end of file diff --git a/filebeat/module/auditd/log/test/test.log b/filebeat/module/auditd/log/test/test.log index 6ee88f1a1a4..73a7c6ed097 100644 --- a/filebeat/module/auditd/log/test/test.log +++ b/filebeat/module/auditd/log/test/test.log @@ -1,2 +1,4 @@ type=MAC_IPSEC_EVENT msg=audit(1485893834.891:18877201): op=SPD-delete auid=4294967295 ses=4294967295 res=1 src=192.168.2.0 src_prefixlen=24 dst=192.168.0.0 dst_prefixlen=16 type=SYSCALL msg=audit(1485893834.891:18877199): arch=c000003e syscall=44 success=yes exit=184 a0=9 a1=7f564b2672a0 a2=b8 a3=0 items=0 ppid=1240 pid=1281 auid=4294967295 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=(none) ses=4294967295 comm="charon" exe=2F7573722F6C6962657865632F7374726F6E677377616E2F636861726F6E202864656C6574656429 key=(null) +type=USER_CMD msg=audit(1489519256.192:19600329): user pid=4151 uid=497 auid=700 ses=11988 msg='cwd="/" cmd=2F7573722F6C696236342F6E6167696F732F706C7567696E732F636865636B5F617374657269736B5F7369705F7065657273202D7020323032 terminal=? res=success' +type=CRYPTO_SESSION msg=audit(1481077041.515:406): pid=1298 uid=0 auid=4294967295 ses=4294967295 subj=system_u:system_r:sshd_t:s0-s0:c0.c1023 msg='op=start direction=from-server cipher=chacha20-poly1305@openssh.com ksize=512 mac= pfs=curve25519-sha256@libssh.org spid=1299 suid=74 rport=63927 laddr=10.142.0.2 lport=22 exe="/usr/sbin/sshd" hostname=? addr=96.241.146.97 terminal=? res=success' diff --git a/filebeat/module/auditd/log/test/test.log-expected.json b/filebeat/module/auditd/log/test/test.log-expected.json index 2ba5e02881d..569e2aa5885 100644 --- a/filebeat/module/auditd/log/test/test.log-expected.json +++ b/filebeat/module/auditd/log/test/test.log-expected.json @@ -1,23 +1,24 @@ [ { "@timestamp": "2017-01-31T20:17:14.891Z", - "auditd.log.auid": "4294967295", - "auditd.log.dst": "192.168.0.0", "auditd.log.dst_prefixlen": "16", "auditd.log.op": "SPD-delete", - "auditd.log.record_type": "MAC_IPSEC_EVENT", - "auditd.log.res": "1", "auditd.log.sequence": 18877201, "auditd.log.ses": "4294967295", - "auditd.log.src": "192.168.2.0", "auditd.log.src_prefixlen": "24", + "destination.address": "192.168.0.0", "ecs.version": "1.0.0-beta2", + "event.action": "mac_ipsec_event", "event.dataset": "auditd.log", "event.module": "auditd", + "event.outcome": "1", "fileset.name": "log", "input.type": "log", "log.offset": 0, - "service.type": "auditd" + "service.type": "auditd", + "source.address": "192.168.2.0", + "source.ip": "192.168.2.0", + "user.audit.id": "4294967295" }, { "@timestamp": "2017-01-31T20:17:14.891Z", @@ -25,34 +26,87 @@ "auditd.log.a1": "7f564b2672a0", "auditd.log.a2": "b8", "auditd.log.a3": "0", - "auditd.log.arch": "x86_64", - "auditd.log.auid": "4294967295", - "auditd.log.comm": "charon", - "auditd.log.egid": "0", - "auditd.log.euid": "0", - "auditd.log.exe": "/usr/libexec/strongswan/charon (deleted)", "auditd.log.exit": "184", - "auditd.log.fsgid": "0", - "auditd.log.fsuid": "0", - "auditd.log.gid": "0", "auditd.log.items": "0", - "auditd.log.pid": "1281", - "auditd.log.ppid": "1240", - "auditd.log.record_type": "SYSCALL", "auditd.log.sequence": 18877199, "auditd.log.ses": "4294967295", - "auditd.log.sgid": "0", "auditd.log.success": "yes", - "auditd.log.suid": "0", "auditd.log.syscall": "44", "auditd.log.tty": "(none)", - "auditd.log.uid": "0", "ecs.version": "1.0.0-beta2", + "event.action": "syscall", "event.dataset": "auditd.log", "event.module": "auditd", "fileset.name": "log", + "host.architecture": "x86_64", "input.type": "log", "log.offset": 174, - "service.type": "auditd" + "process.executable": "/usr/libexec/strongswan/charon (deleted)", + "process.name": "charon", + "process.pid": 1281, + "process.ppid": 1240, + "service.type": "auditd", + "user.audit.id": "4294967295", + "user.effective.group.id": "0", + "user.effective.id": "0", + "user.filesystem.group.id": "0", + "user.filesystem.id": "0", + "user.group.id": "0", + "user.id": "0", + "user.saved.group.id": "0", + "user.saved.id": "0" + }, + { + "@timestamp": "2017-03-14T19:20:56.192Z", + "auditd.log.sequence": 19600329, + "auditd.log.ses": "11988", + "ecs.version": "1.0.0-beta2", + "event.action": "user_cmd", + "event.dataset": "auditd.log", + "event.module": "auditd", + "event.outcome": "success", + "fileset.name": "log", + "input.type": "log", + "log.offset": 536, + "message": "cwd=\"/", + "process.args": [ + "/usr/lib64/nagios/plugins/check_asterisk_sip_peers", + "-p", + "202" + ], + "process.pid": 4151, + "service.type": "auditd", + "user.audit.id": "700", + "user.id": "497" + }, + { + "@timestamp": "2016-12-07T02:17:21.515Z", + "auditd.log.addr": "96.241.146.97", + "auditd.log.cipher": "chacha20-poly1305@openssh.com", + "auditd.log.direction": "from-server", + "auditd.log.ksize": "512", + "auditd.log.laddr": "10.142.0.2", + "auditd.log.lport": "22", + "auditd.log.pfs": "curve25519-sha256@libssh.org", + "auditd.log.rport": "63927", + "auditd.log.sequence": 406, + "auditd.log.ses": "4294967295", + "auditd.log.spid": "1299", + "auditd.log.subj": "system_u:system_r:sshd_t:s0-s0:c0.c1023", + "ecs.version": "1.0.0-beta2", + "event.action": "crypto_session", + "event.dataset": "auditd.log", + "event.module": "auditd", + "event.outcome": "success", + "fileset.name": "log", + "input.type": "log", + "log.offset": 783, + "message": "op=start", + "process.executable": "/usr/sbin/sshd", + "process.pid": 1298, + "service.type": "auditd", + "user.audit.id": "4294967295", + "user.id": "0", + "user.saved.id": "74" } ] \ No newline at end of file diff --git a/filebeat/module/elasticsearch/_meta/fields.yml b/filebeat/module/elasticsearch/_meta/fields.yml index a22b241f68d..02f9f4cac1b 100644 --- a/filebeat/module/elasticsearch/_meta/fields.yml +++ b/filebeat/module/elasticsearch/_meta/fields.yml @@ -7,6 +7,22 @@ type: group description: > fields: + - name: component + description: "Elasticsearch component from where the log event originated" + example: "o.e.c.m.MetaDataCreateIndexService" + type: keyword + - name: cluster.uuid + description: "UUID of the cluster" + example: "GmvrbHlNTiSVYiPf8kxg9g" + type: keyword + - name: cluster.name + description: "Name of the cluster" + example: "docker-cluster" + type: keyword + - 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 46e0b0530a0..ff0a13cd1a6 100644 --- a/filebeat/module/elasticsearch/audit/_meta/fields.yml +++ b/filebeat/module/elasticsearch/audit/_meta/fields.yml @@ -6,15 +6,19 @@ 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 - description: "The authentication realm" + description: "The authentication realm the authentication was validated against" + example": "default_file" + type: keyword + - name: user.realm + description: "The user's authentication realm, if authenticated" example": "active_directory" type: keyword - - name: roles + - name: user.roles description: "Roles to which the principal belongs" example: [ "kibana_user", "beats_admin" ] type: keyword @@ -22,14 +26,29 @@ description: "The name of the action that was executed" example: "cluster:monitor/main" type: keyword + - name: url.params + description: "REST URI parameters" + example: "{username=jacknich2}" + type: keyword + multi_fields: + - name: text + type: text - name: indices description: "Indices accessed by action" example: [ "foo-2019.01.04", "foo-2019.01.03", "foo-2019.01.06" ] type: keyword - - name: request + - name: request.id + description: "Unique ID of request" + example: "WzL_kb6VSvOhAq0twPvHOQ" + type: keyword + - name: request.name description: "The type of request that was executed" example: "ClearScrollRequest" type: keyword + - name: request_body + type: alias + path: http.request.body.content + migration: true - name: event_type type: alias path: event.type @@ -42,10 +61,6 @@ 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 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 00000000000..f0479f63170 --- /dev/null +++ b/filebeat/module/elasticsearch/audit/ingest/pipeline-json.json @@ -0,0 +1,159 @@ +{ + "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}" + ] + } + }, + { + "rename": { + "field": "elasticsearch.audit.origin.address", + "target_field": "source.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": "request.method", + "path": "elasticsearch.audit" + } + }, + { + "rename": { + "field": "elasticsearch.audit.request.method", + "target_field": "http.request.method", + "ignore_missing": true + } + + }, + { + "dot_expander": { + "field": "request.body", + "path": "elasticsearch.audit" + } + }, + { + "rename": { + "field": "elasticsearch.audit.request.body", + "target_field": "http.request.body.content", + "ignore_missing": true + } + } + ], + "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 00000000000..48d599ee030 --- /dev/null +++ b/filebeat/module/elasticsearch/audit/ingest/pipeline-plaintext.json @@ -0,0 +1,64 @@ +{ + "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\\=\\[%{DATA:user.name}\\])?", + "ES_AUDIT_REALM": "(realm\\=\\[%{WORD:elasticsearch.audit.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_URI_PARAMS": "(params=\\[%{DATA:elasticsearch.audit.url.params}\\])?", + "ES_AUDIT_INDICES": "(indices\\=\\[%{DATA:elasticsearch.audit.indices}\\])?", + "ES_AUDIT_REQUEST": "(request\\=\\[%{WORD:elasticsearch.audit.request.name}\\])?", + "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_URI_PARAMS},?\\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 9c97beb4574..25d5c464d7d 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 7ccb68c7e43..677b47eb590 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 82% rename from filebeat/module/elasticsearch/audit/test/test.log rename to filebeat/module/elasticsearch/audit/test/test-access.log index c631cc62837..b71ad94d177 100644 --- a/filebeat/module/elasticsearch/audit/test/test.log +++ b/filebeat/module/elasticsearch/audit/test/test-access.log @@ -6,3 +6,4 @@ [2018-06-19T05:55:26,898] [transport] [access_denied] origin_type=[rest], origin_address=[147.107.128.77], principal=[_anonymous], action=[cluster:monitor/main], request=[MainRequest] [2018-06-19T05:24:15,190] [v_VJhjV] [rest] [authentication_failed] origin_address=[172.18.0.3], principal=[elastic], uri=[/_nodes?filter_path=nodes.*.version%2Cnodes.*.http.publish_address%2Cnodes.*.ip], request_body=[body] [2019-01-08T14:15:02,011] [NodeName-0] [transport] [access_granted] origin_type=[transport], origin_address=[192.168.2.1], principal=[username], realm=[active_directory], roles=[kibana_user,my_custom_role_1,foo_reader], action=[indices:data/read/search[free_context]], indices=[foo-2019.01.04,foo-2019.01.03,foo-2019.01.06,foo-2019.01.05,foo-2019.01.08,servicelog-2019.01.07], request=[SearchFreeContextRequest] +[2019-01-27T20:04:27,244] [node-0] [rest] [authentication_success] origin_address=[::1], principal=[elastic-admin], realm=[default_file], uri=[/_xpack/security/user/jacknich2], params=[{username=jacknich2}], request_body=[{"metadata":{"intelligence":7},"full_name":"Jack Nicholson","roles":["admin","other_role1"],"email":"jacknich@example.com"}] 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 80% rename from filebeat/module/elasticsearch/audit/test/test.log-expected.json rename to filebeat/module/elasticsearch/audit/test/test-access.log-expected.json index 5511a09e38c..3811a496f12 100644 --- a/filebeat/module/elasticsearch/audit/test/test.log-expected.json +++ b/filebeat/module/elasticsearch/audit/test/test-access.log-expected.json @@ -37,8 +37,8 @@ "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.request": "ClearScrollRequest", + "elasticsearch.audit.origin.type": "local_node", + "elasticsearch.audit.request.name": "ClearScrollRequest", "event.dataset": "elasticsearch.audit", "event.module": "elasticsearch", "event.type": "access_granted", @@ -87,8 +87,8 @@ "ecs.version": "1.0.0-beta2", "elasticsearch.audit.action": "cluster:monitor/main", "elasticsearch.audit.layer": "transport", - "elasticsearch.audit.origin_type": "rest", - "elasticsearch.audit.request": "MainRequest", + "elasticsearch.audit.origin.type": "rest", + "elasticsearch.audit.request.name": "MainRequest", "event.dataset": "elasticsearch.audit", "event.module": "elasticsearch", "event.type": "access_denied", @@ -131,10 +131,10 @@ "servicelog-2019.01.07" ], "elasticsearch.audit.layer": "transport", - "elasticsearch.audit.origin_type": "transport", + "elasticsearch.audit.origin.type": "transport", "elasticsearch.audit.realm": "active_directory", - "elasticsearch.audit.request": "SearchFreeContextRequest", - "elasticsearch.audit.roles": [ + "elasticsearch.audit.request.name": "SearchFreeContextRequest", + "elasticsearch.audit.user.roles": [ "kibana_user", "my_custom_role_1", "foo_reader" @@ -150,5 +150,25 @@ "service.type": "elasticsearch", "source.ip": "192.168.2.1", "user.name": "username" + }, + { + "@timestamp": "2019-01-27T20:04:27.244Z", + "ecs.version": "1.0.0-beta2", + "elasticsearch.audit.layer": "rest", + "elasticsearch.audit.realm": "default_file", + "elasticsearch.audit.url.params": "{username=jacknich2}", + "elasticsearch.node.name": "node-0", + "event.dataset": "elasticsearch.audit", + "event.module": "elasticsearch", + "event.type": "authentication_success", + "fileset.name": "audit", + "http.request.body.content": "{\"metadata\":{\"intelligence\":7},\"full_name\":\"Jack Nicholson\",\"roles\":[\"admin\",\"other_role1\"", + "input.type": "log", + "log.offset": 1626, + "message": "[2019-01-27T20:04:27,244] [node-0] [rest] [authentication_success] origin_address=[::1], principal=[elastic-admin], realm=[default_file], uri=[/_xpack/security/user/jacknich2], params=[{username=jacknich2}], request_body=[{\"metadata\":{\"intelligence\":7},\"full_name\":\"Jack Nicholson\",\"roles\":[\"admin\",\"other_role1\"],\"email\":\"jacknich@example.com\"}]", + "service.type": "elasticsearch", + "source.ip": "::1", + "url.original": "/_xpack/security/user/jacknich2", + "user.name": "elastic-admin" } ] \ No newline at end of file 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 00000000000..4937ec8ef76 --- /dev/null +++ b/filebeat/module/elasticsearch/audit/test/test-audit.log @@ -0,0 +1,7 @@ +{"@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"]} +{"@timestamp":"2019-01-27T20:15:10,380", "node.name":"node-0", "node.id":"y8fa3M5zSSGo1M_KJRMUXw", "event.type":"rest", "event.action":"authentication_success", "user.name":"elastic-admin", "origin.type":"rest", "origin.address":"[::1]:58955", "realm":"default_file", "url.path":"/_search", "request.method":"GET", "request.body":"\n{\n \"query\" : {\n \"term\" : { \"user\" : \"kimchy\" }\n }\n}\n", "request.id":"WzL_kb6VSvOhAq0twPvHOQ"} 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 00000000000..c3e4ea3d40e --- /dev/null +++ b/filebeat/module/elasticsearch/audit/test/test-audit.log-expected.json @@ -0,0 +1,170 @@ +[ + { + "@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.address": "[::1]:61598", + "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.address": "[::1]:61599", + "source.ip": "::1", + "source.port": 61599, + "url.original": "/_xpack/security/user/remote_monitoring_user/_password", + "user.name": "elastic" + }, + { + "@timestamp": "2018-10-31T09:35:11.428Z", + "ecs.version": "1.0.0-beta2", + "elasticsearch.audit.action": "cluster:admin/xpack/security/realm/cache/clear", + "elasticsearch.audit.layer": "transport", + "elasticsearch.audit.origin.type": "local_node", + "elasticsearch.audit.request.name": "ClearRealmCacheRequest", + "elasticsearch.audit.user.realm": "__attach", + "elasticsearch.audit.user.roles": [ + "superuser" + ], + "elasticsearch.node.id": "DSiWcTyeThWtUXLB9J0BMw", + "event.action": "access_granted", + "event.dataset": "elasticsearch.audit", + "event.module": "elasticsearch", + "fileset.name": "audit", + "input.type": "log", + "log.offset": 558, + "message": "{\"@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\"}", + "service.type": "elasticsearch", + "source.address": "127.0.0.1:9300", + "source.ip": "127.0.0.1", + "source.port": 9300, + "user.name": "_xpack_security" + }, + { + "@timestamp": "2018-10-31T09:35:11.430Z", + "ecs.version": "1.0.0-beta2", + "elasticsearch.audit.action": "cluster:admin/xpack/security/realm/cache/clear[n]", + "elasticsearch.audit.layer": "transport", + "elasticsearch.audit.origin.type": "local_node", + "elasticsearch.audit.request.name": "Node", + "elasticsearch.audit.user.realm": "__attach", + "elasticsearch.audit.user.roles": [ + "superuser" + ], + "elasticsearch.node.id": "DSiWcTyeThWtUXLB9J0BMw", + "event.action": "access_granted", + "event.dataset": "elasticsearch.audit", + "event.module": "elasticsearch", + "fileset.name": "audit", + "input.type": "log", + "log.offset": 941, + "message": "{\"@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\"}", + "service.type": "elasticsearch", + "source.address": "127.0.0.1:9300", + "source.ip": "127.0.0.1", + "source.port": 9300, + "user.name": "_xpack_security" + }, + { + "@timestamp": "2018-10-31T09:35:12.303Z", + "ecs.version": "1.0.0-beta2", + "elasticsearch.audit.action": "cluster:admin/xpack/security/user/change_password", + "elasticsearch.audit.layer": "transport", + "elasticsearch.audit.origin.type": "rest", + "elasticsearch.audit.request.name": "ChangePasswordRequest", + "elasticsearch.audit.user.realm": "reserved", + "elasticsearch.audit.user.roles": [ + "superuser" + ], + "elasticsearch.node.id": "DSiWcTyeThWtUXLB9J0BMw", + "event.action": "access_granted", + "event.dataset": "elasticsearch.audit", + "event.module": "elasticsearch", + "fileset.name": "audit", + "input.type": "log", + "log.offset": 1309, + "message": "{\"@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\"}", + "service.type": "elasticsearch", + "source.address": "[::1]:61711", + "source.ip": "::1", + "source.port": 61711, + "user.name": "elastic" + }, + { + "@timestamp": "2018-10-31T09:35:12.314Z", + "ecs.version": "1.0.0-beta2", + "elasticsearch.audit.action": "indices:admin/create", + "elasticsearch.audit.indices": [ + ".security-6" + ], + "elasticsearch.audit.layer": "transport", + "elasticsearch.audit.origin.type": "local_node", + "elasticsearch.audit.request.name": "CreateIndexRequest", + "elasticsearch.audit.user.realm": "__attach", + "elasticsearch.audit.user.roles": [ + "superuser" + ], + "elasticsearch.node.id": "DSiWcTyeThWtUXLB9J0BMw", + "event.action": "access_granted", + "event.dataset": "elasticsearch.audit", + "event.module": "elasticsearch", + "fileset.name": "audit", + "input.type": "log", + "log.offset": 1676, + "message": "{\"@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\"]}", + "service.type": "elasticsearch", + "source.address": "127.0.0.1:9300", + "source.ip": "127.0.0.1", + "source.port": 9300, + "user.name": "_xpack_security" + }, + { + "@timestamp": "2019-01-27T20:15:10.380Z", + "ecs.version": "1.0.0-beta2", + "elasticsearch.audit.layer": "rest", + "elasticsearch.audit.origin.type": "rest", + "elasticsearch.audit.realm": "default_file", + "elasticsearch.audit.request.id": "WzL_kb6VSvOhAq0twPvHOQ", + "elasticsearch.node.id": "y8fa3M5zSSGo1M_KJRMUXw", + "elasticsearch.node.name": "node-0", + "event.action": "authentication_success", + "event.dataset": "elasticsearch.audit", + "event.module": "elasticsearch", + "fileset.name": "audit", + "http.request.body.content": "\n{\n \"query\" : {\n \"term\" : { \"user\" : \"kimchy\" }\n }\n}\n", + "http.request.method": "GET", + "input.type": "log", + "log.offset": 2056, + "message": "{\"@timestamp\":\"2019-01-27T20:15:10,380\", \"node.name\":\"node-0\", \"node.id\":\"y8fa3M5zSSGo1M_KJRMUXw\", \"event.type\":\"rest\", \"event.action\":\"authentication_success\", \"user.name\":\"elastic-admin\", \"origin.type\":\"rest\", \"origin.address\":\"[::1]:58955\", \"realm\":\"default_file\", \"url.path\":\"/_search\", \"request.method\":\"GET\", \"request.body\":\"\\n{\\n \\\"query\\\" : {\\n \\\"term\\\" : { \\\"user\\\" : \\\"kimchy\\\" }\\n }\\n}\\n\", \"request.id\":\"WzL_kb6VSvOhAq0twPvHOQ\"}", + "service.type": "elasticsearch", + "source.address": "[::1]:58955", + "source.ip": "::1", + "source.port": 58955, + "url.original": "/_search", + "user.name": "elastic-admin" + } +] \ No newline at end of file diff --git a/filebeat/module/elasticsearch/deprecation/config/log.yml b/filebeat/module/elasticsearch/deprecation/config/log.yml index 95c6d1bd6bf..e7c07ee6ec2 100644 --- a/filebeat/module/elasticsearch/deprecation/config/log.yml +++ b/filebeat/module/elasticsearch/deprecation/config/log.yml @@ -5,7 +5,7 @@ paths: {{ end }} exclude_files: [".gz$","_slowlog.log$","_access.log$"] multiline: - pattern: '^\[[0-9]{4}-[0-9]{2}-[0-9]{2}' + pattern: '^(\[[0-9]{4}-[0-9]{2}-[0-9]{2}|{)' negate: true match: after diff --git a/filebeat/module/elasticsearch/deprecation/ingest/pipeline-json.json b/filebeat/module/elasticsearch/deprecation/ingest/pipeline-json.json new file mode 100755 index 00000000000..e8a74768c28 --- /dev/null +++ b/filebeat/module/elasticsearch/deprecation/ingest/pipeline-json.json @@ -0,0 +1,102 @@ +{ + "description": "Pipeline for parsing the Elasticsearch deprecation log file in JSON format.", + "on_failure": [ + { + "set": { + "field": "error.message", + "value": "{{ _ingest.on_failure_message }}" + } + } + ], + "processors": [ + { + "json": { + "field": "message", + "target_field": "elasticsearch.deprecation" + } + }, + { + "drop": { + "if": "ctx.elasticsearch.deprecation.type != 'deprecation'" + } + }, + { + "remove": { + "field": "elasticsearch.deprecation.type" + } + }, + { + "rename": { + "field": "elasticsearch.deprecation.level", + "target_field": "log.level" + } + }, + { + "rename": { + "field": "elasticsearch.deprecation.component", + "target_field": "elasticsearch.component" + } + }, + { + "dot_expander": { + "field": "cluster.name", + "path": "elasticsearch.deprecation" + } + }, + { + "rename": { + "field": "elasticsearch.deprecation.cluster.name", + "target_field": "elasticsearch.cluster.name" + } + }, + { + "dot_expander": { + "field": "node.name", + "path": "elasticsearch.deprecation" + } + }, + { + "rename": { + "field": "elasticsearch.deprecation.node.name", + "target_field": "elasticsearch.node.name" + } + }, + { + "dot_expander": { + "field": "cluster.uuid", + "path": "elasticsearch.deprecation" + } + }, + { + "rename": { + "field": "elasticsearch.deprecation.cluster.uuid", + "target_field": "elasticsearch.cluster.uuid", + "ignore_missing": true + } + }, + { + "dot_expander": { + "field": "node.id", + "path": "elasticsearch.deprecation" + } + }, + { + "rename": { + "field": "elasticsearch.deprecation.node.id", + "target_field": "elasticsearch.node.id", + "ignore_missing": true + } + }, + { + "remove": { + "field": "message" + } + }, + { + "rename": { + "field": "elasticsearch.deprecation.message", + "target_field": "message" + } + } + ] +} diff --git a/filebeat/module/elasticsearch/deprecation/ingest/pipeline-plaintext.json b/filebeat/module/elasticsearch/deprecation/ingest/pipeline-plaintext.json new file mode 100755 index 00000000000..6fdb52514e8 --- /dev/null +++ b/filebeat/module/elasticsearch/deprecation/ingest/pipeline-plaintext.json @@ -0,0 +1,24 @@ +{ + "description": "Pipeline for parsing the Elasticsearch deprecation log file in plaintext format.", + "on_failure": [ + { + "set": { + "field": "error.message", + "value": "{{ _ingest.on_failure_message }}" + } + } + ], + "processors": [ + { + "grok": { + "field": "message", + "pattern_definitions": { + "GREEDYMULTILINE": "(.|\n)*" + }, + "patterns": [ + "\\[%{TIMESTAMP_ISO8601:elasticsearch.deprecation.timestamp}\\]\\[%{LOGLEVEL:log.level}%{SPACE}*\\]\\[%{DATA:elasticsearch.component}%{SPACE}*\\] %{GREEDYMULTILINE:message}" + ] + } + } + ] +} diff --git a/filebeat/module/elasticsearch/deprecation/ingest/pipeline.json b/filebeat/module/elasticsearch/deprecation/ingest/pipeline.json old mode 100755 new mode 100644 index 8f126f11e2f..3c916919454 --- a/filebeat/module/elasticsearch/deprecation/ingest/pipeline.json +++ b/filebeat/module/elasticsearch/deprecation/ingest/pipeline.json @@ -1,13 +1,5 @@ { - "description": "Pipeline for parsing the Elasticsearch deprecation log file.", - "on_failure": [ - { - "set": { - "field": "error.message", - "value": "{{ _ingest.on_failure_message }}" - } - } - ], + "description": "Pipeline for parsing elasticsearch deprecation logs", "processors": [ { "rename": { @@ -18,17 +10,29 @@ { "grok": { "field": "message", - "pattern_definitions": { - "GREEDYMULTILINE": "(.|\n)*" - }, "patterns": [ - "\\[%{TIMESTAMP_ISO8601:timestamp}\\]\\[%{LOGLEVEL:log.level}%{SPACE}*\\]\\[%{DATA:elasticsearch.server.component}%{SPACE}*\\] %{GREEDYMULTILINE:message}" - ] + "^%{CHAR:first_char}" + ], + "pattern_definitions": { + "CHAR": "." + } + } + }, + { + "pipeline": { + "if": "ctx.first_char != '{'", + "name": "{< IngestPipeline "pipeline-plaintext" >}" + } + }, + { + "pipeline": { + "if": "ctx.first_char == '{'", + "name": "{< IngestPipeline "pipeline-json" >}" } }, { "date": { - "field": "timestamp", + "field": "elasticsearch.deprecation.timestamp", "target_field": "@timestamp", "formats": [ "ISO8601" @@ -39,7 +43,22 @@ }, { "remove": { - "field": "timestamp" + "field": "elasticsearch.deprecation.timestamp" + } + }, + { + "remove": { + "field": [ + "first_char" + ] + } + } + ], + "on_failure": [ + { + "set": { + "field": "error.message", + "value": "{{ _ingest.on_failure_message }}" } } ] diff --git a/filebeat/module/elasticsearch/deprecation/manifest.yml b/filebeat/module/elasticsearch/deprecation/manifest.yml index 28525f4f3da..46389067c5f 100644 --- a/filebeat/module/elasticsearch/deprecation/manifest.yml +++ b/filebeat/module/elasticsearch/deprecation/manifest.yml @@ -4,10 +4,13 @@ var: - name: paths default: - /var/log/elasticsearch/*_deprecation.log + - /var/log/elasticsearch/*_deprecation.json os.darwin: - /usr/local/var/lib/elasticsearch/*_deprecation.log + - /usr/local/var/lib/elasticsearch/*_deprecation.json os.windows: - c:/ProgramData/Elastic/Elasticsearch/logs/*_deprecation.log + - c:/ProgramData/Elastic/Elasticsearch/logs/*_deprecation.json - name: convert_timezone default: false # if ES < 6.1.0, this flag switches to false automatically when evaluating the @@ -16,5 +19,8 @@ var: version: 6.1.0 value: false -ingest_pipeline: ingest/pipeline.json +ingest_pipeline: + - ingest/pipeline.json + - ingest/pipeline-plaintext.json + - ingest/pipeline-json.json input: config/log.yml diff --git a/filebeat/module/elasticsearch/deprecation/test/elasticsearch_deprecation.log-expected.json b/filebeat/module/elasticsearch/deprecation/test/elasticsearch_deprecation.log-expected.json index 9324b6c97e8..156c28f746e 100644 --- a/filebeat/module/elasticsearch/deprecation/test/elasticsearch_deprecation.log-expected.json +++ b/filebeat/module/elasticsearch/deprecation/test/elasticsearch_deprecation.log-expected.json @@ -2,7 +2,7 @@ { "@timestamp": "2018-04-23T16:40:13.737Z", "ecs.version": "1.0.0-beta2", - "elasticsearch.server.component": "o.e.d.a.a.i.t.p.PutIndexTemplateRequest", + "elasticsearch.component": "o.e.d.a.a.i.t.p.PutIndexTemplateRequest", "event.dataset": "elasticsearch.deprecation", "event.module": "elasticsearch", "fileset.name": "deprecation", @@ -15,7 +15,7 @@ { "@timestamp": "2018-04-23T16:40:13.862Z", "ecs.version": "1.0.0-beta2", - "elasticsearch.server.component": "o.e.d.a.a.i.t.p.PutIndexTemplateRequest", + "elasticsearch.component": "o.e.d.a.a.i.t.p.PutIndexTemplateRequest", "event.dataset": "elasticsearch.deprecation", "event.module": "elasticsearch", "fileset.name": "deprecation", @@ -28,7 +28,7 @@ { "@timestamp": "2018-04-23T16:40:14.792Z", "ecs.version": "1.0.0-beta2", - "elasticsearch.server.component": "o.e.d.a.a.i.t.p.PutIndexTemplateRequest", + "elasticsearch.component": "o.e.d.a.a.i.t.p.PutIndexTemplateRequest", "event.dataset": "elasticsearch.deprecation", "event.module": "elasticsearch", "fileset.name": "deprecation", @@ -41,7 +41,7 @@ { "@timestamp": "2018-04-23T16:40:15.127Z", "ecs.version": "1.0.0-beta2", - "elasticsearch.server.component": "o.e.d.a.a.i.t.p.PutIndexTemplateRequest", + "elasticsearch.component": "o.e.d.a.a.i.t.p.PutIndexTemplateRequest", "event.dataset": "elasticsearch.deprecation", "event.module": "elasticsearch", "fileset.name": "deprecation", diff --git a/filebeat/module/elasticsearch/deprecation/test/other_elasticsearch_deprecation.log-expected.json b/filebeat/module/elasticsearch/deprecation/test/other_elasticsearch_deprecation.log-expected.json index 2f00331f2c1..1241db9006e 100644 --- a/filebeat/module/elasticsearch/deprecation/test/other_elasticsearch_deprecation.log-expected.json +++ b/filebeat/module/elasticsearch/deprecation/test/other_elasticsearch_deprecation.log-expected.json @@ -2,7 +2,7 @@ { "@timestamp": "2017-11-30T13:38:16.911Z", "ecs.version": "1.0.0-beta2", - "elasticsearch.server.component": "o.e.d.c.ParseField", + "elasticsearch.component": "o.e.d.c.ParseField", "event.dataset": "elasticsearch.deprecation", "event.module": "elasticsearch", "fileset.name": "deprecation", @@ -15,7 +15,7 @@ { "@timestamp": "2017-11-30T13:38:16.941Z", "ecs.version": "1.0.0-beta2", - "elasticsearch.server.component": "o.e.d.c.ParseField", + "elasticsearch.component": "o.e.d.c.ParseField", "event.dataset": "elasticsearch.deprecation", "event.module": "elasticsearch", "fileset.name": "deprecation", @@ -28,7 +28,7 @@ { "@timestamp": "2017-11-30T13:39:28.986Z", "ecs.version": "1.0.0-beta2", - "elasticsearch.server.component": "o.e.d.i.m.UidFieldMapper", + "elasticsearch.component": "o.e.d.i.m.UidFieldMapper", "event.dataset": "elasticsearch.deprecation", "event.module": "elasticsearch", "fileset.name": "deprecation", @@ -41,7 +41,7 @@ { "@timestamp": "2017-11-30T13:39:36.339Z", "ecs.version": "1.0.0-beta2", - "elasticsearch.server.component": "o.e.d.i.m.UidFieldMapper", + "elasticsearch.component": "o.e.d.i.m.UidFieldMapper", "event.dataset": "elasticsearch.deprecation", "event.module": "elasticsearch", "fileset.name": "deprecation", @@ -54,7 +54,7 @@ { "@timestamp": "2017-11-30T13:40:49.540Z", "ecs.version": "1.0.0-beta2", - "elasticsearch.server.component": "o.e.d.i.m.UidFieldMapper", + "elasticsearch.component": "o.e.d.i.m.UidFieldMapper", "event.dataset": "elasticsearch.deprecation", "event.module": "elasticsearch", "fileset.name": "deprecation", @@ -67,7 +67,7 @@ { "@timestamp": "2017-11-30T14:08:37.413Z", "ecs.version": "1.0.0-beta2", - "elasticsearch.server.component": "o.e.d.i.m.UidFieldMapper", + "elasticsearch.component": "o.e.d.i.m.UidFieldMapper", "event.dataset": "elasticsearch.deprecation", "event.module": "elasticsearch", "fileset.name": "deprecation", @@ -80,7 +80,7 @@ { "@timestamp": "2017-11-30T14:08:37.413Z", "ecs.version": "1.0.0-beta2", - "elasticsearch.server.component": "o.e.d.i.m.UidFieldMapper", + "elasticsearch.component": "o.e.d.i.m.UidFieldMapper", "event.dataset": "elasticsearch.deprecation", "event.module": "elasticsearch", "fileset.name": "deprecation", @@ -93,7 +93,7 @@ { "@timestamp": "2017-11-30T14:08:46.006Z", "ecs.version": "1.0.0-beta2", - "elasticsearch.server.component": "o.e.d.i.m.UidFieldMapper", + "elasticsearch.component": "o.e.d.i.m.UidFieldMapper", "event.dataset": "elasticsearch.deprecation", "event.module": "elasticsearch", "fileset.name": "deprecation", @@ -106,7 +106,7 @@ { "@timestamp": "2017-11-30T14:08:46.006Z", "ecs.version": "1.0.0-beta2", - "elasticsearch.server.component": "o.e.d.i.m.UidFieldMapper", + "elasticsearch.component": "o.e.d.i.m.UidFieldMapper", "event.dataset": "elasticsearch.deprecation", "event.module": "elasticsearch", "fileset.name": "deprecation", @@ -119,7 +119,7 @@ { "@timestamp": "2017-12-01T14:05:54.017Z", "ecs.version": "1.0.0-beta2", - "elasticsearch.server.component": "o.e.d.i.m.AllFieldMapper", + "elasticsearch.component": "o.e.d.i.m.AllFieldMapper", "event.dataset": "elasticsearch.deprecation", "event.module": "elasticsearch", "fileset.name": "deprecation", @@ -132,7 +132,7 @@ { "@timestamp": "2017-12-01T14:05:54.019Z", "ecs.version": "1.0.0-beta2", - "elasticsearch.server.component": "o.e.d.i.m.AllFieldMapper", + "elasticsearch.component": "o.e.d.i.m.AllFieldMapper", "event.dataset": "elasticsearch.deprecation", "event.module": "elasticsearch", "fileset.name": "deprecation", @@ -145,7 +145,7 @@ { "@timestamp": "2017-12-01T14:06:52.059Z", "ecs.version": "1.0.0-beta2", - "elasticsearch.server.component": "o.e.d.i.m.AllFieldMapper", + "elasticsearch.component": "o.e.d.i.m.AllFieldMapper", "event.dataset": "elasticsearch.deprecation", "event.module": "elasticsearch", "fileset.name": "deprecation", @@ -158,7 +158,7 @@ { "@timestamp": "2017-12-01T14:46:10.428Z", "ecs.version": "1.0.0-beta2", - "elasticsearch.server.component": "o.e.d.s.a.InternalOrder$Parser", + "elasticsearch.component": "o.e.d.s.a.InternalOrder$Parser", "event.dataset": "elasticsearch.deprecation", "event.module": "elasticsearch", "fileset.name": "deprecation", @@ -171,7 +171,7 @@ { "@timestamp": "2017-12-04T16:17:18.271Z", "ecs.version": "1.0.0-beta2", - "elasticsearch.server.component": "o.e.d.a.a.i.t.p.PutIndexTemplateRequest", + "elasticsearch.component": "o.e.d.a.a.i.t.p.PutIndexTemplateRequest", "event.dataset": "elasticsearch.deprecation", "event.module": "elasticsearch", "fileset.name": "deprecation", @@ -184,7 +184,7 @@ { "@timestamp": "2017-12-04T16:17:18.282Z", "ecs.version": "1.0.0-beta2", - "elasticsearch.server.component": "o.e.d.i.m.MapperService", + "elasticsearch.component": "o.e.d.i.m.MapperService", "event.dataset": "elasticsearch.deprecation", "event.module": "elasticsearch", "fileset.name": "deprecation", @@ -197,7 +197,7 @@ { "@timestamp": "2017-12-04T16:20:43.248Z", "ecs.version": "1.0.0-beta2", - "elasticsearch.server.component": "o.e.d.i.m.MapperService", + "elasticsearch.component": "o.e.d.i.m.MapperService", "event.dataset": "elasticsearch.deprecation", "event.module": "elasticsearch", "fileset.name": "deprecation", diff --git a/filebeat/module/elasticsearch/deprecation/test/test-json.log b/filebeat/module/elasticsearch/deprecation/test/test-json.log new file mode 100644 index 00000000000..c6852de59d2 --- /dev/null +++ b/filebeat/module/elasticsearch/deprecation/test/test-json.log @@ -0,0 +1,13 @@ +{"type": "deprecation", "timestamp": "2019-01-30T14:16:20,233-0800", "level": "WARN", "component": "o.e.d.r.a.d.RestGetAction", "cluster.name": "es1", "node.name": "es1_1", "cluster.uuid": "S4dWw65ZT1eu3SltmAr84A", "node.id": "gCoNXf3qSQ6a190zBKr7Bw", "message": "[types removal] Specifying types in document get requests is deprecated, use the /{index}/_doc/{id} endpoint instead." } +{"type": "deprecation", "timestamp": "2019-01-30T14:16:22,388-0800", "level": "WARN", "component": "o.e.d.a.b.BulkRequest", "cluster.name": "es1", "node.name": "es1_1", "cluster.uuid": "S4dWw65ZT1eu3SltmAr84A", "node.id": "gCoNXf3qSQ6a190zBKr7Bw", "message": "[types removal] Specifying types in bulk requests is deprecated." } +{"type": "deprecation", "timestamp": "2019-01-30T14:16:22,566-0800", "level": "WARN", "component": "o.e.d.r.a.d.RestUpdateAction", "cluster.name": "es1", "node.name": "es1_1", "cluster.uuid": "S4dWw65ZT1eu3SltmAr84A", "node.id": "gCoNXf3qSQ6a190zBKr7Bw", "message": "[types removal] Specifying types in document update requests is deprecated, use the endpoint /{index}/_update/{id} instead." } +{"type": "deprecation", "timestamp": "2019-01-30T14:16:24,538-0800", "level": "WARN", "component": "o.e.d.r.a.s.RestSearchAction", "cluster.name": "es1", "node.name": "es1_1", "cluster.uuid": "S4dWw65ZT1eu3SltmAr84A", "node.id": "gCoNXf3qSQ6a190zBKr7Bw", "message": "[types removal] Specifying types in search requests is deprecated." } +{"type": "deprecation", "timestamp": "2019-01-30T14:16:59,311-0800", "level": "WARN", "component": "o.e.d.x.s.r.a.u.RestChangePasswordAction", "cluster.name": "es1", "node.name": "es1_1", "cluster.uuid": "S4dWw65ZT1eu3SltmAr84A", "node.id": "gCoNXf3qSQ6a190zBKr7Bw", "message": "[PUT /_xpack/security/user/{username}/_password] is deprecated! Use [PUT /_security/user/{username}/_password] instead." } +{"type": "deprecation", "timestamp": "2019-01-30T14:16:59,922-0800", "level": "WARN", "component": "o.e.d.x.s.r.a.u.RestChangePasswordAction", "cluster.name": "es1", "node.name": "es1_1", "cluster.uuid": "S4dWw65ZT1eu3SltmAr84A", "node.id": "gCoNXf3qSQ6a190zBKr7Bw", "message": "[PUT /_xpack/security/user/{username}/_password] is deprecated! Use [PUT /_security/user/{username}/_password] instead." } +{"type": "deprecation", "timestamp": "2019-01-30T14:17:00,095-0800", "level": "WARN", "component": "o.e.d.x.s.r.a.u.RestChangePasswordAction", "cluster.name": "es1", "node.name": "es1_1", "cluster.uuid": "S4dWw65ZT1eu3SltmAr84A", "node.id": "gCoNXf3qSQ6a190zBKr7Bw", "message": "[PUT /_xpack/security/user/{username}/_password] is deprecated! Use [PUT /_security/user/{username}/_password] instead." } +{"type": "deprecation", "timestamp": "2019-01-30T14:17:13,226-0800", "level": "WARN", "component": "o.e.d.r.a.d.RestGetAction", "cluster.name": "es1", "node.name": "es1_1", "cluster.uuid": "S4dWw65ZT1eu3SltmAr84A", "node.id": "gCoNXf3qSQ6a190zBKr7Bw", "message": "[types removal] Specifying types in document get requests is deprecated, use the /{index}/_doc/{id} endpoint instead." } +{"type": "deprecation", "timestamp": "2019-01-30T14:17:14,747-0800", "level": "WARN", "component": "o.e.d.a.b.BulkRequest", "cluster.name": "es1", "node.name": "es1_1", "cluster.uuid": "S4dWw65ZT1eu3SltmAr84A", "node.id": "gCoNXf3qSQ6a190zBKr7Bw", "message": "[types removal] Specifying types in bulk requests is deprecated." } +{"type": "deprecation", "timestamp": "2019-01-30T14:17:14,801-0800", "level": "WARN", "component": "o.e.d.r.a.d.RestUpdateAction", "cluster.name": "es1", "node.name": "es1_1", "cluster.uuid": "S4dWw65ZT1eu3SltmAr84A", "node.id": "gCoNXf3qSQ6a190zBKr7Bw", "message": "[types removal] Specifying types in document update requests is deprecated, use the endpoint /{index}/_update/{id} instead." } +{"type": "deprecation", "timestamp": "2019-01-30T14:17:17,546-0800", "level": "WARN", "component": "o.e.d.r.a.s.RestSearchAction", "cluster.name": "es1", "node.name": "es1_1", "cluster.uuid": "S4dWw65ZT1eu3SltmAr84A", "node.id": "gCoNXf3qSQ6a190zBKr7Bw", "message": "[types removal] Specifying types in search requests is deprecated." } +{"type": "deprecation", "timestamp": "2019-01-30T14:18:33,367-0800", "level": "WARN", "component": "o.e.d.x.w.a.i.IndexAction", "cluster.name": "es1", "node.name": "es1_1", "cluster.uuid": "S4dWw65ZT1eu3SltmAr84A", "node.id": "gCoNXf3qSQ6a190zBKr7Bw", "message": "[types removal] Specifying types in a watcher index action is deprecated." } +{"type": "deprecation", "timestamp": "2019-01-30T14:18:46,493-0800", "level": "WARN", "component": "o.e.d.i.q.QueryShardContext", "cluster.name": "es1", "node.name": "es1_1", "cluster.uuid": "S4dWw65ZT1eu3SltmAr84A", "node.id": "gCoNXf3qSQ6a190zBKr7Bw", "message": "[types removal] Using the _type field in queries and aggregations is deprecated, prefer to use a field instead." } diff --git a/filebeat/module/elasticsearch/deprecation/test/test-json.log-expected.json b/filebeat/module/elasticsearch/deprecation/test/test-json.log-expected.json new file mode 100644 index 00000000000..8664764a21a --- /dev/null +++ b/filebeat/module/elasticsearch/deprecation/test/test-json.log-expected.json @@ -0,0 +1,223 @@ +[ + { + "@timestamp": "2019-01-30T22:16:20.233Z", + "ecs.version": "1.0.0-beta2", + "elasticsearch.cluster.name": "es1", + "elasticsearch.cluster.uuid": "S4dWw65ZT1eu3SltmAr84A", + "elasticsearch.component": "o.e.d.r.a.d.RestGetAction", + "elasticsearch.node.id": "gCoNXf3qSQ6a190zBKr7Bw", + "elasticsearch.node.name": "es1_1", + "event.dataset": "elasticsearch.deprecation", + "event.module": "elasticsearch", + "fileset.name": "deprecation", + "input.type": "log", + "log.level": "WARN", + "log.offset": 0, + "message": "[types removal] Specifying types in document get requests is deprecated, use the /{index}/_doc/{id} endpoint instead.", + "service.type": "elasticsearch" + }, + { + "@timestamp": "2019-01-30T22:16:22.388Z", + "ecs.version": "1.0.0-beta2", + "elasticsearch.cluster.name": "es1", + "elasticsearch.cluster.uuid": "S4dWw65ZT1eu3SltmAr84A", + "elasticsearch.component": "o.e.d.a.b.BulkRequest", + "elasticsearch.node.id": "gCoNXf3qSQ6a190zBKr7Bw", + "elasticsearch.node.name": "es1_1", + "event.dataset": "elasticsearch.deprecation", + "event.module": "elasticsearch", + "fileset.name": "deprecation", + "input.type": "log", + "log.level": "WARN", + "log.offset": 387, + "message": "[types removal] Specifying types in bulk requests is deprecated.", + "service.type": "elasticsearch" + }, + { + "@timestamp": "2019-01-30T22:16:22.566Z", + "ecs.version": "1.0.0-beta2", + "elasticsearch.cluster.name": "es1", + "elasticsearch.cluster.uuid": "S4dWw65ZT1eu3SltmAr84A", + "elasticsearch.component": "o.e.d.r.a.d.RestUpdateAction", + "elasticsearch.node.id": "gCoNXf3qSQ6a190zBKr7Bw", + "elasticsearch.node.name": "es1_1", + "event.dataset": "elasticsearch.deprecation", + "event.module": "elasticsearch", + "fileset.name": "deprecation", + "input.type": "log", + "log.level": "WARN", + "log.offset": 717, + "message": "[types removal] Specifying types in document update requests is deprecated, use the endpoint /{index}/_update/{id} instead.", + "service.type": "elasticsearch" + }, + { + "@timestamp": "2019-01-30T22:16:24.538Z", + "ecs.version": "1.0.0-beta2", + "elasticsearch.cluster.name": "es1", + "elasticsearch.cluster.uuid": "S4dWw65ZT1eu3SltmAr84A", + "elasticsearch.component": "o.e.d.r.a.s.RestSearchAction", + "elasticsearch.node.id": "gCoNXf3qSQ6a190zBKr7Bw", + "elasticsearch.node.name": "es1_1", + "event.dataset": "elasticsearch.deprecation", + "event.module": "elasticsearch", + "fileset.name": "deprecation", + "input.type": "log", + "log.level": "WARN", + "log.offset": 1113, + "message": "[types removal] Specifying types in search requests is deprecated.", + "service.type": "elasticsearch" + }, + { + "@timestamp": "2019-01-30T22:16:59.311Z", + "ecs.version": "1.0.0-beta2", + "elasticsearch.cluster.name": "es1", + "elasticsearch.cluster.uuid": "S4dWw65ZT1eu3SltmAr84A", + "elasticsearch.component": "o.e.d.x.s.r.a.u.RestChangePasswordAction", + "elasticsearch.node.id": "gCoNXf3qSQ6a190zBKr7Bw", + "elasticsearch.node.name": "es1_1", + "event.dataset": "elasticsearch.deprecation", + "event.module": "elasticsearch", + "fileset.name": "deprecation", + "input.type": "log", + "log.level": "WARN", + "log.offset": 1452, + "message": "[PUT /_xpack/security/user/{username}/_password] is deprecated! Use [PUT /_security/user/{username}/_password] instead.", + "service.type": "elasticsearch" + }, + { + "@timestamp": "2019-01-30T22:16:59.922Z", + "ecs.version": "1.0.0-beta2", + "elasticsearch.cluster.name": "es1", + "elasticsearch.cluster.uuid": "S4dWw65ZT1eu3SltmAr84A", + "elasticsearch.component": "o.e.d.x.s.r.a.u.RestChangePasswordAction", + "elasticsearch.node.id": "gCoNXf3qSQ6a190zBKr7Bw", + "elasticsearch.node.name": "es1_1", + "event.dataset": "elasticsearch.deprecation", + "event.module": "elasticsearch", + "fileset.name": "deprecation", + "input.type": "log", + "log.level": "WARN", + "log.offset": 1856, + "message": "[PUT /_xpack/security/user/{username}/_password] is deprecated! Use [PUT /_security/user/{username}/_password] instead.", + "service.type": "elasticsearch" + }, + { + "@timestamp": "2019-01-30T22:17:00.095Z", + "ecs.version": "1.0.0-beta2", + "elasticsearch.cluster.name": "es1", + "elasticsearch.cluster.uuid": "S4dWw65ZT1eu3SltmAr84A", + "elasticsearch.component": "o.e.d.x.s.r.a.u.RestChangePasswordAction", + "elasticsearch.node.id": "gCoNXf3qSQ6a190zBKr7Bw", + "elasticsearch.node.name": "es1_1", + "event.dataset": "elasticsearch.deprecation", + "event.module": "elasticsearch", + "fileset.name": "deprecation", + "input.type": "log", + "log.level": "WARN", + "log.offset": 2260, + "message": "[PUT /_xpack/security/user/{username}/_password] is deprecated! Use [PUT /_security/user/{username}/_password] instead.", + "service.type": "elasticsearch" + }, + { + "@timestamp": "2019-01-30T22:17:13.226Z", + "ecs.version": "1.0.0-beta2", + "elasticsearch.cluster.name": "es1", + "elasticsearch.cluster.uuid": "S4dWw65ZT1eu3SltmAr84A", + "elasticsearch.component": "o.e.d.r.a.d.RestGetAction", + "elasticsearch.node.id": "gCoNXf3qSQ6a190zBKr7Bw", + "elasticsearch.node.name": "es1_1", + "event.dataset": "elasticsearch.deprecation", + "event.module": "elasticsearch", + "fileset.name": "deprecation", + "input.type": "log", + "log.level": "WARN", + "log.offset": 2664, + "message": "[types removal] Specifying types in document get requests is deprecated, use the /{index}/_doc/{id} endpoint instead.", + "service.type": "elasticsearch" + }, + { + "@timestamp": "2019-01-30T22:17:14.747Z", + "ecs.version": "1.0.0-beta2", + "elasticsearch.cluster.name": "es1", + "elasticsearch.cluster.uuid": "S4dWw65ZT1eu3SltmAr84A", + "elasticsearch.component": "o.e.d.a.b.BulkRequest", + "elasticsearch.node.id": "gCoNXf3qSQ6a190zBKr7Bw", + "elasticsearch.node.name": "es1_1", + "event.dataset": "elasticsearch.deprecation", + "event.module": "elasticsearch", + "fileset.name": "deprecation", + "input.type": "log", + "log.level": "WARN", + "log.offset": 3051, + "message": "[types removal] Specifying types in bulk requests is deprecated.", + "service.type": "elasticsearch" + }, + { + "@timestamp": "2019-01-30T22:17:14.801Z", + "ecs.version": "1.0.0-beta2", + "elasticsearch.cluster.name": "es1", + "elasticsearch.cluster.uuid": "S4dWw65ZT1eu3SltmAr84A", + "elasticsearch.component": "o.e.d.r.a.d.RestUpdateAction", + "elasticsearch.node.id": "gCoNXf3qSQ6a190zBKr7Bw", + "elasticsearch.node.name": "es1_1", + "event.dataset": "elasticsearch.deprecation", + "event.module": "elasticsearch", + "fileset.name": "deprecation", + "input.type": "log", + "log.level": "WARN", + "log.offset": 3381, + "message": "[types removal] Specifying types in document update requests is deprecated, use the endpoint /{index}/_update/{id} instead.", + "service.type": "elasticsearch" + }, + { + "@timestamp": "2019-01-30T22:17:17.546Z", + "ecs.version": "1.0.0-beta2", + "elasticsearch.cluster.name": "es1", + "elasticsearch.cluster.uuid": "S4dWw65ZT1eu3SltmAr84A", + "elasticsearch.component": "o.e.d.r.a.s.RestSearchAction", + "elasticsearch.node.id": "gCoNXf3qSQ6a190zBKr7Bw", + "elasticsearch.node.name": "es1_1", + "event.dataset": "elasticsearch.deprecation", + "event.module": "elasticsearch", + "fileset.name": "deprecation", + "input.type": "log", + "log.level": "WARN", + "log.offset": 3777, + "message": "[types removal] Specifying types in search requests is deprecated.", + "service.type": "elasticsearch" + }, + { + "@timestamp": "2019-01-30T22:18:33.367Z", + "ecs.version": "1.0.0-beta2", + "elasticsearch.cluster.name": "es1", + "elasticsearch.cluster.uuid": "S4dWw65ZT1eu3SltmAr84A", + "elasticsearch.component": "o.e.d.x.w.a.i.IndexAction", + "elasticsearch.node.id": "gCoNXf3qSQ6a190zBKr7Bw", + "elasticsearch.node.name": "es1_1", + "event.dataset": "elasticsearch.deprecation", + "event.module": "elasticsearch", + "fileset.name": "deprecation", + "input.type": "log", + "log.level": "WARN", + "log.offset": 4116, + "message": "[types removal] Specifying types in a watcher index action is deprecated.", + "service.type": "elasticsearch" + }, + { + "@timestamp": "2019-01-30T22:18:46.493Z", + "ecs.version": "1.0.0-beta2", + "elasticsearch.cluster.name": "es1", + "elasticsearch.cluster.uuid": "S4dWw65ZT1eu3SltmAr84A", + "elasticsearch.component": "o.e.d.i.q.QueryShardContext", + "elasticsearch.node.id": "gCoNXf3qSQ6a190zBKr7Bw", + "elasticsearch.node.name": "es1_1", + "event.dataset": "elasticsearch.deprecation", + "event.module": "elasticsearch", + "fileset.name": "deprecation", + "input.type": "log", + "log.level": "WARN", + "log.offset": 4459, + "message": "[types removal] Using the _type field in queries and aggregations is deprecated, prefer to use a field instead.", + "service.type": "elasticsearch" + } +] \ No newline at end of file diff --git a/filebeat/module/elasticsearch/fields.go b/filebeat/module/elasticsearch/fields.go index 013167c476e..3a822014be6 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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" } diff --git a/filebeat/module/elasticsearch/slowlog/_meta/fields.yml b/filebeat/module/elasticsearch/slowlog/_meta/fields.yml index 77a3f9e0ffd..85eaa48dbed 100644 --- a/filebeat/module/elasticsearch/slowlog/_meta/fields.yml +++ b/filebeat/module/elasticsearch/slowlog/_meta/fields.yml @@ -10,15 +10,15 @@ - name: took description: "Time it took to execute the query" example: "300ms" - type: text + type: keyword - name: types description: "Types" example: "" type: keyword - name: stats - description: "Statistics" - example: "" - type: text + description: "Stats groups" + example: "group1" + type: keyword - name: search_type description: "Search type" example: "QUERY_THEN_FETCH" @@ -26,11 +26,11 @@ - name: source_query description: "Slow query" example: "{\"query\":{\"match_all\":{\"boost\":1.0}}}" - type: text + type: keyword - name: extra_source description: "Extra source information" example: "" - type: text + type: keyword - name: total_hits description: "Total hits" example: 42 diff --git a/filebeat/module/elasticsearch/slowlog/config/slowlog.yml b/filebeat/module/elasticsearch/slowlog/config/slowlog.yml index 54c69f3c245..bcacf308e09 100644 --- a/filebeat/module/elasticsearch/slowlog/config/slowlog.yml +++ b/filebeat/module/elasticsearch/slowlog/config/slowlog.yml @@ -6,7 +6,7 @@ paths: exclude_files: [".gz$"] multiline: - pattern: '^\[?[0-9]{4}-[0-9]{2}-[0-9]{2}' + pattern: '^(\[?[0-9]{4}-[0-9]{2}-[0-9]{2}|{)' negate: true match: after diff --git a/filebeat/module/elasticsearch/slowlog/ingest/pipeline-json.json b/filebeat/module/elasticsearch/slowlog/ingest/pipeline-json.json new file mode 100644 index 00000000000..b4dcca93b26 --- /dev/null +++ b/filebeat/module/elasticsearch/slowlog/ingest/pipeline-json.json @@ -0,0 +1,108 @@ +{ + "description": "Pipeline for parsing the Elasticsearch slow logs in JSON format.", + "on_failure": [ + { + "set": { + "field": "error.message", + "value": "{{ _ingest.on_failure_message }}" + } + } + ], + "processors": [ + { + "json": { + "field": "message", + "target_field": "elasticsearch.slowlog" + } + }, + { + "drop": { + "if": "ctx.elasticsearch.slowlog.type != 'index_indexing_slowlog' && ctx.elasticsearch.slowlog.type != 'index_search_slowlog'" + } + }, + { + "remove": { + "field": "elasticsearch.slowlog.type" + } + }, + { + "rename": { + "field": "elasticsearch.slowlog.level", + "target_field": "log.level" + } + }, + { + "rename": { + "field": "elasticsearch.slowlog.component", + "target_field": "elasticsearch.component" + } + }, + { + "dot_expander": { + "field": "cluster.name", + "path": "elasticsearch.slowlog" + } + }, + { + "rename": { + "field": "elasticsearch.slowlog.cluster.name", + "target_field": "elasticsearch.cluster.name" + } + }, + { + "dot_expander": { + "field": "node.name", + "path": "elasticsearch.slowlog" + } + }, + { + "rename": { + "field": "elasticsearch.slowlog.node.name", + "target_field": "elasticsearch.node.name" + } + }, + { + "dot_expander": { + "field": "cluster.uuid", + "path": "elasticsearch.slowlog" + } + }, + { + "rename": { + "field": "elasticsearch.slowlog.cluster.uuid", + "target_field": "elasticsearch.cluster.uuid", + "ignore_missing": true + } + }, + { + "dot_expander": { + "field": "node.id", + "path": "elasticsearch.slowlog" + } + }, + { + "rename": { + "field": "elasticsearch.slowlog.node.id", + "target_field": "elasticsearch.node.id", + "ignore_missing": true + } + }, + { + "grok": { + "field": "elasticsearch.slowlog.message", + "pattern_definitions" : { + "GREEDYMULTILINE" : "(.|\n)*", + "INDEXNAME": "[a-zA-Z0-9_.-]*" + }, + "patterns": [ + "(\\[%{INDEXNAME:elasticsearch.index.name}\\]\\[%{NUMBER:elasticsearch.shard.id}\\])?(%{SPACE})?(\\[%{INDEXNAME:elasticsearch.index.name}\\/%{DATA:elasticsearch.index.id}\\])?(%{SPACE})?%{SPACE}(took\\[%{DATA:elasticsearch.slowlog.took}\\],)?%{SPACE}(took_millis\\[%{NUMBER:elasticsearch.slowlog.duration:long}\\],)?%{SPACE}(type\\[%{DATA:elasticsearch.slowlog.type}\\],)?%{SPACE}(id\\[%{DATA:elasticsearch.slowlog.id}\\],)?%{SPACE}(routing\\[%{DATA:elasticsearch.slowlog.routing}\\],)?%{SPACE}(total_hits\\[%{NUMBER:elasticsearch.slowlog.total_hits:int}\\],)?%{SPACE}(types\\[%{DATA:elasticsearch.slowlog.types}\\],)?%{SPACE}(stats\\[%{DATA:elasticsearch.slowlog.stats}\\],)?%{SPACE}(search_type\\[%{DATA:elasticsearch.slowlog.search_type}\\],)?%{SPACE}(total_shards\\[%{NUMBER:elasticsearch.slowlog.total_shards:int}\\],)?%{SPACE}(source\\[%{GREEDYMULTILINE:elasticsearch.slowlog.source_query}\\])?,?%{SPACE}(extra_source\\[%{DATA:elasticsearch.slowlog.extra_source}\\])?,?" + ] + } + }, + { + "remove": { + "field": "elasticsearch.slowlog.message" + } + } + ] +} diff --git a/filebeat/module/elasticsearch/slowlog/ingest/pipeline-plaintext.json b/filebeat/module/elasticsearch/slowlog/ingest/pipeline-plaintext.json new file mode 100644 index 00000000000..cd3b23f40c9 --- /dev/null +++ b/filebeat/module/elasticsearch/slowlog/ingest/pipeline-plaintext.json @@ -0,0 +1,33 @@ +{ + "description": "Pipeline for parsing elasticsearch slow logs in plaintext format.", + "processors": [ + { + "grok": { + "field": "message", + "pattern_definitions": { + "GREEDYMULTILINE": "(.|\n)*", + "INDEXNAME": "[a-zA-Z0-9_.-]*" + }, + "patterns": [ + "\\[%{TIMESTAMP_ISO8601:elasticsearch.slowlog.timestamp}\\]\\[%{WORD:log.level}(%{SPACE})?\\]\\[%{DATA:elasticsearch.slowlog.logger}\\]%{SPACE}\\[%{WORD:elasticsearch.node.name}\\](%{SPACE})?(\\[%{INDEXNAME:elasticsearch.index.name}\\]\\[%{NUMBER:elasticsearch.shard.id}\\])?(%{SPACE})?(\\[%{INDEXNAME:elasticsearch.index.name}\\/%{DATA:elasticsearch.index.id}\\])?(%{SPACE})?%{SPACE}(took\\[%{DATA:elasticsearch.slowlog.took}\\],)?%{SPACE}(took_millis\\[%{NUMBER:elasticsearch.slowlog.duration:long}\\],)?%{SPACE}(type\\[%{DATA:elasticsearch.slowlog.type}\\],)?%{SPACE}(id\\[%{DATA:elasticsearch.slowlog.id}\\],)?%{SPACE}(routing\\[%{DATA:elasticsearch.slowlog.routing}\\],)?%{SPACE}(total_hits\\[%{NUMBER:elasticsearch.slowlog.total_hits:int}\\],)?%{SPACE}(types\\[%{DATA:elasticsearch.slowlog.types}\\],)?%{SPACE}(stats\\[%{DATA:elasticsearch.slowlog.stats}\\],)?%{SPACE}(search_type\\[%{DATA:elasticsearch.slowlog.search_type}\\],)?%{SPACE}(total_shards\\[%{NUMBER:elasticsearch.slowlog.total_shards:int}\\],)?%{SPACE}(source\\[%{GREEDYMULTILINE:elasticsearch.slowlog.source_query}\\])?,?%{SPACE}(extra_source\\[%{DATA:elasticsearch.slowlog.extra_source}\\])?,?" + ] + } + }, + { + "split": { + "if": "ctx.elasticsearch.slowlog?.stats != ''", + "field": "elasticsearch.slowlog.stats", + "separator": ",", + "ignore_missing": true + } + } + ], + "on_failure": [ + { + "set": { + "field": "error.message", + "value": "{{ _ingest.on_failure_message }}" + } + } + ] +} diff --git a/filebeat/module/elasticsearch/slowlog/ingest/pipeline.json b/filebeat/module/elasticsearch/slowlog/ingest/pipeline.json index fbfec49e08c..91756d80d51 100644 --- a/filebeat/module/elasticsearch/slowlog/ingest/pipeline.json +++ b/filebeat/module/elasticsearch/slowlog/ingest/pipeline.json @@ -1,60 +1,81 @@ { - "description": "Pipeline for parsing elasticsearch slowlog logs", - "processors": [ - { - "rename": { - "field": "@timestamp", - "target_field": "event.created" - } - }, - { - "grok": { - "field": "message", - "pattern_definitions" : { - "GREEDYMULTILINE" : "(.|\n)*", - "INDEXNAME": "[a-zA-Z0-9_.-]*" - }, - "patterns": [ - "\\[%{TIMESTAMP_ISO8601:elasticsearch.slowlog.timestamp}\\]\\[%{WORD:log.level}(%{SPACE})?\\]\\[%{DATA:elasticsearch.slowlog.logger}\\]%{SPACE}\\[%{WORD:elasticsearch.node.name}\\](%{SPACE})?(\\[%{INDEXNAME:elasticsearch.index.name}\\]\\[%{NUMBER:elasticsearch.shard.id}\\])?(%{SPACE})?(\\[%{INDEXNAME:elasticsearch.index.name}\\/%{DATA:elasticsearch.index.id}\\])?(%{SPACE})?%{SPACE}(took\\[%{DATA:elasticsearch.slowlog.took}\\],)?%{SPACE}(took_millis\\[%{NUMBER:temp.duration:long}\\],)?%{SPACE}(type\\[%{DATA:elasticsearch.slowlog.type}\\],)?%{SPACE}(id\\[%{DATA:elasticsearch.slowlog.id}\\],)?%{SPACE}(routing\\[%{DATA:elasticsearch.slowlog.routing}\\],)?%{SPACE}(total_hits\\[%{NUMBER:elasticsearch.slowlog.total_hits:int}\\],)?%{SPACE}(types\\[%{DATA:elasticsearch.slowlog.types}\\],)?%{SPACE}(stats\\[%{DATA:elasticsearch.slowlog.stats}\\],)?%{SPACE}(search_type\\[%{DATA:elasticsearch.slowlog.search_type}\\],)?%{SPACE}(total_shards\\[%{NUMBER:elasticsearch.slowlog.total_shards:int}\\],)?%{SPACE}(source\\[%{GREEDYMULTILINE:elasticsearch.slowlog.source_query}\\])?,?%{SPACE}(extra_source\\[%{DATA:elasticsearch.slowlog.extra_source}\\])?,?" - ] - } - }, - { - "date": { - "field": "elasticsearch.slowlog.timestamp", - "target_field": "@timestamp", - "formats": [ - "ISO8601" - ], - {< if .convert_timezone >}"timezone": "{{ event.timezone }}",{< end >} - "ignore_failure": true - } - }, - { - "remove": { - "field": "elasticsearch.slowlog.timestamp" - } - }, - - { - "script": { - "lang": "painless", - "source": "ctx.event.duration = Math.round(ctx.temp.duration * params.scale)", - "params": { "scale": 1000000 }, - "if": "ctx.temp?.duration != null" - } - }, - { - "remove": { - "field": "temp.duration", - "ignore_missing": true - } - } - ], - "on_failure" : [{ - "set" : { - "field" : "error.message", - "value" : "{{ _ingest.on_failure_message }}" - } - }] + "description": "Pipeline for parsing elasticsearch slow logs.", + "processors": [ + { + "rename": { + "field": "@timestamp", + "target_field": "event.created" + } + }, + { + "grok": { + "field": "message", + "patterns": [ + "^%{CHAR:first_char}" + ], + "pattern_definitions": { + "CHAR": "." + } + } + }, + { + "pipeline": { + "if": "ctx.first_char != '{'", + "name": "{< IngestPipeline "pipeline-plaintext" >}" + } + }, + { + "pipeline": { + "if": "ctx.first_char == '{'", + "name": "{< IngestPipeline "pipeline-json" >}" + } + }, + { + "date": { + "field": "elasticsearch.slowlog.timestamp", + "target_field": "@timestamp", + "formats": [ + "ISO8601" + ], + {< if .convert_timezone >}"timezone": "{{ event.timezone }}",{< end >} + "ignore_failure": true + } + }, + { + "remove": { + "field": "elasticsearch.slowlog.timestamp" + } + }, + { + "script": { + "lang": "painless", + "source": "ctx.event.duration = Math.round(ctx.elasticsearch.slowlog.duration * params.scale)", + "params": { + "scale": 1000000 + }, + "if": "ctx.elasticsearch.slowlog?.duration != null" + } + }, + { + "remove": { + "field": "elasticsearch.slowlog.duration", + "ignore_missing": true + } + }, + { + "remove": { + "field": [ + "first_char" + ] + } + } + ], + "on_failure": [ + { + "set": { + "field": "error.message", + "value": "{{ _ingest.on_failure_message }}" + } + } + ] } diff --git a/filebeat/module/elasticsearch/slowlog/manifest.yml b/filebeat/module/elasticsearch/slowlog/manifest.yml index 904b5db5ef6..0e839abd39d 100644 --- a/filebeat/module/elasticsearch/slowlog/manifest.yml +++ b/filebeat/module/elasticsearch/slowlog/manifest.yml @@ -5,12 +5,18 @@ var: default: - /var/log/elasticsearch/*_index_search_slowlog.log - /var/log/elasticsearch/*_index_indexing_slowlog.log + - /var/log/elasticsearch/*_index_search_slowlog.json + - /var/log/elasticsearch/*_index_indexing_slowlog.json os.darwin: - /usr/local/var/lib/elasticsearch/*_index_search_slowlog.log - /usr/local/var/lib/elasticsearch/*_index_indexing_slowlog.log + - /usr/local/var/lib/elasticsearch/*_index_search_slowlog.json + - /usr/local/var/lib/elasticsearch/*_index_indexing_slowlog.json os.windows: - c:/ProgramData/Elastic/Elasticsearch/logs/*_index_search_slowlog.log - c:/ProgramData/Elastic/Elasticsearch/logs/*_index_indexing_slowlog.log + - c:/ProgramData/Elastic/Elasticsearch/logs/*_index_search_slowlog.json + - c:/ProgramData/Elastic/Elasticsearch/logs/*_index_indexing_slowlog.json - name: convert_timezone default: false # if ES < 6.1.0, this flag switches to false automatically when evaluating the @@ -19,5 +25,8 @@ var: version: 6.1.0 value: false -ingest_pipeline: ingest/pipeline.json +ingest_pipeline: + - ingest/pipeline.json + - ingest/pipeline-plaintext.json + - ingest/pipeline-json.json input: config/slowlog.yml diff --git a/filebeat/module/elasticsearch/slowlog/test/auditlog_index_indexing_slowlog.log-expected.json b/filebeat/module/elasticsearch/slowlog/test/auditlog_index_indexing_slowlog.log-expected.json index 32dda026a23..e5d67738dfc 100644 --- a/filebeat/module/elasticsearch/slowlog/test/auditlog_index_indexing_slowlog.log-expected.json +++ b/filebeat/module/elasticsearch/slowlog/test/auditlog_index_indexing_slowlog.log-expected.json @@ -74,7 +74,6 @@ "elasticsearch.slowlog.id": "s01HZ2QBk9jw4gtgaFtn", "elasticsearch.slowlog.logger": "index.indexing.slowlog.index", "elasticsearch.slowlog.routing": "", - "elasticsearch.slowlog.source_query": "\n{\n \"@timestamp\":\"2018-07-04T21:27:30.730Z\",\n \"metricset\":{\n \"name\":\"network\",\n \"module\":\"system\",\n \"rtt\":7264},\n \"system\":{\n \"network\":{\n \"name\":\"lo0\",\n \"in\":{\n \"errors\":0,\n \"dropped\":0,\n \"bytes\":77666873,\n \"packets\":244595},\n \"out\":{\n \"packets\":244595,\n \"bytes\":77666873,\n \"errors\":0,\n \"dropped\":0\n }\n }\n },\n \"beat\":{\n \"name\":\"Rados-MacBook-Pro.local\",\n \"hostname\":\"Rados-MacBook-Pro.local\",\n \"version\":\"6.3.0\"\n },\n \"host\":{\n \"name\":\"Rados-MacBook-Pro.local\"\n }\n }", "elasticsearch.slowlog.took": "1.7ms", "elasticsearch.slowlog.type": "doc", "event.dataset": "elasticsearch.slowlog", @@ -82,12 +81,9 @@ "event.module": "elasticsearch", "fileset.name": "slowlog", "input.type": "log", - "log.flags": [ - "multiline" - ], "log.level": "INFO", "log.offset": 1817, - "message": "[2018-07-04T21:51:30,411][INFO ][index.indexing.slowlog.index] [v_VJhjV] [metricbeat-6.3.0-2018.07.04/VLKxBLvUSYuIMKzpacGjRg] took[1.7ms], took_millis[1], type[doc], id[s01HZ2QBk9jw4gtgaFtn], routing[], source[\n{\n \"@timestamp\":\"2018-07-04T21:27:30.730Z\",\n \"metricset\":{\n \"name\":\"network\",\n \"module\":\"system\",\n \"rtt\":7264},\n \"system\":{\n \"network\":{\n \"name\":\"lo0\",\n \"in\":{\n \"errors\":0,\n \"dropped\":0,\n \"bytes\":77666873,\n \"packets\":244595},\n \"out\":{\n \"packets\":244595,\n \"bytes\":77666873,\n \"errors\":0,\n \"dropped\":0\n }\n }\n },\n \"beat\":{\n \"name\":\"Rados-MacBook-Pro.local\",\n \"hostname\":\"Rados-MacBook-Pro.local\",\n \"version\":\"6.3.0\"\n },\n \"host\":{\n \"name\":\"Rados-MacBook-Pro.local\"\n }\n }]", + "message": "[2018-07-04T21:51:30,411][INFO ][index.indexing.slowlog.index] [v_VJhjV] [metricbeat-6.3.0-2018.07.04/VLKxBLvUSYuIMKzpacGjRg] took[1.7ms], took_millis[1], type[doc], id[s01HZ2QBk9jw4gtgaFtn], routing[], source[", "service.type": "elasticsearch" }, { diff --git a/filebeat/module/elasticsearch/slowlog/test/es_index_indexing_slowlog-json.log b/filebeat/module/elasticsearch/slowlog/test/es_index_indexing_slowlog-json.log new file mode 100644 index 00000000000..2fff1458a15 --- /dev/null +++ b/filebeat/module/elasticsearch/slowlog/test/es_index_indexing_slowlog-json.log @@ -0,0 +1,2 @@ +{"type": "index_indexing_slowlog", "timestamp": "2019-01-29T08:35:54,170+0100", "level": "WARN", "component": "i.i.s.index", "cluster.name": "distribution_run", "node.name": "node-0", "cluster.uuid": "oqKkg2eoQh2P_KrKliI3DA", "node.id": "U7rdLkcqR9eRvOiyLmr_qQ", "message": "[index1/PJlGJFFURIO1zIboktQMUw] took[4.6ms], took_millis[4], type[_doc], id[1], routing[], source[{\"somefield\":\"somevalue\"}]" } +{"type": "index_indexing_slowlog", "timestamp": "2019-01-29T08:35:58,359+0100", "level": "WARN", "component": "i.i.s.index", "cluster.name": "distribution_run", "node.name": "node-0", "cluster.uuid": "oqKkg2eoQh2P_KrKliI3DA", "node.id": "U7rdLkcqR9eRvOiyLmr_qQ", "message": "[index1/PJlGJFFURIO1zIboktQMUw] took[803micros], took_millis[0], type[_doc], id[2], routing[], source[{\"somefield\":\"somevalue\"}]" } diff --git a/filebeat/module/elasticsearch/slowlog/test/es_index_indexing_slowlog-json.log-expected.json b/filebeat/module/elasticsearch/slowlog/test/es_index_indexing_slowlog-json.log-expected.json new file mode 100644 index 00000000000..74de3b8a203 --- /dev/null +++ b/filebeat/module/elasticsearch/slowlog/test/es_index_indexing_slowlog-json.log-expected.json @@ -0,0 +1,52 @@ +[ + { + "@timestamp": "2019-01-29T07:35:54.170Z", + "ecs.version": "1.0.0-beta2", + "elasticsearch.cluster.name": "distribution_run", + "elasticsearch.cluster.uuid": "oqKkg2eoQh2P_KrKliI3DA", + "elasticsearch.component": "i.i.s.index", + "elasticsearch.index.id": "PJlGJFFURIO1zIboktQMUw", + "elasticsearch.index.name": "index1", + "elasticsearch.node.id": "U7rdLkcqR9eRvOiyLmr_qQ", + "elasticsearch.node.name": "node-0", + "elasticsearch.slowlog.id": "1", + "elasticsearch.slowlog.routing": "", + "elasticsearch.slowlog.source_query": "{\"somefield\":\"somevalue\"}", + "elasticsearch.slowlog.took": "4.6ms", + "elasticsearch.slowlog.type": "_doc", + "event.dataset": "elasticsearch.slowlog", + "event.duration": 4000000, + "event.module": "elasticsearch", + "fileset.name": "slowlog", + "input.type": "log", + "log.level": "WARN", + "log.offset": 0, + "message": "{\"type\": \"index_indexing_slowlog\", \"timestamp\": \"2019-01-29T08:35:54,170+0100\", \"level\": \"WARN\", \"component\": \"i.i.s.index\", \"cluster.name\": \"distribution_run\", \"node.name\": \"node-0\", \"cluster.uuid\": \"oqKkg2eoQh2P_KrKliI3DA\", \"node.id\": \"U7rdLkcqR9eRvOiyLmr_qQ\", \"message\": \"[index1/PJlGJFFURIO1zIboktQMUw] took[4.6ms], took_millis[4], type[_doc], id[1], routing[], source[{\\\"somefield\\\":\\\"somevalue\\\"}]\" }", + "service.type": "elasticsearch" + }, + { + "@timestamp": "2019-01-29T07:35:58.359Z", + "ecs.version": "1.0.0-beta2", + "elasticsearch.cluster.name": "distribution_run", + "elasticsearch.cluster.uuid": "oqKkg2eoQh2P_KrKliI3DA", + "elasticsearch.component": "i.i.s.index", + "elasticsearch.index.id": "PJlGJFFURIO1zIboktQMUw", + "elasticsearch.index.name": "index1", + "elasticsearch.node.id": "U7rdLkcqR9eRvOiyLmr_qQ", + "elasticsearch.node.name": "node-0", + "elasticsearch.slowlog.id": "2", + "elasticsearch.slowlog.routing": "", + "elasticsearch.slowlog.source_query": "{\"somefield\":\"somevalue\"}", + "elasticsearch.slowlog.took": "803micros", + "elasticsearch.slowlog.type": "_doc", + "event.dataset": "elasticsearch.slowlog", + "event.duration": 0, + "event.module": "elasticsearch", + "fileset.name": "slowlog", + "input.type": "log", + "log.level": "WARN", + "log.offset": 409, + "message": "{\"type\": \"index_indexing_slowlog\", \"timestamp\": \"2019-01-29T08:35:58,359+0100\", \"level\": \"WARN\", \"component\": \"i.i.s.index\", \"cluster.name\": \"distribution_run\", \"node.name\": \"node-0\", \"cluster.uuid\": \"oqKkg2eoQh2P_KrKliI3DA\", \"node.id\": \"U7rdLkcqR9eRvOiyLmr_qQ\", \"message\": \"[index1/PJlGJFFURIO1zIboktQMUw] took[803micros], took_millis[0], type[_doc], id[2], routing[], source[{\\\"somefield\\\":\\\"somevalue\\\"}]\" }", + "service.type": "elasticsearch" + } +] \ No newline at end of file diff --git a/filebeat/module/elasticsearch/slowlog/test/es_index_search_slowlog-json.log b/filebeat/module/elasticsearch/slowlog/test/es_index_search_slowlog-json.log new file mode 100644 index 00000000000..6239afaa3ea --- /dev/null +++ b/filebeat/module/elasticsearch/slowlog/test/es_index_search_slowlog-json.log @@ -0,0 +1,3 @@ +{"type": "index_search_slowlog", "timestamp": "2019-01-29T08:31:40,426+0100", "level": "WARN", "component": "i.s.s.query", "cluster.name": "distribution_run", "node.name": "node-0", "cluster.uuid": "oqKkg2eoQh2P_KrKliI3DA", "node.id": "U7rdLkcqR9eRvOiyLmr_qQ", "message": "[index1][0] took[70.4micros], took_millis[0], total_hits[0 hits], types[], stats[], search_type[QUERY_THEN_FETCH], total_shards[1], source[{}], id[], " } +{"type": "index_search_slowlog", "timestamp": "2019-01-29T08:36:01,675+0100", "level": "WARN", "component": "i.s.s.query", "cluster.name": "distribution_run", "node.name": "node-0", "cluster.uuid": "oqKkg2eoQh2P_KrKliI3DA", "node.id": "U7rdLkcqR9eRvOiyLmr_qQ", "message": "[index1][0] took[731.3micros], took_millis[0], total_hits[2 hits], types[], stats[], search_type[QUERY_THEN_FETCH], total_shards[1], source[{}], id[], " } +{"type": "index_search_slowlog", "timestamp": "2019-01-29T08:36:01,685+0100", "level": "WARN", "component": "i.s.s.fetch", "cluster.name": "distribution_run", "node.name": "node-0", "cluster.uuid": "oqKkg2eoQh2P_KrKliI3DA", "node.id": "U7rdLkcqR9eRvOiyLmr_qQ", "message": "[index1][0] took[9.9ms], took_millis[9], total_hits[2 hits], types[], stats[], search_type[QUERY_THEN_FETCH], total_shards[1], source[{}], id[], " } diff --git a/filebeat/module/elasticsearch/slowlog/test/es_index_search_slowlog-json.log-expected.json b/filebeat/module/elasticsearch/slowlog/test/es_index_search_slowlog-json.log-expected.json new file mode 100644 index 00000000000..9ea5fff1821 --- /dev/null +++ b/filebeat/module/elasticsearch/slowlog/test/es_index_search_slowlog-json.log-expected.json @@ -0,0 +1,65 @@ +[ + { + "@timestamp": "2019-01-29T07:31:40.426Z", + "ecs.version": "1.0.0-beta2", + "elasticsearch.cluster.name": "distribution_run", + "elasticsearch.cluster.uuid": "oqKkg2eoQh2P_KrKliI3DA", + "elasticsearch.component": "i.s.s.query", + "elasticsearch.index.name": "index1", + "elasticsearch.node.id": "U7rdLkcqR9eRvOiyLmr_qQ", + "elasticsearch.node.name": "node-0", + "elasticsearch.shard.id": "0", + "elasticsearch.slowlog.took": "70.4micros", + "event.dataset": "elasticsearch.slowlog", + "event.duration": 0, + "event.module": "elasticsearch", + "fileset.name": "slowlog", + "input.type": "log", + "log.level": "WARN", + "log.offset": 0, + "message": "{\"type\": \"index_search_slowlog\", \"timestamp\": \"2019-01-29T08:31:40,426+0100\", \"level\": \"WARN\", \"component\": \"i.s.s.query\", \"cluster.name\": \"distribution_run\", \"node.name\": \"node-0\", \"cluster.uuid\": \"oqKkg2eoQh2P_KrKliI3DA\", \"node.id\": \"U7rdLkcqR9eRvOiyLmr_qQ\", \"message\": \"[index1][0] took[70.4micros], took_millis[0], total_hits[0 hits], types[], stats[], search_type[QUERY_THEN_FETCH], total_shards[1], source[{}], id[], \" }", + "service.type": "elasticsearch" + }, + { + "@timestamp": "2019-01-29T07:36:01.675Z", + "ecs.version": "1.0.0-beta2", + "elasticsearch.cluster.name": "distribution_run", + "elasticsearch.cluster.uuid": "oqKkg2eoQh2P_KrKliI3DA", + "elasticsearch.component": "i.s.s.query", + "elasticsearch.index.name": "index1", + "elasticsearch.node.id": "U7rdLkcqR9eRvOiyLmr_qQ", + "elasticsearch.node.name": "node-0", + "elasticsearch.shard.id": "0", + "elasticsearch.slowlog.took": "731.3micros", + "event.dataset": "elasticsearch.slowlog", + "event.duration": 0, + "event.module": "elasticsearch", + "fileset.name": "slowlog", + "input.type": "log", + "log.level": "WARN", + "log.offset": 429, + "message": "{\"type\": \"index_search_slowlog\", \"timestamp\": \"2019-01-29T08:36:01,675+0100\", \"level\": \"WARN\", \"component\": \"i.s.s.query\", \"cluster.name\": \"distribution_run\", \"node.name\": \"node-0\", \"cluster.uuid\": \"oqKkg2eoQh2P_KrKliI3DA\", \"node.id\": \"U7rdLkcqR9eRvOiyLmr_qQ\", \"message\": \"[index1][0] took[731.3micros], took_millis[0], total_hits[2 hits], types[], stats[], search_type[QUERY_THEN_FETCH], total_shards[1], source[{}], id[], \" }", + "service.type": "elasticsearch" + }, + { + "@timestamp": "2019-01-29T07:36:01.685Z", + "ecs.version": "1.0.0-beta2", + "elasticsearch.cluster.name": "distribution_run", + "elasticsearch.cluster.uuid": "oqKkg2eoQh2P_KrKliI3DA", + "elasticsearch.component": "i.s.s.fetch", + "elasticsearch.index.name": "index1", + "elasticsearch.node.id": "U7rdLkcqR9eRvOiyLmr_qQ", + "elasticsearch.node.name": "node-0", + "elasticsearch.shard.id": "0", + "elasticsearch.slowlog.took": "9.9ms", + "event.dataset": "elasticsearch.slowlog", + "event.duration": 9000000, + "event.module": "elasticsearch", + "fileset.name": "slowlog", + "input.type": "log", + "log.level": "WARN", + "log.offset": 859, + "message": "{\"type\": \"index_search_slowlog\", \"timestamp\": \"2019-01-29T08:36:01,685+0100\", \"level\": \"WARN\", \"component\": \"i.s.s.fetch\", \"cluster.name\": \"distribution_run\", \"node.name\": \"node-0\", \"cluster.uuid\": \"oqKkg2eoQh2P_KrKliI3DA\", \"node.id\": \"U7rdLkcqR9eRvOiyLmr_qQ\", \"message\": \"[index1][0] took[9.9ms], took_millis[9], total_hits[2 hits], types[], stats[], search_type[QUERY_THEN_FETCH], total_shards[1], source[{}], id[], \" }", + "service.type": "elasticsearch" + } +] \ No newline at end of file diff --git a/filebeat/module/elasticsearch/slowlog/test/test.log b/filebeat/module/elasticsearch/slowlog/test/test.log index 8f072b06159..3d6d1ebae79 100644 --- a/filebeat/module/elasticsearch/slowlog/test/test.log +++ b/filebeat/module/elasticsearch/slowlog/test/test.log @@ -1,4 +1,4 @@ -[2018-06-29T10:06:14,933][INFO ][index.search.slowlog.query] [v_VJhjV] [metricbeat-6.3.0-2018.06.26][0] took[4.5ms], took_millis[4], total_hits[19435], types[], stats[], search_type[QUERY_THEN_FETCH], total_shards[1], source[{"query":{"match_all":{"boost":1.0}}}], +[2018-06-29T10:06:14,933][INFO ][index.search.slowlog.query] [v_VJhjV] [metricbeat-6.3.0-2018.06.26][0] took[4.5ms], took_millis[4], total_hits[19435], types[], stats[group1,group2], search_type[QUERY_THEN_FETCH], total_shards[1], source[{"query":{"match_all":{"boost":1.0}}}], [2018-06-29T10:06:14,943][INFO ][index.search.slowlog.fetch] [v_VJhjV] [metricbeat-6.3.0-2018.06.26][0] took[10.8ms], took_millis[10], total_hits[19435], types[], stats[], search_type[QUERY_THEN_FETCH], total_shards[1], source[{"query":{"match_all":{"boost":1.0}}}], [2018-06-29T09:01:01,821][INFO ][index.search.slowlog.query] [v_VJhjV] [metricbeat-6.3.0-2018.06.26][0] took[124.3ms], took_millis[124], total_hits[0], types[], stats[], search_type[QUERY_THEN_FETCH], total_shards[1], source[{"size":500,"query":{"match_none":{"boost":1.0}},"version":true,"_source":{"includes":[],"excludes":[]},"stored_fields":"*","docvalue_fields":["@timestamp","ceph.monitor_health.last_updated","docker.container.created","docker.healthcheck.event.end_date","docker.healthcheck.event.start_date","docker.image.created","kubernetes.container.start_time","kubernetes.event.metadata.timestamp.created","kubernetes.node.start_time","kubernetes.pod.start_time","kubernetes.system.start_time","mongodb.status.background_flushing.last_finished","mongodb.status.local_time","php_fpm.pool.start_time","postgresql.activity.backend_start","postgresql.activity.query_start","postgresql.activity.state_change","postgresql.activity.transaction_start","postgresql.bgwriter.stats_reset","postgresql.database.stats_reset","system.process.cpu.start_time"],"script_fields":{},"sort":[{"@timestamp":{"order":"desc","unmapped_type":"boolean"}}],"aggregations":{"2":{"date_histogram":{"field":"@timestamp","time_zone":"Europe/Berlin","interval":"30s","offset":0,"order":{"_key":"asc"},"keyed":false,"min_doc_count":1}}},"highlight":{"pre_tags":["@kibana-highlighted-field@"],"post_tags":["@/kibana-highlighted-field@"],"fragment_size":2147483647,"fields":{"*":{}}}}], [2018-06-29T09:01:01,827][INFO ][index.search.slowlog.fetch] [v_VJhjV] [metricbeat-6.3.0-2018.06.26][0] took[7.2ms], took_millis[7], total_hits[0], types[], stats[], search_type[QUERY_THEN_FETCH], total_shards[1], source[{"size":500,"query":{"match_none":{"boost":1.0}},"version":true,"_source":{"includes":[],"excludes":[]},"stored_fields":"*","docvalue_fields":["@timestamp","ceph.monitor_health.last_updated","docker.container.created","docker.healthcheck.event.end_date","docker.healthcheck.event.start_date","docker.image.created","kubernetes.container.start_time","kubernetes.event.metadata.timestamp.created","kubernetes.node.start_time","kubernetes.pod.start_time","kubernetes.system.start_time","mongodb.status.background_flushing.last_finished","mongodb.status.local_time","php_fpm.pool.start_time","postgresql.activity.backend_start","postgresql.activity.query_start","postgresql.activity.state_change","postgresql.activity.transaction_start","postgresql.bgwriter.stats_reset","postgresql.database.stats_reset","system.process.cpu.start_time"],"script_fields":{},"sort":[{"@timestamp":{"order":"desc","unmapped_type":"boolean"}}],"aggregations":{"2":{"date_histogram":{"field":"@timestamp","time_zone":"Europe/Berlin","interval":"30s","offset":0,"order":{"_key":"asc"},"keyed":false,"min_doc_count":1}}},"highlight":{"pre_tags":["@kibana-highlighted-field@"],"post_tags":["@/kibana-highlighted-field@"],"fragment_size":2147483647,"fields":{"*":{}}}}], diff --git a/filebeat/module/elasticsearch/slowlog/test/test.log-expected.json b/filebeat/module/elasticsearch/slowlog/test/test.log-expected.json index 4cda0f2d514..ec13bd7ce58 100644 --- a/filebeat/module/elasticsearch/slowlog/test/test.log-expected.json +++ b/filebeat/module/elasticsearch/slowlog/test/test.log-expected.json @@ -8,7 +8,10 @@ "elasticsearch.slowlog.logger": "index.search.slowlog.query", "elasticsearch.slowlog.search_type": "QUERY_THEN_FETCH", "elasticsearch.slowlog.source_query": "{\"query\":{\"match_all\":{\"boost\":1.0}}}", - "elasticsearch.slowlog.stats": "", + "elasticsearch.slowlog.stats": [ + "group1", + "group2" + ], "elasticsearch.slowlog.took": "4.5ms", "elasticsearch.slowlog.total_hits": 19435, "elasticsearch.slowlog.total_shards": 1, @@ -20,7 +23,7 @@ "input.type": "log", "log.level": "INFO", "log.offset": 0, - "message": "[2018-06-29T10:06:14,933][INFO ][index.search.slowlog.query] [v_VJhjV] [metricbeat-6.3.0-2018.06.26][0] took[4.5ms], took_millis[4], total_hits[19435], types[], stats[], search_type[QUERY_THEN_FETCH], total_shards[1], source[{\"query\":{\"match_all\":{\"boost\":1.0}}}],", + "message": "[2018-06-29T10:06:14,933][INFO ][index.search.slowlog.query] [v_VJhjV] [metricbeat-6.3.0-2018.06.26][0] took[4.5ms], took_millis[4], total_hits[19435], types[], stats[group1,group2], search_type[QUERY_THEN_FETCH], total_shards[1], source[{\"query\":{\"match_all\":{\"boost\":1.0}}}],", "service.type": "elasticsearch" }, { @@ -43,7 +46,7 @@ "fileset.name": "slowlog", "input.type": "log", "log.level": "INFO", - "log.offset": 265, + "log.offset": 278, "message": "[2018-06-29T10:06:14,943][INFO ][index.search.slowlog.fetch] [v_VJhjV] [metricbeat-6.3.0-2018.06.26][0] took[10.8ms], took_millis[10], total_hits[19435], types[], stats[], search_type[QUERY_THEN_FETCH], total_shards[1], source[{\"query\":{\"match_all\":{\"boost\":1.0}}}],", "service.type": "elasticsearch" }, @@ -67,7 +70,7 @@ "fileset.name": "slowlog", "input.type": "log", "log.level": "INFO", - "log.offset": 532, + "log.offset": 545, "message": "[2018-06-29T09:01:01,821][INFO ][index.search.slowlog.query] [v_VJhjV] [metricbeat-6.3.0-2018.06.26][0] took[124.3ms], took_millis[124], total_hits[0], types[], stats[], search_type[QUERY_THEN_FETCH], total_shards[1], source[{\"size\":500,\"query\":{\"match_none\":{\"boost\":1.0}},\"version\":true,\"_source\":{\"includes\":[],\"excludes\":[]},\"stored_fields\":\"*\",\"docvalue_fields\":[\"@timestamp\",\"ceph.monitor_health.last_updated\",\"docker.container.created\",\"docker.healthcheck.event.end_date\",\"docker.healthcheck.event.start_date\",\"docker.image.created\",\"kubernetes.container.start_time\",\"kubernetes.event.metadata.timestamp.created\",\"kubernetes.node.start_time\",\"kubernetes.pod.start_time\",\"kubernetes.system.start_time\",\"mongodb.status.background_flushing.last_finished\",\"mongodb.status.local_time\",\"php_fpm.pool.start_time\",\"postgresql.activity.backend_start\",\"postgresql.activity.query_start\",\"postgresql.activity.state_change\",\"postgresql.activity.transaction_start\",\"postgresql.bgwriter.stats_reset\",\"postgresql.database.stats_reset\",\"system.process.cpu.start_time\"],\"script_fields\":{},\"sort\":[{\"@timestamp\":{\"order\":\"desc\",\"unmapped_type\":\"boolean\"}}],\"aggregations\":{\"2\":{\"date_histogram\":{\"field\":\"@timestamp\",\"time_zone\":\"Europe/Berlin\",\"interval\":\"30s\",\"offset\":0,\"order\":{\"_key\":\"asc\"},\"keyed\":false,\"min_doc_count\":1}}},\"highlight\":{\"pre_tags\":[\"@kibana-highlighted-field@\"],\"post_tags\":[\"@/kibana-highlighted-field@\"],\"fragment_size\":2147483647,\"fields\":{\"*\":{}}}}],", "service.type": "elasticsearch" }, @@ -91,7 +94,7 @@ "fileset.name": "slowlog", "input.type": "log", "log.level": "INFO", - "log.offset": 1999, + "log.offset": 2012, "message": "[2018-06-29T09:01:01,827][INFO ][index.search.slowlog.fetch] [v_VJhjV] [metricbeat-6.3.0-2018.06.26][0] took[7.2ms], took_millis[7], total_hits[0], types[], stats[], search_type[QUERY_THEN_FETCH], total_shards[1], source[{\"size\":500,\"query\":{\"match_none\":{\"boost\":1.0}},\"version\":true,\"_source\":{\"includes\":[],\"excludes\":[]},\"stored_fields\":\"*\",\"docvalue_fields\":[\"@timestamp\",\"ceph.monitor_health.last_updated\",\"docker.container.created\",\"docker.healthcheck.event.end_date\",\"docker.healthcheck.event.start_date\",\"docker.image.created\",\"kubernetes.container.start_time\",\"kubernetes.event.metadata.timestamp.created\",\"kubernetes.node.start_time\",\"kubernetes.pod.start_time\",\"kubernetes.system.start_time\",\"mongodb.status.background_flushing.last_finished\",\"mongodb.status.local_time\",\"php_fpm.pool.start_time\",\"postgresql.activity.backend_start\",\"postgresql.activity.query_start\",\"postgresql.activity.state_change\",\"postgresql.activity.transaction_start\",\"postgresql.bgwriter.stats_reset\",\"postgresql.database.stats_reset\",\"system.process.cpu.start_time\"],\"script_fields\":{},\"sort\":[{\"@timestamp\":{\"order\":\"desc\",\"unmapped_type\":\"boolean\"}}],\"aggregations\":{\"2\":{\"date_histogram\":{\"field\":\"@timestamp\",\"time_zone\":\"Europe/Berlin\",\"interval\":\"30s\",\"offset\":0,\"order\":{\"_key\":\"asc\"},\"keyed\":false,\"min_doc_count\":1}}},\"highlight\":{\"pre_tags\":[\"@kibana-highlighted-field@\"],\"post_tags\":[\"@/kibana-highlighted-field@\"],\"fragment_size\":2147483647,\"fields\":{\"*\":{}}}}],", "service.type": "elasticsearch" }, @@ -113,7 +116,7 @@ "fileset.name": "slowlog", "input.type": "log", "log.level": "INFO", - "log.offset": 3462, + "log.offset": 3475, "message": "[2018-07-04T13:48:07,452][INFO ][index.indexing.slowlog.index] [v_VJhjV] [metricbeat-6.3.0-2018.07.04/VLKxBLvUSYuIMKzpacGjRg] took[1.4ms], took_millis[1], type[doc], id[KUyMZWQBk9jw4gtg2y5-], routing[], source[{\"@timestamp\":\"2018-07-04T13:47:50.747Z\",\"system\":{\"process\":{\"ppid\":34526,\"state\":\"running\",\"cpu\":{\"total\":{\"value\":734879,\"pct\":0.0173,\"norm\":{\"pct\":0.0043}},\"start_time\":\"2018-07-04T06:56:34.863Z\"},\"pgid\":34526,\"cmdline\":\"/Applications/Firefox.app/Contents/MacOS/plugin-container.app/Contents/MacOS/plugin-container -childID 1 -isForBrowser -prefsLen 22119 -schedulerPrefs 0001,2 -greomni /Applications/Firefox.app/Contents/Resources/omni.ja -appomni /Applications/Firefox.app/Contents/Resources/browser/omni.ja -appdir /Applications/Firefox.app/Contents/Resources/browser -profile /Users/rado/Library/Application Support/Firefox/Profiles/pt6eoq1j.default-1484133908360 34526 gecko-crash-server-pipe.34526 org.mozilla.machname.231926932 tab\",\"name\":\"plugin-containe\",\"memory\":{\"size\":7489249280,\"rss\":{\"bytes\":567619584,\"pct\":0.033},\"share\":0},\"pid\":34528,\"username\":\"rado\"}},\"metricset\":{\"name\":\"process\",\"module\":\"system\",\"rtt\":43856},\"beat\":{\"hostname\":\"Rados-MacBook-Pro.local\",\"version\":\"6.3.0\",\"name\":\"Rados-MacBook-Pro.local\"},\"host\":{\"name\":\"Rados-MacBook-Pro.local\"}}]", "service.type": "elasticsearch" }, @@ -126,7 +129,6 @@ "elasticsearch.slowlog.id": "s01HZ2QBk9jw4gtgaFtn", "elasticsearch.slowlog.logger": "index.indexing.slowlog.index", "elasticsearch.slowlog.routing": "", - "elasticsearch.slowlog.source_query": "\n{\n \"@timestamp\":\"2018-07-04T21:27:30.730Z\",\n \"metricset\":{\n \"name\":\"network\",\n \"module\":\"system\",\n \"rtt\":7264},\n \"system\":{\n \"network\":{\n \"name\":\"lo0\",\n \"in\":{\n \"errors\":0,\n \"dropped\":0,\n \"bytes\":77666873,\n \"packets\":244595},\n \"out\":{\n \"packets\":244595,\n \"bytes\":77666873,\n \"errors\":0,\n \"dropped\":0\n }\n }\n },\n \"beat\":{\n \"name\":\"Rados-MacBook-Pro.local\",\n \"hostname\":\"Rados-MacBook-Pro.local\",\n \"version\":\"6.3.0\"\n },\n \"host\":{\n \"name\":\"Rados-MacBook-Pro.local\"\n }\n }", "elasticsearch.slowlog.took": "1.7ms", "elasticsearch.slowlog.type": "doc", "event.dataset": "elasticsearch.slowlog", @@ -134,12 +136,9 @@ "event.module": "elasticsearch", "fileset.name": "slowlog", "input.type": "log", - "log.flags": [ - "multiline" - ], "log.level": "INFO", - "log.offset": 4753, - "message": "[2018-07-04T21:51:30,411][INFO ][index.indexing.slowlog.index] [v_VJhjV] [metricbeat-6.3.0-2018.07.04/VLKxBLvUSYuIMKzpacGjRg] took[1.7ms], took_millis[1], type[doc], id[s01HZ2QBk9jw4gtgaFtn], routing[], source[\n{\n \"@timestamp\":\"2018-07-04T21:27:30.730Z\",\n \"metricset\":{\n \"name\":\"network\",\n \"module\":\"system\",\n \"rtt\":7264},\n \"system\":{\n \"network\":{\n \"name\":\"lo0\",\n \"in\":{\n \"errors\":0,\n \"dropped\":0,\n \"bytes\":77666873,\n \"packets\":244595},\n \"out\":{\n \"packets\":244595,\n \"bytes\":77666873,\n \"errors\":0,\n \"dropped\":0\n }\n }\n },\n \"beat\":{\n \"name\":\"Rados-MacBook-Pro.local\",\n \"hostname\":\"Rados-MacBook-Pro.local\",\n \"version\":\"6.3.0\"\n },\n \"host\":{\n \"name\":\"Rados-MacBook-Pro.local\"\n }\n }]", + "log.offset": 4766, + "message": "[2018-07-04T21:51:30,411][INFO ][index.indexing.slowlog.index] [v_VJhjV] [metricbeat-6.3.0-2018.07.04/VLKxBLvUSYuIMKzpacGjRg] took[1.7ms], took_millis[1], type[doc], id[s01HZ2QBk9jw4gtgaFtn], routing[], source[", "service.type": "elasticsearch" } ] \ No newline at end of file diff --git a/filebeat/module/haproxy/_meta/fields.yml b/filebeat/module/haproxy/_meta/fields.yml index 674d20f29be..7f4f038393f 100644 --- a/filebeat/module/haproxy/_meta/fields.yml +++ b/filebeat/module/haproxy/_meta/fields.yml @@ -60,7 +60,7 @@ description: Condition the session was in when the session ended. - name: mode - type: text + type: keyword description: mode that the frontend is operating (TCP or HTTP) - name: connections diff --git a/filebeat/module/haproxy/fields.go b/filebeat/module/haproxy/fields.go index c541a242e12..861f3844435 100644 --- a/filebeat/module/haproxy/fields.go +++ b/filebeat/module/haproxy/fields.go @@ -32,5 +32,5 @@ func init() { // AssetHaproxy returns asset data. // This is the base64 encoded gzipped contents of module/haproxy. func AssetHaproxy() string { - return "eJzMWc2O2zYQvu9TDPbSBEj8AAs0QLFNmwBtsoe9G2NyLBGhSIUc7cZvX/BHsn5ta9dB6pslzszH+fk4Q72Hb3S4gxJrZ38cbgBYsaY7uM1Pbm8AJHnhVM3Kmjv4cAMA7Xr418pG0w3AXpGW/i6+fA8GK+orDT8+1HQHhbNNnZ/M6D0qyn+P2vbOGiYjt+Fv93ak5QtWBHYPXFInAG+sA608kyH3Fp5LJUpwJEg9kQQ0EmpnBXlPMsoJawyJoG8zRbFD8W0NiLx+FsMzevCkSXCwbKFCgwWNMIQX4Ykn90RuBlF6cTEgjZ6zTFCdkIxMJmCGZ6yxZdTbZ1SsTLFlVdG28kt2H8NiCItAGaiU1sqTsEZ68DUZhqwnvA0QntAp23j43lBDfa0pd7Q1xRTREXeEdTVMe+tmQkGecaeVL9u47JVBnR16IeLdgclvHaE8DdI01Y5cCFyUAHZofKU4p0sEp1XEXFLyoLYFKA+UVm0uBBRdFp3+cyK5CkdbYdnvvyKO70AZoRsZxB2xU5dvIVfjBc48RtfR94Y8+5YWyFGPkna0ty6wgvJgDbUeziUcDV0KrnXtz0eXLf3modB2h3olTrWCYROrhlChlI68HzP8WU4n56zbVuQ9FosmP4ZFkBeFOivC7g/w6Y+HeBIqAwJ9RBX1TffK9INnEsY2TtD84qUIldSZTeKdL+xcVZGrlMHIkJ6RF7d4b41UqUBignnfHgbKHCmmfU5GBoaZmKusXLedIABcIg9P7ZBQNTmMRfzm8f4BrINPj48Pb0+dAYu8f28NozK+oyZhG8M+eK4nDShYPXVpnPN8Gst+GwMwbH/G4JLKwaszJSesEY1zgcX62OwA1DQiIVIpMTcja3PFBjO91fVAdmG8IspMKlcAWaKROtVvv0m7ItZJQ/BSqJ6V1m1a2gH3XxFuPuVO4f3SR9qepFkO6EdNTpERrVOVP6KKMN0h1DHbVnrc157H2xV77HmW6vyz2VtXRbYD3NmG+32StBFESe2p9oLCbmGo+maKGLXCoRdr5LJl+U0+oQYLKlU4TNjZDY7l1lJtHa+3NZEaG5o4NhPL+OSdt5ZstQQ5EjlvSsmVFoYSZw1I8pwPvaVM+fO4BNQxa16REisD1cN4PlpTa6sSsG9rJHjWlwXZgci8W2an+fTrzt6/yX5+6DsbCuSSXGANDO1kJrhUrhtVJ2cP2eGr0QeoHYUJFVRqfJLij2G4VcITOlFCrZtCmdBH4BMqjTtNYdof6Gr8kCtPnePCGlaGDI8L5JT3R0VZkN2c1LMc9j6QxrA7bJW3WzHstlZDOanpEjDainHZrASxoOES446K0NW+Lh7LSi4KhuLDaxNiQcUKD7w+FU4rWoLSXfUxL3LBg6YwGKGU/eeraXaGAR352ho/BDuw/VdUAo409m5NwhQxLzwP5QwvYM2NI7kV1n5TJxv9D6OXAF/jG9RwG5T9/oS6oVugUJWgjFQizT/deJQbmRJlarKSzXZeafe0mfPaDOKSUJI72fZNIf+jPIc2MAt32sYgQDbUejzR9XFQvc1Cx8VJ2y3ECbWK1D6cJOZbxMFgOd+JM3LjX1IdIak3nUuX9SwVx2zGjtvONQk7lf3f52seVUoMe+LGGZKAbdY+Ky5BsW+39ovTNmK4MGvT2p+WtA6ft9nIVitzMkj3tqo18TBLIEj17zEDqoq4tPJdtwaNTEJP5OKo5tkpU7wUdLy+jXfwIbC24a1ExuFd/AT96nvc47cLH2Z2hH2j9ZDS34GxnNqkIBlQnBgwL9zTFbcxxJxisXe26jP8m8EWdlYe3gLumVy+rXae42eBOPS3d51rdtntUCye3I/3D/GLQmrXXzEYjb7RzH86moW7xtGksU530vxM+WoEhaA6ZXr39Uto6+nmvwAAAP//o2OPYg==" + return "eJzMWc2O2zYQvu9TDPbSBEj8AAs0QLFNmwBtsoe9G2NyLBFLkQo52o3fvuCPZP3aVtZB6pslzszH+fk4Q72HJzrcQYm1s98PNwCsWNMd3OYntzcAkrxwqmZlzR18uAGAdj38a2Wj6QZgr0hLfxdfvgeDFfWVhh8farqDwtmmzk9m9B4V5b9HbXtnDZOR2/C3ezvS8gUrArsHLqkTgDfWgVaeyZB7Cy+lEiU4EqSeSQIaCbWzgrwnGeWENYZE0LeZotiheFoDIq+fxfCCHjxpEhwsW6jQYEEjDOFFeOLJPZObQZReXAxIo+csE1QnJCOTCZjhGWtsGfX2BRUrU2xZVbSt/JLdx7AYwiJQBiqltfIkrJEefE2GIesJbwOEZ3TKNh6+NdRQX2vKHW1NMUV0xB1hXQ3T3rqZUJBn3GnlyzYue2VQZ4deiHh3YPJbRyhPgzRNtSMXAhclgB0aXynO6RLBaRUxl5Q8qG0BygOlVZsLAUWXRaf/nEiuwtFWWPb7r4jjO1BG6EYGcUfs1OVbyNV4gTOP0XX0rSHPvqUFctSjpB3trQusoDxYQ62HcwlHQ5eCa13789FlS795KLTdoV6JU61g2MSqIVQopSPvxwx/ltPJOeu2FXmPxaLJj2ER5EWhzoqw+wN8+uMhnoTKgEAfUUV9070yfeeZhLGNEzS/eClCJXVmk3jnCztXVeQqZTAypGfkxS3eWyNVKpCYYN63h4EyR4ppn5ORgWEm5iorp9t5osOLdYt8F2SAS+ThwR1yqiaHsY7fPN4/gHXw6fHx4e2pY2CR+u+tYVTGd+wkbGPYB+f1pAEFq+cuk3OqT8PZ72QAhh3QGFxSOXh1puqENaJxLhBZH5sdgJoGJQQr5eZmZG2u3mCmvboeyC6MV0SZeeUKIEs0UqcS7vdpV8Q66Ql+FKpnpXWblnZA/1eEmw+6U3i/9JG2h2mWA/pek1NkROtU5Y+oIkx3CHXMtpUet7bn8XbFHtuepTr/bPbWVZHwAHe24X6rJG0EUVJ7sP1AYbcwVH0zRYxa4dCLNXLZEv0mH1KDBZUqHCbs7AYnc2upto7X25pIjQ1NHJuJZXz4zltLtlqCHImcN6XkSgtDibMGJHnO595Spvx5XALqmDWvSImVgephPB+tqbVVCdi3NRI868uC7EBk3i2zA336dWfv32Q/P/SdDQVySS6wBoaOMhNcKteNqpOzh+zw1egD1I7CkAoq9T5J8ccw3yrhCZ0oodZNoUzoI/AZlcadpjDwD3Q1fsiVp85xYQ0rQ4bHBXLK+6OiLMhuTupZDnsfSGPYHbbK260YNlyroZzUdAkYbcW4bFaCWNBwiXFHRWhsXxePZSUXBUPx4bUJsaBihQdenwqnFS1B6W77mBe54EFTmI1Qyv7z1TQ7w4COfG2NH4Id2P4rKgFHGnsXJ2GKmBeeh3KGF7DmxpHcCmuf1MlG/8PoJcDX+AY13AZlvz+jbugWKFQlKCOVSPNPNx7lRqZEmZqsZLOdV9o9bea8NoO4JJTkTrZ9U8j/KM+hDczCnbYxCJANtR5PdH2cVW+z0HFx0nYLcUitIrUPJ4n5FnE8W84344zc+B8pkJDXm86ry3qW6mM2aced55qcncr+71M2Tyslhj1x4wxJwDZxXxSXoNi3W/vFmRsxXJi4ae3PzFuHL9tsZ6uVORmne1vVmniYKBCk+heaAVhFXFr5rluDRiahZ3JxYPPslClegTte5cb7+BBe2/BWIuPwXn6ygdV3usfvGD4M7wj7Rusht78DYzn1S0EyoDgxaV64pytuY4g5hWPvbNWn+jeDLeysPLwF3DO5fHPtPMdPBHH6b+891+yy26FYPMIf7x/i14XUt79iQhp9r5n/jDQLd42jSWOd7qf5hfIdCQpBdUr27kuY0NbTzX8BAAD///t2lGI=" } diff --git a/filebeat/module/haproxy/log/_meta/fields.yml b/filebeat/module/haproxy/log/_meta/fields.yml index 9176a8ae728..65d8902c1c2 100644 --- a/filebeat/module/haproxy/log/_meta/fields.yml +++ b/filebeat/module/haproxy/log/_meta/fields.yml @@ -14,7 +14,7 @@ - name: captured_headers description: > List of headers captured in the response due to the presence of the "capture response header" statement in the frontend. - type: text + type: keyword - name: status_code type: alias @@ -32,11 +32,11 @@ - name: captured_headers description: > List of headers captured in the request due to the presence of the "capture request header" statement in the frontend. - type: text + type: keyword - name: raw_request_line description: Complete HTTP request line, including the method, request and HTTP version string. - type: text + type: keyword - name: time_wait_without_data_ms description: Total time in milliseconds spent waiting for the server to send a full HTTP response, not counting data. diff --git a/filebeat/module/iis/access/_meta/fields.yml b/filebeat/module/iis/access/_meta/fields.yml index bce554d6156..f27870e2d8b 100644 --- a/filebeat/module/iis/access/_meta/fields.yml +++ b/filebeat/module/iis/access/_meta/fields.yml @@ -81,7 +81,7 @@ fields: - name: device type: alias - path: user_agent.device + path: user_agent.device.name migration: true - name: major type: alias diff --git a/filebeat/module/iis/access/ingest/default.json b/filebeat/module/iis/access/ingest/default.json index ff3a613723c..0c9cbe9836b 100644 --- a/filebeat/module/iis/access/ingest/default.json +++ b/filebeat/module/iis/access/ingest/default.json @@ -50,6 +50,12 @@ "user_agent": { "field": "iis.access.agent" } + }, { + "rename": { + "field": "user_agent.device", + "target_field": "user_agent.device.name", + "ignore_missing": true + } }, { "rename": { "field": "user_agent.os", diff --git a/filebeat/module/iis/access/test/test-iis-7.5.log-expected.json b/filebeat/module/iis/access/test/test-iis-7.5.log-expected.json index cbd93966c51..10db8a88065 100644 --- a/filebeat/module/iis/access/test/test-iis-7.5.log-expected.json +++ b/filebeat/module/iis/access/test/test-iis-7.5.log-expected.json @@ -21,7 +21,7 @@ "url.path": "/", "url.query": "-", "user.name": "-", - "user_agent.device": "Other", + "user_agent.device.name": "Other", "user_agent.major": "7", "user_agent.minor": "0", "user_agent.name": "IE", diff --git a/filebeat/module/iis/access/test/test-ipv6zone.log-expected.json b/filebeat/module/iis/access/test/test-ipv6zone.log-expected.json index dc2796b0f54..6bdc376f1d8 100644 --- a/filebeat/module/iis/access/test/test-ipv6zone.log-expected.json +++ b/filebeat/module/iis/access/test/test-ipv6zone.log-expected.json @@ -29,7 +29,7 @@ "url.path": "/", "url.query": "-", "user.name": "-", - "user_agent.device": "Other", + "user_agent.device.name": "Other", "user_agent.major": "70", "user_agent.minor": "0", "user_agent.name": "Chrome", diff --git a/filebeat/module/iis/access/test/test.log-expected.json b/filebeat/module/iis/access/test/test.log-expected.json index 6dc51db9aa9..8b57fb9187d 100644 --- a/filebeat/module/iis/access/test/test.log-expected.json +++ b/filebeat/module/iis/access/test/test.log-expected.json @@ -29,7 +29,7 @@ "url.path": "/", "url.query": "q=100", "user.name": "-", - "user_agent.device": "Other", + "user_agent.device.name": "Other", "user_agent.major": "57", "user_agent.minor": "0", "user_agent.name": "Firefox", @@ -63,7 +63,7 @@ "url.path": "/", "url.query": "-", "user.name": "-", - "user_agent.device": "Other", + "user_agent.device.name": "Other", "user_agent.major": "57", "user_agent.minor": "0", "user_agent.name": "Firefox", @@ -108,7 +108,7 @@ "url.path": "/", "url.query": "-", "user.name": "-", - "user_agent.device": "Other", + "user_agent.device.name": "Other", "user_agent.major": "70", "user_agent.minor": "0", "user_agent.name": "Chrome", diff --git a/filebeat/module/iis/fields.go b/filebeat/module/iis/fields.go index aa03d312378..7c25f087543 100644 --- a/filebeat/module/iis/fields.go +++ b/filebeat/module/iis/fields.go @@ -32,5 +32,5 @@ func init() { // AssetIis returns asset data. // This is the base64 encoded gzipped contents of module/iis. func AssetIis() string { - return "eJzsmM9u4zYQxu95isGeuzq0Nx96KVA0hwIFNkCPAiON5WlkjjJDxdDbF6SsbOKlZOpPYCwQnQzJ/L4fydFoOF/hCbsdEOkdgCNX4w6+3N9/+3IHUKIWQo0jtjv4/Q4A4G8u2xphzwKNESVbwf39N6i5gj3VqNkdwJ6wLnUX/v8VrDnioO8v1zW4g0q4bc53Ijb++jPIwF74+KOHvwaf1xGDmSkKVH29HfOc8PXXH2ydIatnjzBfz9Are5RXirckbyUGGG0fc3XGtfru8QBVs60uHkxw+evhgPDXw8M/XrkXhoJLzKLuJ7K//bq9/79kSz4pXPVXcpj7n1HzJ+xOLOV8fy8bHMDYEsiqM7ZAsO3xEWWEBOUF5QNYAgbvwXmuYAJs4XSg4hDuDXELaJ10cDIKFVoU47CMoxbMT7QxZcHWoXUDaG8BGm4JCBZIL1j+ArQHY7vvXFHARy67fBiTPXYO48FlajKXTxrjDjs4ONdkgs8tqsu8XFTlSJWYflZOWhxn8fNYw6ENW8V1IOcAo2YmQonqyAb1zJSlvE9dqe5HdAe+jItZuxBVSLFupZ7p20qd+V8LzJ5blC5XJ/RD3kpxDcMX2DYsbsW+RoYnraxOZayJmSpKFhmX4il4ZIfzw1i5lQJXRLDgHkVQ1sTwiEaafZ8Ecv8xW5VF+o9iTCeFw2vlLyhKbJdgxIcmObO6BcHmh2UjY5OD3FRo4y/YZdUGI/XWW8kSX6i4ZJmeC7x7eXqebFRnal5vQY7mP74MxgUcYzLJGGQ3wRiRScVojCsuc/4CjDGZVIxInC6gGFFJheCYyUwE1mzf1nXsCzEPJd8oTllXh6qH2SZaPczKgGUdW9v5LGs3Sagiay4rrSUs40op6bpCHqkNlmRqfywhi9atWehz6VEhZ1f1Ute74NYf13JSjn3JF6FdVUyFq7kIf1sPNaGUCiNYEduN9m9aLHnzyHVbBdSE1MwV2i6UrguOoQ1IKPIuM27WIQvCsxpkgkbZ5s1BjG7c9Ahtsl4fev14w+W5xfYj2lShZdg0NfUvGTTMdbC80l+54RksGC847Z6tFx50b9s7Obvf5ox/u/PeT9cyGqlcfqbT/Wf1tBjts3q6yvNZPX1M9fR/AAAA//+NLFvk" + return "eJzsmM9u4zYQxu95isGeuzq0Nx96KVA0hwIFNkCPAiON5WlkjjJDxdDbF6SsbOKlZOpPYCwQnQzJ/L4fydFoOF/hCbsdEOkdgCNX4w6+3N9/+3IHUKIWQo0jtjv4/Q4A4G8u2xphzwKNESVbwf39N6i5gj3VqNkdwJ6wLnUX/v8VrDnioO8v1zW4g0q4bc53Ijb++jPIwF74+KOHvwaf1xGDmSkKVH29HfOc8PXXH2ydIatnjzBfz9Are5RXirckbyUGGG0fc3XGtfru8QBVs60uHkxw+evhgPDXw8M/XrkXhoJLzKLuJ7K//bq9/79kSz4pXPVXcpj7n1HzJ+xOLOV8fy8bHMDYEsiqM7ZAsO3xEWWEBOUF5QNYAgbvwXmuYAJs4XSg4hDuDXELaJ10cDIKFVoU47CMoxbMT7QxZcHWoXUDaG8BGm4JCBZIL1j+ArQHY7vvXFHARy67fBiTPXYO48FlajKXTxrjDjs4ONdkgs8tqsu8XFTlSJWYflZOWhxn8fNYw6ENW8V1IOcAo2YmQonqyAb1zJSlvE9dqe5HdAe+jItZuxBVSLFupZ7p20qd+V8LzJ5blC5XJ/RD3kpxDcMX2DYsbsW+RoYnraxOZayJmSpKFhmX4il4ZIfzw1i5lQJXRLDgHkVQ1sTwiEaafZ8Ecv8xW5VF+o9iTCeFw2vlLyhKbJdgxIcmObO6BcHmh2UjY5OD3FRo4y/YZdUGI/XWW8kSX6i4ZJmeC7x7eXqerNeJvUnXJveW5mj+48uIXAAzJpOMQXYTjBGZVIzGuOIy8S/AGJNJxRjZ05kUKyODYyYzEVizfVvXsc/EPJR8ozhlXR2qHmabaPUwKwOWdWxt57Os3SShiqy5LLeWsIwrpeTsCnmkQFiSrv3ZhCxat2ahz/VHhZxd1Utd74Jbf2bLSTn2OV+EdlUxFa7mIvxtPdSEUiqMYEVsN9q/abHkzSPXbRVQE1IzV2i7ULouOIY2IKHIu8y4WZssCM/qkgkaZZs3BzG6cecj9Mp6fej1412X5xbbj+hVhb5h09TUv2TQMNfB8kqT5YYHsWC84Mh7tl542r1tA+XsfpuD/u0OfT9d32ikcvmZjvif1dNitM/q6SrPZ/X0MdXT/wEAAP//la5dsw==" } diff --git a/filebeat/module/logstash/fields.go b/filebeat/module/logstash/fields.go index 9dd0d29c8e1..5c6b4ebd64c 100644 --- a/filebeat/module/logstash/fields.go +++ b/filebeat/module/logstash/fields.go @@ -32,5 +32,5 @@ func init() { // AssetLogstash returns asset data. // This is the base64 encoded gzipped contents of module/logstash. func AssetLogstash() string { - return "eJzslc1u2zAMgO95CqLnJg/gQy/FChTYD7DtHjA2LWuRSUM/Tf32g+U4cRw5gbdip+kWKeL3mSKlNeypzcCIch5dtQLw2hvK4GGYelgBFORyqxuvhTN4WgHAaQd8kSIYWgGUmkzhsri6BsaaLuJ2w7cNZaCshOY4k4h8GWkS7TR3Fv08Ej2tXYFmYf14iUgordTgK4IhaPyCzeivU7exXz2kYjx6lT21B7HFZO2GUDd+VnSMCWIhN+gcHCqyFBXpjdiDWK00o6dNUslXlnCK7ZU8vftlPq9ciq2xWwbcSfDRwwZmzeqIGgkaUff0jKht/I6koex+Ub7QcU8tIBfwhiYQFLQLSnV2+ux+VkmfIjmHKn2MaDS6yUqDvprbVWtlsVf1NlA6B/RGZiHNiNqk9s3xBpYzcph00dJOuQ5xsyVuJHN5AT4Le9TsYnkFXjdoHRWx0FKg/335p30535PL7b7jAYpQNyBlxB/RJgEZ8I0JSvO2+/Fxp/YVaxocesAtdgf6wIppmwk7g1dugneP8KKNJ+se4Vvw3UxXU89SUO5mSkdkv9W8rbUxeno99I5GWC0T/PROeYgF5HVNUIoduYJm6GmUCxczXsfENWixTmstr50f3nYl3F/mF/mDXLjUKvTX3X2hbfIx+bt3Zv10aXahBKG7mnbtyPnOw/MvHgJIFBIjS/rA5rmxdzdFKv3X8N8BAAD//3IWvbQ=" + return "eJzslctu2zwQhfd+ioOsEz+AFtn8+AME6AVouxdoaUSxJjkCL3H09oUky5ZkKoWTNECBcmeOOec7oxnyDntqM2iWPghfb4CggqYMN+PWzQYoyRdONUGxzXC/AXA6gc9cRk0boFKkS5/10TtYYWiWt1uhbSiDdByb404i8zzTIttp7wz6aQJ6il0IrYoN66GXROXYINSEMWnvYDv565JtymfGUkzXgLKn9sCuXMReAOrWj5qOOcEOhRbe41CTox6RnsgGsFNSWRFom0QKtSOxlH0D0qOt2BnRhSF2HEOP4qK1ysqj2oRRs1wjBEzUQeWpgs4c0HO4CI4eLoKTVsn7AiWt8+4nFcvQb5zvqYWwJZ6EjoSSdlHKzrM6V+RsMN0e5L2Q6f4QWgm/iDQi1GunjJJODKjBRUrXgJ5IX6mmWW5T59b0Ri2v+bAYz2tH8DLFv1n7K2Ztfc5eZfubOKCMpgFXva+jJ53U+ZO+Gh2lsnn34/3cfRGGRmeDwEvandA79njbLLQzPNomBn+LB6UDOX+LrzF0O90U/MclFX6l2Zn3ubK5UVqr5V0yMGq28jrA/5+piH2/B2UIFbsJK5TFoEYF23KF61i4Rjhh0livKt334LqhGy7/WQlRsK2UjMP1+PHtOTjNk0/a2167u/u535lRRE8ldu2kEukP8hGvEBKNaYXldAOs6/Y3zLZc+ZYL8V8BAAD//6XJ7J4=" } diff --git a/filebeat/module/logstash/log/_meta/fields.yml b/filebeat/module/logstash/log/_meta/fields.yml index dd46dcbef94..5d8fc352bbc 100644 --- a/filebeat/module/logstash/log/_meta/fields.yml +++ b/filebeat/module/logstash/log/_meta/fields.yml @@ -9,9 +9,12 @@ description: > The module or class where the event originate. - name: thread - type: text + type: keyword description: > Information about the running thread where the log originate. + multi_fields: + - name: text + type: text - name: log_event type: object description: > diff --git a/filebeat/module/logstash/slowlog/_meta/fields.yml b/filebeat/module/logstash/slowlog/_meta/fields.yml index f10626d7c12..924a0a457fb 100644 --- a/filebeat/module/logstash/slowlog/_meta/fields.yml +++ b/filebeat/module/logstash/slowlog/_meta/fields.yml @@ -3,22 +3,24 @@ description: > slowlog fields: - - name: message - type: text - description: > - Contains the un-parsed log message - name: module type: keyword description: > The module or class where the event originate. - name: thread - type: text + type: keyword description: > Information about the running thread where the log originate. + multi_fields: + - name: text + type: text - name: event - type: text + type: keyword description: > Raw dump of the original event + multi_fields: + - name: text + type: text - name: plugin_name type: keyword description: > @@ -32,14 +34,16 @@ description: > Execution time for the plugin in milliseconds. - name: plugin_params - type: text + type: keyword description: > String value of the plugin configuration + multi_fields: + - name: text + type: text - name: plugin_params_object type: object description: > key -> value of the configuration used by the plugin. - - name: level type: alias path: log.level diff --git a/filebeat/module/mysql/error/ingest/pipeline.json b/filebeat/module/mysql/error/ingest/pipeline.json index 0c77ca01cbf..06dac9ce0a8 100644 --- a/filebeat/module/mysql/error/ingest/pipeline.json +++ b/filebeat/module/mysql/error/ingest/pipeline.json @@ -24,7 +24,7 @@ "target_field": "@timestamp", "formats": [ "ISO8601", - "YYMMdd H:m:s" + "yyMMdd H:m:s" ], "ignore_failure": true } diff --git a/filebeat/module/nginx/access/_meta/fields.yml b/filebeat/module/nginx/access/_meta/fields.yml index 7e1e7871563..de79b16b923 100644 --- a/filebeat/module/nginx/access/_meta/fields.yml +++ b/filebeat/module/nginx/access/_meta/fields.yml @@ -52,7 +52,7 @@ fields: - name: device type: alias - path: user_agent.device + path: user_agent.device.name migration: true - name: major type: alias diff --git a/filebeat/module/nginx/access/ingest/default.json b/filebeat/module/nginx/access/ingest/default.json index cb2752df216..cd23985d41b 100644 --- a/filebeat/module/nginx/access/ingest/default.json +++ b/filebeat/module/nginx/access/ingest/default.json @@ -49,6 +49,13 @@ } } }, + { + "convert": { + "field": "source.ip", + "target_field": "source.address", + "type": "string" + } + }, { "remove": { "field": "message" @@ -65,7 +72,7 @@ "field": "nginx.access.time", "target_field": "@timestamp", "formats": [ - "dd/MMM/YYYY:H:m:s Z" + "dd/MMM/yyyy:H:m:s Z" ], {< if .convert_timezone >}"timezone": "{{ event.timezone }}",{< end >} "ignore_failure": true @@ -87,6 +94,13 @@ "target_field": "user_agent.original" } }, + { + "rename": { + "field": "user_agent.device", + "target_field": "user_agent.device.name", + "ignore_missing": true + } + }, { "rename": { "field": "user_agent.os", diff --git a/filebeat/module/nginx/access/test/test.log-expected.json b/filebeat/module/nginx/access/test/test.log-expected.json index e5cfc798da2..42d8e5fe9f6 100644 --- a/filebeat/module/nginx/access/test/test.log-expected.json +++ b/filebeat/module/nginx/access/test/test.log-expected.json @@ -18,10 +18,11 @@ "127.0.0.1" ], "service.type": "nginx", + "source.address": "10.0.0.2", "source.ip": "10.0.0.2", "url.original": "/ocelot", "user.name": "-", - "user_agent.device": "Other", + "user_agent.device.name": "Other", "user_agent.major": "49", "user_agent.minor": "0", "user_agent.name": "Firefox", @@ -48,10 +49,11 @@ "172.17.0.1" ], "service.type": "nginx", + "source.address": "172.17.0.1", "source.ip": "172.17.0.1", "url.original": "/stringpatch", "user.name": "-", - "user_agent.device": "Other", + "user_agent.device.name": "Other", "user_agent.major": "15", "user_agent.minor": "0", "user_agent.name": "Firefox Alpha", @@ -79,6 +81,7 @@ "85.181.35.98" ], "service.type": "nginx", + "source.address": "85.181.35.98", "source.geo.city_name": "Berlin", "source.geo.continent_name": "Europe", "source.geo.country_iso_code": "DE", @@ -89,7 +92,7 @@ "source.ip": "85.181.35.98", "url.original": "/ocelot", "user.name": "-", - "user_agent.device": "Other", + "user_agent.device.name": "Other", "user_agent.major": "49", "user_agent.minor": "0", "user_agent.name": "Firefox", @@ -116,6 +119,7 @@ "85.181.35.98" ], "service.type": "nginx", + "source.address": "85.181.35.98", "source.geo.city_name": "Berlin", "source.geo.continent_name": "Europe", "source.geo.country_iso_code": "DE", @@ -126,7 +130,7 @@ "source.ip": "85.181.35.98", "url.original": "/ocelot", "user.name": "-", - "user_agent.device": "Other", + "user_agent.device.name": "Other", "user_agent.major": "70", "user_agent.minor": "0", "user_agent.name": "Chrome", @@ -157,6 +161,7 @@ "10.2.1.185" ], "service.type": "nginx", + "source.address": "199.96.1.1", "source.geo.city_name": "Springfield", "source.geo.continent_name": "North America", "source.geo.country_iso_code": "US", @@ -167,7 +172,7 @@ "source.ip": "199.96.1.1", "url.original": "/assets/xxxx?q=100", "user.name": "-", - "user_agent.device": "Other", + "user_agent.device.name": "Other", "user_agent.name": "Other", "user_agent.original": "Amazon CloudFront", "user_agent.os.full_name": "Other", @@ -192,6 +197,7 @@ "10.2.2.121" ], "service.type": "nginx", + "source.address": "2a03:0000:10ff:f00f:0000:0000:0:8000", "source.geo.continent_name": "Europe", "source.geo.country_iso_code": "PT", "source.geo.location.lat": 39.5, @@ -199,7 +205,7 @@ "source.ip": "2a03:0000:10ff:f00f:0000:0000:0:8000", "url.original": "/test.html", "user.name": "-", - "user_agent.device": "Spider", + "user_agent.device.name": "Spider", "user_agent.major": "1", "user_agent.minor": "0", "user_agent.name": "Facebot", @@ -222,9 +228,10 @@ "127.0.0.1" ], "service.type": "nginx", + "source.address": "127.0.0.1", "source.ip": "127.0.0.1", "user.name": "-", - "user_agent.device": "Other", + "user_agent.device.name": "Other", "user_agent.name": "Other", "user_agent.original": "-", "user_agent.os.full_name": "Other", diff --git a/filebeat/module/nginx/error/ingest/pipeline.json b/filebeat/module/nginx/error/ingest/pipeline.json index c37a9a45d3d..930587ff883 100644 --- a/filebeat/module/nginx/error/ingest/pipeline.json +++ b/filebeat/module/nginx/error/ingest/pipeline.json @@ -17,7 +17,7 @@ "date": { "field": "nginx.error.time", "target_field": "@timestamp", - "formats": ["YYYY/MM/dd H:m:s"], + "formats": ["yyyy/MM/dd H:m:s"], {< if .convert_timezone >}"timezone": "{{ event.timezone }}",{< end >} "ignore_failure": true } diff --git a/filebeat/module/nginx/fields.go b/filebeat/module/nginx/fields.go index 0cb7f07f5ab..c6cd2567c65 100644 --- a/filebeat/module/nginx/fields.go +++ b/filebeat/module/nginx/fields.go @@ -32,5 +32,5 @@ func init() { // AssetNginx returns asset data. // This is the base64 encoded gzipped contents of module/nginx. func AssetNginx() string { - return "eJysl89u4zYQxu9+isGeN3oAHwoUCyywhxZFT70pDDmSp0tx1Bkqrd++oOzNOg4pU7J5ciTN9/3mTyjxCb7jcQ+hp/DfDiBS9LiHT7+nvz/tAByqFRojcdjDLzsAgN/YTR6hY4HRiFLoIR4Q5hDw3ENHHrXZAeiBJbaWQ0f9HqJMuAPoCL3T/Sz1BMEM+NM+rXgccQ+98DSer2QY0vo6C0EnPJQA0rr0u/Q01qLq2+Wc8YJ5Wl84RENBzxZzRX6CnPQTzxtKDucSSXDgiC2NrSeN7x75gWdEzPHqzgJiWr+GUxRwd3aAb3+AcU5QFbWBbxFIwUAyhRe0ZlIEmi9aHgYOEBkoWD85/AwvqORQ50ytJwzXoHAh//md1alXBzQORcHTd4Tnv56+svxrxKFLv56bD2p/ovGgPImdwUlBUCMLusT1fLrT0HgRmq3uC7tjqxhi83KMqPnyejLXd0YTD3s4xDg2gjpyUGySVlZmoF7MqRPneV9o80qEt0w3eE6K0qafKz1TXJOJq/EcMB7YbavzPxNqbLIKVemKX5uo+IaFegrmOrTGMGG3ryhKHLZknA+tm6bTTLaW3druvh9qjSZOmtOp4+hQBOWefhc0auxN/3Enqhnudg5c2/ry/1iZ4/rNAoW3waWkw1ey191YTi2bXlFnqbSXIIP5m6/bsoGjJFONQeEhGAWZWozRRHu4H6MkU4uR2Zc3UBRUaiE4Z7ISgbXpJu9zL6h1KO2D5pT17lFNMI+Z1gRz58Cylmq7nuXeJuV32i0sZaVV+3aPXPgW27JlWw6RAoZ4T8XPn3o9cnNTr7bwlqcQ5diScu4dvwntpmItnGc7P3Y/1IJSLYxgTxwe1L9lsermUTw+aqAWpFZW6HGjdFuwhPYDCUXebZEPPs7P8qtO85ZDQBvntPKnIM+hX3eW//KmCeQwROoI5ca51+Mrrj0Nee6bXFzNl/hYyLbsNgpbVG0+Rtb4xc1+8SBoXLPJdUBV0689cOWjrv3+DwAA///Lb/Q7" + return "eJysl8+O2zYQxu9+ikHOWT2ADwWKAAFyaFH01JuWS47kaSiOOkNt67cvKDsbr5aSKck82ZLn+37zx5T4BN/xfITQUvjvABApejzCp9/T908HAIdqhfpIHI7wywEA4Dd2g0doWKA3ohRaiCeEMQQ8t9CQR60OAHpiibXl0FB7hCgDHgAaQu/0OEo9QTAd/rRPK557PEIrPPTXKxmGtL6OQtAId3MAad363Xoaa1H17XLOeME8rS8coqGgV4uxIj9BLvqJ5w0lh3OLJNhxxJr62pPGdz/5gWdEzHlyZwExrV/DJQq4uTrAtz/AOCeoilrBtwikYCCZwgtaMygCjRctdx0HiAwUrB8cfoYXVHKoY6bWE4YpKNzIf35ndenVCY1DUfD0HeH5r6evLP8acejSp+fqg9qfaDwoD2JHcFIQ1MiCLnE9X+5U1N+EZqv7wu5cK4ZYvZwjar68nsz0Tm/i6QinGPtKUHsOilXSysp01Iq5dOI67wttXonwlukGz0FR6vRxpWeKqzJxJZ4dxhO7bXX+Z0CNVVahKF3xaxMVX7FQS8FMQ0sME3b9iqLEYUvG+dCyabrMZG3Zre3u+6HWaOKgOZ0yjgZFUPb0e0ajxN60H3eikuGux8C1rZ//j81zTJ8sMPM0uJV0+Ep22o3l1LLpXXRyf+SlLKc0nfmbp73ZADMnU4xB4SEYMzKlGL2J9rQfY06mFGOmpyspdk4G50xWIrBWzeB97im1DqV+0Jyy7h7VBPOYaU0wOweWda6261n2Nim/3W5hmVdatXm3yDMvZFv2bcshUsAQ91T8+r7XIld39UoLb3kIUc41Kece9JvQ7iqWwnm248/2Qy0olcIItsThQf1bFituHsXzowZqQWplhR43SvcF59B+IKHIuy3ywWf6UX7Vkd5yCGjjmFb+KOQ5tOsO9F/eNIEchkgNodw5/Hp8xbVHIs9tlYsreR3vZ7Kdd+uFLapWHyNL/OJmv3gSNK7a5NqhqmnXnrryUVO//wMAAP//dT72Cg==" } diff --git a/filebeat/module/redis/log/ingest/pipeline.json b/filebeat/module/redis/log/ingest/pipeline.json index c62c9c749cd..c9ec2d3371b 100644 --- a/filebeat/module/redis/log/ingest/pipeline.json +++ b/filebeat/module/redis/log/ingest/pipeline.json @@ -59,7 +59,7 @@ "field": "redis.log.timestamp", "target_field": "@timestamp", "formats": [ - "dd MMM YYYY H:m:s.SSS", + "dd MMM yyyy H:m:s.SSS", "dd MMM H:m:s.SSS", "dd MMM H:m:s", "UNIX" diff --git a/filebeat/module/traefik/access/_meta/fields.yml b/filebeat/module/traefik/access/_meta/fields.yml index f9cc5a263ba..319419901c3 100644 --- a/filebeat/module/traefik/access/_meta/fields.yml +++ b/filebeat/module/traefik/access/_meta/fields.yml @@ -12,11 +12,11 @@ description: > The number of requests - name: frontend_name - type: text + type: keyword description: > The name of the frontend used - name: backend_url - type: text + type: keyword description: The url of the backend where request is forwarded @@ -62,7 +62,7 @@ fields: - name: device type: alias - path: user_agent.device + path: user_agent.device.name - name: major type: alias path: user_agent.major diff --git a/filebeat/module/traefik/access/config/traefik-access.yml b/filebeat/module/traefik/access/config/traefik-access.yml index 8b640efa575..0afd17317d4 100644 --- a/filebeat/module/traefik/access/config/traefik-access.yml +++ b/filebeat/module/traefik/access/config/traefik-access.yml @@ -4,12 +4,3 @@ paths: - {{$path}} {{ end }} exclude_files: [".gz$"] - -processors: -- dissect: - tokenizer: '%{source.address} %{traefik.access.user_identifier} %{user.name} [%{traefik.access.time}] - "%{http.request.method} %{url.original} HTTP/%{http.version}" - %{http.response.status_code} %{traefik.access.message}' - - field: "message" - target_prefix: "" diff --git a/filebeat/module/traefik/access/ingest/pipeline.json b/filebeat/module/traefik/access/ingest/pipeline.json index 4f75c507633..09d50d38443 100644 --- a/filebeat/module/traefik/access/ingest/pipeline.json +++ b/filebeat/module/traefik/access/ingest/pipeline.json @@ -1,6 +1,12 @@ { "description": "Pipeline for parsing Traefik access logs. Requires the geoip and user_agent plugins.", "processors": [ + { + "dissect": { + "field": "message", + "pattern": "%{source.address} %{traefik.access.user_identifier} %{user.name} [%{traefik.access.time}] \"%{http.request.method} %{url.original} HTTP/%{http.version}\" %{http.response.status_code} %{traefik.access.message}" + } + }, { "grok": { "field": "traefik.access.message", @@ -33,7 +39,7 @@ "field": "traefik.access.time", "target_field": "@timestamp", "formats": [ - "dd/MMM/YYYY:H:m:s Z" + "dd/MMM/yyyy:H:m:s Z" ] } }, @@ -87,6 +93,13 @@ "ignore_failure": true } }, + { + "rename": { + "field": "user_agent.device", + "target_field": "user_agent.device.name", + "ignore_missing": true + } + }, { "rename": { "field": "user_agent.os", diff --git a/filebeat/module/traefik/access/test/test.log-expected.json b/filebeat/module/traefik/access/test/test.log-expected.json index eecee7f1d16..0c18065cb16 100644 --- a/filebeat/module/traefik/access/test/test.log-expected.json +++ b/filebeat/module/traefik/access/test/test.log-expected.json @@ -22,7 +22,7 @@ "traefik.access.user_identifier": "-", "url.original": "/ui/favicons/favicon-16x16.png", "user.name": "-", - "user_agent.device": "Other", + "user_agent.device.name": "Other", "user_agent.name": "Chrome", "user_agent.original": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36", "user_agent.os.full_name": "Linux", @@ -60,7 +60,7 @@ "traefik.access.user_identifier": "-", "url.original": "/ui/favicons/favicon.ico", "user.name": "-", - "user_agent.device": "Other", + "user_agent.device.name": "Other", "user_agent.name": "Chrome", "user_agent.original": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36", "user_agent.os.full_name": "Linux", @@ -97,7 +97,7 @@ "traefik.access.user_identifier": "-", "url.original": "/en/", "user.name": "-", - "user_agent.device": "iPhone", + "user_agent.device.name": "iPhone", "user_agent.name": "Mobile Safari", "user_agent.original": "Mozilla/5.0 (iPhone; CPU iPhone OS 11_2_5 like Mac OS X) AppleWebKit/604.5.6 (KHTML, like Gecko) Version/11.0 Mobile/15D60 Safari/604.1", "user_agent.os.full_name": "iOS 11.2.5", @@ -128,7 +128,7 @@ "traefik.access.user_identifier": "-", "url.original": "/", "user.name": "-", - "user_agent.device": "Other", + "user_agent.device.name": "Other", "user_agent.name": "curl", "user_agent.original": "curl/7.62.0", "user_agent.os.full_name": "Other", @@ -165,7 +165,7 @@ "traefik.access.user_identifier": "-", "url.original": "/assets/52f8f2e711d235d76044799e/owners?oauth_token=ya29.GltABOXd_gtG-XVvYX2YhxXJiXVvbHRMXn9fbzc_mDfl2rDhqK0CrAlwuwwRWnNnEaMDwkmyI7-QGbRSB0Hzje2cc__FjTQ1iuiYTSIBaIPfxSWip5jx6zqvsVVo", "user.name": "-", - "user_agent.device": "Generic Smartphone", + "user_agent.device.name": "Generic Smartphone", "user_agent.name": "Other", "user_agent.original": "Android", "user_agent.os.full_name": "Android", @@ -202,7 +202,7 @@ "traefik.access.user_identifier": "-", "url.original": "/marketplace/tax?oauth_token=ya29.Gl0fBWnrJ7DcEU-tN-O3Vxn2XZVaz2I-hFTjP1JQzhYFVT-SKtlmo9hSzrx3n82LUwUxJ1s5lmU8U3Mc9gA_aCxBk49ShYEwvmYOWxJJyldDIJ7hY4us4LoiSY1OqAM", "user.name": "-", - "user_agent.device": "Generic Smartphone", + "user_agent.device.name": "Generic Smartphone", "user_agent.name": "Other", "user_agent.original": "Android", "user_agent.os.full_name": "Android", diff --git a/filebeat/module/traefik/fields.go b/filebeat/module/traefik/fields.go index 60c1cd39b12..8fb1bba2b34 100644 --- a/filebeat/module/traefik/fields.go +++ b/filebeat/module/traefik/fields.go @@ -32,5 +32,5 @@ func init() { // AssetTraefik returns asset data. // This is the base64 encoded gzipped contents of module/traefik. func AssetTraefik() string { - return "eJysls9uozAQxu95ilHvRap2TznspVKlPexl1TtyYSDeGA87Htrl7VcmIaVoTAmNb4H4+33zx2Pu4Yj9HoQNVva4AxArDvdw93x6crcDKDEUbFux5PfwYwcA8IvKziFUxNAaDtbXIAeE8yZwVENlHYZsB1BZdGXYD/vuwZsGp7y4pG9xDzVT156fKMi4ngYpqJiaNC+uKXPKNUWBIVwea+gFfFyP5MVYH86IIQVTKydCdHQxoxmamuoCcm5L9GIri/zhP6PDI/ZvxOXs3YLPuH6Gwdrvp0d4+P7wDU4M6YGq4UXhLHpRPTH+7TBIXlA3+8foyJGvr7PzfEDwXfOCHA2cCUHFV0xe0Jd5/KniBf/Nfa3BmwbH6EdGzH+pungxxTGa6Nht8aA66NiNBs7y8HZAxjEfYIeeejNcTlzp9qjs84BespdeMKgWjbNm/qY1ctjDQaTNGENLPmAWtVSZxtZsTikV7jDRLA0J5ra90kKgjgvMTFnyx1O5FjycnGSLpMFxX6bsW8NsUA40P4grkz1UOFMVVoWb6MOFQNllxLa23sy3rgFG2/krcrDkt0Ssb13XUqfGzAsqr63ux84OYqQLms46HxUyJ4byynonNNbgTT0f0OuaOx82Xlv69BlL+5hfnZC47KaSJb7aYl6N5dDU8BI6l5Nq/tA86RsouswFYv1NIKrMCGmNFIevQ3SZEaLMww0MVWVEkCZxJYBCVnXOaWN/CspvVH0KnzRARN2mByJqsQ0o6FFvIS2nT58bW0hzJXXG1EiJj4ct46UgL9ajl69k6/xtUiNln+i9Yzsv3Oc2kHbbbAJ/ojiiHRXDPP86Mqn0fiHWlvyNMrskdkmrlf5WhUxKzaK7XQlTgrv/AQAA//+snOpi" + return "eJyslk9vozwQxu/5FKPei1S97ymHvVSqtIe9rHpHLgzgjfGw43Ervv3KJKQU2ZQQfAvEz++ZPx7zCCfsjyCssNKnA4BoMXiEh9fzk4cDQImuYN2JJnuEHwcAgF9UeoNQEUOn2GlbgzQIl01gqIZKG3TZAaDSaEp3HPY9glUtTnlhSd/hEWom312eRJBhvQxSUDG1aV5YU+aUq4oCnbs+jqEX8GE9kxWlrbsghhRMrZwJwdHVTMzQ1JR3yLku0YquNPKX/4wOT9h/EJezdws+w/rpBmu/X57h6f+n/+DMkB6oGl4URqOVqCfGvx6d5AX52T9GR4ZsfZud1wbB+vYNORi4EFwUXzFZQVvm4ed+CRkcqBbHBIyYUIIyauRNFafgw7PZaCNqwrMZPVwI8NEg45gV0ENnfSguJ8biDqnsc4dWsrde0EVdKqPV/E2npDlCI9JljK4j6zALWlGZVteszlkV9phomZYEc93daMGR5wIzVZb89WyuBQ/nJ9koaXDYl0X2rWG2KA3Ny74y2UOFs6jCqnATrbgQKJuMWNfaqvnWNcBgO39HdprslojjW9e11Lkx84LKW6v7tbOdKPEuprPOR4XMidG8st4JjTV4Vc/H9LrmzoeNt5Y+fcbSPuYXKCSuvKlkie+6mFdjObRoeGed2EGeHFf1h+aZ34CKy1wh2u4CicqMkE5J0dwPicuMkGgub2YsVoRiEjcCyGWVNyY2+6egfKfqk/umAQJqnx4IqMU2IBePegtpOX3x4bGFNFeKDpoaKfEFsWXGFGRFW7RyT7YuHyg1UvaN3ifWW+E+145iV84m8DeKI9pQMQz1+5FJpc9bsdZkd8rsktg1rVr6vQqZlJpFt18JU4KHfwEAAP//fX3usQ==" } diff --git a/filebeat/tests/system/config/filebeat.yml.j2 b/filebeat/tests/system/config/filebeat.yml.j2 index 52e099d8511..22c95ef4b4c 100644 --- a/filebeat/tests/system/config/filebeat.yml.j2 +++ b/filebeat/tests/system/config/filebeat.yml.j2 @@ -105,6 +105,19 @@ filebeat.config.{{ reload_type|default("inputs") }}: {% endif -%} {% endif -%} +{% if ilm %} +setup.ilm: + enabled: {{ ilm.enabled | default("auto") }} + policy_name: libbeat-test-default-policy + {% if ilm.pattern %} + pattern: {{ ilm.pattern }} + {% endif %} + {% if ilm.rollover_alias %} + rollover_alias: {{ ilm.rollover_alias }} + {% endif %} +{% endif %} + + #============================== Autodiscover ================================== {% if autodiscover %} diff --git a/filebeat/tests/system/config/filebeat_inputs.yml.j2 b/filebeat/tests/system/config/filebeat_inputs.yml.j2 index 04ef3c2a6ad..db5628fb603 100644 --- a/filebeat/tests/system/config/filebeat_inputs.yml.j2 +++ b/filebeat/tests/system/config/filebeat_inputs.yml.j2 @@ -7,6 +7,19 @@ filebeat.inputs: {% endfor %} filebeat.registry_file: {{ beat.working_dir + '/' }}{{ registryFile|default("registry")}} +{% if ilm %} +setup.ilm: + enabled: {{ ilm.enabled | default("auto") }} + policy_name: libbeat-test-default-policy + {% if ilm.pattern %} + pattern: {{ ilm.pattern }} + {% endif %} + {% if ilm.rollover_alias %} + rollover_alias: {{ ilm.rollover_alias }} + {% endif %} +{% endif %} + + output.file: path: {{ output_file_path|default(beat.working_dir + "/output") }} filename: "{{ output_file_filename|default("filebeat") }}" diff --git a/filebeat/tests/system/config/filebeat_modules.yml.j2 b/filebeat/tests/system/config/filebeat_modules.yml.j2 index 62b66e8a5ff..df3a480277c 100644 --- a/filebeat/tests/system/config/filebeat_modules.yml.j2 +++ b/filebeat/tests/system/config/filebeat_modules.yml.j2 @@ -15,3 +15,16 @@ setup.kibana.host: {{ kibana_url }} {% if kibana_path %} setup.dashboards.directory: {{ kibana_path }} {% endif %} + +{% if ilm %} +setup.ilm: + enabled: {{ ilm.enabled | default("auto") }} + policy_name: libbeat-test-default-policy + {% if ilm.pattern %} + pattern: {{ ilm.pattern }} + {% endif %} + {% if ilm.rollover_alias %} + rollover_alias: {{ ilm.rollover_alias }} + {% endif %} +{% endif %} + diff --git a/filebeat/tests/system/test_ml.py b/filebeat/tests/system/test_ml.py index d6f69356662..aa84b92a0fa 100644 --- a/filebeat/tests/system/test_ml.py +++ b/filebeat/tests/system/test_ml.py @@ -31,21 +31,19 @@ def init(self): self.index_name = "test-filebeat-ml" @parameterized.expand([ - (True, False), - (True, True), - (False, False), - (False, True), + (False,), + (True,), ]) @unittest.skipIf(not INTEGRATION_TESTS, "integration tests are disabled, run with INTEGRATION_TESTS=1 to enable them.") @unittest.skipIf(os.getenv("TESTING_ENVIRONMENT") == "2x", "integration test not available on 2.x") @unittest.skipIf(os.name == "nt", "skipped on Windows") - def test_ml_setup(self, setup_flag, modules_flag): + def test_ml_setup(self, modules_flag): """ Test ML are installed in all possible ways """ - self._run_ml_test(setup_flag, modules_flag) + self._run_ml_test(modules_flag) - def _run_ml_test(self, setup_flag, modules_flag): + def _run_ml_test(self, modules_flag): self.init() from elasticsearch import AuthorizationException @@ -55,7 +53,7 @@ def _run_ml_test(self, setup_flag, modules_flag): except AuthorizationException: print("License already enabled") - print("Test setup_flag: {}, modules_flag: {}".format(setup_flag, modules_flag)) + print("Test modules_flag: {}".format(modules_flag)) # Clean any previous state for df in self.es.transport.perform_request("GET", "/_xpack/ml/datafeeds/")["datafeeds"]: @@ -93,14 +91,9 @@ def _run_ml_test(self, setup_flag, modules_flag): "-c", cfgfile ] - # Skipping dashboard loading to speed up tests, unfortunately only works for setup and not --setup + # Skipping dashboard loading to speed up tests cmd += ["-E", "setup.dashboards.enabled=false"] - - if setup_flag: - cmd += ["--setup"] - else: - cmd += ["setup", "--machine-learning"] - + cmd += ["setup", "--machine-learning"] if modules_flag: cmd += ["--modules=nginx"] diff --git a/filebeat/tests/system/test_modules.py b/filebeat/tests/system/test_modules.py index 0804d8ad28b..322694c4d29 100644 --- a/filebeat/tests/system/test_modules.py +++ b/filebeat/tests/system/test_modules.py @@ -27,6 +27,8 @@ def load_fileset_test_cases(): else: modules = os.listdir(modules_dir) + filesets_env = os.getenv("TESTING_FILEBEAT_FILESETS") + test_cases = [] for module in modules: @@ -35,7 +37,12 @@ def load_fileset_test_cases(): if not os.path.isdir(path): continue - for fileset in os.listdir(path): + if filesets_env: + filesets = filesets_env.split(",") + else: + filesets = os.listdir(path) + + for fileset in filesets: if not os.path.isdir(os.path.join(path, fileset)): continue @@ -89,7 +96,7 @@ def test_fileset_file(self, module, fileset, test_file): template_name="filebeat_modules", output=cfgfile, index_name=self.index_name, - elasticsearch_url=self.elasticsearch_url + elasticsearch_url=self.elasticsearch_url, ) self.run_on_file( @@ -111,6 +118,7 @@ def run_on_file(self, module, fileset, test_file, cfgfile): self.filebeat, "-systemTest", "-e", "-d", "*", "-once", "-c", cfgfile, + "-E", "setup.ilm.enabled=false", "-modules={}".format(module), "-M", "{module}.*.enabled=false".format(module=module), "-M", "{module}.{fileset}.enabled=true".format( diff --git a/filebeat/tests/system/test_pipeline.py b/filebeat/tests/system/test_pipeline.py index 8058306a95d..7b5b6c381bd 100644 --- a/filebeat/tests/system/test_pipeline.py +++ b/filebeat/tests/system/test_pipeline.py @@ -62,6 +62,7 @@ def test_input_pipeline_config(self): pipeline="test", setup_template_name=index_name, setup_template_pattern=index_name + "*", + ilm={"enabled": False}, ) os.mkdir(self.working_dir + "/log/") diff --git a/heartbeat/docs/fields.asciidoc b/heartbeat/docs/fields.asciidoc index 106fb60dc20..71476ed5a08 100644 --- a/heartbeat/docs/fields.asciidoc +++ b/heartbeat/docs/fields.asciidoc @@ -3405,16 +3405,6 @@ type: keyword Major version of the user agent. --- - -*`user_agent.device`*:: -+ --- -type: keyword - -Name of the physical device. - - -- *`user_agent.os.major`*:: @@ -3435,16 +3425,6 @@ type: long Minor version of the operating system. --- - -*`url.hostname`*:: -+ --- -type: keyword - -Hostname of the request, such as "elastic.co". - - -- [[exported-fields-host-processor]] diff --git a/heartbeat/heartbeat.reference.yml b/heartbeat/heartbeat.reference.yml index 741b2229c25..be2ee368934 100644 --- a/heartbeat/heartbeat.reference.yml +++ b/heartbeat/heartbeat.reference.yml @@ -502,11 +502,6 @@ output.elasticsearch: # IPv6 addresses should always be defined as: https://[2001:db8::1]:9200 hosts: ["localhost:9200"] - # Enabled ilm (beta) to use index lifecycle management instead daily indices. - #ilm.enabled: false - #ilm.rollover_alias: "heartbeat" - #ilm.pattern: "{now/d}-000001" - # Set gzip compression level. #compression_level: 0 @@ -1153,6 +1148,25 @@ setup.template.settings: #_source: #enabled: false +#============================== Setup ILM ===================================== + +# Configure Index Lifecycle Management Index Lifecycle Management creates a +# write alias and adds additional settings to the template. +# The elasticsearch.output.index setting will be replaced with the write alias +# if ILM is enabled. + +# Enabled ILM support. Valid values are true, false, and auto. The beat will +# detect availabilty of Index Lifecycle Management in Elasticsearch and enable +# or disable ILM support. +#setup.ilm.enabled: auto + +# Configure the ILM write alias name. +#setup.ilm.rollover_alias: "heartbeat" + +# Configure rollover index pattern. +#setup.ilm.pattern: "{now/d}-000001" + + #============================== Kibana ===================================== # Starting with Beats version 6.0.0, the dashboards are loaded via the Kibana API. diff --git a/heartbeat/heartbeat.yml b/heartbeat/heartbeat.yml index 9f607e4a840..7424bf421b3 100644 --- a/heartbeat/heartbeat.yml +++ b/heartbeat/heartbeat.yml @@ -58,7 +58,7 @@ setup.template.settings: #============================== Dashboards ===================================== # These settings control loading the sample dashboards to the Kibana index. Loading # the dashboards is disabled by default and can be enabled either by setting the -# options here, or by using the `-setup` CLI flag or the `setup` command. +# options here or by using the `setup` command. #setup.dashboards.enabled: false # The URL from where to download the dashboards archive. By default this URL @@ -106,9 +106,6 @@ output.elasticsearch: # Array of hosts to connect to. hosts: ["localhost:9200"] - # Enabled ilm (beta) to use index lifecycle management instead daily indices. - #ilm.enabled: false - # Optional protocol and basic auth credentials. #protocol: "https" #username: "elastic" diff --git a/heartbeat/include/fields.go b/heartbeat/include/fields.go index 1e330507586..8a64493dd9d 100644 --- a/heartbeat/include/fields.go +++ b/heartbeat/include/fields.go @@ -32,5 +32,5 @@ func init() { // AssetFieldsYml returns asset data. // This is the base64 encoded gzipped contents of fields.yml. func AssetFieldsYml() string { - return "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" + return "eJzsfX9zHDdy6P/+FCi66tlOlsMfomSZqXsJT5JtliWZEaU4l1yKi53B7sKcAcYAhqt1ct/9FboBDDAzu9wlubIuj7qqszQ7AzQajUb/7n1yzZanhOX6C0IMNyU7Ja9eXH5BSMF0rnhtuBSn5P9+QQixP5ApZ2Whsy+I+9vpF/DTPhG0Yqdk718Mr5g2tKr34AdCzLJmp6SghrkHJbth5SnJpfJPFPut4YoVp8Soxj9kH2lVW3j2jg+Pnu0fPt0/fvL+8Pnp4dPTJyfZ86dP/sPPMACq/fOSGnZgwSGLORPEzBlhN0wYIhWfcUENK7IvwtvfS0VKOcNXNDFzrgnX8FWxaqAF1WTGBFN2rBGhogjDCWnwbY6vKUbj2d65FSMWyVQqQsvSTZ6lODV0pleiDrF7zZYLqYoe5v7zr3u1kkWTW9z8dW9E/rrHxM3xX/f+6xbcvebaEDn1A2vSaFYQIy0whNF8jqB2IC3phJW3wSonv7LcdEH9byZuTkkL7IjQui55ThGyqZT7E6r+th7qn9jy4IaWDSM15UpH+H5BBZmwsApaFKRihhIuplJVMIl97vBPLueyKQvYxFwKQ7kggmnD2v3FVeiMnJUlgTk1oYoRbaTdVqo96iIgXvnFjguZXzM1thRDxtfP9dihroPPimlNZ6vPDSLUsI89dO79yMpSkl+kKotbtrpH+MzP64jTYQB/sm+6n6OVnQsizZwpi2CSU80Gx0n3IJcip4aJljEQUvDplCl7tBxKF3OezwGxxh6mqWKsXBLNqMrndFKyjJxPSdWUhtdlO4ybVxP2kWszst8u/fS5rCZcsIJwYSSRgnWW43FPZ0x4tDrGeBY9minZ1KfkeD1u388ZDuS4ZaAmx1YooRPZGPinllOzsCtlwnCzHBE+JVQsLfTUkmFZWoIbkYIZ/ItURE40Uzd2obh5UhBK5tKuWSpi6DXTpGJUN4pV6QuZp0ZNuMjLpmDkz4wCQc/gzYouCS21JKoR9jM3ldIZ3AOwquwf/Lr03LKvCSO1rJvSskOy4GZugaW81JaVmIAL1QjBxcyOah9acKLFKMs3ccMdm53TumZ2y+yagKzCioC32nWKzCF9KqUR0rB4G/xSTy2h2hEsiVqYYMnAfUs506MWxswSgeX/U16yCaMmg3NydvFmZDk6Xgxh/HRZbntpXR/YBfGcZREhxBynkEwjk5lTMWOET9uTYImDa6LtN2auZDObk98a1tgZ9FIbVmlS8mtGfqLTazoi71jBkShqJXOmdfRiGFU39jRp8lrOtKF6TnBN5BIQnyVsBSjcIzW+6+NTYgmCSxGeD3EpsuKaWnNu7J9/w6ET0olYTsTsnmWH2eG+yo/78Nn/3wVwby15rITMHnwUHyhA4I4wMqAZv2Fw2VDhPsW33c9zVtbTpoxpAcla+QUTs5Dke0eXhAttqMjd9dM5WtpObs9XMtakMZYLNBUVIJdYRko0q6lCsuSaCMYKe+CE48C96ZIBPbHmsrKTT5WsOvg4nxIhiT9UgAI8bf6RnBomSMmmhrCqNstsaKOnUva32O7eLrb4/bK+ZYv9kbaDE23oUhNaLux/Au7tBa9RmAhbP1lGvNDehlmKKhHYU8B6+/4CxnLTTFj7CvBqPrXEkQy3mlASIqloPueCDaPdDdHHPS92gfkPgv/WMMILexNOOVO4DfY4AQ6+5lO4uOF219909iVIWZZhI4OHbxd+F4Cd82Jwqc/pyfTp4WHRXyqr56xiipZXQ4tmHw0TBSvut/BXfo67rh3ZjhVcVUXLcukuFk1orqS2Wog2VFnhwfKAMZI1L8bhJlqHlOkXqYSUl7wnIr2In20mI525gSwXKNgUZDOKR4gLbjg1EpBAiWBmIdW1FaIEAy0B2SLKPorNqCrg1rO3nxR6FL2JV+OEF1zhA1qSaSkXRLHcKjh4v79/ceGGQ+7UQtYDxz6wr0fAAJfXTBT4+uVf3pKa5tfMfK2/wfFRSK6VNDKXZW8S1CXtvnWmU6AiM6tcePHCI8MoKjQFADJyKSsWpAMri9s3DVMV2fNKr1R79vJRbMpUMr3oLEej1OJ+dnIe7uGEBcEukl9hWmJBETO/g+3gMcyoOzpi8UNbrtToBpbfSpFcWJB+bQSiGIRKJyY6UwQZGKdFpJWu2tEsueCW7MPBTRVu+8eNdeAnUaxWzAphcDXiLW21R80qKgzPQaJnH4270NlHPHEjd29yHS50I8kNt+vjv7NW/rfrYwp0As1NQx3mz6dkKRsVRp/SstSIRpAkDJtJtRzZl/z9og0vS8KEFY0dKcpG5XgHFUwbu/sWhxZBU16W9pzVtZK14tSwcnkH8Y8WhWJa74ofAjmjDuAIyU3oLrHALqoJnzWy0eUSidaZZ3hZJuNpWTGwT5GSa2P36/xiRCgpZGU3QCpCSSP4R6Ktfm4yQv7S4hfv3HQ8q+zDXiq68LB5Yh9n7sEY8dcXH8A41EoHRYMGD1SPxxmvxxakcYbgja3qVzNROPkOCCwZ0t4LoJxkAzd1veFNnby4Zm/OL8KCHTfELeos0xleLGhSBU2dnF/cnNgH5xc3z9pNHYC7lspsCHkpxWwz2C+kMiuhDsYXmu9CuHlz9uJWxHkQcON3AYVjczhBNPOX5A0ziue6B8tkadjAQd9kJ1Dh7Q8RBIyj5yebgf1nOwLqxFbJiK8YI/EWcppsn5CA7d9xBS2kxxtSGM52N1BnLBbhnWT1Q/KwI1rdAs0PTAYDFLXqhVLL2PxEia5Zzqc8J6VEkytRrPSsyN5rN61Yh3+ksnCm5gym+I29Ze16gbl6ztdFb3y5kKELJrIpO4CSyYe3LozO5FUteQfgNfgh5LUUM26aAm/Lkhr4R6qYBSL46r/JXinF3inZ//ZJ9uzo5PmTwxHZK6nZOyUnT7Onh0+/O3pO/vbV0Hrsjc4FE+aqY5u4bVX9833LmmIbRZh1xZLeSmXm5Kxiiud0GOxGGLXcOdAvcB6YdQWsL6igxSCQis24FDuH8R1Msw7Ef23YhOWDeOTmEyCRm7UYfCOFUYyW6zaaa3mVy+KTbPb55c/EzrVqw8/WbPangNNt+K1g7v/riyFIV233gJB8ZxA/aKb2vTwcvYmas2eiI+KMSaj9yCmZKSqakipLMc5NohheCx1JDrYLJdVguEPuwhVeJjkThimn1U5LKRURTTVhCnwZYMTw+qPuDI0glqSeLzW3f/FOkNyTsu6B81aC6c2+Xi7RrcQFoY2RFdxcMyb9ulfs2ERqI8V+kXcNG7IpunaN9tFmZo3v8b6NrlGUAGQDfgwupopqo5rcNLGzo0WM3YfEoIqPb/FvTJ0AhyY/HRuEqSCvXhyju8XeclNm8jnTuHdwZ/NoevQitTDbiz51BSb+K66DCTEFIgyoGuH8T4pV0gSTI5GN0bxg0VzD0FHi3CnxkLHHBT521Jd6LnHYdijwIrnpY0eOmyBF3O16sf88yJpK3vCCqY304kCNLD++n1CfXPiwYg9I8PbFrmqWH4/ILGcjIlXKaPiMG1rKnFExIJ7SG8pLOuGlvcp+l2LA+r5umY3eZ1Sb/aP8fqs9i8Agv4Pu670VQI5A5+1GDiwEb5CNoF8FX39VmwHvbpRtIfY2/OyeNugANt8/On5y8vTZt8+/O6STvGDTww3VfwcJOX/pSQ7AD36E1bAP++QexmIUwIqup9sA878MO5LuglVznFWs4E21oUnAc6LI43QLzDQHOe3B6ODZs2fffvvt8+fPv/vuu82Aft9ya4QFXPhqRgX/3bkRixDr4dwZyzbAI72Q7WXPIRSBUDQS7RsmqDCEiRuupKj6lqX20jv75TIAwYsR+UHKWcnwziY/v/uBnBcYLYEhKuBdSoZqvS2dIBB3gQRO7qWBzuPNJILwVWrxdmbpXjhSZFn3ynkXHIJ2XueecOZeOY2HAXuoZn7KOStrKxajWII34oTqiFjCHNrr8UvLkAxvtYktDMTuy10d93c4PKmooDN7WwMfDUsY9GZh7NUn9mUGkAgvhnhjRWe7ZYyxbACzBbMAgrWgmkwaXhoQeFYAaOhsV/C1h8NBR4fuv11iqIUANefe5El04ybTJ5GOJAQNXt3lXgOkDAYJRq6dlEu97P2wGZ+KvtvA7Rd7lkDXREPrgYsPXTPoFg4/5Gxt7DH5XN1UiZ/t0Vf12fqqon36e3NYDYP+6b1W6+HYnesq5iT/G/xXMcvwniHgd5+pE2sbeB89WY+erP6qHj1Zj56sTZH46Ml69GQ9erLu6sliQRBKcjvJxrrgG2bofnwzhuvVSDvYH5AyMpgsegtVvXpx6efF3XNBhRJWpomRGRmzXGfupTHmbqg0S9NeqFWjDQZfwxZ1czb9n1+sxvRbw9QSgmEx+jooE1wUPGea7O87839Flx4Yi1hd8tnclMv00ITcuGg1MAasCEEsrbzGhWEz5QJWafGrBRkltVQjzOesogEv7n4dXA4YexuFmXnufa7JESTeTJihx2TQ1ha90CFMpWTHqPoqerRxdl1r2cwhmcUF6+L4oKpQsSTXXBSZZSx2hRUGjeMLZh55KDHPzG5JydD/aDfPp9ZB5DXmNnYT1LjRrJy27kYrZtrxAxY3dx1+qoyKqculS+FclXp6GzBRCuotkMAuD2SQtpd2sZNsHpzXju45N5qLUwwE8rzpZTa8urlL8ifSx5C930d2D5v8Szkj6BRQPE+oLCNn8GuaLeEVG0+DdnFR7iUYk+a4YtomVGbkdZv4C5zN54JC3gCvmL1lvYfSPrVDtF+HFFI5jVOI/SDUpyISyDrxYQgutKDN50CtlkwYJm94ZZN6u59V3GK1c4TWr4F0kAkzC8bsHD5eXBQuboApN4FLq8B00ryU2q7kzKP6drR6y5BUzAoFoGeUMBZG5cM/k6RbC8QwQoczWRO8xiTQorZilVRLYtkdxPu7gYpOBvBNUwqm0EnO21xg95rOqbALhXzg7S/ynbKq85d224PdOfDaLbO2LOfvQ/kwZl97vu34yc05lJA14zfg2+we9IU9i97pm1Qi8KMlY/nrZQRGcTuAOzGRSOY1ZLyyYrhah2kyqOVJY3hjPCJjbahh9i+0pKoaZ+QXqizRQ+L0tIFQpSB5yKmVREZkkYoVdUnBMORiT6xA7IpJ0DxntYFsUxeGgreQl15GpC4Z1cAkkyHBCZDTpisABwIAuAcuE5cns5MLBfmCm2Fo24M4MOezucs3Gub2K3bsPN1/rpHpQHKT3e45FW7vMkwAG4+8QV8zoV0WUKtY0JScHOgtnEE+pT4BbIPtTzeKPcD2JyM2mnW2f2j/G6szghMYeOlQvITZUZo6pAHj7ZPT2gB3dRm+KxlC0B1dnl9LE1ykBBA2vT3kc5paEB0F+O0cR9cHHG7g5fu0KOy5dhfyPlzIrBin2zee8pLt54rZ63GM7imsp8J1m1Pq70e3Sm7nqkBhHjybsDc11dridB/T4/obJBuTy905d+1K3BTr2PV59FO0S1S4LR5F5KrTaMh29NQIYo+gT89s73V82e2QbvIcfG9QDmZKedkoljLfZMzVjHib05cOuZIRb3D6HPyfLjX/HQOJDgVph42mo1DYPxe4CnojIRYpBIi0RZcscYLJZ0gFkkVT7rx6BM7ibEq31lHABO+YYSRvRyPqYEfCHHipQtWPwWNaLfVv5YAfjxqq2aYezTtjwU0zZHaQwhIuWv/G7r0x+dqyKs0MOXASsmbmG4uNdNVWhk+NHs3EfmUFa0QTcNnkJMfoDVm8zvrRscm4ak9ctEBg5RgwFYVHbo8tsSLUWdeknUgyAydJsxumuNlUklnl+dv7dm+zvbl083WuKg9GR1D5Ze6MscPhfeErd+1XDFx3wnKwKCQwaG+hiJTdm680aWpiZIerJveO5XgVvWYEdCE3HXfsNZdCc21AG0Q7XM/EFS4hzJEv70ztX5IPlnhMIyCj2tkaXeg1x1o/ei4XAmPwclMuyZIZS6b/QwqJVeOkuk6GtDKB5duaLFgSJPIlOdfk/3x5dHzyTz4GME1Xt9v0P1CBTqprCwicJLA+tHasZEAM2OT5tR6kzr1LVpOj78jh89PjZ6dHhxim+uLV96eHCMclyxu71fivZM/srlnJAsU0hW8cZe7Do8PDwW8WUlX+gpk2VvzQRtY1K/xn+F+t8j8dHWb2f0edEQpt/nScHWXH2bGuzZ+Ojp8cb3gICHlHF2DbCpXM5BTs+SqQ/gcX4VqwSgptFDVovEEbLDddzcCxcLyBHEVwUbCPDO3Lhcyvohj9gmu79QVyKSrs6xPWGRHLobECq3rwUGlIWQbEgh97fIX2lHG8tTD3KZnSMhG8WzD8b73DMqd6fi9xraWqNgZ96G9nf37xcuMd+5HqOfm6ZmpOaw1VvaDO1ZSLGVO14sJ8YzdR0YXbAyMtqkAu6jAZstGmhouyUV3v/h1CTAZG4aJuzJV/QVAhNculKPRmKHnpRkxYtuUp0Uh9KRipG7QEIEv8NxMFUOW1sCwMmBuqB21gWNfJ4Ll7zgJ7BygEkjvOgMHFffGRV2zj/JI7KQXhJLYLiArYJcU+v9IklDZtC7c5e1x6OTmwU2W/VIwWS/I1y2aZVaFoUxpyudSWrsLA+hu88pLxJABPS4xfX3DdFXPPWtE+zI0zAxM5JdRyBCnAMnn+0sGw96pRsmYHZ5U2TBW02vsm1QbpZKLYDZpK/SeX7/e+AeurID/+eFpV7e3Naenf2j98enp4uPfNkHkfdcsND0kR14Zcu5VOB8bRe2lqg4Vb3ctDAna70VYo59pwkTuj9L9Ev7lqLNEjP3FPWHF6N1yu7uXMV94EMDWWdWspwTPxYZHKldfpAINcquQCBdDOojlWoY1LySVjTpZRNTHFkL7BY5TTMiPjdp1jdBbExSzDb+m2fDSK5sbfQDGEo86eBWDDErivmpvujytYlmOga11bMUuCD8Fe0GiDsfoQOukGNqfHo9pXBuCNnRR2gpYbdiHvE+QaOvNV3gB36cZb3Ae8j+IVtFwKy8b11QTLTrdgl9seMGTXtx4vZ12yjGIQOTQ3/MYqBBY/U6608cU/hxbFtjLhb7skexPduiCYKl5OWEJq/qSalHT9ahTX11e6w+7WMcFpKemGztV3XF8TGBvrgHLZU9Ycj9ZOTidalmDZ0d+k5+yDZliBCst6faWDcuSufHu61i7vSkhVbbFxW6zzLZgi+e+sgPluWfIoeLtKEOAPLb84OjxcUbKzolxgFA6W4YQaW1YlrTCAngpwAbpyZ2jf05rPOly/BUxDZXAYZkGx/ItmjFBnUYVlIE6dfkrL0hdx6/ilpzzw7I4P2nmpv29fWIW/Mxil6+gkziqSuqHAV6zJxIptnt05/6t9DnEw3psIpg2AOgMwfIlsf5FRrWXO29LAoDr6YntJZThE2IEzl3jXJxDuiJi51MwVCkcjNEx27kVz8kYKbiRcAf/5/fmb//JFxcEE5hK8oR4fRHmgJdebS/vpLXQ6ZXgh2Ne7azBRTXln79nYkdrGdJtWj1p1SIal22SLL6gFSLr097I9nG0deTVj5uqh5nsPwwH4IFLoZVVyca1788LgScjXPWaNGQHsYBg9Oc5wmEMyTCkXhFG9tHgxDEhjsnTE5T+PDB5BMa3FrIfE2KR9j3UA7OD7BUvmiBRcwblyaPymh8aCJbUP7jH3SxhpRe7oSvLhIg7Nucf053ag1lLl43CQK4nwd8dLumA0UdjBA9GRlSnBEWB1ow/nL79BTuFuyCho6utL+LFFEpELEZXwCnbERZyje18qgdG+Asu2SlITQ5bFw6DkQvGKqiXyLMDFD53l9mdOsh8ebO44eX9w3urupBgO9+Gzk8NhYN5Y+ox3mQsic0PLjnm1B5bmv28KVmL/GU4w6lOCHd8CA+9ZxuGMiNIKLLQovDIytnOMCU8lEvDujvuMpUoytNeDnUjXCYCvrdwLEU6AMhfSACJxJQt7forezPkuZq6YoRjEDa7moiNCxSTrE5KiR5uH9iGpRqF9FXPSXRuGCu9oJyQqy/RKdkNFLxw3CW26ZwjWw9jGVkeM4rp97XBg0gd1SY0l4k+csh17EAGszl5Hle/dVv/YPtm0OrWvypJIy67AMMllVTcGwwpdeRMIz4aQuqg7xoB1MW6P0cqb2AxDRDGCaQ8MLGQhbo8htCsFnLZBg3OqigVVbERuuDINLX2BET0iL6EqQlT9AZWWn5oJU4IZMHcW7C7J13ZFw0Rwfxfyj27suGpK19BiomroXs9feIfl2EM3tltZ2SUrZhqFpao2KMSyq5W9vXVVkP/oLHCwnmgt0Ro+QI44apMun6UpO27s3xpaAof22eV2FB9lawFx0Udt0I+VRTA+SNtz3KkfxXJehOY9qNoaab8ZSvbeZRQpnt2u7e1MB6L0LjjXUAFrw4xA3XdeuMC7LXvnYjZt0jx9LtBOcmuhmtMki6Lx7sQxtCOAbcv6yHnoTHjgCrz2udyfLoH8R3eM1sy860YeA8foe6lcmSBfKc01i3A2i6ROnB0GOu6MQ32ncad1x5TcVCNfhCZKMQtsdRRb36OiRJHZJRmxJbpbCC0EOqp8zg2DqoJ3Rmbrmf34/NnVs5MNva8/10xR0/YdSoAZCreI5VN3QbdjXMIY0RvbZYrbw/bzZbfv1nD8rewAHu+qYg244E+T0Y2srxxOu65zi74abEbpJ/uhwVXnca8/zz6w16u4Axm5S8K5l8qSwXeQsdnbdz8x+RoaTuVMGKlHpJk0wjQjsuCikIuuxbkt0ETVgosdpp+25P2G5pZI/n3vHovFu9KH5FtycoGZ2dAS7OW7iyW8kb/SG3b/daCs6G0yITfQpU51KiNFy6IV7wgV911YwSacim1WdOnAcGQHXTeLOTUjgmONoH/gRBcxCQ4spp+hev/VHB1mRyfZ0X02yG8GKCCKLog2Ki0TGeW9WKn9YQntJDvJDvePjo73XQLCfdaC8G2wpMdKIgO7+1hJ5LGSSArrYyWRx0oij5VEOiA+VhJ5uEoic2M6VvMf37+/cE/uWhHfDhEiae5SXRab4mUVM3O5M1P4j8bUfiqCUw3kqaAzBo1dEB03YXGAh5GklAumIOhrKlUoDpKRS5aehL3X4cUXtObGjgA7tufdo3vnPvfBilSvXlzuEaIxBX4wbH/GzIjUkBReNwPZkR6PE1ksM+e52RU23zsLJFBUQCvMPAQ69jFfSFUOZHd7uKGZodqw3v6d8s1w/DZNDijXTz8Et12dPj04mJRylrmnWS6rg6FV6FoKzTJtqGl0l3PftpLNq0g6QsbZCM7WY95hBSeHJ2tg/SNIxQF+N1pZWXboAZlEUPwHgDvKjjYpUxmO4nC5yk2pYFXJynXYloaWHRezk5T9Kf3aoh60gTmjBVOpCadd6smTb29hMp9+eZfrFraSpJ4/H1yJPwSf1ya583HPXYoP+GezTbcd/bBPrYo8S8WV1+HBevEEnVY0SbmXUXWbO4gpgLU+Fu/v2XgtZ63U6mPnh/LasUJ1Uhbgl7N3b8cjMn717p39z/nb738eD6L21bt3O8iUXJ1SCEIvOO7eLO2CYjPTxtlqK9HXuWAw5Bd8AD682eLQp/vRbnA4XEfRG8lwEzbFUg0lNxgTYEgDqRmhskZNVa+42jn6cRUNZdrI2A3vynE7oow9vtBr2Ccr1GnUP4nJwY0UVy7oFC5wCx/1FtdxbqHLeU5vWMhm0pauMLwn9/Xm6rrkrEBPGRO5xBrgigi2SBU+LpiGXlA3KB/nJaMCkn1T0IfitLfNnyRausTIr3oJlFYSB9e2N9+DDH9rDmXCblz8cspy3iYPN48s8sHQ/YbouayqRjhcY+itvGHKMy0XPaLScGoXO+L6ebuf7hSc4ocN+RvdeGhvFb0Dk9x5nNCM3zB7rzhvH1T/k15t0q3a7hE0xKx+AGnhFz7ln859fY4638+X5xCYWOJBXsR2B0do5DVdMpURXt+cjOz/P7P/r1k+IjWvRoSZ/LPTW9eprXYdAwEjVNArtKHsil4IOT97e0YuXJ9+8hZmI197pW6xWGQWjEyq2QEmf0CltwPf2X8f4es/yD7OTVV2PJ+EXBoqCqoKQLmv2OK/hYPLNaElnwksAoCn7S0z35dyYfleZzwNz72lBXIMkUU0LuVsaH2De/BsgNAVFXqLNgfb9dKA6hk6nMJot116u9CG0bacCyM/4fix9S0ZMsBLSns+yNdNUY+IyWs8I/s8r2o4HNk3n93xWHs+TF4PBIDU2Jljh7ruGaIaGSr6wqJZHbX6rB814UZRxculS5PCsj3pDs25mGkUGSqeK+nTdHDLaallm+kZv6yvlzUbEZ7/lqYuT2nOJlJej4hZcGMwVi3mmt4yqrlpnODSFnW9YaLoQNimDoW8XJbLwgoWztUcEkZRQDgo7E1xfoHR+zoFzxKjhuifBVc+V/vzsymuoz3Kqz7teY61E13n23DN+WnQnUPYxwwsRCNSAp/4leZ248Op96//fSEYDO49DBdcsZ2VsnvpB/f6g5f3jKLTKc87CHzHrDiKqbGtyH3auYr+gXAxkU3vivoHIhsz/AMXhqlUucQfLPsa/KERUJJioAZ3Res6quLsCstaOXkf+t6Rqk0XdCV5R0EQBlErZSxYOcyfdTvOV5qAY90i7YazxVAl8GEoPHqlIjVTvGKGqdVQdThIBGEXqgQc+1+IGwyJ7H6qYZnLbVaP8qZSLagqWHG1m6DUqEdTSLJ2WWnRT05Zr5X8OGwIOvruODvKjrLjodLSoDyZ5dXu0ibOoCwOllwG2EEnjTrmnF9gPWB3BVAnz9Gwri4DJa0XL1X/smC+oMRIWe7TmZDa8JxoJ03GnTdTKi7lomuFeM2oEpjjTE1wX8y4mTcTcFzYLYa69AcBkfu82Nc1ywd34quj0/nP/6jfnvz4j29+ePrmLwfP5+fq3y9+y0/+419/P/zTV5tYw3fQtOlW4ypaHuH6AK8P4H4irULs+eNAwZyx64EEX7tKjnGHLP/cV88ZkbEXcd1PSNpcEd1Ugwh98uz5wJV7n45Qt+LCjX5nbLjvB/DR/jKAkfDjrTg5PkntMJ0QWx9UnD7dMPNHhNH6yfI1yzktPU8dhWxRTJpohWGXtRsa4RbMsNyM/MjwOibW3z7Wvtfn3C0S1Rj0MrcXbynJG21kFVJ+cBzojAxZHW5dnQx/KaZ8BhVsjSSqEVusU8upsRNFRU592tGUK7agZalH9mZXjUa8GKSeg1rBemAQn6bi76roGtRMaKn0iCzYJJk5Gh4iLkqpNRka1OLr7OKNW7szh/ktju1htCzXmMOcbITDQhQHFcsRohJXpcP+al/IAPdYt5f+GlR2CwqQN84a/VvDGhySvHr/GnLPpABS8FeEKzOUtq1wNBJq+kBBxIJBGXi3emgEuVE7ly7/+XT9BnvR85+wXWSgkt7knzK7bTUUPY31wWAILBCnSFpLD4Bxv9Y+63JLWjg6Pva2RKritNyxZTCAgbO5WK4+MDvLZZqnbeLD9vgiureVD2bK5bxZFunvNG9xbEdb1kxnfbdhMtjYqwRqPCJjz4bt33mh4T+1djXHPy7hL7Is8WVk5vZvLUMe9j76YR+zhx6zhx6zhx6zhzZd2GP20GP20GP20GP20GP20GP20EMg8TF76DF76DF76K7ZQ1LNqHAOUfeh19j6v2weKBcP669jJhTP54g+sNutarlW1VQs7aWLiAkDx5p0J74tS1vOzllZQ1lXqhQVM9/gxbiWQlF3GCowSBHCz1z/SBcSGuaNF3OXKONdBtDFu9QV4//IWmQxzrKU4jqNr1dYBjantftaA/qWgJVWgCELwKD+39P+B3T/LShoQON/WCp6AE1/pZ7/YMdgvX6/zfI20e1XaPYPAHZfp98e9q30+ZXa/H0W09fj163ifjr8Q6aKrdXdt9mIzZXcntZ+H6jX6uvbwL+Rrh4FkEEnQQclsu6L5OFdWsOvZNihQ3W24ksq2lseWnZB0I33qCWd4iD+PXS85sVBwolcyE+c1oD3im/JmdW8GBM5NUwQbehS+7gx35gae8xbZTqKScplzdGkADUwSzmhZdTe0IMcCWzb3Acb1+bbPK7gIuAn5equ+52ef1rBxoPTM01izhS03iBWHGZQIm6maOXkdEU0r3hJh8OoBhdSDyL0ARJ7/SpqCrUF+VDfCapm22Ty3QmLVM2aqtNbz/55Q5dWyUHZGMm1VtKw3IBbnxt+w4Y9ixFK/3NP6/neiOztl/b/raBj/+u7vj3b+6/+otlHljfQGWlXSz+bQAcNhsk47hx6JtBOP7iig0argwkXB4PUAtxv1zsGkwwExtoVwG8jzPHCg2B88x2qwxoxBvcFFRimHXcsSj1YUeFDQslEyYUGP6pPlXPAeBwu2ITU0NHHd960orUY7KkCjQWL7D6nq017Pz7Z2EcI7ZTOXz58I572Hj4+PHq2f/h0//jJ+8Pnp4dPT5+cZM+fPvmPDa/j9641U0KWrj3PANgLqa65mF1hbNdg5/S7SBMHc1mxA1rG/QtuBdvBQgIs3vIaruxEdHDW9VR0eJc83FR0aLvCMWzA7Qt7T2nOS26sCFDzGwmES5VsRGFvfs6wgwK2E/bDgQ8dftPd/iouk0AzBo2/KyqWViXKWQjHIe/jScOY2PARfPyoCFcjAjl+IRAbDxF3EoCupQAp3qVNtqLt2KEti7zvZ9BzVzHD4talbVAM06MoIXXCSCMKpkAVDYFPauQCYEdx9OuI5CWHjjz+JSvO+Ki/OMI4I+fYeMcti5YlhM4a2YLM6/EIBTMKkpJweAGkUJeecn5BjOI3nJblckSEJBU1BjImIRLCwARUQfPMZYjvjyc5pdkky7NifJf67AOhSSsP0KbhSWdlyPe2KAHykb44bJT8HQXG9CIiL+8QD+k+GkhLdRQGdWyjuPZcCuESCoD5Y0SaYjOqCgzp09B5ZRS9iWkxEx6iS608i8lsuVSFxq55719chFZB2JfYQ4bg5IzbfzssccGhPeHlX966iNavdehrYYdqp8fhsSZvyL/rzuGKv5fL/uI7WRNC+9bvwAZcKCKhuWm8iRU7wDFVkb0w0h52EZi6uB4/s+gAq30FbvjZqSzeHjyQvuur8ubIuHRn8Bh21932MhmaQpt1hLwNjuQQOPprI/JWD8Jj7r4bGqZFoZAmGszSCW7RPhrUe72aX+DQBx7wtCUHqmy0sLy7osLw3OdPeLfrR2wLMWpbe1sFb9qU9oUbbpfHf2eRFViQnCnQH9tkMc+eVBh9SstSh5aQOTVsJtUS+ZPLsNaGlyVhAppUw2srcgQsgqYcdA5a10rWikM76TswIMeydyVGYoAY9vzD7Qh3BKbfez5RTfiskY0ul0izrj0i74Sz6KBzQUgaeLxHhPqy9MDXGyhoLy2NZIT8pcUv1nBPxzPS5fQpumiTSJDWx5l7MPZO9a4MIuwF0ebHFw0G6aIGM7YXkAVpnCF4Y3vX2dsKCh64Fg3JkNAU1ooUQ+bz3Uex+ujR5LUXeId3vBLk/OLmxD44v7h51m7qANxbJAJvodBKZVZC/elDj1eCgBu/Cygcy8QJsj8oV6bNqnp+shnYf4bkGeh90ybEuphS1OvwahgipPtksrSQbqi8XbjMljuB+hhO9BhO1F/VYzjRYzjRpkh8DCd6DCd6DCe6aziRK8XRN2m0DzcP7PB1Pbr6s4l/kwqCe+y92XZewxgjGnvjyhIiN1YFCk25KFxROe9LhOI8aLHyd3xk58Pp7RedvKd7Ngl8sA5bUVCOL9bYCIHWHQB+sMt24bUqbLhVhi6rS6RC/y2+XtFrpq3iVEuteerMIVA5LsVmlBiLOyeiYo7DYIUeXd7sqBiE4SjORA7+Ca0bptG6YcdTrLALcU3/QM9PBrRinIsF8520eeFbf4eMTFG0+48WAS5m0HDUNRP8YkjGLZ58y56yyZQdUvYsP/nu2+Niwr6bHh59e0KPnj35djJ5fnzy7XSgdNO9MhVbpwQrqTY8R3PrvlvNhh6JWOjx9N0mrrnzsyJ3LeZp4WPIZnMN/qCLLxh+Q82sUi40cLeFTIbzKG6VPGh050+cagnZt7q0v7tmYCkBIlcWie8LgwZdt7yxJzqBbd6Sz89KrE3oQLWkUHBtFJ80dghfCgnpQzVg6w1q+lxqo4lJl9YeB7RPejudXzCWGHHLGvB8u4pzUMxGTsmreLdj1MNyXNK5j7FAvanRppOohm7C76Uif2bU6P4wXFtsFWxKm9JArYs6eHwC/ixpjpNxnUdjSoQkfpzQrfChm8ytOAHb+OKi3M2tqR8+9j4XV1AAu7EOXCkJE7T3luyQrZ/ejrqGG8JgnSzyFNKUQEad3Qo1t5IZxgkCx8MeVLOTFNoXrgMjTNDZi22CwbammSfZcbZpK71/86F2KanEUsdt9NJyPyhjJa+taEldZDIz2DQ6FTzaCL8poUPEMoAfVs9ZxRQtd1hV55WfoydutLIC+ZpP4WZmH7k2ndy8Vu5oe8GCG0ATmiupNVEMvOKu4lwgYV6MSSGh++1wnf/n9GT69PBw2hFQwbDfkU/jZ5uJp/jJJp6d0L6fOjvaQVKHtTvU5p6c2C/h3DnbS6Cf0AvhPCqPXojP1wuBpYH+3rwQXaj/AC/EKhB26IXA4/S/wguBS3Gm/bgU1WfqitgC3kd/xKM/or+qR3/Eoz9iUyQ++iMe/RGP/oht/BGJvteoMlX2Prx7vV61+/Dutb9hayVveMGwvmtdMsPsr5g4SHRuVd+Ri66FyrHUzO+gg63u2PNQSbrYB4YVbSudRkFlWx/gbOapmtbZoLfSuLg4LgYqQI7igmcFILDCvBKKnWss0pIBIcaXgqZFc4h8L+XMUZv9nGuXb/Vro00bSOiLfCKi+1aE0HsmxIWHT8PQFPwVC6oDwKOwu12paJVpIcVv3HvCGc+yXJ6enDw5QCPaP//2p8So9qWRtR1+xc87SEFdpwZOwx6hTs4rq7I5/EEkZaPR5DxCttIqvCGNPhlx3Kgys2OOR3ajIWLXJNujWC6FNqoBG5lUxG8SkmJ6whOyHNiMO6F/wKoJx3lnhhAYvdPcbhRaFOzBIvYGjt0ppiKejn1LpZpGqi+MuhormyukD7PKl84Ms2qV6RZ1l3suMKPJkpo95Z6PuHBr6fQQV7cVGghgLHq5bHO5U+OoswuhiwOcJ9D/wpFyUtkcaHomQ58vZ7Ppqz0BxelqNrV8rE4yEIbNEt/MhgaQHp5PTp4M9w09eTKkUZv5rujhAtpgraIGdzz3BtRmyPbYFVT2QMEEjiEFQQbgxF8wB7oLezJMWEeHvXTJGs7vP8P5ZR+h7nLUECCeDULXkex9G7hkICHtOEC5oVRotA74PPxGYc5JY8JbKfSmgwS0zbe9wqratHDBEvCN1MeHI3QcX4mnlUyYWTDXNcAsJJ7uodoEis6qHbastScm8tuAADQ1Lo9j/OU4Ikwj68FN/HKQCXvAB9bUaKZ2mSP9wY3fodNBu5nWnXEf+KTj+MOQxPjoSON6y1wnuxEQS9B1vQzXfIFXUXKF/ubshkYkZiRpRd/M9xkNvRTBZwVabWz5tk84w0ST9raBieZUY58GM6cCrfnFqNUiBJQjWnpJGngBuAKJnLYwzTesTGNUc1thGgyTTh5F5srkea9czUBJm9R39keHOf3c8Ug03bCnYJ63ezNwJh4m5IaWE5bc8+ukwLm9tn2VglLOWmFpBYxWjO7amO6R7nsGwJJX0KotkQNv4TJfadQSXPGZKaE3lJeYP98DmlWU706btQcNZvCy2wAEc6p3JtS48Dp/4OdpmFvMhtCFDy9CpTEplhV0r7KvdC6YD5pNm9JidgykACVHlPsHBCeFQB5oBgFUTsuU7XU6NuVU2MvKXc1D3omO7d77JzqPty/QjbEvkUt7QCGHd1zwFAR1Oe7sJPC+Engn38MKLrSeKtZRxi2rJ2uroiFefNgaJH0e+FJb2TDYPYvjDgGP3QwA6sD9nZYwa29xEj/f7i7HIT25tHEgVhl01Xl8UQovV9hvl2gjCsPpuVy4rs4LNgnRJxAmFRXex0oFVFlptQmAh6pHMRI/E/OdA/YmjTxqMTeo7O29kb/zsqQHT7ND8jW/mEvB/om8uPhA8O/k50tydHx1hO0afUG1b8hZXZfsFzb5iZuDZ4dPs6Ps6Cn5+qcf3795PcJ3f2D5tfzGB0IdHB1nh+SNnPCSHRw9fXV08pxc0ilV/ODZIVTX2vDivct9hhNthseYuNt936JVxsNs57/1d7ELSeKpzg4HrDgsRGc+DB6RJLbHowNk4FA8toB4bAERYe2xBcRjC4jHFhArN+j/uxYQX4YWmVZDiVucfUne//zy59OhPpfOzHrAcn2AWT8HR98+TyRUvEk7rb+GULBiTd3GXu5mdpBdshuIde4LrQsGGkwlQ9BUb0Ef6sIqiFNesgmj5oBzfeCcnzTPJRTe8ZVE+gJ3VlMTokW3WNCF/WxIdIyFjoHpKi5C27ItpntjP7vLdPTXO01nP7vDdFIPzBhFC24z3bCEsWLSHlY3mHQIpf1Jv9i3KDsllra+CDLXn/FfA0O/cC4GaF0rBXwXNHZv3AG7Relq/bjeY1+kLQdDeCmjJjO8Yr+3kiQukZY8ZGLW1MxPnTrfebniM0URQjBtJqPjjMmwcvIry70Qhf+42oJ0wvqBXHyDTVi0j6ZPIGBKdbYuFtdWTPLKftQRVKHCU1FwV0LLiq0Q3+9yumCeEMq/qrljJ1nqLpkbAFqUYpRsZI979jfRMtn4vbX7B4MOsuX+wIM8vDu6o/a8lE3RkvsL+09vcIfMKFpQQ4dPwBv3K/LrPPlU2y1q0wRpUVzBC1d+SF/rUKr4QCRrhg+yWklLmm0JzHDtul/2P66noVjfcp9YevlBylnJcMXhgjqzyMTM2rKID02IiWeGZgEwWOotuzH48tq9jubwmY1tBtL6aUJ2bXh/65k2ILDOXJvScDSbSza9io7h+sncB1n0waZzOWbMS26WVxsw1/VfbTqro7RNN65H5ZvOg6GfG82RvLqCHxQyvwYqdQzhpf/3wOHC3yDbsJuy536zR1vPpTJXeD+0FgEq8rlUfr79wAxWXI4BLLLWtuiPfBxgTrkAd0CP28doilA1/MngdqyYqqKz/t1y62z2q65FaotZO19uNundpyvphJW6Fc5/lAtiJKlobfmsZv/cgyURN8h6kYPcEnJncUUQhMxTrjMVObr9Ef81MMi5lRcianWeBfu5z4HPIgK1z4fIk/z33/zM183EqmyY2uPm/yl+NgBF+3u4ZNMbsx2UxLOvP03tR7eeqATo7U5VLYthcttqEyMM1LJA+9XgVM3A2b3rTBeyIB/OXw6b1HVN84dbVDtifzJZ9I76PSfzFqj+ZHhMbj+Om03kzn1FByJAwVuKVUQfarpoyOE5b2GAd8VnGHYFUm/j9vefF8d1HKZtHtJrHDIwrq+AHxhLkGOHGEGnMcnGXIB93PS+8VXNB1sWrNJLQKluF7z3ArXsOaPKgKrtwuf3OjhYsUr39rb6p5vVfZ0qtrtULt/PWZi0U8VihcH/fpPooMCzVaz4vlw4mQ1iYX6VE3L+klCNkY+TZbu7Q6nAjepGXQ17fRIY3ktDyyjVghimzdBY3b0kiQ0qebwiO7Y390s3C1TA57mSmuVSFANR5b3Y+XXHqVFl1vuge4w22pIzF7vdqNKHwkMdG18bf2zyejwiY1NCt/u5MfafVBT4dz1U3KTjsbttIb14+jstJLY6+7CFCbPb7XaeFZlPxK+41pBzw6dRoaVkuPCRpcnzi9uKCWxdNmAdlOcxVCkk3qLTD0Qe89oF2ofIZ1/AXZY3rCC8DoH6XuB1zUfQDDVcpCahe8V+a7hiRW9f7iLAi4Ln1DI3Pg18Dqst3dCSF77AUygOA91X1oXM5XOWX1/14rHu0PWGGHnNhI+GNJJQonnVlIYKBgU2CBc38poVPhhpipNrQkPlC7BFYh+GqOCDqzBkX3aKB/VV0V6+vcRI2dYOrZuqohDE76/ANw5R7pe9FVdd+yG5/arbu0gyh0hJtXHWalZxYzWkogHGSRFyF2QfNg2XHgX3FpBurkeADh2hg5s5GVeyAH5QjrO9W27RgZ3cKsUDqqpBziWYhT1grhpdk+eMxb6+1tSw6N8xDzfxlPKSFWGX3QmNdtnyMihq1NQbyjbtGBtseOqqcxN16t+s2pHPlrc/NINueWUjWhMBtl1ZwS+VuT0eZVg68IwVozthK33FCCWrwPWzP0pY8fxI5tf6aUSolz+/+OnyqZXuPy43pNQwxjCOVtWKiiYKtThSFKyi2K13pXOSX1+Ski6ZItgKySheY8OwTXfDtTQZ3JIuILcAAwDxiiUEw7RVqLieQ451aEpzw6lHm33JsaDecKFcYuuwjAYxMkF91vl8aNlkHSGSdcTYW/xqgvQUacpII96ze8VErpaY6w/btiFZmnK1+rsq9LaljIQgb2OhOVOGT6HI1ZWQ5grEnasJmw5lTXRapyWgvKKq5FaXgR5o1ETFe9st/ErHE6L8ATP2ozaHAYMMr63gek3NA0L1h5/fORWFntPrbgj5w53gKRf2+FpQw2TRwSwVo8UyOqCufEBv4KjT0+dyUL1l3pg6FnDev7/Y0nrjRhhG/Crxxk6z3eFsa12QDcSbTrjinYQbV9kbNHBvBHGoefDDAAi532nwytndDsP/9CgpkI5Pcp1ypQ2USbPinttCyFhwMt9CWaWkn6pDQEPVtRTai4fO7IhIdZBLlUWTdg5Zb8DeoUsOWe91LDfAp+1kPj7PDm9BmsgCciGhQCkltWJT/nEEWfUDp8z+cXqEL9QJhrNlpyqHARsXcFyR6jSdbZIACHxjIfls+MTQZJ7UriykD05vMt6kVACnWMY+sUN4yuoLUIDGR0ogO6QEe+TZlWMDd6KEtXSgXUFRlFFclaKY8wxwjD6nWHVNf47X8tBknsKv5owWnTSMO6M5lXU8j48RDpWJu7tgkT/A3PEasGczuiVARXa7lXB/aJsckLBG8/l73zlo0yhMdje9/3b5VDGjOLthRSie6MyFABpxsK1IvgSG9ODcOwbPd372hNPpTQqVr8H22Bsu7p+adh8l1BhW1SYjr0Tha3pArYvAz3ujFbxAY2hyYXzOd8PnQtVOS+B5FWsJ5y/eXGyoHbgvyTbawfkFqUPb6FsVA8d8+mENW9mF3+Iu8SmxiyOv8rl85wYG/vcQVsUwMnkXMcx3rLb0kEr9G8r8D21P9MabPN5te/62stjkW++4ncKz9rtYbjrVqG7TDjuv30s7hFJdeMYfgkY6NpIX99UKH9jGOcjmYztnh1lvoca1pv3PhfntQM1eg9FW7bH/0obVLfagPJVliZ224Z8Jov5fAAAA//9RvvcA" } diff --git a/heartbeat/monitors/active/http/http_test.go b/heartbeat/monitors/active/http/http_test.go index 4822dc0cb68..bd5bd1f5dd4 100644 --- a/heartbeat/monitors/active/http/http_test.go +++ b/heartbeat/monitors/active/http/http_test.go @@ -38,7 +38,6 @@ import ( "github.com/elastic/beats/libbeat/common/file" "github.com/elastic/beats/libbeat/common/mapval" btesting "github.com/elastic/beats/libbeat/testing" - "github.com/elastic/beats/libbeat/testing/mapvaltest" ) func testRequest(t *testing.T, testURL string) *beat.Event { @@ -192,7 +191,7 @@ func TestUpStatuses(t *testing.T) { t.Run(fmt.Sprintf("Test OK HTTP status %d", status), func(t *testing.T) { server, event := checkServer(t, hbtest.HelloWorldHandler(status)) - mapvaltest.Test( + mapval.Test( t, mapval.Strict(mapval.Compose( hbtest.BaseChecks("127.0.0.1", "up", "http"), @@ -212,7 +211,7 @@ func TestDownStatuses(t *testing.T) { t.Run(fmt.Sprintf("test down status %d", status), func(t *testing.T) { server, event := checkServer(t, hbtest.HelloWorldHandler(status)) - mapvaltest.Test( + mapval.Test( t, mapval.Strict(mapval.Compose( hbtest.BaseChecks("127.0.0.1", "down", "http"), @@ -249,7 +248,7 @@ func TestLargeResponse(t *testing.T) { _, err = job(event) require.NoError(t, err) - mapvaltest.Test( + mapval.Test( t, mapval.Strict(mapval.Compose( hbtest.BaseChecks("127.0.0.1", "up", "http"), @@ -282,7 +281,7 @@ func runHTTPSServerCheck( event := testTLSRequest(t, server.URL, mergedExtraConfig) - mapvaltest.Test( + mapval.Test( t, mapval.Strict(mapval.Compose( hbtest.BaseChecks("127.0.0.1", "up", "http"), @@ -348,7 +347,7 @@ func TestConnRefusedJob(t *testing.T) { event := testRequest(t, url) - mapvaltest.Test( + mapval.Test( t, mapval.Strict(mapval.Compose( hbtest.BaseChecks(ip, "down", "http"), @@ -370,7 +369,7 @@ func TestUnreachableJob(t *testing.T) { event := testRequest(t, url) - mapvaltest.Test( + mapval.Test( t, mapval.Strict(mapval.Compose( hbtest.BaseChecks(ip, "down", "http"), diff --git a/heartbeat/monitors/active/tcp/tcp_test.go b/heartbeat/monitors/active/tcp/tcp_test.go index 36c6c79f3de..fc857a298b5 100644 --- a/heartbeat/monitors/active/tcp/tcp_test.go +++ b/heartbeat/monitors/active/tcp/tcp_test.go @@ -35,7 +35,6 @@ import ( "github.com/elastic/beats/libbeat/common" "github.com/elastic/beats/libbeat/common/mapval" btesting "github.com/elastic/beats/libbeat/testing" - "github.com/elastic/beats/libbeat/testing/mapvaltest" ) func testTCPCheck(t *testing.T, host string, port uint16) *beat.Event { @@ -102,7 +101,7 @@ func TestUpEndpointJob(t *testing.T) { event := testTCPCheck(t, "localhost", port) - mapvaltest.Test( + mapval.Test( t, mapval.Strict(mapval.Compose( hbtest.BaseChecks("127.0.0.1", "up", "tcp"), @@ -145,7 +144,7 @@ func TestTLSConnection(t *testing.T) { defer os.Remove(certFile.Name()) event := testTLSTCPCheck(t, ip, port, certFile.Name()) - mapvaltest.Test( + mapval.Test( t, mapval.Strict(mapval.Compose( hbtest.TLSChecks(0, 0, cert), @@ -166,7 +165,7 @@ func TestConnectionRefusedEndpointJob(t *testing.T) { event := testTCPCheck(t, ip, port) dialErr := fmt.Sprintf("dial tcp %s:%d", ip, port) - mapvaltest.Test( + mapval.Test( t, mapval.Strict(mapval.Compose( tcpMonitorChecks(ip, ip, port, "down"), @@ -184,7 +183,7 @@ func TestUnreachableEndpointJob(t *testing.T) { event := testTCPCheck(t, ip, port) dialErr := fmt.Sprintf("dial tcp %s:%d", ip, port) - mapvaltest.Test( + mapval.Test( t, mapval.Strict(mapval.Compose( tcpMonitorChecks(ip, ip, port, "down"), diff --git a/heartbeat/monitors/jobs/job.go b/heartbeat/monitors/jobs/job.go index 4c9b9db43f5..efa38739401 100644 --- a/heartbeat/monitors/jobs/job.go +++ b/heartbeat/monitors/jobs/job.go @@ -17,7 +17,9 @@ package jobs -import "github.com/elastic/beats/libbeat/beat" +import ( + "github.com/elastic/beats/libbeat/beat" +) // A Job represents a unit of execution, and may return multiple continuation jobs. type Job func(event *beat.Event) ([]Job, error) @@ -46,6 +48,24 @@ func WrapAll(jobs []Job, wrappers ...JobWrapper) []Job { return wrapped } +// JobWrapperFactory can be used to created new instances of JobWrappers. +type JobWrapperFactory func() JobWrapper + +// WrapAllSeparately wraps the given jobs using the given JobWrapperFactory instances. +// This enables us to use a different JobWrapper for the jobs passed in, but recursively apply +// the same wrapper to their children. +func WrapAllSeparately(jobs []Job, factories ...JobWrapperFactory) []Job { + var wrapped []Job + for _, j := range jobs { + for _, factory := range factories { + wrapper := factory() + j = Wrap(j, wrapper) + } + wrapped = append(wrapped, j) + } + return wrapped +} + // Wrap wraps the given Job and also any continuations with the given JobWrapper. func Wrap(job Job, wrapper JobWrapper) Job { return func(event *beat.Event) ([]Job, error) { diff --git a/heartbeat/monitors/jobs/job_test.go b/heartbeat/monitors/jobs/job_test.go index c26077ba28d..6a3e06223a0 100644 --- a/heartbeat/monitors/jobs/job_test.go +++ b/heartbeat/monitors/jobs/job_test.go @@ -26,7 +26,6 @@ import ( "github.com/elastic/beats/libbeat/beat" "github.com/elastic/beats/libbeat/common" "github.com/elastic/beats/libbeat/common/mapval" - "github.com/elastic/beats/libbeat/testing/mapvaltest" ) func TestWrapAll(t *testing.T) { @@ -117,7 +116,7 @@ func TestWrapAll(t *testing.T) { fr := results[idx].Fields validator := mapval.Strict(mapval.MustCompile(rf)) - mapvaltest.Test(t, validator, fr) + mapval.Test(t, validator, fr) } }) } diff --git a/heartbeat/monitors/monitor_test.go b/heartbeat/monitors/monitor_test.go index bc459278e71..ce86bb16371 100644 --- a/heartbeat/monitors/monitor_test.go +++ b/heartbeat/monitors/monitor_test.go @@ -21,11 +21,12 @@ import ( "testing" "time" + "github.com/elastic/beats/libbeat/common/mapval" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/elastic/beats/heartbeat/scheduler" - "github.com/elastic/beats/libbeat/testing/mapvaltest" ) func TestMonitor(t *testing.T) { @@ -58,7 +59,7 @@ func TestMonitor(t *testing.T) { pcClient.Close() for _, event := range pcClient.Publishes() { - mapvaltest.Test(t, mockEventMonitorValidator(""), event.Fields) + mapval.Test(t, mockEventMonitorValidator(""), event.Fields) } } else { // Let's yield this goroutine so we don't spin diff --git a/heartbeat/monitors/wrappers/monitors.go b/heartbeat/monitors/wrappers/monitors.go index 3d67f36808b..ad05d22c5e9 100644 --- a/heartbeat/monitors/wrappers/monitors.go +++ b/heartbeat/monitors/wrappers/monitors.go @@ -23,38 +23,61 @@ import ( "time" "github.com/gofrs/uuid" + "github.com/mitchellh/hashstructure" + "github.com/pkg/errors" "github.com/elastic/beats/heartbeat/eventext" "github.com/elastic/beats/heartbeat/look" "github.com/elastic/beats/heartbeat/monitors/jobs" "github.com/elastic/beats/libbeat/beat" "github.com/elastic/beats/libbeat/common" + "github.com/elastic/beats/libbeat/logp" ) // WrapCommon applies the common wrappers that all monitor jobs get. func WrapCommon(js []jobs.Job, id string, name string, typ string) []jobs.Job { - return jobs.WrapAll( - js, - addMonitorStatus, - addMonitorDuration, - addMonitorMeta(id, name, typ), - makeAddSummary(id, uint16(len(js))), - ) + return jobs.WrapAllSeparately( + jobs.WrapAll( + js, + addMonitorStatus, + addMonitorDuration, + ), func() jobs.JobWrapper { + return addMonitorMeta(id, name, typ, len(js) > 1) + }, func() jobs.JobWrapper { + return makeAddSummary() + }) } // addMonitorMeta adds the id, name, and type fields to the monitor. -func addMonitorMeta(id string, name string, typ string) jobs.JobWrapper { +func addMonitorMeta(id string, name string, typ string, isMulti bool) jobs.JobWrapper { return func(job jobs.Job) jobs.Job { - return WithFields( - common.MapStr{ - "monitor": common.MapStr{ - "id": id, - "name": name, - "type": typ, + return func(event *beat.Event) ([]jobs.Job, error) { + cont, e := job(event) + thisID := id + + if isMulti { + url, err := event.GetValue("url.full") + if err != nil { + logp.Error(errors.Wrap(err, "Mandatory url.full key missing!")) + url = "n/a" + } + urlHash, _ := hashstructure.Hash(url, nil) + thisID = fmt.Sprintf("%s-%x", id, urlHash) + } + + eventext.MergeEventFields( + event, + common.MapStr{ + "monitor": common.MapStr{ + "id": thisID, + "name": name, + "type": typ, + }, }, - }, - job, - ) + ) + + return cont, e + } } } @@ -99,7 +122,7 @@ func addMonitorDuration(job jobs.Job) jobs.Job { } // makeAddSummary summarizes the job, adding the `summary` field to the last event emitted. -func makeAddSummary(id string, numJobs uint16) jobs.JobWrapper { +func makeAddSummary() jobs.JobWrapper { // This is a tricky method. The way this works is that we track the state across jobs in the // state struct here. state := struct { @@ -114,7 +137,7 @@ func makeAddSummary(id string, numJobs uint16) jobs.JobWrapper { } // Note this is not threadsafe, must be called from a mutex resetState := func() { - state.remaining = numJobs + state.remaining = 1 state.up = 0 state.down = 0 state.generation++ diff --git a/heartbeat/monitors/wrappers/monitors_test.go b/heartbeat/monitors/wrappers/monitors_test.go index b6a150d2103..00755b4c6e2 100644 --- a/heartbeat/monitors/wrappers/monitors_test.go +++ b/heartbeat/monitors/wrappers/monitors_test.go @@ -19,152 +19,213 @@ package wrappers import ( "fmt" + "net/url" "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "github.com/elastic/beats/heartbeat/eventext" "github.com/elastic/beats/heartbeat/monitors/jobs" "github.com/elastic/beats/libbeat/beat" "github.com/elastic/beats/libbeat/common" "github.com/elastic/beats/libbeat/common/mapval" - "github.com/elastic/beats/libbeat/testing/mapvaltest" ) -func TestWrapCommon(t *testing.T) { - var simpleJob jobs.Job = func(event *beat.Event) ([]jobs.Job, error) { - eventext.MergeEventFields(event, common.MapStr{"simple": "job"}) - return nil, nil - } +type fields struct { + id string + name string + typ string +} - var errorJob jobs.Job = func(event *beat.Event) ([]jobs.Job, error) { - return nil, fmt.Errorf("myerror") - } +type testDef struct { + name string + fields fields + jobs []jobs.Job + want []mapval.Validator +} - var contJob jobs.Job = func(event *beat.Event) ([]jobs.Job, error) { - eventext.MergeEventFields(event, common.MapStr{"cont": "1st"}) - return []jobs.Job{ - func(event *beat.Event) ([]jobs.Job, error) { - eventext.MergeEventFields(event, common.MapStr{"cont": "2nd"}) - return nil, nil - }, - }, nil - } +func testCommonWrap(t *testing.T, tt testDef) { + t.Run(tt.name, func(t *testing.T) { + wrapped := WrapCommon(tt.jobs, tt.fields.id, tt.fields.name, tt.fields.typ) + + results, err := jobs.ExecJobsAndConts(t, wrapped) + assert.NoError(t, err) - type fields struct { - id string - name string - typ string + for idx, r := range results { + t.Run(fmt.Sprintf("result at index %d", idx), func(t *testing.T) { + mapval.Test(t, mapval.Strict(tt.want[idx]), r.Fields) + }) + } + }) +} + +func TestSimpleJob(t *testing.T) { + fields := fields{"myid", "myname", "mytyp"} + testCommonWrap(t, testDef{ + "simple", + fields, + []jobs.Job{makeURLJob(t, "tcp://foo.com:80")}, + []mapval.Validator{ + mapval.Compose( + urlValidator(t, "tcp://foo.com:80"), + mapval.MustCompile(mapval.Map{ + "monitor": mapval.Map{ + "duration.us": mapval.IsDuration, + "id": fields.id, + "name": fields.name, + "type": fields.typ, + "status": "up", + "check_group": mapval.IsString, + }, + }), + summaryValidator(1, 0), + )}, + }) +} + +func TestErrorJob(t *testing.T) { + fields := fields{"myid", "myname", "mytyp"} + + errorJob := func(event *beat.Event) ([]jobs.Job, error) { + return nil, fmt.Errorf("myerror") } - testFields := fields{"myid", "myname", "mytyp"} - commonFieldsValidator := func(f fields, status string) mapval.Validator { - return mapval.MustCompile(mapval.Map{ + errorJobValidator := mapval.Compose( + mapval.MustCompile(mapval.Map{"error": mapval.Map{"message": "myerror", "type": "io"}}), + mapval.MustCompile(mapval.Map{ "monitor": mapval.Map{ "duration.us": mapval.IsDuration, - "id": f.id, - "name": f.name, - "type": f.typ, - "status": status, + "id": fields.id, + "name": fields.name, + "type": fields.typ, + "status": "down", "check_group": mapval.IsString, }, - }) - } + }), + ) - // This duplicates hbtest.SummaryChecks to avoid an import cycle. - // It could be refactored out, but it just isn't worth it. - summaryValidator := func(up int, down int) mapval.Validator { - return mapval.MustCompile(mapval.Map{ - "summary": mapval.Map{ - "up": uint16(up), - "down": uint16(down), - }, - }) + testCommonWrap(t, testDef{ + "job error", + fields, + []jobs.Job{errorJob}, + []mapval.Validator{ + mapval.Compose( + errorJobValidator, + summaryValidator(0, 1), + )}, + }) +} + +func TestMultiJobNoConts(t *testing.T) { + fields := fields{"myid", "myname", "mytyp"} + + uniqScope := mapval.ScopedIsUnique() + + validator := func(u string) mapval.Validator { + return mapval.Compose( + urlValidator(t, u), + mapval.MustCompile(mapval.Map{ + "monitor": mapval.Map{ + "duration.us": mapval.IsDuration, + "id": uniqScope.IsUniqueTo("id"), + "name": fields.name, + "type": fields.typ, + "status": "up", + "check_group": uniqScope.IsUniqueTo("check_group"), + }, + }), + summaryValidator(1, 0), + ) } - simpleJobValidator := mapval.Compose( - mapval.MustCompile(mapval.Map{"simple": "job"}), - commonFieldsValidator(testFields, "up"), - ) + testCommonWrap(t, testDef{ + "multi-job", + fields, + []jobs.Job{makeURLJob(t, "http://foo.com"), makeURLJob(t, "http://bar.com")}, + []mapval.Validator{validator("http://foo.com"), validator("http://bar.com")}, + }) +} - errorJobValidator := mapval.Compose( - mapval.MustCompile(mapval.Map{"error": mapval.Map{"message": "myerror", "type": "io"}}), - commonFieldsValidator(testFields, "down"), - ) +func TestMultiJobConts(t *testing.T) { + fields := fields{"myid", "myname", "mytyp"} + + uniqScope := mapval.ScopedIsUnique() + + makeContJob := func(t *testing.T, u string) jobs.Job { + return func(event *beat.Event) ([]jobs.Job, error) { + eventext.MergeEventFields(event, common.MapStr{"cont": "1st"}) + u, err := url.Parse(u) + require.NoError(t, err) + eventext.MergeEventFields(event, common.MapStr{"url": URLFields(u)}) + return []jobs.Job{ + func(event *beat.Event) ([]jobs.Job, error) { + eventext.MergeEventFields(event, common.MapStr{"cont": "2nd"}) + eventext.MergeEventFields(event, common.MapStr{"url": URLFields(u)}) + return nil, nil + }, + }, nil + } + } - contJobValidator := func(msg string) mapval.Validator { + contJobValidator := func(u string, msg string) mapval.Validator { return mapval.Compose( + urlValidator(t, u), mapval.MustCompile(mapval.Map{"cont": msg}), - commonFieldsValidator(testFields, "up"), + mapval.MustCompile(mapval.Map{ + "monitor": mapval.Map{ + "duration.us": mapval.IsDuration, + "id": uniqScope.IsUniqueTo(u), + "name": fields.name, + "type": fields.typ, + "status": "up", + "check_group": uniqScope.IsUniqueTo(u), + }, + }), ) } - tests := []struct { - name string - fields fields - jobs []jobs.Job - want []mapval.Validator - }{ - { - "simple", - testFields, - []jobs.Job{simpleJob}, - []mapval.Validator{ - mapval.Compose( - simpleJobValidator, - summaryValidator(1, 0), - )}, - }, - { - "job error", - testFields, - []jobs.Job{errorJob}, - []mapval.Validator{ - mapval.Compose( - errorJobValidator, - summaryValidator(0, 1), - )}, - }, - { - "multi-job", - testFields, - []jobs.Job{simpleJob, simpleJob}, - []mapval.Validator{ - simpleJobValidator, - mapval.Compose( - simpleJobValidator, - summaryValidator(2, 0), - ), - }, + testCommonWrap(t, testDef{ + "multi-job-continuations", + fields, + []jobs.Job{makeContJob(t, "http://foo.com"), makeContJob(t, "http://bar.com")}, + []mapval.Validator{ + contJobValidator("http://foo.com", "1st"), + mapval.Compose( + contJobValidator("http://foo.com", "2nd"), + summaryValidator(2, 0), + ), + contJobValidator("http://bar.com", "1st"), + mapval.Compose( + contJobValidator("http://bar.com", "2nd"), + summaryValidator(2, 0), + ), }, - { - "multi-job-continuations", - testFields, - []jobs.Job{contJob, contJob}, - []mapval.Validator{ - contJobValidator("1st"), - contJobValidator("2nd"), - contJobValidator("1st"), - mapval.Compose( - contJobValidator("2nd"), - commonFieldsValidator(testFields, "up"), - summaryValidator(4, 0), - ), - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - wrapped := WrapCommon(tt.jobs, tt.fields.id, tt.fields.name, tt.fields.typ) - - results, err := jobs.ExecJobsAndConts(t, wrapped) - assert.NoError(t, err) - - for idx, r := range results { - t.Run(fmt.Sprintf("result at index %d", idx), func(t *testing.T) { - mapvaltest.Test(t, mapval.Strict(tt.want[idx]), r.Fields) - }) - } - }) + }) +} + +func makeURLJob(t *testing.T, u string) jobs.Job { + parsed, err := url.Parse(u) + require.NoError(t, err) + return func(event *beat.Event) (i []jobs.Job, e error) { + eventext.MergeEventFields(event, common.MapStr{"url": URLFields(parsed)}) + return nil, nil } } + +func urlValidator(t *testing.T, u string) mapval.Validator { + parsed, err := url.Parse(u) + require.NoError(t, err) + return mapval.MustCompile(mapval.Map{"url": mapval.Map(URLFields(parsed))}) +} + +// This duplicates hbtest.SummaryChecks to avoid an import cycle. +// It could be refactored out, but it just isn't worth it. +func summaryValidator(up int, down int) mapval.Validator { + return mapval.MustCompile(mapval.Map{ + "summary": mapval.Map{ + "up": uint16(up), + "down": uint16(down), + }, + }) +} diff --git a/heartbeat/monitors/wrappers/util.go b/heartbeat/monitors/wrappers/util.go index 9244ee72cec..83778069d99 100644 --- a/heartbeat/monitors/wrappers/util.go +++ b/heartbeat/monitors/wrappers/util.go @@ -58,7 +58,15 @@ func URLFields(u *url.URL) common.MapStr { } if u.Port() != "" { - fields["port"], _ = strconv.ParseUint(u.Port(), 10, 16) + // Returns a uint64 believe it or not. + val, _ := strconv.ParseUint(u.Port(), 10, 16) + fields["port"] = uint16(val) + } else { + if u.Scheme == "http" { + fields["port"] = uint16(80) + } else if u.Scheme == "https" { + fields["port"] = uint16(443) + } } if u.Path != "" { diff --git a/heartbeat/monitors/wrappers/util_test.go b/heartbeat/monitors/wrappers/util_test.go index 573ab1dfebe..a374d4ac44b 100644 --- a/heartbeat/monitors/wrappers/util_test.go +++ b/heartbeat/monitors/wrappers/util_test.go @@ -25,7 +25,6 @@ import ( "github.com/elastic/beats/libbeat/common" "github.com/elastic/beats/libbeat/common/mapval" - "github.com/elastic/beats/libbeat/testing/mapvaltest" ) func TestURLFields(t *testing.T) { @@ -41,6 +40,7 @@ func TestURLFields(t *testing.T) { "full": "http://elastic.co", "scheme": "http", "domain": "elastic.co", + "port": uint16(80), }, }, { @@ -50,15 +50,17 @@ func TestURLFields(t *testing.T) { "full": "https://elastic.co", "scheme": "https", "domain": "elastic.co", + "port": uint16(443), }, }, { "fancy-proto", - "tcp+ssl://elastic.co", + "tcp+ssl://elastic.co:1234", common.MapStr{ - "full": "tcp+ssl://elastic.co", + "full": "tcp+ssl://elastic.co:1234", "scheme": "tcp+ssl", "domain": "elastic.co", + "port": uint16(1234), }, }, { @@ -68,7 +70,7 @@ func TestURLFields(t *testing.T) { "full": "tcp+ssl://myuser:%3Chidden%3E@elastic.co:65500/foo/bar?q=dosomething&x=y", "scheme": "tcp+ssl", "domain": "elastic.co", - "port": uint64(65500), + "port": uint16(65500), "path": "/foo/bar", "query": "q=dosomething&x=y", "username": "myuser", @@ -82,7 +84,7 @@ func TestURLFields(t *testing.T) { require.NoError(t, err) got := URLFields(parsed) - mapvaltest.Test(t, mapval.MustCompile(mapval.Map(tt.want)), got) + mapval.Test(t, mapval.MustCompile(mapval.Map(tt.want)), got) }) } } diff --git a/journalbeat/_meta/beat.yml b/journalbeat/_meta/beat.yml index 0b7b4fd64ab..2bd2c91ce91 100644 --- a/journalbeat/_meta/beat.yml +++ b/journalbeat/_meta/beat.yml @@ -44,3 +44,9 @@ journalbeat.inputs: # Name of the registry file. If a relative path is used, it is considered relative to the # data path. #registry_file: registry + +#==================== Elasticsearch template setting ========================== +setup.template.settings: + index.number_of_shards: 1 + #index.codec: best_compression + #_source.enabled: false diff --git a/journalbeat/docs/fields.asciidoc b/journalbeat/docs/fields.asciidoc index 368e6eb5806..c392cad930f 100644 --- a/journalbeat/docs/fields.asciidoc +++ b/journalbeat/docs/fields.asciidoc @@ -3672,16 +3672,6 @@ type: keyword Major version of the user agent. --- - -*`user_agent.device`*:: -+ --- -type: keyword - -Name of the physical device. - - -- *`user_agent.os.major`*:: @@ -3702,16 +3692,6 @@ type: long Minor version of the operating system. --- - -*`url.hostname`*:: -+ --- -type: keyword - -Hostname of the request, such as "elastic.co". - - -- [[exported-fields-host-processor]] diff --git a/journalbeat/include/fields.go b/journalbeat/include/fields.go index 05a31c8f515..7f852eee070 100644 --- a/journalbeat/include/fields.go +++ b/journalbeat/include/fields.go @@ -32,5 +32,5 @@ func init() { // AssetFieldsYml returns asset data. // This is the base64 encoded gzipped contents of fields.yml. func AssetFieldsYml() string { - return "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" + return "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" } diff --git a/journalbeat/journalbeat.reference.yml b/journalbeat/journalbeat.reference.yml index f31f7c8b2a9..006bfe9324c 100644 --- a/journalbeat/journalbeat.reference.yml +++ b/journalbeat/journalbeat.reference.yml @@ -45,6 +45,12 @@ journalbeat.inputs: # data path. #registry_file: registry +#==================== Elasticsearch template setting ========================== +setup.template.settings: + index.number_of_shards: 1 + #index.codec: best_compression + #_source.enabled: false + #================================ General ====================================== # The name of the shipper that publishes the network data. It can be used to group @@ -292,11 +298,6 @@ output.elasticsearch: # IPv6 addresses should always be defined as: https://[2001:db8::1]:9200 hosts: ["localhost:9200"] - # Enabled ilm (beta) to use index lifecycle management instead daily indices. - #ilm.enabled: false - #ilm.rollover_alias: "journalbeat" - #ilm.pattern: "{now/d}-000001" - # Set gzip compression level. #compression_level: 0 @@ -943,6 +944,25 @@ setup.template.settings: #_source: #enabled: false +#============================== Setup ILM ===================================== + +# Configure Index Lifecycle Management Index Lifecycle Management creates a +# write alias and adds additional settings to the template. +# The elasticsearch.output.index setting will be replaced with the write alias +# if ILM is enabled. + +# Enabled ILM support. Valid values are true, false, and auto. The beat will +# detect availabilty of Index Lifecycle Management in Elasticsearch and enable +# or disable ILM support. +#setup.ilm.enabled: auto + +# Configure the ILM write alias name. +#setup.ilm.rollover_alias: "journalbeat" + +# Configure rollover index pattern. +#setup.ilm.pattern: "{now/d}-000001" + + #============================== Kibana ===================================== # Starting with Beats version 6.0.0, the dashboards are loaded via the Kibana API. diff --git a/journalbeat/journalbeat.yml b/journalbeat/journalbeat.yml index ed12b9d1551..daa8b40e0e3 100644 --- a/journalbeat/journalbeat.yml +++ b/journalbeat/journalbeat.yml @@ -45,6 +45,12 @@ journalbeat.inputs: # data path. #registry_file: registry +#==================== Elasticsearch template setting ========================== +setup.template.settings: + index.number_of_shards: 1 + #index.codec: best_compression + #_source.enabled: false + #================================ General ===================================== # The name of the shipper that publishes the network data. It can be used to group @@ -64,7 +70,7 @@ journalbeat.inputs: #============================== Dashboards ===================================== # These settings control loading the sample dashboards to the Kibana index. Loading # the dashboards is disabled by default and can be enabled either by setting the -# options here, or by using the `-setup` CLI flag or the `setup` command. +# options here or by using the `setup` command. #setup.dashboards.enabled: false # The URL from where to download the dashboards archive. By default this URL @@ -112,9 +118,6 @@ output.elasticsearch: # Array of hosts to connect to. hosts: ["localhost:9200"] - # Enabled ilm (beta) to use index lifecycle management instead daily indices. - #ilm.enabled: false - # Optional protocol and basic auth credentials. #protocol: "https" #username: "elastic" diff --git a/libbeat/_meta/config.reference.yml b/libbeat/_meta/config.reference.yml index fb77508c109..c88d26a22f1 100644 --- a/libbeat/_meta/config.reference.yml +++ b/libbeat/_meta/config.reference.yml @@ -246,11 +246,6 @@ output.elasticsearch: # IPv6 addresses should always be defined as: https://[2001:db8::1]:9200 hosts: ["localhost:9200"] - # Enabled ilm (beta) to use index lifecycle management instead daily indices. - #ilm.enabled: false - #ilm.rollover_alias: "beat-index-prefix" - #ilm.pattern: "{now/d}-000001" - # Set gzip compression level. #compression_level: 0 @@ -897,6 +892,25 @@ setup.template.settings: #_source: #enabled: false +#============================== Setup ILM ===================================== + +# Configure Index Lifecycle Management Index Lifecycle Management creates a +# write alias and adds additional settings to the template. +# The elasticsearch.output.index setting will be replaced with the write alias +# if ILM is enabled. + +# Enabled ILM support. Valid values are true, false, and auto. The beat will +# detect availabilty of Index Lifecycle Management in Elasticsearch and enable +# or disable ILM support. +#setup.ilm.enabled: auto + +# Configure the ILM write alias name. +#setup.ilm.rollover_alias: "beat-index-prefix" + +# Configure rollover index pattern. +#setup.ilm.pattern: "{now/d}-000001" + + #============================== Kibana ===================================== # Starting with Beats version 6.0.0, the dashboards are loaded via the Kibana API. diff --git a/libbeat/_meta/config.yml b/libbeat/_meta/config.yml index af767564d8b..9364f344a39 100644 --- a/libbeat/_meta/config.yml +++ b/libbeat/_meta/config.yml @@ -18,7 +18,7 @@ #============================== Dashboards ===================================== # These settings control loading the sample dashboards to the Kibana index. Loading # the dashboards is disabled by default and can be enabled either by setting the -# options here, or by using the `-setup` CLI flag or the `setup` command. +# options here or by using the `setup` command. #setup.dashboards.enabled: false # The URL from where to download the dashboards archive. By default this URL @@ -66,9 +66,6 @@ output.elasticsearch: # Array of hosts to connect to. hosts: ["localhost:9200"] - # Enabled ilm (beta) to use index lifecycle management instead daily indices. - #ilm.enabled: false - # Optional protocol and basic auth credentials. #protocol: "https" #username: "elastic" diff --git a/libbeat/_meta/fields.ecs.yml b/libbeat/_meta/fields.ecs.yml index f12e29ce7df..ca3794ef8a8 100644 --- a/libbeat/_meta/fields.ecs.yml +++ b/libbeat/_meta/fields.ecs.yml @@ -2261,10 +2261,6 @@ type: keyword description: > Major version of the user agent. - - name: user_agent.device - type: keyword - description: > - Name of the physical device. - name: user_agent.os.major type: long description: > @@ -2273,10 +2269,3 @@ type: long description: > Minor version of the operating system. - - # url.hostname was removed from ECS. - # TODO: Update Suricata module to use url.domain. - - name: url.hostname - type: keyword - description: > - Hostname of the request, such as "elastic.co". diff --git a/libbeat/cfgfile/cfgfile.go b/libbeat/cfgfile/cfgfile.go index f2ff9ca1653..a973ffccfed 100644 --- a/libbeat/cfgfile/cfgfile.go +++ b/libbeat/cfgfile/cfgfile.go @@ -18,7 +18,6 @@ package cfgfile import ( - "flag" "fmt" "os" "path/filepath" @@ -34,7 +33,6 @@ var ( // be called prior to flags.Parse(). configfiles = common.StringArrFlag(nil, "c", "beat.yml", "Configuration file, relative to path.config") overwrites = common.SettingFlag(nil, "E", "Configuration overwrite") - testConfig = flag.Bool("configtest", false, "Test configuration and exit.") // Additional default settings, that must be available for variable expansion defaults = common.MustNewConfigFrom(map[string]interface{}{ @@ -199,8 +197,3 @@ func GetPathConfig() string { // TODO: Do we need this or should we always return *homePath? return "" } - -// IsTestConfig returns whether or not this is configuration used for testing -func IsTestConfig() bool { - return *testConfig -} diff --git a/libbeat/cmd/export.go b/libbeat/cmd/export.go index 07f9cd16da0..2bd37f33adb 100644 --- a/libbeat/cmd/export.go +++ b/libbeat/cmd/export.go @@ -33,7 +33,7 @@ func genExportCmd(settings instance.Settings, name, idxPrefix, beatVersion strin exportCmd.AddCommand(export.GenExportConfigCmd(settings, name, idxPrefix, beatVersion)) exportCmd.AddCommand(export.GenTemplateConfigCmd(settings, name, idxPrefix, beatVersion)) exportCmd.AddCommand(export.GenDashboardCmd(name, idxPrefix, beatVersion)) - exportCmd.AddCommand(export.GenGetILMPolicyCmd()) + exportCmd.AddCommand(export.GenGetILMPolicyCmd(settings, name, idxPrefix, beatVersion)) return exportCmd } diff --git a/libbeat/cmd/export/ilm_policy.go b/libbeat/cmd/export/ilm_policy.go index a9e4542fc29..9850da3527c 100644 --- a/libbeat/cmd/export/ilm_policy.go +++ b/libbeat/cmd/export/ilm_policy.go @@ -19,19 +19,43 @@ package export import ( "fmt" + "os" "github.com/spf13/cobra" "github.com/elastic/beats/libbeat/cmd/instance" + "github.com/elastic/beats/libbeat/common" + "github.com/elastic/beats/libbeat/idxmgmt/ilm" ) // GenGetILMPolicyCmd is the command used to export the ilm policy. -func GenGetILMPolicyCmd() *cobra.Command { +func GenGetILMPolicyCmd(settings instance.Settings, name, idxPrefix, version string) *cobra.Command { genTemplateConfigCmd := &cobra.Command{ Use: "ilm-policy", Short: "Export ILM policy", Run: func(cmd *cobra.Command, args []string) { - fmt.Println(instance.ILMPolicy.StringToPrint()) + b, err := instance.NewBeat(name, idxPrefix, version) + if err != nil { + fmt.Fprintf(os.Stderr, "Error initializing beat: %s\n", err) + os.Exit(1) + } + err = b.InitWithSettings(settings) + if err != nil { + fmt.Fprintf(os.Stderr, "Error initializing beat: %s\n", err) + os.Exit(1) + } + + ilmFactory := settings.ILM + if ilmFactory == nil { + ilmFactory = ilm.DefaultSupport + } + + ilm, err := ilmFactory(nil, b.Info, b.RawConfig) + if err != nil { + fmt.Fprintf(os.Stderr, "Error initializing ILM support: %s\n", err) + } + + fmt.Println(common.MapStr(ilm.Policy().Body).StringToPrint()) }, } diff --git a/libbeat/cmd/export/template.go b/libbeat/cmd/export/template.go index 1f4dea5751a..e804a0df5ce 100644 --- a/libbeat/cmd/export/template.go +++ b/libbeat/cmd/export/template.go @@ -25,6 +25,8 @@ import ( "github.com/elastic/beats/libbeat/cmd/instance" "github.com/elastic/beats/libbeat/common" + "github.com/elastic/beats/libbeat/idxmgmt" + "github.com/elastic/beats/libbeat/logp" "github.com/elastic/beats/libbeat/paths" "github.com/elastic/beats/libbeat/template" ) @@ -36,65 +38,83 @@ func GenTemplateConfigCmd(settings instance.Settings, name, idxPrefix, beatVersi Run: func(cmd *cobra.Command, args []string) { version, _ := cmd.Flags().GetString("es.version") index, _ := cmd.Flags().GetString("index") + noILM, _ := cmd.Flags().GetBool("noilm") b, err := instance.NewBeat(name, idxPrefix, beatVersion) if err != nil { - fmt.Fprintf(os.Stderr, "Error initializing beat: %s\n", err) - os.Exit(1) + fatalf("Error initializing beat: %+v", err) } err = b.InitWithSettings(settings) if err != nil { - fmt.Fprintf(os.Stderr, "Error initializing beat: %s\n", err) - os.Exit(1) - } - - cfg := template.DefaultConfig - if b.Config.Template.Enabled() { - err = b.Config.Template.Unpack(&cfg) - if err != nil { - fmt.Fprintf(os.Stderr, "Error getting template settings: %+v", err) - os.Exit(1) - } + fatalf("Error initializing beat: %+v", err) } if version == "" { version = b.Info.Version } - esVersion, err := common.NewVersion(version) if err != nil { - fmt.Fprintf(os.Stderr, "Invalid Elasticsearch version: %s\n", err) + fatalf("Invalid Elasticsearch version: %+v", err) + } + + imFactory := settings.IndexManagement + if imFactory == nil { + imFactory = idxmgmt.MakeDefaultSupport(settings.ILM) + } + indexManager, err := imFactory(logp.NewLogger("index-management"), b.Info, b.RawConfig) + if err != nil { + fatalf("Error initializing the index manager: %+v", err) + } + + tmplCfg, err := indexManager.TemplateConfig(!noILM) + if err != nil { + fatalf("Template error detected: %+v", err) + } + if tmplCfg.Enabled == false { + tmplCfg = template.DefaultConfig() + } + + var withMigration bool + if b.RawConfig.HasField("migration") { + sub, err := b.RawConfig.Child("migration", -1) + if err != nil { + fatalf("Failed to read migration setting: %+v", err) + } + withMigration = sub.Enabled() } - tmpl, err := template.New(b.Info.Version, index, *esVersion, cfg, b.Config.Migration.Enabled()) + tmpl, err := template.New(b.Info.Version, index, *esVersion, tmplCfg, withMigration) if err != nil { - fmt.Fprintf(os.Stderr, "Error generating template: %+v", err) - os.Exit(1) + fatalf("Error generating template: %+v", err) } var templateString common.MapStr - if cfg.Fields != "" { - fieldsPath := paths.Resolve(paths.Config, cfg.Fields) + if tmplCfg.Fields != "" { + fieldsPath := paths.Resolve(paths.Config, tmplCfg.Fields) templateString, err = tmpl.LoadFile(fieldsPath) } else { templateString, err = tmpl.LoadBytes(b.Fields) } - if err != nil { - fmt.Fprintf(os.Stderr, "Error generating template: %+v", err) - os.Exit(1) + fatalf("Error generating template: %+v", err) } _, err = os.Stdout.WriteString(templateString.StringToPrint() + "\n") if err != nil { - fmt.Fprintf(os.Stderr, "Error writing template: %+v", err) - os.Exit(1) + fatalf("Error writing template: %+v", err) } }, } genTemplateConfigCmd.Flags().String("es.version", beatVersion, "Elasticsearch version") genTemplateConfigCmd.Flags().String("index", idxPrefix, "Base index name") + genTemplateConfigCmd.Flags().Bool("noilm", false, "Generate template with ILM disabled") return genTemplateConfigCmd } + +func fatalf(msg string, vs ...interface{}) { + fmt.Fprintf(os.Stderr, msg, vs...) + fmt.Fprintln(os.Stderr) + os.Exit(1) +} diff --git a/libbeat/cmd/instance/beat.go b/libbeat/cmd/instance/beat.go index 3e0465d5eb6..765bc5316a4 100644 --- a/libbeat/cmd/instance/beat.go +++ b/libbeat/cmd/instance/beat.go @@ -28,7 +28,6 @@ import ( "math/big" "math/rand" "os" - "path/filepath" "runtime" "strings" "time" @@ -43,11 +42,11 @@ import ( "github.com/elastic/beats/libbeat/cfgfile" "github.com/elastic/beats/libbeat/cloudid" "github.com/elastic/beats/libbeat/common" - "github.com/elastic/beats/libbeat/common/cfgwarn" "github.com/elastic/beats/libbeat/common/file" "github.com/elastic/beats/libbeat/common/reload" "github.com/elastic/beats/libbeat/common/seccomp" "github.com/elastic/beats/libbeat/dashboards" + "github.com/elastic/beats/libbeat/idxmgmt" "github.com/elastic/beats/libbeat/keystore" "github.com/elastic/beats/libbeat/logp" "github.com/elastic/beats/libbeat/logp/configure" @@ -56,12 +55,12 @@ import ( "github.com/elastic/beats/libbeat/monitoring" "github.com/elastic/beats/libbeat/monitoring/report" "github.com/elastic/beats/libbeat/monitoring/report/log" + "github.com/elastic/beats/libbeat/outputs" "github.com/elastic/beats/libbeat/outputs/elasticsearch" "github.com/elastic/beats/libbeat/paths" "github.com/elastic/beats/libbeat/plugin" "github.com/elastic/beats/libbeat/publisher/pipeline" svc "github.com/elastic/beats/libbeat/service" - "github.com/elastic/beats/libbeat/template" "github.com/elastic/beats/libbeat/version" sysinfo "github.com/elastic/go-sysinfo" "github.com/elastic/go-sysinfo/types" @@ -74,7 +73,9 @@ type Beat struct { Config beatConfig RawConfig *common.Config // Raw config that can be unpacked to get Beat specific config data. - keystore keystore.Keystore + + keystore keystore.Keystore + index idxmgmt.Supporter } type beatConfig struct { @@ -103,26 +104,13 @@ type beatConfig struct { // elastic stack 'setup' configurations Dashboards *common.Config `config:"setup.dashboards"` - Template *common.Config `config:"setup.template"` Kibana *common.Config `config:"setup.kibana"` - Migration *common.Config `config:"migration"` - - // ILM Config options - ILM *common.Config `config:"output.elasticsearch.ilm"` } -var ( - printVersion bool - setup bool -) - var debugf = logp.MakeDebug("beat") func init() { initRand() - - flag.BoolVar(&printVersion, "version", false, "Print the version and exit") - flag.BoolVar(&setup, "setup", false, "Load sample Kibana dashboards and setup Machine Learning") } // initRand initializes the runtime random number generator seed using @@ -282,7 +270,7 @@ func (b *Beat) createBeater(bt beat.Creator) (beat.Beater, error) { logSystemInfo(b.Info) logp.Info("Setup Beat: %s; Version: %s", b.Info.Beat, b.Info.Version) - err = b.registerTemplateLoading() + err = b.registerESIndexManagement() if err != nil { return nil, err } @@ -320,13 +308,14 @@ func (b *Beat) createBeater(bt beat.Creator) (beat.Beater, error) { Logger: logp.L().Named("publisher"), }, b.Config.Pipeline, - b.Config.Output) + b.makeOutputFactory(b.Config.Output), + ) if err != nil { return nil, fmt.Errorf("error initializing publisher: %+v", err) } - reload.Register.MustRegister("output", pipeline.OutputReloader()) + reload.Register.MustRegister("output", b.makeOutputReloader(pipeline.OutputReloader())) // TODO: some beats race on shutdown with publisher.Stop -> do not call Stop yet, // but refine publisher to disconnect clients on stop automatically @@ -381,13 +370,6 @@ func (b *Beat) launch(settings Settings, bt beat.Creator) error { defer reporter.Stop() } - // If -configtest was specified, exit now prior to run. - if cfgfile.IsTestConfig() { - cfgwarn.Deprecate("6.0", "-configtest flag has been deprecated, use configtest subcommand") - fmt.Println("Config OK") - return beat.GracefulExit - } - ctx, cancel := context.WithCancel(context.Background()) svc.HandleSignals(beater.Stop, cancel) @@ -395,12 +377,6 @@ func (b *Beat) launch(settings Settings, bt beat.Creator) error { if err != nil { return err } - if setup && b.SetupMLCallback != nil { - err = b.SetupMLCallback(&b.Beat, b.Config.Kibana) - if err != nil { - return err - } - } logp.Info("%s start running.", b.Info.Beat) @@ -460,47 +436,29 @@ func (b *Beat) Setup(bt beat.Creator, settings SetupSettings) error { return err } - if settings.Template { + if settings.Template || settings.ILMPolicy { outCfg := b.Config.Output if outCfg.Name() != "elasticsearch" { - return fmt.Errorf("Template loading requested but the Elasticsearch output is not configured/enabled") - } - - if b.Config.ILM.Enabled() { - cfgwarn.Beta("Index lifecycle management is enabled which is in beta.") - - ilmCfg, err := getILMConfig(b) - if err != nil { - return err - } - - err = b.prepareILMTemplate(ilmCfg) - if err != nil { - return err - } + return fmt.Errorf("Index management requested but the Elasticsearch output is not configured/enabled") } esConfig := outCfg.Config() - if tmplCfg := b.Config.Template; tmplCfg == nil || tmplCfg.Enabled() { - loadCallback, err := b.templateLoadingCallback() - if err != nil { - return err - } - + if b.index.Enabled() { esClient, err := elasticsearch.NewConnectedClient(esConfig) if err != nil { return err } - // Load template - err = loadCallback(esClient) + // prepare index by loading templates, lifecycle policies and write aliases + + m := b.index.Manager(esClient, idxmgmt.BeatsAssets(b.Fields)) + err = m.Setup(settings.Template, settings.ILMPolicy) if err != nil { return err } } - - fmt.Println("Loaded index template") + fmt.Println("Index setup complete.") } if settings.Dashboard { @@ -537,29 +495,14 @@ func (b *Beat) Setup(bt beat.Creator, settings SetupSettings) error { fmt.Println("Loaded Ingest pipelines") } - if settings.ILMPolicy { - if err := b.loadILMPolicy(); err != nil { - return err - } - fmt.Println("Loaded Index Lifecycle Management (ILM) policy") - } - return nil }()) } -// handleFlags parses the command line flags. It handles the '-version' flag -// and invokes the HandleFlags callback if implemented by the Beat. +// handleFlags parses the command line flags. It invokes the HandleFlags +// callback if implemented by the Beat. func (b *Beat) handleFlags() error { flag.Parse() - - if printVersion { - cfgwarn.Deprecate("6.0", "-version flag has been deprecated, use version subcommand") - fmt.Printf("%s version %s (%s), libbeat %s\n", - b.Info.Beat, b.Info.Version, runtime.GOARCH, version.GetDefaultVersion()) - return beat.GracefulExit - } - return cfgfile.HandleFlags() } @@ -576,10 +519,7 @@ func (b *Beat) configure(settings Settings) error { // We have to initialize the keystore before any unpack or merging the cloud // options. - keystoreCfg, _ := cfg.Child("keystore", -1) - defaultPathConfig, _ := cfg.String("path.config", -1) - defaultPathConfig = filepath.Join(defaultPathConfig, fmt.Sprintf("%s.keystore", b.Info.Beat)) - store, err := keystore.Factory(keystoreCfg, defaultPathConfig) + store, err := LoadKeystore(cfg, b.Info.Beat) if err != nil { return fmt.Errorf("could not initialize the keystore: %v", err) } @@ -647,7 +587,12 @@ func (b *Beat) configure(settings Settings) error { return err } - return nil + imFactory := settings.IndexManagement + if imFactory == nil { + imFactory = idxmgmt.MakeDefaultSupport(settings.ILM) + } + b.index, err = imFactory(nil, b.Beat.Info, b.RawConfig) + return err } func (b *Beat) loadMeta() error { @@ -722,8 +667,8 @@ func openRegular(filename string) (*os.File, error) { } func (b *Beat) loadDashboards(ctx context.Context, force bool) error { - if setup || force { - // -setup implies dashboards.enabled=true + if force { + // force implies dashboards.enabled=true if b.Config.Dashboards == nil { b.Config.Dashboards = common.NewConfig() } @@ -734,13 +679,8 @@ func (b *Beat) loadDashboards(ctx context.Context, force bool) error { } if b.Config.Dashboards.Enabled() { - var esConfig *common.Config - - if b.Config.Output.Name() == "elasticsearch" { - esConfig = b.Config.Output.Config() - } err := dashboards.ImportDashboards(ctx, b.Info.Beat, b.Info.Hostname, paths.Resolve(paths.Home, ""), - b.Config.Kibana, esConfig, b.Config.Dashboards, nil) + b.Config.Kibana, b.Config.Dashboards, nil) if err != nil { return errw.Wrap(err, "Error importing Kibana dashboards") } @@ -750,159 +690,50 @@ func (b *Beat) loadDashboards(ctx context.Context, force bool) error { return nil } -// registerTemplateLoading registers the loading of the template as a callback with -// the elasticsearch output. It is important the the registration happens before -// the publisher is created. -func (b *Beat) registerTemplateLoading() error { - var templateCfg template.TemplateConfig - - // Check if outputting to file is enabled, and output to file if it is - if b.Config.Template.Enabled() { - err := b.Config.Template.Unpack(&templateCfg) - if err != nil { - return fmt.Errorf("unpacking template config fails: %v", err) - } - } - - // Loads template by default if esOutput is enabled - if b.Config.Output.Name() == "elasticsearch" { - - // Get ES Index name for comparison - esCfg := struct { - Index string `config:"index"` - }{} - err := b.Config.Output.Config().Unpack(&esCfg) - if err != nil { - return err - } - - if esCfg.Index != "" && - (templateCfg.Name == "" || templateCfg.Pattern == "") && - (b.Config.Template == nil || b.Config.Template.Enabled()) { - return errors.New("setup.template.name and setup.template.pattern have to be set if index name is modified") - } - - if b.Config.Template == nil || (b.Config.Template != nil && b.Config.Template.Enabled()) { - - // load template through callback to make sure it is also loaded - // on reconnecting - callback, err := b.templateLoadingCallback() - if err != nil { - return err - } - elasticsearch.RegisterConnectCallback(callback) - } else if b.Config.ILM.Enabled() { - return errors.New("templates cannot be disable when using ILM") - } - - if b.Config.ILM.Enabled() { - cfgwarn.Beta("Index lifecycle management is enabled which is in beta.") - - ilmCfg, err := getILMConfig(b) - if err != nil { - return err - } - - err = b.prepareILMTemplate(ilmCfg) - if err != nil { - return err - } - - // Set the ingestion index to the rollover alias - logp.Info("Set output.elasticsearch.index to '%s' as ILM is enabled.", ilmCfg.RolloverAlias) - esCfg.Index = ilmCfg.RolloverAlias - err = b.Config.Output.Config().SetString("index", -1, ilmCfg.RolloverAlias) - if err != nil { - return errw.Wrap(err, "error setting output.elasticsearch.index") - } - - writeAliasCallback, err := b.writeAliasLoadingCallback() - if err != nil { - return err - } - - // Load write alias already on - esConfig := b.Config.Output.Config() - - // Check that ILM is enabled and the right elasticsearch version exists - esClient, err := elasticsearch.NewConnectedClient(esConfig) - if err != nil { - return err - } - - err = checkElasticsearchVersionIlm(esClient) - if err != nil { - return err - } - - err = checkILMFeatureEnabled(esClient) - if err != nil { - return err - } - - elasticsearch.RegisterConnectCallback(writeAliasCallback) - } - } - - return nil -} - -func (b *Beat) prepareILMTemplate(ilmCfg *ilmConfig) error { - // In case no template settings are set, config must be created - if b.Config.Template == nil { - b.Config.Template = common.NewConfig() - } - // Template name and pattern can't be configure when using ILM - logp.Info("Set setup.template.name to '%s' as ILM is enabled.", ilmCfg.RolloverAlias) - err := b.Config.Template.SetString("name", -1, ilmCfg.RolloverAlias) - if err != nil { - return errw.Wrap(err, "error setting setup.template.name") - } - pattern := fmt.Sprintf("%s-*", ilmCfg.RolloverAlias) - logp.Info("Set setup.template.pattern to '%s' as ILM is enabled.", pattern) - err = b.Config.Template.SetString("pattern", -1, pattern) - if err != nil { - return errw.Wrap(err, "error setting setup.template.pattern") +// registerESIndexManagement registers the loading of the template and ILM +// policy as a callback with the elasticsearch output. It is important the +// registration happens before the publisher is created. +func (b *Beat) registerESIndexManagement() error { + if b.Config.Output.Name() != "elasticsearch" || !b.index.Enabled() { + return nil } - // rollover_alias and lifecycle.name can't be configured and will be overwritten - logp.Info("Set settings.index.lifecycle.rollover_alias in template to %s as ILM is enabled.", ilmCfg.RolloverAlias) - err = b.Config.Template.SetString("settings.index.lifecycle.rollover_alias", -1, ilmCfg.RolloverAlias) + _, err := elasticsearch.RegisterConnectCallback(b.indexSetupCallback()) if err != nil { - return errw.Wrap(err, "error setting settings.index.lifecycle.rollover_alias") + return fmt.Errorf("failed to register index management with elasticsearch: %+v", err) } - logp.Info("Set settings.index.lifecycle.name in template to %s as ILM is enabled.", ILMPolicyName) - err = b.Config.Template.SetString("settings.index.lifecycle.name", -1, ILMPolicyName) - if err != nil { - return errw.Wrap(err, "error setting settings.index.lifecycle.name") - } - return nil } // Build and return a callback to load index template into ES -func (b *Beat) templateLoadingCallback() (func(esClient *elasticsearch.Client) error, error) { - callback := func(esClient *elasticsearch.Client) error { - if b.Config.Template == nil { - b.Config.Template = common.NewConfig() - } - - loader, err := template.NewLoader(b.Config.Template, esClient, b.Info, b.Fields, b.Config.Migration.Enabled()) - if err != nil { - return fmt.Errorf("Error creating Elasticsearch template loader: %v", err) - } +func (b *Beat) indexSetupCallback() func(esClient *elasticsearch.Client) error { + return func(esClient *elasticsearch.Client) error { + m := b.index.Manager(esClient, idxmgmt.BeatsAssets(b.Fields)) + return m.Setup(true, true) + } +} - err = loader.Load() - if err != nil { - return fmt.Errorf("Error loading Elasticsearch template: %v", err) - } +func (b *Beat) makeOutputReloader(outReloader pipeline.OutputReloader) reload.Reloadable { + return reload.ReloadableFunc(func(config *reload.ConfigWithMeta) error { + return outReloader.Reload(config, b.createOutput) + }) +} - logp.Info("Template successfully loaded.") +func (b *Beat) makeOutputFactory( + cfg common.ConfigNamespace, +) func(outputs.Observer) (string, outputs.Group, error) { + return func(outStats outputs.Observer) (string, outputs.Group, error) { + out, err := b.createOutput(outStats, cfg) + return cfg.Name(), out, err + } +} - return nil +func (b *Beat) createOutput(stats outputs.Observer, cfg common.ConfigNamespace) (outputs.Group, error) { + if !cfg.IsSet() { + return outputs.Group{}, nil } - return callback, nil + return outputs.Load(b.index, b.Info, stats, cfg.Name(), cfg.Config()) } // handleError handles the given error by logging it and then returning the @@ -1008,3 +839,10 @@ func obfuscateConfigOpts() []ucfg.Option { ucfg.ResolveNOOP, } } + +// LoadKeystore returns the appropriate keystore based on the configuration. +func LoadKeystore(cfg *common.Config, name string) (keystore.Keystore, error) { + keystoreCfg, _ := cfg.Child("keystore", -1) + defaultPathConfig := paths.Resolve(paths.Data, fmt.Sprintf("%s.keystore", name)) + return keystore.Factory(keystoreCfg, defaultPathConfig) +} diff --git a/libbeat/cmd/instance/ilm.go b/libbeat/cmd/instance/ilm.go deleted file mode 100644 index 0ef1f9778a6..00000000000 --- a/libbeat/cmd/instance/ilm.go +++ /dev/null @@ -1,216 +0,0 @@ -// Licensed to Elasticsearch B.V. under one or more contributor -// license agreements. See the NOTICE file distributed with -// this work for additional information regarding copyright -// ownership. Elasticsearch B.V. licenses this file to you under -// the Apache License, Version 2.0 (the "License"); you may -// not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. - -package instance - -import ( - "encoding/json" - "fmt" - "net/url" - - "github.com/pkg/errors" - - "github.com/elastic/beats/libbeat/common" - "github.com/elastic/beats/libbeat/logp" - "github.com/elastic/beats/libbeat/outputs/elasticsearch" -) - -type ilmConfig struct { - RolloverAlias string `config:"ilm.rollover_alias" ` - Pattern string `config:"ilm.pattern"` -} - -// ILMPolicy contains the default policy -var ILMPolicy = common.MapStr{ - "policy": common.MapStr{ - "phases": common.MapStr{ - "hot": common.MapStr{ - "actions": common.MapStr{ - "rollover": common.MapStr{ - "max_size": "50gb", - "max_age": "30d", - }, - }, - }, - }, - }, -} - -const ( - // ILMPolicyName is the default policy name - ILMPolicyName = "beats-default-policy" - // ILMDefaultPattern is the default pattern - ILMDefaultPattern = "{now/d}-000001" -) - -// Build and return a callback to load ILM write alias -func (b *Beat) writeAliasLoadingCallback() (func(esClient *elasticsearch.Client) error, error) { - callback := func(esClient *elasticsearch.Client) error { - if b.Config.ILM == nil { - b.Config.ILM = common.NewConfig() - } - - config, err := getILMConfig(b) - if err != nil { - return err - } - - // Escaping because of date pattern - pattern := url.PathEscape(config.Pattern) - // This always assume it's a date pattern by sourrounding it by <...> - firstIndex := fmt.Sprintf("%%3C%s-%s%%3E", config.RolloverAlias, pattern) - - // Check if alias already exists - status, b, err := esClient.Request("HEAD", "/_alias/"+config.RolloverAlias, "", nil, nil) - if err != nil && status != 404 { - logp.Err("Failed to check for alias: %s: %+v", err, string(b)) - return errors.Wrap(err, "failed to check for alias") - } - if status == 200 { - logp.Info("Write alias already exists") - return nil - } - - body := common.MapStr{ - "aliases": common.MapStr{ - config.RolloverAlias: common.MapStr{ - "is_write_index": true, - }, - }, - } - - // Create alias with write index - code, res, err := esClient.Request("PUT", "/"+firstIndex, "", nil, body) - if code == 400 { - logp.Err("Error creating alias with write index. As return code is 400, assuming already exists: %s, %s", err, string(res)) - return nil - - } else if err != nil { - logp.Err("Error creating alias with write index: %s, %s", err, string(res)) - return errors.Wrap(err, "failed to create write alias: "+string(res)) - } - - logp.Info("Alias with write index created: %s", firstIndex) - - return nil - } - - return callback, nil -} - -func (b *Beat) loadILMPolicy() error { - esClient, err := getElasticsearchClient(b) - if err != nil { - return err - } - - err = checkElasticsearchVersionIlm(esClient) - if err != nil { - return err - } - - err = checkILMFeatureEnabled(esClient) - if err != nil { - return err - } - - _, _, err = esClient.Request("PUT", "/_ilm/policy/"+ILMPolicyName, "", nil, ILMPolicy) - return err -} - -func getElasticsearchClient(b *Beat) (*elasticsearch.Client, error) { - outCfg := b.Config.Output - if outCfg.Name() != "elasticsearch" { - return nil, fmt.Errorf("Policy loading requested but the Elasticsearch output is not configured/enabled") - } - - esConfig := outCfg.Config() - - return elasticsearch.NewConnectedClient(esConfig) -} - -func loadConfigWithDefaults(config *ilmConfig, b *Beat) { - if config.RolloverAlias == "" { - config.RolloverAlias = fmt.Sprintf("%s-%s", b.Info.Beat, b.Info.Version) - } - - if config.Pattern == "" { - config.Pattern = ILMDefaultPattern - } -} - -func checkElasticsearchVersionIlm(client *elasticsearch.Client) error { - esV := client.GetVersion() - requiredVersion, err := common.NewVersion("6.6.0") - if err != nil { - return err - } - - if esV.LessThan(requiredVersion) { - return fmt.Errorf("ILM requires at least Elasticsearch 6.6.0. Used version: %s", esV.String()) - } - - return nil -} - -func checkILMFeatureEnabled(client *elasticsearch.Client) error { - code, body, err := client.Request("GET", "/_xpack", "", nil, nil) - - // If we get a 400, it's assumed to be the OSS version of Elasticsearch - if code == 400 { - return fmt.Errorf("ILM feature is not available in this Elasticsearch version") - } - if err != nil { - return err - } - - var response struct { - Features struct { - ILM struct { - Available bool `json:"available"` - Enabled bool `json:"enabled"` - } `json:"ilm"` - } `json:"features"` - } - - err = json.Unmarshal(body, &response) - if err != nil { - return fmt.Errorf("failed to parse JSON response: %v", err) - } - - if !response.Features.ILM.Available { - return fmt.Errorf("ILM feature is not available in Elasticsearch") - } - - if !response.Features.ILM.Enabled { - return fmt.Errorf("ILM feature is not enabled in Elasticsearch") - } - - return nil -} - -func getILMConfig(b *Beat) (*ilmConfig, error) { - config := &ilmConfig{} - err := b.Config.Output.Config().Unpack(config) - if err != nil { - return nil, errors.Wrap(err, "problem unpacking ilm configs") - } - - loadConfigWithDefaults(config, b) - - return config, nil -} diff --git a/libbeat/cmd/instance/settings.go b/libbeat/cmd/instance/settings.go index 8275282daad..9b2bfd34a05 100644 --- a/libbeat/cmd/instance/settings.go +++ b/libbeat/cmd/instance/settings.go @@ -21,16 +21,23 @@ import ( "github.com/spf13/pflag" "github.com/elastic/beats/libbeat/common" + "github.com/elastic/beats/libbeat/idxmgmt" + "github.com/elastic/beats/libbeat/idxmgmt/ilm" "github.com/elastic/beats/libbeat/monitoring/report" ) // Settings contains basic settings for any beat to pass into GenRootCmd type Settings struct { - Name string - IndexPrefix string - Version string - Monitoring report.Settings - RunFlags *pflag.FlagSet - ConfigOverrides *common.Config + Name string + IndexPrefix string + Version string + Monitoring report.Settings + RunFlags *pflag.FlagSet + ConfigOverrides *common.Config + DisableConfigResolver bool + + // load custom index manager. The config object will be the Beats root configuration. + IndexManagement idxmgmt.SupportFactory + ILM ilm.SupportFactory } diff --git a/libbeat/cmd/run.go b/libbeat/cmd/run.go index 944b18e3cb5..4ebab4c6f7a 100644 --- a/libbeat/cmd/run.go +++ b/libbeat/cmd/run.go @@ -46,14 +46,6 @@ func genRunCmd(settings instance.Settings, beatCreator beat.Creator, runFlags *p runCmd.Flags().AddGoFlag(flag.CommandLine.Lookup("httpprof")) runCmd.Flags().AddGoFlag(flag.CommandLine.Lookup("cpuprofile")) runCmd.Flags().AddGoFlag(flag.CommandLine.Lookup("memprofile")) - runCmd.Flags().AddGoFlag(flag.CommandLine.Lookup("setup")) - - // TODO deprecate in favor of subcommands (7.0): - runCmd.Flags().AddGoFlag(flag.CommandLine.Lookup("configtest")) - runCmd.Flags().AddGoFlag(flag.CommandLine.Lookup("version")) - - runCmd.Flags().MarkDeprecated("version", "use version subcommand") - runCmd.Flags().MarkDeprecated("configtest", "use test config subcommand") if runFlags != nil { runCmd.Flags().AddFlagSet(runFlags) diff --git a/libbeat/cmd/test/output.go b/libbeat/cmd/test/output.go index a94f9053898..ee855d8688b 100644 --- a/libbeat/cmd/test/output.go +++ b/libbeat/cmd/test/output.go @@ -24,6 +24,7 @@ import ( "github.com/spf13/cobra" "github.com/elastic/beats/libbeat/cmd/instance" + "github.com/elastic/beats/libbeat/idxmgmt" "github.com/elastic/beats/libbeat/outputs" "github.com/elastic/beats/libbeat/testing" ) @@ -45,7 +46,8 @@ func GenTestOutputCmd(name, beatVersion string) *cobra.Command { os.Exit(1) } - output, err := outputs.Load(b.Info, nil, b.Config.Output.Name(), b.Config.Output.Config()) + im, _ := idxmgmt.DefaultSupport(nil, b.Info, nil) + output, err := outputs.Load(im, b.Info, nil, b.Config.Output.Name(), b.Config.Output.Config()) if err != nil { fmt.Fprintf(os.Stderr, "Error initializing output: %s\n", err) os.Exit(1) diff --git a/libbeat/common/config.go b/libbeat/common/config.go index 3bf2e653ce1..7cfca092245 100644 --- a/libbeat/common/config.go +++ b/libbeat/common/config.go @@ -188,6 +188,14 @@ func (c *Config) PathOf(field string) string { return c.access().PathOf(field, ".") } +func (c *Config) Remove(name string, idx int) (bool, error) { + return c.access().Remove(name, idx, configOpts...) +} + +func (c *Config) Has(name string, idx int) (bool, error) { + return c.access().Has(name, idx, configOpts...) +} + func (c *Config) HasField(name string) bool { return c.access().HasField(name) } diff --git a/libbeat/common/kafka/version.go b/libbeat/common/kafka/version.go index 6cc8d9cb369..1ca8472a97b 100644 --- a/libbeat/common/kafka/version.go +++ b/libbeat/common/kafka/version.go @@ -75,8 +75,10 @@ var ( "1": v1_1_1, "2.0.0": sarama.V2_0_0_0, - "2.0": sarama.V2_0_0_0, - "2": sarama.V2_0_0_0, + "2.0.1": sarama.V2_0_1_0, + "2.0": sarama.V2_0_1_0, + "2.1": sarama.V2_1_0_0, + "2": sarama.V2_1_0_0, } ) diff --git a/libbeat/common/kubernetes/metadata.go b/libbeat/common/kubernetes/metadata.go index d9698daad99..ad53378abac 100644 --- a/libbeat/common/kubernetes/metadata.go +++ b/libbeat/common/kubernetes/metadata.go @@ -55,8 +55,8 @@ func NewMetaGenerator(cfg *common.Config) (MetaGenerator, error) { // default settings: generator := metaGenerator{ IncludeCreatorMetadata: true, - LabelsDedot: false, - AnnotationsDedot: false, + LabelsDedot: true, + AnnotationsDedot: true, } err := cfg.Unpack(&generator) diff --git a/libbeat/common/kubernetes/metadata_test.go b/libbeat/common/kubernetes/metadata_test.go index a4717eb2fe4..8d0cd301a3e 100644 --- a/libbeat/common/kubernetes/metadata_test.go +++ b/libbeat/common/kubernetes/metadata_test.go @@ -35,9 +35,8 @@ func TestPodMetadata(t *testing.T) { True := true False := false tests := []struct { - pod *Pod - meta common.MapStr - config *common.Config + pod *Pod + meta common.MapStr }{ { pod: &Pod{ @@ -59,7 +58,6 @@ func TestPodMetadata(t *testing.T) { "namespace": "test", "labels": common.MapStr{"a": common.MapStr{"value": "bar", "key": "foo"}}, }, - config: common.NewConfig(), }, { pod: &Pod{ @@ -92,12 +90,17 @@ func TestPodMetadata(t *testing.T) { "labels": common.MapStr{"a": common.MapStr{"value": "bar", "key": "foo"}}, "deployment": common.MapStr{"name": "test"}, }, - config: common.NewConfig(), }, } for _, test := range tests { - metaGen, err := NewMetaGenerator(test.config) + config, err := common.NewConfigFrom(map[string]interface{}{ + "labels.dedot": false, + "annotations.dedot": false, + "include_annotations": []string{"b", "b.key"}, + }) + + metaGen, err := NewMetaGenerator(config) if err != nil { t.Fatal(err) } @@ -113,9 +116,8 @@ func TestPodMetadataDeDot(t *testing.T) { True := true False := false tests := []struct { - pod *Pod - meta common.MapStr - config *common.Config + pod *Pod + meta common.MapStr }{ { pod: &Pod{ @@ -139,7 +141,6 @@ func TestPodMetadataDeDot(t *testing.T) { "labels": common.MapStr{"a": "bar", "a_key": "foo"}, "annotations": common.MapStr{"b": "bar", "b_key": "foo"}, }, - config: common.NewConfig(), }, { pod: &Pod{ @@ -172,14 +173,11 @@ func TestPodMetadataDeDot(t *testing.T) { "labels": common.MapStr{"a": "bar", "a_key": "foo"}, "deployment": common.MapStr{"name": "test"}, }, - config: common.NewConfig(), }, } for _, test := range tests { config, err := common.NewConfigFrom(map[string]interface{}{ - "labels.dedot": true, - "annotations.dedot": true, "include_annotations": []string{"b", "b.key"}, }) metaGen, err := NewMetaGenerator(config) diff --git a/libbeat/common/mapval/is_defs.go b/libbeat/common/mapval/is_defs.go index d85369d4c95..457e85343dc 100644 --- a/libbeat/common/mapval/is_defs.go +++ b/libbeat/common/mapval/is_defs.go @@ -177,6 +177,34 @@ func IsAny(of ...IsDef) IsDef { }) } +// IsUnique instances are used in multiple spots, flagging a value as being in error if it's seen across invocations. +// To use it, assign IsUnique to a variable, then use that variable multiple times in a Map. +func IsUnique() IsDef { + return ScopedIsUnique().IsUniqueTo("") +} + +// UniqScopeTracker is represents the tracking data for invoking IsUniqueTo. +type UniqScopeTracker map[interface{}]string + +// IsUniqueTo validates that the given value is only ever seen within a single namespace. +func (ust UniqScopeTracker) IsUniqueTo(namespace string) IsDef { + return Is("unique", func(path path, v interface{}) *Results { + for trackerK, trackerNs := range ust { + hasNamespace := len(namespace) > 0 + if reflect.DeepEqual(trackerK, v) && (!hasNamespace || namespace != trackerNs) { + return SimpleResult(path, false, "Value '%v' is repeated", v) + } + } + + ust[v] = namespace + return ValidResult(path) + }) +} + +func ScopedIsUnique() UniqScopeTracker { + return UniqScopeTracker{} +} + // isStrCheck is a helper for IsDefs that must assert that the value is a string first. func isStrCheck(path path, v interface{}) (str string, errorResults *Results) { strV, ok := v.(string) diff --git a/libbeat/common/mapval/is_defs_test.go b/libbeat/common/mapval/is_defs_test.go index a6f0e6dcaaf..f50250595fd 100644 --- a/libbeat/common/mapval/is_defs_test.go +++ b/libbeat/common/mapval/is_defs_test.go @@ -23,6 +23,7 @@ import ( "time" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "github.com/elastic/beats/libbeat/common" ) @@ -152,3 +153,58 @@ func TestIsNil(t *testing.T) { assertIsDefValid(t, IsNil, nil) assertIsDefInvalid(t, IsNil, "foo") } + +func TestIsUnique(t *testing.T) { + tests := []struct { + name string + validator func() Validator + data common.MapStr + isValid bool + }{ + { + "IsUnique find dupes", + func() Validator { + v := IsUnique() + return MustCompile(Map{"a": v, "b": v}) + }, + common.MapStr{"a": 1, "b": 1}, + false, + }, + { + "IsUnique separate instances don't care about dupes", + func() Validator { return MustCompile(Map{"a": IsUnique(), "b": IsUnique()}) }, + common.MapStr{"a": 1, "b": 1}, + true, + }, + { + "IsUniqueTo duplicates across namespaces fail", + func() Validator { + s := ScopedIsUnique() + return MustCompile(Map{"a": s.IsUniqueTo("test"), "b": s.IsUniqueTo("test2")}) + }, + common.MapStr{"a": 1, "b": 1}, + false, + }, + + { + "IsUniqueTo duplicates within a namespace succeeds", + func() Validator { + s := ScopedIsUnique() + return MustCompile(Map{"a": s.IsUniqueTo("test"), "b": s.IsUniqueTo("test")}) + }, + common.MapStr{"a": 1, "b": 1}, + true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + + if tt.isValid { + Test(t, tt.validator(), tt.data) + } else { + result := tt.validator()(tt.data) + require.False(t, result.Valid) + } + }) + } +} diff --git a/libbeat/testing/mapvaltest/mapvaltest.go b/libbeat/common/mapval/testing.go similarity index 79% rename from libbeat/testing/mapvaltest/mapvaltest.go rename to libbeat/common/mapval/testing.go index 0701d4c61d5..e318b5d5c8a 100644 --- a/libbeat/testing/mapvaltest/mapvaltest.go +++ b/libbeat/common/mapval/testing.go @@ -15,11 +15,7 @@ // specific language governing permissions and limitations // under the License. -package mapvaltest - -// skimatest is a separate package from skima since we don't want to import "testing" -// into skima, since there is a good chance we'll use skima for running user-defined -// tests in heartbeat at runtime. +package mapval import ( "testing" @@ -28,12 +24,11 @@ import ( "github.com/stretchr/testify/assert" "github.com/elastic/beats/libbeat/common" - "github.com/elastic/beats/libbeat/common/mapval" ) // Test takes the output from a Validator invocation and runs test assertions on the result. // If you are using this library for testing you will probably want to run Test(t, Compile(Map{...}), actual) as a pattern. -func Test(t *testing.T, v mapval.Validator, m common.MapStr) *mapval.Results { +func Test(t *testing.T, v Validator, m common.MapStr) *Results { r := v(m) if !r.Valid { diff --git a/libbeat/common/reload/reload.go b/libbeat/common/reload/reload.go index 0e6933889e3..ceb377274b5 100644 --- a/libbeat/common/reload/reload.go +++ b/libbeat/common/reload/reload.go @@ -47,6 +47,9 @@ type Reloadable interface { Reload(config *ConfigWithMeta) error } +// ReloadableFunc wraps a custom function in order to implement the Reloadable interface. +type ReloadableFunc func(config *ConfigWithMeta) error + // Registry of reloadable objects and lists type Registry struct { sync.RWMutex @@ -152,3 +155,8 @@ func (r *Registry) nameTaken(name string) bool { return false } + +// Reload calls the underlying function. +func (fn ReloadableFunc) Reload(config *ConfigWithMeta) error { + return fn(config) +} diff --git a/libbeat/dashboards/dashboards.go b/libbeat/dashboards/dashboards.go index 833c7a1ebe8..79d664c3db8 100644 --- a/libbeat/dashboards/dashboards.go +++ b/libbeat/dashboards/dashboards.go @@ -28,23 +28,11 @@ import ( "github.com/elastic/beats/libbeat/common" ) -type importMethod uint8 - -// check import route -const ( - importNone importMethod = iota - importViaKibana - importViaES -) - // ImportDashboards tries to import the kibana dashboards. -// If the Elastic Stack is at version 6.0+, the dashboards should be installed -// via the kibana dashboard loader plugin. For older versions of the Elastic Stack -// we write the dashboards directly into the .kibana index. func ImportDashboards( ctx context.Context, beatName, hostname, homePath string, - kibanaConfig, esConfig, dashboardsConfig *common.Config, + kibanaConfig, dashboardsConfig *common.Config, msgOutputter MessageOutputter, ) error { if dashboardsConfig == nil || !dashboardsConfig.Enabled() { @@ -65,61 +53,12 @@ func ImportDashboards( kibanaConfig = common.NewConfig() } - if esConfig.Enabled() { - username, _ := esConfig.String("username", -1) - password, _ := esConfig.String("password", -1) - - if !kibanaConfig.HasField("username") && username != "" { - kibanaConfig.SetString("username", -1, username) - } - if !kibanaConfig.HasField("password") && password != "" { - kibanaConfig.SetString("password", -1, password) - } - } - - var esLoader *ElasticsearchLoader - - importVia := importNone - useKibana := importViaKibana if !kibanaConfig.Enabled() { - useKibana = importNone + return errors.New("kibana configuration missing for loading dashboards.") } - requiresKibana := dashConfig.AlwaysKibana || !esConfig.Enabled() - if requiresKibana { - importVia = useKibana - } else { - // Check import route via elasticsearch version. If Elasticsearch major - // version is >6, we assume Kibana also being at versions >6.0. In this - // case dashboards will be imported using the new kibana dashboard loader - // plugin. - // XXX(urso): Why do we test the Elasticsearch version? If kibana is - // configured, why not test the kibana version and plugin - // availability first? - esLoader, err = NewElasticsearchLoader(esConfig, &dashConfig, msgOutputter) - if err != nil { - return fmt.Errorf("fail to create the Elasticsearch loader: %v", err) - } - defer esLoader.Close() - - esLoader.statusMsg("Elasticsearch URL %v", esLoader.client.Connection.URL) - - if esLoader.version.Major < 6 { - importVia = importViaES - } else { - importVia = useKibana - } - } + return setupAndImportDashboardsViaKibana(ctx, hostname, kibanaConfig, &dashConfig, msgOutputter) - // Try to import dashboards. - switch importVia { - case importViaES: - return ImportDashboardsViaElasticsearch(esLoader) - case importViaKibana: - return setupAndImportDashboardsViaKibana(ctx, hostname, kibanaConfig, &dashConfig, msgOutputter) - default: - return errors.New("Elasticsearch or Kibana configuration missing for loading dashboards.") - } } func setupAndImportDashboardsViaKibana(ctx context.Context, hostname string, kibanaConfig *common.Config, @@ -159,26 +98,6 @@ func ImportDashboardsViaKibana(kibanaLoader *KibanaLoader) error { return nil } -func ImportDashboardsViaElasticsearch(esLoader *ElasticsearchLoader) error { - - if err := esLoader.CreateKibanaIndex(); err != nil { - return fmt.Errorf("fail to create the kibana index: %v", err) - } - - version, _ := common.NewVersion("5.0.0") - - importer, err := NewImporter(*version, esLoader.config, esLoader) - if err != nil { - return fmt.Errorf("fail to create an Elasticsearch importer for loading the dashboards: %v", err) - } - - if err := importer.Import(); err != nil { - return fmt.Errorf("fail to import the dashboards in Elasticsearch: %v", err) - } - - return nil -} - func isKibanaAPIavailable(version common.Version) bool { return (version.Major == 5 && version.Minor >= 6) || version.Major >= 6 } diff --git a/libbeat/dashboards/es_loader.go b/libbeat/dashboards/es_loader.go deleted file mode 100644 index 4787bb3faf5..00000000000 --- a/libbeat/dashboards/es_loader.go +++ /dev/null @@ -1,335 +0,0 @@ -// Licensed to Elasticsearch B.V. under one or more contributor -// license agreements. See the NOTICE file distributed with -// this work for additional information regarding copyright -// ownership. Elasticsearch B.V. licenses this file to you under -// the Apache License, Version 2.0 (the "License"); you may -// not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. - -package dashboards - -import ( - "encoding/json" - "errors" - "fmt" - "io/ioutil" - "path" - "path/filepath" - "strings" - - "github.com/elastic/beats/libbeat/common" - "github.com/elastic/beats/libbeat/logp" - "github.com/elastic/beats/libbeat/outputs/elasticsearch" -) - -type ElasticsearchLoader struct { - client *elasticsearch.Client - config *Config - version common.Version - msgOutputter MessageOutputter -} - -func NewElasticsearchLoader(cfg *common.Config, dashboardsConfig *Config, msgOutputter MessageOutputter) (*ElasticsearchLoader, error) { - if cfg == nil || !cfg.Enabled() { - return nil, fmt.Errorf("Elasticsearch output is not configured/enabled") - } - - esClient, err := elasticsearch.NewConnectedClient(cfg) - if err != nil { - return nil, fmt.Errorf("Error creating Elasticsearch client: %v", err) - } - - version := esClient.GetVersion() - if !version.IsValid() { - return nil, errors.New("No valid Elasticsearch version available") - } - - loader := ElasticsearchLoader{ - client: esClient, - config: dashboardsConfig, - version: version, - msgOutputter: msgOutputter, - } - - loader.statusMsg("Initialize the Elasticsearch %s loader", version.String()) - - return &loader, nil -} - -// CreateKibanaIndex creates the kibana index if it doesn't exists and sets -// some index properties which are needed as a workaround for: -// https://github.com/elastic/beats-dashboards/issues/94 -func (loader ElasticsearchLoader) CreateKibanaIndex() error { - status, err := loader.client.IndexExists(loader.config.KibanaIndex) - - if err != nil { - if status != 404 { - return err - } - - _, _, err = loader.client.CreateIndex(loader.config.KibanaIndex, nil) - if err != nil { - return fmt.Errorf("Failed to create index: %v", err) - } - - _, _, err = loader.client.CreateIndex(loader.config.KibanaIndex+"/_mapping/search", - common.MapStr{ - "search": common.MapStr{ - "properties": common.MapStr{ - "hits": common.MapStr{ - "type": "integer", - }, - "version": common.MapStr{ - "type": "integer", - }, - }, - }, - }) - if err != nil { - return fmt.Errorf("Failed to set the mapping: %v", err) - } - } - - return nil -} - -func (loader ElasticsearchLoader) ImportIndex(file string) error { - reader, err := ioutil.ReadFile(file) - if err != nil { - return err - } - var indexContent common.MapStr - err = json.Unmarshal(reader, &indexContent) - if err != nil { - return fmt.Errorf("fail to unmarshal index content: %v", err) - } - - indexName, ok := indexContent["title"].(string) - if !ok { - return fmt.Errorf("Missing title in the index-pattern file at %s", file) - } - - if loader.config.Index != "" { - // change index pattern name - loader.statusMsg("Change index in index-pattern %s", indexName) - indexContent["title"] = loader.config.Index - } - - path := "/" + loader.config.KibanaIndex + "/index-pattern/" + indexName - - if _, err = loader.client.LoadJSON(path, indexContent); err != nil { - return err - } - - return nil -} - -func (loader ElasticsearchLoader) importJSONFile(fileType string, file string) error { - path := "/" + loader.config.KibanaIndex + "/" + fileType - - reader, err := ioutil.ReadFile(file) - if err != nil { - return fmt.Errorf("Failed to read %s. Error: %s", file, err) - } - var jsonContent map[string]interface{} - err = json.Unmarshal(reader, &jsonContent) - if err != nil { - return fmt.Errorf("fail to unmarshal json file: %v", err) - } - - fileBase := strings.TrimSuffix(filepath.Base(file), filepath.Ext(file)) - - body, err := loader.client.LoadJSON(path+"/"+fileBase, jsonContent) - if err != nil { - return fmt.Errorf("Failed to load %s under %s/%s: %s. Response body: %s", file, path, fileBase, err, body) - } - - return nil -} - -func (loader ElasticsearchLoader) importPanelsFromDashboard(file string) (err error) { - // directory with the dashboards - dir := filepath.Dir(file) - - // main directory with dashboard, search, visualizations directories - mainDir := filepath.Dir(dir) - - reader, err := ioutil.ReadFile(file) - if err != nil { - return - } - type record struct { - Title string `json:"title"` - PanelsJSON string `json:"panelsJSON"` - } - type panel struct { - ID string `json:"id"` - Type string `json:"type"` - } - - var jsonContent record - err = json.Unmarshal(reader, &jsonContent) - if err != nil { - return fmt.Errorf("fail to unmarshal json content: %v", err) - } - - var widgets []panel - err = json.Unmarshal([]byte(jsonContent.PanelsJSON), &widgets) - if err != nil { - return fmt.Errorf("fail to unmarshal panels content: %v", err) - } - - for _, widget := range widgets { - if widget.Type == "visualization" { - err = loader.importVisualization(path.Join(mainDir, "visualization", widget.ID+".json")) - if err != nil { - return err - } - } else if widget.Type == "search" { - err = loader.importSearch(path.Join(mainDir, "search", widget.ID+".json")) - if err != nil { - return err - } - } else { - loader.statusMsg("Widgets: %v", widgets) - return fmt.Errorf("Unknown panel type %s in %s", widget.Type, file) - } - } - return -} - -func (loader ElasticsearchLoader) importVisualization(file string) error { - loader.statusMsg("Import visualization %s", file) - reader, err := ioutil.ReadFile(file) - if err != nil { - return err - } - var vizContent common.MapStr - err = json.Unmarshal(reader, &vizContent) - if err != nil { - return fmt.Errorf("fail to unmarshal visualization content %s: %v", file, err) - } - - if loader.config.Index != "" { - if savedObject, ok := vizContent["kibanaSavedObjectMeta"].(map[string]interface{}); ok { - vizContent["kibanaSavedObjectMeta"] = ReplaceIndexInSavedObject(loader.config.Index, savedObject) - } - - if visState, ok := vizContent["visState"].(string); ok { - vizContent["visState"] = ReplaceIndexInVisState(loader.config.Index, visState) - } - } - - vizName := strings.TrimSuffix(filepath.Base(file), filepath.Ext(file)) - path := "/" + loader.config.KibanaIndex + "/visualization/" + vizName - if _, err := loader.client.LoadJSON(path, vizContent); err != nil { - return err - } - - return loader.importSearchFromVisualization(file) -} - -func (loader ElasticsearchLoader) importSearch(file string) error { - reader, err := ioutil.ReadFile(file) - if err != nil { - return err - } - searchName := strings.TrimSuffix(filepath.Base(file), filepath.Ext(file)) - - var searchContent common.MapStr - err = json.Unmarshal(reader, &searchContent) - if err != nil { - return fmt.Errorf("fail to unmarshal search content %s: %v", searchName, err) - } - - if loader.config.Index != "" { - - // change index pattern name - if savedObject, ok := searchContent["kibanaSavedObjectMeta"].(map[string]interface{}); ok { - - searchContent["kibanaSavedObjectMeta"] = ReplaceIndexInSavedObject(loader.config.Index, savedObject) - - } - } - - path := "/" + loader.config.KibanaIndex + "/search/" + searchName - loader.statusMsg("Import search %s", file) - - if _, err = loader.client.LoadJSON(path, searchContent); err != nil { - return err - } - - return nil -} - -func (loader ElasticsearchLoader) importSearchFromVisualization(file string) error { - type record struct { - Title string `json:"title"` - SavedSearchID string `json:"savedSearchId"` - } - - reader, err := ioutil.ReadFile(file) - if err != nil { - return nil - } - - var jsonContent record - err = json.Unmarshal(reader, &jsonContent) - if err != nil { - return fmt.Errorf("fail to unmarshal the search content: %v", err) - } - - id := jsonContent.SavedSearchID - if len(id) == 0 { - // no search used - return nil - } - - // directory with the visualizations - dir := filepath.Dir(file) - - // main directory - mainDir := filepath.Dir(dir) - - searchFile := path.Join(mainDir, "search", id+".json") - - if searchFile != "" { - // visualization depends on search - if err := loader.importSearch(searchFile); err != nil { - return err - } - } - return nil -} - -func (loader ElasticsearchLoader) ImportDashboard(file string) error { - /* load dashboard */ - err := loader.importJSONFile("dashboard", file) - if err != nil { - return err - } - - /* load the visualizations and searches that depend on the dashboard */ - return loader.importPanelsFromDashboard(file) -} - -func (loader ElasticsearchLoader) Close() error { - return loader.client.Close() -} - -func (loader ElasticsearchLoader) statusMsg(msg string, a ...interface{}) { - if loader.msgOutputter != nil { - loader.msgOutputter(msg, a...) - } else { - logp.Debug("dashboards", msg, a...) - } -} diff --git a/libbeat/dashboards/es_loader_test.go b/libbeat/dashboards/es_loader_test.go deleted file mode 100644 index 29f5684da90..00000000000 --- a/libbeat/dashboards/es_loader_test.go +++ /dev/null @@ -1,99 +0,0 @@ -// Licensed to Elasticsearch B.V. under one or more contributor -// license agreements. See the NOTICE file distributed with -// this work for additional information regarding copyright -// ownership. Elasticsearch B.V. licenses this file to you under -// the Apache License, Version 2.0 (the "License"); you may -// not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. - -// +build integration - -package dashboards - -import ( - "testing" - - "github.com/stretchr/testify/assert" - - "github.com/elastic/beats/libbeat/common" - "github.com/elastic/beats/libbeat/logp" - "github.com/elastic/beats/libbeat/outputs/elasticsearch/estest" -) - -func TestImporter(t *testing.T) { - logp.TestingSetup() - - dashboardsConfig := Config{ - KibanaIndex: ".kibana-test", - File: "testdata/testbeat-dashboards.zip", - Beat: "testbeat", - } - - client := estest.GetTestingElasticsearch(t) - major := client.GetVersion().Major - - if major == 6 || major == 7 { - t.Skip("Skipping tests for Elasticsearch 6.x releases") - } - - loader := ElasticsearchLoader{ - client: client, - config: &dashboardsConfig, - } - - err := loader.CreateKibanaIndex() - - assert.NoError(t, err) - - version, _ := common.NewVersion("5.0.0") - - imp, err := NewImporter(*version, &dashboardsConfig, loader) - assert.NoError(t, err) - - err = imp.Import() - assert.NoError(t, err) - - status, _, _ := client.Request("GET", "/.kibana-test/dashboard/1e4389f0-e871-11e6-911d-3f8ed6f72700", "", nil, nil) - assert.Equal(t, 200, status) -} - -func TestImporterEmptyBeat(t *testing.T) { - logp.TestingSetup() - - dashboardsConfig := Config{ - KibanaIndex: ".kibana-test-nobeat", - File: "testdata/testbeat-dashboards.zip", - Beat: "", - } - - client := estest.GetTestingElasticsearch(t) - major := client.GetVersion().Major - if major == 6 || major == 7 { - t.Skip("Skipping tests for Elasticsearch 6.x releases") - } - - loader := ElasticsearchLoader{ - client: client, - config: &dashboardsConfig, - } - - version, _ := common.NewVersion("5.0.0") - - imp, err := NewImporter(*version, &dashboardsConfig, loader) - assert.NoError(t, err) - - err = imp.Import() - assert.NoError(t, err) - - status, _, _ := client.Request("GET", "/.kibana-test-nobeat/dashboard/1e4389f0-e871-11e6-911d-3f8ed6f72700", "", nil, nil) - assert.Equal(t, 200, status) -} diff --git a/libbeat/docs/command-reference.asciidoc b/libbeat/docs/command-reference.asciidoc index 014c321caf7..ecb5408a83e 100644 --- a/libbeat/docs/command-reference.asciidoc +++ b/libbeat/docs/command-reference.asciidoc @@ -15,7 +15,15 @@ :global-flags: Also see <>. :deploy-command-short-desc: Deploys the specified function to your serverless environment + +ifndef::no_dashboards[] :export-command-short-desc: Exports the configuration, index template, or a dashboard to stdout +endif::no_dashboards[] + +ifdef::no_dashboards[] +:export-command-short-desc: Exports the configuration or index template to stdout +endif::no_dashboards[] + :help-command-short-desc: Shows help for any command :keystore-command-short-desc: Manages the <> :modules-command-short-desc: Manages configured modules @@ -23,22 +31,16 @@ :remove-command-short-desc: Removes the specified function from your serverless environment :run-command-short-desc: Runs {beatname_uc}. This command is used by default if you start {beatname_uc} without specifying a command -ifndef::deprecate_dashboard_loading[] - ifdef::has_ml_jobs[] :setup-command-short-desc: Sets up the initial environment, including the index template, {kib} dashboards (when available), and machine learning jobs (when available) endif::[] -ifndef::has_ml_jobs[] -:setup-command-short-desc: Sets up the initial environment, including the index template and {kib} dashboards (when available) -endif::[] - -endif::[] - -ifdef::deprecate_dashboard_loading[] - -:setup-command-short-desc: Sets up the initial environment, including the ES index template and {kib} dashboards (deprecated). +ifdef::no_dashboards[] +:setup-command-short-desc: Sets up the initial environment, including the ES index template +endif::no_dashboards[] +ifndef::has_ml_jobs,no_dashboards[] +:setup-command-short-desc: Sets up the initial environment, including the index template and {kib} dashboards (when available) endif::[] :update-command-short-desc: Updates the specified function @@ -53,15 +55,15 @@ endif::[] Command reference ++++ -ifndef::deprecate_dashboard_loading[] +ifndef::no_dashboards[] {beatname_uc} provides a command-line interface for starting {beatname_uc} and performing common tasks, like testing configuration files and loading dashboards. -endif::[] +endif::no_dashboards[] -ifdef::deprecate_dashboard_loading[] +ifdef::no_dashboards[] {beatname_uc} provides a command-line interface for starting {beatname_uc} and -performing common tasks, like testing configuration files and loading dashboards (deprecated). -endif::[] +performing common tasks, like testing configuration files. +endif::no_dashboards[] The command-line also supports <> for controlling global behaviors. @@ -140,9 +142,17 @@ endif::[] [[export-command]] ==== `export` command +ifndef::no_dashboards[] {export-command-short-desc}. You can use this command to quickly view your configuration, see the contents of the index template, or export a dashboard from {kib}. +endif::no_dashboards[] + +ifdef::no_dashboards[] +{export-command-short-desc}. You can use this +command to quickly view your configuration or see the contents of the index +template. +endif::no_dashboards[] *SYNOPSIS* @@ -157,6 +167,7 @@ template, or export a dashboard from {kib}. Exports the current configuration to stdout. If you use the `-c` flag, this command exports the configuration that's defined in the specified file. +ifndef::no_dashboards[] [[dashboard-subcommand]]*`dashboard`*:: Exports a dashboard. You can use this option to store a dashboard on disk in a module and load it automatically. For example, to export the dashboard to a JSON @@ -179,6 +190,7 @@ To load the dashboard, copy the generated `dashboard.json` file into the + If {kib} is not running on `localhost:5061`, you must also adjust the {beatname_uc} configuration under `setup.kibana`. +endif::no_dashboards[] [[template-subcommand]]*`template`*:: Exports the index template to stdout. You can specify the `--es.version` and @@ -202,20 +214,31 @@ When used with <>, sets the base name to use for the index template. If this flag is not specified, the default base name is +{beatname_lc}+. +ifndef::no_dashboards[] *`--id DASHBOARD_ID`*:: When used with <>, specifies the dashboard ID. +endif::no_dashboards[] {global-flags} *EXAMPLES* +ifndef::no_dashboards[] ["source","sh",subs="attributes"] ----- {beatname_lc} export config {beatname_lc} export template --es.version {stack-version} --index myindexname {beatname_lc} export dashboard --id="a7b35890-8baa-11e8-9676-ef67484126fb" > dashboard.json ----- +endif::no_dashboards[] +ifdef::no_dashboards[] +["source","sh",subs="attributes"] +----- +{beatname_lc} export config +{beatname_lc} export template --es.version {stack-version} --index myindexname +----- +endif::no_dashboards[] [[help-command]] ==== `help` command @@ -523,21 +546,6 @@ the end of the file is reached. By default harvesters are closed after `close_inactive` is reached. endif::[] -*`--setup`*:: -ifdef::deprecate_dashboard_loading[] -deprecated[{deprecate_dashboard_loading}] -endif::[] -+ -ifdef::has_ml_jobs[] -Loads the initial setup, including Elasticsearch template, {kib} index pattern, -{kib} dashboards (when available), and Machine learning jobs. -endif::[] -ifndef::has_ml_jobs[] -Loads the initial setup, including Elasticsearch template, {kib} index pattern, and {kib} dashboards (when available). -endif::[] -If you want to use the command without running {beatname_uc}, use the <> command instead. - - ifeval::["{beatname_lc}"=="metricbeat"] *`--system.hostfs MOUNT_POINT`*:: @@ -558,14 +566,14 @@ endif::[] ["source","sh",subs="attributes"] ----- -{beatname_lc} run -e --setup +{beatname_lc} run -e ----- Or: ["source","sh",subs="attributes"] ----- -{beatname_lc} -e --setup +{beatname_lc} -e ----- [[setup-command]] @@ -575,16 +583,18 @@ Or: * The index template ensures that fields are mapped correctly in Elasticsearch. +ifndef::no_dashboards[] * The {kib} dashboards make it easier for you to visualize {beatname_uc} data in {kib}. +endif::no_dashboards[] ifdef::has_ml_jobs[] * The machine learning jobs contain the configuration information and metadata necessary to analyze data for anomalies. endif::[] -Use this command instead of `run --setup` when you want to set up the -environment without actually running {beatname_uc} and ingesting data. +Use this command if you want to set up the environment without actually running +{beatname_uc} and ingesting data. *SYNOPSIS* @@ -596,21 +606,13 @@ environment without actually running {beatname_uc} and ingesting data. *FLAGS* -ifndef::deprecate_dashboard_loading[] +ifndef::no_dashboards[] *`--dashboards`*:: Sets up the {kib} dashboards (when available). This option loads the dashboards from the {beatname_uc} package. For more options, such as loading customized dashboards, see {beatsdevguide}/import-dashboards.html[Importing Existing Beat Dashboards] in the _Beats Developer Guide_. -endif::[] - -ifdef::deprecate_dashboard_loading[] -*`--dashboards`*:: - -deprecated[{deprecate_dashboard_loading}] -+ -Sets up the {kib} dashboards only. -endif::[] +endif::no_dashboards[] *`-h, --help`*:: Shows help for the `setup` command. diff --git a/libbeat/docs/communitybeats.asciidoc b/libbeat/docs/communitybeats.asciidoc index d9d7615bfec..b5254947a30 100644 --- a/libbeat/docs/communitybeats.asciidoc +++ b/libbeat/docs/communitybeats.asciidoc @@ -93,9 +93,11 @@ https://github.com/consulthys/retsbeat[retsbeat]:: Collects counts of http://www https://github.com/yourdream/rsbeat[rsbeat]:: Ships redis slow logs to elasticsearch and analyze by Kibana. https://github.com/martinhoefling/saltbeat[saltbeat]:: Reads events from salt master event bus. https://github.com/benben/serialbeat[serialbeat]:: Reads from a serial device. +https://github.com/Corwind/servicebeat[servicebeat]:: Send services status to Elasticsearch https://github.com/consulthys/springbeat[springbeat]:: Collects health and metrics data from Spring Boot applications running with the actuator module. https://github.com/philkra/springboot2beat[springboot2beat]:: Query and accumulate all metrics endpoints of a Spring Boot 2 web app via the web channel, leveraging the http://micrometer.io/[mircometer.io] metrics facade. https://github.com/sentient/statsdbeat[statsdbeat]:: Receives UDP https://github.com/etsy/statsd/wiki[statsd] events from a statsd client. +https://github.com/Corwind/supervisorctlbeat.git[supervisorctlbeat]:: This beat aims to parse the supervisorctl status command output and send it to elasticsearch for indexation https://github.com/live-wire/terminalbeat[terminalbeat]:: Runs an external command and forwards the https://www.computerhope.com/jargon/s/stdout.htm[stdout] for the same to Elasticsearch/Logstash. https://github.com/berfinsari/tracebeat[tracebeat]:: Reads traceroute output and indexes them into Elasticsearch. https://github.com/buehler/go-elastic-twitterbeat[twitterbeat]:: Reads tweets for specified screen names. diff --git a/libbeat/docs/config-file-format.asciidoc b/libbeat/docs/config-file-format.asciidoc index 98faf676e16..f1062e58271 100644 --- a/libbeat/docs/config-file-format.asciidoc +++ b/libbeat/docs/config-file-format.asciidoc @@ -77,11 +77,11 @@ For example this setting: output: elasticsearch: - index: 'beat-%{[agent.version]}-%{+yyyy.MM.dd}' + index: 'beat-%{[{beat_version_key}]}-%{+yyyy.MM.dd}' ------------------------------------------------------------------------------ -gets collapsed into `output.elasticsearch.index: 'beat-%{[agent.version]}-%{+yyyy.MM.dd}'`. The +gets collapsed into +output.elasticsearch.index: \'beat-%{[{beat_version_key}]}-%{+yyyy.MM.dd}\'+. The full name of a setting is based on all parent structures involved. Lists create numeric names starting with 0. diff --git a/libbeat/docs/dashboardsconfig.asciidoc b/libbeat/docs/dashboardsconfig.asciidoc index 83099f84d16..fbc9f1a202d 100644 --- a/libbeat/docs/dashboardsconfig.asciidoc +++ b/libbeat/docs/dashboardsconfig.asciidoc @@ -11,11 +11,6 @@ [[configuration-dashboards]] == Load the Kibana dashboards -ifdef::deprecate_dashboard_loading[] - -deprecated[{deprecate_dashboard_loading}] - -endif::[] {beatname_uc} comes packaged with example Kibana dashboards, visualizations, and searches for visualizing {beatname_uc} data in Kibana. diff --git a/libbeat/docs/generalconfig.asciidoc b/libbeat/docs/generalconfig.asciidoc index a3f7426160a..2bc385c68cf 100644 --- a/libbeat/docs/generalconfig.asciidoc +++ b/libbeat/docs/generalconfig.asciidoc @@ -29,7 +29,7 @@ tags: ["service-X", "web-tier"] ==== `name` The name of the Beat. If this option is empty, the `hostname` of the server is -used. The name is included as the `beat.name` field in each published transaction. You can +used. The name is included as the `agent.name` field in each published transaction. You can use the name to group all transactions sent by a single Beat. Example: diff --git a/libbeat/docs/outputconfig.asciidoc b/libbeat/docs/outputconfig.asciidoc index 1e1e82604c3..c6254625434 100644 --- a/libbeat/docs/outputconfig.asciidoc +++ b/libbeat/docs/outputconfig.asciidoc @@ -60,7 +60,7 @@ Example configuration: output.elasticsearch: hosts: ["https://localhost:9200"] - index: "{beatname_lc}-%{[agent.version]}-%{+yyyy.MM.dd}" + index: "{beatname_lc}-%{[{beat_version_key}]}-%{+yyyy.MM.dd}" ssl.certificate_authorities: ["/etc/pki/root/ca.pem"] ssl.certificate: "/etc/pki/client/cert.pem" ssl.key: "/etc/pki/client/cert.key" @@ -92,10 +92,12 @@ output.elasticsearch: For more information about securing {beatname_uc}, see <>. +ifndef::no_ilm[] If you are indexing large amounts of time-series data, you might also want to configure {beatname_uc} to use index lifecycle management. For more information about configuring and using index lifecycle management with {beatname_uc}, see <>. +endif::no_ilm[] ==== Compatibility @@ -217,7 +219,7 @@ for more information about the environment variables. ifndef::apm-server[] The index name to write events to. The default is -+"{beatname_lc}-%\{[agent.version]\}-%\{+yyyy.MM.dd\}"+ (for example, ++"{beatname_lc}-%\{[{beat_version_key}]\}-%\{+yyyy.MM.dd\}"+ (for example, +"{beatname_lc}-{version}-{localdate}"+). If you change this setting, you also need to configure the `setup.template.name` and `setup.template.pattern` options (see <>). @@ -225,7 +227,7 @@ endif::apm-server[] ifdef::apm-server[] The index name to write events to. The default is -+"apm-%\{[agent.version]\}-{type\}-%\{+yyyy.MM.dd\}"+ (for example, ++"apm-%\{[{beat_version_key}]\}-{type\}-%\{+yyyy.MM.dd\}"+ (for example, +"apm-{version}-transaction-{localdate}"+). See <> for more information on default index configuration. @@ -235,7 +237,7 @@ you need to configure the `setup.template.name` and `setup.template.pattern` opt (see <>). You also must set the default index configuration in the `apm-server.yml` file. -NOTE: `agent.version` is a field managed by Beats that is added to every document. +NOTE: +{beat_version_key}+ is a field managed by Beats that is added to every document. It holds the current version of APM Server. endif::apm-server[] @@ -243,9 +245,6 @@ ifndef::no_dashboards[] If you are using the pre-built Kibana dashboards, you also need to set the `setup.dashboards.index` option (see <>). -ifdef::deprecate_dashboard_loading[] -deprecated[{deprecate_dashboard_loading}] -endif::deprecate_dashboard_loading[] endif::no_dashboards[] You can set the index dynamically by using a format string to access any event @@ -256,10 +255,10 @@ to set the index: ------------------------------------------------------------------------------ output.elasticsearch: hosts: ["http://localhost:9200"] - index: "%\{[fields.log_type]\}-%\{[agent.version]\}-%\{+yyyy.MM.dd}\" <1> + index: "%\{[fields.log_type]\}-%\{[{beat_version_key}]\}-%\{+yyyy.MM.dd}\" <1> ------------------------------------------------------------------------------ -<1> We recommend including `agent.version` in the name to avoid mapping issues +<1> We recommend including +{beat_version_key}+ in the name to avoid mapping issues when you upgrade. With this configuration, all events with `log_type: normal` are sent to an @@ -303,15 +302,15 @@ endif::no-processors[] The following example sets the index based on whether the `message` field contains the specified string: -["source","yaml"] +["source","yaml",subs="attributes"] ------------------------------------------------------------------------------ output.elasticsearch: hosts: ["http://localhost:9200"] indices: - - index: "warning-%{[agent.version]}-%{+yyyy.MM.dd}" + - index: "warning-%{[{beat_version_key}]}-%{+yyyy.MM.dd}" when.contains: message: "WARN" - - index: "error-%{[agent.version]}-%{+yyyy.MM.dd}" + - index: "error-%{[{beat_version_key}]}-%{+yyyy.MM.dd}" when.contains: message: "ERR" ------------------------------------------------------------------------------ @@ -344,12 +343,14 @@ values. You cannot specify format strings within the mapping pairs. //TODO: MOVE ILM OPTIONS TO APPEAR LOGICALLY BASED ON LOCATION IN THE YAML FILE. +ifndef::no_ilm[] [[ilm-es]] ===== `ilm` Configuration options for index lifecycle management. See <> for more information. +endif::no_ilm[] ifndef::no-pipeline[] [[pipeline-option-es]] @@ -816,7 +817,7 @@ NOTE: Events bigger than <> will be [[kafka-compatibility]] ==== Compatibility -This output works with all Kafka versions in between 0.11 and 2.0.0. Older versions +This output works with all Kafka versions in between 0.11 and 2.1.0. Older versions might work as well, but are not supported. ==== Configuration options diff --git a/libbeat/docs/processors-using.asciidoc b/libbeat/docs/processors-using.asciidoc index 53baa0a2598..c8fe56fdbe9 100644 --- a/libbeat/docs/processors-using.asciidoc +++ b/libbeat/docs/processors-using.asciidoc @@ -805,6 +805,9 @@ by default. `host`:: (Optional) Identify the node where {beatname_lc} is running in case it cannot be accurately detected, as when running {beatname_lc} in host network mode. +`namespace`:: (Optional) Select the namespace from which to collect the +metadata. If it is not set, the processor collects metadata from all namespaces. +It is unset by default. `kube_config`:: (Optional) Use given config file as configuration for Kubernetes client. `default_indexers.enabled`:: (Optional) Enable/Disable default pod indexers, in diff --git a/libbeat/docs/shared-autodiscover.asciidoc b/libbeat/docs/shared-autodiscover.asciidoc index 25b65323e69..ca7947f301b 100644 --- a/libbeat/docs/shared-autodiscover.asciidoc +++ b/libbeat/docs/shared-autodiscover.asciidoc @@ -180,6 +180,9 @@ The `kubernetes` autodiscover provider has the following configuration settings: `host`:: (Optional) Identify the node where {beatname_lc} is running in case it cannot be accurately detected, as when running {beatname_lc} in host network mode. +`namespace`:: (Optional) Select the namespace from which to collect the + metadata. If it is not set, the processor collects metadata from all + namespaces. It is unset by default. `kube_config`:: (Optional) Use given config file as configuration for Kubernetes client. diff --git a/libbeat/docs/shared-beats-attributes.asciidoc b/libbeat/docs/shared-beats-attributes.asciidoc index 09f05b7b1c3..88f1d2d6aba 100644 --- a/libbeat/docs/shared-beats-attributes.asciidoc +++ b/libbeat/docs/shared-beats-attributes.asciidoc @@ -25,3 +25,4 @@ :beat_monitoring_user: beats_system :beat_monitoring_user_version: 6.3.0 :beat_monitoring_version: 6.2 +:beat_version_key: agent.version diff --git a/libbeat/docs/shared-config-ingest.asciidoc b/libbeat/docs/shared-config-ingest.asciidoc index 5157918a479..db3a214aa55 100644 --- a/libbeat/docs/shared-config-ingest.asciidoc +++ b/libbeat/docs/shared-config-ingest.asciidoc @@ -42,7 +42,7 @@ named `pipeline.json`: "processors": [ { "lowercase": { - "field": "beat.name" + "field": "agent.name" } } ] @@ -65,7 +65,7 @@ output.elasticsearch: pipeline: "test-pipeline" ------------------------------------------------------------------------------ -When you run {beatname_uc}, the value of `beat.name` is converted to lowercase before indexing. +When you run {beatname_uc}, the value of `agent.name` is converted to lowercase before indexing. For more information about defining a pre-processing pipeline, see the {elasticsearch}/ingest.html[Ingest Node] documentation. diff --git a/libbeat/docs/shared-ilm.asciidoc b/libbeat/docs/shared-ilm.asciidoc index 0019698d964..acfc479266c 100644 --- a/libbeat/docs/shared-ilm.asciidoc +++ b/libbeat/docs/shared-ilm.asciidoc @@ -18,15 +18,12 @@ existing indices. To use index lifecycle management on {beatname_uc} indices: -. Enable index lifecycle management by setting `ilm.enabled: true` in the {es} -output configuration. For example: +. Enable index lifecycle management by setting `setup.ilm.enabled: true`. For example: + -- [source,yaml] ------------------------------------------------------------------------------ -output.elasticsearch: - hosts: ["localhost:9200"] - ilm.enabled: true +setup.ilm.enabled: true ------------------------------------------------------------------------------ This configuration overwrites your index settings and adjusts the {beatname_uc} @@ -35,9 +32,9 @@ template to use the correct settings for index lifecycle management. NOTE: If you've previously loaded the index template for this version into {es}, you must overwrite the template by setting `setup.template.overwrite: true`. -The rollover alias is set to +{beatname_lc}-\{beat.version\}+ by default. You +The rollover alias is set to +{beatname_lc}-\{{beat_version_key}\}+ by default. You can change the prefix used in the alias by setting `ilm.rollover_alias`, but you -can't remove `{beat.version}` from the rollover alias name. The default pattern +can't remove +{beat_version_key}+ from the rollover alias name. The default pattern used for the rollover index is `%{now/d}-000001`. You can change the pattern by setting `ilm.pattern`. For example: diff --git a/libbeat/docs/shared-kibana-config.asciidoc b/libbeat/docs/shared-kibana-config.asciidoc index 0f72529efb8..90dcdb012f4 100644 --- a/libbeat/docs/shared-kibana-config.asciidoc +++ b/libbeat/docs/shared-kibana-config.asciidoc @@ -11,22 +11,9 @@ [[setup-kibana-endpoint]] == Set up the Kibana endpoint -ifdef::deprecate_dashboard_loading[] -deprecated[{deprecate_dashboard_loading}] - -endif::[] - -ifeval::["{beatname_lc}" == "apm-server"] -The Kibana dashboards are loaded into Kibana via the Kibana API. -This requires a Kibana endpoint configuration. -endif::[] - -ifeval::["{beatname_lc}" != "apm-server"] Starting with {beatname_uc} 6.0.0, the Kibana dashboards are loaded into Kibana via the Kibana API. This requires a Kibana endpoint configuration. -endif::[] - You configure the endpoint in the `setup.kibana` section of the +{beatname_lc}.yml+ config file. diff --git a/libbeat/docs/shared-template-load.asciidoc b/libbeat/docs/shared-template-load.asciidoc index f09aec8305e..b746bdc42f8 100644 --- a/libbeat/docs/shared-template-load.asciidoc +++ b/libbeat/docs/shared-template-load.asciidoc @@ -75,11 +75,11 @@ date information. You also need to configure the `setup.template.name` and + ["source","sh",subs="attributes,callouts"] ----- -output.elasticsearch.index: "customname-%{[beat.version]}-%{+yyyy.MM.dd}" +output.elasticsearch.index: "customname-%{[{beat_version_key}]}-%{+yyyy.MM.dd}" setup.template.name: "customname" setup.template.pattern: "customname-*" ----- -ifndef::deprecate_dashboard_loading,no_dashboards[] +ifndef::no_dashboards[] + If you're using pre-built Kibana dashboards, also set the `setup.dashboards.index` option. For example: @@ -88,12 +88,11 @@ If you're using pre-built Kibana dashboards, also set the ---- setup.dashboards.index: "customname-*" ---- -endif::[] +endif::no_dashboards[] -ifdef::deprecate_dashboard_loading[] -+ +ifdef::no_dashboards[] Remember to change the index name when you load dashboards via the Kibana UI. -endif::[] +endif::no_dashboards[] See <> for the full list of configuration options. diff --git a/libbeat/docs/template-config.asciidoc b/libbeat/docs/template-config.asciidoc index 19e3084cb2b..b465ba5c086 100644 --- a/libbeat/docs/template-config.asciidoc +++ b/libbeat/docs/template-config.asciidoc @@ -24,18 +24,18 @@ you must <>. *`setup.template.name`*:: The name of the template. The default is +{beatname_lc}+. The {beatname_uc} version is always appended to the given -name, so the final name is +{beatname_lc}-%\{[agent.version]\}+. +name, so the final name is +{beatname_lc}-%\{[{beat_version_key}]\}+. // Maintainers: a backslash character is required to escape curly braces and // asterisks in inline code examples that contain asciidoc attributes. You'll // note that a backslash does not appear before the asterisk -// in +{beatname_lc}-%\{[agent.version]\}-*+. This is intentional and formats +// in +{beatname_lc}-%\{[{beat_version_key}]\}-*+. This is intentional and formats // the example as expected. *`setup.template.pattern`*:: The template pattern to apply to the default index settings. The default pattern is +{beat_default_index_prefix}-\*+. The {beatname_uc} version is always included in the pattern, so the final pattern is -+{beat_default_index_prefix}-%\{[agent.version]\}-*+. The wildcard character `-*` is used to ++{beat_default_index_prefix}-%\{[{beat_version_key}]\}-*+. The wildcard character `-*` is used to match all daily indices. + Example: diff --git a/libbeat/testing/mapvaltest/mapvaltest_test.go b/libbeat/idxmgmt/assets.go similarity index 65% rename from libbeat/testing/mapvaltest/mapvaltest_test.go rename to libbeat/idxmgmt/assets.go index 9f22e28f3dd..e5ca4ca2e08 100644 --- a/libbeat/testing/mapvaltest/mapvaltest_test.go +++ b/libbeat/idxmgmt/assets.go @@ -15,23 +15,18 @@ // specific language governing permissions and limitations // under the License. -package mapvaltest +package idxmgmt -import ( - "testing" - - "github.com/stretchr/testify/assert" - - "github.com/elastic/beats/libbeat/common" - "github.com/elastic/beats/libbeat/common/mapval" -) - -func TestTest(t *testing.T) { - // Should pass - Test(t, mapval.MustCompile(mapval.Map{}), common.MapStr{}) +type assets struct { + fields []byte +} - fakeT := new(testing.T) - Test(fakeT, mapval.MustCompile(mapval.Map{"foo": "bar"}), common.MapStr{}) +// BeatsAssets creates an asseter with a predefine set of fields that is always +// reported. +func BeatsAssets(fields []byte) Asseter { + return &assets{fields: fields} +} - assert.True(t, fakeT.Failed()) +func (a *assets) Fields(name string) []byte { + return a.fields // assume we have the beats global assets } diff --git a/libbeat/idxmgmt/idxmgmt.go b/libbeat/idxmgmt/idxmgmt.go new file mode 100644 index 00000000000..b2f7f38568e --- /dev/null +++ b/libbeat/idxmgmt/idxmgmt.go @@ -0,0 +1,162 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package idxmgmt + +import ( + "errors" + "fmt" + + "github.com/elastic/beats/libbeat/beat" + "github.com/elastic/beats/libbeat/common" + "github.com/elastic/beats/libbeat/idxmgmt/ilm" + "github.com/elastic/beats/libbeat/logp" + "github.com/elastic/beats/libbeat/outputs" + "github.com/elastic/beats/libbeat/template" +) + +// SupportFactory is used to provide custom index management support to libbeat. +type SupportFactory func(*logp.Logger, beat.Info, *common.Config) (Supporter, error) + +// Supporter provides index management and configuration related services +// throughout libbeat. +// The BuildSelector is used by the output to create an IndexSelector. The +// index selector will report the per event index name to be used. +// A manager instantiated via Supporter is responsible for instantiating/configuring +// the index throughout the Elastic Stack. +type Supporter interface { + // Enalbed checks if index management is configured to configure templates, + // ILM, or aliases. + Enabled() bool + + // ILM provides access to the configured ILM support. + ILM() ilm.Supporter + + // TemplateConfig returns the template configuration used by the index supporter. + TemplateConfig(withILM bool) (template.TemplateConfig, error) + + // BuildSelector create an index selector. + // The defaultIndex string is interpreted as format string. It is used + // as default index if the configuration provided does not define an index or + // has no default fallback if all indices are guarded by conditionals. + BuildSelector(cfg *common.Config) (outputs.IndexSelector, error) + + // Manager creates a new manager that can be used to execute the required steps + // for initializing an index, ILM policies, and write aliases. + Manager(client ESClient, assets Asseter) Manager +} + +// Asseter provides access to beats assets required to load the template. +type Asseter interface { + Fields(name string) []byte +} + +// ESClient defines the minimal interface required for the index manager to +// prepare an index. +type ESClient interface { + Request(method, path string, pipeline string, params map[string]string, body interface{}) (int, []byte, error) + GetVersion() common.Version +} + +// Manager is used to initialize indices, ILM policies, and aliases within the +// Elastic Stack. +type Manager interface { + Setup(template, policy bool) error +} + +// DefaultSupport initializes the default index management support used by most Beats. +func DefaultSupport(log *logp.Logger, info beat.Info, configRoot *common.Config) (Supporter, error) { + factory := MakeDefaultSupport(nil) + return factory(log, info, configRoot) +} + +// MakeDefaultSupport creates some default index management support, with a +// custom ILM support implementation. +func MakeDefaultSupport(ilmSupport ilm.SupportFactory) SupportFactory { + if ilmSupport == nil { + ilmSupport = ilm.DefaultSupport + } + + return func(log *logp.Logger, info beat.Info, configRoot *common.Config) (Supporter, error) { + const logName = "index-management" + + cfg := struct { + ILM *common.Config `config:"setup.ilm"` + Template *common.Config `config:"setup.template"` + Output common.ConfigNamespace `config:"output"` + Migration *common.Config `config:"migration"` + }{} + if configRoot != nil { + if err := configRoot.Unpack(&cfg); err != nil { + return nil, err + } + } + + if log == nil { + log = logp.NewLogger(logName) + } else { + log = log.Named(logName) + } + + if err := checkTemplateESSettings(cfg.Template, cfg.Output); err != nil { + return nil, err + } + + return newIndexSupport(log, info, ilmSupport, cfg.Template, cfg.ILM, cfg.Migration.Enabled()) + } +} + +// checkTemplateESSettings validates template settings and output.elasticsearch +// settings to be consistent. +// XXX: This is some legacy check that will not be active if the output is +// configured via Central Config Management. +// In the future we will have CM deal with index setup and providing a +// consistent output configuration. +// TODO: check if it's safe to move this check to the elasticsearch output +// (Not doing so, so to not interfere with outputs being setup via Central +// Management for now). +func checkTemplateESSettings(tmpl *common.Config, out common.ConfigNamespace) error { + if out.Name() != "elasticsearch" { + return nil + } + + enabled := tmpl == nil || tmpl.Enabled() + if !enabled { + return nil + } + + var tmplCfg template.TemplateConfig + if tmpl != nil { + if err := tmpl.Unpack(&tmplCfg); err != nil { + return fmt.Errorf("unpacking template config fails: %v", err) + } + } + + esCfg := struct { + Index string `config:"index"` + }{} + if err := out.Config().Unpack(&esCfg); err != nil { + return err + } + + tmplSet := tmplCfg.Name != "" && tmplCfg.Pattern != "" + if esCfg.Index != "" && !tmplSet { + return errors.New("setup.template.name and setup.template.pattern have to be set if index name is modified") + } + + return nil +} diff --git a/libbeat/idxmgmt/idxmgmt_test.go b/libbeat/idxmgmt/idxmgmt_test.go new file mode 100644 index 00000000000..3d7a841b797 --- /dev/null +++ b/libbeat/idxmgmt/idxmgmt_test.go @@ -0,0 +1,272 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package idxmgmt + +import ( + "fmt" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/elastic/beats/libbeat/beat" + "github.com/elastic/beats/libbeat/common" + "github.com/elastic/beats/libbeat/idxmgmt/ilm" + "github.com/elastic/beats/libbeat/template" +) + +func TestDefaultSupport_Enabled(t *testing.T) { + cases := map[string]struct { + ilmCalls []onCall + cfg map[string]interface{} + want bool + }{ + "templates and ilm disabled": { + want: false, + ilmCalls: []onCall{ + onMode().Return(ilm.ModeDisabled), + }, + cfg: map[string]interface{}{ + "setup.template.enabled": false, + }, + }, + "templates only": { + want: true, + ilmCalls: []onCall{ + onMode().Return(ilm.ModeDisabled), + }, + cfg: map[string]interface{}{ + "setup.template.enabled": true, + }, + }, + "ilm only": { + want: true, + ilmCalls: []onCall{ + onMode().Return(ilm.ModeEnabled), + }, + cfg: map[string]interface{}{ + "setup.template.enabled": false, + }, + }, + "ilm tentatively": { + want: true, + ilmCalls: []onCall{ + onMode().Return(ilm.ModeAuto), + }, + cfg: map[string]interface{}{ + "setup.template.enabled": false, + }, + }, + } + for name, test := range cases { + t.Run(name, func(t *testing.T) { + info := beat.Info{Beat: "test", Version: "9.9.9"} + factory := MakeDefaultSupport(makeMockILMSupport(test.ilmCalls...)) + im, err := factory(nil, info, common.MustNewConfigFrom(test.cfg)) + require.NoError(t, err) + assert.Equal(t, test.want, im.Enabled()) + }) + } +} + +func TestDefaultSupport_TemplateConfig(t *testing.T) { + ilmTemplateSettings := func(alias, policy string) []onCall { + return []onCall{ + onMode().Return(ilm.ModeEnabled), + onAlias().Return(ilm.Alias{Name: alias}), + onPolicy().Return(ilm.Policy{Name: policy}), + } + } + + cloneCfg := func(c template.TemplateConfig) template.TemplateConfig { + if c.AppendFields != nil { + tmp := make(common.Fields, len(c.AppendFields)) + copy(tmp, c.AppendFields) + c.AppendFields = tmp + } + + if c.Settings.Index != nil { + c.Settings.Index = (map[string]interface{})(common.MapStr(c.Settings.Index).Clone()) + } + if c.Settings.Index != nil { + c.Settings.Source = (map[string]interface{})(common.MapStr(c.Settings.Source).Clone()) + } + return c + } + + cfgWith := func(s template.TemplateConfig, mods ...map[string]interface{}) template.TemplateConfig { + for _, mod := range mods { + cfg := common.MustNewConfigFrom(mod) + s = cloneCfg(s) + err := cfg.Unpack(&s) + if err != nil { + panic(err) + } + } + return s + } + + cases := map[string]struct { + ilmCalls []onCall + cfg map[string]interface{} + want template.TemplateConfig + fail bool + }{ + "default template config": { + want: template.DefaultConfig(), + }, + "default template with ilm": { + ilmCalls: ilmTemplateSettings("alias", "test-9.9.9"), + want: cfgWith(template.DefaultConfig(), map[string]interface{}{ + "name": "alias", + "pattern": "alias-*", + "settings.index.lifecycle.name": "test-9.9.9", + "settings.index.lifecycle.rollover_alias": "alias", + }), + }, + } + for name, test := range cases { + t.Run(name, func(t *testing.T) { + info := beat.Info{Beat: "test", Version: "9.9.9"} + factory := MakeDefaultSupport(makeMockILMSupport(test.ilmCalls...)) + im, err := factory(nil, info, common.MustNewConfigFrom(test.cfg)) + require.NoError(t, err) + withILM := len(test.ilmCalls) > 0 + + tmpl, err := im.TemplateConfig(withILM) + if test.fail { + assert.Error(t, err) + } else { + require.NoError(t, err) + assert.Equal(t, test.want, tmpl) + } + }) + } +} + +func TestDefaultSupport_BuildSelector(t *testing.T) { + type nameFunc func(time.Time) string + + noILM := []onCall{onMode().Return(ilm.ModeDisabled)} + ilmTemplateSettings := func(alias, policy string) []onCall { + return []onCall{ + onMode().Return(ilm.ModeEnabled), + onAlias().Return(ilm.Alias{Name: alias}), + onPolicy().Return(ilm.Policy{Name: policy}), + } + } + + stable := func(s string) nameFunc { + return func(_ time.Time) string { return s } + } + dateIdx := func(base string) nameFunc { + return func(ts time.Time) string { + ext := fmt.Sprintf("%d.%02d.%02d", ts.Year(), ts.Month(), ts.Day()) + return fmt.Sprintf("%v-%v", base, ext) + } + } + + cases := map[string]struct { + ilmCalls []onCall + imCfg map[string]interface{} + cfg map[string]interface{} + want nameFunc + meta common.MapStr + }{ + "without ilm": { + ilmCalls: noILM, + cfg: map[string]interface{}{"index": "test-%{[agent.version]}"}, + want: stable("test-9.9.9"), + }, + "event alias without ilm": { + ilmCalls: noILM, + cfg: map[string]interface{}{"index": "test-%{[agent.version]}"}, + want: stable("test"), + meta: common.MapStr{ + "alias": "test", + }, + }, + "event index without ilm": { + ilmCalls: noILM, + cfg: map[string]interface{}{"index": "test-%{[agent.version]}"}, + want: dateIdx("test"), + meta: common.MapStr{ + "index": "test", + }, + }, + "with ilm": { + ilmCalls: ilmTemplateSettings("test-9.9.9", "test-9.9.9"), + cfg: map[string]interface{}{"index": "wrong-%{[agent.version]}"}, + want: stable("test-9.9.9"), + }, + "event alias wit ilm": { + ilmCalls: ilmTemplateSettings("test-9.9.9", "test-9.9.9"), + cfg: map[string]interface{}{"index": "test-%{[agent.version]}"}, + want: stable("event-alias"), + meta: common.MapStr{ + "alias": "event-alias", + }, + }, + "event index with ilm": { + ilmCalls: ilmTemplateSettings("test-9.9.9", "test-9.9.9"), + cfg: map[string]interface{}{"index": "test-%{[agent.version]}"}, + want: dateIdx("event-index"), + meta: common.MapStr{ + "index": "event-index", + }, + }, + "use indices": { + ilmCalls: ilmTemplateSettings("test-9.9.9", "test-9.9.9"), + cfg: map[string]interface{}{ + "index": "test-%{[agent.version]}", + "indices": []map[string]interface{}{ + {"index": "myindex"}, + }, + }, + want: stable("myindex"), + }, + } + for name, test := range cases { + t.Run(name, func(t *testing.T) { + ts := time.Now() + info := beat.Info{Beat: "test", Version: "9.9.9"} + + factory := MakeDefaultSupport(makeMockILMSupport(test.ilmCalls...)) + im, err := factory(nil, info, common.MustNewConfigFrom(test.imCfg)) + require.NoError(t, err) + + sel, err := im.BuildSelector(common.MustNewConfigFrom(test.cfg)) + require.NoError(t, err) + + meta := test.meta + idx, err := sel.Select(&beat.Event{ + Timestamp: ts, + Fields: common.MapStr{ + "test": "value", + "agent": common.MapStr{ + "version": "9.9.9", + }, + }, + Meta: meta, + }) + require.NoError(t, err) + assert.Equal(t, test.want(ts), idx) + }) + } +} diff --git a/libbeat/idxmgmt/ilm/config.go b/libbeat/idxmgmt/ilm/config.go new file mode 100644 index 00000000000..24934c1c6a6 --- /dev/null +++ b/libbeat/idxmgmt/ilm/config.go @@ -0,0 +1,124 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package ilm + +import ( + "fmt" + "strconv" + "strings" + + "github.com/elastic/beats/libbeat/beat" + "github.com/elastic/beats/libbeat/common" + "github.com/elastic/beats/libbeat/common/fmtstr" +) + +// Config is used for unpacking a common.Config. +type Config struct { + Mode Mode `config:"enabled"` + PolicyName fmtstr.EventFormatString `config:"policy_name"` + PolicyFile string `config:"policy_file"` + RolloverAlias string `config:"rollover_alias"` + Pattern string `config:"pattern"` + + // CheckExists can disable the check for an existing policy. Check required + // read_ilm privileges. If check is disabled the policy will only be + // installed if Overwrite is enabled. + CheckExists bool `config:"check_exists"` + + // Enable always overwrite policy mode. This required manage_ilm privileges. + Overwrite bool `config:"overwrite"` +} + +//Mode is used for enumerating the ilm mode. +type Mode uint8 + +const ( + //ModeAuto enum 'auto' + ModeAuto Mode = iota + + //ModeEnabled enum 'true' + ModeEnabled + + //ModeDisabled enum 'false' + ModeDisabled +) + +const ilmDefaultPattern = "{now/d}-000001" + +// DefaultPolicy defines the default policy to be used if no custom policy is +// configured. +// By default the policy contains not warm, cold, or delete phase. +// The index is configured to rollover every 50GB or after 30d. +var DefaultPolicy = common.MapStr{ + "policy": common.MapStr{ + "phases": common.MapStr{ + "hot": common.MapStr{ + "actions": common.MapStr{ + "rollover": common.MapStr{ + "max_size": "50gb", + "max_age": "30d", + }, + }, + }, + }, + }, +} + +//Unpack creates enumeration value true, false or auto +func (m *Mode) Unpack(in string) error { + in = strings.ToLower(in) + + if in == "auto" { + *m = ModeAuto + return nil + } + + b, err := strconv.ParseBool(in) + if err != nil { + return fmt.Errorf("ilm.enabled` mode '%v' is invalid (try auto, true, false)", in) + } + + if b { + *m = ModeEnabled + } else { + *m = ModeDisabled + } + return nil +} + +//Validate verifies that expected config options are given and valid +func (cfg *Config) Validate() error { + if cfg.RolloverAlias == "" && cfg.Mode != ModeDisabled { + return fmt.Errorf("rollover_alias must be set when ILM is not disabled") + } + return nil +} + +func defaultConfig(info beat.Info) Config { + name := fmt.Sprintf("%s-%s", info.Beat, info.Version) + nameFmt := fmtstr.MustCompileEvent(name) + + return Config{ + Mode: ModeAuto, + PolicyName: *nameFmt, + RolloverAlias: name, + Pattern: ilmDefaultPattern, + PolicyFile: "", + CheckExists: true, + } +} diff --git a/libbeat/idxmgmt/ilm/error.go b/libbeat/idxmgmt/ilm/error.go new file mode 100644 index 00000000000..4d644de9b98 --- /dev/null +++ b/libbeat/idxmgmt/ilm/error.go @@ -0,0 +1,94 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package ilm + +import ( + "errors" + "fmt" +) + +// Error indicates an error + reason describing the last error. +// The Reason() method returns a sentinal error value for comparison. +type Error struct { + reason error + cause error + message string +} + +var ( + ErrESVersionNotSupported = errors.New("ILM is not supported by the Elasticsearch version in use") + ErrILMCheckRequestFailed = errors.New("request checking for ILM availability failed") + ErrInvalidResponse = errors.New("invalid response received") + ErrESILMDisabled = errors.New("ILM is disabled in Elasticsearch") + ErrRequestFailed = errors.New("request failed") + ErrAliasAlreadyExists = errors.New("alias already exists") + ErrAliasCreateFailed = errors.New("failed to create write alias") + ErrOpNotAvailable = errors.New("operation not available") +) + +func errOf(reason error) error { + return &Error{reason: reason} +} + +func errf(reason error, msg string, vs ...interface{}) error { + return wrapErrf(nil, reason, msg, vs...) +} + +func wrapErr(cause, reason error) error { + return wrapErrf(cause, reason, "") +} + +func wrapErrf(cause, reason error, msg string, vs ...interface{}) error { + return &Error{ + cause: cause, + reason: reason, + message: fmt.Sprintf(msg, vs...), + } +} + +// ErrReason calls Reason() if the error implements this method. Otherwise return nil. +func ErrReason(err error) error { + if err == nil { + return nil + } + + ifc, ok := err.(interface{ Reason() error }) + if !ok { + return nil + } + return ifc.Reason() +} + +// Cause returns the errors cause, if present. +func (e *Error) Cause() error { return e.cause } + +// Reason returns a sentinal error value define within the ilm package. +func (e *Error) Reason() error { return e.reason } + +// Error returns the formatted error string. +func (e *Error) Error() string { + msg := e.message + if e.message == "" { + msg = e.reason.Error() + } + + if e.cause != nil { + return fmt.Sprintf("%v: %+v", msg, e.cause) + } + return msg +} diff --git a/libbeat/idxmgmt/ilm/eshandler.go b/libbeat/idxmgmt/ilm/eshandler.go new file mode 100644 index 00000000000..de493721bf7 --- /dev/null +++ b/libbeat/idxmgmt/ilm/eshandler.go @@ -0,0 +1,210 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package ilm + +import ( + "encoding/json" + "fmt" + "net/url" + "path" + + "github.com/elastic/beats/libbeat/common" +) + +type esClientHandler struct { + client ESClient +} + +var ( + esMinILMVersion = common.MustNewVersion("6.6.0") + esMinDefaultILMVesion = common.MustNewVersion("7.0.0") +) + +const ( + // esFeaturesPath is used to query Elasticsearch for availability of licensed + // features. + esFeaturesPath = "/_xpack" + + esILMPath = "/_ilm/policy" + + esAliasPath = "/_alias" +) + +// ESClientHandler creates a new APIHandler executing ILM, and alias queries +// against Elasticsearch. +func ESClientHandler(client ESClient) APIHandler { + if client == nil { + return nil + } + return &esClientHandler{client} +} + +// ESClient defines the minimal interface required for the ESClientHandler to +// prepare a policy and write alias. +type ESClient interface { + GetVersion() common.Version + Request( + method, path string, + pipeline string, + params map[string]string, + body interface{}, + ) (int, []byte, error) +} + +func (h *esClientHandler) ILMEnabled(mode Mode) (bool, error) { + if mode == ModeDisabled { + return false, nil + } + + avail, probe := h.checkILMVersion(mode) + if !avail { + if mode == ModeEnabled { + ver := h.client.GetVersion() + return false, errf(ErrESVersionNotSupported, + "Elasticsearch %v does not support ILM", ver.String()) + } + return false, nil + } + + if !probe { + // version potentially supports ILM, but mode + version indicates that we + // want to disable ILM support. + return false, nil + } + + avail, enabled, err := h.checkILMSupport() + if err != nil { + return false, err + } + + if !avail { + if mode == ModeEnabled { + return false, errOf(ErrESVersionNotSupported) + } + return false, nil + } + + if !enabled && mode == ModeEnabled { + return false, errOf(ErrESILMDisabled) + } + return enabled, nil +} + +func (h *esClientHandler) CreateILMPolicy(policy Policy) error { + path := path.Join(esILMPath, policy.Name) + _, _, err := h.client.Request("PUT", path, "", nil, policy.Body) + return err +} + +func (h *esClientHandler) HasILMPolicy(name string) (bool, error) { + // XXX: HEAD method does currently not work for checking if a policy exists + path := path.Join(esILMPath, name) + status, b, err := h.client.Request("GET", path, "", nil, nil) + if err != nil && status != 404 { + return false, wrapErrf(err, ErrRequestFailed, + "failed to check for policy name '%v': (status=%v) %s", name, status, b) + } + return status == 200, nil +} + +func (h *esClientHandler) HasAlias(name string) (bool, error) { + path := path.Join(esAliasPath, name) + status, b, err := h.client.Request("HEAD", path, "", nil, nil) + if err != nil && status != 404 { + return false, wrapErrf(err, ErrRequestFailed, + "failed to check for alias '%v': (status=%v) %s", name, status, b) + } + return status == 200, nil +} + +func (h *esClientHandler) CreateAlias(alias Alias) error { + // Escaping because of date pattern + // This always assume it's a date pattern by sourrounding it by <...> + firstIndex := fmt.Sprintf("<%s-%s>", alias.Name, alias.Pattern) + firstIndex = url.PathEscape(firstIndex) + + body := common.MapStr{ + "aliases": common.MapStr{ + alias.Name: common.MapStr{ + "is_write_index": true, + }, + }, + } + + // Note: actual aliases are accessible via the index + status, res, err := h.client.Request("PUT", "/"+firstIndex, "", nil, body) + if status == 400 { + return errOf(ErrAliasAlreadyExists) + } else if err != nil { + return wrapErrf(err, ErrAliasCreateFailed, "failed to create alias: %s", res) + } + + return nil +} + +func (h *esClientHandler) checkILMVersion(mode Mode) (avail, probe bool) { + ver := h.client.GetVersion() + avail = !ver.LessThan(esMinILMVersion) + if avail { + probe = (mode == ModeEnabled) || + (mode == ModeAuto && !ver.LessThan(esMinDefaultILMVesion)) + } + + return avail, probe +} + +func (h *esClientHandler) checkILMSupport() (avail, enbaled bool, err error) { + var response struct { + Features struct { + ILM struct { + Available bool `json:"available"` + Enabled bool `json:"enabled"` + } `json:"ilm"` + } `json:"features"` + } + status, err := h.queryFeatures(&response) + if status == 400 { + // If we get a 400, it's assumed to be the OSS version of Elasticsearch + return false, false, nil + } + if err != nil { + return false, false, wrapErr(err, ErrILMCheckRequestFailed) + } + + avail = response.Features.ILM.Available + enbaled = response.Features.ILM.Enabled + return avail, enbaled, nil +} + +func (h *esClientHandler) queryFeatures(to interface{}) (int, error) { + status, body, err := h.client.Request("GET", esFeaturesPath, "", nil, nil) + if status >= 400 || err != nil { + return status, err + } + + if to != nil { + if err := json.Unmarshal(body, to); err != nil { + return status, wrapErrf(err, ErrInvalidResponse, "failed to parse JSON response") + } + } + return status, nil +} + +func (h *esClientHandler) access() ESClient { + return h.client +} diff --git a/libbeat/idxmgmt/ilm/eshandler_integration_test.go b/libbeat/idxmgmt/ilm/eshandler_integration_test.go new file mode 100644 index 00000000000..808ab48b47d --- /dev/null +++ b/libbeat/idxmgmt/ilm/eshandler_integration_test.go @@ -0,0 +1,207 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +//+build integration + +package ilm_test + +import ( + "fmt" + "os" + "testing" + "time" + + "github.com/gofrs/uuid" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/elastic/beats/libbeat/idxmgmt/ilm" + "github.com/elastic/beats/libbeat/outputs/elasticsearch" + "github.com/elastic/beats/libbeat/outputs/outil" +) + +const ( + // ElasticsearchDefaultHost is the default host for elasticsearch. + ElasticsearchDefaultHost = "localhost" + // ElasticsearchDefaultPort is the default port for elasticsearch. + ElasticsearchDefaultPort = "9200" +) + +func TestESClientHandler_ILMEnabled(t *testing.T) { + t.Run("no ilm if disabled", func(t *testing.T) { + h := newESClientHandler(t) + b, err := h.ILMEnabled(ilm.ModeDisabled) + assert.NoError(t, err) + assert.False(t, b) + }) + + t.Run("with ilm if auto", func(t *testing.T) { + h := newESClientHandler(t) + b, err := h.ILMEnabled(ilm.ModeAuto) + assert.NoError(t, err) + assert.True(t, b) + }) + + t.Run("with ilm if enabled", func(t *testing.T) { + h := newESClientHandler(t) + b, err := h.ILMEnabled(ilm.ModeEnabled) + assert.NoError(t, err) + assert.True(t, b) + }) +} + +func TestESClientHandler_ILMPolicy(t *testing.T) { + t.Run("does not exist", func(t *testing.T) { + name := makeName("esch-policy-no") + h := newESClientHandler(t) + b, err := h.HasILMPolicy(name) + assert.NoError(t, err) + assert.False(t, b) + }) + + t.Run("create new", func(t *testing.T) { + policy := ilm.Policy{ + Name: makeName("esch-policy-create"), + Body: ilm.DefaultPolicy, + } + h := newESClientHandler(t) + err := h.CreateILMPolicy(policy) + require.NoError(t, err) + + b, err := h.HasILMPolicy(policy.Name) + assert.NoError(t, err) + assert.True(t, b) + }) + + t.Run("overwrite", func(t *testing.T) { + policy := ilm.Policy{ + Name: makeName("esch-policy-overwrite"), + Body: ilm.DefaultPolicy, + } + h := newESClientHandler(t) + + err := h.CreateILMPolicy(policy) + require.NoError(t, err) + + // check second 'create' does not throw (assuming race with other beat) + err = h.CreateILMPolicy(policy) + require.NoError(t, err) + + b, err := h.HasILMPolicy(policy.Name) + assert.NoError(t, err) + assert.True(t, b) + }) +} + +func TestESClientHandler_Alias(t *testing.T) { + makeAlias := func(base string) ilm.Alias { + return ilm.Alias{ + Name: makeName(base), + Pattern: "{now/d}-000001", + } + } + + t.Run("does not exist", func(t *testing.T) { + name := makeName("esch-alias-no") + h := newESClientHandler(t) + b, err := h.HasAlias(name) + assert.NoError(t, err) + assert.False(t, b) + }) + + t.Run("create new", func(t *testing.T) { + alias := makeAlias("esch-alias-create") + h := newESClientHandler(t) + err := h.CreateAlias(alias) + assert.NoError(t, err) + + b, err := h.HasAlias(alias.Name) + assert.NoError(t, err) + assert.True(t, b) + }) + + t.Run("second create", func(t *testing.T) { + alias := makeAlias("esch-alias-2create") + h := newESClientHandler(t) + + err := h.CreateAlias(alias) + assert.NoError(t, err) + + err = h.CreateAlias(alias) + require.Error(t, err) + assert.Equal(t, ilm.ErrAliasAlreadyExists, ilm.ErrReason(err)) + + b, err := h.HasAlias(alias.Name) + assert.NoError(t, err) + assert.True(t, b) + }) +} + +func newESClientHandler(t *testing.T) ilm.APIHandler { + client, err := elasticsearch.NewClient(elasticsearch.ClientSettings{ + URL: getURL(), + Index: outil.MakeSelector(), + Username: getUser(), + Password: getUser(), + Timeout: 60 * time.Second, + CompressionLevel: 3, + }, nil) + if err != nil { + t.Fatal(err) + } + + if err := client.Connect(); err != nil { + t.Fatalf("Failed to connect to Test Elasticsearch instance: %v", err) + } + + return ilm.ESClientHandler(client) +} + +func makeName(base string) string { + id, err := uuid.NewV4() + if err != nil { + panic(err) + } + return fmt.Sprintf("%v-%v", base, id.String()) +} + +func getURL() string { + return fmt.Sprintf("http://%v:%v", getEsHost(), getEsPort()) +} + +// GetEsHost returns the Elasticsearch testing host. +func getEsHost() string { + return getEnv("ES_HOST", ElasticsearchDefaultHost) +} + +// GetEsPort returns the Elasticsearch testing port. +func getEsPort() string { + return getEnv("ES_PORT", ElasticsearchDefaultPort) +} + +// GetUser returns the Elasticsearch testing user. +func getUser() string { return getEnv("ES_USER", "") } + +// GetPass returns the Elasticsearch testing user's password. +func getPass() string { return getEnv("ES_PASS", "") } + +func getEnv(name, def string) string { + if v := os.Getenv(name); v != "" { + return v + } + return def +} diff --git a/libbeat/idxmgmt/ilm/ilm.go b/libbeat/idxmgmt/ilm/ilm.go new file mode 100644 index 00000000000..6ca833b978f --- /dev/null +++ b/libbeat/idxmgmt/ilm/ilm.go @@ -0,0 +1,147 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package ilm + +import ( + "encoding/json" + "io/ioutil" + "time" + + "github.com/pkg/errors" + + "github.com/elastic/beats/libbeat/beat" + "github.com/elastic/beats/libbeat/common" + "github.com/elastic/beats/libbeat/common/fmtstr" + "github.com/elastic/beats/libbeat/logp" +) + +// SupportFactory is used to define a policy type to be used. +type SupportFactory func(*logp.Logger, beat.Info, *common.Config) (Supporter, error) + +// Supporter implements ILM support. For loading the policies and creating +// write alias a manager instance must be generated. +type Supporter interface { + Mode() Mode + Alias() Alias + Policy() Policy + Manager(h APIHandler) Manager +} + +// Manager uses an APIHandler to install a policy. +type Manager interface { + Enabled() (bool, error) + EnsureAlias() error + EnsurePolicy(overwrite bool) error +} + +// APIHandler defines the interface between a remote service and the Manager. +type APIHandler interface { + ILMEnabled(Mode) (bool, error) + + HasAlias(name string) (bool, error) + CreateAlias(alias Alias) error + + HasILMPolicy(name string) (bool, error) + CreateILMPolicy(policy Policy) error +} + +// Policy describes a policy to be loaded into Elasticsearch. +// See: [Policy phases and actions documentation](https://www.elastic.co/guide/en/elasticsearch/reference/master/ilm-policy-definition.html). +type Policy struct { + Name string + Body map[string]interface{} +} + +// Alias describes the alias to be created in Elasticsearch. +type Alias struct { + Name string + Pattern string +} + +// DefaultSupport configures a new default ILM support implementation. +func DefaultSupport(log *logp.Logger, info beat.Info, config *common.Config) (Supporter, error) { + if log == nil { + log = logp.NewLogger("ilm") + } else { + log = log.Named("ilm") + } + + cfg := defaultConfig(info) + if config != nil { + if err := config.Unpack(&cfg); err != nil { + return nil, err + } + } + + if cfg.Mode == ModeDisabled { + return NoopSupport(info, config) + } + + name, err := applyStaticFmtstr(info, &cfg.PolicyName) + if err != nil { + return nil, errors.Wrap(err, "failed to read ilm policy name") + } + + alias := Alias{ + Name: cfg.RolloverAlias, + Pattern: cfg.Pattern, + } + + policy := Policy{ + Name: name, + Body: DefaultPolicy, + } + if path := cfg.PolicyFile; path != "" { + contents, err := ioutil.ReadFile(path) + if err != nil { + return nil, errors.Wrapf(err, "failed to read policy file '%v'", path) + } + + var body map[string]interface{} + if err := json.Unmarshal(contents, &body); err != nil { + return nil, errors.Wrapf(err, "failed to decode policy file '%v'", path) + } + + policy.Body = body + } + + log.Infof("Policy name: %v", name) + return NewDefaultSupport(log, cfg.Mode, alias, policy, cfg.Overwrite, cfg.CheckExists), nil +} + +func applyStaticFmtstr(info beat.Info, fmt *fmtstr.EventFormatString) (string, error) { + return fmt.Run(&beat.Event{ + Fields: common.MapStr{ + // beat object was left in for backward compatibility reason for older configs. + "beat": common.MapStr{ + "name": info.Beat, + "version": info.Version, + }, + "agent": common.MapStr{ + "name": info.Beat, + "version": info.Version, + }, + // For the Beats that have an observer role + "observer": common.MapStr{ + "name": info.Beat, + "version": info.Version, + }, + }, + Timestamp: time.Now(), + }) +} diff --git a/libbeat/idxmgmt/ilm/ilm_test.go b/libbeat/idxmgmt/ilm/ilm_test.go new file mode 100644 index 00000000000..e4a6ccf2864 --- /dev/null +++ b/libbeat/idxmgmt/ilm/ilm_test.go @@ -0,0 +1,279 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package ilm + +import ( + "errors" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/elastic/beats/libbeat/beat" + "github.com/elastic/beats/libbeat/common" +) + +func TestDefaultSupport_Init(t *testing.T) { + info := beat.Info{Beat: "test", Version: "9.9.9"} + + t.Run("mode from config", func(t *testing.T) { + cases := map[string]Mode{ + "true": ModeEnabled, + "false": ModeDisabled, + "auto": ModeAuto, + } + for setting, expected := range cases { + expected := expected + t.Run(setting, func(t *testing.T) { + cfg := common.MustNewConfigFrom(map[string]interface{}{ + "enabled": setting, + "rollover_alias": "test", + }) + + s, err := DefaultSupport(nil, info, cfg) + require.NoError(t, err) + assert.Equal(t, expected, s.Mode()) + }) + } + }) + + t.Run("with custom config", func(t *testing.T) { + tmp, err := DefaultSupport(nil, info, common.MustNewConfigFrom( + map[string]interface{}{ + "enabled": true, + "name": "test-%{[agent.version]}", + "rollover_alias": "alias", + "pattern": "01", + "check_exists": false, + "overwrite": true, + }, + )) + require.NoError(t, err) + + s := tmp.(*ilmSupport) + assert := assert.New(t) + assert.Equal(true, s.overwrite) + assert.Equal(false, s.checkExists) + assert.Equal(ModeEnabled, s.Mode()) + assert.Equal(DefaultPolicy, common.MapStr(s.Policy().Body)) + assert.Equal(Alias{Name: "alias", Pattern: "01"}, s.Alias()) + }) + + t.Run("load external policy", func(t *testing.T) { + s, err := DefaultSupport(nil, info, common.MustNewConfigFrom( + map[string]interface{}{ + "policy_file": "testfiles/custom.json", + }, + )) + require.NoError(t, err) + assert.Equal(t, map[string]interface{}{"hello": "world"}, s.Policy().Body) + }) +} + +func TestDefaultSupport_Manager_Enabled(t *testing.T) { + cases := map[string]struct { + calls []onCall + cfg map[string]interface{} + b bool + fail error + err bool + }{ + "disabled via config": { + cfg: map[string]interface{}{"enabled": false}, + }, + "disabled via handler": { + calls: []onCall{ + onILMEnabled(ModeAuto).Return(false, nil), + }, + }, + "enabled via handler": { + calls: []onCall{ + onILMEnabled(ModeAuto).Return(true, nil), + }, + b: true, + }, + "handler confirms enabled flag": { + calls: []onCall{ + onILMEnabled(ModeEnabled).Return(true, nil), + }, + cfg: map[string]interface{}{"enabled": true}, + b: true, + }, + "fail enabled": { + calls: []onCall{ + onILMEnabled(ModeEnabled).Return(false, nil), + }, + cfg: map[string]interface{}{"enabled": true}, + fail: ErrESILMDisabled, + }, + "io error": { + calls: []onCall{ + onILMEnabled(ModeAuto).Return(false, errors.New("ups")), + }, + cfg: map[string]interface{}{}, + err: true, + }, + } + + for name, test := range cases { + t.Run(name, func(t *testing.T) { + cfg := test.cfg + if cfg == nil { + cfg = map[string]interface{}{} + } + + h := newMockHandler(test.calls...) + m := createManager(t, h, test.cfg) + b, err := m.Enabled() + + if test.fail == nil && !test.err { + require.NoError(t, err) + } + if test.err || test.fail != nil { + require.Error(t, err) + } + if test.fail != nil { + assert.Equal(t, test.fail, ErrReason(err)) + } + + assert.Equal(t, test.b, b) + h.AssertExpectations(t) + }) + } +} + +func TestDefaultSupport_Manager_EnsureAlias(t *testing.T) { + alias := Alias{ + Name: "test-9.9.9", + Pattern: ilmDefaultPattern, + } + + cases := map[string]struct { + calls []onCall + cfg map[string]interface{} + fail error + }{ + "create new alias": { + calls: []onCall{ + onHasAlias(alias.Name).Return(false, nil), + onCreateAlias(alias).Return(nil), + }, + }, + "alias already exists": { + calls: []onCall{ + onHasAlias(alias.Name).Return(true, nil), + }, + }, + "fail": { + calls: []onCall{ + onHasAlias(alias.Name).Return(false, nil), + onCreateAlias(alias).Return(errOf(ErrRequestFailed)), + }, + fail: ErrRequestFailed, + }, + } + + for name, test := range cases { + t.Run(name, func(t *testing.T) { + cfg := test.cfg + if cfg == nil { + cfg = map[string]interface{}{"alias": "test"} + } + + h := newMockHandler(test.calls...) + m := createManager(t, h, test.cfg) + err := m.EnsureAlias() + + if test.fail == nil { + require.NoError(t, err) + } else { + require.Error(t, err) + assert.Equal(t, test.fail, ErrReason(err)) + } + h.AssertExpectations(t) + }) + } +} + +func TestDefaultSupport_Manager_EnsurePolicy(t *testing.T) { + testPolicy := Policy{ + Name: "test-9.9.9", + Body: DefaultPolicy, + } + + cases := map[string]struct { + calls []onCall + overwrite bool + cfg map[string]interface{} + fail error + }{ + "create new policy": { + calls: []onCall{ + onHasILMPolicy(testPolicy.Name).Return(false, nil), + onCreateILMPolicy(testPolicy).Return(nil), + }, + }, + "policy already exists": { + calls: []onCall{ + onHasILMPolicy(testPolicy.Name).Return(true, nil), + }, + }, + "overwrite existing": { + overwrite: true, + calls: []onCall{ + onCreateILMPolicy(testPolicy).Return(nil), + }, + }, + "fail": { + calls: []onCall{ + onHasILMPolicy(testPolicy.Name).Return(false, nil), + onCreateILMPolicy(testPolicy).Return(errOf(ErrRequestFailed)), + }, + fail: ErrRequestFailed, + }, + } + + for name, test := range cases { + test := test + t.Run(name, func(t *testing.T) { + cfg := test.cfg + if cfg == nil { + cfg = map[string]interface{}{"name": "test"} + } + + h := newMockHandler(test.calls...) + m := createManager(t, h, test.cfg) + err := m.EnsurePolicy(test.overwrite) + + if test.fail == nil { + require.NoError(t, err) + } else { + require.Error(t, err) + assert.Equal(t, test.fail, ErrReason(err)) + } + h.AssertExpectations(t) + }) + } +} + +func createManager(t *testing.T, h APIHandler, cfg map[string]interface{}) Manager { + info := beat.Info{Beat: "test", Version: "9.9.9"} + s, err := DefaultSupport(nil, info, common.MustNewConfigFrom(cfg)) + require.NoError(t, err) + return s.Manager(h) +} diff --git a/libbeat/idxmgmt/ilm/mockapihandler_test.go b/libbeat/idxmgmt/ilm/mockapihandler_test.go new file mode 100644 index 00000000000..6c1313b3091 --- /dev/null +++ b/libbeat/idxmgmt/ilm/mockapihandler_test.go @@ -0,0 +1,79 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package ilm + +import ( + "github.com/stretchr/testify/mock" +) + +type mockHandler struct { + mock.Mock +} + +type onCall struct { + name string + args []interface{} + returns []interface{} +} + +func (c onCall) Return(values ...interface{}) onCall { + c.returns = values + return c +} + +func newMockHandler(calls ...onCall) *mockHandler { + m := &mockHandler{} + for _, c := range calls { + m.On(c.name, c.args...).Return(c.returns...) + } + return m +} + +func onILMEnabled(m Mode) onCall { return makeOnCall("ILMEnabled", m) } +func (h *mockHandler) ILMEnabled(mode Mode) (bool, error) { + args := h.Called(mode) + return args.Bool(0), args.Error(1) +} + +func onHasAlias(name string) onCall { return makeOnCall("HasAlias", name) } +func (h *mockHandler) HasAlias(name string) (bool, error) { + args := h.Called(name) + return args.Bool(0), args.Error(1) +} + +func onCreateAlias(alias Alias) onCall { return makeOnCall("CreateAlias", alias) } +func (h *mockHandler) CreateAlias(alias Alias) error { + args := h.Called(alias) + return args.Error(0) +} + +func onHasILMPolicy(name string) onCall { return makeOnCall("HasILMPolicy", name) } +func (h *mockHandler) HasILMPolicy(name string) (bool, error) { + args := h.Called(name) + return args.Bool(0), args.Error(1) +} + +func onCreateILMPolicy(policy Policy) onCall { return makeOnCall("CreateILMPolicy", policy) } +func (h *mockHandler) CreateILMPolicy(policy Policy) error { + args := h.Called(policy) + return args.Error(0) +} + +func makeOnCall(name string, args ...interface{}) onCall { + return onCall{name: name, args: args} +} diff --git a/libbeat/idxmgmt/ilm/noop.go b/libbeat/idxmgmt/ilm/noop.go new file mode 100644 index 00000000000..129ca4c9eab --- /dev/null +++ b/libbeat/idxmgmt/ilm/noop.go @@ -0,0 +1,41 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package ilm + +import ( + "github.com/elastic/beats/libbeat/beat" + "github.com/elastic/beats/libbeat/common" +) + +type noopSupport struct{} +type noopManager struct{} + +// NoopSupport creates a noop ILM implementation with ILM support being always +// disabled. Attempts to install a policy or create a write alias will fail. +func NoopSupport(info beat.Info, config *common.Config) (Supporter, error) { + return (*noopSupport)(nil), nil +} + +func (*noopSupport) Mode() Mode { return ModeDisabled } +func (*noopSupport) Alias() Alias { return Alias{} } +func (*noopSupport) Policy() Policy { return Policy{} } +func (*noopSupport) Manager(_ APIHandler) Manager { return (*noopManager)(nil) } + +func (*noopManager) Enabled() (bool, error) { return false, nil } +func (*noopManager) EnsureAlias() error { return errOf(ErrOpNotAvailable) } +func (*noopManager) EnsurePolicy(_ bool) error { return errOf(ErrOpNotAvailable) } diff --git a/libbeat/idxmgmt/ilm/std.go b/libbeat/idxmgmt/ilm/std.go new file mode 100644 index 00000000000..8c55e471136 --- /dev/null +++ b/libbeat/idxmgmt/ilm/std.go @@ -0,0 +1,141 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package ilm + +import ( + "time" + + "github.com/elastic/beats/libbeat/logp" +) + +type ilmSupport struct { + log *logp.Logger + + mode Mode + overwrite bool + checkExists bool + + alias Alias + policy Policy +} + +type singlePolicyManager struct { + *ilmSupport + client APIHandler + + // cached info + cache infoCache +} + +type infoCache struct { + LastUpdate time.Time + Enabled bool +} + +var defaultCacheDuration = 5 * time.Minute + +// NewDefaultSupport creates an instance of default ILM support implementation. +func NewDefaultSupport( + log *logp.Logger, + mode Mode, + alias Alias, + policy Policy, + overwrite, checkExists bool, +) Supporter { + return &ilmSupport{ + log: log, + mode: mode, + overwrite: overwrite, + checkExists: checkExists, + alias: alias, + policy: policy, + } +} + +func (s *ilmSupport) Mode() Mode { return s.mode } +func (s *ilmSupport) Alias() Alias { return s.alias } +func (s *ilmSupport) Policy() Policy { return s.policy } + +func (s *ilmSupport) Manager(h APIHandler) Manager { + return &singlePolicyManager{ + client: h, + ilmSupport: s, + } +} + +func (m *singlePolicyManager) Enabled() (bool, error) { + if m.mode == ModeDisabled { + return false, nil + } + + if m.cache.Valid() { + return m.cache.Enabled, nil + } + + enabled, err := m.client.ILMEnabled(m.mode) + if err != nil { + return enabled, err + } + + if !enabled && m.mode == ModeEnabled { + return false, errOf(ErrESILMDisabled) + } + + m.cache.Enabled = enabled + m.cache.LastUpdate = time.Now() + return enabled, nil +} + +func (m *singlePolicyManager) EnsureAlias() error { + b, err := m.client.HasAlias(m.alias.Name) + if err != nil { + return err + } + if b { + return nil + } + + // This always assume it's a date pattern by sourrounding it by <...> + return m.client.CreateAlias(m.alias) +} + +func (m *singlePolicyManager) EnsurePolicy(overwrite bool) error { + log := m.log + overwrite = overwrite || m.overwrite + + exists := true + if m.checkExists && !overwrite { + b, err := m.client.HasILMPolicy(m.policy.Name) + if err != nil { + return err + } + exists = b + } + + if !exists || overwrite { + return m.client.CreateILMPolicy(m.policy) + } + + log.Infof("do not generate ilm policy: exists=%v, overwrite=%v", + exists, overwrite) + return nil +} + +func (c *infoCache) Valid() bool { + return !c.LastUpdate.IsZero() && time.Since(c.LastUpdate) < defaultCacheDuration +} diff --git a/libbeat/idxmgmt/ilm/testfiles/custom.json b/libbeat/idxmgmt/ilm/testfiles/custom.json new file mode 100644 index 00000000000..56c8e280338 --- /dev/null +++ b/libbeat/idxmgmt/ilm/testfiles/custom.json @@ -0,0 +1 @@ +{"hello": "world"} diff --git a/libbeat/idxmgmt/mockilm_test.go b/libbeat/idxmgmt/mockilm_test.go new file mode 100644 index 00000000000..5aac9419ace --- /dev/null +++ b/libbeat/idxmgmt/mockilm_test.go @@ -0,0 +1,96 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package idxmgmt + +import ( + "github.com/stretchr/testify/mock" + + "github.com/elastic/beats/libbeat/beat" + "github.com/elastic/beats/libbeat/common" + "github.com/elastic/beats/libbeat/idxmgmt/ilm" + "github.com/elastic/beats/libbeat/logp" +) + +type mockILMSupport struct { + mock.Mock +} + +type onCall struct { + name string + args []interface{} + returns []interface{} +} + +func makeMockILMSupport(calls ...onCall) ilm.SupportFactory { + return func(_ *logp.Logger, _ beat.Info, _ *common.Config) (ilm.Supporter, error) { + m := &mockILMSupport{} + for _, c := range calls { + m.On(c.name, c.args...).Return(c.returns...) + } + return m, nil + } +} + +func (c onCall) Return(values ...interface{}) onCall { + c.returns = values + return c +} + +func onMode() onCall { return makeOnCall("Mode") } +func (m *mockILMSupport) Mode() ilm.Mode { + args := m.Called() + return args.Get(0).(ilm.Mode) +} + +func onAlias() onCall { return makeOnCall("Alias") } +func (m *mockILMSupport) Alias() ilm.Alias { + args := m.Called() + return args.Get(0).(ilm.Alias) +} + +func onPolicy() onCall { return makeOnCall("Policy") } +func (m *mockILMSupport) Policy() ilm.Policy { + args := m.Called() + return args.Get(0).(ilm.Policy) +} + +func (m *mockILMSupport) Manager(_ ilm.APIHandler) ilm.Manager { + return m +} + +func onEnabled() onCall { return makeOnCall("Enabled") } +func (m *mockILMSupport) Enabled() (bool, error) { + args := m.Called() + return args.Bool(0), args.Error(1) +} + +func onEnsureAlias() onCall { return makeOnCall("EnsureAlias") } +func (m *mockILMSupport) EnsureAlias() error { + args := m.Called() + return args.Error(0) +} + +func onEnsurePolicy() onCall { return makeOnCall("EnsurePolicy") } +func (m *mockILMSupport) EnsurePolicy(overwrite bool) error { + args := m.Called() + return args.Error(0) +} + +func makeOnCall(name string, args ...interface{}) onCall { + return onCall{name: name, args: args} +} diff --git a/libbeat/idxmgmt/std.go b/libbeat/idxmgmt/std.go new file mode 100644 index 00000000000..a2e8149e7bb --- /dev/null +++ b/libbeat/idxmgmt/std.go @@ -0,0 +1,403 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package idxmgmt + +import ( + "errors" + "fmt" + + "github.com/elastic/beats/libbeat/beat" + "github.com/elastic/beats/libbeat/common" + "github.com/elastic/beats/libbeat/common/atomic" + "github.com/elastic/beats/libbeat/idxmgmt/ilm" + "github.com/elastic/beats/libbeat/logp" + "github.com/elastic/beats/libbeat/outputs" + "github.com/elastic/beats/libbeat/outputs/outil" + "github.com/elastic/beats/libbeat/template" +) + +type indexSupport struct { + log *logp.Logger + ilm ilm.Supporter + info beat.Info + migration bool + templateCfg template.TemplateConfig + defaultIndex string + + st indexState +} + +type indexState struct { + withILM atomic.Bool +} + +type indexManager struct { + support *indexSupport + ilm ilm.Manager + + client ESClient + assets Asseter +} + +type indexSelector outil.Selector + +type ilmIndexSelector struct { + index outil.Selector + alias outil.Selector + st *indexState +} + +func newIndexSupport( + log *logp.Logger, + info beat.Info, + ilmFactory ilm.SupportFactory, + tmplConfig *common.Config, + ilmConfig *common.Config, + migration bool, +) (*indexSupport, error) { + if ilmFactory == nil { + ilmFactory = ilm.DefaultSupport + } + + ilm, err := ilmFactory(log, info, ilmConfig) + if err != nil { + return nil, err + } + + tmplCfg, err := unpackTemplateConfig(tmplConfig) + if err != nil { + return nil, err + } + + return &indexSupport{ + log: log, + ilm: ilm, + info: info, + templateCfg: tmplCfg, + migration: migration, + defaultIndex: fmt.Sprintf("%v-%v-%%{+yyyy.MM.dd}", info.IndexPrefix, info.Version), + }, nil +} + +func (s *indexSupport) Enabled() bool { + return s.templateCfg.Enabled || (s.ilm.Mode() != ilm.ModeDisabled) +} + +func (s *indexSupport) ILM() ilm.Supporter { + return s.ilm +} + +func (s *indexSupport) TemplateConfig(withILM bool) (template.TemplateConfig, error) { + log := s.log + + cfg := s.templateCfg + if withILM { + if mode := s.ilm.Mode(); mode == ilm.ModeDisabled { + withILM = false + } else if mode == ilm.ModeEnabled { + withILM = true + } + } + + var err error + if withILM { + cfg, err = applyILMSettings(log, cfg, s.ilm.Policy(), s.ilm.Alias()) + } + return cfg, err +} + +func (s *indexSupport) Manager( + client ESClient, + assets Asseter, +) Manager { + ilm := s.ilm.Manager(ilm.ESClientHandler(client)) + return &indexManager{ + support: s, + ilm: ilm, + client: client, + assets: assets, + } +} + +func (s *indexSupport) BuildSelector(cfg *common.Config) (outputs.IndexSelector, error) { + var err error + log := s.log + + // we construct our own configuration object based on the available settings + // in cfg and defaultIndex. The configuration object provided must not be + // modified. + selCfg := common.NewConfig() + if cfg.HasField("indices") { + sub, err := cfg.Child("indices", -1) + if err != nil { + return nil, err + } + selCfg.SetChild("indices", -1, sub) + } + + var indexName string + if cfg.HasField("index") { + indexName, err = cfg.String("index", -1) + if err != nil { + return nil, err + } + } + + var alias string + mode := s.ilm.Mode() + if mode != ilm.ModeDisabled { + alias = s.ilm.Alias().Name + log.Infof("Set %v to '%s' as ILM is enabled.", cfg.PathOf("index"), alias) + } + if mode == ilm.ModeEnabled { + indexName = alias + } + + // no index name configuration found yet -> define default index name based on + // beat.Info provided to the indexSupport on during setup. + if indexName == "" { + indexName = s.defaultIndex + } + + selCfg.SetString("index", -1, indexName) + buildSettings := outil.Settings{ + Key: "index", + MultiKey: "indices", + EnableSingleOnly: true, + FailEmpty: mode != ilm.ModeEnabled, + } + + indexSel, err := outil.BuildSelectorFromConfig(selCfg, buildSettings) + if err != nil { + return nil, err + } + + if mode != ilm.ModeAuto { + return indexSelector(indexSel), nil + } + + selCfg.SetString("index", -1, alias) + aliasSel, err := outil.BuildSelectorFromConfig(selCfg, buildSettings) + return &ilmIndexSelector{ + index: indexSel, + alias: aliasSel, + st: &s.st, + }, nil +} + +func (m *indexManager) Setup(template, policy bool) error { + return m.load(template, policy) +} + +func (m *indexManager) Load() error { + return m.load(false, false) +} + +func (m *indexManager) load(forceTemplate, forcePolicy bool) error { + var err error + log := m.support.log + + withILM := m.support.st.withILM.Load() + if !withILM { + withILM, err = m.ilm.Enabled() + if err != nil { + return err + } + + if withILM { + log.Info("Auto ILM enable success.") + } + } + + // mark ILM as enabled in indexState if withILM is true + if withILM { + m.support.st.withILM.CAS(false, withILM) + } + + // install ilm policy + if withILM { + if err := m.ilm.EnsurePolicy(forcePolicy); err != nil { + return err + } + log.Info("ILM policy successfully loaded.") + } + + // create and install template + if m.support.templateCfg.Enabled { + tmplCfg := m.support.templateCfg + if withILM { + ilm := m.support.ilm + tmplCfg, err = applyILMSettings(log, tmplCfg, ilm.Policy(), ilm.Alias()) + if err != nil { + return err + } + } + + if forceTemplate { + tmplCfg.Overwrite = true + } + + fields := m.assets.Fields(m.support.info.Beat) + loader, err := template.NewLoader(tmplCfg, m.client, m.support.info, fields, m.support.migration) + if err != nil { + return fmt.Errorf("Error creating Elasticsearch template loader: %v", err) + } + + err = loader.Load() + if err != nil { + return fmt.Errorf("Error loading Elasticsearch template: %v", err) + } + + log.Info("Loaded index template.") + } + + // create alias + if withILM { + if err := m.ilm.EnsureAlias(); err != nil { + if ilm.ErrReason(err) != ilm.ErrAliasAlreadyExists { + return err + } + log.Info("Write alias exists already") + } else { + log.Info("Write alias successfully generated.") + } + } + + return nil +} + +func (s *ilmIndexSelector) Select(evt *beat.Event) (string, error) { + if idx := getEventCustomIndex(evt); idx != "" { + return idx, nil + } + + if s.st.withILM.Load() { + idx, err := s.alias.Select(evt) + return idx, err + } + + idx, err := s.index.Select(evt) + return idx, err +} + +func (s indexSelector) Select(evt *beat.Event) (string, error) { + if idx := getEventCustomIndex(evt); idx != "" { + return idx, nil + } + return outil.Selector(s).Select(evt) +} + +func getEventCustomIndex(evt *beat.Event) string { + if len(evt.Meta) == 0 { + return "" + } + + if tmp := evt.Meta["alias"]; tmp != nil { + if alias, ok := tmp.(string); ok { + return alias + } + } + + if tmp := evt.Meta["index"]; tmp != nil { + if idx, ok := tmp.(string); ok { + ts := evt.Timestamp.UTC() + return fmt.Sprintf("%s-%d.%02d.%02d", + idx, ts.Year(), ts.Month(), ts.Day()) + } + } + + return "" +} + +func unpackTemplateConfig(cfg *common.Config) (config template.TemplateConfig, err error) { + config = template.DefaultConfig() + if cfg != nil { + err = cfg.Unpack(&config) + } + return config, err +} + +func applyILMSettings( + log *logp.Logger, + tmpl template.TemplateConfig, + policy ilm.Policy, + alias ilm.Alias, +) (template.TemplateConfig, error) { + if !tmpl.Enabled { + return tmpl, nil + } + + if alias.Name == "" { + return tmpl, errors.New("no ilm rollover alias configured") + } + + if policy.Name == "" { + return tmpl, errors.New("no ilm policy name configured") + } + + tmpl.Name = alias.Name + if log != nil { + log.Infof("Set setup.template.name to '%s' as ILM is enabled.", alias) + } + + tmpl.Pattern = fmt.Sprintf("%s-*", alias.Name) + if log != nil { + log.Infof("Set setup.template.pattern to '%s' as ILM is enabled.", tmpl.Pattern) + } + + // rollover_alias and lifecycle.name can't be configured and will be overwritten + + // init/copy index settings + idxSettings := tmpl.Settings.Index + if idxSettings == nil { + idxSettings = map[string]interface{}{} + } else { + tmp := make(map[string]interface{}, len(idxSettings)) + for k, v := range idxSettings { + tmp[k] = v + } + idxSettings = tmp + } + tmpl.Settings.Index = idxSettings + + // init/copy index.lifecycle settings + var lifecycle map[string]interface{} + if ifcLifecycle := idxSettings["lifecycle"]; ifcLifecycle == nil { + lifecycle = map[string]interface{}{} + } else if tmp, ok := ifcLifecycle.(map[string]interface{}); ok { + lifecycle = make(map[string]interface{}, len(tmp)) + for k, v := range tmp { + lifecycle[k] = v + } + } else { + return tmpl, errors.New("settings.index.lifecycle must be an object") + } + idxSettings["lifecycle"] = lifecycle + + // add rollover_alias and name to index.lifecycle settings + if _, exists := lifecycle["rollover_alias"]; !exists { + log.Infof("Set settings.index.lifecycle.rollover_alias in template to %s as ILM is enabled.", alias) + lifecycle["rollover_alias"] = alias.Name + } + if _, exists := lifecycle["name"]; !exists { + log.Infof("Set settings.index.lifecycle.name in template to %s as ILM is enabled.", policy) + lifecycle["name"] = policy.Name + } + + return tmpl, nil +} diff --git a/libbeat/keystore/file_keystore.go b/libbeat/keystore/file_keystore.go index 0a7e03080a1..3f530a58120 100644 --- a/libbeat/keystore/file_keystore.go +++ b/libbeat/keystore/file_keystore.go @@ -234,41 +234,53 @@ func (k *FileKeystore) doSave(override bool) error { return nil } -func (k *FileKeystore) load() error { - k.Lock() - defer k.Unlock() - +func (k *FileKeystore) loadRaw() ([]byte, error) { f, err := os.OpenFile(k.Path, os.O_RDONLY, filePermission) if err != nil { if os.IsNotExist(err) { - return nil + return nil, nil } - return err + return nil, err } defer f.Close() if common.IsStrictPerms() { if err := k.checkPermissions(k.Path); err != nil { - return err + return nil, err } } raw, err := ioutil.ReadAll(f) if err != nil { - return err + return nil, err } v := raw[0:len(version)] if !bytes.Equal(v, version) { - return fmt.Errorf("keystore format doesn't match expected version: '%s' got '%s'", version, v) + return nil, fmt.Errorf("keystore format doesn't match expected version: '%s' got '%s'", version, v) } - base64Content := raw[len(version):] - if len(base64Content) == 0 { - return fmt.Errorf("corrupt or empty keystore") + if len(raw) <= len(version) { + return nil, fmt.Errorf("corrupt or empty keystore") } - base64Decoder := base64.NewDecoder(base64.StdEncoding, bytes.NewReader(base64Content)) + return raw, nil +} + +func (k *FileKeystore) load() error { + k.Lock() + defer k.Unlock() + + raw, err := k.loadRaw() + if err != nil { + return err + } + + if len(raw) == 0 { + return nil + } + + base64Decoder := base64.NewDecoder(base64.StdEncoding, bytes.NewReader(raw[len(version):])) plaintext, err := k.decrypt(base64Decoder) if err != nil { return fmt.Errorf("could not decrypt the keystore: %v", err) @@ -396,6 +408,13 @@ func (k *FileKeystore) checkPermissions(f string) error { return nil } +// Package returns the bytes of the encrypted keystore. +func (k *FileKeystore) Package() ([]byte, error) { + k.Lock() + defer k.Unlock() + return k.loadRaw() +} + func (k *FileKeystore) hashPassword(password, salt []byte) []byte { return pbkdf2.Key(password, salt, iterationsCount, keyLength, sha512.New) } diff --git a/libbeat/keystore/keystore.go b/libbeat/keystore/keystore.go index eb95c3e041d..f8d2da5c38c 100644 --- a/libbeat/keystore/keystore.go +++ b/libbeat/keystore/keystore.go @@ -64,6 +64,11 @@ type Keystore interface { Save() error } +// Packager defines a keystore that we can read the raw bytes and be packaged in an artifact. +type Packager interface { + Package() ([]byte, error) +} + // Factory Create the right keystore with the configured options. func Factory(cfg *common.Config, defaultPath string) (Keystore, error) { config := defaultConfig diff --git a/libbeat/kibana/client.go b/libbeat/kibana/client.go index d759ad432fb..e28bfb8d4fa 100644 --- a/libbeat/kibana/client.go +++ b/libbeat/kibana/client.go @@ -226,23 +226,15 @@ func (client *Client) readVersion() error { var kibanaVersion kibanaVersionResponse err = json.Unmarshal(result, &kibanaVersion) if err != nil { - var kibanaVersion5x kibanaVersionResponse5x - - // The response returned by /api/status is different in Kibana 5.x than in Kibana 6.x - err5x := json.Unmarshal(result, &kibanaVersion5x) - if err5x != nil { + return fmt.Errorf("fail to unmarshal the response from GET %s/api/status. Response: %s. Kibana status api returns: %v", + client.Connection.URL, truncateString(result), err) + } - return fmt.Errorf("fail to unmarshal the response from GET %s/api/status. Response: %s. Kibana 5.x status api returns: %v. Kibana 6.x status api returns: %v", - client.Connection.URL, truncateString(result), err5x, err) - } - versionString = kibanaVersion5x.Version - } else { - versionString = kibanaVersion.Version.Number + versionString = kibanaVersion.Version.Number - if kibanaVersion.Version.Snapshot { - // needed for the tests - versionString += "-SNAPSHOT" - } + if kibanaVersion.Version.Snapshot { + // needed for the tests + versionString += "-SNAPSHOT" } version, err := common.NewVersion(versionString) diff --git a/libbeat/metric/system/process/process.go b/libbeat/metric/system/process/process.go index 189b0964831..6cec53a27b4 100644 --- a/libbeat/metric/system/process/process.go +++ b/libbeat/metric/system/process/process.go @@ -45,14 +45,16 @@ type ProcsMap map[int]*Process // Process is the structure which holds the information of a process running on the host. // It includes pid, gid and it interacts with gosigar to fetch process data from the host. type Process struct { - Pid int `json:"pid"` - Ppid int `json:"ppid"` - Pgid int `json:"pgid"` - Name string `json:"name"` - Username string `json:"username"` - State string `json:"state"` - CmdLine string `json:"cmdline"` - Cwd string `json:"cwd"` + Pid int `json:"pid"` + Ppid int `json:"ppid"` + Pgid int `json:"pgid"` + Name string `json:"name"` + Username string `json:"username"` + State string `json:"state"` + Args []string `json:"args"` + CmdLine string `json:"cmdline"` + Cwd string `json:"cwd"` + Executable string `json:"executable"` Mem sigar.ProcMem Cpu sigar.ProcTime SampleTime time.Time @@ -98,15 +100,16 @@ func newProcess(pid int, cmdline string, env common.MapStr) (*Process, error) { } proc := Process{ - Pid: pid, - Ppid: state.Ppid, - Pgid: state.Pgid, - Name: state.Name, - Username: state.Username, - State: getProcState(byte(state.State)), - CmdLine: cmdline, - Cwd: exe.Cwd, - Env: env, + Pid: pid, + Ppid: state.Ppid, + Pgid: state.Pgid, + Name: state.Name, + Username: state.Username, + State: getProcState(byte(state.State)), + CmdLine: cmdline, + Cwd: exe.Cwd, + Executable: exe.Name, + Env: env, } return &proc, nil @@ -130,12 +133,16 @@ func (proc *Process) getDetails(envPredicate func(string) bool) error { return fmt.Errorf("error getting process cpu time for pid=%d: %v", proc.Pid, err) } - if proc.CmdLine == "" { + if len(proc.Args) == 0 { args := sigar.ProcArgs{} if err := args.Get(proc.Pid); err != nil && !sigar.IsNotImplemented(err) { return fmt.Errorf("error getting process arguments for pid=%d: %v", proc.Pid, err) } - proc.CmdLine = strings.Join(args.List, " ") + proc.Args = args.List + } + + if proc.CmdLine == "" && len(proc.Args) > 0 { + proc.CmdLine = strings.Join(proc.Args, " ") } if fd, err := getProcFDUsage(proc.Pid); err != nil { @@ -283,6 +290,10 @@ func (procStats *Stats) getProcessEvent(process *Process) common.MapStr { }, } + if len(process.Args) > 0 { + proc["args"] = process.Args + } + if process.CmdLine != "" { proc["cmdline"] = process.CmdLine } @@ -291,6 +302,10 @@ func (procStats *Stats) getProcessEvent(process *Process) common.MapStr { proc["cwd"] = process.Cwd } + if process.Executable != "" { + proc["exe"] = process.Executable + } + if len(process.Env) > 0 { proc["env"] = process.Env } diff --git a/libbeat/outputs/console/console.go b/libbeat/outputs/console/console.go index 66598fd6c53..c91d420674d 100644 --- a/libbeat/outputs/console/console.go +++ b/libbeat/outputs/console/console.go @@ -53,6 +53,7 @@ func init() { } func makeConsole( + _ outputs.IndexManager, beat beat.Info, observer outputs.Observer, cfg *common.Config, diff --git a/libbeat/outputs/elasticsearch/client.go b/libbeat/outputs/elasticsearch/client.go index 18dc9683345..5230f3c57a3 100644 --- a/libbeat/outputs/elasticsearch/client.go +++ b/libbeat/outputs/elasticsearch/client.go @@ -43,7 +43,7 @@ type Client struct { Connection tlsConfig *transport.TLSConfig - index outil.Selector + index outputs.IndexSelector pipeline *outil.Selector params map[string]string timeout time.Duration @@ -70,7 +70,7 @@ type ClientSettings struct { EscapeHTML bool Parameters map[string]string Headers map[string]string - Index outil.Selector + Index outputs.IndexSelector Pipeline *outil.Selector Timeout time.Duration CompressionLevel int @@ -366,7 +366,7 @@ func (client *Client) publishEvents( // successfully added to bulk request. func bulkEncodePublishRequest( body bulkWriter, - index outil.Selector, + index outputs.IndexSelector, pipeline *outil.Selector, eventType string, data []publisher.Event, @@ -390,7 +390,7 @@ func bulkEncodePublishRequest( } func createEventBulkMeta( - indexSel outil.Selector, + indexSel outputs.IndexSelector, pipelineSel *outil.Selector, eventType string, event *beat.Event, @@ -401,7 +401,7 @@ func createEventBulkMeta( return nil, err } - index, err := getIndex(event, indexSel) + index, err := indexSel.Select(event) if err != nil { err := fmt.Errorf("failed to select event index: %v", err) return nil, err @@ -447,24 +447,6 @@ func getPipeline(event *beat.Event, pipelineSel *outil.Selector) (string, error) return "", nil } -// getIndex returns the full index name -// Index is either defined in the config as part of the output -// or can be overload by the event through setting index -func getIndex(event *beat.Event, index outil.Selector) (string, error) { - if event.Meta != nil { - if str, exists := event.Meta["index"]; exists { - idx, ok := str.(string) - if ok { - ts := event.Timestamp.UTC() - return fmt.Sprintf("%s-%d.%02d.%02d", - idx, ts.Year(), ts.Month(), ts.Day()), nil - } - } - } - - return index.Select(event) -} - // bulkCollectPublishFails checks per item errors returning all events // to be tried again due to error code returned for that items. If indexing an // event failed due to some error in the event itself (e.g. does not respect mapping), diff --git a/libbeat/outputs/elasticsearch/client_integration_test.go b/libbeat/outputs/elasticsearch/client_integration_test.go index 8a3a7d5f5b9..fb690c23c9f 100644 --- a/libbeat/outputs/elasticsearch/client_integration_test.go +++ b/libbeat/outputs/elasticsearch/client_integration_test.go @@ -28,6 +28,7 @@ import ( "github.com/elastic/beats/libbeat/beat" "github.com/elastic/beats/libbeat/common" + "github.com/elastic/beats/libbeat/idxmgmt" "github.com/elastic/beats/libbeat/logp" "github.com/elastic/beats/libbeat/outputs" "github.com/elastic/beats/libbeat/outputs/elasticsearch/internal" @@ -262,7 +263,9 @@ func connectTestEs(t *testing.T, cfg interface{}) (outputs.Client, *Client) { t.Fatal(err) } - output, err := makeES(beat.Info{Beat: "libbeat"}, outputs.NewNilObserver(), config) + info := beat.Info{Beat: "libbeat"} + im, _ := idxmgmt.DefaultSupport(nil, info, nil) + output, err := makeES(im, info, outputs.NewNilObserver(), config) if err != nil { t.Fatal(err) } diff --git a/libbeat/outputs/elasticsearch/client_test.go b/libbeat/outputs/elasticsearch/client_test.go index 3d626935caa..27825c65353 100644 --- a/libbeat/outputs/elasticsearch/client_test.go +++ b/libbeat/outputs/elasticsearch/client_test.go @@ -32,7 +32,7 @@ import ( "github.com/elastic/beats/libbeat/beat" "github.com/elastic/beats/libbeat/common" - "github.com/elastic/beats/libbeat/common/fmtstr" + "github.com/elastic/beats/libbeat/idxmgmt" "github.com/elastic/beats/libbeat/logp" "github.com/elastic/beats/libbeat/outputs/outest" "github.com/elastic/beats/libbeat/outputs/outil" @@ -189,47 +189,6 @@ func TestCollectPipelinePublishFail(t *testing.T) { assert.Equal(t, events, res) } -func TestGetIndexStandard(t *testing.T) { - ts := time.Now().UTC() - extension := fmt.Sprintf("%d.%02d.%02d", ts.Year(), ts.Month(), ts.Day()) - fields := common.MapStr{"field": 1} - - pattern := "beatname-%{+yyyy.MM.dd}" - fmtstr := fmtstr.MustCompileEvent(pattern) - indexSel := outil.MakeSelector(outil.FmtSelectorExpr(fmtstr, "")) - - event := &beat.Event{Timestamp: ts, Fields: fields} - index, _ := getIndex(event, indexSel) - assert.Equal(t, index, "beatname-"+extension) -} - -func TestGetIndexOverwrite(t *testing.T) { - time := time.Now().UTC() - extension := fmt.Sprintf("%d.%02d.%02d", time.Year(), time.Month(), time.Day()) - - fields := common.MapStr{ - "@timestamp": common.Time(time), - "field": 1, - "beat": common.MapStr{ - "name": "testbeat", - }, - } - - pattern := "beatname-%%{+yyyy.MM.dd}" - fmtstr := fmtstr.MustCompileEvent(pattern) - indexSel := outil.MakeSelector(outil.FmtSelectorExpr(fmtstr, "")) - - event := &beat.Event{ - Timestamp: time, - Meta: map[string]interface{}{ - "index": "dynamicindex", - }, - Fields: fields} - index, _ := getIndex(event, indexSel) - expected := "dynamicindex-" + extension - assert.Equal(t, expected, index) -} - func BenchmarkCollectPublishFailsNone(b *testing.B) { response := []byte(` { "items": [ @@ -406,11 +365,15 @@ func TestBulkEncodeEvents(t *testing.T) { test := test t.Run(name, func(t *testing.T) { cfg := common.MustNewConfigFrom(test.config) - - index, pipeline, err := buildSelectors(beat.Info{ + info := beat.Info{ IndexPrefix: "test", Version: version.GetDefaultVersion(), - }, cfg) + } + + im, err := idxmgmt.DefaultSupport(nil, info, common.NewConfig()) + require.NoError(t, err) + + index, pipeline, err := buildSelectors(im, info, cfg) require.NoError(t, err) events := make([]publisher.Event, len(test.events)) diff --git a/libbeat/outputs/elasticsearch/elasticsearch.go b/libbeat/outputs/elasticsearch/elasticsearch.go index 0c9dd08537c..f604190c31c 100644 --- a/libbeat/outputs/elasticsearch/elasticsearch.go +++ b/libbeat/outputs/elasticsearch/elasticsearch.go @@ -100,6 +100,7 @@ func DeregisterConnectCallback(key uuid.UUID) { } func makeES( + im outputs.IndexManager, beat beat.Info, observer outputs.Observer, cfg *common.Config, @@ -108,7 +109,7 @@ func makeES( cfg.SetInt("bulk_max_size", -1, defaultBulkSize) } - index, pipeline, err := buildSelectors(beat, cfg) + index, pipeline, err := buildSelectors(im, beat, cfg) if err != nil { return outputs.Fail(err) } @@ -177,20 +178,11 @@ func makeES( } func buildSelectors( + im outputs.IndexManager, beat beat.Info, cfg *common.Config, -) (index outil.Selector, pipeline *outil.Selector, err error) { - if !cfg.HasField("index") { - pattern := fmt.Sprintf("%v-%v-%%{+yyyy.MM.dd}", beat.IndexPrefix, beat.Version) - cfg.SetString("index", -1, pattern) - } - - index, err = outil.BuildSelectorFromConfig(cfg, outil.Settings{ - Key: "index", - MultiKey: "indices", - EnableSingleOnly: true, - FailEmpty: true, - }) +) (index outputs.IndexSelector, pipeline *outil.Selector, err error) { + index, err = im.BuildSelector(cfg) if err != nil { return index, pipeline, err } diff --git a/libbeat/outputs/fileout/file.go b/libbeat/outputs/fileout/file.go index 1b05fbb783f..ab5b040bea5 100644 --- a/libbeat/outputs/fileout/file.go +++ b/libbeat/outputs/fileout/file.go @@ -44,6 +44,7 @@ type fileOutput struct { // makeFileout instantiates a new file output instance. func makeFileout( + _ outputs.IndexManager, beat beat.Info, observer outputs.Observer, cfg *common.Config, diff --git a/libbeat/outputs/kafka/kafka.go b/libbeat/outputs/kafka/kafka.go index d9f9e86ac18..dc34e77c558 100644 --- a/libbeat/outputs/kafka/kafka.go +++ b/libbeat/outputs/kafka/kafka.go @@ -70,6 +70,7 @@ func kafkaMetricsRegistry() gometrics.Registry { } func makeKafka( + _ outputs.IndexManager, beat beat.Info, observer outputs.Observer, cfg *common.Config, diff --git a/libbeat/outputs/kafka/kafka_integration_test.go b/libbeat/outputs/kafka/kafka_integration_test.go index 78df0a12a08..4d85003a719 100644 --- a/libbeat/outputs/kafka/kafka_integration_test.go +++ b/libbeat/outputs/kafka/kafka_integration_test.go @@ -199,7 +199,7 @@ func TestKafkaPublish(t *testing.T) { } t.Run(name, func(t *testing.T) { - grp, err := makeKafka(beat.Info{Beat: "libbeat"}, outputs.NewNilObserver(), cfg) + grp, err := makeKafka(nil, beat.Info{Beat: "libbeat"}, outputs.NewNilObserver(), cfg) if err != nil { t.Fatal(err) } diff --git a/libbeat/outputs/logstash/logstash.go b/libbeat/outputs/logstash/logstash.go index 02928ad0d08..0c14bf5882b 100644 --- a/libbeat/outputs/logstash/logstash.go +++ b/libbeat/outputs/logstash/logstash.go @@ -40,6 +40,7 @@ func init() { } func makeLogstash( + _ outputs.IndexManager, beat beat.Info, observer outputs.Observer, cfg *common.Config, diff --git a/libbeat/outputs/logstash/logstash_integration_test.go b/libbeat/outputs/logstash/logstash_integration_test.go index ba00c59de13..ead21295c76 100644 --- a/libbeat/outputs/logstash/logstash_integration_test.go +++ b/libbeat/outputs/logstash/logstash_integration_test.go @@ -32,6 +32,7 @@ import ( "github.com/elastic/beats/libbeat/beat" "github.com/elastic/beats/libbeat/common" "github.com/elastic/beats/libbeat/common/fmtstr" + "github.com/elastic/beats/libbeat/idxmgmt" "github.com/elastic/beats/libbeat/outputs" "github.com/elastic/beats/libbeat/outputs/elasticsearch" "github.com/elastic/beats/libbeat/outputs/outest" @@ -175,7 +176,17 @@ func newTestElasticsearchOutput(t *testing.T, test string) *testOutputer { "template.enabled": false, }) - grp, err := plugin(beat.Info{Beat: "libbeat"}, outputs.NewNilObserver(), config) + info := beat.Info{Beat: "libbeat"} + im, err := idxmgmt.DefaultSupport(nil, info, common.MustNewConfigFrom( + map[string]interface{}{ + "setup.ilm.enabled": false, + }, + )) + if err != nil { + t.Fatal("init index management:", err) + } + + grp, err := plugin(im, info, outputs.NewNilObserver(), config) if err != nil { t.Fatalf("init elasticsearch output plugin failed: %v", err) } diff --git a/libbeat/outputs/logstash/logstash_test.go b/libbeat/outputs/logstash/logstash_test.go index 9c94a18ae1d..7c1552fcb83 100644 --- a/libbeat/outputs/logstash/logstash_test.go +++ b/libbeat/outputs/logstash/logstash_test.go @@ -179,7 +179,7 @@ func newTestLumberjackOutput( } cfg, _ := common.NewConfigFrom(config) - grp, err := outputs.Load(beat.Info{}, nil, "logstash", cfg) + grp, err := outputs.Load(nil, beat.Info{}, nil, "logstash", cfg) if err != nil { t.Fatalf("init logstash output plugin failed: %v", err) } diff --git a/libbeat/outputs/output_reg.go b/libbeat/outputs/output_reg.go index 5b669c9413b..6625f1125b1 100644 --- a/libbeat/outputs/output_reg.go +++ b/libbeat/outputs/output_reg.go @@ -28,10 +28,25 @@ var outputReg = map[string]Factory{} // Factory is used by output plugins to build an output instance type Factory func( + im IndexManager, beat beat.Info, stats Observer, cfg *common.Config) (Group, error) +// IndexManager provides additional index related services to the outputs. +type IndexManager interface { + // BuildSelector can be used by an output to create an IndexSelector based on + // the outputs configuration. + // The defaultIndex is interpreted as format string and used as default fallback + // if no index is configured or all indices are guarded using conditionals. + BuildSelector(cfg *common.Config) (IndexSelector, error) +} + +// IndexSelector is used to find the index name an event shall be indexed to. +type IndexSelector interface { + Select(event *beat.Event) (string, error) +} + // Group configures and combines multiple clients into load-balanced group of clients // being managed by the publisher pipeline. type Group struct { @@ -54,7 +69,13 @@ func FindFactory(name string) Factory { } // Load creates and configures a output Group using a configuration object.. -func Load(info beat.Info, stats Observer, name string, config *common.Config) (Group, error) { +func Load( + im IndexManager, + info beat.Info, + stats Observer, + name string, + config *common.Config, +) (Group, error) { factory := FindFactory(name) if factory == nil { return Group{}, fmt.Errorf("output type %v undefined", name) @@ -63,5 +84,5 @@ func Load(info beat.Info, stats Observer, name string, config *common.Config) (G if stats == nil { stats = NewNilObserver() } - return factory(info, stats, config) + return factory(im, info, stats, config) } diff --git a/libbeat/outputs/redis/redis.go b/libbeat/outputs/redis/redis.go index 66bcaa0e72d..97a6b9fdafa 100644 --- a/libbeat/outputs/redis/redis.go +++ b/libbeat/outputs/redis/redis.go @@ -49,6 +49,7 @@ func init() { } func makeRedis( + _ outputs.IndexManager, beat beat.Info, observer outputs.Observer, cfg *common.Config, diff --git a/libbeat/outputs/redis/redis_integration_test.go b/libbeat/outputs/redis/redis_integration_test.go index 57f491caa43..bf0fcf0ac21 100644 --- a/libbeat/outputs/redis/redis_integration_test.go +++ b/libbeat/outputs/redis/redis_integration_test.go @@ -289,7 +289,7 @@ func newRedisTestingOutput(t *testing.T, cfg map[string]interface{}) outputs.Cli t.Fatalf("redis output module not registered") } - out, err := plugin(beat.Info{Beat: testBeatname, Version: testBeatversion}, outputs.NewNilObserver(), config) + out, err := plugin(nil, beat.Info{Beat: testBeatname, Version: testBeatversion}, outputs.NewNilObserver(), config) if err != nil { t.Fatalf("Failed to initialize redis output: %v", err) } diff --git a/libbeat/processors/add_docker_metadata/add_docker_metadata_test.go b/libbeat/processors/add_docker_metadata/add_docker_metadata_test.go index 719e95556bf..385951765e6 100644 --- a/libbeat/processors/add_docker_metadata/add_docker_metadata_test.go +++ b/libbeat/processors/add_docker_metadata/add_docker_metadata_test.go @@ -104,6 +104,7 @@ func TestMatchNoContainer(t *testing.T) { func TestMatchContainer(t *testing.T) { testConfig, err := common.NewConfigFrom(map[string]interface{}{ "match_fields": []string{"foo"}, + "labels.dedot": false, }) assert.NoError(t, err) @@ -152,7 +153,6 @@ func TestMatchContainer(t *testing.T) { func TestMatchContainerWithDedot(t *testing.T) { testConfig, err := common.NewConfigFrom(map[string]interface{}{ "match_fields": []string{"foo"}, - "labels.dedot": true, }) assert.NoError(t, err) diff --git a/libbeat/processors/add_docker_metadata/config.go b/libbeat/processors/add_docker_metadata/config.go index dc1a3d4fc89..aa78dd08ca0 100644 --- a/libbeat/processors/add_docker_metadata/config.go +++ b/libbeat/processors/add_docker_metadata/config.go @@ -46,6 +46,6 @@ func defaultConfig() Config { MatchSource: true, SourceIndex: 4, // Use 4 to match the CID in /var/lib/docker/containers//*.log. MatchPIDs: []string{"process.pid", "process.ppid"}, - DeDot: false, + DeDot: true, } } diff --git a/libbeat/publisher/pipeline/controller.go b/libbeat/publisher/pipeline/controller.go index 72e4c1487a4..cbd1b520537 100644 --- a/libbeat/publisher/pipeline/controller.go +++ b/libbeat/publisher/pipeline/controller.go @@ -146,16 +146,22 @@ func makeWorkQueue() workQueue { } // Reload the output -func (c *outputController) Reload(cfg *reload.ConfigWithMeta) error { - outputCfg := common.ConfigNamespace{} - +func (c *outputController) Reload( + cfg *reload.ConfigWithMeta, + outFactory func(outputs.Observer, common.ConfigNamespace) (outputs.Group, error), +) error { + outCfg := common.ConfigNamespace{} if cfg != nil { - if err := cfg.Config.Unpack(&outputCfg); err != nil { + if err := cfg.Config.Unpack(&outCfg); err != nil { return err } } - output, err := loadOutput(c.beat, c.monitors, outputCfg) + output, err := loadOutput(c.monitors, func(stats outputs.Observer) (string, outputs.Group, error) { + name := outCfg.Name() + out, err := outFactory(stats, outCfg) + return name, out, err + }) if err != nil { return err } diff --git a/libbeat/publisher/pipeline/module.go b/libbeat/publisher/pipeline/module.go index e9f50779d02..2c854c13676 100644 --- a/libbeat/publisher/pipeline/module.go +++ b/libbeat/publisher/pipeline/module.go @@ -45,6 +45,11 @@ type Monitors struct { Logger *logp.Logger } +// OutputFactory is used by the publisher pipeline to create an output instance. +// If the group returned can be empty. The pipeline will accept events, but +// eventually block. +type OutputFactory func(outputs.Observer) (string, outputs.Group, error) + func init() { flag.BoolVar(&publishDisabled, "N", false, "Disable actual publishing for testing") } @@ -55,7 +60,7 @@ func Load( beatInfo beat.Info, monitors Monitors, config Config, - outcfg common.ConfigNamespace, + makeOutput func(outputs.Observer) (string, outputs.Group, error), ) (*Pipeline, error) { log := monitors.Logger if log == nil { @@ -95,7 +100,7 @@ func Load( return nil, err } - out, err := loadOutput(beatInfo, monitors, outcfg) + out, err := loadOutput(monitors, makeOutput) if err != nil { return nil, err } @@ -110,9 +115,8 @@ func Load( } func loadOutput( - beatInfo beat.Info, monitors Monitors, - outcfg common.ConfigNamespace, + makeOutput OutputFactory, ) (outputs.Group, error) { log := monitors.Logger if log == nil { @@ -123,7 +127,7 @@ func loadOutput( return outputs.Group{}, nil } - if !outcfg.IsSet() { + if makeOutput == nil { return outputs.Group{}, nil } @@ -141,13 +145,13 @@ func loadOutput( outStats = outputs.NewStats(metrics) } - out, err := outputs.Load(beatInfo, outStats, outcfg.Name(), outcfg.Config()) + outName, out, err := makeOutput(outStats) if err != nil { return outputs.Fail(err) } if metrics != nil { - monitoring.NewString(metrics, "type").Set(outcfg.Name()) + monitoring.NewString(metrics, "type").Set(outName) } if monitors.Telemetry != nil { telemetry := monitors.Telemetry.GetRegistry("output") @@ -156,7 +160,7 @@ func loadOutput( } else { telemetry = monitors.Telemetry.NewRegistry("output") } - monitoring.NewString(telemetry, "name").Set(outcfg.Name()) + monitoring.NewString(telemetry, "name").Set(outName) } return out, nil diff --git a/libbeat/publisher/pipeline/pipeline.go b/libbeat/publisher/pipeline/pipeline.go index 00c203f3f22..fbe49510e0d 100644 --- a/libbeat/publisher/pipeline/pipeline.go +++ b/libbeat/publisher/pipeline/pipeline.go @@ -131,6 +131,15 @@ const ( WaitOnClientClose ) +// OutputReloader interface, that can be queried from an active publisher pipeline. +// The output reloader can be used to change the active output. +type OutputReloader interface { + Reload( + cfg *reload.ConfigWithMeta, + factory func(outputs.Observer, common.ConfigNamespace) (outputs.Group, error), + ) error +} + type pipelineEventer struct { mutex sync.Mutex modifyable bool @@ -442,6 +451,6 @@ func makePipelineProcessors( } // OutputReloader returns a reloadable object for the output section of this pipeline -func (p *Pipeline) OutputReloader() reload.Reloadable { +func (p *Pipeline) OutputReloader() OutputReloader { return p.output } diff --git a/libbeat/publisher/pipeline/stress/out.go b/libbeat/publisher/pipeline/stress/out.go index 5bc72ed33cd..211e056ce60 100644 --- a/libbeat/publisher/pipeline/stress/out.go +++ b/libbeat/publisher/pipeline/stress/out.go @@ -53,7 +53,7 @@ func init() { outputs.RegisterType("test", makeTestOutput) } -func makeTestOutput(beat beat.Info, observer outputs.Observer, cfg *common.Config) (outputs.Group, error) { +func makeTestOutput(_ outputs.IndexManager, beat beat.Info, observer outputs.Observer, cfg *common.Config) (outputs.Group, error) { config := defaultTestOutputConfig if err := cfg.Unpack(&config); err != nil { return outputs.Fail(err) diff --git a/libbeat/publisher/pipeline/stress/run.go b/libbeat/publisher/pipeline/stress/run.go index e21d3f29d26..3ebce0351f1 100644 --- a/libbeat/publisher/pipeline/stress/run.go +++ b/libbeat/publisher/pipeline/stress/run.go @@ -25,6 +25,7 @@ import ( "github.com/elastic/beats/libbeat/beat" "github.com/elastic/beats/libbeat/common" "github.com/elastic/beats/libbeat/logp" + "github.com/elastic/beats/libbeat/outputs" "github.com/elastic/beats/libbeat/publisher/pipeline" ) @@ -57,13 +58,19 @@ func RunTests( return fmt.Errorf("unpacking config failed: %v", err) } - pipeline, err := pipeline.Load(info, pipeline.Monitors{ - Metrics: nil, - Telemetry: nil, - Logger: logp.L(), - }, + pipeline, err := pipeline.Load(info, + pipeline.Monitors{ + Metrics: nil, + Telemetry: nil, + Logger: logp.L(), + }, config.Pipeline, - config.Output) + func(stat outputs.Observer) (string, outputs.Group, error) { + cfg := config.Output + out, err := outputs.Load(nil, info, stat, cfg.Name(), cfg.Config()) + return cfg.Name(), out, err + }, + ) if err != nil { return fmt.Errorf("loading pipeline failed: %+v", err) } diff --git a/libbeat/scripts/Makefile b/libbeat/scripts/Makefile index 0aba68b88d6..47196dd5f9c 100755 --- a/libbeat/scripts/Makefile +++ b/libbeat/scripts/Makefile @@ -202,7 +202,12 @@ integration-tests: prepare-tests .PHONY: integration-tests-environment integration-tests-environment: ## @testing Runs the integration inside a virtual environment. This can be run on any docker-machine (local, remote) integration-tests-environment: prepare-tests build-image - ${DOCKER_COMPOSE} run beat make integration-tests RACE_DETECTOR=$(RACE_DETECTOR) DOCKER_COMPOSE_PROJECT_NAME=${DOCKER_COMPOSE_PROJECT_NAME} || ${DOCKER_COMPOSE} logs --tail 200 + # If you want to get logs on CI when the `docker-compose` fails, you can + # add `|| ${DOCKER_COMPOSE} logs --tail 200` after the command. + # + # This will make docker-compose command to display the logs on stdout on error, It's not enabled + # by default because it can create noise if the test inside the container fails. + ${DOCKER_COMPOSE} run beat make integration-tests RACE_DETECTOR=$(RACE_DETECTOR) DOCKER_COMPOSE_PROJECT_NAME=${DOCKER_COMPOSE_PROJECT_NAME} # Runs the system tests .PHONY: system-tests diff --git a/libbeat/template/config.go b/libbeat/template/config.go index 77e93d72011..ed939fcc9a0 100644 --- a/libbeat/template/config.go +++ b/libbeat/template/config.go @@ -39,10 +39,10 @@ type TemplateSettings struct { Source map[string]interface{} `config:"_source"` } -var ( - // DefaultConfig for index template - DefaultConfig = TemplateConfig{ +// DefaultConfig for index template +func DefaultConfig() TemplateConfig { + return TemplateConfig{ Enabled: true, Fields: "", } -) +} diff --git a/libbeat/template/load.go b/libbeat/template/load.go index cf357f952ea..99978796f78 100644 --- a/libbeat/template/load.go +++ b/libbeat/template/load.go @@ -45,14 +45,13 @@ type Loader struct { } // NewLoader creates a new template loader -func NewLoader(cfg *common.Config, client ESClient, beatInfo beat.Info, fields []byte, migration bool) (*Loader, error) { - config := DefaultConfig - - err := cfg.Unpack(&config) - if err != nil { - return nil, err - } - +func NewLoader( + config TemplateConfig, + client ESClient, + beatInfo beat.Info, + fields []byte, + migration bool, +) (*Loader, error) { return &Loader{ config: config, client: client, diff --git a/libbeat/template/load_integration_test.go b/libbeat/template/load_integration_test.go index 741fc684441..b1f4e01c4a5 100644 --- a/libbeat/template/load_integration_test.go +++ b/libbeat/template/load_integration_test.go @@ -236,17 +236,17 @@ func TestOverwrite(t *testing.T) { client.Request("DELETE", "/_template/"+templateName, "", nil, nil) // Load template - config := newConfigFrom(t, TemplateConfig{ + config := TemplateConfig{ Enabled: true, Fields: absPath + "/fields.yml", - }) + } loader, err := NewLoader(config, client, beatInfo, nil, false) assert.NoError(t, err) err = loader.Load() assert.NoError(t, err) // Load template again, this time with custom settings - config = newConfigFrom(t, TemplateConfig{ + config = TemplateConfig{ Enabled: true, Fields: absPath + "/fields.yml", Settings: TemplateSettings{ @@ -254,7 +254,7 @@ func TestOverwrite(t *testing.T) { "enabled": false, }, }, - }) + } loader, err = NewLoader(config, client, beatInfo, nil, false) assert.NoError(t, err) err = loader.Load() @@ -265,7 +265,7 @@ func TestOverwrite(t *testing.T) { assert.Equal(t, true, templateJSON.SourceEnabled()) // Load template again, this time with custom settings AND overwrite: true - config = newConfigFrom(t, TemplateConfig{ + config = TemplateConfig{ Enabled: true, Overwrite: true, Fields: absPath + "/fields.yml", @@ -274,7 +274,7 @@ func TestOverwrite(t *testing.T) { "enabled": false, }, }, - }) + } loader, err = NewLoader(config, client, beatInfo, nil, false) assert.NoError(t, err) err = loader.Load() @@ -372,12 +372,6 @@ func TestTemplateWithData(t *testing.T) { assert.False(t, loader.CheckTemplate(tmpl.GetName())) } -func newConfigFrom(t *testing.T, from interface{}) *common.Config { - cfg, err := common.NewConfigFrom(from) - assert.NoError(t, err) - return cfg -} - func getTemplate(t *testing.T, client ESClient, templateName string) testTemplate { status, body, err := client.Request("GET", "/_template/"+templateName, "", nil, nil) assert.NoError(t, err) diff --git a/libbeat/template/template.go b/libbeat/template/template.go index bd5b0714599..1e829d196b1 100644 --- a/libbeat/template/template.go +++ b/libbeat/template/template.go @@ -46,13 +46,20 @@ type Template struct { name string pattern string beatVersion common.Version + beatName string esVersion common.Version config TemplateConfig migration bool } // New creates a new template instance -func New(beatVersion string, beatName string, esVersion common.Version, config TemplateConfig, migration bool) (*Template, error) { +func New( + beatVersion string, + beatName string, + esVersion common.Version, + config TemplateConfig, + migration bool, +) (*Template, error) { bV, err := common.NewVersion(beatVersion) if err != nil { return nil, err @@ -116,6 +123,7 @@ func New(beatVersion string, beatName string, esVersion common.Version, config T name: name, beatVersion: *bV, esVersion: esVersion, + beatName: beatName, config: config, migration: migration, }, nil @@ -189,7 +197,7 @@ func (t *Template) Generate(properties common.MapStr, dynamicTemplates []common. keyPattern: patterns, "mappings": buildMappings( - t.beatVersion, t.esVersion, + t.beatVersion, t.esVersion, t.beatName, properties, append(dynamicTemplates, buildDynTmpl(t.esVersion)), common.MapStr(t.config.Settings.Source), @@ -215,6 +223,7 @@ func buildPatternSettings(ver common.Version, pattern string) (string, interface func buildMappings( beatVersion, esVersion common.Version, + beatName string, properties common.MapStr, dynTmpls []common.MapStr, source common.MapStr, @@ -222,6 +231,7 @@ func buildMappings( mapping := common.MapStr{ "_meta": common.MapStr{ "version": beatVersion.String(), + "beat": beatName, }, "date_detection": defaultDateDetection, "dynamic_templates": dynTmpls, diff --git a/libbeat/template/template_test.go b/libbeat/template/template_test.go index 144b3318e35..814656f9185 100644 --- a/libbeat/template/template_test.go +++ b/libbeat/template/template_test.go @@ -76,3 +76,17 @@ func TestNumberOfRoutingShardsOverwrite(t *testing.T) { assert.Equal(t, 5, shards.(int)) } + +func TestTemplate(t *testing.T) { + beatVersion := "6.6.0" + beatName := "testbeat" + ver := common.MustNewVersion("6.6.0") + template, err := New(beatVersion, beatName, *ver, TemplateConfig{}, false) + assert.NoError(t, err) + + data := template.Generate(nil, nil) + assert.Equal(t, []string{"testbeat-6.6.0-*"}, data["index_patterns"]) + meta, err := data.GetValue("mappings.doc._meta") + assert.NoError(t, err) + assert.Equal(t, common.MapStr{"beat": "testbeat", "version": "6.6.0"}, meta) +} diff --git a/libbeat/tests/system/beat/beat.py b/libbeat/tests/system/beat/beat.py index 7e114f2def2..fa88fb1aba3 100644 --- a/libbeat/tests/system/beat/beat.py +++ b/libbeat/tests/system/beat/beat.py @@ -221,6 +221,8 @@ def start_beat(self, def render_config_template(self, template_name=None, output=None, **kargs): + print("render config") + # Init defaults if template_name is None: template_name = self.beat_name diff --git a/libbeat/tests/system/config/libbeat.yml.j2 b/libbeat/tests/system/config/libbeat.yml.j2 index 839836be287..5ac42df8446 100644 --- a/libbeat/tests/system/config/libbeat.yml.j2 +++ b/libbeat/tests/system/config/libbeat.yml.j2 @@ -22,6 +22,18 @@ setup.template.name: "{{setup_template_name}}" setup.template.pattern: "{{setup_template_pattern}}" {%- endif %} +{% if ilm %} +setup.ilm: + enabled: {{ ilm.enabled | default("auto") }} + policy_name: libbeat-test-default-policy + {% if ilm.pattern %} + pattern: {{ ilm.pattern }} + {% endif %} + {% if ilm.rollover_alias %} + rollover_alias: {{ ilm.rollover_alias }} + {% endif %} +{% endif %} + #================================ Processors ===================================== {%- if processors %} diff --git a/libbeat/tests/system/config/mockbeat.yml.j2 b/libbeat/tests/system/config/mockbeat.yml.j2 index 134545b9970..45893b1bddb 100644 --- a/libbeat/tests/system/config/mockbeat.yml.j2 +++ b/libbeat/tests/system/config/mockbeat.yml.j2 @@ -79,6 +79,18 @@ setup.template: path: {{ template_json_path }} name: {{ template_json_name }} +{% if ilm %} +setup.ilm: + enabled: {{ ilm.enabled | default("auto") }} + policy_name: libbeat-test-default-policy + {% if ilm.pattern %} + pattern: {{ ilm.pattern }} + {% endif %} + {% if ilm.rollover_alias %} + rollover_alias: {{ ilm.rollover_alias }} + {% endif %} +{% endif %} + #================================ Logging ===================================== {% if metrics_period -%} diff --git a/libbeat/tests/system/test_base.py b/libbeat/tests/system/test_base.py index a20ae6d1d61..a08af980e9f 100644 --- a/libbeat/tests/system/test_base.py +++ b/libbeat/tests/system/test_base.py @@ -65,25 +65,6 @@ def test_invalid_config_cli_param(self): assert exit_code == 1 assert self.log_contains("error unpacking config data") is True - def test_config_test(self): - """ - Checks if -configtest works as expected - """ - shutil.copy(self.beat_path + "/_meta/config.yml", - os.path.join(self.working_dir, "libbeat.yml")) - with open(self.working_dir + "/mockbeat.template.json", "w") as f: - f.write('{"template": true}') - with open(self.working_dir + "/mockbeat.template-es2x.json", "w") as f: - f.write('{"template": true}') - - exit_code = self.run_beat( - config="libbeat.yml", - extra_args=["-configtest", - "-path.config", self.working_dir]) - - assert exit_code == 0 - assert self.log_contains("Config OK") is True - # NOTE(ph): I've removed the code to crash with theses settings, but the test is still usefull if # more settings are added. # def test_invalid_config_with_removed_settings(self): @@ -101,45 +82,6 @@ def test_config_test(self): # assert self.log_contains("setting 'queue_size' has been removed") # assert self.log_contains("setting 'bulk_queue_size' has been removed") - def test_version_simple(self): - """ - Tests -version prints a version and exits. - """ - self.start_beat(extra_args=["-version"]).check_wait() - assert self.log_contains("beat version") is True - - def test_version(self): - """ - Checks if version param works - """ - args = [self.beat_path + "/libbeat.test"] - - args.extend(["-version", - "-e", - "-systemTest", - "-v", - "-d", "*", - ]) - if os.getenv("TEST_COVERAGE") == "true": - args.extend([ - "-test.coverprofile", - os.path.join(self.working_dir, "coverage.cov"), - ]) - - assert self.log_contains("error loading config file") is False - - with open(os.path.join(self.working_dir, "mockbeat.log"), "wb") \ - as outputfile: - proc = subprocess.Popen(args, - stdout=outputfile, - stderr=subprocess.STDOUT) - exit_code = proc.wait() - assert exit_code == 0 - - assert self.log_contains("mockbeat") is True - assert self.log_contains("version") is True - assert self.log_contains("9.9.9") is True - def test_console_output_timed_flush(self): """ outputs/console - timed flush diff --git a/libbeat/tests/system/test_cmd.py b/libbeat/tests/system/test_cmd.py index c657a3cad3c..e29af7301db 100644 --- a/libbeat/tests/system/test_cmd.py +++ b/libbeat/tests/system/test_cmd.py @@ -67,41 +67,6 @@ def test_setup_template(self): assert exit_code == 0 assert len(self.es.cat.templates(name='mockbeat-*', h='name')) > 0 - @unittest.skipUnless(INTEGRATION_TESTS, "integration test") - @attr('integration') - def test_setup_flag(self): - """ - Test --setup flag on run command - """ - # Delete any existing template - try: - self.es.indices.delete_template('mockbeat-*') - except: - pass - - assert len(self.es.cat.templates(name='mockbeat-*', h='name')) == 0 - - shutil.copy(self.beat_path + "/_meta/config.yml", - os.path.join(self.working_dir, "libbeat.yml")) - shutil.copy(self.beat_path + "/fields.yml", - os.path.join(self.working_dir, "fields.yml")) - - proc = self.start_beat( - extra_args=["--setup", - "--path.config", self.working_dir, - "-E", "setup.dashboards.file=" + - os.path.join(self.beat_path, "tests", "files", "testbeat-dashboards.zip"), - "-E", "setup.dashboards.beat=testbeat", - "-E", "setup.kibana.protocol=http", - "-E", "setup.kibana.host=" + self.get_kibana_host(), - "-E", "setup.kibana.port=" + self.get_kibana_port(), - "-E", "output.elasticsearch.hosts=['" + self.get_host() + "']"], - config="libbeat.yml") - - self.wait_until(lambda: self.es.cat.templates(name='mockbeat-*', h='name') > 0) - self.wait_until(lambda: self.log_contains("Kibana dashboards successfully loaded")) - proc.check_kill_and_wait() - @unittest.skipUnless(INTEGRATION_TESTS, "integration test") @attr('integration') def test_test_config(self): diff --git a/libbeat/tests/system/test_ilm.py b/libbeat/tests/system/test_ilm.py index 04d1fb1da55..069093253a2 100644 --- a/libbeat/tests/system/test_ilm.py +++ b/libbeat/tests/system/test_ilm.py @@ -10,6 +10,9 @@ INTEGRATION_TESTS = os.environ.get('INTEGRATION_TESTS', False) +testPolicyName = "libbeat-test-default-policy" + + class Test(BaseTest): def setUp(self): @@ -19,7 +22,7 @@ def setUp(self): print("Using elasticsearch: {}".format(self.elasticsearch_url)) self.es = Elasticsearch([self.elasticsearch_url]) self.alias_name = "mockbeat-9.9.9" - self.policy_name = "beats-default-policy" + self.policy_name = testPolicyName logging.getLogger("urllib3").setLevel(logging.WARNING) logging.getLogger("elasticsearch").setLevel(logging.ERROR) @@ -31,9 +34,11 @@ def test_enabled(self): """ self.render_config_template( + ilm={ + "enabled": True, + }, elasticsearch={ "hosts": self.get_elasticsearch_url(), - "ilm.enabled": True, }, ) @@ -48,7 +53,8 @@ def test_enabled(self): # Check if template is loaded with settings template = self.es.transport.perform_request('GET', '/_template/' + self.alias_name) - assert template[self.alias_name]["settings"]["index"]["lifecycle"]["name"] == "beats-default-policy" + print(self.alias_name) + assert template[self.alias_name]["settings"]["index"]["lifecycle"]["name"] == testPolicyName assert template[self.alias_name]["settings"]["index"]["lifecycle"]["rollover_alias"] == self.alias_name # Make sure the correct index + alias was created @@ -75,11 +81,13 @@ def test_rollover_alias(self): alias_name = "foo" self.render_config_template( + ilm={ + "enabled": True, + "pattern": "1", + "rollover_alias": alias_name + }, elasticsearch={ "hosts": self.get_elasticsearch_url(), - "ilm.enabled": True, - "ilm.pattern": "1", - "ilm.rollover_alias": alias_name }, ) @@ -109,10 +117,12 @@ def test_pattern(self): """ self.render_config_template( + ilm={ + "enabled": True, + "pattern": "1" + }, elasticsearch={ "hosts": self.get_elasticsearch_url(), - "ilm.enabled": True, - "ilm.pattern": "1" }, ) @@ -142,10 +152,12 @@ def test_pattern_date(self): """ self.render_config_template( + ilm={ + "enabled": True, + "pattern": "'{now/d}'" + }, elasticsearch={ "hosts": self.get_elasticsearch_url(), - "ilm.enabled": True, - "ilm.pattern": "'{now/d}'" }, ) diff --git a/metricbeat/_meta/fields.common.yml b/metricbeat/_meta/fields.common.yml index 78ba58faf8e..b3154f976d8 100644 --- a/metricbeat/_meta/fields.common.yml +++ b/metricbeat/_meta/fields.common.yml @@ -15,11 +15,20 @@ description: > The name of the metricset that generated the event. + - name: process.pgid + type: long + description: > + Process group id. + - name: service.address description: > - Connection address of the machine from which the metricset was collected. This + Address of the machine where the service is running. This field may not be present when the data was collected locally. + - name: service.hostname + description: > + Host name of the machine where the service is running. + - name: type required: true example: metricsets diff --git a/metricbeat/beater/metricbeat.go b/metricbeat/beater/metricbeat.go index a2eec746e9b..7703720521c 100644 --- a/metricbeat/beater/metricbeat.go +++ b/metricbeat/beater/metricbeat.go @@ -92,6 +92,7 @@ func DefaultCreator() beat.Creator { return Creator( WithModuleOptions( module.WithMetricSetInfo(), + module.WithServiceName(), ), ) } diff --git a/metricbeat/docker-compose.yml b/metricbeat/docker-compose.yml index 36a1c938eef..f22db755f9d 100644 --- a/metricbeat/docker-compose.yml +++ b/metricbeat/docker-compose.yml @@ -110,7 +110,7 @@ services: build: context: ./module/kafka/_meta args: - KAFKA_VERSION: 2.0.0 + KAFKA_VERSION: 2.1.0 kafka_1_1_0: build: diff --git a/metricbeat/docs/fields.asciidoc b/metricbeat/docs/fields.asciidoc index 7e7667cc672..2ab48ae83c8 100644 --- a/metricbeat/docs/fields.asciidoc +++ b/metricbeat/docs/fields.asciidoc @@ -992,6 +992,96 @@ type: long Reports whether the instance has passed the instance status check in the last minute. +-- + +*`aws.ec2.instance.core.count`*:: ++ +-- +type: integer + +The number of CPU cores for the instance. + + +-- + +*`aws.ec2.instance.image.id`*:: ++ +-- +type: keyword + +The ID of the image used to launch the instance. + + +-- + +*`aws.ec2.instance.monitoring.state`*:: ++ +-- +type: keyword + +Indicates whether detailed monitoring is enabled. + + +-- + +*`aws.ec2.instance.private.dns_name`*:: ++ +-- +type: keyword + +The private DNS name of the network interface. + + +-- + +*`aws.ec2.instance.private.ip`*:: ++ +-- +type: ip + +The private IPv4 address associated with the network interface. + + +-- + +*`aws.ec2.instance.public.dns_name`*:: ++ +-- +type: keyword + +The public DNS name of the instance. + + +-- + +*`aws.ec2.instance.public.ip`*:: ++ +-- +type: ip + +The address of the Elastic IP address (IPv4) bound to the network interface. + + +-- + +*`aws.ec2.instance.state.code`*:: ++ +-- +type: integer + +The state of the instance, as a 16-bit unsigned integer. + + +-- + +*`aws.ec2.instance.threads_per_core`*:: ++ +-- +type: integer + +The state of the instance (pending | running | shutting-down | terminated | stopping | stopped). + + -- [[exported-fields-beat]] @@ -1587,7 +1677,7 @@ osd node id *`ceph.osd_df.name`*:: + -- -type: text +type: keyword osd node name @@ -1682,7 +1772,7 @@ osd or bucket node id *`ceph.osd_tree.name`*:: + -- -type: text +type: keyword osd or bucket node name @@ -1712,7 +1802,7 @@ osd or bucket node typeID *`ceph.osd_tree.children`*:: + -- -type: text +type: keyword bucket children list, separated by comma. @@ -1975,12 +2065,30 @@ The name of the module that generated the event. The name of the metricset that generated the event. +-- + +*`process.pgid`*:: ++ +-- +type: long + +Process group id. + + -- *`service.address`*:: + -- -Connection address of the machine from which the metricset was collected. This field may not be present when the data was collected locally. +Address of the machine where the service is running. This field may not be present when the data was collected locally. + + +-- + +*`service.hostname`*:: ++ +-- +Host name of the machine where the service is running. -- @@ -7051,16 +7159,6 @@ type: keyword Major version of the user agent. --- - -*`user_agent.device`*:: -+ --- -type: keyword - -Name of the physical device. - - -- *`user_agent.os.major`*:: @@ -7081,16 +7179,6 @@ type: long Minor version of the operating system. --- - -*`url.hostname`*:: -+ --- -type: keyword - -Hostname of the request, such as "elastic.co". - - -- [[exported-fields-elasticsearch]] @@ -7475,9 +7563,9 @@ Target node id. *`elasticsearch.index.recovery.target.host`*:: + -- -type: ip +type: keyword -Target node host address. +Target node host address (could be IP address or hostname). -- @@ -7505,9 +7593,9 @@ Source node id. *`elasticsearch.index.recovery.source.host`*:: + -- -type: ip +type: keyword -Source node host address. +Source node host address (could be IP address or hostname). -- @@ -10779,7 +10867,7 @@ consumer offset into partition being read *`kafka.consumergroup.meta`*:: + -- -type: text +type: keyword custom consumer meta data string @@ -11018,7 +11106,9 @@ Kibana stats and run-time metrics. *`kibana.stats.uuid`*:: + -- -type: keyword +type: alias + +alias to: service.id Kibana instance UUID @@ -11028,7 +11118,7 @@ Kibana instance UUID *`kibana.stats.name`*:: + -- -type: text +type: keyword Kibana instance name @@ -11058,7 +11148,9 @@ Kibana instance hostname *`kibana.stats.transport_address`*:: + -- -type: keyword +type: alias + +alias to: service.address Kibana server's hostname and port @@ -11068,7 +11160,9 @@ Kibana server's hostname and port *`kibana.stats.version`*:: + -- -type: keyword +type: alias + +alias to: service.version Kibana version @@ -11249,7 +11343,9 @@ Kibana instance name. *`kibana.status.uuid`*:: + -- -type: keyword +type: alias + +alias to: service.id Kibana instance uuid. @@ -11259,7 +11355,9 @@ Kibana instance uuid. *`kibana.status.version.number`*:: + -- -type: keyword +type: alias + +alias to: service.version Kibana version number. @@ -12443,7 +12541,7 @@ Total memory available -- -*`kubernetes.pod.memory.workingSet.bytes`*:: +*`kubernetes.pod.memory.working_set.bytes`*:: + -- type: long @@ -12468,7 +12566,7 @@ Total resident set size memory -- -*`kubernetes.pod.memory.pageFaults`*:: +*`kubernetes.pod.memory.page_faults`*:: + -- type: long @@ -12478,7 +12576,7 @@ Total page faults -- -*`kubernetes.pod.memory.majorPageFaults`*:: +*`kubernetes.pod.memory.major_page_faults`*:: + -- type: long @@ -13299,7 +13397,9 @@ node *`logstash.node.host`*:: + -- -type: keyword +type: alias + +alias to: host.hostname Host name @@ -13309,7 +13409,9 @@ Host name *`logstash.node.version`*:: + -- -type: keyword +type: alias + +alias to: service.version Logstash Version @@ -13336,9 +13438,11 @@ Version *`logstash.node.jvm.pid`*:: + -- -type: long +type: alias -Pid +alias to: process.pid + +Process ID -- @@ -14736,7 +14840,7 @@ type: boolean *`mongodb.metrics.replication.executor.network_interface`*:: + -- -type: text +type: keyword -- @@ -15421,7 +15525,9 @@ MongoDB server status metrics. *`mongodb.status.version`*:: + -- -type: keyword +type: alias + +alias to: service.version Instance version. @@ -15431,7 +15537,9 @@ Instance version. *`mongodb.status.process`*:: + -- -type: keyword +type: alias + +alias to: process.name The current MongoDB process. Possible values are mongos or mongod. @@ -17335,11 +17443,25 @@ Munin node metrics exporter -[float] -== munin fields +*`munin.metrics.*`*:: ++ +-- +type: object + +Metrics exposed by a plugin of a munin node agent. -munin contains metrics exposed by a munin node agent +-- + +*`munin.plugin.name`*:: ++ +-- +type: keyword + +Name of the plugin collecting these metrics. + + +-- [[exported-fields-mysql]] @@ -18851,7 +18973,9 @@ process contains the metrics that were obtained from the PHP-FPM process. *`php_fpm.process.pid`*:: + -- -type: integer +type: alias + +alias to: process.pid The PID of the process @@ -18913,7 +19037,9 @@ The duration in microseconds (1 million in a second) of the current request (my *`php_fpm.process.request_method`*:: + -- -type: keyword +type: alias + +alias to: http.request.method The request method (GET, POST, etc) (of the current request) @@ -18923,7 +19049,9 @@ The request method (GET, POST, etc) (of the current request) *`php_fpm.process.request_uri`*:: + -- -type: text +type: alias + +alias to: url.original The request URI with the query string (of the current request) @@ -18933,7 +19061,9 @@ The request URI with the query string (of the current request) *`php_fpm.process.content_length`*:: + -- -type: integer +type: alias + +alias to: http.response.body.bytes The content length of the request (only with POST) (of the current request) @@ -18943,7 +19073,9 @@ The content length of the request (only with POST) (of the current request) *`php_fpm.process.user`*:: + -- -type: keyword +type: alias + +alias to: user.name The user (PHP_AUTH_USER) (or - if not set) (for the current request) @@ -18953,7 +19085,7 @@ The user (PHP_AUTH_USER) (or - if not set) (for the current request) *`php_fpm.process.script`*:: + -- -type: text +type: keyword The main script called (or - if not set) (for the current request) @@ -23334,10 +23466,9 @@ The number of outgoing packets that were dropped. This value is always 0 on Darw *`system.process.name`*:: + -- -type: keyword - -The process name. +type: alias +alias to: process.name -- @@ -23354,30 +23485,27 @@ The process state. For example: "running". *`system.process.pid`*:: + -- -type: long - -The process pid. +type: alias +alias to: process.pid -- *`system.process.ppid`*:: + -- -type: long - -The process parent pid. +type: alias +alias to: process.ppid -- *`system.process.pgid`*:: + -- -type: long - -The process group id. +type: alias +alias to: process.pgid -- @@ -23394,20 +23522,18 @@ The full command-line used to start the process, including the arguments separat *`system.process.username`*:: + -- -type: keyword - -The username of the user that created the process. If the username cannot be determined, the field will contain the user's numeric identifier (UID). On Windows, this field includes the user's domain and is formatted as `domain\username`. +type: alias +alias to: user.name -- *`system.process.cwd`*:: + -- -type: keyword - -The current working directory of the process. This field is only available on Linux. +type: alias +alias to: process.working_directory -- diff --git a/metricbeat/docs/how-metricbeat-works.asciidoc b/metricbeat/docs/how-metricbeat-works.asciidoc index 24399b1388f..185abb543a1 100644 --- a/metricbeat/docs/how-metricbeat-works.asciidoc +++ b/metricbeat/docs/how-metricbeat-works.asciidoc @@ -46,12 +46,10 @@ For more about the benefits of using Metricbeat, see <>. Every event sent by Metricbeat has the same basic structure. It contains the following fields: *`@timestamp`*:: Time when the event was captured -*`beat.hostname`*:: Hostname of the server on which the Beat is running -*`beat.name`*:: Name given to the Beat -*`metricset.module`*:: Name of the module that the data is from -*`metricset.name`*:: Name of the metricset that the data is from -*`metricset.rtt`*:: Round trip time of the request in microseconds -*`type`*:: This is always "metricsets" +*`host.hostname`*:: Hostname of the server on which the Beat is running +*`agent.type`*:: Name given to the Beat +*`event.module`*:: Name of the module that the data is from +*`event.dataset`*:: Name of the module that the data is from For example: @@ -59,14 +57,15 @@ For example: ---- { "@timestamp": "2016-06-22T22:05:53.291Z", - "beat": { - "hostname": "host.example.com", - "name": "host.example.com" + "agent": { + "type": "metricbeat" }, - "metricset": { - "module": "system", - "name": "process", - "rtt": 7419 + "host": { + "hostname": "host.example.com", + }, + "event": { + "dataset": "system.process", + "module": process }, . . diff --git a/metricbeat/docs/images/metricbeat-zookeeper.png b/metricbeat/docs/images/metricbeat-zookeeper.png new file mode 100644 index 00000000000..b0c57e76608 Binary files /dev/null and b/metricbeat/docs/images/metricbeat-zookeeper.png differ diff --git a/metricbeat/docs/metricbeat-filtering.asciidoc b/metricbeat/docs/metricbeat-filtering.asciidoc index 6ba234145ea..68d23b39107 100644 --- a/metricbeat/docs/metricbeat-filtering.asciidoc +++ b/metricbeat/docs/metricbeat-filtering.asciidoc @@ -4,13 +4,13 @@ include::{libbeat-dir}/docs/processors.asciidoc[] For example, the following configuration reduces the exported fields by -dropping the `beat.name` and `beat.hostname` fields under `beat` from all documents. +dropping the `agent.name` and `agent.version` fields under `beat` from all documents. [source, yaml] ---- processors: - drop_fields: - fields: ['beat'] + fields: ['agent'] ---- include::{libbeat-dir}/docs/processors-using.asciidoc[] diff --git a/metricbeat/docs/metricbeat-options.asciidoc b/metricbeat/docs/metricbeat-options.asciidoc index 1228a39efad..10e75d025e6 100644 --- a/metricbeat/docs/metricbeat-options.asciidoc +++ b/metricbeat/docs/metricbeat-options.asciidoc @@ -200,6 +200,13 @@ A list of processors to apply to the data generated by the metricset. See <> for information about specifying processors in your config. +[float] +==== `service.name` + +A name given by the user to the service the data is collected from. It can be +used for example to identify information collected from nodes of different +clusters with the same `service.type`. + [float] [[module-http-config-options]] === Standard HTTP config options diff --git a/metricbeat/docs/modules/aws.asciidoc b/metricbeat/docs/modules/aws.asciidoc index eb6148f0ceb..b2b31db599e 100644 --- a/metricbeat/docs/modules/aws.asciidoc +++ b/metricbeat/docs/modules/aws.asciidoc @@ -32,8 +32,8 @@ see https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp.html[Te aws> sts get-session-token --serial-number arn:aws:iam::1234:mfa/your-email@example.com --token-code 456789 --duration-seconds 129600 ---- -Specific permissions needs to be added into the IAM user's policy to allow Metricbeat collecting AWS monitoring metrics. Please -see documentation under each metric set for required permissions. +Specific permissions needs to be added into the IAM user's policy to authorize Metricbeat to collect AWS monitoring metrics. Please +see documentation under each metricset for required permissions. By default, Amazon EC2 sends metric data to CloudWatch every 5 minutes. With this basic monitoring, `period` in aws module configuration should be larger or equal than `300s`. If `period` is set to be less than `300s`, the same cloudwatch metrics diff --git a/metricbeat/docs/modules/kafka.asciidoc b/metricbeat/docs/modules/kafka.asciidoc index 054d8cdb2be..3c8f205ebe7 100644 --- a/metricbeat/docs/modules/kafka.asciidoc +++ b/metricbeat/docs/modules/kafka.asciidoc @@ -12,7 +12,7 @@ The default metricsets are `consumergroup` and `partition`. [float] === Compability -This module is tested with Kafka 0.10.2.1, 1.1.0 and 2.0.0. +This module is tested with Kafka 0.10.2.1, 1.1.0 and 2.1.0. [float] diff --git a/metricbeat/docs/modules/munin.asciidoc b/metricbeat/docs/modules/munin.asciidoc index a30361167bb..19d2fb1a2c1 100644 --- a/metricbeat/docs/modules/munin.asciidoc +++ b/metricbeat/docs/modules/munin.asciidoc @@ -5,8 +5,6 @@ This file is generated! See scripts/docs_collector.py [[metricbeat-module-munin]] == Munin module -beta[] - This is the munin module. The default metricset is `node`. @@ -33,7 +31,15 @@ metricbeat.modules: enabled: true period: 10s hosts: ["localhost:4949"] - node.namespace: node + + # List of plugins to collect metrics from, by default it collects from + # all the available ones. + #munin.plugins: [] + + # If set to true, it sanitizes fields names in concordance with munin + # implementation (all characters that are not alphanumeric, or underscore + # are replaced by underscores). + #munin.sanitize: false ---- [float] diff --git a/metricbeat/docs/modules/munin/node.asciidoc b/metricbeat/docs/modules/munin/node.asciidoc index a485a6a730e..d995cc42f37 100644 --- a/metricbeat/docs/modules/munin/node.asciidoc +++ b/metricbeat/docs/modules/munin/node.asciidoc @@ -5,8 +5,6 @@ This file is generated! See scripts/docs_collector.py [[metricbeat-metricset-munin-node]] === Munin node metricset -beta[] - include::../../../module/munin/node/_meta/docs.asciidoc[] @@ -15,3 +13,9 @@ include::../../../module/munin/node/_meta/docs.asciidoc[] For a description of each field in the metricset, see the <> section. +Here is an example document generated by this metricset: + +[source,json] +---- +include::../../../module/munin/node/_meta/data.json[] +---- diff --git a/metricbeat/docs/modules/zookeeper.asciidoc b/metricbeat/docs/modules/zookeeper.asciidoc index 32eb71cfc56..dce89ec92ac 100644 --- a/metricbeat/docs/modules/zookeeper.asciidoc +++ b/metricbeat/docs/modules/zookeeper.asciidoc @@ -14,6 +14,13 @@ metricset is `mntr` and `server`. The ZooKeeper metricsets were tested with ZooKeeper 3.4.8 and are expected to work with all version >= 3.4.0. Versions prior to 3.4 do not support the `mntr` command. +[float] +=== Dashboard + +The Zookeeper module comes with a predefined dashboard: + +image::./images/metricbeat-zookeeper.png[] + [float] === Example configuration diff --git a/metricbeat/docs/modules_list.asciidoc b/metricbeat/docs/modules_list.asciidoc index dc02877d6cc..69984a9da13 100644 --- a/metricbeat/docs/modules_list.asciidoc +++ b/metricbeat/docs/modules_list.asciidoc @@ -103,8 +103,8 @@ This file is generated! See scripts/docs_collector.py |<> beta[] |image:./images/icon-yes.png[Prebuilt dashboards are available] | .2+| .2+| |<> experimental[] |<> experimental[] -|<> beta[] |image:./images/icon-no.png[No prebuilt dashboards] | -.1+| .1+| |<> beta[] +|<> |image:./images/icon-no.png[No prebuilt dashboards] | +.1+| .1+| |<> |<> |image:./images/icon-yes.png[Prebuilt dashboards are available] | .2+| .2+| |<> beta[] |<> @@ -160,7 +160,7 @@ This file is generated! See scripts/docs_collector.py |<> |image:./images/icon-yes.png[Prebuilt dashboards are available] | .2+| .2+| |<> beta[] |<> -|<> |image:./images/icon-no.png[No prebuilt dashboards] | +|<> |image:./images/icon-yes.png[Prebuilt dashboards are available] | .2+| .2+| |<> |<> |================================ diff --git a/metricbeat/include/fields.go b/metricbeat/include/fields.go index 96d61794b2a..a1115095c3c 100644 --- a/metricbeat/include/fields.go +++ b/metricbeat/include/fields.go @@ -32,5 +32,5 @@ func init() { // AssetMetricbeatMetaFieldsCommonYml returns asset data. // This is the base64 encoded gzipped contents of ../metricbeat/_meta/fields.common.yml. func AssetMetricbeatMetaFieldsCommonYml() string { - return "eJyUUktu2zAU3OsUg+yjA2hRoPAVcoEXchwS5Ucln6zq9gVpOqiN1kW04+ObD2f0ih88FpgcY04ToF4DF5xuZ8tqil/V57Tg2wQAp5xUfKoDhLNnsBVyER/kPRA+QUIAL0wKPVbWecJYW6bO8YokkQsitXhTqXPMdgvsl39Vbd+bY8chn6GOuGKgThQfTCyitP2ma88D2CwskOCljskq6paxdCcc/UeRq6yWjf8y285ftHrDPnF7J1ZZLt5wFmsLa32udsop0bQpxv6nrhjnE3EuOWJ33rgHO7u0IkOgUdoZb87XT9peGaIcSFnxTqyFtZW6O6bOY0XlngIhGwnheHhO62DwFv7cfKEdEV+H/CVxDX+G/J8nt4BtNlu8/WQzvoddjooecsaLzeZlnn4HAAD///yQ6RY=" + return "eJyUU0tu3DAM3fsUD9nHB/CiQJBNl13kAozEWET1cUV6pr59YVkT1EUzab0TxfcR+fyI77xNcCWlkgfAxCJPeL6dPaurspiUPOHLAADPJRtJ1g7Cm3D0CrqQRHqNDMmgGMEXzgbbFtZxQG+bhsbxiEyJJyS2Kk7ZxlT8Grld/lV1/14CNxzKGywwDgwskGHmzJWMfbtp2mMH7hYmUBTSXlnIwtSbTsJJ5kqHrNWVPzK7n//T6g17x+1JbKnFseq4zOJP74glz/e1vx1QzLWsC8T/waxcL+J4JO8rq97nejqa3p9BLkhmXANXbpXOBlHUNWfJ84iXIPrO0PaORBtyMbwylsq6J+MaODcKT0a40h6oGNntU4nFUYzbB85DUft8BV+L2nkH/2L+rLiPvBNW/rFKZd+TcRT5J6Ul/p6NT+a558IXt6bbvzHiKV5pU7RsFDz44h7G4VcAAAD//8J8HrM=" } diff --git a/metricbeat/include/fields/fields.go b/metricbeat/include/fields/fields.go index 73ad084e4f9..8a7493c954b 100644 --- a/metricbeat/include/fields/fields.go +++ b/metricbeat/include/fields/fields.go @@ -32,5 +32,5 @@ func init() { // AssetLibbeatFieldsYml returns asset data. // This is the base64 encoded gzipped contents of ../libbeat/fields.yml. func AssetLibbeatFieldsYml() string { - return "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" + return "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" } diff --git a/metricbeat/magefile.go b/metricbeat/magefile.go index 981a681f465..39de5fa4704 100644 --- a/metricbeat/magefile.go +++ b/metricbeat/magefile.go @@ -47,11 +47,6 @@ func GolangCrossBuild() error { return mage.GolangCrossBuild(mage.DefaultGolangCrossBuildArgs()) } -// CrossBuildXPack cross-builds the beat with XPack for all target platforms. -func CrossBuildXPack() error { - return mage.CrossBuildXPack() -} - // BuildGoDaemon builds the go-daemon binary (use crossBuildGoDaemon). func BuildGoDaemon() error { return mage.BuildGoDaemon() @@ -80,11 +75,11 @@ func Package() { start := time.Now() defer func() { fmt.Println("package ran for", time.Since(start)) }() - mage.UseElasticBeatPackaging() + mage.UseElasticBeatOSSPackaging() customizePackaging() mg.Deps(Update) - mg.Deps(CrossBuild, CrossBuildXPack, CrossBuildGoDaemon) + mg.Deps(CrossBuild, CrossBuildGoDaemon) mg.SerialDeps(mage.Package, TestPackages) } diff --git a/metricbeat/mb/mb.go b/metricbeat/mb/mb.go index e3c19cfaaff..17e835084bb 100644 --- a/metricbeat/mb/mb.go +++ b/metricbeat/mb/mb.go @@ -306,14 +306,15 @@ func (b *BaseMetricSet) Registration() MetricSetRegistration { // the metricset fetches not only the predefined fields but add alls raw data under // the raw namespace to the event. type ModuleConfig struct { - Hosts []string `config:"hosts"` - Period time.Duration `config:"period" validate:"positive"` - Timeout time.Duration `config:"timeout" validate:"positive"` - Module string `config:"module" validate:"required"` - MetricSets []string `config:"metricsets"` - Enabled bool `config:"enabled"` - Raw bool `config:"raw"` - Query QueryParams `config:"query"` + Hosts []string `config:"hosts"` + Period time.Duration `config:"period" validate:"positive"` + Timeout time.Duration `config:"timeout" validate:"positive"` + Module string `config:"module" validate:"required"` + MetricSets []string `config:"metricsets"` + Enabled bool `config:"enabled"` + Raw bool `config:"raw"` + Query QueryParams `config:"query"` + ServiceName string `config:"service.name"` } func (c ModuleConfig) String() string { diff --git a/metricbeat/mb/module/options.go b/metricbeat/mb/module/options.go index cf5c7ac03ff..e5d07667491 100644 --- a/metricbeat/mb/module/options.go +++ b/metricbeat/mb/module/options.go @@ -20,6 +20,7 @@ package module import ( "time" + "github.com/elastic/beats/libbeat/common" "github.com/elastic/beats/metricbeat/mb" ) @@ -58,3 +59,27 @@ func WithEventModifier(modifier mb.EventModifier) Option { func WithMetricSetInfo() Option { return WithEventModifier(mb.AddMetricSetInfo) } + +// WithServiceName sets the `service.name` field of the event with the value +// given to the `service.name` setting in the module configuration. +func WithServiceName() Option { + return func(w *Wrapper) { + modifier := func(_, _ string, event *mb.Event) { + if event == nil { + return + } + serviceName := w.Module.Config().ServiceName + if serviceName == "" { + return + } + if event.RootFields == nil { + event.RootFields = common.MapStr{} + } else if current, err := event.RootFields.GetValue("service.name"); err == nil && current != "" { + // Already set by the metricset, don't overwrite + return + } + event.RootFields.Put("service.name", serviceName) + } + w.eventModifiers = append(w.eventModifiers, modifier) + } +} diff --git a/metricbeat/mb/testing/data_generator.go b/metricbeat/mb/testing/data_generator.go index 0619a3a54e3..4fc76dd1fda 100644 --- a/metricbeat/mb/testing/data_generator.go +++ b/metricbeat/mb/testing/data_generator.go @@ -170,7 +170,7 @@ func WriteEventToDataJSON(t testing.TB, fullEvent beat.Event, postfixPath string t.Fatal(err) } - if stat, err := os.Stat(postfixPath); err == nil && stat.IsDir() { + if stat, err := os.Stat(postfixPath); postfixPath == "" || (err == nil && stat.IsDir()) { p = path.Join(p, postfixPath, "_meta", "data.json") } else { p = postfixPath diff --git a/metricbeat/metricbeat.reference.yml b/metricbeat/metricbeat.reference.yml index 94314930f7f..d6f0d0a79f7 100644 --- a/metricbeat/metricbeat.reference.yml +++ b/metricbeat/metricbeat.reference.yml @@ -507,7 +507,15 @@ metricbeat.modules: enabled: true period: 10s hosts: ["localhost:4949"] - node.namespace: node + + # List of plugins to collect metrics from, by default it collects from + # all the available ones. + #munin.plugins: [] + + # If set to true, it sanitizes fields names in concordance with munin + # implementation (all characters that are not alphanumeric, or underscore + # are replaced by underscores). + #munin.sanitize: false #-------------------------------- MySQL Module ------------------------------- - module: mysql @@ -948,11 +956,6 @@ output.elasticsearch: # IPv6 addresses should always be defined as: https://[2001:db8::1]:9200 hosts: ["localhost:9200"] - # Enabled ilm (beta) to use index lifecycle management instead daily indices. - #ilm.enabled: false - #ilm.rollover_alias: "metricbeat" - #ilm.pattern: "{now/d}-000001" - # Set gzip compression level. #compression_level: 0 @@ -1599,6 +1602,25 @@ setup.template.settings: #_source: #enabled: false +#============================== Setup ILM ===================================== + +# Configure Index Lifecycle Management Index Lifecycle Management creates a +# write alias and adds additional settings to the template. +# The elasticsearch.output.index setting will be replaced with the write alias +# if ILM is enabled. + +# Enabled ILM support. Valid values are true, false, and auto. The beat will +# detect availabilty of Index Lifecycle Management in Elasticsearch and enable +# or disable ILM support. +#setup.ilm.enabled: auto + +# Configure the ILM write alias name. +#setup.ilm.rollover_alias: "metricbeat" + +# Configure rollover index pattern. +#setup.ilm.pattern: "{now/d}-000001" + + #============================== Kibana ===================================== # Starting with Beats version 6.0.0, the dashboards are loaded via the Kibana API. diff --git a/metricbeat/metricbeat.yml b/metricbeat/metricbeat.yml index 9732d9907b1..52916c295de 100644 --- a/metricbeat/metricbeat.yml +++ b/metricbeat/metricbeat.yml @@ -45,7 +45,7 @@ setup.template.settings: #============================== Dashboards ===================================== # These settings control loading the sample dashboards to the Kibana index. Loading # the dashboards is disabled by default and can be enabled either by setting the -# options here, or by using the `-setup` CLI flag or the `setup` command. +# options here or by using the `setup` command. #setup.dashboards.enabled: false # The URL from where to download the dashboards archive. By default this URL @@ -93,9 +93,6 @@ output.elasticsearch: # Array of hosts to connect to. hosts: ["localhost:9200"] - # Enabled ilm (beta) to use index lifecycle management instead daily indices. - #ilm.enabled: false - # Optional protocol and basic auth credentials. #protocol: "https" #username: "elastic" diff --git a/metricbeat/module/ceph/cluster_disk/_meta/data.json b/metricbeat/module/ceph/cluster_disk/_meta/data.json index 7306589ec33..a26d6f1dde3 100644 --- a/metricbeat/module/ceph/cluster_disk/_meta/data.json +++ b/metricbeat/module/ceph/cluster_disk/_meta/data.json @@ -1,26 +1,32 @@ { "@timestamp": "2017-10-12T08:05:34.853Z", - "beat": { + "agent": { "hostname": "host.example.com", "name": "host.example.com" }, "ceph": { "cluster_disk": { "available": { - "bytes": 50847313920 + "bytes": 0 }, "total": { - "bytes": 67371577344 + "bytes": 0 }, "used": { - "bytes": 13071564800 + "bytes": 0 } } }, + "event": { + "dataset": "ceph.cluster_disk", + "duration": 115000, + "module": "ceph" + }, "metricset": { - "host": "ceph:5000", - "module": "ceph", - "name": "cluster_disk", - "rtt": 115 + "name": "cluster_disk" + }, + "service": { + "address": "127.0.0.1:5000", + "type": "ceph" } } \ No newline at end of file diff --git a/metricbeat/module/ceph/cluster_health/_meta/data.json b/metricbeat/module/ceph/cluster_health/_meta/data.json index 5a7c98cf0cd..08de2b92e68 100644 --- a/metricbeat/module/ceph/cluster_health/_meta/data.json +++ b/metricbeat/module/ceph/cluster_health/_meta/data.json @@ -1,12 +1,12 @@ { "@timestamp": "2017-10-12T08:05:34.853Z", - "beat": { + "agent": { "hostname": "host.example.com", "name": "host.example.com" }, "ceph": { "cluster_health": { - "overall_status": "HEALTH_WARN", + "overall_status": "HEALTH_OK", "timechecks": { "epoch": 3, "round": { @@ -16,10 +16,16 @@ } } }, + "event": { + "dataset": "ceph.cluster_health", + "duration": 115000, + "module": "ceph" + }, "metricset": { - "host": "ceph:5000", - "module": "ceph", - "name": "cluster_health", - "rtt": 115 + "name": "cluster_health" + }, + "service": { + "address": "127.0.0.1:5000", + "type": "ceph" } } \ No newline at end of file diff --git a/metricbeat/module/ceph/cluster_status/_meta/data.json b/metricbeat/module/ceph/cluster_status/_meta/data.json index a6d41779df7..c6ce81c817a 100644 --- a/metricbeat/module/ceph/cluster_status/_meta/data.json +++ b/metricbeat/module/ceph/cluster_status/_meta/data.json @@ -1,6 +1,6 @@ { "@timestamp": "2017-10-12T08:05:34.853Z", - "beat": { + "agent": { "hostname": "host.example.com", "name": "host.example.com" }, @@ -17,19 +17,19 @@ "total": 0 }, "osd": { - "epoch": 18, + "epoch": 3, "full": false, - "in_osd_count": 1, + "in_osd_count": 0, "nearfull": false, "osd_count": 1, "remapped_pg_count": 0, - "up_osd_count": 1 + "up_osd_count": 0 }, "pg": { - "avail_bytes": 50847313920, - "data_bytes": 3656, - "total_bytes": 67371577344, - "used_bytes": 13071564800 + "avail_bytes": 0, + "data_bytes": 0, + "total_bytes": 0, + "used_bytes": 0 }, "traffic": { "read_bytes": 0, @@ -37,13 +37,19 @@ "write_bytes": 0, "write_op_per_sec": 0 }, - "version": 32 + "version": 4 } }, + "event": { + "dataset": "ceph.cluster_status", + "duration": 115000, + "module": "ceph" + }, "metricset": { - "host": "ceph:5000", - "module": "ceph", - "name": "cluster_status", - "rtt": 115 + "name": "cluster_status" + }, + "service": { + "address": "127.0.0.1:5000", + "type": "ceph" } } \ No newline at end of file diff --git a/metricbeat/module/ceph/fields.go b/metricbeat/module/ceph/fields.go index 7112808d2df..8f8636152e8 100644 --- a/metricbeat/module/ceph/fields.go +++ b/metricbeat/module/ceph/fields.go @@ -32,5 +32,5 @@ func init() { // AssetCeph returns asset data. // This is the base64 encoded gzipped contents of ../metricbeat/module/ceph. func AssetCeph() string { - return "eJzEmt9v2zYQx9/zVxzytAGJsb36YUDXFWiwpQ2W9mkYVJo8SZwpkeBRcf3fD6RkOdZP26FdP22x+72P7k7Hu5PuYY3bJXA0+Q2Ak07hEm7fo8lvbwAEErfSOKnLJfx2AwDgv4JCi0rhDQDl2rqE6zKV2RJSpsj/1aJCRriEjPnfoHOyzGgJ/9wSqds7uM2dM7f/3gCkEpWgZVC+h5IV2LL4j9sar2J1ZZq/DBD5zzf/j74B16VjsiRwOUKBzkru/5s52KBFIG6ZQQGp1QW8//D0cdEIvMY4QFEVObSJkLRuvxzCmkDznxGdQz/tPl2Y10DshUnFVgoXq61DOvjNjkvpMut8MYHmP+92qhBUQafBgQ115+eptgVzS+gD7CCddkxFBfziFePAVYQiKttXQnE+WjfTcmTK5TFyrad0erbpF7RMqYQcc9Wwv9a43WgrTnPZ51oXat0pt7UpJQvkOfI1LdBonkeK3SMz8IKWpC7nzFpdlWLxwlSFkYy34hC0jwOIHYrngxAcgnRzs2d7ODeH8+yIjO3pT+XmUNjOj0U41RpfTCaEZWkq+cIiE8lJVWS8Ms3D1f4BbxRcbnWV5aZyYNACIddjudOwbqx0eHXYYPUM2uBZbRLjEwJ5rPi+dqHUhk5z3sV4ai8dA1RIMopxXITTNTLGThxMBmVVrEaqcMugV/8hd7FO0B5FI38UimVO6kEQ4kyhSFKlmRvJb4OWY9n99lTcPsKOUmBmmUBxkaDtxGeC1jJcJmgtxRFBa1F+XNBa3PGgmWwhmGNXr5gmA293onc12SIMAFdHY4cDwhhcyPKrw7n9cDAG5pv+q3NV7WAwghUaHgwtHSb+j5FuzacsdNb4+nfTCFxXvRvqXOvPud4Q5HoDBSu3YDICZhFk2UDpdPbSe3yRm70mQDTf72kSi7RSw6V7pbVC1qWaMf5AXhR6oq8tlsjsRax64RnTVZFoErFOiU4yeGWfCXPj3o6kMpeG8ampa6A2Pb8+TULJ8kdAPXyahLJYMGNQJCa7NtnfHx7fPT19+GOUL+bEHrS6PUbbFOpSOn30EuXkQfVQP3iAwpl95My6394ZHqvc7nd3TV+0m+UfPw9nTM858LYNwsfaGTNW95e+XkW/8j9/nzNfrySjma73kfNmw7IxmtWwaZw3qhi5pDKCOeyGsrbsvznxemWBsMmxhA0jGNLeGR9tY87KrU+swLnLJacthj6BFkpnUXe7f+lsv9rtAhyzbX4NV0jiUekeJfF4eEQuKt3z85d4cBd/pPBWwNmb7ozkY+R699qrczURafwjjqPJQ6soJK2hIpb51r32xWGTPHXQyVg+CE2rFthVnC03Dr+fuCFoTY2OIwJfJMeEK0YRN/CtXa9xB1IpzJgK/wey5KoSCLkQd0AkAB1fTBxwPlevMOG2+VGP3i9aVSMua5+2XZOqztoJqjOfosZg229SJvhMlpRVEekWou48DkpzX1PqRl6GyXA8eGOd6qUWdZ0oNhosw8ES6CzibBE8ptx5oVDoZovn1eqetrCq+BrdNStgx+hoLfQ24tbAjuWJaqhJ3EGuyd2B1dpNFMWtweSSMfFSD8ODL8+lEhaHd1anx6axulMFJf3lExpmw7282gLXRcGGHcFtRXmyQZnlw3fz0G187LkVxGFAfH9umpGB8/QgBIt9wZ0p/C5p5BHHWXszSbVFclKpWt3nQKndT7/eb5Hu4Jf7Uv88XMWtLJjdJixNZSndNpbj/TBUe9s3rhaZkGXz6CC82NTYHS3rFicS4YzXGHJsJT3QmNnYby7sXx4J9UA6f9JVSsAKoTI+SkJvhre6l2nkvCNqZQjKLZqS62NauJS5vPfW0Bt5DLNYNsUqzNH1cd+pY72j1Wit3vzS25DIDzxSH8Ruk+DBTjtL37y8GDVZT49Xfa2vx3LcrOs54z5C/hS2uOE2aZ4dH+etK7zC9wYfXWDnt14dcP0fAAD//wWoKpo=" + return "eJzEms9v27gSx+/5KwY5vQckxntXHx7Q1y3QYDdtsGlPi4VKkyOJa0okOFS8/u8XpGTZ1k/boV2fWtv5zsczo+HMSI+wxu0SOJr8DsBJp3AJ9x/R5Pd3AAKJW2mc1OUS/ncHAOA/gkKLSuEdAOXauoTrMpXZElKmyL9rUSEjXELG/HfQOVlmtIQ/7onU/QPc586Z+z/vAFKJStAyKD9CyQpsWfzLbY1XsboyzTsDRP71w//RD+C6dEyWBC5HKNBZyf2/mYMNWgTilhkUkFpdwMdPL58XjcAhxhGKqsihTYSkdfvhENYEmn+N6Bz7affqwhwCsTcmFVspXKy2DunoOzsupcus88EEmn992KlCUAWdBgc21J2vp9oWzC2hD7CDdNoxFRXwm1eMA1cRiqhs3wnF5WjdTMuRKZfHyLWe0vnZpt/QMqUScsxVw/5a43ajrTjPZV9rXah1p9zWppQskOfI17RAo3keKXbPzMAbWpK6nDNrdVWKxRtTFUYy3opD0D4NIHYoXo9CcAzSzc2e7eHcHM6zEzK2pz+Vm0NhuzwW4VRrfDGZEJalqeQLi0wkZ1WR8co0D1f7B7xRcLnVVZabyoFBC4Rcj+VOw7qx0uHNYYPVC2iDZ7VJjE8I5LHie+hCqQ2d57yr8dReOgWokGQU47gIp2tkjJ04mAzKqliNVOGWQa/+Qu5inaA9ikb+JBTLnNSDIMSZQpGkSjM3kt8GLcey++m5uH2EHaXAzDKB4ipB24nPBK1luE7QWooTgtai/LygtbjjQTPZQjDHbl4xTQbe7kTvarJFGABujsaOB4QxuJDlN4dz++FgDMw3/TfnqtrBYAQrNDwYWjpM/JuRLs2XLHTWePi9aQSuq94Fdan111xvCHK9gYKVWzAZAbMIsmygdDr703t8kZu9JkA03+9pEou0UsOle6W1QtalmjH+RF4UeqKHFktk9ipWvfCM6apINIlYp0QnGbyyz4S5cW9HUplrw/jU1DVQm57fXyahZPkzoJ6+TEJZLJgxKBKT3Zrs90/PH15ePv0yyhdzYg9a3R6jbQp1KZ0+eYly9qB6rB88QOHMPnFm3W/vDI9Vbve7u6Yv2s3yz1+HM6bnHHjfBuFz7YwZq/ufvl5F/+W//n/OfL2SjGa63kfOmw3LxmhWw6Zx3qhi5JLKCOawG8rasv/kzN8rC4RNjiVsGMGQ9s74aBtzUW59YQXO/Vxy2mLoE2ihdBZ1t/ubzvar3S7AKdvmQ7hCEo9K9yyJx8MjclHpXl+/xYO7+i2F9wLOXnQXJB8j17vWDs7VRKTxjziOJg+topC0hopY5lv32hfHTfLUQSdj+SA0rVpgV/E65aa1NjqRCHyTHBOuGEVcwrd2vcYDSKUwYyr8D2TJVSUQciEegEgAOr6YOON8ut5gyG1TpJ6+37SqRlzW3nC7JVWduBNUF95IjcG2X6ZM8JksKasi0lVE3ZEclOa+rNS9vAzD4XjwxprVa+3qOlFsNFiGg1XQWcTZOnhKxfNCodbN1s+blT5tYVXxNbobF8GO3dFy6M1c1fJEQdQkHiDX5B7Aau0m6uLWYHLNsHipp+Hxl+dSCYvDm6uLnNQY3gmDkt4DhIbZcEWvtsB1UbBhX3BbUZ5sUGb58DU9dDGfenoFcRgQ35+eZmTyPD8OwWJfcGcK/5Y0cq/jogWapNoiOalUre7ToNTuX/993CI9wH8eS/3v4VpuZcHsNmFpKkvptrEc76ei2tu+g7XIhCybewjhCafG7mhxtziRCBc8z5BjK+mBxszGfoRh/xRJKAnS+fOuUgJWCJXxURJ6M7zevU475x1RK0NQbtGUXJ/SyKXM5b3Hh97JY5jFsqlXYaCuD/1OKesdsEZr9e6n34ZEfuLB+iR2KwUPdoMT9XCLMWqyHiNv+nxfj+W0oddzxr2X/CWsc8Nl0txEPs1bN3iW7x0+usLyb7064vonAAD//8IHLlo=" } diff --git a/metricbeat/module/ceph/monitor_health/_meta/data.json b/metricbeat/module/ceph/monitor_health/_meta/data.json index 38d4741b2c5..3557141daf4 100644 --- a/metricbeat/module/ceph/monitor_health/_meta/data.json +++ b/metricbeat/module/ceph/monitor_health/_meta/data.json @@ -1,18 +1,18 @@ { "@timestamp": "2017-10-12T08:05:34.853Z", - "beat": { + "agent": { "hostname": "host.example.com", "name": "host.example.com" }, "ceph": { "monitor_health": { "available": { - "kb": 49653688, - "pct": 75 + "kb": 46814916, + "pct": 76 }, "health": "HEALTH_OK", - "last_updated": "2017-12-07T07:26:33.344939Z", - "name": "93c038136d94", + "last_updated": "2019-01-25T12:37:24.921587Z", + "name": "f0e2841bb524", "store_stats": { "last_updated": "0.000000", "log": { @@ -22,24 +22,30 @@ "bytes": 65552 }, "sst": { - "bytes": 726 + "bytes": 1087 }, "total": { - "bytes": 2097894 + "bytes": 2098255 } }, "total": { - "kb": 65792556 + "kb": 61255492 }, "used": { - "kb": 12767092 + "kb": 11299252 } } }, + "event": { + "dataset": "ceph.monitor_health", + "duration": 115000, + "module": "ceph" + }, "metricset": { - "host": "ceph:5000", - "module": "ceph", - "name": "monitor_health", - "rtt": 115 + "name": "monitor_health" + }, + "service": { + "address": "127.0.0.1:5000", + "type": "ceph" } } \ No newline at end of file diff --git a/metricbeat/module/ceph/osd_df/_meta/data.json b/metricbeat/module/ceph/osd_df/_meta/data.json index a84eb48dfe3..fb8e83329dc 100644 --- a/metricbeat/module/ceph/osd_df/_meta/data.json +++ b/metricbeat/module/ceph/osd_df/_meta/data.json @@ -1,25 +1,30 @@ { "@timestamp": "2017-10-12T08:05:34.853Z", - "beat": { + "agent": { "hostname": "host.example.com", "name": "host.example.com" }, "ceph": { "osd_df": { - "available.byte": 49655580, + "available.byte": 0, "device_class": "", "id": 0, "name": "osd.0", - "pg_num": 120, - "total.byte": 65792556, - "used.byte": 12765200, - "used.pct": 0.1940219498388237 + "pg_num": 0, + "total.byte": 0, + "used.byte": 0 } }, + "event": { + "dataset": "ceph.osd_df", + "duration": 115000, + "module": "ceph" + }, "metricset": { - "host": "ceph:5000", - "module": "ceph", - "name": "osd_df", - "rtt": 115 + "name": "osd_df" + }, + "service": { + "address": "127.0.0.1:5000", + "type": "ceph" } } \ No newline at end of file diff --git a/metricbeat/module/ceph/osd_df/_meta/fields.yml b/metricbeat/module/ceph/osd_df/_meta/fields.yml index 8ad2589c314..033a1ddd253 100644 --- a/metricbeat/module/ceph/osd_df/_meta/fields.yml +++ b/metricbeat/module/ceph/osd_df/_meta/fields.yml @@ -9,7 +9,7 @@ description: > osd node id - name: name - type: text + type: keyword description: > osd node name - name: device_class diff --git a/metricbeat/module/ceph/osd_tree/_meta/data.json b/metricbeat/module/ceph/osd_tree/_meta/data.json index daf3e06953d..6f8cf7a9106 100644 --- a/metricbeat/module/ceph/osd_tree/_meta/data.json +++ b/metricbeat/module/ceph/osd_tree/_meta/data.json @@ -1,12 +1,14 @@ { "@timestamp": "2017-10-12T08:05:34.853Z", - "beat": { + "agent": { "hostname": "host.example.com", "name": "host.example.com" }, "ceph": { "osd_tree": { - "children": "-2", + "children": [ + "-2" + ], "father": "", "id": -1, "name": "default", @@ -14,10 +16,16 @@ "type_id": 10 } }, + "event": { + "dataset": "ceph.osd_tree", + "duration": 115000, + "module": "ceph" + }, "metricset": { - "host": "ceph:5000", - "module": "ceph", - "name": "osd_tree", - "rtt": 115 + "name": "osd_tree" + }, + "service": { + "address": "127.0.0.1:5000", + "type": "ceph" } } \ No newline at end of file diff --git a/metricbeat/module/ceph/osd_tree/_meta/fields.yml b/metricbeat/module/ceph/osd_tree/_meta/fields.yml index 67078e0206a..07dcd1c78c8 100644 --- a/metricbeat/module/ceph/osd_tree/_meta/fields.yml +++ b/metricbeat/module/ceph/osd_tree/_meta/fields.yml @@ -9,7 +9,7 @@ description: > osd or bucket node id - name: name - type: text + type: keyword description: > osd or bucket node name - name: type @@ -21,7 +21,7 @@ description: > osd or bucket node typeID - name: children - type: text + type: keyword description: > bucket children list, separated by comma. - name: crush_weight diff --git a/metricbeat/module/ceph/osd_tree/data.go b/metricbeat/module/ceph/osd_tree/data.go index 27a3ea14278..bc0290acddf 100644 --- a/metricbeat/module/ceph/osd_tree/data.go +++ b/metricbeat/module/ceph/osd_tree/data.go @@ -86,7 +86,7 @@ func eventsMapping(content []byte) ([]common.MapStr, error) { nodeInfo := common.MapStr{} if node.ID < 0 { //bucket node - nodeInfo["children"] = childrenMap[node.Name] + nodeInfo["children"] = strings.Split(childrenMap[node.Name], ",") } else { //osd node nodeInfo["crush_weight"] = node.CrushWeight diff --git a/metricbeat/module/ceph/osd_tree/osd_tree_test.go b/metricbeat/module/ceph/osd_tree/osd_tree_test.go index dcfedc2e291..8207c4e8156 100644 --- a/metricbeat/module/ceph/osd_tree/osd_tree_test.go +++ b/metricbeat/module/ceph/osd_tree/osd_tree_test.go @@ -59,7 +59,7 @@ func TestFetchEventContents(t *testing.T) { nodeInfo := events[0] assert.EqualValues(t, "default", nodeInfo["name"]) assert.EqualValues(t, "root", nodeInfo["type"]) - assert.EqualValues(t, "-3", nodeInfo["children"]) + assert.EqualValues(t, []string{"-3"}, nodeInfo["children"]) assert.EqualValues(t, -1, nodeInfo["id"]) assert.EqualValues(t, 10, nodeInfo["type_id"]) assert.EqualValues(t, "", nodeInfo["father"]) @@ -68,7 +68,7 @@ func TestFetchEventContents(t *testing.T) { nodeInfo = events[1] assert.EqualValues(t, "ceph-mon1", nodeInfo["name"]) assert.EqualValues(t, "host", nodeInfo["type"]) - assert.EqualValues(t, "1,0", nodeInfo["children"]) + assert.EqualValues(t, []string{"1", "0"}, nodeInfo["children"]) assert.EqualValues(t, -3, nodeInfo["id"]) assert.EqualValues(t, 1, nodeInfo["type_id"]) assert.EqualValues(t, "default", nodeInfo["father"]) diff --git a/metricbeat/module/ceph/pool_disk/_meta/data.json b/metricbeat/module/ceph/pool_disk/_meta/data.json index 930a560cebb..8d0d0ba019d 100644 --- a/metricbeat/module/ceph/pool_disk/_meta/data.json +++ b/metricbeat/module/ceph/pool_disk/_meta/data.json @@ -1,6 +1,6 @@ { "@timestamp": "2017-10-12T08:05:34.853Z", - "beat": { + "agent": { "hostname": "host.example.com", "name": "host.example.com" }, @@ -10,7 +10,7 @@ "name": "rbd", "stats": { "available": { - "bytes": 47478734249 + "bytes": 0 }, "objects": 0, "used": { @@ -20,10 +20,16 @@ } } }, + "event": { + "dataset": "ceph.pool_disk", + "duration": 115000, + "module": "ceph" + }, "metricset": { - "host": "ceph:5000", - "module": "ceph", - "name": "pool_disk", - "rtt": 115 + "name": "pool_disk" + }, + "service": { + "address": "127.0.0.1:5000", + "type": "ceph" } } \ No newline at end of file diff --git a/metricbeat/module/elasticsearch/_meta/Dockerfile b/metricbeat/module/elasticsearch/_meta/Dockerfile index 535344e910b..ec61f1c3160 100644 --- a/metricbeat/module/elasticsearch/_meta/Dockerfile +++ b/metricbeat/module/elasticsearch/_meta/Dockerfile @@ -1,2 +1,2 @@ -FROM docker.elastic.co/elasticsearch/elasticsearch:6.5.1 +FROM docker.elastic.co/elasticsearch/elasticsearch:6.6.0 HEALTHCHECK --interval=1s --retries=300 CMD curl -f http://localhost:9200/_xpack/license diff --git a/metricbeat/module/elasticsearch/fields.go b/metricbeat/module/elasticsearch/fields.go index 3d771737ef1..90b49186eb6 100644 --- a/metricbeat/module/elasticsearch/fields.go +++ b/metricbeat/module/elasticsearch/fields.go @@ -32,5 +32,5 @@ func init() { // AssetElasticsearch returns asset data. // This is the base64 encoded gzipped contents of ../metricbeat/module/elasticsearch. func AssetElasticsearch() string { - return "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" + return "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" } diff --git a/metricbeat/module/elasticsearch/index_recovery/_meta/fields.yml b/metricbeat/module/elasticsearch/index_recovery/_meta/fields.yml index e41b967c780..e61a341e272 100644 --- a/metricbeat/module/elasticsearch/index_recovery/_meta/fields.yml +++ b/metricbeat/module/elasticsearch/index_recovery/_meta/fields.yml @@ -26,9 +26,9 @@ description: > Target node id. - name: target.host - type: ip + type: keyword description: > - Target node host address. + Target node host address (could be IP address or hostname). - name: target.name type: keyword description: > @@ -39,9 +39,9 @@ description: > Source node id. - name: source.host - type: ip + type: keyword description: > - Source node host address. + Source node host address (could be IP address or hostname). - name: source.name type: keyword description: > diff --git a/metricbeat/module/kafka/_meta/Dockerfile b/metricbeat/module/kafka/_meta/Dockerfile index 35d19a8bc5c..3813e4c65d2 100644 --- a/metricbeat/module/kafka/_meta/Dockerfile +++ b/metricbeat/module/kafka/_meta/Dockerfile @@ -1,6 +1,6 @@ FROM debian:stretch -ARG KAFKA_VERSION=2.0.0 +ARG KAFKA_VERSION=2.1.0 ENV KAFKA_HOME /kafka # The advertised host is kafka. This means it will not work if container is started locally and connected from localhost to it diff --git a/metricbeat/module/kafka/_meta/docs.asciidoc b/metricbeat/module/kafka/_meta/docs.asciidoc index aa228583dbc..11961a72456 100644 --- a/metricbeat/module/kafka/_meta/docs.asciidoc +++ b/metricbeat/module/kafka/_meta/docs.asciidoc @@ -5,4 +5,4 @@ The default metricsets are `consumergroup` and `partition`. [float] === Compability -This module is tested with Kafka 0.10.2.1, 1.1.0 and 2.0.0. +This module is tested with Kafka 0.10.2.1, 1.1.0 and 2.1.0. diff --git a/metricbeat/module/kafka/consumergroup/_meta/fields.yml b/metricbeat/module/kafka/consumergroup/_meta/fields.yml index 0cea9183255..963844e43b4 100644 --- a/metricbeat/module/kafka/consumergroup/_meta/fields.yml +++ b/metricbeat/module/kafka/consumergroup/_meta/fields.yml @@ -40,7 +40,7 @@ description: consumer offset into partition being read - name: meta - type: text + type: keyword description: custom consumer meta data string - name: error.code diff --git a/metricbeat/module/kafka/fields.go b/metricbeat/module/kafka/fields.go index d5a0bd09a9e..7a7d0fa0bb2 100644 --- a/metricbeat/module/kafka/fields.go +++ b/metricbeat/module/kafka/fields.go @@ -32,5 +32,5 @@ func init() { // AssetKafka returns asset data. // This is the base64 encoded gzipped contents of ../metricbeat/module/kafka. func AssetKafka() string { - return "eJy8WE1v2zgQvftXDHJyDtGedg8+LNAmRWE0aIu0PRu0OLIJU6RK0k797wtSli1RIvVhJzrFjPTe43D4ZsgH2OFxATuS7cgMwDDDcQF3X+zvuxkARZ0qVhgmxQL+nwEAuP9BLume4wxAb6Uyq1SKjG0WkBGu7ahCjkTjAjYWNmPIqV64zx9AkBwvlPYxx8K+quS+OI108DZh6lBrJXeozsNdeEHM8vnoEOBRCr3PUcFn+yksRSZVTuwHsCUHhDWiAIWEQqZkDvPTZ1siKGdi04A0W4S0wnNS7pPaC/5c6vNhtDFczYdLjyI6pdq0GJ118hBKFWrdSbbD46tUvpBhfIQeUBmmkZ4pWmtmZMHSxP7dWrc2dYT2p8VxmCEOVEqqJJW0zeRFtJfGQYGFStpsBVGG2W+TxvqNZfpewQCjURY3u1UHVzx+DbJfgv3eIzAKMnMZW1zYhRsoY9ivo9yD7yMHiKDuV0matMRV+853gNG+EAJq2lv1xPZ0y6PCekpNhcKUGKQL+C/5d9I2vKWbQb+jhSIAMWeDmLsNmCo0XK4aCfJ3Ox70uN44HT7HGIsf4Lr+qj7FidyGGcU1OPFq1huVcN7Ew6vaYA0Xt+wLhMwyjWZSYT0nfYkBTBhZs6Y1MrFxGymuIEdDOvkN/vGFNfn32sj8IsMCASWGgDaqvlM7aTvr3/DJt3PddW5nNf9cAlEvj1FJKWcoutei2wyjej5ozTYC6QnWrYRdETygMLo0t1AOXmFYA73isRS1fIJ5GTiNxlh5pdqE0ft+39pK7YdrqpAGVJAwx3ztl/NJrEwYVIJwr3KcCOq7NmYXowt3F8j4oh3xjCl5eiCMkzXHE66uupwNO6CodVQjc1TgK0bSY3ph/eqAK9fzWzJfZi1snL6NoG8OuF/QhDI0YT0vtcd68Xu0QrGyOEAwtI4X1WhQJUdCW23rAKW9Op4dsG3152Vr3Oola6HSYQVXdWzPzGWTJQBGdViBwoKz1K/et4jDS4ncHYhwRIQ+inTVJ2stJUfiJ/xAZUtBmc02DSyrAgBMAxMp31Ok1ZGMiQcrpnrFoK1wMF/+eBk0E73qybE3mYQV7vEGJQYbKLjB+n8690xlo5KhSbe2PRhqa5FLjqi+q06YpnUdEpbm3fHA7Q4gbV1DjySni4MxF23Xn8c9Nk/LlMu46zUFD6uzvwEAAP//PK58aA==" + return "eJy8WM1u2zwQvPspFjk5h+g7fT34UKBNisJo0BZpezZocWUTpkiVpB347QtSli1RIvVjJzrFjDQzXC5nl3yAHR4XsCPZjswADDMcF3D3zf6+mwFQ1KlihWFSLODjDADA/Q9ySfccZwB6K5VZpVJkbLOAjHBtRxVyJBoXsLGwGUNO9cJ9/gCC5HihtI85FvZVJffFaaSDtwlTh1oruUN1Hu7CC2KWz2eHAI9S6H2OCr7aT2EpMqlyYj+ALTkgrBEFKCQUMiVzmJ8+2xJBORObBqTZIqQVnpNyn9Re8OdSnw+jjeFqPlx6FNEp1abF6KyTh1CqUOtOsh0eX6XyhQzjI/SAyjCN9EzRWjMjC5Ym9u/WurWpI7S/LY7DDHGgUlIlqaRtJi+ivTQOCixU0mYriDLMfps01m8s088KBhiNsrjZrTq44vFrkP0R7O8egVGQmcvY4sIu3EAZw34d5R58HzlABHW/StKkJa7ad74DjPaFEFDT3qontqdbHhXWU2oqFKbEIF3Ah+T/Sdvwlm4G/Y4WigDEnA1i7jZgqtBwuWokyN/teNDjeuN0+BxjLH6A6/qr+hQnchtmFNfgxKtZb1TCeRMPr2qDNVzcsi8QMss0mkmF9Zz0JQYwYWTNmtbIxMZtpLiCHA2ZuurpXhuZX5RYLKDEENBG1TdrJ3NnCRw+/3a6u+btrOa/SyzqFTIqKeUMRfdydPthVM8nrdlGID3BusWwi4IHFEaX/hZKwys8a6BdPJailk8wLwOn0Rgrr1SbMHrfb11bqf1wTRXSgAoS5piv/Yo+iZUJg0oQ7hWPE0F948YcY3Tt7gIZX7cjtjElTw+EcbLmeMLVVaOzYQcUtaZqZI4KfMVIekyvrd8dcGV8flfmy6yFjdO3EfTDAfcLmlCJJqznpfxYL36PbihWGQcIhtYJoxoNquRIaKtzHaC0V8ezA7bd/rzsjlvtZC1UOqzgqqbtmblssgTAqA4rUFhwlvoF/BZxeCmRuwMRjojQR5Gu+mStpeRI/IQfqGwpKLPZpoFlVQCAaWAi5XuKtDqVMfFgxVSvGLQVDubLXy+DZqJXPTn2JpOwwj3eoMRgAwU3WP8v556pbFQyNOnWtgdDbS1yzxHVd9Uh07RuRMLSvGseuN0ZpK1r6KnkdHcw5q7t+iO5x+ZpmXIfd72m4Hl19i8AAP//Xtd9qA==" } diff --git a/metricbeat/module/kibana/_meta/Dockerfile b/metricbeat/module/kibana/_meta/Dockerfile index ce77bb67674..f94f858d7f1 100644 --- a/metricbeat/module/kibana/_meta/Dockerfile +++ b/metricbeat/module/kibana/_meta/Dockerfile @@ -1,2 +1,2 @@ -FROM docker.elastic.co/kibana/kibana:6.5.1 +FROM docker.elastic.co/kibana/kibana:6.6.0 HEALTHCHECK --interval=1s --retries=300 CMD curl -f http://localhost:5601/api/status | grep '"disconnects"' diff --git a/metricbeat/module/kibana/fields.go b/metricbeat/module/kibana/fields.go index b59d1e5e13c..5fa5228de54 100644 --- a/metricbeat/module/kibana/fields.go +++ b/metricbeat/module/kibana/fields.go @@ -32,5 +32,5 @@ func init() { // AssetKibana returns asset data. // This is the base64 encoded gzipped contents of ../metricbeat/module/kibana. func AssetKibana() string { - return "eJzEmM9u40YMxu95CsKXvSR6AB8KFG2BFkWCYtugh6IwaA1tTTMaqkPKsfv0xeiPI8ujWNm1sToEiWR/32/IIanJA7zQYQkvdo0e7wDUqqMlLH5tbizuAAxJHmyllv0SvrsDAGgfQsmmdnQHIAUHXeXsN3a7hA06iXcDOUKhJWyjsJCq9VtZwl8LEbe4h0WhWi3+vgPYWHJGlo32A3gsaUAULz1UUSdwXXV3ElSnOkMtUVQ53k3JTUq2V7fgRgfQGwi1f1BbEpSkweaSDT5+uu7+GrMN+erampMHPeILHV45jJ+9AzqAtV4UfU7w/PzLj0nb+DNpq7TXr/M8k+49rTe0v95an7Ak4E3n/0nAeqXg0SWMeoSCRbPJtV8l5NFiMgQa0EsVKwaNCSRydQ6hsKPwSY4czZ6NlkmgHQWx7K+OkdI9lqTHSgoeb7PWdM3sCMdAF0z/LEgLCqAF9QDr2joDVgCPfu29NJKi1tdPRr8pYjoInRYpo54hZ5/XIZBv2qmnPIqnmRz77QeLpS7XFGK55M6SVxhYQImGQLkJX7t/MnhiJdACFdaBX4WCQI4ehLyBsnZqK0cgNv6KnriWE0VlCPRvTaKDDzfKgCIUO6kC+5zum+2pBR0a+UAPtRCQKK6dlYLMUDZLxq0KnE9V0rjPz4jUb61c391Hz1Ot/A2FdjF7jrlaGXJ4yMrx93swydGRWW0c47gOLhIC/BR9IPpA4wPWQ2mds0I5ezM27fFKKjkcsoJwHJP34jWDp49ZVJ4I3FToBr2RFV22Piidf/edjd9IcyhRlzD15YsLAPgjurcLQOc4RyUTt3G3u2KE0+rHQS5kvhn9z5E7EsD6MB9Z7H+0cra0+s3AH3GfATsDUmFODdFp/J/YUPaP9Iu6n5GIKr6dpUrvwnJm4D432rGNDoI8WXk9UdcKr9WhAD53vTXn2uuHOtUblLHStdZbBOpt3nSLl3aYvFKggTWN5+qoHdyArC10f8Y3kTmp2AutYtqvlb/PnShE0Y8Nmjcy3G1vs8W/31HALR2X3lBeGDCDEYP722A94t6WdTkTa/K97sPHv98bhS4dX3XSu+2xI6qn349ue8KM6mnf7giQtaV2qxNGV8lphDb9Gcct7VwW/7x+Cjr1xmwiBekS/+IG8tjKnW9KmNE4Zh0z4AoV+0PrMjCcfpeH82k5jZR+SZ3F1A/OmCsrOvrvTX9NhRBmj0+4FL+ZvJAYWMPp+X5EYcY4vSXpCdz/AQAA//98JjTx" + return "eJzMmM1u4zYQx+9+ioEve0n0AD4UKNoCLYoExbZBD0VhjMWxxYYiVc7Iifv0BSnJkWXKllsLuzoEiT7+85vhfJB5hFc6rOBVb9DiAkC0GFrB8ud4Y7kAUMS515VoZ1fwzQIAoHkIpVO1oQUAF87LOnd2q3cr2KLhcNeTIWRawS4IM4lou+MV/LFkNssHWBYi1fLPBcBWk1G8itqPYLGkHlG45FAFHe/qqr2ToDrV6WuxoPDxbkpuVLK5WoejDqBV4Gv7KLokKEm8zjnrvX7qd3cN2fp8da3VyYMOEY1GHjypUIoVMPm9zik7+7DUO4+ND+JrGjy94GPPT21Z0OYELy8/fZ8kDj+TxK90eHN+CHWj2TP1zqy2it7vZ/cZSwK3be1/YtBWyFs0CUMdQuFYsnndDyZGQyAeLVeh3lApTzxMj6mJk/76HtkTLJD/xEc/YsUE5KRDe/KsnZ3sxjSItOppCNLvXArBsaFYrLhwkoTeOGcIh7JXsH8vSAryIAV1LmxqbRRoBjzaa+6lkQSlTifDPZIyLCehkSJlqGPInc1r78nGYWApD+JpJuPs7sZircsN+VCuudFkBXomoERFIC6Gr8m/DJ6dEEiBAhvv3pg8Q44WmKyCsjaiK0PAOvyKllzNJ4riwNPfNbH0Xo7KgMwU5oCAszk9xPSWgg5R3tNjzQTEghujuSDVl82Scau8y8cqeTilJkTql0aum02D56lB9IFC+7B6xrlqrcjgISuH33dgnKMhtd4ah8M6uEoI8EOwA8EORDugLZTaGM2UO6uGRju8kkrnD1lBOIzJpXhN4OliFpRHAjcWul5vdoIm2xyEzr+9kPhR2vkSZQVjH191AOC3YL1xAI1xOQqpkMZtdoUIp9WP2xAm9cXofwzcgQA2h+nIrP+htdGlli8G/oTvGTijgCvMKRKdxv/ZKcr+4s6phwkLUYW9Zar0rrgzAfclaoc22gvyaOV1RG0rvFeHAvjc9tbc1VZu6lQfUEpz21rnCNTHvGmd52aYvJGnnmkaztVBO5iBrCl0e8Y3snJcOcu0Dst+r/X73IpCEL1t0HyQ4X43T4p/uyePOzq6HimvDJjeiMH3ebCe8F2XdTkRa3Rfd/Ph9deo0C7H/zqnzn/qS++PvtrzcQBLI7dni6yp0nkON20LyOY65MS8yVyoJmOy8Of9V79Vj8ZGVj/dXf5z73pq5M7rASb0rEknHLhDs/iusdIzOH6MgPNBPY6U3h9PYupmdlgrzTL4t1d3jYUQJk9uuBa/ibyQmJX9wX05ojBhks9JegL3bwAAAP//xlKE7A==" } diff --git a/metricbeat/module/kibana/stats/_meta/data.json b/metricbeat/module/kibana/stats/_meta/data.json index 7097d36ba42..bafeb99255b 100644 --- a/metricbeat/module/kibana/stats/_meta/data.json +++ b/metricbeat/module/kibana/stats/_meta/data.json @@ -1,41 +1,46 @@ { "@timestamp": "2017-10-12T08:05:34.853Z", - "beat": { + "agent": { "hostname": "host.example.com", "name": "host.example.com" }, "elasticsearch": { "cluster": { - "id": "njqU4EQaTROIDlWPeUMQyw" + "id": "hyK0oDhoThywCtJd2DV8Bg" } }, + "event": { + "dataset": "kibana.stats", + "duration": 115000, + "module": "kibana" + }, "kibana": { "stats": { - "concurrent_connections": 6, + "concurrent_connections": 1, "host": { "name": "localhost" }, - "index": "Shaunaks-MBP-2", - "name": "Shaunaks-MBP-2", + "index": "Shaunaks-MBP-2.attlocal.net", + "name": "Shaunaks-MBP-2.attlocal.net", "process": { "event_loop_delay": { - "ms": 0.23628300055861473 + "ms": 0.45250000059604645 }, "memory": { "heap": { "size_limit": { - "bytes": 1501560832 + "bytes": 1526909922 }, "total": { - "bytes": 215003136 + "bytes": 307593216 }, "used": { - "bytes": 185343400 + "bytes": 226887112 } } }, "uptime": { - "ms": 1343714 + "ms": 124098 } }, "request": { @@ -49,22 +54,20 @@ } }, "snapshot": false, - "status": "green", - "transport_address": "localhost:5601", - "uuid": "5b2de169-2785-441b-ae8c-186a1936b17d", - "version": "7.0.0-alpha1" + "status": "green" } }, "metricset": { - "host": "127.0.0.1:5601", - "module": "kibana", - "name": "stats", - "rtt": 115 + "name": "stats" }, "process": { - "pid": 20173 + "pid": 93807 }, "service": { - "name": "kibana" + "address": "127.0.0.1:5601", + "id": "5b2de169-2785-441b-ae8c-186a1936b17d", + "name": "kibana", + "type": "kibana", + "version": "7.0.0" } } \ No newline at end of file diff --git a/metricbeat/module/kibana/stats/_meta/fields.yml b/metricbeat/module/kibana/stats/_meta/fields.yml index 3c7048dda2c..55d55e9d76b 100644 --- a/metricbeat/module/kibana/stats/_meta/fields.yml +++ b/metricbeat/module/kibana/stats/_meta/fields.yml @@ -5,11 +5,13 @@ release: ga fields: - name: uuid - type: keyword + type: alias + path: service.id + migration: true description: > Kibana instance UUID - name: name - type: text + type: keyword description: > Kibana instance name - name: index @@ -21,13 +23,17 @@ description: > Kibana instance hostname - name: transport_address - type: keyword + type: alias + path: service.address + migration: true description: > Kibana server's hostname and port - name: version - type: keyword + type: alias description: > Kibana version + path: service.version + migration: true - name: snapshot type: boolean description: > diff --git a/metricbeat/module/kibana/stats/data.go b/metricbeat/module/kibana/stats/data.go index 8f494e8596c..09ea29a1759 100644 --- a/metricbeat/module/kibana/stats/data.go +++ b/metricbeat/module/kibana/stats/data.go @@ -126,6 +126,36 @@ func eventMapping(r mb.ReporterV2, content []byte) error { } event.RootFields.Put("process.pid", int(pid)) + // Set service ID + uuid, err := dataFields.GetValue("uuid") + if err != nil { + event.Error = elastic.MakeErrorForMissingField("kibana.uuid", elastic.Kibana) + r.Event(event) + return event.Error + } + event.RootFields.Put("service.id", uuid) + dataFields.Delete("uuid") + + // Set service version + version, err := dataFields.GetValue("version") + if err != nil { + event.Error = elastic.MakeErrorForMissingField("kibana.version", elastic.Kibana) + r.Event(event) + return event.Error + } + event.RootFields.Put("service.version", version) + dataFields.Delete("version") + + // Set service address + serviceAddress, err := dataFields.GetValue("transport_address") + if err != nil { + event.Error = elastic.MakeErrorForMissingField("kibana.transport_address", elastic.Kibana) + r.Event(event) + return event.Error + } + event.RootFields.Put("service.address", serviceAddress) + dataFields.Delete("transport_address") + event.MetricSetFields = dataFields r.Event(event) diff --git a/metricbeat/module/kibana/status/_meta/data.json b/metricbeat/module/kibana/status/_meta/data.json index 6e0d8089239..107ec83ab2c 100644 --- a/metricbeat/module/kibana/status/_meta/data.json +++ b/metricbeat/module/kibana/status/_meta/data.json @@ -1,9 +1,14 @@ { - "@timestamp": "2016-05-23T08:05:34.853Z", - "beat": { + "@timestamp": "2017-10-12T08:05:34.853Z", + "agent": { "hostname": "host.example.com", "name": "host.example.com" }, + "event": { + "dataset": "kibana.status", + "duration": 115000, + "module": "kibana" + }, "kibana": { "status": { "metrics": { @@ -13,23 +18,22 @@ "total": 0 } }, - "name": "kibana", + "name": "Shaunaks-MBP-2", "status": { "overall": { - "state": "red" + "state": "green" } - }, - "uuid": "c94305f7-ad4e-4e0c-8aba-5c3e869dce59", - "version": { - "number": "6.0.0-alpha1" } } }, "metricset": { - "host": "kibana:5601", - "module": "kibana", - "name": "status", - "rtt": 115 + "name": "status" }, - "type": "metricsets" + "service": { + "address": "127.0.0.1:5601", + "id": "5b2de169-2785-441b-ae8c-186a1936b17d", + "name": "kibana", + "type": "kibana", + "version": "7.0.0" + } } \ No newline at end of file diff --git a/metricbeat/module/kibana/status/_meta/fields.yml b/metricbeat/module/kibana/status/_meta/fields.yml index e80113fb947..8f0993e7cdf 100644 --- a/metricbeat/module/kibana/status/_meta/fields.yml +++ b/metricbeat/module/kibana/status/_meta/fields.yml @@ -9,13 +9,17 @@ description: > Kibana instance name. - name: uuid - type: keyword + type: alias + path: service.id + migration: true description: > Kibana instance uuid. - name: version.number - type: keyword + type: alias description: > Kibana version number. + path: service.version + migration: true - name: status.overall.state type: keyword description: > diff --git a/metricbeat/module/kibana/status/data.go b/metricbeat/module/kibana/status/data.go index 2c498488f2b..9fe0773420d 100644 --- a/metricbeat/module/kibana/status/data.go +++ b/metricbeat/module/kibana/status/data.go @@ -25,6 +25,7 @@ import ( "github.com/elastic/beats/libbeat/common" s "github.com/elastic/beats/libbeat/common/schema" c "github.com/elastic/beats/libbeat/common/schema/mapstriface" + "github.com/elastic/beats/metricbeat/helper/elastic" "github.com/elastic/beats/metricbeat/mb" "github.com/elastic/beats/metricbeat/module/kibana" ) @@ -71,6 +72,26 @@ func eventMapping(r mb.ReporterV2, content []byte) error { return event.Error } + // Set service ID + uuid, err := dataFields.GetValue("uuid") + if err != nil { + event.Error = elastic.MakeErrorForMissingField("uuid", elastic.Kibana) + r.Event(event) + return event.Error + } + event.RootFields.Put("service.id", uuid) + dataFields.Delete("uuid") + + // Set service version + version, err := dataFields.GetValue("version.number") + if err != nil { + event.Error = elastic.MakeErrorForMissingField("version.number", elastic.Kibana) + r.Event(event) + return event.Error + } + event.RootFields.Put("service.version", version) + dataFields.Delete("version") + event.MetricSetFields = dataFields r.Event(event) diff --git a/metricbeat/module/kubernetes/_meta/config.yml b/metricbeat/module/kubernetes/_meta/config.yml index ec678aa79b5..e0d140ddaa8 100644 --- a/metricbeat/module/kubernetes/_meta/config.yml +++ b/metricbeat/module/kubernetes/_meta/config.yml @@ -17,8 +17,8 @@ # Enriching parameters: #add_metadata: true #in_cluster: true - #labels.dedot: false - #annotations.dedot: false + #labels.dedot: true + #annotations.dedot: true # When used outside the cluster: #in_cluster: false #host: node_name diff --git a/metricbeat/module/kubernetes/event/config.go b/metricbeat/module/kubernetes/event/config.go index daabe2b292e..93cf663eca8 100644 --- a/metricbeat/module/kubernetes/event/config.go +++ b/metricbeat/module/kubernetes/event/config.go @@ -39,8 +39,8 @@ func defaultKubernetesEventsConfig() kubeEventsConfig { return kubeEventsConfig{ InCluster: true, SyncPeriod: 1 * time.Second, - LabelsDedot: false, - AnnotationsDedot: false, + LabelsDedot: true, + AnnotationsDedot: true, } } diff --git a/metricbeat/module/kubernetes/event/event.go b/metricbeat/module/kubernetes/event/event.go index 2d10068d8a5..defdb9857bd 100644 --- a/metricbeat/module/kubernetes/event/event.go +++ b/metricbeat/module/kubernetes/event/event.go @@ -46,7 +46,8 @@ type MetricSet struct { } // dedotConfig defines LabelsDedot and AnnotationsDedot. -// Default to be false. If set to true, replace dots in labels with `_`. +// If set to true, replace dots in labels with `_`. +// Default to be true. type dedotConfig struct { LabelsDedot bool `config:"labels.dedot"` AnnotationsDedot bool `config:"annotations.dedot"` diff --git a/metricbeat/module/kubernetes/fields.go b/metricbeat/module/kubernetes/fields.go index 3c6284e8883..558c3154dd5 100644 --- a/metricbeat/module/kubernetes/fields.go +++ b/metricbeat/module/kubernetes/fields.go @@ -32,5 +32,5 @@ func init() { // AssetKubernetes returns asset data. // This is the base64 encoded gzipped contents of ../metricbeat/module/kubernetes. func AssetKubernetes() string { - return "eJzsXU9v6zgOv79PQfTUB2R6WuyhhwVmO/uwxfszRV/fzGGxKBSbSTS1JY8kp5P99AvJtuzYku0kSpq21qm1E/IniqJIkVJ+gifcXMNTPkfBUKH8AKCoSvAaLj7bhxcfAGKUkaCZopxdwz8+AADUH4AUlaCR/rbABInEa1iSDwASlaJsKa/hPxdSJhczuFgplV38V79bcaEeI84WdHkNC5JI/ACwoJjE8tow+AkYSbEFTze1yTQHwfOsfOKAp9stW3CREv0YCItBKqKoVDSSwBeQ8VhCShhZYgzzTYPPVUmhiaaJiGRUolijsG9coHqAteT3890tFAQboqzatkir1obWhCfwzxyluooSikxtfaTC+YSbZy7i1rsetLrdGHoQc8qWoFZYMZK9KARKnosIw+G4LyhjDE7abQAynx8Tg498B0bEs/AAwJCFyyjJpUIxM0xlRiKcWel87MW1RjEPD+vfDw930CHd0VCeexQ04Wy5G+cHrkgCLE/nKPT0HqWcCVHIos2VzNNAMEoBSChJz0DmqcZT/E9RAmWQ0khwiRFn8TiAISVVjZFFuKfQ5nn0hG5QfP4HRu1XxcPHQLBhRaXiS0FSKIDIjp2OOFOEssPsdL0s1PSCmGmpiFCPiqZuqxAT1X4xIKDvmiB0CFppZLmTUVsWIzjd3P2AXJIlOgTh63YTivlu520foD6qW53kwkV4mPgQgyYT1u5vl41DvZttQL7NdmOVTkv9hgssRc8Ic5qQDlrCuBaLD/Qg4JFgC6XAeIChhcVjvMo6RmIblYxIgvHjIuHE98HCxbuGDEWETLkVa+duaAETCaRBVttH7fWoYqHhMQJJEh4RReYJ6u/19jehKVWvssMxLijDuOiBZm+e1sbwUj/xCgXoAnJmvoux2xVJ+LKtK3ubpi98qVfYBd/RJJE1oYnGfBSzNN+o/edfNeB9REaOtZGO7SpEJCMRVRvtkripW7tafvLtS6fQ5PGS0Sbv7UvFGPbxQqHaErj4hljhu57wNvXDl7Iilqjnibc7NayFwH7HIxQqzWgMII9ehgdkVMMBqAKSYspF23D49WAy1ODQQKcQT+FQn5dACjF4u3v+3uXXRgd2dDA9KgCvwccc0+0D3MxSLfyeZlNIQh5nYTqXmXL//Xv/PKkAP3PxRNlSdvZw4E3J4/eimyBRjZNLRpa4IHmi/HriQT4C0Te716bZgIePXTvJH1ycCI/h5UVlZw/najE+Whtazd9FXHHPuYIFTVBupMJ05xDjfbg8bik1nfD3Hom5JVT63y8XkZ0g0vjhiDEq9rjeznLuvMP/sMJmNtbQs0ltVBDxJMFI2TdqRRQQgbBEhoKoIn1cZDckiJwx2uovZZLGxtH53E5mww65A38I7JFzr3RvNJWCCwiMuIil8bjqbJCiKRbPMiIUjfKEiEIIsCISeBTlQmyNfYXQfFORNHOg7KpaX5ZkQYVUjyUr5knh7p4reagA6n4aHlDz0M/aWtVwssnRAWkWA3jq6Fp2nBl//rYXxNeCVKkMGFsffEnXyBwSiXi2eVTcBaLOGRLJWQh094bSWHBWETdZENk8bDIbqPRzTFGRmCgySvMHxqOgBERKHlFjaJ6pWvWOSd9ccs/K3d03a4cEalAdXfbOgRH2fmseGAaUs37JN1NdnmTqXrUM30hqx7yfpym4CMvYkNTL+vOKRqvS6j4TWS86bve8rPl4XKOQtDXzDgL1W0FwSyD9BTg5bbM4gP0PRv/MEWiMTNEFRQGKN4A4Cg5snh2TxWNC2VNAMPdfQGAmUGo0ZTWUzyBQtubJGuNHB8Zj2YWKp0sufRaCZDS85vx8dwvrbe3pGa4nygKqjeatKY5gHNZ4sIbx6GF6vPlaUd5B9GEn7I/bXwZ4NzdrD3HgGyU6Zmtwqs6ZqnM8LXR1zjetb6+7MGfK07nalKdrtXB5uikR0wI8JWLcwKdETE8ihqHSehPMXou/3rTy3WOEdG22an207IayEFwce1G+/8vHx+7WvO0BeRCEyZQqdT5j8uAcE7sTPWU9izZSmp+mhOeOAppynXXrCOc9pDlrH8BXVNkGdYpq2BrVedTB1nh8tbDWp8mZdwdnH7tNU+0BHqmu2b8mDDMYYgIjZziM3SIZM9Nht62U29R4vLuvGjBy5YD3LMYRawvsYuzeoQjdK5ANVnlTYIfsYWc8fpVb2FNEWrQpIq3baxqQVxeRvouc0ZlkSTqwzvSAyS7Hl9/bkWW9sNrzJLJ9oGTcWeXAWbIpIdSCfa7zajq4FXSy7X16631sDW5NGn+XWwnE7288gVhI5bmTRvRHEG88w1wIRKA0ZZFGIpL+b7AKISNL/HSsPGaBaXRO9e74WPz51HoX9Zwvitq7Kq/r5Vtc1F0+LBVRebg8V7Yi0m+o3R1od6LPDbTdMYzgsjx7M4NnQpX5Q6FIKSP9B4KRxP5U3JzzBEm7MHMkyhqhYeKW71Y1qCKiZypQpnC5paZ7gin4eNIHPYc3mmAOGr/fixGCS4vqxhT760G7EUSuvnCe/ZNET3yxmMG/hDCbcnd5kszA/lm+7w6tbtrhKEefcqYZpVmCCuNZLYkbwhhX9zkzLLiYwa+/fv1MkwTjj2X3rw4OvYdmSeH8+ULOQ23g9v1hhkvBsWfYq9sZT4FI2Js03fy2pdQXnA/gygRG2hBcw9+v/hYCucUyUp592IfhHUvqJy0KLQbR5/n0dnHIbdpJBKVnXUQsg0nlagBfHnc9bFXQ5EtHxJglfJMeeDS24dPUBIM4NWFPW3x24uzwqB1gR36rb7nfi33BxbXm14qVJTQi4x2uvXBUXPa5Fi9GSUVPqclBDskvjaGyOf2SY436UmYYHbIhEApjnUL1jFtjW4+dDlaD1whgWew8shkcVMGnC+jMz/yEjH7644qDvGdz7KQZUsClEjnOiov0teubsyfGn5l/3uRMRiuM834lPSj6MSi3+PQZw5AudWN7ccCN9W2mju2edqaam5n9TmxVzHI039pismUzpzvw05D5S3lK33x7y2PvN31Z5CXa/pInd8EJBBs7s0t/LNVsjk3GHQfkOgNyVDgmSdEuvDvfih7qvrmh83i8m6iR3d45ma24VI/H4ahJ+9juuAjvxrhcLPercTjibmYLZrmdeV9tZ94hiylbXl1d7buLGRLdYX5H6Q30+KAhsVpuLryzLtp2ZIahwueSYJm1OuP4uQnUG0AfMXBt8vdH0CHOHx+QV9q6JstGqhkKuC/+caVix8bUL4Wr34KEQ6Wtx67Y+Nz8QNaxhFZeqmOuqig5wXxj6hhqcCatJ3iSOOJju8FJ5thn20JJcZEnyabiNijN5tqKizwJZ9Yqiudv17aQeg2b+0or79gNXeq2wvoOK3v7FlxixqPVR1Mq872E1Vb+E1jaLYlYFdrL2B55etZ6b2fnlsr7hAgvYHU7+5d9ACtwtf059jg3LB2tfx7xvIbbDnID7HkMczW4I4BZm2uOkIQyt8V5lEZdXQib6yp/gcMMb52y8pra6YalTgtwivDN3bA0Xa7kIDeVjb+jKs/pHqHtNt0jdOg9QhWaNU/yNFQatiB2hkHgbwUwryMyXexStulil+liF/cHpotdDuu06/crXFBOcHvKp5G/H3i631kswfw/AAD//7QUBBw=" + return "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" } diff --git a/metricbeat/module/kubernetes/pod/_meta/fields.yml b/metricbeat/module/kubernetes/pod/_meta/fields.yml index f449f367045..1e52a5939ed 100644 --- a/metricbeat/module/kubernetes/pod/_meta/fields.yml +++ b/metricbeat/module/kubernetes/pod/_meta/fields.yml @@ -86,7 +86,7 @@ format: bytes description: > Total memory available - - name: workingSet + - name: working_set type: group fields: - name: bytes @@ -102,11 +102,11 @@ format: bytes description: > Total resident set size memory - - name: pageFaults + - name: page_faults type: long description: > Total page faults - - name: majorPageFaults + - name: major_page_faults type: long description: > Total major page faults diff --git a/metricbeat/module/kubernetes/pod/data.go b/metricbeat/module/kubernetes/pod/data.go index c1370487474..75b43d74328 100644 --- a/metricbeat/module/kubernetes/pod/data.go +++ b/metricbeat/module/kubernetes/pod/data.go @@ -79,14 +79,14 @@ func eventMapping(content []byte, perfMetrics *util.PerfMetricsCache) ([]common. "available": common.MapStr{ "bytes": availMem, }, - "workingSet": common.MapStr{ + "working_set": common.MapStr{ "bytes": workingSet, }, "rss": common.MapStr{ "bytes": rss, }, - "pageFaults": pageFaults, - "majorPageFaults": majorPageFaults, + "page_faults": pageFaults, + "major_page_faults": majorPageFaults, }, "network": common.MapStr{ diff --git a/metricbeat/module/logstash/_meta/Dockerfile b/metricbeat/module/logstash/_meta/Dockerfile index 4c47a068ed1..4c30c288cc2 100644 --- a/metricbeat/module/logstash/_meta/Dockerfile +++ b/metricbeat/module/logstash/_meta/Dockerfile @@ -1,4 +1,4 @@ -FROM docker.elastic.co/logstash/logstash:6.3.0 +FROM docker.elastic.co/logstash/logstash:6.6.0 COPY healthcheck.sh / ENV XPACK_MONITORING_ENABLED=FALSE diff --git a/metricbeat/module/logstash/fields.go b/metricbeat/module/logstash/fields.go index f5ef7f46e78..8bf468ea371 100644 --- a/metricbeat/module/logstash/fields.go +++ b/metricbeat/module/logstash/fields.go @@ -32,5 +32,5 @@ func init() { // AssetLogstash returns asset data. // This is the base64 encoded gzipped contents of ../metricbeat/module/logstash. func AssetLogstash() string { - return "eJy0k8Fum0AQhu88xS/OtR+AQ2+t6qpVcvIliiIEY7zxsoN2Bkd++wgMFpAldmxnjjvi+7/ZHRbY0SGB5UI0lW0EqFFLCeJ/3VEcATlJ5k2lhl2CnxEA9G2UnNeWIsCTpVQoQZFGgJCqcYUkeIpFbPwD8Va1ip8jYGPI5pK0nAVcWtLIoCk9VA3Jc111JwGHMWlIc5zT6TBEmyUea/L9eLS+puFDgS2Ljhq9xI4Ob+zzSe8Tlab+sGgLDmbtyYthd7+409uuA+Q+9XVfBhOnt3xB3t/1f6zchieN0P2en/vc7Bf4NBUafRhemRD4GGzZFdelPg6ow11eiqYqt270S0tBSepNJsubFpz25EZG81YXzP6rpWE65ZzEUMTML8D177ByGZfGFd2YyLh2Sn45a8H19He/h8ZDrQV/RWNjrJKn71jN3x36g8t7AAAA//+4rIER" + return "eJyslM2OmzAQgO88xYhzwwNw6KmtmqpV95TLarWyYALeGA/yDKzy9isCZMEx+d055OCJP3/jGbOCHe5TMFSwKC4jANFiMIX477AURwA5cuZ0LZpsCt8jAIAxDRXljcEIwKFBxZhCoSIARhFtC07hOWY28TeIS5E6fokAthpNzumBswKrKpwZdCH7uiM5auphJeAwJ01plnI8LoZoi8Q+vP3z0sbwD58KlMQyS4wSymjFXqZWUvZbku6nI3j/qHThVC8qrvGzZwrp4jexwAl0NG3RsSZ7oyyja3WGSXj3Q7rHydoE2KP1W1sFjf0eX3Hen80/WNsteYlQdy/f26fJDvfv5PJA/oJPF6HSp4fXOgRebhocG1c7ypA5CRPON+5K+af+CFj/CL7LhEUJP/o6Xw8UqFCczjh56LFii1b8K7t7nn4eaOBXuSQxFdHL42TIFve1Y20zqrQthjIho8YKumTRghr/0/UVGv8bKegWja02gg6XB/1+l18D+sTlIwAA//9sYbU7" } diff --git a/metricbeat/module/logstash/node/_meta/data.json b/metricbeat/module/logstash/node/_meta/data.json index 3059f1d20e2..a9e3bbb153a 100644 --- a/metricbeat/module/logstash/node/_meta/data.json +++ b/metricbeat/module/logstash/node/_meta/data.json @@ -4,23 +4,30 @@ "hostname": "host.example.com", "name": "host.example.com" }, + "event": { + "dataset": "logstash.node", + "duration": 115000, + "module": "logstash" + }, "logstash": { "node": { - "host": "Shaunaks-MBP-2", "jvm": { - "pid": 3674, - "version": "1.8.0_171" - }, - "version": "7.0.0-alpha1" + "version": "1.8.0_191" + } } }, "metricset": { - "host": "127.0.0.1:9600", - "module": "logstash", - "name": "node", - "rtt": 115 + "name": "node" + }, + "process": { + "pid": 93559 }, "service": { - "name": "logstash" + "address": "127.0.0.1:9600", + "hostname": "Shaunaks-MBP-2.attlocal.net", + "id": "7565df20-c3aa-4261-81d5-3b0ab8d15c16", + "name": "logstash", + "type": "logstash", + "version": "7.0.0" } } \ No newline at end of file diff --git a/metricbeat/module/logstash/node/_meta/fields.yml b/metricbeat/module/logstash/node/_meta/fields.yml index 065c7b54ae4..658825edb0b 100644 --- a/metricbeat/module/logstash/node/_meta/fields.yml +++ b/metricbeat/module/logstash/node/_meta/fields.yml @@ -5,11 +5,15 @@ release: ga fields: - name: host - type: keyword + type: alias + path: host.hostname + migration: true description: > Host name - name: version - type: keyword + type: alias + path: service.version + migration: true description: > Logstash Version - name: jvm @@ -22,6 +26,8 @@ description: > Version - name: pid - type: long + type: alias + path: process.pid + migration: true description: > - Pid + Process ID diff --git a/metricbeat/module/logstash/node/data.go b/metricbeat/module/logstash/node/data.go index e3493baa826..25cac855012 100644 --- a/metricbeat/module/logstash/node/data.go +++ b/metricbeat/module/logstash/node/data.go @@ -25,12 +25,14 @@ import ( "github.com/elastic/beats/libbeat/common" s "github.com/elastic/beats/libbeat/common/schema" c "github.com/elastic/beats/libbeat/common/schema/mapstriface" + "github.com/elastic/beats/metricbeat/helper/elastic" "github.com/elastic/beats/metricbeat/mb" "github.com/elastic/beats/metricbeat/module/logstash" ) var ( schema = s.Schema{ + "id": c.Str("id"), "host": c.Str("host"), "version": c.Str("version"), "jvm": c.Dict("jvm", s.Schema{ @@ -60,6 +62,46 @@ func eventMapping(r mb.ReporterV2, content []byte) error { return event.Error } + // Set service ID + serviceID, err := fields.GetValue("id") + if err != nil { + event.Error = elastic.MakeErrorForMissingField("id", elastic.Logstash) + r.Event(event) + return event.Error + } + event.RootFields.Put("service.id", serviceID) + fields.Delete("id") + + // Set service hostname + host, err := fields.GetValue("host") + if err != nil { + event.Error = elastic.MakeErrorForMissingField("host", elastic.Logstash) + r.Event(event) + return event.Error + } + event.RootFields.Put("service.hostname", host) + fields.Delete("host") + + // Set service version + version, err := fields.GetValue("version") + if err != nil { + event.Error = elastic.MakeErrorForMissingField("version", elastic.Logstash) + r.Event(event) + return event.Error + } + event.RootFields.Put("service.version", version) + fields.Delete("version") + + // Set PID + pid, err := fields.GetValue("jvm.pid") + if err != nil { + event.Error = elastic.MakeErrorForMissingField("jvm.pid", elastic.Logstash) + r.Event(event) + return event.Error + } + event.RootFields.Put("process.pid", pid) + fields.Delete("jvm.pid") + event.MetricSetFields = fields r.Event(event) diff --git a/metricbeat/module/logstash/node_stats/_meta/data.json b/metricbeat/module/logstash/node_stats/_meta/data.json index d36b978c579..d6e05f6a6ec 100644 --- a/metricbeat/module/logstash/node_stats/_meta/data.json +++ b/metricbeat/module/logstash/node_stats/_meta/data.json @@ -4,25 +4,31 @@ "hostname": "host.example.com", "name": "host.example.com" }, + "event": { + "dataset": "logstash.node.stats", + "duration": 115000, + "module": "logstash" + }, "logstash": { "node": { "stats": { "events": { - "filtered": 30750, - "in": 1, - "out": 30750 + "filtered": 0, + "in": 0, + "out": 0 } } } }, "metricset": { - "host": "127.0.0.1:9600", - "module": "logstash", - "name": "node_stats", - "namespace": "logstash.node.stats", - "rtt": 115 + "name": "node_stats" }, "service": { - "name": "logstash" + "address": "127.0.0.1:9600", + "hostname": "Shaunaks-MBP-2.attlocal.net", + "id": "7565df20-c3aa-4261-81d5-3b0ab8d15c16", + "name": "logstash", + "type": "logstash", + "version": "7.0.0" } } \ No newline at end of file diff --git a/metricbeat/module/logstash/node_stats/data.go b/metricbeat/module/logstash/node_stats/data.go index 0603c35ab49..c8896df8482 100644 --- a/metricbeat/module/logstash/node_stats/data.go +++ b/metricbeat/module/logstash/node_stats/data.go @@ -25,12 +25,16 @@ import ( "github.com/elastic/beats/libbeat/common" s "github.com/elastic/beats/libbeat/common/schema" c "github.com/elastic/beats/libbeat/common/schema/mapstriface" + "github.com/elastic/beats/metricbeat/helper/elastic" "github.com/elastic/beats/metricbeat/mb" "github.com/elastic/beats/metricbeat/module/logstash" ) var ( schema = s.Schema{ + "id": c.Str("id"), + "host": c.Str("host"), + "version": c.Str("version"), "events": c.Dict("events", s.Schema{ "in": c.Int("in"), "out": c.Int("out"), @@ -59,6 +63,36 @@ func eventMapping(r mb.ReporterV2, content []byte) error { return event.Error } + // Set service ID + serviceID, err := fields.GetValue("id") + if err != nil { + event.Error = elastic.MakeErrorForMissingField("id", elastic.Logstash) + r.Event(event) + return event.Error + } + event.RootFields.Put("service.id", serviceID) + fields.Delete("id") + + // Set service hostname + host, err := fields.GetValue("host") + if err != nil { + event.Error = elastic.MakeErrorForMissingField("host", elastic.Logstash) + r.Event(event) + return event.Error + } + event.RootFields.Put("service.hostname", host) + fields.Delete("host") + + // Set service version + version, err := fields.GetValue("version") + if err != nil { + event.Error = elastic.MakeErrorForMissingField("version", elastic.Logstash) + r.Event(event) + return event.Error + } + event.RootFields.Put("service.version", version) + fields.Delete("version") + event.MetricSetFields = fields r.Event(event) diff --git a/metricbeat/module/mongodb/fields.go b/metricbeat/module/mongodb/fields.go index 8f4c35037c0..9394cc66c58 100644 --- a/metricbeat/module/mongodb/fields.go +++ b/metricbeat/module/mongodb/fields.go @@ -32,5 +32,5 @@ func init() { // AssetMongodb returns asset data. // This is the base64 encoded gzipped contents of ../metricbeat/module/mongodb. func AssetMongodb() string { - return "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" + return "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" } diff --git a/metricbeat/module/mongodb/metrics/_meta/fields.yml b/metricbeat/module/mongodb/metrics/_meta/fields.yml index 973153e2b9b..a3b89c0c1ec 100644 --- a/metricbeat/module/mongodb/metrics/_meta/fields.yml +++ b/metricbeat/module/mongodb/metrics/_meta/fields.yml @@ -384,7 +384,7 @@ - name: shutting_down type: boolean - name: network_interface - type: text + type: keyword - name: apply type: group description: > diff --git a/metricbeat/module/mongodb/status/_meta/data.json b/metricbeat/module/mongodb/status/_meta/data.json index 7fd0937fc7d..0ec8996ddaf 100644 --- a/metricbeat/module/mongodb/status/_meta/data.json +++ b/metricbeat/module/mongodb/status/_meta/data.json @@ -1,209 +1,209 @@ { - "@timestamp": "2017-10-12T08:05:34.853Z", - "beat": { - "hostname": "host.example.com", - "name": "host.example.com" - }, - "metricset": { - "host": "mongodb:27017", - "module": "mongodb", - "name": "status", - "rtt": 115 - }, - "mongodb": { - "status": { - "version": "3.4.7", - "ops": { - "latencies": { - "writes": { - "latency": 0, - "count": 0 - }, - "commands": { - "latency": 439756604, - "count": 7607434 - }, - "reads": { - "count": 19245, - "latency": 5504505 - } - }, - "counters": { - "update": 0, - "delete": 0, - "getmore": 14, - "command": 7607435, - "insert": 0, - "query": 19200 - }, - "replicated": { - "query": 0, - "update": 0, - "delete": 0, - "getmore": 0, - "command": 0, - "insert": 0 - } - }, - "uptime": { - "ms": 4909716080 - }, - "asserts": { - "warning": 0, - "msg": 0, - "user": 32, - "rollovers": 0, - "regular": 0 - }, - "local_time": "2018-07-17T18:31:45.823Z", - "global_lock": { - "total_time": { - "us": 4909712143000 + "@timestamp": "2017-10-12T08:05:34.853Z", + "agent": { + "hostname": "host.example.com", + "name": "host.example.com" + }, + "event": { + "dataset": "mongodb.status", + "duration": 115000, + "module": "mongodb" + }, + "metricset": { + "name": "status" + }, + "mongodb": { + "status": { + "asserts": { + "msg": 0, + "regular": 0, + "rollovers": 0, + "user": 0, + "warning": 0 + }, + "connections": { + "available": 838859, + "current": 1, + "total_created": 69 + }, + "extra_info": { + "heap_usage": {}, + "page_faults": 0 + }, + "global_lock": { + "active_clients": { + "readers": 0, + "total": 7, + "writers": 0 }, - "current_queue": { - "total": 0, - "readers": 0, - "writers": 0 - }, - "active_clients": { - "readers": 0, - "writers": 0, - "total": 19 - } - }, - "network": { - "out": { - "bytes": 5137630772 - }, - "requests": 15253359, - "in": { - "bytes": 973355182 - } - }, - "locks": { - "oplog": { - "acquire": { - "count": { - "r": 4909608 - } - }, - "wait": {}, - "deadlock": {} - }, - "global": { - "wait": {}, - "deadlock": {}, - "acquire": { - "count": { - "w": 22, - "W": 2, - "r": 17350359 - } - } - }, - "database": { - "wait": {}, - "deadlock": {}, - "acquire": { - "count": { - "R": 21447, - "W": 22, - "r": 11087051 - } - } - }, - "collection": { - "wait": {}, - "deadlock": {}, - "acquire": { - "count": { - "r": 6155983 - } - } - }, - "meta_data": { - "acquire": { - "count": { - "w": 1 - } - }, - "wait": {}, - "deadlock": {} - } - }, - "wired_tiger": { - "cache": { - "maximum": { - "bytes": 1534066688 - }, - "used": { - "bytes": 65472 - }, - "dirty": { - "bytes": 0 - }, - "pages": { - "write": 63, - "evicted": 0, - "read": 18 - } - }, - "log": { - "syncs": 38, - "size": { - "bytes": 33554432 - }, - "write": { - "bytes": 15104 - }, - "max_file_size": { - "bytes": 104857600 - }, - "flushes": 49022011, - "writes": 47, - "scans": 5 - }, - "concurrent_transactions": { - "write": { - "available": 128, - "total_tickets": 128, - "out": 0 - }, - "read": { - "available": 128, - "total_tickets": 128, - "out": 0 - } - } - }, - "storage_engine": { - "name": "wiredTiger" - }, - "process": "mongod", - "memory": { - "mapped": { - "mb": 0 - }, - "mapped_with_journal": { - "mb": 0 - }, - "bits": 64, - "resident": { - "mb": 21 - }, - "virtual": { - "mb": 1024 - } - }, - "connections": { - "total_created": 66310, - "current": 11, - "available": 51189 - }, - "extra_info": { - "page_faults": 1197, - "heap_usage": {} - } - } - } -} + "current_queue": { + "readers": 0, + "total": 0, + "writers": 0 + }, + "total_time": { + "us": 68634000 + } + }, + "local_time": "2019-01-29T13:47:39.864Z", + "locks": { + "collection": { + "acquire": { + "count": { + "r": 42, + "w": 2 + } + }, + "deadlock": {}, + "wait": {} + }, + "database": { + "acquire": { + "count": { + "R": 1, + "W": 7, + "r": 42 + } + }, + "deadlock": {}, + "wait": {} + }, + "global": { + "acquire": { + "count": { + "W": 3, + "r": 176, + "w": 7 + } + }, + "deadlock": {}, + "wait": {} + }, + "meta_data": { + "acquire": { + "count": { + "w": 1 + } + }, + "deadlock": {}, + "wait": {} + } + }, + "memory": { + "bits": 64, + "mapped": { + "mb": 0 + }, + "mapped_with_journal": { + "mb": 0 + }, + "resident": { + "mb": 59 + }, + "virtual": { + "mb": 928 + } + }, + "network": { + "in": { + "bytes": 1624 + }, + "out": { + "bytes": 143648 + }, + "requests": 55 + }, + "ops": { + "counters": { + "command": 28, + "delete": 0, + "getmore": 0, + "insert": 0, + "query": 1, + "update": 0 + }, + "latencies": { + "commands": { + "count": 27, + "latency": 2834 + }, + "reads": { + "count": 0, + "latency": 0 + }, + "writes": { + "count": 0, + "latency": 0 + } + }, + "replicated": { + "command": 0, + "delete": 0, + "getmore": 0, + "insert": 0, + "query": 0, + "update": 0 + } + }, + "storage_engine": { + "name": "wiredTiger" + }, + "uptime": { + "ms": 68626 + }, + "wired_tiger": { + "cache": { + "dirty": { + "bytes": 19336 + }, + "maximum": { + "bytes": 2585788416 + }, + "pages": { + "evicted": 0, + "read": 0, + "write": 14 + }, + "used": { + "bytes": 29316 + } + }, + "concurrent_transactions": { + "read": { + "available": 128, + "out": 0, + "total_tickets": 128 + }, + "write": { + "available": 128, + "out": 0, + "total_tickets": 128 + } + }, + "log": { + "flushes": 683, + "max_file_size": { + "bytes": 104857600 + }, + "scans": 0, + "size": { + "bytes": 33554432 + }, + "syncs": 13, + "write": { + "bytes": 16640 + }, + "writes": 42 + } + } + } + }, + "process": { + "name": "mongod" + }, + "service": { + "address": "127.0.0.1:27017", + "type": "mongodb", + "version": "3.4.19" + } +} \ No newline at end of file diff --git a/metricbeat/module/mongodb/status/_meta/fields.yml b/metricbeat/module/mongodb/status/_meta/fields.yml index d3cbb3b88a8..7f6d4380979 100644 --- a/metricbeat/module/mongodb/status/_meta/fields.yml +++ b/metricbeat/module/mongodb/status/_meta/fields.yml @@ -5,11 +5,13 @@ release: ga fields: - name: version - type: keyword + type: alias + path: service.version description: > Instance version. - name: process - type: keyword + type: alias + path: process.name description: > The current MongoDB process. Possible values are mongos or mongod. - name: uptime.ms diff --git a/metricbeat/module/mongodb/status/status.go b/metricbeat/module/mongodb/status/status.go index e7c2bc810a4..e3ade9d45c2 100644 --- a/metricbeat/module/mongodb/status/status.go +++ b/metricbeat/module/mongodb/status/status.go @@ -26,11 +26,6 @@ import ( "gopkg.in/mgo.v2/bson" ) -/* -TODOs: - * add a metricset for "metrics" data -*/ - var debugf = logp.MakeDebug("mongodb.status") func init() { @@ -62,20 +57,34 @@ func New(base mb.BaseMetricSet) (mb.MetricSet, error) { // Fetch methods implements the data gathering and data conversion to the right format // It returns the event which is then forward to the output. In case of an error, a // descriptive error must be returned. -func (m *MetricSet) Fetch() (common.MapStr, error) { +func (m *MetricSet) Fetch(r mb.ReporterV2) { // instantiate direct connections to each of the configured Mongo hosts mongoSession, err := mongodb.NewDirectSession(m.DialInfo) if err != nil { - return nil, err + r.Error(err) + return } defer mongoSession.Close() result := map[string]interface{}{} if err := mongoSession.DB("admin").Run(bson.D{{Name: "serverStatus", Value: 1}}, &result); err != nil { - return nil, err + r.Error(err) + return } - data, _ := schema.Apply(result) - return data, nil + event := mb.Event{ + RootFields: common.MapStr{}, + } + event.MetricSetFields, _ = schema.Apply(result) + + if v, err := event.MetricSetFields.GetValue("version"); err == nil { + event.RootFields.Put("service.version", v) + event.MetricSetFields.Delete("version") + } + if v, err := event.MetricSetFields.GetValue("process"); err == nil { + event.RootFields.Put("process.name", v) + event.MetricSetFields.Delete("process") + } + r.Event(event) } diff --git a/metricbeat/module/mongodb/status/status_integration_test.go b/metricbeat/module/mongodb/status/status_integration_test.go index 4b3969979d9..83ce4046829 100644 --- a/metricbeat/module/mongodb/status/status_integration_test.go +++ b/metricbeat/module/mongodb/status/status_integration_test.go @@ -24,7 +24,6 @@ import ( "github.com/stretchr/testify/assert" - "github.com/elastic/beats/libbeat/common" "github.com/elastic/beats/libbeat/tests/compose" mbtest "github.com/elastic/beats/metricbeat/mb/testing" "github.com/elastic/beats/metricbeat/module/mongodb" @@ -33,33 +32,40 @@ import ( func TestFetch(t *testing.T) { compose.EnsureUp(t, "mongodb") - f := mbtest.NewEventFetcher(t, getConfig()) - event, err := f.Fetch() - if !assert.NoError(t, err) { + f := mbtest.NewReportingMetricSetV2(t, getConfig()) + events, errs := mbtest.ReportingFetchV2(f) + + assert.Empty(t, errs) + if !assert.NotEmpty(t, events) { t.FailNow() } - t.Logf("%s/%s event: %+v", f.Module().Name(), f.Name(), event) + t.Logf("%s/%s event: %+v", f.Module().Name(), f.Name(), + events[0].BeatEvent("mongodb", "status").Fields.StringToPrint()) + + event := events[0].BeatEvent("mongodb", "status").Fields // Check event fields - current := event["connections"].(common.MapStr)["current"].(int64) - assert.True(t, current >= 0) + current, _ := event.GetValue("mongodb.status.connections.current") + assert.True(t, current.(int64) >= 0) - available := event["connections"].(common.MapStr)["available"].(int64) - assert.True(t, available > 0) + available, _ := event.GetValue("mongodb.status.connections.available") + assert.True(t, available.(int64) > 0) - pageFaults := event["extra_info"].(common.MapStr)["page_faults"].(int64) - assert.True(t, pageFaults >= 0) + pageFaults, _ := event.GetValue("mongodb.status.extra_info.page_faults") + assert.True(t, pageFaults.(int64) >= 0) } func TestData(t *testing.T) { compose.EnsureUp(t, "mongodb") - f := mbtest.NewEventFetcher(t, getConfig()) - err := mbtest.WriteEvent(f, t) + config := getConfig() + f := mbtest.NewReportingMetricSetV2(t, config) + err := mbtest.WriteEventsReporterV2(f, t, ".") if err != nil { t.Fatal("write", err) } + } func getConfig() map[string]interface{} { diff --git a/metricbeat/module/munin/_meta/config.reference.yml b/metricbeat/module/munin/_meta/config.reference.yml index cdef11f3670..3fe0cb3271a 100644 --- a/metricbeat/module/munin/_meta/config.reference.yml +++ b/metricbeat/module/munin/_meta/config.reference.yml @@ -3,4 +3,12 @@ enabled: true period: 10s hosts: ["localhost:4949"] - node.namespace: node + + # List of plugins to collect metrics from, by default it collects from + # all the available ones. + #munin.plugins: [] + + # If set to true, it sanitizes fields names in concordance with munin + # implementation (all characters that are not alphanumeric, or underscore + # are replaced by underscores). + #munin.sanitize: false diff --git a/metricbeat/module/munin/_meta/config.yml b/metricbeat/module/munin/_meta/config.yml index c8267c385dd..e14277e55d2 100644 --- a/metricbeat/module/munin/_meta/config.yml +++ b/metricbeat/module/munin/_meta/config.yml @@ -3,4 +3,3 @@ # - node period: 10s hosts: ["localhost:4949"] - node.namespace: node diff --git a/metricbeat/module/munin/_meta/fields.yml b/metricbeat/module/munin/_meta/fields.yml index 83e41a59fa9..1516f1c775f 100644 --- a/metricbeat/module/munin/_meta/fields.yml +++ b/metricbeat/module/munin/_meta/fields.yml @@ -2,10 +2,18 @@ title: "Munin" description: > Munin node metrics exporter - release: beta + release: ga fields: + - name: munin.metrics.* + type: object + object_type: double + object_type_mapping_type: '*' + description: > + Metrics exposed by a plugin of a munin node agent. + - name: munin.plugin.name + type: keyword + description: > + Name of the plugin collecting these metrics. - name: munin type: group - description: > - munin contains metrics exposed by a munin node agent fields: diff --git a/metricbeat/module/munin/fields.go b/metricbeat/module/munin/fields.go index ef7d4e1121e..3d879b85f9e 100644 --- a/metricbeat/module/munin/fields.go +++ b/metricbeat/module/munin/fields.go @@ -32,5 +32,5 @@ func init() { // AssetMunin returns asset data. // This is the base64 encoded gzipped contents of ../metricbeat/module/munin. func AssetMunin() string { - return "eJxsjk2qhDAQhPc5ReHeC2TxbvAOEU2NNKOdkLQw3n4wBmaEqV3/FN834snDY9tV1AEmttJj+D/nwQGRdS6STZJ6/DkAaDdoisRGKzJX8JVTMRYHFK4MlR4TLTjgIVxj9a05QsPGD+2MHZkeS0l77psfyCuthjmpBdF6g1dGTAdC/2lyYaFar39bXCZ3z3cAAAD//xiKUG4=" + return "eJx8kUFuwyAURPc+xSibSJHiA7DoDdorRNhMKA0GBN9qffvKhlaOanX5Z/5oHp8rHlwUpjm40AHixFPh9LrOpw4wLGN2SVwMCi8dAGweQjTERMluLOBXilmYOyDTUxcqWN0Bd0dvitpyVwQ9sXX1LdpfNg+QJVEhDh8cpUl1uFXHxHnw/OvcJp2SC7atnS/ntnNAvtHvkAsNhgUayc/WBcQ7dMWrz9OWQfoD+Lrfr9IT/oPLZ8zmf4I3PXGtknf+FI/Re47igl3V8nvYg+6nPpvjnJqyP3VN7b/iOwAA//84wJVl" } diff --git a/metricbeat/module/munin/munin.go b/metricbeat/module/munin/munin.go index d81a5ab0281..7c2fd40c0d8 100644 --- a/metricbeat/module/munin/munin.go +++ b/metricbeat/module/munin/munin.go @@ -19,23 +19,29 @@ package munin import ( "bufio" - "fmt" "io" "net" + "regexp" "strconv" "strings" "time" - "github.com/joeshaw/multierror" "github.com/pkg/errors" "github.com/elastic/beats/libbeat/common" + "github.com/elastic/beats/libbeat/logp" ) const ( unknownValue = "U" ) +var ( + // Field names must match with this expression + // http://guide.munin-monitoring.org/en/latest/reference/plugin.html#notes-on-fieldnames + nameRegexp = regexp.MustCompile("^[a-zA-Z_][a-zA-Z0-9_]*$") +) + // Node connection type Node struct { conn net.Conn @@ -66,7 +72,7 @@ func (n *Node) Close() error { return n.conn.Close() } -// List of items exposed by the node +// List of plugins exposed by the node func (n *Node) List() ([]string, error) { _, err := io.WriteString(n.writer, "list\n") if err != nil { @@ -79,54 +85,64 @@ func (n *Node) List() ([]string, error) { } // Fetch metrics from munin node -func (n *Node) Fetch(items ...string) (common.MapStr, error) { - var errs multierror.Errors +func (n *Node) Fetch(plugin string, sanitize bool) (common.MapStr, error) { + _, err := io.WriteString(n.writer, "fetch "+plugin+"\n") + if err != nil { + return nil, errors.Wrapf(err, "failed to fetch metrics for plugin '%s'", plugin) + } + event := common.MapStr{} + scanner := bufio.NewScanner(n.reader) + scanner.Split(bufio.ScanWords) + for scanner.Scan() { + name := strings.TrimSpace(scanner.Text()) - for _, item := range items { - _, err := io.WriteString(n.writer, "fetch "+item+"\n") - if err != nil { - errs = append(errs, err) - continue + // Munin delimits metrics with a dot + if name == "." { + break } - scanner := bufio.NewScanner(n.reader) - scanner.Split(bufio.ScanWords) - for scanner.Scan() { - name := strings.TrimSpace(scanner.Text()) - - // Munin delimits metrics with a dot - if name == "." { - break - } - - name = strings.TrimSuffix(name, ".value") - - if !scanner.Scan() { - if scanner.Err() == nil { - errs = append(errs, errors.New("unexpected EOF when expecting value")) - } - break + name = strings.TrimSuffix(name, ".value") + if !scanner.Scan() { + if scanner.Err() == nil { + return nil, errors.New("unexpected EOF when expecting value") } - value := scanner.Text() + } + value := scanner.Text() - key := fmt.Sprintf("%s.%s", item, name) + if strings.Contains(name, ".") { + logp.Debug("munin", "ignoring field name with dot '%s'", name) + continue + } - if value == unknownValue { - errs = append(errs, errors.Errorf("unknown value for %s", key)) - continue - } - if f, err := strconv.ParseFloat(value, 64); err == nil { - event.Put(key, f) - continue - } - event.Put(key, value) + if value == unknownValue { + logp.Debug("munin", "unknown value for '%s'", name) + continue } - if scanner.Err() != nil { - errs = append(errs, scanner.Err()) + if sanitize && !nameRegexp.MatchString(name) { + logp.Debug("munin", "sanitizing name with invalid characters '%s'", name) + name = sanitizeName(name) + } + if f, err := strconv.ParseFloat(value, 64); err == nil { + event[name] = f + continue } } - return event, errs.Err() + if err := scanner.Err(); err != nil { + return nil, err + } + + return event, nil +} + +var ( + invalidCharactersRegexp = regexp.MustCompile("(^[^a-zA-Z_]|[^a-zA-Z_0-9])") +) + +// Mimic munin master implementation +// https://github.com/munin-monitoring/munin/blob/20abb861/lib/Munin/Master/Node.pm#L385 +func sanitizeName(name string) string { + return invalidCharactersRegexp.ReplaceAllString(name, "_") } diff --git a/metricbeat/module/munin/munin_test.go b/metricbeat/module/munin/munin_test.go index 8312858652a..1da93165ddb 100644 --- a/metricbeat/module/munin/munin_test.go +++ b/metricbeat/module/munin/munin_test.go @@ -45,8 +45,8 @@ func TestList(t *testing.T) { assert.ElementsMatch(t, expected, list) } -func TestFetch(t *testing.T) { - response := `user.value 4679836 +const ( + responseCPU = `user.value 4679836 nice.value 59278 system.value 1979168 idle.value 59957502 @@ -57,43 +57,100 @@ steal.value 0 guest.value 0 . ` - n := dummyNode(response) - - event, err := n.Fetch("cpu", "swap") - - assert.Nil(t, err) - - expected := common.MapStr{ - "cpu": common.MapStr{ - "user": float64(4679836), - "nice": float64(59278), - "system": float64(1979168), - "idle": float64(59957502), - "iowait": float64(705373), - "irq": float64(76), - "softirq": float64(36404), - "steal": float64(0), - "guest": float64(0), - }, - } - assert.Equal(t, expected, event) -} - -func TestFetchUnknown(t *testing.T) { - response := `some.value U + responseUnknown = `some.value U other.value 42 . ` - n := dummyNode(response) + responseWithWrongFields = `user.value 4679836 +nice.value 59278 +system.value 1979168 +idle.value 59957502 +user.1000.value 23456 +user.0.value 38284 +. +` +) - event, err := n.Fetch("test") +func TestFetch(t *testing.T) { + cases := []struct { + title string + response string + expected common.MapStr + }{ + { + "normal case", + responseCPU, + common.MapStr{ + "user": float64(4679836), + "nice": float64(59278), + "system": float64(1979168), + "idle": float64(59957502), + "iowait": float64(705373), + "irq": float64(76), + "softirq": float64(36404), + "steal": float64(0), + "guest": float64(0), + }, + }, + { + "unknown values", + responseUnknown, + common.MapStr{ + "other": float64(42), + }, + }, + { + "wrong field names", + responseWithWrongFields, + common.MapStr{ + "user": float64(4679836), + "nice": float64(59278), + "system": float64(1979168), + "idle": float64(59957502), + }, + }, + } - assert.NotNil(t, err) + for _, c := range cases { + t.Run(c.title, func(t *testing.T) { + n := dummyNode(c.response) + event, err := n.Fetch("cpu", true) + assert.Equal(t, c.expected, event) + assert.NoError(t, err) + }) + } +} - expected := common.MapStr{ - "test": common.MapStr{ - "other": float64(42), +func TestSanitizeName(t *testing.T) { + cases := []struct { + name string + expected string + }{ + { + "if_eth0", + "if_eth0", + }, + { + "/dev/sda1", + "_dev_sda1", + }, + { + "eth0:100", + "eth0_100", + }, + { + "user@host", + "user_host", }, + { + "404", + "_04", + }, + } + + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + assert.Equal(t, c.expected, sanitizeName(c.name)) + }) } - assert.Equal(t, expected, event) } diff --git a/metricbeat/module/munin/node/_meta/data.json b/metricbeat/module/munin/node/_meta/data.json new file mode 100644 index 00000000000..790c54a5f3b --- /dev/null +++ b/metricbeat/module/munin/node/_meta/data.json @@ -0,0 +1,35 @@ +{ + "@timestamp": "2017-10-12T08:05:34.853Z", + "agent": { + "hostname": "host.example.com", + "name": "host.example.com" + }, + "event": { + "dataset": "munin.node", + "duration": 115000, + "module": "munin" + }, + "metricset": { + "name": "node" + }, + "munin": { + "metrics": { + "guest": 0, + "idle": 6999219, + "iowait": 5441, + "irq": 0, + "nice": 0, + "softirq": 6419, + "steal": 0, + "system": 374903, + "user": 486780 + }, + "plugin": { + "name": "cpu" + } + }, + "service": { + "address": "127.0.0.1:4949", + "type": "cpu" + } +} \ No newline at end of file diff --git a/metricbeat/module/munin/node/_meta/docs.asciidoc b/metricbeat/module/munin/node/_meta/docs.asciidoc index 403f5fa93b9..3a5ead627e1 100644 --- a/metricbeat/module/munin/node/_meta/docs.asciidoc +++ b/metricbeat/module/munin/node/_meta/docs.asciidoc @@ -11,31 +11,40 @@ and sends them as events to Elastic. - module: munin metricsets: ["node"] hosts: ["localhost:4949"] - node.namespace: node + munin.plugins: ["cpu", "swap"] --- -All metrics exposed by a single munin node will be sent in a single event, -grouped by munin items, e.g: +Metrics exposed by a single munin node will be sent in an event per plugin. + +For example with the previous configuration two events are sent like the +following ones. [source,json] --- "munin": { - "node": { - "swap": { - "swap_in": 198609, - "swap_out": 612629 - }, - "cpu": { - "softirq": 680, - "guest": 0, - "user": 158212, - "iowait": 71095, - "irq": 1, - "system": 35906, - "idle": 1185709, - "steal": 0, - "nice": 1633 - } + "plugin": { + "name": "swap" + }, + "metrics": { + "swap_in": 198609, + "swap_out": 612629 + } +} + +"munin": { + "plugin": { + "name": "cpu" + } + "metrics": { + "softirq": 680, + "guest": 0, + "user": 158212, + "iowait": 71095, + "irq": 1, + "system": 35906, + "idle": 1185709, + "steal": 0, + "nice": 1633 } } --- diff --git a/metricbeat/module/munin/node/_meta/fields.yml b/metricbeat/module/munin/node/_meta/fields.yml index 8033a27f5ac..a927f3fc9f8 100644 --- a/metricbeat/module/munin/node/_meta/fields.yml +++ b/metricbeat/module/munin/node/_meta/fields.yml @@ -1 +1 @@ -- release: beta +- release: ga diff --git a/metricbeat/module/munin/node/config.go b/metricbeat/module/munin/node/config.go new file mode 100644 index 00000000000..9c649de9041 --- /dev/null +++ b/metricbeat/module/munin/node/config.go @@ -0,0 +1,28 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package node + +// Config is the configuration for munin +type Config struct { + Plugins []string `config:"munin.plugins"` + Sanitize bool `config:"munin.sanitize"` +} + +var defaultConfig = Config{ + Sanitize: false, +} diff --git a/metricbeat/module/munin/node/node.go b/metricbeat/module/munin/node/node.go index 9fe283db0f0..7bc9cde2342 100644 --- a/metricbeat/module/munin/node/node.go +++ b/metricbeat/module/munin/node/node.go @@ -21,7 +21,7 @@ import ( "time" "github.com/elastic/beats/libbeat/common" - "github.com/elastic/beats/libbeat/common/cfgwarn" + "github.com/elastic/beats/libbeat/logp" "github.com/elastic/beats/metricbeat/mb" "github.com/elastic/beats/metricbeat/module/munin" ) @@ -42,53 +42,71 @@ func init() { // interface methods except for Fetch. type MetricSet struct { mb.BaseMetricSet - namespace string - timeout time.Duration + serviceType string + plugins []string + sanitize bool + timeout time.Duration } // New creates a new instance of the MetricSet. New is responsible for unpacking // any MetricSet specific configuration options if there are any. func New(base mb.BaseMetricSet) (mb.MetricSet, error) { - cfgwarn.Beta("The munin node metricset is beta.") - config := struct { - Namespace string `config:"node.namespace" validate:"required"` - }{} + config := defaultConfig if err := base.Module().UnpackConfig(&config); err != nil { return nil, err } return &MetricSet{ BaseMetricSet: base, - namespace: config.Namespace, + plugins: config.Plugins, + sanitize: config.Sanitize, timeout: base.Module().Config().Timeout, }, nil } // Fetch method implements the data gathering -func (m *MetricSet) Fetch() (common.MapStr, error) { +func (m *MetricSet) Fetch(r mb.ReporterV2) { node, err := munin.Connect(m.Host(), m.timeout) if err != nil { - return nil, err + r.Error(err) + return } defer node.Close() - items, err := node.List() - if err != nil { - return nil, err + plugins := m.plugins + if len(plugins) == 0 { + plugins, err = node.List() + if err != nil { + r.Error(err) + return + } } - event, err := node.Fetch(items...) - if err != nil { - return nil, err - } + for _, plugin := range plugins { + metrics, err := node.Fetch(plugin, m.sanitize) + if err != nil { + r.Error(err) + } - // Set dynamic namespace. - _, err = event.Put(mb.NamespaceKey, m.namespace) - if err != nil { - return nil, err + // Even if there was some error, keep sending succesfully collected metrics if any + if len(metrics) == 0 { + continue + } + event := mb.Event{ + Service: plugin, + RootFields: common.MapStr{ + "munin": common.MapStr{ + "plugin": common.MapStr{ + "name": plugin, + }, + "metrics": metrics, + }, + }, + } + if !r.Event(event) { + logp.Debug("munin", "Failed to report event, interrupting Fetch") + return + } } - - return event, nil - } diff --git a/metricbeat/module/munin/node/node_integration_test.go b/metricbeat/module/munin/node/node_integration_test.go new file mode 100644 index 00000000000..1e8fbbe2fd6 --- /dev/null +++ b/metricbeat/module/munin/node/node_integration_test.go @@ -0,0 +1,88 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +// +build integration + +package node + +import ( + "os" + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/elastic/beats/libbeat/tests/compose" + mbtest "github.com/elastic/beats/metricbeat/mb/testing" +) + +func TestFetch(t *testing.T) { + compose.EnsureUp(t, "munin") + + f := mbtest.NewReportingMetricSetV2(t, getConfig()) + events, errs := mbtest.ReportingFetchV2(f) + + assert.Empty(t, errs) + if !assert.NotEmpty(t, events) { + t.FailNow() + } + + t.Logf("%s/%s event: %+v", f.Module().Name(), f.Name(), + events[0].BeatEvent("munin", "node").Fields.StringToPrint()) +} + +func TestData(t *testing.T) { + compose.EnsureUp(t, "munin") + + config := getConfig() + f := mbtest.NewReportingMetricSetV2(t, config) + err := mbtest.WriteEventsReporterV2(f, t, ".") + if err != nil { + t.Fatal("write", err) + } +} + +func getConfig() map[string]interface{} { + return map[string]interface{}{ + "module": "munin", + "metricsets": []string{"node"}, + "hosts": []string{GetEnvHost() + ":" + GetEnvPort()}, + } +} + +// GetEnvHost returns the hostname of the Mongodb server to use for testing. +// It reads the value from the MONGODB_HOST environment variable and returns +// 127.0.0.1 if it is not set. +func GetEnvHost() string { + host := os.Getenv("MUNIN_HOST") + + if len(host) == 0 { + host = "127.0.0.1" + } + return host +} + +// GetEnvPort returns the port of the Mongodb server to use for testing. +// It reads the value from the MONGODB_PORT environment variable and returns +// 27017 if it is not set. +func GetEnvPort() string { + port := os.Getenv("MUNIN_PORT") + + if len(port) == 0 { + port = "4949" + } + return port +} diff --git a/metricbeat/module/php_fpm/fields.go b/metricbeat/module/php_fpm/fields.go index 8fe2f02141e..42f9698f953 100644 --- a/metricbeat/module/php_fpm/fields.go +++ b/metricbeat/module/php_fpm/fields.go @@ -32,5 +32,5 @@ func init() { // AssetPhpFpm returns asset data. // This is the base64 encoded gzipped contents of ../metricbeat/module/php_fpm. func AssetPhpFpm() string { - return "eJzMWFtv47oRfs+vGOQlNuoY57y6QIHi9GyTh90a2exTUcg0NbKI8KIlR3a8v74YipJlWc5l4xarhwARzeE333xzoW7hCfcLqMoqKypzBUCKNC7genm3zD4tP19fAeQYpFcVKWcX8LcrAIDl3fL20/IzBPRb9BBIUB3AIHklA0inNUrCHArvTPvj+RVAKJ2nTDpbqM0CCqEDXgF41CgCLmAj+DdIpOwmLODf1yHo6xlcl0TV9X+uAAqFOg+LiOEWrDDYx84P7Ss25F1dpTcj8PlZpX0rkM6SUDYAldj5QKUg2KFHcGteHTiTXO6sVWKDIIXW8/Sqj/QIrXO6ezkG9wXIDWzn9Dsx829a3JV3EkOIOOY9y0O8fcz892ihxf2E+53z+WDtBfT8PJYYLYIrIrJjJL8YT0c2h5wd6/YtTCarmRFWbNBfjtSvJEjJGeR7K4yS4Dw4m6MRNp+PIpHOWpRsLoyiGFL9Bgx/dCZjdiCECqUqlIz/qkBKhvlg1xhZB5BCSqwIh1y0GLWzm5OlV0Am/dVmjZ4VqKx0RtkNePxeY6Ckjr4UUpErRegA/XXE7q5EC6JHLKjDBlDE/3o0btuXm1aB0ML3GmscctOyEBcvz4GsvUdLPS56mmhYKMUWYY1oQVlFShDmM1jXBNbRiNU9UufwHO45vVWArdA1svPW2dsf6B1zQftKcbncg0HRHie0HrHKNImtUFqsNbbBSImEAYTvXNF7WNdhPwNhc97mMa5aN2K1Z6CzTa4xH4+0+EytKObwIFRQI0xzoTFzI54zWSqde7QrmFTebVWOEUMLWAoLpbC5RlA05UZY6xxK1MMk4+cJsWrIS8HRbjeHR35ReVehpz0UTmu3CwcpFUJS5HHEYCvlRm4BtkqAgODkExJMHv9YcsEolEZYi4D5tKWwDqBsiV7RsErwE9yhistSeCEJfZPn/Loxf5LyrayZtAZPFhV+cYEb8axMbXoC73Jc2Qg6nssLFdp8PLz9nAjKSgTmkWtBIOFZ6Gfc67uWabQXdy+oHx37KZJDf/rox9tAlwaXagLLNF9cqAOoXF9eGAdBNMnZ6WG1E4pnT64EiZlV8mSi5jg/U/cO5Yft8X4RB8JpytmmAqaUL4TWsEbacVml8tS7VFKUzUIlPGYJ4yqmZFttBkvxhNC0oHGb7dyXJg+ux6s0LKzOSVhIUtv/B/1tK0ogDwzCbTO4KRszmUH/vhoxOwkOcIsWHLfgoubGwtJJJ8yiFY+h1gQ7FQPAsQSPIofVb6vpOQrIkThtShccPxjkXxLTh2x8qWb+j6JyWi2HoN5X//o9MfMoZHnhGeZLB5SUwdDEuJW5VkZRhBnHl3T+7NzYVhkgrzDEEYD9AuN4rkj4YbJz/imAs5ob73Bu56cycJPy6SZm6k07f99Mx0tv0G6XtR1ptPyO8PKWu9UxLyDatgf4jLKOkymvRHbwWSLmI5FZpU0Zw9Ruk/EWV9PqjDNMWhYFciFXvvSqhHQ2f5P8juEw5FE0uaDhQuG8EbQArJwss+bI93PPhpvRiQk+C3XQfT9yz20F/9GL7s/cazs31DFVjRPKEg7vuC9SyAQu7//RfRc4IacX3UH8zt+cXz2xae/HZ8LkPtc4g4faWmU3M0CS03EgYzI7I7JXJfYq1mN59QH3ZHYW52l2fiBQ9kx6vh3UaOm7CKLeVX4AhqeBF7Bkee0Fn3ApTK09HjCNkt61ZE1+B6O0TksikTjtblNpIGor98Tswe0s5FjEm7izY3JsvTBIpRtLyZ9KkBZDYxUm//zzcQbLf319bNICJuOYXwJYezWCjvCZfg7at4d72Ckq20ud30Mgz2PkO8BxBUVLfFfbUHkpBSSr0Fht49vFNU4UEToz+i4y6zDA8oEYsy2YLO+W2d+/Pd5l377++cBYPNyCKuKkHJCmMCmcfyu85sSLhNkIZZO9+I0d8w9i0yJQO3plsqpHUJ4MLG9A+QzCuNoSR9mgcX7ffOMT4ZDJ0tlQG/ag+SIo9E7sA/zGvvTLVfxeRlwc7uM9JnapNUpRB2yNS6FlrUX7rTF3FrsrYHdg707FJZDQG2UFYf4qMc0pv1oi/DcAAP//XeSCQA==" + return "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" } diff --git a/metricbeat/module/php_fpm/pool/_meta/data.json b/metricbeat/module/php_fpm/pool/_meta/data.json index 6845096a33c..0fa62df1869 100644 --- a/metricbeat/module/php_fpm/pool/_meta/data.json +++ b/metricbeat/module/php_fpm/pool/_meta/data.json @@ -1,19 +1,21 @@ { "@timestamp": "2017-10-12T08:05:34.853Z", - "beat": { + "agent": { "hostname": "host.example.com", "name": "host.example.com" }, + "event": { + "dataset": "php_fpm.pool", + "duration": 115000, + "module": "php_fpm" + }, "metricset": { - "host": "phpfpm:81", - "module": "php_fpm", - "name": "pool", - "rtt": 115 + "name": "pool" }, "php_fpm": { "pool": { "connections": { - "accepted": 1, + "accepted": 10, "listen_queue_len": 0, "max_listen_queue": 0, "queued": 0 @@ -28,8 +30,12 @@ "total": 2 }, "slow_requests": 0, - "start_since": 670, - "start_time": 1512631228 + "start_since": 600, + "start_time": 1548749474 } + }, + "service": { + "address": "127.0.0.1:81", + "type": "php_fpm" } -} +} \ No newline at end of file diff --git a/metricbeat/module/php_fpm/pool/pool_integration_test.go b/metricbeat/module/php_fpm/pool/pool_integration_test.go index 24b4ae4c585..0fc2e43e10d 100644 --- a/metricbeat/module/php_fpm/pool/pool_integration_test.go +++ b/metricbeat/module/php_fpm/pool/pool_integration_test.go @@ -23,10 +23,27 @@ import ( "os" "testing" + "github.com/stretchr/testify/assert" + "github.com/elastic/beats/libbeat/tests/compose" mbtest "github.com/elastic/beats/metricbeat/mb/testing" ) +func TestFetch(t *testing.T) { + compose.EnsureUp(t, "phpfpm") + + f := mbtest.NewReportingMetricSetV2(t, getConfig()) + events, errs := mbtest.ReportingFetchV2(f) + + assert.Empty(t, errs) + if !assert.NotEmpty(t, events) { + t.FailNow() + } + + t.Logf("%s/%s event: %+v", f.Module().Name(), f.Name(), + events[0].BeatEvent("haproxy", "info").Fields.StringToPrint()) + +} func TestData(t *testing.T) { compose.EnsureUp(t, "phpfpm") f := mbtest.NewReportingMetricSetV2(t, getConfig()) diff --git a/metricbeat/module/php_fpm/process/_meta/data.json b/metricbeat/module/php_fpm/process/_meta/data.json index 8de0329a832..612fa24f87f 100644 --- a/metricbeat/module/php_fpm/process/_meta/data.json +++ b/metricbeat/module/php_fpm/process/_meta/data.json @@ -1,9 +1,25 @@ { - "@timestamp": "2018-09-25T16:47:04.998Z", + "@timestamp": "2017-10-12T08:05:34.853Z", + "agent": { + "hostname": "host.example.com", + "name": "host.example.com" + }, + "event": { + "dataset": "php_fpm.process", + "duration": 115000, + "module": "php_fpm" + }, + "http": { + "request": { + "method": "get" + }, + "response": { + "body": { + "bytes": 0 + } + } + }, "metricset": { - "module": "php_fpm", - "host": "phpfpm:81", - "rtt": 24681267, "name": "process" }, "php_fpm": { @@ -13,25 +29,25 @@ "process": { "last_request_cpu": 0, "last_request_memory": 2097152, - "request_duration": 1404, + "request_duration": 204, + "requests": 6, "script": "-", - "pid": 6, - "start_time": 1537974596, - "user": "-", - "state": "Idle", - "start_since": 3946, - "requests": 28, - "request_method": "GET", - "request_uri": "/status?json=", - "content_length": 0 + "start_since": 128, + "start_time": 1548769887, + "state": "Idle" } }, - "beat": { - "name": "host.example.com", - "hostname": "host.example.com", - "version": "7.0.0-alpha1" + "process": { + "pid": 17 + }, + "service": { + "address": "127.0.0.1:81", + "type": "php_fpm" + }, + "url": { + "original": "/status?full=\u0026json=" }, - "host": { - "name": "DESKTOP-RFOOE09" + "user": { + "name": "-" } -} +} \ No newline at end of file diff --git a/metricbeat/module/php_fpm/process/_meta/fields.yml b/metricbeat/module/php_fpm/process/_meta/fields.yml index 284b96fd148..aa3fe71dca9 100644 --- a/metricbeat/module/php_fpm/process/_meta/fields.yml +++ b/metricbeat/module/php_fpm/process/_meta/fields.yml @@ -5,7 +5,9 @@ release: ga fields: - name: pid - type: integer + type: alias + path: process.pid + migration: true description: > The PID of the process - name: state @@ -30,23 +32,31 @@ description: > The duration in microseconds (1 million in a second) of the current request (my own definition) - name: request_method - type: keyword + type: alias + path: http.request.method + migration: true description: > The request method (GET, POST, etc) (of the current request) - name: request_uri - type: text + type: alias + path: url.original + migration: true description: > The request URI with the query string (of the current request) - name: content_length - type: integer + type: alias + path: http.response.body.bytes + migration: true description: > The content length of the request (only with POST) (of the current request) - name: user - type: keyword + type: alias + path: user.name + migration: true description: > The user (PHP_AUTH_USER) (or - if not set) (for the current request) - name: script - type: text + type: keyword description: > The main script called (or - if not set) (for the current request) - name: last_request_cpu diff --git a/metricbeat/module/php_fpm/process/data.go b/metricbeat/module/php_fpm/process/data.go index 4082180b710..bb8e6c5cacb 100644 --- a/metricbeat/module/php_fpm/process/data.go +++ b/metricbeat/module/php_fpm/process/data.go @@ -19,6 +19,7 @@ package process import ( "encoding/json" + "strings" "github.com/elastic/beats/metricbeat/mb" @@ -55,22 +56,40 @@ func eventsMapping(r mb.ReporterV2, content []byte) { } //remapping process details to match the naming format for _, process := range status.Processes { - proc := common.MapStr{ - "pid": process.PID, - "state": process.State, - "start_time": process.StartTime, - "start_since": process.StartSince, - "requests": process.Requests, - "request_duration": process.RequestDuration, - "request_method": process.RequestMethod, - "request_uri": process.RequestURI, - "content_length": process.ContentLength, - "user": process.User, - "script": process.Script, - "last_request_cpu": process.LastRequestCPU, - "last_request_memory": process.LastRequestMemory, + event := mb.Event{ + RootFields: common.MapStr{ + "http": common.MapStr{ + "request": common.MapStr{ + "method": strings.ToLower(process.RequestMethod), + }, + "response": common.MapStr{ + "body": common.MapStr{ + "bytes": process.ContentLength, + }, + }, + }, + "user": common.MapStr{ + "name": process.User, + }, + "process": common.MapStr{ + "pid": process.PID, + }, + "url": common.MapStr{ + "original": process.RequestURI, + }, + }, + MetricSetFields: common.MapStr{ + "state": process.State, + "start_time": process.StartTime, + "start_since": process.StartSince, + "requests": process.Requests, + "request_duration": process.RequestDuration, + "script": process.Script, + "last_request_cpu": process.LastRequestCPU, + "last_request_memory": process.LastRequestMemory, + }, } - event := mb.Event{MetricSetFields: proc} + event.ModuleFields = common.MapStr{} event.ModuleFields.Put("pool.name", status.Name) r.Event(event) diff --git a/metricbeat/module/php_fpm/process/process_integration_test.go b/metricbeat/module/php_fpm/process/process_integration_test.go index 182f4815de4..6894b2c0e96 100644 --- a/metricbeat/module/php_fpm/process/process_integration_test.go +++ b/metricbeat/module/php_fpm/process/process_integration_test.go @@ -23,14 +23,32 @@ import ( "os" "testing" + "github.com/stretchr/testify/assert" + "github.com/elastic/beats/libbeat/tests/compose" mbtest "github.com/elastic/beats/metricbeat/mb/testing" ) +func TestFetch(t *testing.T) { + compose.EnsureUp(t, "phpfpm") + + f := mbtest.NewReportingMetricSetV2(t, getConfig()) + events, errs := mbtest.ReportingFetchV2(f) + + assert.Empty(t, errs) + if !assert.NotEmpty(t, events) { + t.FailNow() + } + + t.Logf("%s/%s event: %+v", f.Module().Name(), f.Name(), + events[0].BeatEvent("php_fpm", "process").Fields.StringToPrint()) + +} + func TestData(t *testing.T) { compose.EnsureUp(t, "phpfpm") f := mbtest.NewReportingMetricSetV2(t, getConfig()) - err := mbtest.WriteEventsReporterV2(f, t, "") + err := mbtest.WriteEventsReporterV2(f, t, ".") if err != nil { t.Fatal("write", err) } diff --git a/metricbeat/module/system/fields.go b/metricbeat/module/system/fields.go index f4dca16bc38..29264b26883 100644 --- a/metricbeat/module/system/fields.go +++ b/metricbeat/module/system/fields.go @@ -32,5 +32,5 @@ func init() { // AssetSystem returns asset data. // This is the base64 encoded gzipped contents of ../metricbeat/module/system. func AssetSystem() string { - return "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" + return "eJzsXd2PGzeSf/dfUZjDIuO7Gdnj3WRz83CAE2+AAZJ44LGxCxwOMtVdkrjDJjskW7Ly1x/40d9sqVtqfTiIHpyM1E3+qlgsVhWLxVt4xs09qI3SmLwA0FQzvIerJ/vF1QuAGFUkaaqp4PfwPy8AANyPoDTRmYIEtaSRugFGnxF+fPwEhMeQYCLkBjJFFngDekk0EIkQCcYw0hjDXIoE9BJBpCiJpnzhUUxeAKilkHoaCT6ni3vQMsMXABIZEoX3sCAvAOYUWazuLaBb4CTBChnmozepeVaKLPXfBEgxn8/utc8QCa4J5QqYiAjzreX0Tfzz1X6rfUdCYvFlqPctCCoobk07FSiGnx4BzIUEAoryBUPbH4g5EEgypql9r8LB/FNnWv5pElElhMa1r3NSmOCLxg9bqDEfA/1Hg4pnyQxliar25H/AI8oIuSYLVEFAmUI5SSMdhKUiwjCezpkgzQfmQiZE30Pq2h8G/uMS8xfJwjLakKNpgqBS5Boot8BApSTCDtpqFGgaPatxWGvAkURkXB8IzMvLJTL3GSVHNoSKERm8k8MD0HEa4eVxWHBgYn2bSiok1RtIpYhQKVR9qDkZp/dFSWN2gTy3qHoAP50g9wAk1oTqC+QlBwMMrgWHmKrnl/3oOKWOGIZP/nZ5TFYoVzQyppkx6ZaEx8z8sSQyXhtrjnKNUmap3jkf5W+nY/1oqJWY669pXAze/Sg899jsgVwjYZc3MpQD5SvBMq6J3DgVMNtYP2dFpc4Is2+sl5Sh/Xa5SQ1LlJCtztZE1fgl9BJlvgQKOWm98HZFKCMzhiA425jF8xOnX3ox8pR68XIZVPhyaXaQKxelWcubNFQZj1kd5p0ZN2/MgXK+WT5QtnVIJSpvfdkREEpP3MOC33Izfxj9HZtuIlRmhoI1ZQyWZIXGQSVfaJIlsCIss5Pm893r13+B/3TdfbZttxor+6m1S5hEEm9Ak2cjH1T5VinXAkgUWbFzumXVbjSAxUD5Q7um8J63QwTqptXsRmQQEe4GrcryInizkEg0SvMFd3yDn4QE/EKSlOEN0Dn8tdWsEynzOtHw3eu/GGg3Rq6ccPmwxyRKs0nOzc9OemYId993Ds4fy4X9YzmJX6/79Ufxdr4iq/VPuzxA4Z/W7TjWrRb6QhlpbEFU4Mi2K+pDzNAKzsP7fxot1GWU/FpaRr3sE2NJXSQLhoapL5aQoQv9ZRJy0Gp/mST1X/IvFP8e6/5lUjL64v9VkbmvBXCZRH6tZsClcbOPFXCTB0IUxjmTy5iNda4DtDcsho+t6N7XsjN9yXu6X8cu6AVuJl70Jty5t0L2XxHPjXzfRe7PvYcqT4ycUvGiyYoh2w+micr+g/kTHt4XaWQ9c/Dyz/A9CvNvcDyfcbMWsrlx4OPH96Bicjd8uC15pstdwoaSEjZ1i+cAeD0hfKN8D3m6G3xcUgUJ2QAXGmZohGNFY7eME8ZKprfa9DH6HQRJJPHEbniMOHmspVSxMEwnRmTMCBmRUVlkJHyeMbbZgW8tqcajA7S97InQcnC20f131HJTMPTSHuBtMxZGHTa85/Az5dkXt8VFm11Bww5UGGkhfUt2sydl1EsaB6JUlhjO2KdA0d+tHfrt3ZteI3h+BhkcGvk4PMob68mmVqu72WbFyqw7RxT7hDLjE0SCx8ovb16t2Bnba2DPBtHN2Z3G4rEBhjHGwqyDD6/e7wZovLeJHW2Jv2Wo9CRBuUA1TVFOFUZB7CEPcwf45la9nea+SwW2T7tLDo4St2O7RonwW4YZxqCFnQwxruhO38aT5UTktHTZPo9NWG28TjpQJXqqVAt9hc49Bui0IzMuJXZEPAFbVpsRyPihXG8L27eFuWm7h5e0nQQR41+MSwhZoSQLrPo0cyEbUhYcES2MBWocFoyHzP8TjooTsWMOiyPpdOPSmDQjDUw+48lqMTU2ynFIsdbPNeW52fTSDJTB3VMH9KPFavEjU2L7AIZ8oZdHIeKUE31cUXIBDJx22lmHi5HrwRFixKlqcL20RD28ej/ueMwytRmPmsdw7D7OpDET10saLeskdC+L1zPC4zWN9RIyTRn9nZhuLRPKp15O4J17XBGdSfeIiKLMuC4ua65MelQQMaHs0NfzGHOWzCnD2qlI2CuiVDbTymstfxojvZXkUbIT+Zs2eGQj/mXXhpuEQ8ZTSVeUobFB7b4F5a6bSRC6G77pwODXoABXNd3yHj6/inH1yvx69zmIyPR7BCimjSYU/KL/FgZhg7HTVNCOKNDeWGzDZg7atlu8CaOx0npEl9K0D1zEqIy0mFltv2lHVSuQJJ5V2rdLtUE3HZtrFX5JxH2YZvl+0phUhXfbOZYpPG1Q0XQ4EN75N8EboLftYReiqMwCc9A65kXKtVRZyiqLWL5LQhYLiQtSbJMQxpzKaRx8KF89+GTHvoHyX+vqx6OBuciaXlNt+hwwrT8G1F6HvLmuAub9NqkPj2ybcFR2fEqqIRaRanmKAa7Ddg28lRW70LdwhuxK8Ey0GrAxB5oAzWQ5G0A7U3cADKnj0yF0au/aAk1ZpixPX7aNYSZIfIj6MMa/aSP3bg6c8Fd3V0OVsPmJ8sV0ToxTfm+M/mGK+OcK/MLxYERpSCjPNIbn8NW3l4T0W4+1Q+Fc3V0U2rsA3DBum552LpkIyALEtNiv7pd21ibnXEMRFpgxKDqbdHVI1YE02YfCFB33QGlLO7uKUQeZd66JVojC16IaITxxMrfDrmsO9+7aQqdzNz6ZJbYXrBN6tc4/K7O+rEXlx7zwhVw2jgubxQKVTcqhPGJZXDwcCe4yAGab3JyMSLREBYS37a9ZNp+jVHCtsPBVPWtIpDPCJg0z5OLdsV4D62jbz15vI3lrWyurxWFscwoN53ZZ8Vut5eCMgBNYpJ6gCj8rMvigQaJXhsrFfKkRIuQRwgz1Gv2paC/Sdse7GqvxIxQ8MG8+zSchxhR5rHLN+/7JxckSIRFi1IQydQOpVYMQLTF6Lnzkigx/7hAJOL8P5dkdnvIP2kbICYsyZh35GTHDUuFFkUJEtZ39VGmV7xFV2gx2bT2NUj/k+sA2+v7pX6ZJqoCAypKmVsoHlnISaboqx/U9h39SHou1uvHv42/t2eZZK4qx8q/3HasOnQN99A7s1D09R66tg0hr6uzK6FyTprrpVkSpxDn9cg9X/2vJ+r+mzVOPb5jFwrZS2hLGfKBK00gZw8HKk9/eMThqBS9zEQtFMHeHI87sTJfE9BWlc+laa40Mw3suNVXo5T3Ye3kzNevH+JyKZbbAtHVG+QyT1QABi+Ts8zSYht4iLTwgZby3QpDfOUmFYJc+b3+pWHuUA2HGTTNDVJIzxtzYQcBBc6KeFVCTq87kCxhJD40jOqWpuLcQGUNWrvYPTY9DSI4CZpm2Xl1IngZSpjJpzLvzEiZWKCORJHTw1IhxTjKmQ7suvWk4YH6/c927HLK5kEHwxWEh1Gshn1804Q2J+/g2KoEf/0312FOtenb+uz2qNm/sbZzuxBPq5cBQoA2xNcH3Of4kMn3SXenmGQ7j8+7K3+JnRSgxQro7d84wMiXRM+rjpET4tnsy7HhIZIGkJ2Mon6CUQh6HLa5pfzjTIaJ80WOsToVJIY93I6J8EkuRpq1VcxxElEcisUlRfuzKhErfbQ+OHROgyPRCbAdYDdVSBYStyaa9Er02xvs7ItfGguQx/PD0DmYYkUyhD50YW0BiKqQud0e6D7rmDPBHeA9aj3wblfXIf2MWIxITTW6qFzvcVG/MaFw3AeOuR4RR0uRlSvSyoHsSeDWhC5eHWlzF0e7RFrIYsAT2iVB7ntmmG/l9VzLjnPLFVXgPOO24xGI3+e03+1CfHtDhnj0u9u+x/WqfHqMkZpSPPMbzjDEwRi7h8a1p3nmFWphRl9rZ7A73jd+uMZNXB+LfRC6yxAbWFaZEEj/rg5krdMGFxCmZiRXew5vXf/s+SHKmUO4xlVzVtf3mUbTed1iNSUj5YhpTaY9sNKPWfXpHvgr2Lmb/xpan7b6cHigByFdUCm5GDlZEUuOSqm4pcIWIjQoNnfgllSIL8JNE/OHp3Y0L8Tsl+/4J/hVWGfWazzBeeOrHx0+3KsWIzmlUjUulZb2IoZGnzqo9sMu76xnuC5TQ0FWNvK2cTxOsq71kl/MjoS1qORuwLrCnKI/QSY/XF1283l0rEs4cbW1UMfGWTDEWltIiMSRLY7taPuiKCaVoQhmRfr8i2O1fTC8FI6sdxFSljGxKG0qLNFfZeRmTdsWKMHM7KnB9VRzGVc0xq7dcNVwrFcxbuTllbozhItUgCW8HyD3RUiTwun3Gp8niLSWz4MR6IVxKqwnYycQx8bp99K3Du4WfRnuEzsaV6OK20TsEncG0ziuh50y0lYVM19iRvB3ck+6ZgeFC70PXo13r3a716kxxyFIC8vJO3seqsntJtoiAVOpsuyQG/QdUNDYy+4QanujvOGlMwwBBIoqylLqtlYSYf9wz1x/e/vJyO6mXp5nHo08tiTyXENq+4xAxmeo8eBX2A/bIsfqJMiyeEdJbSHmcwS1aCr04uVg5VRVTeh4of+ayZsza5a3ssVWGSJEftCg0zpnVeaBs+71XAkYTqidKzAdvNvYVEDHXrpd8S3oH9MKmCDZZ85UqbUeEwwwhWhpjI27aOUQD4Ru7Ku1ixZK0XL2xWGGaPhYrKm0bVthCfDMESfLqqlII3eEehibe3lPyl/yQGfd4VFn4wvVki67Yw+XW5Cbq2WWTJVi/SzX/+LeK02sSy9hny8RYEuUbUkua2j34VoNc8FvDDt+yZaDCWgeWfzWX26qFod5sKxoFO+JKPRgMXpoe3llXxUiSsEf4HDUKiFIiojZItKZ66U7VGzaHLfsH6xPZg/38Gw0kb/XhnQtV+MJWeeu2NUt3fpVNsFUy27LJU2VRSvTyeEwyree5bF6ObNIMXWHja5XNnJfxjXKHId3Z60Ess72dgmntiA5snbMDOBalWckLUNES44yhu62Z2Bp17rgGUc9F6oGfR8E237p3cv0suJaCMa/Z1qKIaBZdSXUDP/70ZBXIh4/hRs3vShMeOzB5hUS2gTmhsmzK65lUCqMvqOCEsXDKqztq4jKXC6cqz1vOh7FIsl0jXSz1BD58rMAItiuRMO+hNUAp1Kpya1fQ/wzao1BWSa4PgGWyz/TPq3gQWNAVcmN7UrEtfadbmcEuhQY95is0JfDhXR6NaUrPVgAd6mIvCOFJYD6P+6iNztZC6mQrkdFcTfyABTN1YJdBMoRU248dC1+4PaGRFHnhQJvjItYgcZExIs2q2NmUY8k3KtcTWlhZlqhEJiNUoJYiY7G1S7BIZRrAk98yocnxWfKxceqmkzFuIhMWzmK3kHI1SapzVGY8n5+Co5+bcE0UxDinzuzr5nJVOLrO4IS4Z121Y/PuLbe5OwuUPlpoN8F9UAaNwismksVTVXidjdbq1+RGY42tk0q0PO8s9tqxm5Np5pnizO8kU7aa8RswQk8Xy86b/ZvslfqC52sxL7v52zFfqdpjoko9kRm3rtYlMMMGtwVfoLLnYDTlmciUn3OdDVPecFHqk9jd/hnmWk82uSObDsax2VQmXnpVY9PrVoQpq3RqE8ZMirqK6VZuZmpbViAj6fbs4DbpeimF1gzjkzPByIrqGtWZO6jmscG1JZIGLhXNP3le7tpt6xrdnifr6CVuPIO+LElmK3rY8ujzrXqpou6MVNdGyMUDqAS7FvZV/02O86MvoUV8Oi8p56q9XVMOnHBRK5PnZ1oxHjsMjNA49fOZSLQlCtzLb/JeUF6fK5DRlH/+tKaLz5mtab8/exqrsXoDUkXQawewjQqo+s875H3QHHcJOuPQWtBSBV8AtwkSiYi7srPD+PL7Dk+G8Npt2L4cAjVFGY6wwPa0ofxTSx86XLIKKlvasyBbcEASLe2jDQnbsnxTtVvEtm7NwjDt6U9H+bCwywz9U4EeSYEOV5QJJhO7g9a5MQx9ZuiuncUBhFeLhfjNvdmmM/x1nZ+P72sWlQQn5MvlEL3EIipYrR4xNuVuv+sSqS5DL26RqVc/MNTm2bLGb+9ePkm0xJfOTQnEq93N/aXlnqm+64Ph3pxQlh0/nlLf6/WeiyVoWdRccNt+140xfQnrVlpt+ZFmuejtsSWYqPWl6YYl5gUookxK5LquKGwRC3uS2xXR83Oos70x51bBrEvVK600G7MYt5lVY8oWQ+JQZl28KsojSV7gmkyzg709TjK2AlLrS1JBzclmB7SzxevWqFtlNVApPV+qveJTRI9mtjxfvt3SZMFO86Wz1eGcuXhlIuYN/mxTEN1Rwn0Ux/Nlmi7NcRvTdjFtT3WUXqSqqOkIs8h8/PGxKL/XrnY+hNBLVQ1VndCkOKAjdgTH9tOelk1fg57wzGryqaUwdnFpb0uj4NZlKo3mQHZvVg23L1zA0pWxmxIuwiUcejNgRFl5ywXfJCJTpQVqfV0QHHzZPYZE6VuJEXLNNrd2tl3//OFTN4MYVbp2EDVJ5wqu1TLB5OXNUGVUY57x0k/MvJ8ow9sZiZ7L5PSSOT9/+FSQuwdVltcnpufRLBC247HHaElREhktaUTY1LFqelmqsRo2LjyxHLa3nopyBBU94XRf987tKOxS68vkVumR9eZbZ5N1fu7Ht7wg6NejSYsSplV1UZt53Q5uc0buxakzqM1uToUVapBHe0hHQtIU48ui+MlfJu6ovXUQwf/HXgvTrYrH1TkpWeDUFh47eZKM0RGkOF1R9061pIsFSozNE9sCYBb6QHn4t5DTr4BuC3QH4XD1i3nqyv2pYGlEiJdnV3wwwBUJZht7hkWLbe6vq7FsS0XYwzUxrZ7u6ClRatoZdjlC4pktVWj+tdlnolLvm7pDee6WqN4pl1U6RNYVazwuISKrOGmHkrLtQG4vUk6xLPqtNzNDJOEqJXbfpaiC+/IGuOiO+45ruEqlpqbni+Har42qe2IOpGBkkF/DUmfWJL0YWp+KbY89Ry/juKKRJrMLWvF/qYRjI8K50O6sQsQITTDuRWlO5Yw905AOH5Au8wMTUbXO559ZMmNnyeyRJOOyCS9FYpu3FTrFY3XNHKV00T537Yi/6HFmhcrd6NvtwHRv1gxiExWnybosGfDw6n1eCVFwm+ZvuO0y5Az5+xNu87CxPFrvc4/dNb2MRpvOgosTlSUJqV8ARTXDe3j09uVT+4GhlRl9E7WawJWD0ajA1xUMX1V19Cujel0LVSkVW8AW/l7wJt7KiRNH2EhIKuW3PcOGYKFx/wu+ewMxjQ5CoRhiegyW5A0PQ6PHLLpaAePaHYTld5HM6Pgj5JodhCRGMj5LTKNdKOBBf6NghXIDGWf0GZk3dah2p9KNW0qkLTZPOSiR+LN0hIGiOvMqlWpIyMY7sWHSMv7MxbrpXB5OXUlY5djIEl0ZVeN0sZh/4202LSmujN6XxiXziNoqWhJ60MWpjfePXl99F69IUlS5c0tdeIRs1I7qzXTk4rZ5u7duSHogial6zjeKRhKY6lUQLjrpOtnS/zGXMttDhQ/V67jCmKyNNi6oim9qG6/iWQoWd3DHI1EbHh1BgXso3iDxaFzoTdoTNKbf9pxVInrGg25L//jjo29Flf05WTnMJHIVaqkI677uSrf+3oNJ1/tdFW6DIOYkoa0yF30RmOcO6dzeSzqh4XpCra+Lqtd3//1m8nryZnJnFPab16/v7l+/++H7+7c//OPd/fff/vW7+/u7YVL2s70f9eERSBxLXyaJFnVICIeHx9XfTGcPj6vviof60JYK2fsy+4K+N2/2gW+62oFJYiI0XgDDP1ggI3PcU3cSlnsC+vPcWFUDls4C2N+/u31zd3d7d/f3279+N+Hrif9lEonWTV07MD9+/AASIyHjQNkn9EDh4TG/MVLMNLEFJlaUgMQVStXetnl4BCbEc6ev32ADahZPU5apqRhUQ728KmZf8m2R7fkcIx/jSW8ZrpBBLGz1xGv8+PO7l7kR4nlhBs1tjguO9rbQZquMzJDVivLf2AZMa/91Z03Pq7kQkxmRk4VghC8mQi4mV4a/V9UvWvG6or63aSNGjTKhPC/ibJqHSCToC6YRDpjMMI4xhkikm8KYIrp1Qtq+sNQ6vX/1Ks1mjEYqm8/pF4ujtyxP7aUf4xmD/zDN+YdmOZnuZHwxJlYCvbiBzzHbgbj7PoPxb0LYCsBXit4TxIAq+ttRjH15wU+Viwug1vRWHPhl33s58AtGmd0JOIQf9uTzYJEIvzW8433vUphnjE0HiELdBu6OLD7Z3yHw+6GBRTF3FUZz+5mW4URfp/sgC7pdTWnvUpRvrRxz7izq5iDsqt1o93DbvmVuWLkiR4HfewSuDTDLw2501bKkSmMgvjkilqILa/yo4LjoaLQSoaZL44HtPzbhklsH1z/cDWwbOOghPNBj0HqChaog7UbdT6COgM9A2yZgTXiozFJA1TJ4++bIAMtYRKXbQdyMmFA4XZPO425HQdtAaJTwtEQyDd5ZVcetaXIZsAsgIdTFGhuPqn0+vbtQ7bMd2DZwcIHaZ8v/FAObNir/DbZPPrsmPtdTwJu34fmOvBVzYJDPV7OaJIPv88xfbfxMeZrpaf5QQhmjvsbLMNk2Dub7p5xWWw6vbGry4v8DAAD//8nRYx0=" } diff --git a/metricbeat/module/system/process/_meta/data.json b/metricbeat/module/system/process/_meta/data.json index dbf4f77633c..78ece4aa114 100644 --- a/metricbeat/module/system/process/_meta/data.json +++ b/metricbeat/module/system/process/_meta/data.json @@ -12,36 +12,209 @@ "metricset": { "name": "process" }, + "process": { + "args": [ + "/usr/bin/dockerd", + "-H", + "unix://" + ], + "executable": "/usr/bin/dockerd-ce", + "name": "dockerd", + "pgid": 2080, + "pid": 2080, + "ppid": 1, + "working_directory": "/" + }, "service": { "type": "system" }, "system": { "process": { - "cmdline": "/var/folders/k3/xlwbcsmj6dd7vjv2tg1d7c_40000gn/T/go-build019306533/b001/process.test -data", + "cgroup": { + "blkio": { + "id": "docker.service", + "path": "/system.slice/docker.service", + "total": { + "bytes": 844576104448, + "ios": 54869430 + } + }, + "cpu": { + "cfs": { + "period": { + "us": 100000 + }, + "quota": { + "us": 0 + }, + "shares": 1024 + }, + "id": "docker.service", + "path": "/system.slice/docker.service", + "rt": { + "period": { + "us": 0 + }, + "runtime": { + "us": 0 + } + }, + "stats": { + "periods": 0, + "throttled": { + "ns": 0, + "periods": 0 + } + } + }, + "cpuacct": { + "id": "docker.service", + "path": "/system.slice/docker.service", + "percpu": { + "1": 7058282754012, + "2": 7053634662537, + "3": 7069386293853, + "4": 7050055153087 + }, + "stats": { + "system": { + "ns": 7202920000000 + }, + "user": { + "ns": 19573240000000 + } + }, + "total": { + "ns": 28231358863489 + } + }, + "id": "docker.service", + "memory": { + "id": "docker.service", + "kmem": { + "failures": 0, + "limit": { + "bytes": 9223372036854771712 + }, + "usage": { + "bytes": 21139456, + "max": { + "bytes": 480030720 + } + } + }, + "kmem_tcp": { + "failures": 0, + "limit": { + "bytes": 9223372036854771712 + }, + "usage": { + "bytes": 0, + "max": { + "bytes": 0 + } + } + }, + "mem": { + "failures": 0, + "limit": { + "bytes": 9223372036854771712 + }, + "usage": { + "bytes": 3337703424, + "max": { + "bytes": 5245300736 + } + } + }, + "memsw": { + "failures": 0, + "limit": { + "bytes": 0 + }, + "usage": { + "bytes": 0, + "max": { + "bytes": 0 + } + } + }, + "path": "/system.slice/docker.service", + "stats": { + "active_anon": { + "bytes": 779677696 + }, + "active_file": { + "bytes": 1753378816 + }, + "cache": { + "bytes": 2127810560 + }, + "hierarchical_memory_limit": { + "bytes": 9223372036854771712 + }, + "hierarchical_memsw_limit": { + "bytes": 0 + }, + "inactive_anon": { + "bytes": 409075712 + }, + "inactive_file": { + "bytes": 374431744 + }, + "major_page_faults": 53164, + "mapped_file": { + "bytes": 7491584 + }, + "page_faults": 21923702, + "pages_in": 57261049, + "pages_out": 56521859, + "rss": { + "bytes": 1188753408 + }, + "rss_huge": { + "bytes": 56623104 + }, + "swap": { + "bytes": 0 + }, + "unevictable": { + "bytes": 0 + } + } + }, + "path": "/system.slice/docker.service" + }, + "cmdline": "/usr/bin/dockerd -H unix://", "cpu": { - "start_time": "2019-01-23T13:12:46.639Z", + "start_time": "2019-01-08T17:06:39.000Z", "total": { "norm": { - "pct": 0.0087 + "pct": 0.0049 }, - "pct": 0.0692, - "value": 239 + "pct": 0.0195, + "value": 27827810 } }, + "fd": { + "limit": { + "hard": 1048576, + "soft": 1048576 + }, + "open": 68 + }, "memory": { "rss": { - "bytes": 49573888, - "pct": 0.0029 + "bytes": 1187336192, + "pct": 0.0716 }, - "share": 0, - "size": 4515098624 + "share": 8253440, + "size": 4438523904 }, - "name": "process.test", - "pgid": 40547, - "pid": 40583, - "ppid": 40547, - "state": "running", - "username": "ruflin" + "state": "sleeping" } + }, + "user": { + "name": "root" } -} \ No newline at end of file +} diff --git a/metricbeat/module/system/process/_meta/fields.yml b/metricbeat/module/system/process/_meta/fields.yml index 9acb7b271ab..d89f43461c1 100644 --- a/metricbeat/module/system/process/_meta/fields.yml +++ b/metricbeat/module/system/process/_meta/fields.yml @@ -5,25 +5,25 @@ release: ga fields: - name: name - type: keyword - description: > - The process name. + type: alias + path: process.name + migration: true - name: state type: keyword description: > The process state. For example: "running". - name: pid - type: long - description: > - The process pid. + type: alias + path: process.pid + migration: true - name: ppid - type: long - description: > - The process parent pid. + type: alias + path: process.ppid + migration: true - name: pgid - type: long - description: > - The process group id. + type: alias + path: process.pgid + migration: true - name: cmdline type: keyword description: > @@ -31,17 +31,13 @@ arguments separated by space. ignore_above: 2048 - name: username - type: keyword - description: > - The username of the user that created the process. If the username - cannot be determined, the field will contain the user's - numeric identifier (UID). On Windows, this field includes the user's - domain and is formatted as `domain\username`. + type: alias + path: user.name + migration: true - name: cwd - type: keyword - description: > - The current working directory of the process. This field is only - available on Linux. + type: alias + path: process.working_directory + migration: true - name: env type: object object_type: keyword diff --git a/metricbeat/module/system/process/process.go b/metricbeat/module/system/process/process.go index 964e48fbb61..ec707e67bda 100644 --- a/metricbeat/module/system/process/process.go +++ b/metricbeat/module/system/process/process.go @@ -25,6 +25,7 @@ import ( "github.com/pkg/errors" + "github.com/elastic/beats/libbeat/common" "github.com/elastic/beats/libbeat/logp" "github.com/elastic/beats/libbeat/metric/system/process" "github.com/elastic/beats/metricbeat/mb" @@ -124,9 +125,42 @@ func (m *MetricSet) Fetch(r mb.ReporterV2) { } for _, proc := range procs { + rootFields := common.MapStr{ + "process": common.MapStr{ + "name": getAndRemove(proc, "name"), + "pid": getAndRemove(proc, "pid"), + "ppid": getAndRemove(proc, "ppid"), + "pgid": getAndRemove(proc, "pgid"), + }, + "user": common.MapStr{ + "name": getAndRemove(proc, "username"), + }, + } + + if cwd := getAndRemove(proc, "cwd"); cwd != nil { + rootFields.Put("process.working_directory", cwd) + } + + if exe := getAndRemove(proc, "exe"); exe != nil { + rootFields.Put("process.executable", exe) + } + + if args := getAndRemove(proc, "args"); args != nil { + rootFields.Put("process.args", args) + } + e := mb.Event{ + RootFields: rootFields, MetricSetFields: proc, } r.Event(e) } } + +func getAndRemove(from common.MapStr, field string) interface{} { + if v, ok := from[field]; ok { + delete(from, field) + return v + } + return nil +} diff --git a/metricbeat/module/zookeeper/_meta/docs.asciidoc b/metricbeat/module/zookeeper/_meta/docs.asciidoc index 67f71737cad..847229fa0bd 100644 --- a/metricbeat/module/zookeeper/_meta/docs.asciidoc +++ b/metricbeat/module/zookeeper/_meta/docs.asciidoc @@ -6,3 +6,10 @@ metricset is `mntr` and `server`. The ZooKeeper metricsets were tested with ZooKeeper 3.4.8 and are expected to work with all version >= 3.4.0. Versions prior to 3.4 do not support the `mntr` command. + +[float] +=== Dashboard + +The Zookeeper module comes with a predefined dashboard: + +image::./images/metricbeat-zookeeper.png[] diff --git a/metricbeat/module/zookeeper/_meta/kibana/7/dashboard/Metricbeat-zookeeper-overview.json b/metricbeat/module/zookeeper/_meta/kibana/7/dashboard/Metricbeat-zookeeper-overview.json new file mode 100644 index 00000000000..eae9c76f04a --- /dev/null +++ b/metricbeat/module/zookeeper/_meta/kibana/7/dashboard/Metricbeat-zookeeper-overview.json @@ -0,0 +1,504 @@ +{ + "objects": [ + { + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "query": { + "language": "lucene", + "query": "" + } + } + }, + "title": "Approximate data size [Metricbeat Zookeeper]", + "uiStateJSON": {}, + "version": 1, + "visState": { + "aggs": [], + "params": { + "axis_formatter": "number", + "axis_min": "0", + "axis_position": "left", + "axis_scale": "normal", + "id": "61ca57f0-469d-11e7-af02-69e470af7417", + "index_pattern": "metricbeat-*", + "interval": "auto", + "series": [ + { + "axis_position": "right", + "chart_type": "line", + "color": "#68BC00", + "fill": 0.5, + "formatter": "bytes", + "id": "61ca57f1-469d-11e7-af02-69e470af7417", + "label": "Approximate data size", + "line_width": 1, + "metrics": [ + { + "field": "zookeeper.mntr.approximate_data_size", + "id": "61ca57f2-469d-11e7-af02-69e470af7417", + "type": "avg" + } + ], + "point_size": 1, + "separate_axis": 0, + "split_mode": "everything", + "stacked": "none" + } + ], + "show_grid": 1, + "show_legend": 1, + "time_field": "@timestamp", + "type": "timeseries" + }, + "title": "Approximate data size [Metricbeat Zookeeper]", + "type": "metrics" + } + }, + "id": "8d3b7770-2319-11e9-bb66-8baac426dfd4", + "type": "visualization", + "updated_at": "2019-01-30T13:29:19.163Z", + "version": 2 + }, + { + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "query": { + "language": "lucene", + "query": "" + } + } + }, + "title": "Latency [Metricbeat Zookeeper]", + "uiStateJSON": {}, + "version": 1, + "visState": { + "aggs": [], + "params": { + "axis_formatter": "number", + "axis_min": "0", + "axis_position": "left", + "axis_scale": "normal", + "id": "61ca57f0-469d-11e7-af02-69e470af7417", + "index_pattern": "metricbeat-*", + "interval": "auto", + "series": [ + { + "axis_position": "right", + "chart_type": "line", + "color": "#68BC00", + "fill": 0.5, + "formatter": "ms,ms,2", + "id": "61ca57f1-469d-11e7-af02-69e470af7417", + "label": "Latency", + "line_width": 1, + "metrics": [ + { + "field": "zookeeper.mntr.latency.avg", + "id": "61ca57f2-469d-11e7-af02-69e470af7417", + "type": "avg" + } + ], + "point_size": 1, + "separate_axis": 0, + "split_mode": "everything", + "stacked": "none" + } + ], + "show_grid": 1, + "show_legend": 1, + "time_field": "@timestamp", + "type": "timeseries" + }, + "title": "Latency [Metricbeat Zookeeper]", + "type": "metrics" + } + }, + "id": "c0be43c0-2319-11e9-bb66-8baac426dfd4", + "type": "visualization", + "updated_at": "2019-01-30T13:29:05.974Z", + "version": 2 + }, + { + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "query": { + "language": "lucene", + "query": "" + } + } + }, + "title": "Alive Connections [Metricbeat Zookeeper]", + "uiStateJSON": {}, + "version": 1, + "visState": { + "aggs": [], + "params": { + "axis_formatter": "number", + "axis_min": "0", + "axis_position": "left", + "axis_scale": "normal", + "id": "61ca57f0-469d-11e7-af02-69e470af7417", + "index_pattern": "metricbeat-*", + "interval": "auto", + "series": [ + { + "axis_position": "right", + "chart_type": "line", + "color": "#68BC00", + "fill": 0.5, + "formatter": "0,0.[00]", + "id": "61ca57f1-469d-11e7-af02-69e470af7417", + "label": "Alive connections", + "line_width": 1, + "metrics": [ + { + "field": "zookeeper.mntr.num_alive_connections", + "id": "61ca57f2-469d-11e7-af02-69e470af7417", + "type": "avg" + } + ], + "point_size": 1, + "separate_axis": 0, + "split_mode": "everything", + "stacked": "none" + } + ], + "show_grid": 1, + "show_legend": 1, + "time_field": "@timestamp", + "type": "timeseries" + }, + "title": "Alive Connections [Metricbeat Zookeeper]", + "type": "metrics" + } + }, + "id": "1c2f8930-231a-11e9-bb66-8baac426dfd4", + "type": "visualization", + "updated_at": "2019-01-30T13:28:52.034Z", + "version": 3 + }, + { + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "query": { + "language": "lucene", + "query": "" + } + } + }, + "title": "Used file descriptors [Metricbeat Zookeeper]", + "uiStateJSON": {}, + "version": 1, + "visState": { + "aggs": [], + "params": { + "axis_formatter": "number", + "axis_position": "left", + "axis_scale": "normal", + "id": "61ca57f0-469d-11e7-af02-69e470af7417", + "index_pattern": "metricbeat-*", + "interval": "auto", + "series": [ + { + "axis_min": "0", + "axis_position": "left", + "chart_type": "line", + "color": "#68BC00", + "fill": "0.1", + "formatter": "percent", + "id": "61ca57f1-469d-11e7-af02-69e470af7417", + "label": "Used file descriptors", + "line_width": 1, + "metrics": [ + { + "field": "zookeeper.mntr.open_file_descriptor_count", + "id": "b92e4550-231a-11e9-9e57-679640dc0c7c", + "metric_agg": "avg", + "type": "avg" + }, + { + "field": "zookeeper.mntr.max_file_descriptor_count", + "id": "918d0c60-231b-11e9-9e57-679640dc0c7c", + "type": "avg" + }, + { + "id": "7e4d11e0-231b-11e9-9e57-679640dc0c7c", + "script": "params.a/params.b", + "type": "math", + "variables": [ + { + "field": "b92e4550-231a-11e9-9e57-679640dc0c7c", + "id": "81c03fa0-231b-11e9-9e57-679640dc0c7c", + "name": "a" + }, + { + "field": "918d0c60-231b-11e9-9e57-679640dc0c7c", + "id": "8a3af6c0-231b-11e9-9e57-679640dc0c7c", + "name": "b" + } + ] + } + ], + "point_size": 1, + "separate_axis": 1, + "split_mode": "everything", + "stacked": "none", + "value_template": "{{value}}" + }, + { + "axis_min": "0", + "axis_position": "right", + "chart_type": "line", + "color": "rgba(0,98,177,1)", + "fill": "0", + "formatter": "0,0.[00]", + "id": "dffaffe0-23cc-11e9-b1ff-37c851471450", + "label": "Open file descriptors", + "line_width": "1", + "metrics": [ + { + "field": "zookeeper.mntr.open_file_descriptor_count", + "id": "dffaffe1-23cc-11e9-b1ff-37c851471450", + "type": "avg" + } + ], + "point_size": 1, + "separate_axis": 1, + "split_mode": "everything", + "stacked": "none" + } + ], + "show_grid": 1, + "show_legend": 1, + "time_field": "@timestamp", + "type": "timeseries" + }, + "title": "Used file descriptors [Metricbeat Zookeeper]", + "type": "metrics" + } + }, + "id": "ddb13c60-231b-11e9-bb66-8baac426dfd4", + "type": "visualization", + "updated_at": "2019-01-30T13:28:22.583Z", + "version": 5 + }, + { + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "query": { + "language": "lucene", + "query": "" + } + } + }, + "title": "Packets received / sent [Metricbeat Zookeeper]", + "uiStateJSON": {}, + "version": 1, + "visState": { + "aggs": [], + "params": { + "axis_formatter": "number", + "axis_min": "0", + "axis_position": "left", + "axis_scale": "normal", + "id": "61ca57f0-469d-11e7-af02-69e470af7417", + "index_pattern": "metricbeat-*", + "interval": "auto", + "series": [ + { + "axis_position": "right", + "chart_type": "line", + "color": "#68BC00", + "fill": "0", + "formatter": "0,0.[00]", + "id": "61ca57f1-469d-11e7-af02-69e470af7417", + "label": "Packets received", + "line_width": "2", + "metrics": [ + { + "field": "zookeeper.mntr.packets.received", + "id": "61ca57f2-469d-11e7-af02-69e470af7417", + "type": "avg" + }, + { + "field": "61ca57f2-469d-11e7-af02-69e470af7417", + "id": "34949540-231c-11e9-9707-f128cdaa3bf2", + "type": "derivative", + "unit": "" + } + ], + "point_size": "0", + "separate_axis": 0, + "split_filters": [ + { + "color": "#68BC00", + "id": "5811d190-231c-11e9-9707-f128cdaa3bf2" + } + ], + "split_mode": "terms", + "stacked": "none" + }, + { + "axis_position": "right", + "chart_type": "line", + "color": "rgba(115,216,255,1)", + "fill": "0", + "formatter": "0,0.[00]", + "id": "753b2e60-231c-11e9-9707-f128cdaa3bf2", + "label": "Packets sent", + "line_width": "2", + "metrics": [ + { + "field": "zookeeper.mntr.packets.sent", + "id": "753b2e61-231c-11e9-9707-f128cdaa3bf2", + "type": "avg" + }, + { + "field": "753b2e61-231c-11e9-9707-f128cdaa3bf2", + "id": "7ed33c60-231c-11e9-9707-f128cdaa3bf2", + "type": "derivative", + "unit": "" + } + ], + "point_size": "0", + "separate_axis": 0, + "split_mode": "everything", + "stacked": "none" + } + ], + "show_grid": 1, + "show_legend": 1, + "time_field": "@timestamp", + "type": "timeseries" + }, + "title": "Packets received / sent [Metricbeat Zookeeper]", + "type": "metrics" + } + }, + "id": "d2f52b50-231c-11e9-bb66-8baac426dfd4", + "type": "visualization", + "updated_at": "2019-01-30T13:26:42.583Z", + "version": 3 + }, + { + "attributes": { + "description": "", + "hits": 0, + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "query": { + "language": "lucene", + "query": "" + } + } + }, + "optionsJSON": { + "darkTheme": false, + "hidePanelTitles": false, + "useMargins": true + }, + "panelsJSON": [ + { + "embeddableConfig": {}, + "gridData": { + "h": 15, + "i": "1", + "w": 15, + "x": 0, + "y": 15 + }, + "id": "8d3b7770-2319-11e9-bb66-8baac426dfd4", + "panelIndex": "1", + "title": "Approximate data size", + "type": "visualization", + "version": "7.0.0-alpha2" + }, + { + "embeddableConfig": {}, + "gridData": { + "h": 15, + "i": "2", + "w": 17, + "x": 15, + "y": 15 + }, + "id": "c0be43c0-2319-11e9-bb66-8baac426dfd4", + "panelIndex": "2", + "title": "Latency", + "type": "visualization", + "version": "7.0.0-alpha2" + }, + { + "embeddableConfig": {}, + "gridData": { + "h": 15, + "i": "3", + "w": 16, + "x": 32, + "y": 15 + }, + "id": "1c2f8930-231a-11e9-bb66-8baac426dfd4", + "panelIndex": "3", + "title": "Alive Connections", + "type": "visualization", + "version": "7.0.0-alpha2" + }, + { + "embeddableConfig": {}, + "gridData": { + "h": 15, + "i": "4", + "w": 25, + "x": 23, + "y": 0 + }, + "id": "ddb13c60-231b-11e9-bb66-8baac426dfd4", + "panelIndex": "4", + "title": "Used file descriptors", + "type": "visualization", + "version": "7.0.0-alpha2" + }, + { + "embeddableConfig": {}, + "gridData": { + "h": 15, + "i": "5", + "w": 23, + "x": 0, + "y": 0 + }, + "id": "d2f52b50-231c-11e9-bb66-8baac426dfd4", + "panelIndex": "5", + "title": "Packets received / sent", + "type": "visualization", + "version": "7.0.0-alpha2" + } + ], + "timeRestore": false, + "title": "[Metricbeat Zookeeper] Overview", + "version": 1 + }, + "id": "467207a0-231e-11e9-bb66-8baac426dfd4", + "type": "dashboard", + "updated_at": "2019-01-30T13:34:01.235Z", + "version": 6 + } + ], + "version": "7.0.0-alpha2" +} \ No newline at end of file diff --git a/metricbeat/module/zookeeper/server/server_integration_test.go b/metricbeat/module/zookeeper/server/server_integration_test.go index 7c5d4abecbc..75b8beb9940 100644 --- a/metricbeat/module/zookeeper/server/server_integration_test.go +++ b/metricbeat/module/zookeeper/server/server_integration_test.go @@ -48,7 +48,7 @@ func TestFetch(t *testing.T) { metricsetFields := event.MetricSetFields // Check values - assert.Equal(t, "06/29/2018 04:05 GMT", metricsetFields["version_date"]) + assert.Equal(t, "02/06/2016 03:18 GMT", metricsetFields["version_date"]) received := metricsetFields["received"].(int64) assert.True(t, received >= 0) diff --git a/metricbeat/modules.d/kubernetes.yml.disabled b/metricbeat/modules.d/kubernetes.yml.disabled index 38b08c8e05d..6ef57d33225 100644 --- a/metricbeat/modules.d/kubernetes.yml.disabled +++ b/metricbeat/modules.d/kubernetes.yml.disabled @@ -20,8 +20,8 @@ # Enriching parameters: #add_metadata: true #in_cluster: true - #labels.dedot: false - #annotations.dedot: false + #labels.dedot: true + #annotations.dedot: true # When used outside the cluster: #in_cluster: false #host: node_name diff --git a/metricbeat/modules.d/munin.yml.disabled b/metricbeat/modules.d/munin.yml.disabled index a86e6df767f..d42b1d9919e 100644 --- a/metricbeat/modules.d/munin.yml.disabled +++ b/metricbeat/modules.d/munin.yml.disabled @@ -6,4 +6,3 @@ # - node period: 10s hosts: ["localhost:4949"] - node.namespace: node diff --git a/metricbeat/tests/system/test_config.py b/metricbeat/tests/system/test_config.py index 02030bccab0..f2ac414a68b 100644 --- a/metricbeat/tests/system/test_config.py +++ b/metricbeat/tests/system/test_config.py @@ -1,11 +1,33 @@ import os -import metricbeat +from metricbeat import BaseTest import unittest from nose.plugins.attrib import attr import urllib2 import time -class ConfigTest(metricbeat.BaseTest): - def get_host(self): - return 'http://' + os.getenv('ES_HOST', 'localhost') + ':' + os.getenv('ES_PORT', '9200') +class ConfigTest(BaseTest): + def test_service_name(self): + """ + Test setting service name + """ + service_name = "testing" + self.render_config_template(modules=[{ + # Do it with any module that don't set the service name by itself + "name": "system", + "metricsets": ["cpu"], + "period": "5s", + "extras": { + "service.name": service_name, + }, + }]) + proc = self.start_beat() + self.wait_until(lambda: self.output_lines() > 0) + proc.check_kill_and_wait() + self.assert_no_logged_warnings() + + output = self.read_output_json() + event = output[0] + self.assert_fields_are_documented(event) + + self.assertEqual(service_name, event["service"]["name"]) diff --git a/metricbeat/tests/system/test_mongodb.py b/metricbeat/tests/system/test_mongodb.py index 3a8493584a4..4400d1c95b8 100644 --- a/metricbeat/tests/system/test_mongodb.py +++ b/metricbeat/tests/system/test_mongodb.py @@ -31,7 +31,7 @@ def test_status(self): self.assertEqual(len(output), 1) evt = output[0] - self.assertItemsEqual(self.de_dot(MONGODB_FIELDS), evt.keys()) + self.assertItemsEqual(self.de_dot(MONGODB_FIELDS + ["process"]), evt.keys()) self.assert_fields_are_documented(evt) diff --git a/metricbeat/tests/system/test_munin.py b/metricbeat/tests/system/test_munin.py index e1618202fa4..63cc80a94ff 100644 --- a/metricbeat/tests/system/test_munin.py +++ b/metricbeat/tests/system/test_munin.py @@ -18,7 +18,7 @@ def test_munin_node(self): "hosts": self.get_hosts(), "period": "1s", "extras": { - "node.namespace": namespace, + "munin.plugins": ["cpu"], }, }]) proc = self.start_beat() @@ -31,8 +31,12 @@ def test_munin_node(self): evt = output[0] print(evt) - assert evt["munin"][namespace]["cpu"]["user"] > 0 + assert evt["service"]["type"] == "cpu" + assert evt["munin"]["plugin"]["name"] == "cpu" + assert evt["munin"]["metrics"]["user"] > 0 + + self.assert_fields_are_documented(evt) def get_hosts(self): - return [os.getenv('MUNIN_HOST', 'localhost') + ':' + + return [self.compose_hosts()[0] + ':' + os.getenv('MUNIN_PORT', '4949')] diff --git a/metricbeat/tests/system/test_processors.py b/metricbeat/tests/system/test_processors.py index 0adabd129e6..92a73ddc221 100644 --- a/metricbeat/tests/system/test_processors.py +++ b/metricbeat/tests/system/test_processors.py @@ -188,7 +188,7 @@ def test_multiple_actions(self): "period": "1s" }], processors=[{ - "include_fields": {"fields": ["system.process"]}, + "include_fields": {"fields": ["system.process", "process"]}, }, { "drop_fields": {"fields": ["system.process.memory"]}, }] @@ -207,17 +207,17 @@ def test_multiple_actions(self): for key in [ "system.process.cpu.start_time", "system.process.cpu.total.pct", - "system.process.name", - "system.process.pid", + "process.name", + "process.pid", ]: - assert key in output + assert key in output, "'%s' not found" % key for key in [ "system.process.memory.size", "system.process.memory.rss.bytes", "system.process.memory.rss.pct" ]: - assert key not in output + assert key not in output, "'%s' not expected but found" % key def test_contradictory_multiple_actions(self): """ diff --git a/metricbeat/tests/system/test_system.py b/metricbeat/tests/system/test_system.py index 0405900163e..b439ca2c85e 100644 --- a/metricbeat/tests/system/test_system.py +++ b/metricbeat/tests/system/test_system.py @@ -48,8 +48,7 @@ # for some kernel level processes. fd is also part of the system process, but # is not available on all OSes and requires root to read for all processes. # cgroup is only available on linux. -SYSTEM_PROCESS_FIELDS = ["cpu", "memory", "name", "pid", "ppid", "pgid", - "state", "username"] +SYSTEM_PROCESS_FIELDS = ["cpu", "memory", "state"] class Test(metricbeat.BaseTest): @@ -442,11 +441,11 @@ def test_process_metricbeat(self): output = self.read_output()[0] - assert re.match("(?i)metricbeat.test(.exe)?", output["system.process.name"]) + assert re.match("(?i)metricbeat.test(.exe)?", output["process.name"]) assert re.match("(?i).*metricbeat.test(.exe)? -systemTest", output["system.process.cmdline"]) assert isinstance(output["system.process.state"], six.string_types) assert isinstance(output["system.process.cpu.start_time"], six.string_types) - self.check_username(output["system.process.username"]) + self.check_username(output["user.name"]) @unittest.skipUnless(re.match("(?i)win|linux|darwin|freebsd", sys.platform), "os") def test_socket_summary(self): diff --git a/packetbeat/_meta/beat.yml b/packetbeat/_meta/beat.yml index 82a9b185f74..8c8037a5e7c 100644 --- a/packetbeat/_meta/beat.yml +++ b/packetbeat/_meta/beat.yml @@ -104,6 +104,6 @@ packetbeat.protocols: #==================== Elasticsearch template setting ========================== setup.template.settings: - index.number_of_shards: 3 + index.number_of_shards: 1 #index.codec: best_compression #_source.enabled: false diff --git a/packetbeat/docs/fields.asciidoc b/packetbeat/docs/fields.asciidoc index 9bed1b8e59a..9f0f6f64a2d 100644 --- a/packetbeat/docs/fields.asciidoc +++ b/packetbeat/docs/fields.asciidoc @@ -5307,16 +5307,6 @@ type: keyword Major version of the user agent. --- - -*`user_agent.device`*:: -+ --- -type: keyword - -Name of the physical device. - - -- *`user_agent.os.major`*:: @@ -5337,16 +5327,6 @@ type: long Minor version of the operating system. --- - -*`url.hostname`*:: -+ --- -type: keyword - -Hostname of the request, such as "elastic.co". - - -- [[exported-fields-flows_event]] diff --git a/packetbeat/include/fields.go b/packetbeat/include/fields.go index 3dfb472191f..24ff7e76031 100644 --- a/packetbeat/include/fields.go +++ b/packetbeat/include/fields.go @@ -32,5 +32,5 @@ func init() { // AssetFieldsYml returns asset data. // This is the base64 encoded gzipped contents of fields.yml. func AssetFieldsYml() string { - return "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" + return "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" } diff --git a/packetbeat/packetbeat.reference.yml b/packetbeat/packetbeat.reference.yml index 6474a5e37b8..09e21d901db 100644 --- a/packetbeat/packetbeat.reference.yml +++ b/packetbeat/packetbeat.reference.yml @@ -726,11 +726,6 @@ output.elasticsearch: # IPv6 addresses should always be defined as: https://[2001:db8::1]:9200 hosts: ["localhost:9200"] - # Enabled ilm (beta) to use index lifecycle management instead daily indices. - #ilm.enabled: false - #ilm.rollover_alias: "packetbeat" - #ilm.pattern: "{now/d}-000001" - # Set gzip compression level. #compression_level: 0 @@ -1377,6 +1372,25 @@ setup.template.settings: #_source: #enabled: false +#============================== Setup ILM ===================================== + +# Configure Index Lifecycle Management Index Lifecycle Management creates a +# write alias and adds additional settings to the template. +# The elasticsearch.output.index setting will be replaced with the write alias +# if ILM is enabled. + +# Enabled ILM support. Valid values are true, false, and auto. The beat will +# detect availabilty of Index Lifecycle Management in Elasticsearch and enable +# or disable ILM support. +#setup.ilm.enabled: auto + +# Configure the ILM write alias name. +#setup.ilm.rollover_alias: "packetbeat" + +# Configure rollover index pattern. +#setup.ilm.pattern: "{now/d}-000001" + + #============================== Kibana ===================================== # Starting with Beats version 6.0.0, the dashboards are loaded via the Kibana API. diff --git a/packetbeat/packetbeat.yml b/packetbeat/packetbeat.yml index 0c207a4d199..d7b75fe695d 100644 --- a/packetbeat/packetbeat.yml +++ b/packetbeat/packetbeat.yml @@ -104,7 +104,7 @@ packetbeat.protocols: #==================== Elasticsearch template setting ========================== setup.template.settings: - index.number_of_shards: 3 + index.number_of_shards: 1 #index.codec: best_compression #_source.enabled: false @@ -127,7 +127,7 @@ setup.template.settings: #============================== Dashboards ===================================== # These settings control loading the sample dashboards to the Kibana index. Loading # the dashboards is disabled by default and can be enabled either by setting the -# options here, or by using the `-setup` CLI flag or the `setup` command. +# options here or by using the `setup` command. #setup.dashboards.enabled: false # The URL from where to download the dashboards archive. By default this URL @@ -175,9 +175,6 @@ output.elasticsearch: # Array of hosts to connect to. hosts: ["localhost:9200"] - # Enabled ilm (beta) to use index lifecycle management instead daily indices. - #ilm.enabled: false - # Optional protocol and basic auth credentials. #protocol: "https" #username: "elastic" diff --git a/packetbeat/tests/system/test_0026_test_config.py b/packetbeat/tests/system/test_0026_test_config.py index 81011b74162..d9ea29a89aa 100644 --- a/packetbeat/tests/system/test_0026_test_config.py +++ b/packetbeat/tests/system/test_0026_test_config.py @@ -1,5 +1,7 @@ from packetbeat import BaseTest import os +import unittest +import subprocess """ Tests for checking the -configtest CLI option and the @@ -9,24 +11,63 @@ class Test(BaseTest): + @unittest.skipIf(os.name != 'linux', "default device 'any' only exists on linux") def test_ok_config(self): """ - With -configtest and correct configuration, it should exit with + With 'test config' and correct configuration, it should exit with status 0 but not actually process any packets. """ + + print("start test") + self.render_config_template() - self.run_packetbeat(pcap="http_post.pcap", - extra_args=["-configtest"]) + self.run_pb_config_tst() assert not os.path.isfile( os.path.join(self.working_dir, "output/packetbeat")) def test_config_error(self): """ - With -configtest and an error in the configuration, it should + With 'test config' and an error in the configuration, it should return a non-zero error code. """ self.render_config_template( bpf_filter="invalid BPF filter" ) - self.start_packetbeat(extra_args=["-configtest"]).check_wait(exit_code=1) + + self.run_pb_config_tst(exit_code=1) + + def run_pb_config_tst(self, exit_code=0): + config = "packetbeat.yml" + + cmd = os.path.join(self.beat_path, "packetbeat.test") + args = [ + cmd, "-systemTest", + "-c", os.path.join(self.working_dir, config), + ] + + if os.getenv("TEST_COVERAGE") == "true": + args += [ + "-test.coverprofile", + os.path.join(self.working_dir, "coverage.cov"), + ] + + args.extend(["test", "config"]) + + output = "packetbeat.log" + + with open(os.path.join(self.working_dir, output), "wb") as outfile: + proc = subprocess.Popen(args, + stdout=outfile, + stderr=subprocess.STDOUT + ) + actual_exit_code = proc.wait() + + if actual_exit_code != exit_code: + print("============ Log Output =====================") + with open(os.path.join(self.working_dir, output)) as f: + print(f.read()) + print("============ Log End Output =====================") + assert actual_exit_code == exit_code, "Expected exit code to be %d, but it was %d" % ( + exit_code, actual_exit_code) + return actual_exit_code diff --git a/testing/environments/docker/kafka/Dockerfile b/testing/environments/docker/kafka/Dockerfile index 581513efcbd..753102d5055 100644 --- a/testing/environments/docker/kafka/Dockerfile +++ b/testing/environments/docker/kafka/Dockerfile @@ -4,7 +4,7 @@ ENV KAFKA_HOME /kafka # The advertised host is kafka. This means it will not work if container is started locally and connected from localhost to it ENV KAFKA_ADVERTISED_HOST kafka ENV KAFKA_LOGS_DIR="/kafka-logs" -ENV KAFKA_VERSION 2.0.0 +ENV KAFKA_VERSION 2.1.0 ENV _JAVA_OPTIONS "-Djava.net.preferIPv4Stack=true" ENV TERM=linux diff --git a/testing/environments/latest.yml b/testing/environments/latest.yml index 4eab4441e98..7ecc80ece0b 100644 --- a/testing/environments/latest.yml +++ b/testing/environments/latest.yml @@ -3,7 +3,7 @@ version: '2.3' services: elasticsearch: - image: docker.elastic.co/elasticsearch/elasticsearch:6.5.4 + image: docker.elastic.co/elasticsearch/elasticsearch:6.6.0 healthcheck: test: ["CMD", "curl", "-f", "http://localhost:9200"] retries: 300 @@ -16,7 +16,7 @@ services: - "xpack.security.enabled=false" logstash: - image: docker.elastic.co/logstash/logstash:6.5.4 + image: docker.elastic.co/logstash/logstash:6.6.0 healthcheck: test: ["CMD", "curl", "-f", "http://localhost:9600/_node/stats"] retries: 300 @@ -26,7 +26,7 @@ services: - ./docker/logstash/pki:/etc/pki:ro kibana: - image: docker.elastic.co/kibana/kibana:6.5.4 + image: docker.elastic.co/kibana/kibana:6.6.0 healthcheck: test: ["CMD", "curl", "-f", "http://localhost:5601"] retries: 300 diff --git a/vendor/github.com/elastic/go-ucfg/CHANGELOG.md b/vendor/github.com/elastic/go-ucfg/CHANGELOG.md index 3ec656a8887..f6c0b4c3026 100644 --- a/vendor/github.com/elastic/go-ucfg/CHANGELOG.md +++ b/vendor/github.com/elastic/go-ucfg/CHANGELOG.md @@ -14,6 +14,15 @@ This project adheres to [Semantic Versioning](http://semver.org/). ### Fixed +## [0.7.0] + +### Added +- Add (*Config).Has. #127 +- Add (*Config).Remove. #126 + +### Removed +- Remove CI and support for go versions <1.10. #128 + ## [0.6.5] ### Added @@ -225,8 +234,9 @@ This project adheres to [Semantic Versioning](http://semver.org/). - Introduced CHANGELOG.md for documenting changes to ucfg. -[Unreleased]: https://github.com/elastic/go-ucfg/compare/v0.6.5...HEAD -[0.6.4]: https://github.com/elastic/go-ucfg/compare/v0.6.4...v0.6.5 +[Unreleased]: https://github.com/elastic/go-ucfg/compare/v0.7.0...HEAD +[0.7.0]: https://github.com/elastic/go-ucfg/compare/v0.6.5...v0.7.0 +[0.6.5]: https://github.com/elastic/go-ucfg/compare/v0.6.4...v0.6.5 [0.6.4]: https://github.com/elastic/go-ucfg/compare/v0.6.3...v0.6.4 [0.6.3]: https://github.com/elastic/go-ucfg/compare/v0.6.2...v0.6.3 [0.6.2]: https://github.com/elastic/go-ucfg/compare/v0.6.1...v0.6.2 diff --git a/vendor/github.com/elastic/go-ucfg/README.md b/vendor/github.com/elastic/go-ucfg/README.md index 515d38d5f9c..9898e38a46f 100644 --- a/vendor/github.com/elastic/go-ucfg/README.md +++ b/vendor/github.com/elastic/go-ucfg/README.md @@ -89,4 +89,4 @@ The above uses `Counter` as the config variable. ucfg assures that the value is ucfg has the following requirements: -* Golang 1.7+ +* Golang 1.10+ diff --git a/vendor/github.com/elastic/go-ucfg/error.go b/vendor/github.com/elastic/go-ucfg/error.go index 2c057b9cf12..a48690e30a8 100644 --- a/vendor/github.com/elastic/go-ucfg/error.go +++ b/vendor/github.com/elastic/go-ucfg/error.go @@ -61,6 +61,8 @@ type criticalError struct { var ( ErrMissing = errors.New("missing field") + ErrNoParse = errors.New("parsing dynamic configs is disabled") + ErrCyclicReference = errors.New("cyclic reference detected") ErrDuplicateValidator = errors.New("validator already registered") @@ -259,6 +261,11 @@ func raiseUnsupportedInputType(ctx context, meta *Meta, v reflect.Value) Error { return raiseCritical(reason, messagePath(reason, meta, message, ctx.path("."))) } +func raiseNoParse(ctx context, meta *Meta) Error { + reason := ErrNoParse + return raisePathErr(reason, meta, "", ctx.path(".")) +} + func raiseNil(reason error) Error { // programmers error (passed unexpected nil pointer) return raiseCritical(reason, "") diff --git a/vendor/github.com/elastic/go-ucfg/fieldset.go b/vendor/github.com/elastic/go-ucfg/fieldset.go index 1d1fb6f450c..e797f18417d 100644 --- a/vendor/github.com/elastic/go-ucfg/fieldset.go +++ b/vendor/github.com/elastic/go-ucfg/fieldset.go @@ -22,7 +22,7 @@ type fieldSet struct { parent *fieldSet } -func NewFieldSet(parent *fieldSet) *fieldSet { +func newFieldSet(parent *fieldSet) *fieldSet { return &fieldSet{ fields: map[string]struct{}{}, parent: parent, diff --git a/vendor/github.com/elastic/go-ucfg/flag/file.go b/vendor/github.com/elastic/go-ucfg/flag/file.go index d59c98396a0..de08b4b2dd8 100644 --- a/vendor/github.com/elastic/go-ucfg/flag/file.go +++ b/vendor/github.com/elastic/go-ucfg/flag/file.go @@ -24,8 +24,17 @@ import ( "github.com/elastic/go-ucfg" ) +// FileLoader is used by NewFlagFiles to define customer file loading functions +// for different file extensions. type FileLoader func(name string, opts ...ucfg.Option) (*ucfg.Config, error) +// NewFlagFiles create a new flag, that will load external configurations file +// when being used. Configurations loaded from multiple files will be merged +// into one common Config object. If cfg is not nil, then the loaded +// configurations will be merged into cfg. +// The extensions parameter define custom file loaders for different file +// extensions. If extensions contains an entry with key "", then this loader +// will be used as default fallback. func NewFlagFiles( cfg *ucfg.Config, extensions map[string]FileLoader, diff --git a/vendor/github.com/elastic/go-ucfg/json/json.go b/vendor/github.com/elastic/go-ucfg/json/json.go index cd2ded820a1..d45a0143551 100644 --- a/vendor/github.com/elastic/go-ucfg/json/json.go +++ b/vendor/github.com/elastic/go-ucfg/json/json.go @@ -24,6 +24,7 @@ import ( "github.com/elastic/go-ucfg" ) +// NewConfig creates a new configuration object from the JSON string passed via in. func NewConfig(in []byte, opts ...ucfg.Option) (*ucfg.Config, error) { var m interface{} if err := json.Unmarshal(in, &m); err != nil { @@ -32,12 +33,15 @@ func NewConfig(in []byte, opts ...ucfg.Option) (*ucfg.Config, error) { return ucfg.NewFrom(m, opts...) } +// NewConfigWithFile loads a new configuration object from an external JSON file. func NewConfigWithFile(name string, opts ...ucfg.Option) (*ucfg.Config, error) { input, err := ioutil.ReadFile(name) if err != nil { return nil, err } - opts = append([]ucfg.Option{ucfg.MetaData(ucfg.Meta{name})}, opts...) + opts = append([]ucfg.Option{ + ucfg.MetaData(ucfg.Meta{Source: name}), + }, opts...) return NewConfig(input, opts...) } diff --git a/vendor/github.com/elastic/go-ucfg/opts.go b/vendor/github.com/elastic/go-ucfg/opts.go index 69937340c77..565adef6900 100644 --- a/vendor/github.com/elastic/go-ucfg/opts.go +++ b/vendor/github.com/elastic/go-ucfg/opts.go @@ -33,6 +33,7 @@ type options struct { env []*Config resolvers []func(name string) (string, error) varexp bool + noParse bool configValueHandling configHandling @@ -137,7 +138,7 @@ func doResolveNOOP(o *options) { } var ( - // ReplacesValues option configures all merging and unpacking operations to + // ReplaceValues option configures all merging and unpacking operations to // replace old dictionaries and arrays while merging. Value merging can be // overwritten in unpack by using struct tags. ReplaceValues = makeOptValueHandling(cfgReplaceValue) @@ -170,7 +171,7 @@ func makeOptions(opts []Option) *options { validatorTag: "validate", pathSep: "", // no separator by default parsed: map[string]spliceValue{}, - activeFields: NewFieldSet(nil), + activeFields: newFieldSet(nil), } for _, opt := range opts { opt(&o) diff --git a/vendor/github.com/elastic/go-ucfg/path.go b/vendor/github.com/elastic/go-ucfg/path.go index 44755e95645..ff7e6301423 100644 --- a/vendor/github.com/elastic/go-ucfg/path.go +++ b/vendor/github.com/elastic/go-ucfg/path.go @@ -32,6 +32,7 @@ type field interface { String() string SetValue(opt *options, elem value, v value) Error GetValue(opt *options, elem value) (value, Error) + Remove(opt *options, elem value) (bool, Error) } type namedField struct { @@ -110,6 +111,32 @@ func (i idxField) String() string { return fmt.Sprintf("%d", i.i) } +func (p cfgPath) Has(cfg *Config, opt *options) (bool, Error) { + fields := p.fields + + cur := value(cfgSub{cfg}) + for ; len(fields) > 0; fields = fields[1:] { + field := fields[0] + next, err := field.GetValue(opt, cur) + if err != nil { + // has checks if a value is missing -> ErrMissing is no error but a valid + // outcome + if err.Reason() == ErrMissing { + err = nil + } + return false, err + } + + if next == nil { + return false, nil + } + + cur = next + } + + return true, nil +} + func (p cfgPath) GetValue(cfg *Config, opt *options) (value, Error) { fields := p.fields @@ -223,3 +250,60 @@ func (i idxField) SetValue(opts *options, elem value, v value) Error { v.SetContext(context{parent: elem, field: i.String()}) return nil } + +func (p cfgPath) Remove(cfg *Config, opt *options) (bool, error) { + fields := p.fields + + // Loop over intermediate objects. Returns an error if any intermediate is + // actually no object. + cur := value(cfgSub{cfg}) + for ; len(fields) > 1; fields = fields[1:] { + field := fields[0] + next, err := field.GetValue(opt, cur) + if err != nil { + // Ignore ErrMissing when walking down a config tree. If intermediary is + // missing we can't remove our setting. + if err.Reason() == ErrMissing { + err = nil + } + + return false, err + } + + if next == nil { + return false, err + } + + cur = next + } + + // resolve config object in case we deal with references + tmp, err := cur.toConfig(opt) + if err != nil { + return false, err + } + cur = cfgSub{tmp} + + field := fields[0] + return field.Remove(opt, cur) +} + +func (n namedField) Remove(opts *options, elem value) (bool, Error) { + sub, ok := elem.(cfgSub) + if !ok { + return false, raiseExpectedObject(opts, elem) + } + + removed := sub.c.fields.del(n.name) + return removed, nil +} + +func (i idxField) Remove(opts *options, elem value) (bool, Error) { + sub, ok := elem.(cfgSub) + if !ok { + return false, raiseExpectedObject(opts, elem) + } + + removed := sub.c.fields.delAt(i.i) + return removed, nil +} diff --git a/vendor/github.com/elastic/go-ucfg/reify.go b/vendor/github.com/elastic/go-ucfg/reify.go index ae88d18b77e..c209964b883 100644 --- a/vendor/github.com/elastic/go-ucfg/reify.go +++ b/vendor/github.com/elastic/go-ucfg/reify.go @@ -189,7 +189,7 @@ func reifyMap(opts *options, to reflect.Value, from *Config) Error { to.Set(reflect.MakeMap(to.Type())) } for k, value := range fields { - opts.activeFields = NewFieldSet(parentFields) + opts.activeFields = newFieldSet(parentFields) key := reflect.ValueOf(k) old := to.MapIndex(key) @@ -249,7 +249,7 @@ func reifyStruct(opts *options, orig reflect.Value, cfg *Config) Error { opts = tmp } - opts.activeFields = NewFieldSet(parentFields) + opts.activeFields = newFieldSet(parentFields) vField := to.Field(i) validators, err := parseValidatorTags(stField.Tag.Get(opts.validatorTag)) @@ -656,7 +656,7 @@ func doReifyPrimitive( } previous := opts.opts.activeFields - opts.opts.activeFields = NewFieldSet(previous) + opts.opts.activeFields = newFieldSet(previous) valT, err := val.typ(opts.opts) if err != nil { ctx := val.Context() diff --git a/vendor/github.com/elastic/go-ucfg/types.go b/vendor/github.com/elastic/go-ucfg/types.go index 7ed109e7353..a5b9d3eb58e 100644 --- a/vendor/github.com/elastic/go-ucfg/types.go +++ b/vendor/github.com/elastic/go-ucfg/types.go @@ -377,7 +377,7 @@ func (c cfgSub) reify(opts *options) (interface{}, error) { case len(fields) > 0 && len(arr) == 0: m := make(map[string]interface{}) for k, v := range fields { - opts.activeFields = NewFieldSet(parentFields) + opts.activeFields = newFieldSet(parentFields) var err error if m[k], err = v.reify(opts); err != nil { return nil, err @@ -387,7 +387,7 @@ func (c cfgSub) reify(opts *options) (interface{}, error) { case len(fields) == 0 && len(arr) > 0: m := make([]interface{}, len(arr)) for i, v := range arr { - opts.activeFields = NewFieldSet(parentFields) + opts.activeFields = newFieldSet(parentFields) var err error if m[i], err = v.reify(opts); err != nil { return nil, err @@ -397,14 +397,14 @@ func (c cfgSub) reify(opts *options) (interface{}, error) { default: m := make(map[string]interface{}) for k, v := range fields { - opts.activeFields = NewFieldSet(parentFields) + opts.activeFields = newFieldSet(parentFields) var err error if m[k], err = v.reify(opts); err != nil { return nil, err } } for i, v := range arr { - opts.activeFields = NewFieldSet(parentFields) + opts.activeFields = newFieldSet(parentFields) var err error m[fmt.Sprintf("%d", i)], err = v.reify(opts) if err != nil { @@ -554,6 +554,10 @@ func (s spliceDynValue) String() string { } func parseValue(p *cfgPrimitive, opts *options, str string) (value, error) { + if opts.noParse { + return nil, raiseNoParse(p.ctx, p.meta()) + } + ifc, err := parse.Value(str) if err != nil { return nil, err diff --git a/vendor/github.com/elastic/go-ucfg/ucfg.go b/vendor/github.com/elastic/go-ucfg/ucfg.go index 094c88e7d4c..69f6eaab2d6 100644 --- a/vendor/github.com/elastic/go-ucfg/ucfg.go +++ b/vendor/github.com/elastic/go-ucfg/ucfg.go @@ -83,7 +83,7 @@ func New() *Config { } } -// NustNewFrom creates a new config object normalizing and copying from into the new +// MustNewFrom creates a new config object normalizing and copying from into the new // Config object. MustNewFrom uses Merge to copy from. // // MustNewFrom supports the options: PathSep, MetaData, StructTag, VarExp @@ -126,12 +126,50 @@ func (c *Config) GetFields() []string { return names } +// Has checks if a field by the given path+idx configuration exists. +// Has returns an error if the path can not be resolved because a primitive +// value is found in the middle of the traversal. +func (c *Config) Has(name string, idx int, options ...Option) (bool, error) { + opts := makeOptions(options) + p := parsePathIdx(name, opts.pathSep, idx) + return p.Has(c, opts) +} + // HasField checks if c has a top-level named key name. func (c *Config) HasField(name string) bool { _, ok := c.fields.get(name) return ok } +// Remove removes a setting from the config. If the configuration references +// another configuration namespace, then the setting will be removed from the +// linked reference. +// Remove returns true if the setting was removed. If the path can't be +// resolved (e.g. due to type mismatch) Remove will return an error. +// +// Settings can be created on Unpack via Env, Resolve, and ResolveEnv. Settings +// generated dynamically on Unpack can not be removed. Remove ignores any +// configured environments and will return an error if a value can not be +// removed for this reason. +// +// The setting path is constructed from name and idx. If name is set and idx is -1, +// only the name is used to access the setting by name. If name is empty, idx +// must be >= 0, assuming the Config is a list. If both name and idx are set, +// the name must point to a list. +// +// Remove supports the options: PathSep +func (c *Config) Remove(name string, idx int, options ...Option) (bool, error) { + opts := makeOptions(options) + + // ignore environments + opts.env = nil + opts.resolvers = nil + opts.noParse = true + + p := parsePathIdx(name, opts.pathSep, idx) + return p.Remove(c, opts) +} + // Path gets the absolute path of c separated by sep. If c is a root-Config an // empty string will be returned. func (c *Config) Path(sep string) string { @@ -219,6 +257,26 @@ func (f *fields) array() []value { return f.a } +func (f *fields) del(name string) bool { + _, exists := f.d[name] + if exists { + delete(f.d, name) + } + return exists +} + +func (f *fields) delAt(i int) bool { + a := f.a + if i < 0 || len(a) <= i { + return false + } + + copy(a[i:], a[i+1:]) + a[len(a)-1] = nil + f.a = a[:len(a)-1] + return true +} + func (f *fields) set(name string, v value) { if f.d == nil { f.d = map[string]value{} diff --git a/vendor/github.com/elastic/go-ucfg/unpack.go b/vendor/github.com/elastic/go-ucfg/unpack.go index 1951cabd73d..85ba18176e3 100644 --- a/vendor/github.com/elastic/go-ucfg/unpack.go +++ b/vendor/github.com/elastic/go-ucfg/unpack.go @@ -155,12 +155,7 @@ func implementsUnpacker(t reflect.Type) bool { // method receiver is known, check config parameters being compatible tIn := method.Type.In(1) - acceptsConfig := tConfig.ConvertibleTo(tIn) || tConfigPtr.ConvertibleTo(tIn) - if !acceptsConfig { - return false - } - - return true + return tConfig.ConvertibleTo(tIn) || tConfigPtr.ConvertibleTo(tIn) } func unpackWith(opts *options, v reflect.Value, with value) Error { diff --git a/vendor/github.com/elastic/go-ucfg/yaml/yaml.go b/vendor/github.com/elastic/go-ucfg/yaml/yaml.go index 0c35f8efa79..4ce5e08fe0a 100644 --- a/vendor/github.com/elastic/go-ucfg/yaml/yaml.go +++ b/vendor/github.com/elastic/go-ucfg/yaml/yaml.go @@ -25,6 +25,7 @@ import ( "github.com/elastic/go-ucfg" ) +// NewConfig creates a new configuration object from the YAML string passed via in. func NewConfig(in []byte, opts ...ucfg.Option) (*ucfg.Config, error) { var m interface{} if err := yaml.Unmarshal(in, &m); err != nil { @@ -34,12 +35,15 @@ func NewConfig(in []byte, opts ...ucfg.Option) (*ucfg.Config, error) { return ucfg.NewFrom(m, opts...) } +// NewConfigWithFile loads a new configuration object from an external JSON file. func NewConfigWithFile(name string, opts ...ucfg.Option) (*ucfg.Config, error) { input, err := ioutil.ReadFile(name) if err != nil { return nil, err } - opts = append([]ucfg.Option{ucfg.MetaData(ucfg.Meta{name})}, opts...) + opts = append([]ucfg.Option{ + ucfg.MetaData(ucfg.Meta{Source: name}), + }, opts...) return NewConfig(input, opts...) } diff --git a/vendor/vendor.json b/vendor/vendor.json index 4936b24f53a..681cec96c91 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -1109,52 +1109,52 @@ "versionExact": "v0.0.6" }, { - "checksumSHA1": "Yb61Nqnh+3igFci61hv9WYgk/hc=", + "checksumSHA1": "b91TSC0atoGVSMZdKuWTYsMOGiM=", "path": "github.com/elastic/go-ucfg", - "revision": "92d43887f91851c9936621665af7f796f4d03412", - "revisionTime": "2018-10-26T17:42:06Z", - "version": "v0.6.5", - "versionExact": "v0.6.5" + "revision": "0539807037ce820e147797f051ff32b05f4f9288", + "revisionTime": "2019-01-28T11:18:48Z", + "version": "v0.7.0", + "versionExact": "v0.7.0" }, { "checksumSHA1": "X+R/CD8SokJrmlxFTx2nSevRDhQ=", "path": "github.com/elastic/go-ucfg/cfgutil", - "revision": "581f7b1fe9d84f4c18ef0694d6e0eb944a925dae", - "revisionTime": "2018-07-13T14:04:29Z", - "version": "v0.6.1", - "versionExact": "v0.6.1" + "revision": "0539807037ce820e147797f051ff32b05f4f9288", + "revisionTime": "2019-01-28T11:18:48Z", + "version": "v0.7.0", + "versionExact": "v0.7.0" }, { - "checksumSHA1": "zC8mCPW/pPPNcuHQOc/B/Ej1W1U=", + "checksumSHA1": "/pq8HNEdzHmky9S9HSo1WofXQ4Y=", "path": "github.com/elastic/go-ucfg/flag", - "revision": "581f7b1fe9d84f4c18ef0694d6e0eb944a925dae", - "revisionTime": "2018-07-13T14:04:29Z", - "version": "v0.6.1", - "versionExact": "v0.6.1" + "revision": "0539807037ce820e147797f051ff32b05f4f9288", + "revisionTime": "2019-01-28T11:18:48Z", + "version": "v0.7.0", + "versionExact": "v0.7.0" }, { "checksumSHA1": "esXpiQlEvTOUwsE0nNesso8albo=", "path": "github.com/elastic/go-ucfg/internal/parse", - "revision": "581f7b1fe9d84f4c18ef0694d6e0eb944a925dae", - "revisionTime": "2018-07-13T14:04:29Z", - "version": "v0.6.1", - "versionExact": "v0.6.1" + "revision": "0539807037ce820e147797f051ff32b05f4f9288", + "revisionTime": "2019-01-28T11:18:48Z", + "version": "v0.7.0", + "versionExact": "v0.7.0" }, { - "checksumSHA1": "5mXUhhlPdvcAFKiQENInTJWrtQM=", + "checksumSHA1": "cfMNsyQm0gZOV0hRJrBSdKDQSBo=", "path": "github.com/elastic/go-ucfg/json", - "revision": "581f7b1fe9d84f4c18ef0694d6e0eb944a925dae", - "revisionTime": "2018-07-13T14:04:29Z", - "version": "v0.6.1", - "versionExact": "v0.6.1" + "revision": "0539807037ce820e147797f051ff32b05f4f9288", + "revisionTime": "2019-01-28T11:18:48Z", + "version": "v0.7.0", + "versionExact": "v0.7.0" }, { - "checksumSHA1": "Bg6vistPQLftv2fEYB7GWwSExv8=", + "checksumSHA1": "PJCBACDGPhnRAEqjGPMPCMjbj4o=", "path": "github.com/elastic/go-ucfg/yaml", - "revision": "581f7b1fe9d84f4c18ef0694d6e0eb944a925dae", - "revisionTime": "2018-07-13T14:04:29Z", - "version": "v0.6.1", - "versionExact": "v0.6.1" + "revision": "0539807037ce820e147797f051ff32b05f4f9288", + "revisionTime": "2019-01-28T11:18:48Z", + "version": "v0.7.0", + "versionExact": "v0.7.0" }, { "checksumSHA1": "rnd3qf1FE22X3MxXWbetqq6EoBk=", diff --git a/winlogbeat/_meta/beat.yml b/winlogbeat/_meta/beat.yml index 783591d7a0c..ba8f85214ed 100644 --- a/winlogbeat/_meta/beat.yml +++ b/winlogbeat/_meta/beat.yml @@ -26,6 +26,6 @@ winlogbeat.event_logs: #==================== Elasticsearch template setting ========================== setup.template.settings: - index.number_of_shards: 3 + index.number_of_shards: 1 #index.codec: best_compression #_source.enabled: false diff --git a/winlogbeat/docs/fields.asciidoc b/winlogbeat/docs/fields.asciidoc index ca76cfc10a8..da449fcbf75 100644 --- a/winlogbeat/docs/fields.asciidoc +++ b/winlogbeat/docs/fields.asciidoc @@ -3303,16 +3303,6 @@ type: keyword Major version of the user agent. --- - -*`user_agent.device`*:: -+ --- -type: keyword - -Name of the physical device. - - -- *`user_agent.os.major`*:: @@ -3333,16 +3323,6 @@ type: long Minor version of the operating system. --- - -*`url.hostname`*:: -+ --- -type: keyword - -Hostname of the request, such as "elastic.co". - - -- [[exported-fields-eventlog]] diff --git a/winlogbeat/include/fields.go b/winlogbeat/include/fields.go index 1544ba81bee..1757d10a0fd 100644 --- a/winlogbeat/include/fields.go +++ b/winlogbeat/include/fields.go @@ -32,5 +32,5 @@ func init() { // AssetFieldsYml returns asset data. // This is the base64 encoded gzipped contents of fields.yml. func AssetFieldsYml() string { - return "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" + return "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" } diff --git a/winlogbeat/tests/system/test_config.py b/winlogbeat/tests/system/test_config.py index 13b51694cd6..16862bf44f9 100644 --- a/winlogbeat/tests/system/test_config.py +++ b/winlogbeat/tests/system/test_config.py @@ -1,3 +1,5 @@ +import os +import subprocess import sys import unittest from winlogbeat import BaseTest @@ -20,7 +22,7 @@ def test_valid_config(self): {"name": "Application", "ignore_older": "48h"} ] ) - self.start_beat(extra_args=["-configtest"]).check_wait() + self.run_config_tst(exit_code=0) def test_invalid_ignore_older(self): """ @@ -31,7 +33,7 @@ def test_invalid_ignore_older(self): {"name": "Application", "ignore_older": "1 hour"} ] ) - self.start_beat(extra_args=["-configtest"]).check_wait(exit_code=1) + self.run_config_tst(exit_code=1) assert self.log_contains( "unknown unit hour in duration 1 hour " "accessing 'winlogbeat.event_logs.0.ignore_older'") @@ -45,7 +47,7 @@ def test_invalid_level(self): {"name": "Application", "level": "errors"} ] ) - self.start_beat(extra_args=["-configtest"]).check_wait(exit_code=1) + self.run_config_tst(exit_code=1) assert self.log_contains( "invalid level ('errors') for query") @@ -58,6 +60,41 @@ def test_invalid_api(self): {"name": "Application", "api": "file"} ] ) - self.start_beat(extra_args=["-configtest"]).check_wait(exit_code=1) + self.run_config_tst(exit_code=1) assert self.log_contains("Failed to create new event log. " "file API is not available") + + def run_config_tst(self, pcap=None, exit_code=0): + config = "winlogbeat.yml" + + cmd = os.path.join(self.beat_path, "winlogbeat.test") + args = [ + cmd, "-systemTest", + "-c", os.path.join(self.working_dir, config), + ] + + if os.getenv("TEST_COVERAGE") == "true": + args += [ + "-test.coverprofile", + os.path.join(self.working_dir, "coverage.cov"), + ] + + args.extend(["test", "config"]) + + output = "winlogbeat.log" + + with open(os.path.join(self.working_dir, output), "wb") as outfile: + proc = subprocess.Popen(args, + stdout=outfile, + stderr=subprocess.STDOUT + ) + actual_exit_code = proc.wait() + + if actual_exit_code != exit_code: + print("============ Log Output =====================") + with open(os.path.join(self.working_dir, output)) as f: + print(f.read()) + print("============ Log End Output =====================") + assert actual_exit_code == exit_code, "Expected exit code to be %d, but it was %d" % ( + exit_code, actual_exit_code) + return actual_exit_code diff --git a/winlogbeat/tests/system/test_eventlogging.py b/winlogbeat/tests/system/test_eventlogging.py index adfbef28078..2cc635bdc4e 100644 --- a/winlogbeat/tests/system/test_eventlogging.py +++ b/winlogbeat/tests/system/test_eventlogging.py @@ -169,7 +169,7 @@ def test_unknown_eventlog_config(self): } ] ) - self.start_beat(extra_args=["-configtest"]).check_wait(exit_code=1) + self.start_beat().check_wait(exit_code=1) assert self.log_contains("4 errors: Invalid event log key") def test_utf16_characters(self): diff --git a/winlogbeat/tests/system/test_wineventlog.py b/winlogbeat/tests/system/test_wineventlog.py index 757bdbc7e18..fc71c9d1693 100644 --- a/winlogbeat/tests/system/test_wineventlog.py +++ b/winlogbeat/tests/system/test_wineventlog.py @@ -170,7 +170,8 @@ def test_include_xml(self): self.assertTrue(len(evts), 1) self.assert_common_fields(evts[0], msg=msg) self.assertTrue("xml" in evts[0]) - self.assertTrue(evts[0]["xml"].endswith(''), 'xml value: "{}"'.format(evts[0]["xml"])) + self.assertTrue(evts[0]["xml"].endswith(''), + 'xml value: "{}"'.format(evts[0]["xml"])) def test_query_event_id(self): """ @@ -205,8 +206,10 @@ def test_query_level_single(self): """ self.write_event_log("success", level=win32evtlog.EVENTLOG_SUCCESS) self.write_event_log("error", level=win32evtlog.EVENTLOG_ERROR_TYPE) - self.write_event_log("warning", level=win32evtlog.EVENTLOG_WARNING_TYPE) - self.write_event_log("information", level=win32evtlog.EVENTLOG_INFORMATION_TYPE) + self.write_event_log( + "warning", level=win32evtlog.EVENTLOG_WARNING_TYPE) + self.write_event_log( + "information", level=win32evtlog.EVENTLOG_INFORMATION_TYPE) evts = self.read_events(config={ "event_logs": [ { @@ -223,10 +226,14 @@ def test_query_level_multiple(self): """ wineventlog - Query by level (error, warning) """ - self.write_event_log("success", level=win32evtlog.EVENTLOG_SUCCESS) # Level 0, Info - self.write_event_log("error", level=win32evtlog.EVENTLOG_ERROR_TYPE) # Level 2 - self.write_event_log("warning", level=win32evtlog.EVENTLOG_WARNING_TYPE) # Level 3 - self.write_event_log("information", level=win32evtlog.EVENTLOG_INFORMATION_TYPE) # Level 4 + self.write_event_log( + "success", level=win32evtlog.EVENTLOG_SUCCESS) # Level 0, Info + self.write_event_log( + "error", level=win32evtlog.EVENTLOG_ERROR_TYPE) # Level 2 + self.write_event_log( + "warning", level=win32evtlog.EVENTLOG_WARNING_TYPE) # Level 3 + self.write_event_log( + "information", level=win32evtlog.EVENTLOG_INFORMATION_TYPE) # Level 4 evts = self.read_events(config={ "event_logs": [ { @@ -284,7 +291,8 @@ def test_query_multi_param(self): self.write_event_log("selected", source=self.otherAppName, eventID=556, level=win32evtlog.EVENTLOG_ERROR_TYPE) self.write_event_log("filtered", source=self.otherAppName, eventID=556) - self.write_event_log("filtered", level=win32evtlog.EVENTLOG_WARNING_TYPE) + self.write_event_log( + "filtered", level=win32evtlog.EVENTLOG_WARNING_TYPE) evts = self.read_events(config={ "event_logs": [ { @@ -312,8 +320,9 @@ def test_unknown_eventlog_config(self): "invalid": "garbage"} ] ) - self.start_beat(extra_args=["-configtest"]).check_wait(exit_code=1) - assert self.log_contains("1 error: Invalid event log key 'invalid' found.") + self.start_beat().check_wait(exit_code=1) + assert self.log_contains( + "1 error: Invalid event log key 'invalid' found.") def test_utf16_characters(self): """ diff --git a/winlogbeat/winlogbeat.reference.yml b/winlogbeat/winlogbeat.reference.yml index 1e797e44582..ec534c84fcf 100644 --- a/winlogbeat/winlogbeat.reference.yml +++ b/winlogbeat/winlogbeat.reference.yml @@ -275,11 +275,6 @@ output.elasticsearch: # IPv6 addresses should always be defined as: https://[2001:db8::1]:9200 hosts: ["localhost:9200"] - # Enabled ilm (beta) to use index lifecycle management instead daily indices. - #ilm.enabled: false - #ilm.rollover_alias: "winlogbeat" - #ilm.pattern: "{now/d}-000001" - # Set gzip compression level. #compression_level: 0 @@ -926,6 +921,25 @@ setup.template.settings: #_source: #enabled: false +#============================== Setup ILM ===================================== + +# Configure Index Lifecycle Management Index Lifecycle Management creates a +# write alias and adds additional settings to the template. +# The elasticsearch.output.index setting will be replaced with the write alias +# if ILM is enabled. + +# Enabled ILM support. Valid values are true, false, and auto. The beat will +# detect availabilty of Index Lifecycle Management in Elasticsearch and enable +# or disable ILM support. +#setup.ilm.enabled: auto + +# Configure the ILM write alias name. +#setup.ilm.rollover_alias: "winlogbeat" + +# Configure rollover index pattern. +#setup.ilm.pattern: "{now/d}-000001" + + #============================== Kibana ===================================== # Starting with Beats version 6.0.0, the dashboards are loaded via the Kibana API. diff --git a/winlogbeat/winlogbeat.yml b/winlogbeat/winlogbeat.yml index f310887207c..f0cbb3092ed 100644 --- a/winlogbeat/winlogbeat.yml +++ b/winlogbeat/winlogbeat.yml @@ -26,7 +26,7 @@ winlogbeat.event_logs: #==================== Elasticsearch template setting ========================== setup.template.settings: - index.number_of_shards: 3 + index.number_of_shards: 1 #index.codec: best_compression #_source.enabled: false @@ -49,7 +49,7 @@ setup.template.settings: #============================== Dashboards ===================================== # These settings control loading the sample dashboards to the Kibana index. Loading # the dashboards is disabled by default and can be enabled either by setting the -# options here, or by using the `-setup` CLI flag or the `setup` command. +# options here or by using the `setup` command. #setup.dashboards.enabled: false # The URL from where to download the dashboards archive. By default this URL @@ -97,9 +97,6 @@ output.elasticsearch: # Array of hosts to connect to. hosts: ["localhost:9200"] - # Enabled ilm (beta) to use index lifecycle management instead daily indices. - #ilm.enabled: false - # Optional protocol and basic auth credentials. #protocol: "https" #username: "elastic" diff --git a/x-pack/auditbeat/auditbeat.reference.yml b/x-pack/auditbeat/auditbeat.reference.yml index cc483d28c2b..239da5691a1 100644 --- a/x-pack/auditbeat/auditbeat.reference.yml +++ b/x-pack/auditbeat/auditbeat.reference.yml @@ -117,6 +117,8 @@ auditbeat.modules: - module: system datasets: - host # General host information, e.g. uptime, IPs + - login # User logins, logouts, and system boots. + - package # Installed, updated, and removed packages - process # Started and stopped processes - socket # Opened and closed sockets - user # User information @@ -128,6 +130,7 @@ auditbeat.modules: # The state.period can be overridden for any dataset. # host.state.period: 12h + # package.state.period: 12h # process.state.period: 12h # socket.state.period: 12h # user.state.period: 12h @@ -137,6 +140,12 @@ auditbeat.modules: # detect any changes. user.detect_password_changes: true + # File patterns of the login record files. + # wtmp: History of successful logins, logouts, and system shutdowns and boots. + # btmp: Failed login attempts. + login.wtmp_file_pattern: /var/log/wtmp* + login.btmp_file_pattern: /var/log/btmp* + #================================ General ====================================== # The name of the shipper that publishes the network data. It can be used to group @@ -384,11 +393,6 @@ output.elasticsearch: # IPv6 addresses should always be defined as: https://[2001:db8::1]:9200 hosts: ["localhost:9200"] - # Enabled ilm (beta) to use index lifecycle management instead daily indices. - #ilm.enabled: false - #ilm.rollover_alias: "auditbeat" - #ilm.pattern: "{now/d}-000001" - # Set gzip compression level. #compression_level: 0 @@ -1035,6 +1039,25 @@ setup.template.settings: #_source: #enabled: false +#============================== Setup ILM ===================================== + +# Configure Index Lifecycle Management Index Lifecycle Management creates a +# write alias and adds additional settings to the template. +# The elasticsearch.output.index setting will be replaced with the write alias +# if ILM is enabled. + +# Enabled ILM support. Valid values are true, false, and auto. The beat will +# detect availabilty of Index Lifecycle Management in Elasticsearch and enable +# or disable ILM support. +#setup.ilm.enabled: auto + +# Configure the ILM write alias name. +#setup.ilm.rollover_alias: "auditbeat" + +# Configure rollover index pattern. +#setup.ilm.pattern: "{now/d}-000001" + + #============================== Kibana ===================================== # Starting with Beats version 6.0.0, the dashboards are loaded via the Kibana API. diff --git a/x-pack/auditbeat/auditbeat.yml b/x-pack/auditbeat/auditbeat.yml index 0bd74c6efe9..bd5296e0c5f 100644 --- a/x-pack/auditbeat/auditbeat.yml +++ b/x-pack/auditbeat/auditbeat.yml @@ -50,6 +50,8 @@ auditbeat.modules: - module: system datasets: - host # General host information, e.g. uptime, IPs + - login # User logins, logouts, and system boots. + - package # Installed, updated, and removed packages - process # Started and stopped processes - socket # Opened and closed sockets - user # User information @@ -64,9 +66,13 @@ auditbeat.modules: # detect any changes. user.detect_password_changes: true + # File patterns of the login record files. + login.wtmp_file_pattern: /var/log/wtmp* + login.btmp_file_pattern: /var/log/btmp* + #==================== Elasticsearch template setting ========================== setup.template.settings: - index.number_of_shards: 3 + index.number_of_shards: 1 #index.codec: best_compression #_source.enabled: false @@ -89,7 +95,7 @@ setup.template.settings: #============================== Dashboards ===================================== # These settings control loading the sample dashboards to the Kibana index. Loading # the dashboards is disabled by default and can be enabled either by setting the -# options here, or by using the `-setup` CLI flag or the `setup` command. +# options here or by using the `setup` command. #setup.dashboards.enabled: false # The URL from where to download the dashboards archive. By default this URL @@ -137,9 +143,6 @@ output.elasticsearch: # Array of hosts to connect to. hosts: ["localhost:9200"] - # Enabled ilm (beta) to use index lifecycle management instead daily indices. - #ilm.enabled: false - # Optional protocol and basic auth credentials. #protocol: "https" #username: "elastic" diff --git a/x-pack/auditbeat/docs/modules/system.asciidoc b/x-pack/auditbeat/docs/modules/system.asciidoc index 64198c06d27..d6f902d07df 100644 --- a/x-pack/auditbeat/docs/modules/system.asciidoc +++ b/x-pack/auditbeat/docs/modules/system.asciidoc @@ -51,6 +51,8 @@ sample suggested configuration. - module: system datasets: - host + - login + - package - process - socket - user @@ -86,6 +88,8 @@ so a longer polling interval can be used. - module: system datasets: - host + - login + - package - user period: 1m user.detect_password_changes: true @@ -111,6 +115,8 @@ auditbeat.modules: - module: system datasets: - host # General host information, e.g. uptime, IPs + - login # User logins, logouts, and system boots. + - package # Installed, updated, and removed packages - process # Started and stopped processes - socket # Opened and closed sockets - user # User information @@ -124,6 +130,10 @@ auditbeat.modules: # /etc/passwd and /etc/shadow and store a hash locally to # detect any changes. user.detect_password_changes: true + + # File patterns of the login record files. + login.wtmp_file_pattern: /var/log/wtmp* + login.btmp_file_pattern: /var/log/btmp* ---- [float] @@ -133,6 +143,10 @@ The following datasets are available: * <<{beatname_lc}-dataset-system-host,host>> +* <<{beatname_lc}-dataset-system-login,login>> + +* <<{beatname_lc}-dataset-system-package,package>> + * <<{beatname_lc}-dataset-system-process,process>> * <<{beatname_lc}-dataset-system-socket,socket>> @@ -141,6 +155,10 @@ The following datasets are available: include::system/host.asciidoc[] +include::system/login.asciidoc[] + +include::system/package.asciidoc[] + include::system/process.asciidoc[] include::system/socket.asciidoc[] diff --git a/x-pack/auditbeat/docs/modules/system/login.asciidoc b/x-pack/auditbeat/docs/modules/system/login.asciidoc new file mode 100644 index 00000000000..d042abb7add --- /dev/null +++ b/x-pack/auditbeat/docs/modules/system/login.asciidoc @@ -0,0 +1,21 @@ +//// +This file is generated! See scripts/docs_collector.py +//// + +[id="{beatname_lc}-dataset-system-login"] +=== System login dataset + +include::../../../module/system/login/_meta/docs.asciidoc[] + + +==== Fields + +For a description of each field in the dataset, see the +<> section. + +Here is an example document generated by this dataset: + +[source,json] +---- +include::../../../module/system/login/_meta/data.json[] +---- diff --git a/x-pack/auditbeat/docs/modules/system/package.asciidoc b/x-pack/auditbeat/docs/modules/system/package.asciidoc new file mode 100644 index 00000000000..ec87bd976da --- /dev/null +++ b/x-pack/auditbeat/docs/modules/system/package.asciidoc @@ -0,0 +1,21 @@ +//// +This file is generated! See scripts/docs_collector.py +//// + +[id="{beatname_lc}-dataset-system-package"] +=== System package dataset + +include::../../../module/system/package/_meta/docs.asciidoc[] + + +==== Fields + +For a description of each field in the dataset, see the +<> section. + +Here is an example document generated by this dataset: + +[source,json] +---- +include::../../../module/system/package/_meta/data.json[] +---- diff --git a/x-pack/auditbeat/include/list.go b/x-pack/auditbeat/include/list.go index d903941baef..1bfa48fc08f 100644 --- a/x-pack/auditbeat/include/list.go +++ b/x-pack/auditbeat/include/list.go @@ -10,7 +10,8 @@ import ( // Import packages that need to register themselves. _ "github.com/elastic/beats/x-pack/auditbeat/module/system" _ "github.com/elastic/beats/x-pack/auditbeat/module/system/host" - _ "github.com/elastic/beats/x-pack/auditbeat/module/system/packages" + _ "github.com/elastic/beats/x-pack/auditbeat/module/system/login" + _ "github.com/elastic/beats/x-pack/auditbeat/module/system/package" _ "github.com/elastic/beats/x-pack/auditbeat/module/system/process" _ "github.com/elastic/beats/x-pack/auditbeat/module/system/socket" _ "github.com/elastic/beats/x-pack/auditbeat/module/system/user" diff --git a/x-pack/auditbeat/magefile.go b/x-pack/auditbeat/magefile.go index b2c8ac9e8b7..af8e08f1957 100644 --- a/x-pack/auditbeat/magefile.go +++ b/x-pack/auditbeat/magefile.go @@ -12,6 +12,8 @@ import ( "time" "github.com/magefile/mage/mg" + "github.com/magefile/mage/sh" + "github.com/pkg/errors" auditbeat "github.com/elastic/beats/auditbeat/scripts/mage" "github.com/elastic/beats/dev-tools/mage" @@ -20,6 +22,7 @@ import ( func init() { mage.BeatDescription = "Audit the activities of users and processes on your system." mage.BeatLicense = "Elastic License" + mage.Platforms = mage.Platforms.Filter("!linux/ppc64 !linux/mips64") } // Aliases provides compatibility with CI while we transition all Beats @@ -36,6 +39,9 @@ func Build() error { // GolangCrossBuild build the Beat binary inside of the golang-builder. // Do not use directly, use crossBuild instead. func GolangCrossBuild() error { + if d, ok := deps[mage.Platform.Name]; ok { + mg.Deps(d) + } return mage.GolangCrossBuild(mage.DefaultGolangCrossBuildArgs()) } @@ -173,3 +179,83 @@ func PythonIntegTest(ctx context.Context) error { return mage.PythonNoseTest(mage.DefaultPythonTestIntegrationArgs()) }) } + +// ----------------------------------------------------------------------------- +// - Install the librpm-dev package +var ( + deps = map[string]func() error{ + "linux/386": installLinux386, + "linux/amd64": installLinuxAMD64, + "linux/arm64": installLinuxARM64, + "linux/armv5": installLinuxARMLE, + "linux/armv6": installLinuxARMLE, + "linux/armv7": installLinuxARMHF, + "linux/mips": installLinuxMIPS, + "linux/mipsle": installLinuxMIPSLE, + "linux/mips64le": installLinuxMIPS64LE, + "linux/ppc64le": installLinuxPPC64LE, + "linux/s390x": installLinuxS390X, + + //"linux/ppc64": installLinuxPpc64, + //"linux/mips64": installLinuxMips64, + } +) + +const ( + librpmDevPkgName = "librpm-dev" +) + +func installLinuxAMD64() error { + return installDependencies(librpmDevPkgName, "") +} + +func installLinuxARM64() error { + return installDependencies(librpmDevPkgName+":arm64", "arm64") +} + +func installLinuxARMHF() error { + return installDependencies(librpmDevPkgName+":armhf", "armhf") +} + +func installLinuxARMLE() error { + return installDependencies(librpmDevPkgName+":armel", "armel") +} + +func installLinux386() error { + return installDependencies(librpmDevPkgName+":i386", "i386") +} + +func installLinuxMIPS() error { + return installDependencies(librpmDevPkgName+":mips", "mips") +} + +func installLinuxMIPS64LE() error { + return installDependencies(librpmDevPkgName+":mips64el", "mips64el") +} + +func installLinuxMIPSLE() error { + return installDependencies(librpmDevPkgName+":mipsel", "mipsel") +} + +func installLinuxPPC64LE() error { + return installDependencies(librpmDevPkgName+":ppc64el", "ppc64el") +} + +func installLinuxS390X() error { + return installDependencies(librpmDevPkgName+":s390x", "s390x") +} + +func installDependencies(pkg, arch string) error { + if arch != "" { + err := sh.Run("dpkg", "--add-architecture", arch) + if err != nil { + return errors.Wrap(err, "error while adding architecture") + } + } + + if err := sh.Run("apt-get", "update"); err != nil { + return err + } + + return sh.Run("apt-get", "install", "-y", "--no-install-recommends", pkg) +} diff --git a/x-pack/auditbeat/module/system/_meta/config.yml.tmpl b/x-pack/auditbeat/module/system/_meta/config.yml.tmpl index f06f53cab3e..6e7bf4a44b7 100644 --- a/x-pack/auditbeat/module/system/_meta/config.yml.tmpl +++ b/x-pack/auditbeat/module/system/_meta/config.yml.tmpl @@ -7,9 +7,12 @@ - module: system datasets: - host # General host information, e.g. uptime, IPs - {{ if false -}} - - packages # Installed packages - {{- end -}} + {{ if eq .GOOS "linux" -}} + - login # User logins, logouts, and system boots. + {{- end }} + {{ if ne .GOOS "windows" -}} + - package # Installed, updated, and removed packages + {{- end }} - process # Started and stopped processes {{ if eq .GOOS "linux" -}} - socket # Opened and closed sockets @@ -23,6 +26,9 @@ {{ if .Reference }} # The state.period can be overridden for any dataset. # host.state.period: 12h + {{ if ne .GOOS "windows" -}} + # package.state.period: 12h + {{- end }} # process.state.period: 12h {{ if eq .GOOS "linux" -}} # socket.state.period: 12h @@ -35,3 +41,13 @@ # detect any changes. user.detect_password_changes: true {{- end }} + + {{ if eq .GOOS "linux" -}} + # File patterns of the login record files. +{{- if .Reference }} + # wtmp: History of successful logins, logouts, and system shutdowns and boots. + # btmp: Failed login attempts. +{{- end }} + login.wtmp_file_pattern: /var/log/wtmp* + login.btmp_file_pattern: /var/log/btmp* + {{- end }} diff --git a/x-pack/auditbeat/module/system/_meta/docs.asciidoc b/x-pack/auditbeat/module/system/_meta/docs.asciidoc index 4dc42e8c17d..55614c4d69f 100644 --- a/x-pack/auditbeat/module/system/_meta/docs.asciidoc +++ b/x-pack/auditbeat/module/system/_meta/docs.asciidoc @@ -46,6 +46,8 @@ sample suggested configuration. - module: system datasets: - host + - login + - package - process - socket - user @@ -81,6 +83,8 @@ so a longer polling interval can be used. - module: system datasets: - host + - login + - package - user period: 1m user.detect_password_changes: true diff --git a/x-pack/auditbeat/module/system/_meta/fields.yml b/x-pack/auditbeat/module/system/_meta/fields.yml index f27178a0931..fd47f2be771 100644 --- a/x-pack/auditbeat/module/system/_meta/fields.yml +++ b/x-pack/auditbeat/module/system/_meta/fields.yml @@ -4,7 +4,25 @@ These are the fields generated by the system module. release: experimental fields: - - name: system.audit - type: group + + - name: event + type: group + fields: + - name: origin + type: keyword description: > - fields: + Origin of the event. This can be a file path (e.g. `/var/log/log.1`), + or the name of the system component that supplied the data (e.g. `netlink`). + + - name: user + type: group + fields: + - name: terminal + type: keyword + description: > + Terminal of the user. + + - name: system.audit + type: group + description: > + fields: diff --git a/x-pack/auditbeat/module/system/fields.go b/x-pack/auditbeat/module/system/fields.go index 8608bc90198..0719e4b4ac6 100644 --- a/x-pack/auditbeat/module/system/fields.go +++ b/x-pack/auditbeat/module/system/fields.go @@ -19,5 +19,5 @@ func init() { // AssetSystem returns asset data. // This is the base64 encoded gzipped contents of module/system. func AssetSystem() string { - return "eJy0WE2P2zYQvftXDHLZXcBRLkVQ+FAgaYE2QNMssBugN3skjiR2JY7AoeJVfn1BfdmyKTvOenXz2H7vcd48kvZbeKJmBdKIo3IB4LQraAVvHtrCmwWAIkmsrpxms4LfFgAAjzkJAVoClxOkmgolkJEhi44UxE1b7zChZFUXFC0ALBWEQiug54qsLsk4LBbQA6xa6LdgsKRBUIS10q59A8A1Fa0gs1xXfSUgbYq2j5izuLEYQptF7J6NB9hAwsahNsN6ixYXtEnZlui/F+19a27Fw3Oodae2rpwuaSKgU1ywySblE5L987UFAm3AoGGhhI2SKMAYM7sZToWOLuH8yOzAY4V4+gaS1d9JBchi5oLQXML3QA502jshgDuOkAAv7DsbivzLgIAnarZs1SUC/sGSgNN27Af44bVXtQSKsgg+PjyeFMRpKuQioeQKxj/udHhUPwEn3Pcqr9ePv3q0EJMOmf6THPDpjxAF2iTXjhJX2ysuaAILt62jz7++X7//5S4kosSQiz/B/fnD74BKWRKhoHe6ChAdFM9wfLo/TcESoDjcPM+wbFj2ts+9HRMw5tq1YeHKnyHaZMMBMME43i53CqsCnUc8IJ3v+tmefHkYQXu3EzKOZQl1XBtXL2GrjeKt3EVBRUdxeqkaD9gr+YyJr/w7Q51iqYvmquQdZE9vSeXolqAo1miWkFqiWNS5jnwjK5rNVXX1mGHCJ7KGiuvxPQZG9EZ6mmMp42EuZF909fAAZ7PjPyTgS4HwXH4Tea1BBvgqZOHweNjrlj5EfDnb9JzYcWXX5frTezpLprS99sJuBHIuyUNT4tg2YWbJqbhiDADuLWcWS3AMtjaADgrO9EwK/WCu92Y2KMTRs7u43f0V3BNMruDwxcDf2tTPS3C5Fn8p9AnJKGHpRn1mHI5OtEEex/9RcqHATQt3Mrdomo5Uxgh7sRVa5++PtzE1bNT43o1AZXWJtv/WwT4bjjGcjjKcmYUfcgLG4T/ONZzM245eG0cZHUbkQvq57FUoEljc3EXmvLcD4Gl7R9f6T8Ot4W63HivaCRXpxU565a/l5Icj2R42gnsW0XFB8A2LmqT9B2AjOSrersd+zGDeThado+R+0LXp/ipoMSDVBd0td71dKy0YF6Q2yxnUjeEds+fowq7QZGS5FkDxGfM/gxI0fpMCbe6WgCbUnBYxsU3l9kG3OZmpZa03Xvs7csm7tqxAiEqZAXU8TAmgATItB6kR8cj9nc8FilsnuV/QfHSOfqB3zw+Z7e80CpvJHjMsdIvSCoBeQLT4PwAA///ff7jT" + return "eJy0WF+L27gXfc+nuPSlE0g9/OBHWfKw0O7CbqHdDjsp7NtEtm5s7ci6RleeTPrpF8l2YidyMum4hkKjkc45uv90pXfwiLsl8I4dljMAp5zGJby5DwNvZgASObOqcorMEn6dAQCsCmQEYRFcgbBRqCVDjgatcCgh3YXxBhNKkrXGZAZgUaNgXAI+V2hVicYJPYMWYDmbAbwDI0o/4wmNC1xuV+ESckt1FX53k/3/u9lkVa5MGOoWPOJuS1a2Y5E9+O9rWAe0CXoDZwKrQjFkwkCKIGCjNEIlXAE3mOQJrG+fhL3VlPt/yf/W88UejWyA8ZI6yNYEGZUVGTQOXCEccF1VWqEMU6RwosM26LQyj+t50rdFzWhfbAqHtlQmmPVKY6zalZ12TzvQ0WwmEbVUcddEgPsS+zILYrcfPEU6K9R/aw+whoyME8p0wacDLiizIVsKvy7prRoLv+471trzQOVUiQMBjWJNJh8Mn5Hsv28BCJQBIwwxZmQkJxHGlMiNcErh8BrOj0QOPFaMpzUgWvUdZYQsJdIozDV89+hAbVpPMIgDR0yAF/adDCb+Z0TAMHRfJOCvXgJ28N1vr2oBIds+3q/OCqLNhtEljNkEjl8ddHhUHwFnvO9VTmePP1u0GJOKOf0HOeDT7zEKYbNCOcxcbSfc0AC2rZ/Pv7x/eP//eUxEKWJe/AHuLx9+AyGlRWaM+k5VEaKjwQscn+7OUxBHKI6L5wWWNXGvfPYqJoiUaheShSp/oCuTd6V/gHFaLg8KKy2cRzwiHbf6RZt8vd+Dtt7O0DjiBdRpbVy9gK0ykrY8T6KKTtLptWrCMd8o+SIyP/LPCPVGlErvJiVvIFt6i7IQbgESUyXMAjYWMWV5ySJPaFmRmVRXixknfERrUE/Ht4qE6FtuaU6l7GNTZI8ix1d1Hy3G2QwSBpRhJ7RG6XtDiyU9oez4p+lMpjol7hpRMHZOxILllVSRWOnYWntMydZCjh1QU1L1T6YYn1YZmml310JGz6MmBidpJju6FnO0q2T1/fXdckfmwaIkdVkKu/sBwGZhDLO2ekq3fPv782n92V/n+hTXFB8PcPHs9pMY/FDk8L6+3vysgxTgG6M9KTs9a6ljxNezDfvUA1c+Ldcf3qejZFLZqTf2lqGgEj00Zo6GId5LnAL1hMcwwJ2l3IoSHIGtDQgHmnI10gX4wHzoxWxUiMNnd7W52ycATzB4AoCvBj4rUz8vwBWK/aXUZ0iOGXET6iPhcNJRd/Io/RezKwWuA9yFjmHXkPI+hb3YSljn7683Ke7IyP3f3jJUVvlS1qw66vPiaQznUxkuxMKLPAH74D/Nazibbwd6ZRzmeJwiV9KP5V4lmCObG7tIXfZtB3jevXuvtbPhxlBTrfcjyjHqzdWe9Mp/lic/nMj2sAncEbNKNcKT0DVyeA5ecyEkbR/29hjBvBlsuhBc+EBXpnk0DRjh5XW+ONj2QSoWqUa5Xoygrg0dmD1Hk+xSmBwt1QyCfY6RwfC+qykHZeYLECZmnICY2V3l+qDbAs3QZcE3Xvstuuw2DEtgxJJHQB11UeLvCGgCR7gYNIgn3u+1joLdQ1b4DY2nzklP13wvcvYqvEjvBjWm2+hWcBAArYBk9l8AAAD//y0/V6s=" } diff --git a/x-pack/auditbeat/module/system/login/_meta/data.json b/x-pack/auditbeat/module/system/login/_meta/data.json new file mode 100644 index 00000000000..311893d5926 --- /dev/null +++ b/x-pack/auditbeat/module/system/login/_meta/data.json @@ -0,0 +1,30 @@ +{ + "@timestamp": "2017-10-12T08:05:34.853Z", + "agent": { + "hostname": "host.example.com", + "name": "host.example.com" + }, + "event": { + "action": "user_login", + "dataset": "login", + "module": "system", + "origin": "/var/log/wtmp.1", + "outcome": "success", + "type": "event" + }, + "message": "Login by user vagrant (UID: 1000) on pts/1 (PID: 17559) from 10.0.2.2 (IP: 10.0.2.2)", + "process": { + "pid": 17559 + }, + "service": { + "type": "system" + }, + "source": { + "ip": "10.0.2.2" + }, + "user": { + "id": 1000, + "name": "vagrant", + "terminal": "pts/1" + } +} diff --git a/x-pack/auditbeat/module/system/login/_meta/docs.asciidoc b/x-pack/auditbeat/module/system/login/_meta/docs.asciidoc new file mode 100644 index 00000000000..ea1eff21763 --- /dev/null +++ b/x-pack/auditbeat/module/system/login/_meta/docs.asciidoc @@ -0,0 +1,7 @@ +[role="xpack"] + +experimental[] + +This is the `login` dataset of the system module. + +It is implemented for Linux only. diff --git a/x-pack/auditbeat/module/system/login/config.go b/x-pack/auditbeat/module/system/login/config.go new file mode 100644 index 00000000000..2cdfca54904 --- /dev/null +++ b/x-pack/auditbeat/module/system/login/config.go @@ -0,0 +1,20 @@ +// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +// or more contributor license agreements. Licensed under the Elastic License; +// you may not use this file except in compliance with the Elastic License. + +// +build linux + +package login + +// config defines the metricset's configuration options. +type config struct { + WtmpFilePattern string `config:"login.wtmp_file_pattern"` + BtmpFilePattern string `config:"login.btmp_file_pattern"` +} + +func defaultConfig() config { + return config{ + WtmpFilePattern: "/var/log/wtmp*", + BtmpFilePattern: "/var/log/btmp*", + } +} diff --git a/x-pack/auditbeat/module/system/login/login.go b/x-pack/auditbeat/module/system/login/login.go new file mode 100644 index 00000000000..7af40d1e6a1 --- /dev/null +++ b/x-pack/auditbeat/module/system/login/login.go @@ -0,0 +1,232 @@ +// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +// or more contributor license agreements. Licensed under the Elastic License; +// you may not use this file except in compliance with the Elastic License. + +// +build linux + +package login + +import ( + "fmt" + "net" + "time" + + "github.com/pkg/errors" + + "github.com/elastic/beats/auditbeat/datastore" + "github.com/elastic/beats/libbeat/common" + "github.com/elastic/beats/libbeat/common/cfgwarn" + "github.com/elastic/beats/libbeat/logp" + "github.com/elastic/beats/metricbeat/mb" +) + +const ( + moduleName = "system" + metricsetName = "login" + namespace = "system.audit.login" + + bucketName = "login.v1" + + eventTypeEvent = "event" +) + +// loginRecordType represents the type of a login record. +type loginRecordType uint8 + +const ( + bootRecord loginRecordType = iota + 1 + shutdownRecord + userLoginRecord + userLogoutRecord + userLoginFailedRecord +) + +// String returns the string representation of a LoginRecordType. +func (t loginRecordType) string() string { + switch t { + case bootRecord: + return "boot" + case shutdownRecord: + return "shutdown" + + case userLoginFailedRecord: + fallthrough + case userLoginRecord: + return "user_login" + + case userLogoutRecord: + return "user_logout" + default: + return "" + } +} + +// LoginRecord represents a login record. +type LoginRecord struct { + Utmp *Utmp + Type loginRecordType + PID int + TTY string + UID int + Username string + Hostname string + IP *net.IP + Timestamp time.Time + Origin string +} + +func init() { + mb.Registry.MustAddMetricSet(moduleName, metricsetName, New, + mb.DefaultMetricSet(), + mb.WithNamespace(namespace), + ) +} + +// MetricSet collects login records from /var/log/wtmp. +type MetricSet struct { + mb.BaseMetricSet + config config + log *logp.Logger + utmpReader *UtmpFileReader +} + +// New constructs a new MetricSet. +func New(base mb.BaseMetricSet) (mb.MetricSet, error) { + cfgwarn.Experimental("The %v/%v dataset is experimental", moduleName, metricsetName) + + config := defaultConfig() + if err := base.Module().UnpackConfig(&config); err != nil { + return nil, errors.Wrapf(err, "failed to unpack the %v/%v config", moduleName, metricsetName) + } + + bucket, err := datastore.OpenBucket(bucketName) + if err != nil { + return nil, errors.Wrap(err, "failed to open persistent datastore") + } + + ms := &MetricSet{ + BaseMetricSet: base, + config: config, + log: logp.NewLogger(metricsetName), + } + + ms.utmpReader, err = NewUtmpFileReader(ms.log, bucket, config) + if err != nil { + return nil, err + } + + return ms, nil +} + +// Close cleans up the MetricSet when it finishes. +func (ms *MetricSet) Close() error { + return ms.utmpReader.Close() +} + +// Fetch collects any new login records from /var/log/wtmp. It is invoked periodically. +func (ms *MetricSet) Fetch(report mb.ReporterV2) { + count := ms.readAndEmit(report) + + ms.log.Debugf("%d new login records.", count) + + // Save new state to disk + if count > 0 { + err := ms.utmpReader.saveStateToDisk() + if err != nil { + ms.log.Error(err) + report.Error(err) + } + } +} + +// readAndEmit reads and emits login events and returns the number of events. +func (ms *MetricSet) readAndEmit(report mb.ReporterV2) int { + loginRecordC, errorC := ms.utmpReader.ReadNew() + + var count int + for { + select { + case loginRecord, ok := <-loginRecordC: + if !ok { + return count + } + report.Event(ms.loginEvent(&loginRecord)) + count++ + case err, ok := <-errorC: + if !ok { + return count + } + ms.log.Error(err) + } + } +} + +func (ms *MetricSet) loginEvent(loginRecord *LoginRecord) mb.Event { + event := mb.Event{ + Timestamp: loginRecord.Timestamp, + RootFields: common.MapStr{ + "event": common.MapStr{ + "type": eventTypeEvent, + "action": loginRecord.Type.string(), + "origin": loginRecord.Origin, + }, + "message": loginMessage(loginRecord), + // Very useful for development + // "debug": fmt.Sprintf("%v", login.Utmp), + }, + } + + if loginRecord.Username != "" { + event.RootFields.Put("user.name", loginRecord.Username) + + if loginRecord.UID != -1 { + event.RootFields.Put("user.id", loginRecord.UID) + } + } + + if loginRecord.TTY != "" { + event.RootFields.Put("user.terminal", loginRecord.TTY) + } + + if loginRecord.PID != -1 { + event.RootFields.Put("process.pid", loginRecord.PID) + } + + if loginRecord.IP != nil { + event.RootFields.Put("source.ip", loginRecord.IP) + } + + if loginRecord.Hostname != "" && loginRecord.Hostname != loginRecord.IP.String() { + event.RootFields.Put("source.domain", loginRecord.Hostname) + } + + switch loginRecord.Type { + case userLoginRecord: + event.RootFields.Put("event.outcome", "success") + case userLoginFailedRecord: + event.RootFields.Put("event.outcome", "failure") + } + + return event +} + +func loginMessage(loginRecord *LoginRecord) string { + var actionString string + + switch loginRecord.Type { + case bootRecord: + return "System boot" + case shutdownRecord: + return "System shutdown" + case userLoginRecord: + actionString = "Login" + case userLoginFailedRecord: + actionString = "Failed login" + case userLogoutRecord: + actionString = "Logout" + } + + return fmt.Sprintf("%v by user %v (UID: %d) on %v (PID: %d) from %v (IP: %v)", + actionString, loginRecord.Username, loginRecord.UID, loginRecord.TTY, loginRecord.PID, + loginRecord.Hostname, loginRecord.IP) +} diff --git a/x-pack/auditbeat/module/system/login/login_other.go b/x-pack/auditbeat/module/system/login/login_other.go new file mode 100644 index 00000000000..73462e1c2f1 --- /dev/null +++ b/x-pack/auditbeat/module/system/login/login_other.go @@ -0,0 +1,29 @@ +// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +// or more contributor license agreements. Licensed under the Elastic License; +// you may not use this file except in compliance with the Elastic License. + +// +build !linux + +package login + +import ( + "fmt" + + "github.com/elastic/beats/metricbeat/mb" +) + +const ( + moduleName = "system" + metricsetName = "login" +) + +func init() { + mb.Registry.MustAddMetricSet(moduleName, metricsetName, New, + mb.DefaultMetricSet(), + ) +} + +// New returns an error. +func New(base mb.BaseMetricSet) (mb.MetricSet, error) { + return nil, fmt.Errorf("the %v/%v dataset is only supported on Linux", moduleName, metricsetName) +} diff --git a/x-pack/auditbeat/module/system/login/login_test.go b/x-pack/auditbeat/module/system/login/login_test.go new file mode 100644 index 00000000000..cbab49369f5 --- /dev/null +++ b/x-pack/auditbeat/module/system/login/login_test.go @@ -0,0 +1,63 @@ +// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +// or more contributor license agreements. Licensed under the Elastic License; +// you may not use this file except in compliance with the Elastic License. + +// +build linux + +package login + +import ( + "encoding/binary" + "io/ioutil" + "os" + "testing" + + "github.com/elastic/beats/auditbeat/core" + "github.com/elastic/beats/libbeat/paths" + mbtest "github.com/elastic/beats/metricbeat/mb/testing" +) + +func TestData(t *testing.T) { + if byteOrder != binary.LittleEndian { + t.Skip("Test only works on little-endian systems - skipping.") + } + + defer setup(t)() + + f := mbtest.NewReportingMetricSetV2(t, getConfig()) + + events, errs := mbtest.ReportingFetchV2(f) + if len(errs) > 0 { + t.Fatalf("received error: %+v", errs[0]) + } + + if len(events) == 0 { + t.Fatal("no events were generated") + } else if len(events) != 1 { + t.Fatal("only one event expected") + } + + fullEvent := mbtest.StandardizeEvent(f, events[0], core.AddDatasetToEvent) + mbtest.WriteEventToDataJSON(t, fullEvent, "") +} + +func getConfig() map[string]interface{} { + return map[string]interface{}{ + "module": "system", + "datasets": []string{"login"}, + "login.wtmp_file_pattern": "../../../tests/files/wtmp", + "login.btmp_file_pattern": "", + } +} + +// setup is copied from file_integrity/metricset_test.go. +// TODO: Move to shared location and use in all unit tests. +func setup(t testing.TB) func() { + // path.data should be set so that the DB is written to a predictable location. + var err error + paths.Paths.Data, err = ioutil.TempDir("", "beat-data-dir") + if err != nil { + t.Fatal() + } + return func() { os.RemoveAll(paths.Paths.Data) } +} diff --git a/x-pack/auditbeat/module/system/login/utmp.go b/x-pack/auditbeat/module/system/login/utmp.go new file mode 100644 index 00000000000..318ca94927e --- /dev/null +++ b/x-pack/auditbeat/module/system/login/utmp.go @@ -0,0 +1,535 @@ +// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +// or more contributor license agreements. Licensed under the Elastic License; +// you may not use this file except in compliance with the Elastic License. + +// +build linux + +package login + +import ( + "bytes" + "encoding/gob" + "io" + "net" + "os" + "os/user" + "path/filepath" + "sort" + "strconv" + "syscall" + + "github.com/pkg/errors" + + "github.com/elastic/beats/auditbeat/datastore" + "github.com/elastic/beats/libbeat/logp" +) + +const ( + bucketKeyFileRecords = "file_records" + bucketKeyLoginSessions = "login_sessions" +) + +// Inode represents a file's inode on Linux. +type Inode uint64 + +// UtmpType represents the type of a UTMP file and records. +// Two types are possible: wtmp (records from the "good" file, i.e. /var/log/wtmp) +// and btmp (failed logins from /var/log/btmp). +type UtmpType uint8 + +const ( + // Wtmp is the "normal" wtmp file that includes successful logins, logouts, + // and system boots. + Wtmp UtmpType = iota + // Btmp contains bad logins only. + Btmp +) + +// UtmpFile represents a UTMP file at a point in time. +type UtmpFile struct { + Inode Inode + Path string + Size int64 + Offset int64 + Type UtmpType +} + +// UtmpFileReader can read a UTMP formatted file (usually /var/log/wtmp). +type UtmpFileReader struct { + log *logp.Logger + bucket datastore.Bucket + config config + savedUtmpFiles map[Inode]UtmpFile + loginSessions map[string]LoginRecord +} + +// NewUtmpFileReader creates and initializes a new UTMP file reader. +func NewUtmpFileReader(log *logp.Logger, bucket datastore.Bucket, config config) (*UtmpFileReader, error) { + r := &UtmpFileReader{ + log: log, + bucket: bucket, + config: config, + savedUtmpFiles: make(map[Inode]UtmpFile), + loginSessions: make(map[string]LoginRecord), + } + + // Load state (file records, tty mapping) from disk + err := r.restoreStateFromDisk() + if err != nil { + return nil, errors.Wrap(err, "failed to restore state from disk") + } + + return r, nil +} + +// Close performs any cleanup tasks when the UTMP reader is done. +func (r *UtmpFileReader) Close() error { + err := r.bucket.Close() + return errors.Wrap(err, "error closing bucket") +} + +// ReadNew returns any new UTMP entries in any files matching the configured pattern. +func (r *UtmpFileReader) ReadNew() (<-chan LoginRecord, <-chan error) { + loginRecordC := make(chan LoginRecord) + errorC := make(chan error) + + go func() { + defer close(loginRecordC) + defer close(errorC) + + wtmpFiles, err := r.findFiles(r.config.WtmpFilePattern, Wtmp) + if err != nil { + errorC <- errors.Wrap(err, "failed to expand file pattern") + return + } + + btmpFiles, err := r.findFiles(r.config.BtmpFilePattern, Btmp) + if err != nil { + errorC <- errors.Wrap(err, "failed to expand file pattern") + return + } + + utmpFiles := append(wtmpFiles, btmpFiles...) + defer r.deleteOldUtmpFiles(&utmpFiles) + + for _, utmpFile := range utmpFiles { + r.readNewInFile(loginRecordC, errorC, utmpFile) + } + }() + + return loginRecordC, errorC +} + +func (r *UtmpFileReader) findFiles(filePattern string, utmpType UtmpType) ([]UtmpFile, error) { + paths, err := filepath.Glob(filePattern) + if err != nil { + return nil, errors.Wrapf(err, "failed to expand file pattern %v", filePattern) + } + + // Sort paths in reverse order (oldest/most-rotated file first) + sort.Sort(sort.Reverse(sort.StringSlice(paths))) + + var utmpFiles []UtmpFile + for _, path := range paths { + fileInfo, err := os.Stat(path) + if err != nil { + if os.IsNotExist(err) { + // Skip - file might have been rotated out + r.log.Debugf("File %v does not exist anymore.", path) + continue + } else { + return nil, errors.Wrapf(err, "unexpected error when looking up file %v", path) + } + } + + utmpFiles = append(utmpFiles, UtmpFile{ + Inode: Inode(fileInfo.Sys().(*syscall.Stat_t).Ino), + Path: path, + Size: fileInfo.Size(), + Offset: 0, + Type: utmpType, + }) + } + + return utmpFiles, nil +} + +// deleteOldUtmpFiles cleans up old UTMP file records where the inode no longer exists. +func (r *UtmpFileReader) deleteOldUtmpFiles(existingFiles *[]UtmpFile) { + existingInodes := make(map[Inode]struct{}) + for _, utmpFile := range *existingFiles { + existingInodes[utmpFile.Inode] = struct{}{} + } + + for savedInode := range r.savedUtmpFiles { + if _, exists := existingInodes[savedInode]; !exists { + r.log.Debugf("Deleting file record for old inode %d.", savedInode) + delete(r.savedUtmpFiles, savedInode) + } + } +} + +// readNewInFile reads a UTMP formatted file and emits the records after the last known record. +func (r *UtmpFileReader) readNewInFile(loginRecordC chan<- LoginRecord, errorC chan<- error, utmpFile UtmpFile) { + savedUtmpFile, isKnownFile := r.savedUtmpFiles[utmpFile.Inode] + if !isKnownFile { + r.log.Debugf("Found new file: %v (utmpFile=%+v)", utmpFile.Path, utmpFile) + } + + size := utmpFile.Size + oldSize := savedUtmpFile.Size + if size < oldSize { + // UTMP files are append-only and so this is weird. It might be a sign of + // a highly unlikely inode reuse - or of something more nefarious. + // Setting isKnownFile to false so we read the whole file from the beginning. + isKnownFile = false + + r.log.Warnf("Unexpectedly, the file %v is smaller than before (new: %v, old: %v) - reading whole file.", + utmpFile.Path, size, oldSize) + } + + if !isKnownFile && size == 0 { + // Empty new file - save but don't read. + err := r.updateSavedUtmpFile(utmpFile, nil) + if err != nil { + errorC <- errors.Wrapf(err, "error updating file record for file %v", utmpFile.Path) + } + return + } + + if !isKnownFile || size != oldSize { + r.log.Debugf("Reading file %v (utmpFile=%+v)", utmpFile.Path, utmpFile) + + f, err := os.Open(utmpFile.Path) + if err != nil { + errorC <- errors.Wrapf(err, "error opening file %v", utmpFile.Path) + return + } + defer func() { + // Once we start reading a file, we update the file record even if something fails - + // otherwise we will just keep trying to re-read very frequently forever. + r.updateSavedUtmpFile(utmpFile, f) + if err != nil { + errorC <- errors.Wrapf(err, "error updating file record for file %v", utmpFile.Path) + } + + f.Close() + }() + + _, err = f.Seek(utmpFile.Offset, 0) + if err != nil { + errorC <- errors.Wrapf(err, "error setting offset for file %v", utmpFile.Path) + + // Try one more time, this time resetting to the beginning of the file. + _, err = f.Seek(0, 0) + if err != nil { + errorC <- errors.Wrapf(err, "error setting offset 0 for file %v", utmpFile.Path) + + // Even that did not work, so return. + return + } + } + + for { + utmp, err := ReadNextUtmp(f) + if err != nil && err != io.EOF { + errorC <- errors.Wrapf(err, "error reading entry in UTMP file %v", utmpFile.Path) + return + } + + if utmp != nil { + r.log.Debugf("utmp: (ut_type=%d, ut_pid=%d, ut_line=%v, ut_user=%v, ut_host=%v, ut_tv.tv_sec=%v, ut_addr_v6=%v)", + utmp.UtType, utmp.UtPid, utmp.UtLine, utmp.UtUser, utmp.UtHost, utmp.UtTv, utmp.UtAddrV6) + + loginRecord := r.processLoginRecord(utmp) + if loginRecord != nil { + loginRecord.Origin = utmpFile.Path + if utmpFile.Type == Btmp && loginRecord.Type == userLoginRecord { + loginRecord.Type = userLoginFailedRecord + } + + loginRecordC <- *loginRecord + } + } else { + // Eventually, we have read all UTMP records in the file. + break + } + } + } +} + +func (r *UtmpFileReader) updateSavedUtmpFile(utmpFile UtmpFile, f *os.File) error { + if f != nil { + offset, err := f.Seek(0, 1) + if err != nil { + return errors.Wrap(err, "error calling Seek") + } + utmpFile.Offset = offset + } + + r.log.Debugf("Saving UTMP file record (%+v)", utmpFile) + + r.savedUtmpFiles[utmpFile.Inode] = utmpFile + + return nil +} + +// processLoginRecord receives UTMP login records in order and returns +// a corresponding LoginRecord. Some UTMP records do not translate +// into a LoginRecord, in this case the return value is nil. +func (r *UtmpFileReader) processLoginRecord(utmp *Utmp) *LoginRecord { + record := LoginRecord{ + Utmp: utmp, + Timestamp: utmp.UtTv, + UID: -1, + PID: -1, + } + + if utmp.UtLine != "~" { + record.TTY = utmp.UtLine + } + + switch utmp.UtType { + // See utmp(5) for C constants. + case RUN_LVL: + // The runlevel - though a number - is stored as + // the ASCII character of that number. + runlevel := string(rune(utmp.UtPid)) + + // 0 - halt; 6 - reboot + if utmp.UtUser == "shutdown" || runlevel == "0" || runlevel == "6" { + record.Type = shutdownRecord + + // Clear any old logins + // TODO: Issue logout events for login events that are still around + // at this point. + r.loginSessions = make(map[string]LoginRecord) + } else { + // Ignore runlevel changes that are not halt or reboot. + return nil + } + case BOOT_TIME: + if utmp.UtLine == "~" && utmp.UtUser == "reboot" { + record.Type = bootRecord + + // Clear any old logins + // TODO: Issue logout events for login events that are still around + // at this point. + r.loginSessions = make(map[string]LoginRecord) + } else { + // Ignore unknown record + return nil + } + case USER_PROCESS: + record.Type = userLoginRecord + + record.Username = utmp.UtUser + record.UID = lookupUsername(record.Username) + record.PID = utmp.UtPid + record.IP = newIP(utmp.UtAddrV6) + record.Hostname = utmp.UtHost + + // Store TTY from user login record for enrichment when user logout + // record comes along (which, alas, does not contain the username). + r.loginSessions[record.TTY] = record + case DEAD_PROCESS: + savedRecord, found := r.loginSessions[record.TTY] + if found { + record.Type = userLogoutRecord + record.Username = savedRecord.Username + record.UID = savedRecord.UID + record.PID = savedRecord.PID + record.IP = savedRecord.IP + record.Hostname = savedRecord.Hostname + } else { + // Skip - this is usually the DEAD_PROCESS event for + // a previous INIT_PROCESS or LOGIN_PROCESS event - + // those are ignored - (see default case below). + return nil + } + default: + /* + Every other record type is ignored: + - EMPTY - empty record + - NEW_TIME and OLD_TIME - could be useful, but not written when time changes, + at least not using `date` + - INIT_PROCESS and LOGIN_PROCESS - written on boot but do not contain any + interesting information + - ACCOUNTING - not implemented according to manpage + */ + return nil + } + + return &record +} + +// lookupUsername looks up a username and returns its UID. +// It does not pass through errors (e.g. when the user is not found) +// but will return -1 instead. +func lookupUsername(username string) int { + if username != "" { + user, err := user.Lookup(username) + if err == nil { + uid, err := strconv.Atoi(user.Uid) + if err == nil { + return uid + } + } + } + + return -1 +} + +func newIP(utAddrV6 [4]uint32) *net.IP { + var ip net.IP + + // See utmp(5) for the utmp struct fields. + if utAddrV6[1] != 0 || utAddrV6[2] != 0 || utAddrV6[3] != 0 { + // IPv6 + b := make([]byte, 16) + byteOrder.PutUint32(b[:4], utAddrV6[0]) + byteOrder.PutUint32(b[4:8], utAddrV6[1]) + byteOrder.PutUint32(b[8:12], utAddrV6[2]) + byteOrder.PutUint32(b[12:], utAddrV6[3]) + ip = net.IP(b) + } else { + // IPv4 + b := make([]byte, 4) + byteOrder.PutUint32(b, utAddrV6[0]) + ip = net.IP(b) + } + + return &ip +} + +func (r *UtmpFileReader) saveStateToDisk() error { + err := r.saveFileRecordsToDisk() + if err != nil { + return err + } + + err = r.saveLoginSessionsToDisk() + if err != nil { + return err + } + + return nil +} + +func (r *UtmpFileReader) saveFileRecordsToDisk() error { + var buf bytes.Buffer + encoder := gob.NewEncoder(&buf) + + for _, utmpFile := range r.savedUtmpFiles { + err := encoder.Encode(utmpFile) + if err != nil { + return errors.Wrap(err, "error encoding UTMP file record") + } + } + + err := r.bucket.Store(bucketKeyFileRecords, buf.Bytes()) + if err != nil { + return errors.Wrap(err, "error writing UTMP file records to disk") + } + + r.log.Debugf("Wrote %d UTMP file records to disk", len(r.savedUtmpFiles)) + return nil +} + +func (r *UtmpFileReader) saveLoginSessionsToDisk() error { + var buf bytes.Buffer + encoder := gob.NewEncoder(&buf) + + for _, loginRecord := range r.loginSessions { + err := encoder.Encode(loginRecord) + if err != nil { + return errors.Wrap(err, "error encoding login record") + } + } + + err := r.bucket.Store(bucketKeyLoginSessions, buf.Bytes()) + if err != nil { + return errors.Wrap(err, "error writing login records to disk") + } + + r.log.Debugf("Wrote %d open login sessions to disk", len(r.loginSessions)) + return nil +} + +func (r *UtmpFileReader) restoreStateFromDisk() error { + err := r.restoreFileRecordsFromDisk() + if err != nil { + return err + } + + err = r.restoreLoginSessionsFromDisk() + if err != nil { + return err + } + + return nil +} + +func (r *UtmpFileReader) restoreFileRecordsFromDisk() error { + var decoder *gob.Decoder + err := r.bucket.Load(bucketKeyFileRecords, func(blob []byte) error { + if len(blob) > 0 { + buf := bytes.NewBuffer(blob) + decoder = gob.NewDecoder(buf) + } + return nil + }) + if err != nil { + return err + } + + if decoder != nil { + for { + var utmpFile UtmpFile + err = decoder.Decode(&utmpFile) + if err == nil { + r.savedUtmpFiles[utmpFile.Inode] = utmpFile + } else if err == io.EOF { + // Read all + break + } else { + return errors.Wrap(err, "error decoding file record") + } + } + } + r.log.Debugf("Restored %d UTMP file records from disk", len(r.savedUtmpFiles)) + + return nil +} + +func (r *UtmpFileReader) restoreLoginSessionsFromDisk() error { + var decoder *gob.Decoder + err := r.bucket.Load(bucketKeyLoginSessions, func(blob []byte) error { + if len(blob) > 0 { + buf := bytes.NewBuffer(blob) + decoder = gob.NewDecoder(buf) + } + return nil + }) + if err != nil { + return err + } + + if decoder != nil { + for { + loginRecord := new(LoginRecord) + err = decoder.Decode(loginRecord) + if err == nil { + r.loginSessions[loginRecord.TTY] = *loginRecord + } else if err == io.EOF { + // Read all + break + } else { + return errors.Wrap(err, "error decoding login record") + } + } + } + r.log.Debugf("Restored %d open login sessions from disk", len(r.loginSessions)) + + return nil +} diff --git a/x-pack/auditbeat/module/system/login/utmp_c.go b/x-pack/auditbeat/module/system/login/utmp_c.go new file mode 100644 index 00000000000..804683acd53 --- /dev/null +++ b/x-pack/auditbeat/module/system/login/utmp_c.go @@ -0,0 +1,120 @@ +// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +// or more contributor license agreements. Licensed under the Elastic License; +// you may not use this file except in compliance with the Elastic License. + +// +build linux + +// Pure Go reader for UTMP formatted files. +// See utmp(5) and getutent(3) for the C structs and functions this is +// replacing. + +package login + +import ( + "bytes" + "encoding/binary" + "io" + "time" + "unsafe" +) + +var byteOrder = getByteOrder() + +func getByteOrder() binary.ByteOrder { + var b [2]byte + *((*uint16)(unsafe.Pointer(&b[0]))) = 1 + if b[0] == 1 { + return binary.LittleEndian + } + return binary.BigEndian +} + +// UtType represents the ut_type field. See utmp(5). +type UtType int16 + +// Possible values for UtType. +const ( + EMPTY UtType = 0 + RUN_LVL UtType = 1 + BOOT_TIME UtType = 2 + NEW_TIME UtType = 3 + OLD_TIME UtType = 4 + INIT_PROCESS UtType = 5 + LOGIN_PROCESS UtType = 6 + USER_PROCESS UtType = 7 + DEAD_PROCESS UtType = 8 + ACCOUNTING UtType = 9 + + UT_LINESIZE = 32 + UT_NAMESIZE = 32 + UT_HOSTSIZE = 256 +) + +// utmpC is a Go representation of the C utmp struct that the UTMP files consist of. +type utmpC struct { + Type UtType + + // Alignment + _ [2]byte + + Pid int32 + Device [UT_LINESIZE]byte + Terminal [4]byte + Username [UT_NAMESIZE]byte + Hostname [UT_HOSTSIZE]byte + + ExitStatusTermination int16 + ExitStatusExit int16 + + SessionID int32 + + TimeSeconds int32 + TimeMicroseconds int32 + + IP [4]int32 + + Unused [20]byte +} + +// Utmp contains a Go version of UtmpC. +type Utmp struct { + UtType UtType + UtPid int + UtLine string + UtUser string + UtHost string + UtTv time.Time + UtAddrV6 [4]uint32 +} + +// newUtmp creates a Utmp out of a utmpC. +func newUtmp(utmp *utmpC) *Utmp { + // See utmp(5) for the utmp struct fields. + return &Utmp{ + UtType: utmp.Type, + UtPid: int(utmp.Pid), + UtLine: byteToString(utmp.Device[:]), + UtUser: byteToString(utmp.Username[:]), + UtHost: byteToString(utmp.Hostname[:]), + UtTv: time.Unix(int64(utmp.TimeSeconds), int64(utmp.TimeMicroseconds)*1000), + UtAddrV6: [4]uint32{uint32(utmp.IP[0]), uint32(utmp.IP[1]), uint32(utmp.IP[2]), uint32(utmp.IP[3])}, + } +} + +// byteToString converts a NULL terminated char array to a Go string. +func byteToString(b []byte) string { + n := bytes.IndexByte(b, 0) + return string(b[:n]) +} + +// ReadNextUtmp reads the next UTMP entry in a reader pointing to UTMP formatted data. +func ReadNextUtmp(r io.Reader) (*Utmp, error) { + utmpC := new(utmpC) + + err := binary.Read(r, byteOrder, utmpC) + if err != nil { + return nil, err + } + + return newUtmp(utmpC), nil +} diff --git a/x-pack/auditbeat/module/system/package/_meta/data.json b/x-pack/auditbeat/module/system/package/_meta/data.json new file mode 100644 index 00000000000..ca570150245 --- /dev/null +++ b/x-pack/auditbeat/module/system/package/_meta/data.json @@ -0,0 +1,29 @@ +{ + "@timestamp": "2017-10-12T08:05:34.853Z", + "agent": { + "hostname": "host.example.com", + "name": "host.example.com" + }, + "event": { + "action": "existing_package", + "dataset": "package", + "id": "9ac4ea4c-5a0c-475f-b4c9-ec9d981ff11b", + "kind": "state", + "module": "system" + }, + "message": "Package zstd (1.3.5) is already installed", + "service": { + "type": "system" + }, + "system": { + "audit": { + "package": { + "installtime": "2018-08-30T18:41:23.85657356+01:00", + "name": "zstd", + "summary": "Zstandard is a real-time compression algorithm", + "url": "http://zstd.net/", + "version": "1.3.5" + } + } + } +} \ No newline at end of file diff --git a/x-pack/auditbeat/module/system/package/_meta/docs.asciidoc b/x-pack/auditbeat/module/system/package/_meta/docs.asciidoc new file mode 100644 index 00000000000..b1504e3aeb3 --- /dev/null +++ b/x-pack/auditbeat/module/system/package/_meta/docs.asciidoc @@ -0,0 +1,8 @@ +[role="xpack"] + +experimental[] + +This is the `package` dataset of the system module. + +It is implemented for Linux distributions using dpkg or rpm as their package +manager, and for Homebrew on macOS (Darwin). diff --git a/x-pack/auditbeat/module/system/package/_meta/fields.yml b/x-pack/auditbeat/module/system/package/_meta/fields.yml new file mode 100644 index 00000000000..d87ba9aac06 --- /dev/null +++ b/x-pack/auditbeat/module/system/package/_meta/fields.yml @@ -0,0 +1,41 @@ +- name: package + type: group + description: > + `package` contains information about an installed or removed package. + release: experimental + fields: + - name: name + type: keyword + description: > + Package name. + - name: version + type: keyword + description: > + Package version. + - name: release + type: keyword + description: > + Package release. + - name: arch + type: keyword + description: > + Package architecture. + - name: license + type: keyword + description: > + Package license. + - name: installtime + type: date + description: > + Package install time. + - name: size + type: long + description: > + Package size. + - name: summary + description: > + Package summary. + - name: url + type: keyword + description: > + Package URL. diff --git a/x-pack/auditbeat/module/system/package/config.go b/x-pack/auditbeat/module/system/package/config.go new file mode 100644 index 00000000000..5af2d0a7d6a --- /dev/null +++ b/x-pack/auditbeat/module/system/package/config.go @@ -0,0 +1,30 @@ +// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +// or more contributor license agreements. Licensed under the Elastic License; +// you may not use this file except in compliance with the Elastic License. + +// +build !windows + +package pkg + +import ( + "time" +) + +// config defines the package metricset's configuration options. +type config struct { + StatePeriod time.Duration `config:"state.period"` + PackageStatePeriod time.Duration `config:"package.state.period"` +} + +func (c *config) effectiveStatePeriod() time.Duration { + if c.PackageStatePeriod != 0 { + return c.PackageStatePeriod + } + return c.StatePeriod +} + +func defaultConfig() config { + return config{ + StatePeriod: 12 * time.Hour, + } +} diff --git a/x-pack/auditbeat/module/system/package/package.go b/x-pack/auditbeat/module/system/package/package.go new file mode 100644 index 00000000000..b77e30854cb --- /dev/null +++ b/x-pack/auditbeat/module/system/package/package.go @@ -0,0 +1,541 @@ +// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +// or more contributor license agreements. Licensed under the Elastic License; +// you may not use this file except in compliance with the Elastic License. + +// +build !windows + +package pkg + +import ( + "bufio" + "bytes" + "encoding/gob" + "fmt" + "io" + "io/ioutil" + "os" + "path" + "strconv" + "strings" + "time" + + "github.com/OneOfOne/xxhash" + "github.com/gofrs/uuid" + "github.com/pkg/errors" + + "github.com/elastic/beats/auditbeat/datastore" + "github.com/elastic/beats/libbeat/common" + "github.com/elastic/beats/libbeat/common/cfgwarn" + "github.com/elastic/beats/libbeat/logp" + "github.com/elastic/beats/metricbeat/mb" + "github.com/elastic/beats/x-pack/auditbeat/cache" + "github.com/elastic/go-sysinfo" + "github.com/elastic/go-sysinfo/types" +) + +const ( + moduleName = "system" + metricsetName = "package" + namespace = "system.audit.package" + + redhat = "redhat" + debian = "debian" + darwin = "darwin" + + dpkgStatusFile = "/var/lib/dpkg/status" + homebrewCellarPath = "/usr/local/Cellar" + + bucketName = "package.v1" + bucketKeyPackages = "packages" + bucketKeyStateTimestamp = "state_timestamp" + + eventTypeState = "state" + eventTypeEvent = "event" +) + +type eventAction uint8 + +const ( + eventActionExistingPackage eventAction = iota + eventActionPackageInstalled + eventActionPackageRemoved +) + +func (action eventAction) String() string { + switch action { + case eventActionExistingPackage: + return "existing_package" + case eventActionPackageInstalled: + return "package_installed" + case eventActionPackageRemoved: + return "package_removed" + default: + return "" + } +} + +func init() { + mb.Registry.MustAddMetricSet(moduleName, metricsetName, New, + mb.DefaultMetricSet(), + mb.WithNamespace(namespace), + ) +} + +// MetricSet collects data about the system's packages. +type MetricSet struct { + mb.BaseMetricSet + config config + log *logp.Logger + cache *cache.Cache + bucket datastore.Bucket + lastState time.Time + osFamily string +} + +// Package represents information for a package. +type Package struct { + Name string + Version string + Release string + Arch string + License string + InstallTime time.Time + Size uint64 + Summary string + URL string +} + +// Hash creates a hash for Package. +func (pkg Package) Hash() uint64 { + h := xxhash.New64() + h.WriteString(pkg.Name) + h.WriteString(pkg.InstallTime.String()) + return h.Sum64() +} + +func (pkg Package) toMapStr() common.MapStr { + mapstr := common.MapStr{ + "name": pkg.Name, + "version": pkg.Version, + "installtime": pkg.InstallTime, + } + + if pkg.Arch != "" { + mapstr.Put("arch", pkg.Arch) + } + + if pkg.License != "" { + mapstr.Put("license", pkg.License) + } + + if pkg.Release != "" { + mapstr.Put("release", pkg.Release) + } + + if pkg.Size != 0 { + mapstr.Put("size", pkg.Size) + } + + if pkg.Summary != "" { + mapstr.Put("summary", pkg.Summary) + } + + if pkg.URL != "" { + mapstr.Put("url", pkg.URL) + } + + return mapstr +} + +func getOS() (*types.OSInfo, error) { + host, err := sysinfo.Host() + if err != nil { + return nil, errors.Wrap(err, "error getting the OS") + } + + hostInfo := host.Info() + if hostInfo.OS == nil { + return nil, errors.New("no host info") + } + + return hostInfo.OS, nil +} + +// New constructs a new MetricSet. +func New(base mb.BaseMetricSet) (mb.MetricSet, error) { + cfgwarn.Experimental("The %v/%v dataset is experimental", moduleName, metricsetName) + + config := defaultConfig() + if err := base.Module().UnpackConfig(&config); err != nil { + return nil, errors.Wrapf(err, "failed to unpack the %v/%v config", moduleName, metricsetName) + } + + bucket, err := datastore.OpenBucket(bucketName) + if err != nil { + return nil, errors.Wrap(err, "failed to open persistent datastore") + } + + ms := &MetricSet{ + BaseMetricSet: base, + config: config, + log: logp.NewLogger(metricsetName), + cache: cache.New(), + bucket: bucket, + } + + osInfo, err := getOS() + if err != nil { + return nil, errors.Wrap(err, "error determining operating system") + } + ms.osFamily = osInfo.Family + switch osInfo.Family { + case redhat: + // ok + case debian: + if _, err := os.Stat(dpkgStatusFile); err != nil { + return nil, errors.Wrapf(err, "error looking up %s", dpkgStatusFile) + } + case darwin: + if _, err := os.Stat(homebrewCellarPath); err != nil { + return nil, errors.Wrapf(err, "error looking up %s - is Homebrew installed?", homebrewCellarPath) + } + default: + return nil, fmt.Errorf("this metricset does not support OS family %v", osInfo.Family) + } + + // Load from disk: Time when state was last sent + err = bucket.Load(bucketKeyStateTimestamp, func(blob []byte) error { + if len(blob) > 0 { + return ms.lastState.UnmarshalBinary(blob) + } + return nil + }) + if err != nil { + return nil, err + } + if !ms.lastState.IsZero() { + ms.log.Debugf("Last state was sent at %v. Next state update by %v.", ms.lastState, ms.lastState.Add(ms.config.effectiveStatePeriod())) + } else { + ms.log.Debug("No state timestamp found") + } + + // Load from disk: Packages + packages, err := ms.restorePackagesFromDisk() + if err != nil { + return nil, errors.Wrap(err, "failed to restore packages from disk") + } + ms.log.Debugf("Restored %d packages from disk", len(packages)) + + ms.cache.DiffAndUpdateCache(convertToCacheable(packages)) + + return ms, nil +} + +// Close cleans up the MetricSet when it finishes. +func (ms *MetricSet) Close() error { + if ms.bucket != nil { + return ms.bucket.Close() + } + return nil +} + +// Fetch collects data about the host. It is invoked periodically. +func (ms *MetricSet) Fetch(report mb.ReporterV2) { + needsStateUpdate := time.Since(ms.lastState) > ms.config.effectiveStatePeriod() + if needsStateUpdate || ms.cache.IsEmpty() { + ms.log.Debugf("State update needed (needsStateUpdate=%v, cache.IsEmpty()=%v)", needsStateUpdate, ms.cache.IsEmpty()) + err := ms.reportState(report) + if err != nil { + ms.log.Error(err) + report.Error(err) + } + ms.log.Debugf("Next state update by %v", ms.lastState.Add(ms.config.effectiveStatePeriod())) + } + + err := ms.reportChanges(report) + if err != nil { + ms.log.Error(err) + report.Error(err) + } +} + +// reportState reports all installed packages on the system. +func (ms *MetricSet) reportState(report mb.ReporterV2) error { + ms.lastState = time.Now() + + packages, err := getPackages(ms.osFamily) + if err != nil { + return errors.Wrap(err, "failed to get packages") + } + ms.log.Debugf("Found %v packages", len(packages)) + + stateID, err := uuid.NewV4() + if err != nil { + return errors.Wrap(err, "error generating state ID") + } + for _, pkg := range packages { + event := packageEvent(pkg, eventTypeState, eventActionExistingPackage) + event.RootFields.Put("event.id", stateID.String()) + report.Event(event) + } + + // This will initialize the cache with the current packages + ms.cache.DiffAndUpdateCache(convertToCacheable(packages)) + + // Save time so we know when to send the state again (config.StatePeriod) + timeBytes, err := ms.lastState.MarshalBinary() + if err != nil { + return err + } + err = ms.bucket.Store(bucketKeyStateTimestamp, timeBytes) + if err != nil { + return errors.Wrap(err, "error writing state timestamp to disk") + } + + return ms.savePackagesToDisk(packages) +} + +// reportChanges detects and reports any changes to installed packages on this system since the last call. +func (ms *MetricSet) reportChanges(report mb.ReporterV2) error { + packages, err := getPackages(ms.osFamily) + if err != nil { + return errors.Wrap(err, "failed to get packages") + } + ms.log.Debugf("Found %v packages", len(packages)) + + installed, removed := ms.cache.DiffAndUpdateCache(convertToCacheable(packages)) + + for _, cacheValue := range installed { + report.Event(packageEvent(cacheValue.(*Package), eventTypeEvent, eventActionPackageInstalled)) + } + + for _, cacheValue := range removed { + report.Event(packageEvent(cacheValue.(*Package), eventTypeEvent, eventActionPackageRemoved)) + } + + if len(installed) > 0 || len(removed) > 0 { + return ms.savePackagesToDisk(packages) + } + + return nil +} + +func packageEvent(pkg *Package, eventType string, action eventAction) mb.Event { + return mb.Event{ + RootFields: common.MapStr{ + "event": common.MapStr{ + "kind": eventType, + "action": action.String(), + }, + "message": packageMessage(pkg, action), + }, + MetricSetFields: pkg.toMapStr(), + } +} + +func packageMessage(pkg *Package, action eventAction) string { + var actionString string + switch action { + case eventActionExistingPackage: + actionString = "is already installed" + case eventActionPackageInstalled: + actionString = "installed" + case eventActionPackageRemoved: + actionString = "removed" + } + + return fmt.Sprintf("Package %v (%v) %v", + pkg.Name, pkg.Version, actionString) +} + +func convertToCacheable(packages []*Package) []cache.Cacheable { + c := make([]cache.Cacheable, 0, len(packages)) + + for _, p := range packages { + c = append(c, p) + } + + return c +} + +// restorePackagesFromDisk loads the packages from disk. +func (ms *MetricSet) restorePackagesFromDisk() (packages []*Package, err error) { + var decoder *gob.Decoder + err = ms.bucket.Load(bucketKeyPackages, func(blob []byte) error { + if len(blob) > 0 { + buf := bytes.NewBuffer(blob) + decoder = gob.NewDecoder(buf) + } + return nil + }) + if err != nil { + return nil, err + } + + if decoder != nil { + for { + pkg := new(Package) + err = decoder.Decode(pkg) + if err == nil { + packages = append(packages, pkg) + } else if err == io.EOF { + // Read all packages + break + } else { + return nil, errors.Wrap(err, "error decoding packages") + } + } + } + + return packages, nil +} + +// Save packages to disk. +func (ms *MetricSet) savePackagesToDisk(packages []*Package) error { + var buf bytes.Buffer + encoder := gob.NewEncoder(&buf) + + for _, pkg := range packages { + err := encoder.Encode(*pkg) + if err != nil { + return errors.Wrap(err, "error encoding packages") + } + } + + err := ms.bucket.Store(bucketKeyPackages, buf.Bytes()) + if err != nil { + return errors.Wrap(err, "error writing packages to disk") + } + return nil +} + +func getPackages(osFamily string) (packages []*Package, err error) { + switch osFamily { + case redhat: + packages, err = listRPMPackages() + if err != nil { + err = errors.Wrap(err, "error getting RPM packages") + } + case debian: + packages, err = listDebPackages() + if err != nil { + err = errors.Wrap(err, "error getting DEB packages") + } + case darwin: + packages, err = listBrewPackages() + if err != nil { + err = errors.Wrap(err, "error getting Homebrew packages") + } + default: + err = errors.Errorf("unknown OS %v - this should not have happened", osFamily) + } + + return +} + +func listDebPackages() ([]*Package, error) { + file, err := os.Open(dpkgStatusFile) + if err != nil { + return nil, errors.Wrapf(err, "error opening %s", dpkgStatusFile) + } + defer file.Close() + + var packages []*Package + pkg := &Package{} + scanner := bufio.NewScanner(file) + for scanner.Scan() { + line := scanner.Text() + if len(strings.TrimSpace(line)) == 0 { + // empty line signals new package + packages = append(packages, pkg) + pkg = &Package{} + continue + } + if strings.HasPrefix(line, " ") { + // not interested in multi-lines for now + continue + } + words := strings.SplitN(line, ":", 2) + if len(words) != 2 { + return nil, fmt.Errorf("the following line was unexpected (no ':' found): '%s'", line) + } + value := strings.TrimSpace(words[1]) + switch strings.ToLower(words[0]) { + case "package": + pkg.Name = value + case "architecture": + pkg.Arch = value + case "version": + pkg.Version = value + case "description": + pkg.Summary = value + case "installed-size": + pkg.Size, err = strconv.ParseUint(value, 10, 64) + if err != nil { + return nil, errors.Wrapf(err, "error converting %s to int", value) + } + default: + continue + } + } + if err = scanner.Err(); err != nil { + return nil, errors.Wrap(err, "error scanning file") + } + return packages, nil +} + +func listBrewPackages() ([]*Package, error) { + packageDirs, err := ioutil.ReadDir(homebrewCellarPath) + if err != nil { + return nil, errors.Wrapf(err, "error reading directory %s", homebrewCellarPath) + } + + var packages []*Package + for _, packageDir := range packageDirs { + if !packageDir.IsDir() { + continue + } + pkgPath := path.Join(homebrewCellarPath, packageDir.Name()) + versions, err := ioutil.ReadDir(pkgPath) + if err != nil { + return nil, errors.Wrapf(err, "error reading directory: %s", pkgPath) + } + + for _, version := range versions { + if !version.IsDir() { + continue + } + pkg := &Package{ + Name: packageDir.Name(), + Version: version.Name(), + InstallTime: version.ModTime(), + } + + // read formula + formulaPath := path.Join(homebrewCellarPath, pkg.Name, pkg.Version, ".brew", pkg.Name+".rb") + file, err := os.Open(formulaPath) + if err != nil { + //fmt.Printf("WARNING: Can't get formula for package %s-%s\n", pkg.Name, pkg.Version) + // TODO: follow the path from INSTALL_RECEIPT.json to find the formula + continue + } + scanner := bufio.NewScanner(file) + count := 15 // only look into the first few lines of the formula + for scanner.Scan() { + count-- + if count == 0 { + break + } + line := scanner.Text() + if strings.HasPrefix(line, " desc ") { + pkg.Summary = strings.Trim(line[7:], " \"") + } else if strings.HasPrefix(line, " homepage ") { + pkg.URL = strings.Trim(line[11:], " \"") + } + } + + packages = append(packages, pkg) + } + } + return packages, nil +} diff --git a/x-pack/auditbeat/module/system/packages/packages_test.go b/x-pack/auditbeat/module/system/package/package_test.go similarity index 52% rename from x-pack/auditbeat/module/system/packages/packages_test.go rename to x-pack/auditbeat/module/system/package/package_test.go index 67d17dff765..8e8cde472b8 100644 --- a/x-pack/auditbeat/module/system/packages/packages_test.go +++ b/x-pack/auditbeat/module/system/package/package_test.go @@ -2,27 +2,35 @@ // or more contributor license agreements. Licensed under the Elastic License; // you may not use this file except in compliance with the Elastic License. -package packages +// +build !windows + +package pkg import ( "testing" + "github.com/elastic/beats/auditbeat/core" mbtest "github.com/elastic/beats/metricbeat/mb/testing" ) func TestData(t *testing.T) { - t.Skip("Packages metricset is disabled") - f := mbtest.NewReportingMetricSetV2(t, getConfig()) - err := mbtest.WriteEventsReporterV2(f, t, "") - if err != nil { - t.Fatal("write", err) + events, errs := mbtest.ReportingFetchV2(f) + if len(errs) > 0 { + t.Fatalf("received error: %+v", errs[0]) } + + if len(events) == 0 { + t.Fatal("no events were generated") + } + + fullEvent := mbtest.StandardizeEvent(f, events[len(events)-1], core.AddDatasetToEvent) + mbtest.WriteEventToDataJSON(t, fullEvent, "") } func getConfig() map[string]interface{} { return map[string]interface{}{ - "module": "system", - "metricsets": []string{"packages"}, + "module": "system", + "datasets": []string{"package"}, } } diff --git a/x-pack/auditbeat/module/system/package/package_windows.go b/x-pack/auditbeat/module/system/package/package_windows.go new file mode 100644 index 00000000000..498eea61111 --- /dev/null +++ b/x-pack/auditbeat/module/system/package/package_windows.go @@ -0,0 +1,29 @@ +// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +// or more contributor license agreements. Licensed under the Elastic License; +// you may not use this file except in compliance with the Elastic License. + +// +build windows + +package pkg + +import ( + "fmt" + + "github.com/elastic/beats/metricbeat/mb" +) + +const ( + moduleName = "system" + metricsetName = "package" +) + +func init() { + mb.Registry.MustAddMetricSet(moduleName, metricsetName, New, + mb.DefaultMetricSet(), + ) +} + +// New returns an error. +func New(base mb.BaseMetricSet) (mb.MetricSet, error) { + return nil, fmt.Errorf("the %v/%v dataset is not supported on Windows", moduleName, metricsetName) +} diff --git a/x-pack/auditbeat/module/system/package/rpm_common_test.go b/x-pack/auditbeat/module/system/package/rpm_common_test.go new file mode 100644 index 00000000000..2e75d475811 --- /dev/null +++ b/x-pack/auditbeat/module/system/package/rpm_common_test.go @@ -0,0 +1,69 @@ +// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +// or more contributor license agreements. Licensed under the Elastic License; +// you may not use this file except in compliance with the Elastic License. + +// +build !windows + +package pkg + +import ( + "fmt" + "os/exec" + "strconv" + "strings" + "time" +) + +func rpmPackagesByExec() ([]*Package, error) { + format := "%{NAME}|%{VERSION}|%{RELEASE}|%{ARCH}|%{LICENSE}|%{INSTALLTIME}|%{SIZE}|%{URL}|%{SUMMARY}\\n" + out, err := exec.Command("/usr/bin/rpm", "--qf", format, "-qa").Output() + if err != nil { + return nil, fmt.Errorf("Error running rpm -qa command: %v", err) + } + + lines := strings.Split(string(out), "\n") + var packages []*Package + for _, line := range lines { + if len(strings.TrimSpace(line)) == 0 { + continue + } + words := strings.SplitN(line, "|", 9) + if len(words) < 9 { + return nil, fmt.Errorf("line '%s' doesn't have enough elements", line) + } + pkg := Package{ + Name: words[0], + Version: words[1], + Release: words[2], + Arch: words[3], + License: words[4], + // install time - 5 + // size - 6 + URL: words[7], + Summary: words[8], + } + ts, err := strconv.ParseInt(words[5], 10, 64) + if err != nil { + return nil, fmt.Errorf("error converting %s to string: %v", words[5], err) + } + pkg.InstallTime = time.Unix(ts, 0) + + pkg.Size, err = strconv.ParseUint(words[6], 10, 64) + if err != nil { + return nil, fmt.Errorf("error converting %s to string: %v", words[6], err) + } + + // Avoid "(none)" in favor of empty strings + if pkg.URL == "(none)" { + pkg.URL = "" + } + if pkg.Arch == "(none)" { + pkg.Arch = "" + } + + packages = append(packages, &pkg) + + } + + return packages, nil +} diff --git a/x-pack/auditbeat/module/system/package/rpm_linux.go b/x-pack/auditbeat/module/system/package/rpm_linux.go new file mode 100644 index 00000000000..a1d395f54b1 --- /dev/null +++ b/x-pack/auditbeat/module/system/package/rpm_linux.go @@ -0,0 +1,302 @@ +// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +// or more contributor license agreements. Licensed under the Elastic License; +// you may not use this file except in compliance with the Elastic License. + +// +build linux,cgo + +package pkg + +import ( + "fmt" + "time" + "unsafe" + + "github.com/coreos/pkg/dlopen" +) + +/* +#include +#include + +#include +#include +#include +#include + +rpmts +my_rpmtsCreate(void *f) { + rpmts (*rpmtsCreate)(); + rpmtsCreate = (rpmts (*)())f; + + return rpmtsCreate(); +} + +int +my_rpmReadConfigFiles(void *f) { + int (*rpmReadConfigFiles)(const char*, const char*); + rpmReadConfigFiles = (int (*)(const char*, const char*))f; + return rpmReadConfigFiles(NULL, NULL); +} + +rpmdbMatchIterator +my_rpmtsInitIterator(void *f, rpmts ts) { + rpmdbMatchIterator (*rpmtsInitIterator)(const rpmts, rpmTag, const void*, size_t); + rpmtsInitIterator = (rpmdbMatchIterator (*)(const rpmts, rpmTag, const void*, size_t))f; + + return rpmtsInitIterator(ts, RPMDBI_PACKAGES, NULL, 0); +} + +Header +my_rpmdbNextIterator(void *f, rpmdbMatchIterator mi) { + Header (*rpmdbNextIterator)(rpmdbMatchIterator); + rpmdbNextIterator = (Header (*)(rpmdbMatchIterator))f; + + return rpmdbNextIterator(mi); +} + +Header +my_headerLink(void *f, Header h) { + Header (*headerLink)(Header); + headerLink = (Header (*)(Header))f; + + return headerLink(h); +} + +int +my_headerGetEntry(void *f, Header h, rpm_tag_t tag, char **p) { + int (*headerGetEntry)(Header, rpm_tag_t, rpm_tagtype_t*, rpm_data_t*, rpm_count_t*); + headerGetEntry = (int (*)(Header, rpm_tag_t, rpm_tagtype_t*, rpm_data_t*, rpm_count_t*))f; + + return headerGetEntry(h, tag, NULL, (void**)p, NULL); +} + +int +my_headerGetEntryInt(void *f, Header h, rpm_tag_t tag, int **p) { + int (*headerGetEntry)(Header, rpm_tag_t, rpm_tagtype_t*, rpm_data_t*, rpm_count_t*); + headerGetEntry = (int (*)(Header, rpm_tag_t, rpm_tagtype_t*, rpm_data_t*, rpm_count_t*))f; + + return headerGetEntry(h, tag, NULL, (void**)p, NULL); +} + +void +my_headerFree(void *f, Header h) { + Header (*headerFree)(Header); + headerFree = (Header (*)(Header))f; + + headerFree(h); +} + +void +my_rpmdbFreeIterator(void *f, rpmdbMatchIterator mi) { + rpmdbMatchIterator (*rpmdbFreeIterator)(rpmdbMatchIterator); + rpmdbFreeIterator = (rpmdbMatchIterator (*)(rpmdbMatchIterator))f; + + rpmdbFreeIterator(mi); +} + +void +my_rpmtsFree(void *f, rpmts ts) { + rpmts (*rpmtsFree)(rpmts); + rpmtsFree = (rpmts (*)(rpmts))f; + + rpmtsFree(ts); +}*/ +import "C" + +// Constants in sync with /usr/include/rpm/rpmtag.h +const ( + RPMTAG_NAME = 1000 + RPMTAG_VERSION = 1001 + RPMTAG_RELEASE = 1002 + RPMTAG_SUMMARY = 1004 + RPMTAG_LICENSE = 1014 + RPMTAG_URL = 1020 + RPMTAG_ARCH = 1022 + RPMTAG_SIZE = 1009 + RPMTAG_INSTALLTIME = 1008 +) + +type cFunctions struct { + rpmtsCreate unsafe.Pointer + rpmReadConfigFiles unsafe.Pointer + rpmtsInitIterator unsafe.Pointer + rpmdbNextIterator unsafe.Pointer + headerLink unsafe.Pointer + headerGetEntry unsafe.Pointer + headerFree unsafe.Pointer + rpmdbFreeIterator unsafe.Pointer + rpmtsFree unsafe.Pointer +} + +var cFun *cFunctions + +func dlopenCFunctions() (*cFunctions, error) { + var librpmNames = []string{ + "/usr/lib64/librpm.so", + } + var cFun cFunctions + + librpm, err := dlopen.GetHandle(librpmNames) + if err != nil { + return nil, err + } + + cFun.rpmtsCreate, err = librpm.GetSymbolPointer("rpmtsCreate") + if err != nil { + return nil, err + } + + cFun.rpmReadConfigFiles, err = librpm.GetSymbolPointer("rpmReadConfigFiles") + if err != nil { + return nil, err + } + + cFun.rpmtsInitIterator, err = librpm.GetSymbolPointer("rpmtsInitIterator") + if err != nil { + return nil, err + } + + cFun.rpmdbNextIterator, err = librpm.GetSymbolPointer("rpmdbNextIterator") + if err != nil { + return nil, err + } + + cFun.headerLink, err = librpm.GetSymbolPointer("headerLink") + if err != nil { + return nil, err + } + + cFun.headerGetEntry, err = librpm.GetSymbolPointer("headerGetEntry") + if err != nil { + return nil, err + } + + cFun.headerFree, err = librpm.GetSymbolPointer("headerFree") + if err != nil { + return nil, err + } + + cFun.rpmdbFreeIterator, err = librpm.GetSymbolPointer("rpmdbFreeIterator") + if err != nil { + return nil, err + } + + cFun.rpmtsFree, err = librpm.GetSymbolPointer("rpmtsFree") + if err != nil { + return nil, err + } + + return &cFun, nil +} + +func listRPMPackages() ([]*Package, error) { + if cFun == nil { + var err error + cFun, err = dlopenCFunctions() + if err != nil { + return nil, err + } + } + + rpmts := C.my_rpmtsCreate(cFun.rpmtsCreate) + if rpmts == nil { + return nil, fmt.Errorf("Failed to get rpmts") + } + defer C.my_rpmtsFree(cFun.rpmtsFree, rpmts) + res := C.my_rpmReadConfigFiles(cFun.rpmReadConfigFiles) + if int(res) != 0 { + return nil, fmt.Errorf("Error: %d", int(res)) + } + + mi := C.my_rpmtsInitIterator(cFun.rpmtsInitIterator, rpmts) + if mi == nil { + return nil, fmt.Errorf("Failed to get match iterator") + } + defer C.my_rpmdbFreeIterator(cFun.rpmdbFreeIterator, mi) + + var packages []*Package + for header := C.my_rpmdbNextIterator(cFun.rpmdbNextIterator, mi); header != nil; header = C.my_rpmdbNextIterator(cFun.rpmdbNextIterator, mi) { + + pkg, err := packageFromHeader(header, cFun) + if err != nil { + return nil, err + } + + packages = append(packages, pkg) + } + + return packages, nil +} + +func packageFromHeader(header C.Header, cFun *cFunctions) (*Package, error) { + + header = C.my_headerLink(cFun.headerLink, header) + if header == nil { + return nil, fmt.Errorf("Error calling headerLink") + } + defer C.my_headerFree(cFun.headerFree, header) + + pkg := Package{} + + var name *C.char + res := C.my_headerGetEntry(cFun.headerGetEntry, header, RPMTAG_NAME, &name) + if res != 1 { + return nil, fmt.Errorf("Failed to call headerGetEntry(name): %d", res) + } + pkg.Name = C.GoString(name) + + var version *C.char + res = C.my_headerGetEntry(cFun.headerGetEntry, header, RPMTAG_VERSION, &version) + if res != 1 { + return nil, fmt.Errorf("Failed to call headerGetEntry(version): %d", res) + } + pkg.Version = C.GoString(version) + + var release *C.char + res = C.my_headerGetEntry(cFun.headerGetEntry, header, RPMTAG_RELEASE, &release) + if res != 1 { + return nil, fmt.Errorf("Failed to call headerGetEntry(release): %d", res) + } + pkg.Release = C.GoString(release) + + var license *C.char + res = C.my_headerGetEntry(cFun.headerGetEntry, header, RPMTAG_LICENSE, &license) + if res != 1 { + return nil, fmt.Errorf("Failed to call headerGetEntry(license): %d", res) + } + pkg.License = C.GoString(license) + + var arch *C.char + res = C.my_headerGetEntry(cFun.headerGetEntry, header, RPMTAG_ARCH, &arch) + if res == 1 { // not always successful + pkg.Arch = C.GoString(arch) + } + + var url *C.char + res = C.my_headerGetEntry(cFun.headerGetEntry, header, RPMTAG_URL, &url) + if res == 1 { // not always successful + pkg.URL = C.GoString(url) + } + + var summary *C.char + res = C.my_headerGetEntry(cFun.headerGetEntry, header, RPMTAG_SUMMARY, &summary) + if res == 1 { // not always successful + pkg.Summary = C.GoString(summary) + } + + var size *C.int + res = C.my_headerGetEntryInt(cFun.headerGetEntry, header, RPMTAG_SIZE, &size) + if res != 1 { + return nil, fmt.Errorf("Failed to call headerGetEntry(size): %d", res) + } + pkg.Size = uint64(*size) + + var installTime *C.int + res = C.my_headerGetEntryInt(cFun.headerGetEntry, header, RPMTAG_INSTALLTIME, &installTime) + if res != 1 { + return nil, fmt.Errorf("Failed to call headerGetEntry(installTime): %d", res) + } + pkg.InstallTime = time.Unix(int64(*installTime), 0) + + return &pkg, nil +} diff --git a/x-pack/auditbeat/module/system/package/rpm_linux_test.go b/x-pack/auditbeat/module/system/package/rpm_linux_test.go new file mode 100644 index 00000000000..989fb740876 --- /dev/null +++ b/x-pack/auditbeat/module/system/package/rpm_linux_test.go @@ -0,0 +1,38 @@ +// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +// or more contributor license agreements. Licensed under the Elastic License; +// you may not use this file except in compliance with the Elastic License. + +// +build linux,cgo + +package pkg + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestRPMPackages(t *testing.T) { + os, err := getOS() + if err != nil { + t.Fatal(err) + } + + if os.Family != "redhat" { + t.Skip("RPM test only on Redhat systems") + } + + // Control using the exec command + packagesExpected, err := rpmPackagesByExec() + if err != nil { + t.Fatal(err) + } + + packages, err := listRPMPackages() + if err != nil { + t.Fatal(err) + } + + assert.EqualValues(t, packagesExpected, packages) + +} diff --git a/x-pack/auditbeat/module/system/package/rpm_others.go b/x-pack/auditbeat/module/system/package/rpm_others.go new file mode 100644 index 00000000000..8dd8462197b --- /dev/null +++ b/x-pack/auditbeat/module/system/package/rpm_others.go @@ -0,0 +1,14 @@ +// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +// or more contributor license agreements. Licensed under the Elastic License; +// you may not use this file except in compliance with the Elastic License. + +// +build !linux !cgo +// +build !windows + +package pkg + +import "github.com/pkg/errors" + +func listRPMPackages() ([]*Package, error) { + return nil, errors.New("listing RPM packages is only supported on Linux") +} diff --git a/x-pack/auditbeat/module/system/packages/_meta/data.json b/x-pack/auditbeat/module/system/packages/_meta/data.json deleted file mode 100644 index 36bbb2fd0ba..00000000000 --- a/x-pack/auditbeat/module/system/packages/_meta/data.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "@timestamp": "2017-10-12T08:05:34.853Z", - "beat": { - "hostname": "host.example.com", - "name": "host.example.com" - }, - "metricset": { - "module": "system", - "name": "packages", - "rtt": 115 - }, - "system": { - "packages": { - "package": [ - { - "arch": "amd64", - "installtime": "0001-01-01T00:00:00Z", - "license": "", - "name": "vim-tiny", - "release": "", - "size": 1271, - "status": "installed", - "summary": "Vi IMproved - enhanced vi editor - compact version", - "url": "", - "version": "2:8.0.1453-1ubuntu1" - } - ] - } - } -} diff --git a/x-pack/auditbeat/module/system/packages/_meta/docs.asciidoc.disabled b/x-pack/auditbeat/module/system/packages/_meta/docs.asciidoc.disabled deleted file mode 100644 index 4dd5d809c94..00000000000 --- a/x-pack/auditbeat/module/system/packages/_meta/docs.asciidoc.disabled +++ /dev/null @@ -1,8 +0,0 @@ -The System `packages` dataset provides ... TODO. - -The module is implemented for Linux, macOS (Darwin), and Windows. - -[float] -=== Configuration options - -TODO diff --git a/x-pack/auditbeat/module/system/packages/_meta/fields.yml.disabled b/x-pack/auditbeat/module/system/packages/_meta/fields.yml.disabled deleted file mode 100644 index f57093bc150..00000000000 --- a/x-pack/auditbeat/module/system/packages/_meta/fields.yml.disabled +++ /dev/null @@ -1,50 +0,0 @@ -- name: packages - type: group - description: > - `packages` contains information about installed packages. - release: experimental - fields: - - name: package - type: array - description: > - One or more packages. - fields: - - name: status - type: keyword - description: > - Package change - `new`, `installed` or `removed`. - - name: package.name - type: keyword - description: > - Package name. - - name: package.version - type: keyword - description: > - Package version. - - name: package.release - type: keyword - description: > - Package release. - - name: package.arch - type: keyword - description: > - Package architecture. - - name: package.license - type: keyword - description: > - Package license. - - name: package.installtime - type: date - description: > - Package install time. - - name: package.size - type: long - description: > - Package size. - - name: package.summary - description: > - Package summary. - - name: package.url - type: keyword - description: > - Package URL. diff --git a/x-pack/auditbeat/module/system/packages/config.go b/x-pack/auditbeat/module/system/packages/config.go deleted file mode 100644 index 05fdce2fcb1..00000000000 --- a/x-pack/auditbeat/module/system/packages/config.go +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one -// or more contributor license agreements. Licensed under the Elastic License; -// you may not use this file except in compliance with the Elastic License. - -package packages - -// Config defines the host metricset's configuration options. -type Config struct { - ReportChanges bool `config:"packages.report_changes"` -} - -// Validate validates the host metricset config. -func (c *Config) Validate() error { - return nil -} - -var defaultConfig = Config{ - ReportChanges: true, -} diff --git a/x-pack/auditbeat/module/system/packages/packages.go b/x-pack/auditbeat/module/system/packages/packages.go deleted file mode 100644 index 6cb0216b249..00000000000 --- a/x-pack/auditbeat/module/system/packages/packages.go +++ /dev/null @@ -1,338 +0,0 @@ -// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one -// or more contributor license agreements. Licensed under the Elastic License; -// you may not use this file except in compliance with the Elastic License. - -package packages - -import ( - "bufio" - "fmt" - "io/ioutil" - "os" - "path" - "strconv" - "strings" - "time" - - "github.com/pkg/errors" - - "github.com/elastic/beats/libbeat/common" - "github.com/elastic/beats/libbeat/common/cfgwarn" - "github.com/elastic/beats/metricbeat/mb" - "github.com/elastic/beats/x-pack/auditbeat/cache" - "github.com/elastic/go-sysinfo/types" - - "github.com/OneOfOne/xxhash" - - "github.com/elastic/beats/libbeat/logp" - "github.com/elastic/go-sysinfo" -) - -const ( - moduleName = "system" - metricsetName = "packages" - //namespace = "system.audit.packages" - - redhat = "redhat" - debian = "debian" - darwin = "darwin" -) - -func init() { - /*mb.Registry.MustAddMetricSet(moduleName, metricsetName, New, - mb.DefaultMetricSet(), - mb.WithNamespace(namespace), - )*/ -} - -// MetricSet collects data about the host. -type MetricSet struct { - mb.BaseMetricSet - config Config - osFamily string - cache *cache.Cache - log *logp.Logger -} - -// Package represents information for a package. -type Package struct { - Name string - Version string - Release string - Arch string - License string - InstallTime time.Time - Size uint64 - Summary string - URL string -} - -// Hash creates a hash for Package. -func (pkg Package) Hash() uint64 { - h := xxhash.New64() - h.WriteString(pkg.Name) - h.WriteString(pkg.InstallTime.String()) - return h.Sum64() -} - -func (pkg Package) toMapStr() common.MapStr { - return common.MapStr{ - "name": pkg.Name, - "version": pkg.Version, - "release": pkg.Release, - "arch": pkg.Arch, - "license": pkg.License, - "installtime": pkg.InstallTime, - "size": pkg.Size, - "summary": pkg.Summary, - "url": pkg.URL, - } -} - -func getOS() (*types.OSInfo, error) { - host, err := sysinfo.Host() - if err != nil { - return nil, errors.Wrap(err, "error getting the OS") - } - - hostInfo := host.Info() - if hostInfo.OS == nil { - return nil, errors.New("no host info") - } - - return hostInfo.OS, nil -} - -// New constructs a new MetricSet. -func New(base mb.BaseMetricSet) (mb.MetricSet, error) { - cfgwarn.Experimental("The %v/%v dataset is experimental", moduleName, metricsetName) - - config := defaultConfig - if err := base.Module().UnpackConfig(&config); err != nil { - return nil, errors.Wrapf(err, "failed to unpack the %v/%v config", moduleName, metricsetName) - } - - ms := &MetricSet{ - BaseMetricSet: base, - config: config, - log: logp.NewLogger(moduleName), - } - - if os, err := getOS(); err == nil { - switch os.Family { - case redhat, debian, darwin: - ms.osFamily = os.Family - default: - return nil, fmt.Errorf("this metricset does not support OS family %v", os.Family) - } - } else if err != nil { - return nil, err - } - - if config.ReportChanges { - ms.cache = cache.New() - } - - return ms, nil -} - -// Fetch collects data about the host. It is invoked periodically. -func (ms *MetricSet) Fetch(report mb.ReporterV2) { - packages, err := getPackages(ms.osFamily) - if err != nil { - ms.log.Error(err) - report.Error(err) - return - } - - if ms.cache != nil && !ms.cache.IsEmpty() { - installed, removed := ms.cache.DiffAndUpdateCache(convertToCacheable(packages)) - - for _, pkgInfo := range installed { - pkgInfoMapStr := pkgInfo.(*Package).toMapStr() - pkgInfoMapStr.Put("status", "new") - - report.Event(mb.Event{ - MetricSetFields: common.MapStr{ - "package": pkgInfoMapStr, - }, - }) - } - - for _, pkgInfo := range removed { - pkgInfoMapStr := pkgInfo.(*Package).toMapStr() - pkgInfoMapStr.Put("status", "removed") - - report.Event(mb.Event{ - MetricSetFields: common.MapStr{ - "package": pkgInfoMapStr, - }, - }) - } - } else { - // Report all installed packages - var pkgInfos []common.MapStr - - for _, pkgInfo := range packages { - pkgInfoMapStr := pkgInfo.toMapStr() - pkgInfoMapStr.Put("status", "installed") - - pkgInfos = append(pkgInfos, pkgInfoMapStr) - } - - report.Event(mb.Event{ - MetricSetFields: common.MapStr{ - "package": pkgInfos, - }, - }) - - if ms.cache != nil { - // This will initialize the cache with the current packages - ms.cache.DiffAndUpdateCache(convertToCacheable(packages)) - } - } -} - -func convertToCacheable(packages []*Package) []cache.Cacheable { - c := make([]cache.Cacheable, 0, len(packages)) - - for _, p := range packages { - c = append(c, p) - } - - return c -} - -func getPackages(osFamily string) (packages []*Package, err error) { - switch osFamily { - case redhat: - // TODO: Implement RPM - err = errors.New("RPM not yet supported") - case debian: - packages, err = listDebPackages() - if err != nil { - err = errors.Wrap(err, "error getting DEB packages") - } - case darwin: - packages, err = listBrewPackages() - if err != nil { - err = errors.Wrap(err, "error getting Homebrew packages") - } - default: - panic("unknown OS - this should not have happened") - } - - return -} - -func listDebPackages() ([]*Package, error) { - const statusFile = "/var/lib/dpkg/status" - file, err := os.Open(statusFile) - if err != nil { - return nil, errors.Wrapf(err, "error opening '%s'", statusFile) - } - defer file.Close() - - var packages []*Package - pkg := &Package{} - scanner := bufio.NewScanner(file) - for scanner.Scan() { - line := scanner.Text() - if len(strings.TrimSpace(line)) == 0 { - // empty line signals new package - packages = append(packages, pkg) - pkg = &Package{} - continue - } - if strings.HasPrefix(line, " ") { - // not interested in multi-lines for now - continue - } - words := strings.SplitN(line, ":", 2) - if len(words) != 2 { - return nil, fmt.Errorf("the following line was unexpected (no ':' found): '%s'", line) - } - value := strings.TrimSpace(words[1]) - switch strings.ToLower(words[0]) { - case "package": - pkg.Name = value - case "architecture": - pkg.Arch = value - case "version": - pkg.Version = value - case "description": - pkg.Summary = value - case "installed-size": - pkg.Size, err = strconv.ParseUint(value, 10, 64) - if err != nil { - return nil, errors.Wrapf(err, "error converting %s to int", value) - } - default: - continue - } - } - if err = scanner.Err(); err != nil { - return nil, errors.Wrap(err, "error scanning file") - } - return packages, nil -} - -func listBrewPackages() ([]*Package, error) { - const cellarPath = "/usr/local/Cellar" - - packageDirs, err := ioutil.ReadDir(cellarPath) - if os.IsNotExist(err) { - return nil, errors.Wrapf(err, "%s does not exist - is Homebrew installed?", cellarPath) - } else if err != nil { - return nil, errors.Wrapf(err, "error reading directory %s", cellarPath) - } - - var packages []*Package - for _, packageDir := range packageDirs { - if !packageDir.IsDir() { - continue - } - pkgPath := path.Join(cellarPath, packageDir.Name()) - versions, err := ioutil.ReadDir(pkgPath) - if err != nil { - return nil, errors.Wrapf(err, "error reading directory: %s", pkgPath) - } - - for _, version := range versions { - if !version.IsDir() { - continue - } - pkg := &Package{ - Name: packageDir.Name(), - Version: version.Name(), - InstallTime: version.ModTime(), - } - - // read formula - formulaPath := path.Join(cellarPath, pkg.Name, pkg.Version, ".brew", pkg.Name+".rb") - file, err := os.Open(formulaPath) - if err != nil { - //fmt.Printf("WARNING: Can't get formula for package %s-%s\n", pkg.Name, pkg.Version) - // TODO: follow the path from INSTALL_RECEIPT.json to find the formula - continue - } - scanner := bufio.NewScanner(file) - count := 15 // only look into the first few lines of the formula - for scanner.Scan() { - count-- - if count == 0 { - break - } - line := scanner.Text() - if strings.HasPrefix(line, " desc ") { - pkg.Summary = strings.Trim(line[7:], " \"") - } else if strings.HasPrefix(line, " homepage ") { - pkg.URL = strings.Trim(line[11:], " \"") - } - } - - packages = append(packages, pkg) - } - } - return packages, nil -} diff --git a/x-pack/auditbeat/tests/files/wtmp b/x-pack/auditbeat/tests/files/wtmp new file mode 100644 index 00000000000..4b49a1c0b74 Binary files /dev/null and b/x-pack/auditbeat/tests/files/wtmp differ diff --git a/x-pack/auditbeat/tests/system/auditbeat_xpack.py b/x-pack/auditbeat/tests/system/auditbeat_xpack.py index bf3b3edf65c..af8e5ae68fa 100644 --- a/x-pack/auditbeat/tests/system/auditbeat_xpack.py +++ b/x-pack/auditbeat/tests/system/auditbeat_xpack.py @@ -29,14 +29,17 @@ def setUp(self): ) # Adapted from metricbeat.py - def check_metricset(self, module, metricset, fields=[], errors_allowed=False, warnings_allowed=False): + def check_metricset(self, module, metricset, fields=[], extras={}, errors_allowed=False, warnings_allowed=False): """ Method to test a metricset for its fields """ + # Set to 1 hour so we only test one Fetch + extras["period"] = "1h" + self.render_config_template(modules=[{ "name": module, "datasets": [metricset], - "period": "10s", + "extras": extras, }]) proc = self.start_beat() self.wait_until(lambda: self.output_lines() > 0) diff --git a/x-pack/auditbeat/tests/system/test_metricsets.py b/x-pack/auditbeat/tests/system/test_metricsets.py index d4b5fff28ca..0952eabc382 100644 --- a/x-pack/auditbeat/tests/system/test_metricsets.py +++ b/x-pack/auditbeat/tests/system/test_metricsets.py @@ -1,5 +1,6 @@ import jinja2 import os +import platform import sys import time import unittest @@ -21,16 +22,36 @@ def test_metricset_host(self): # Metricset is experimental and that generates a warning, TODO: remove later self.check_metricset("system", "host", COMMON_FIELDS + fields, warnings_allowed=True) - @unittest.skip("Packages metricset is disabled") - def test_metricset_packages(self): + @unittest.skipUnless(sys.platform == "linux2", "Only implemented for Linux") + @unittest.skipIf(sys.byteorder != "little", "Test only implemented for little-endian systems") + def test_metricset_login(self): + """ + login metricset collects information about logins (successful and failed) and system restarts. + """ + + fields = ["event.origin", "event.outcome", "message", "process.pid", "source.ip", + "user.name", "user.terminal"] + + config = { + "login.wtmp_file_pattern": os.path.abspath(os.path.join(self.beat_path, "tests/files/wtmp")), + "login.btmp_file_pattern": "-1" + } + + # Metricset is experimental and that generates a warning, TODO: remove later + self.check_metricset("system", "login", COMMON_FIELDS + fields, config, warnings_allowed=True) + + @unittest.skipIf(sys.platform == "win32", "Not implemented for Windows") + @unittest.skipIf(sys.platform == "linux2" and not (os.path.isdir("/var/lib/dpkg") or os.path.isdir("/var/lib/rpm")), + "Only implemented for dpkg and rpm") + def test_metricset_package(self): """ - packages metricset collects information about installed packages on a system. + package metricset collects information about installed packages on a system. """ - fields = ["system.audit.packages.package"] + fields = ["system.audit.package.name", "system.audit.package.version", "system.audit.package.installtime"] # Metricset is experimental and that generates a warning, TODO: remove later - self.check_metricset("system", "packages", COMMON_FIELDS + fields, warnings_allowed=True) + self.check_metricset("system", "package", COMMON_FIELDS + fields, warnings_allowed=True) def test_metricset_process(self): """ @@ -38,12 +59,12 @@ def test_metricset_process(self): """ fields = ["process.pid", "process.ppid", "process.name", "process.executable", "process.args", - "process.start", "process.working_directory", "user.id", "user.group.id", "user.group.name"] + "process.start", "process.working_directory", "user.id", "user.group.id"] # Windows does not have effective and saved IDs, and user.name is not always filled for system processes. if sys.platform != "win32": fields.extend(["user.effective.id", "user.saved.id", "user.effective.group.id", "user.saved.group.id", - "user.name"]) + "user.name", "user.group.name"]) # Metricset is experimental and that generates a warning, TODO: remove later self.check_metricset("system", "process", COMMON_FIELDS + fields, warnings_allowed=True) diff --git a/x-pack/filebeat/filebeat.reference.yml b/x-pack/filebeat/filebeat.reference.yml index e84f3acdb1e..56279565fb0 100644 --- a/x-pack/filebeat/filebeat.reference.yml +++ b/x-pack/filebeat/filebeat.reference.yml @@ -206,6 +206,18 @@ filebeat.modules: # can be added under this section. #input: +#------------------------------- Iptables Module ------------------------------- +- module: iptables + log: + enabled: true + + # Set which input to use between syslog (default) or file. + #var.input: + + # Set custom paths for the log files. If left empty, + # Filebeat will choose the paths depending on your OS. + #var.paths: + #-------------------------------- Kafka Module -------------------------------- - module: kafka # All logs @@ -1111,11 +1123,6 @@ output.elasticsearch: # IPv6 addresses should always be defined as: https://[2001:db8::1]:9200 hosts: ["localhost:9200"] - # Enabled ilm (beta) to use index lifecycle management instead daily indices. - #ilm.enabled: false - #ilm.rollover_alias: "filebeat" - #ilm.pattern: "{now/d}-000001" - # Set gzip compression level. #compression_level: 0 @@ -1762,6 +1769,25 @@ setup.template.settings: #_source: #enabled: false +#============================== Setup ILM ===================================== + +# Configure Index Lifecycle Management Index Lifecycle Management creates a +# write alias and adds additional settings to the template. +# The elasticsearch.output.index setting will be replaced with the write alias +# if ILM is enabled. + +# Enabled ILM support. Valid values are true, false, and auto. The beat will +# detect availabilty of Index Lifecycle Management in Elasticsearch and enable +# or disable ILM support. +#setup.ilm.enabled: auto + +# Configure the ILM write alias name. +#setup.ilm.rollover_alias: "filebeat" + +# Configure rollover index pattern. +#setup.ilm.pattern: "{now/d}-000001" + + #============================== Kibana ===================================== # Starting with Beats version 6.0.0, the dashboards are loaded via the Kibana API. diff --git a/x-pack/filebeat/filebeat.yml b/x-pack/filebeat/filebeat.yml index 2664206c560..c06827965ff 100644 --- a/x-pack/filebeat/filebeat.yml +++ b/x-pack/filebeat/filebeat.yml @@ -78,7 +78,7 @@ filebeat.config.modules: #==================== Elasticsearch template setting ========================== setup.template.settings: - index.number_of_shards: 3 + index.number_of_shards: 1 #index.codec: best_compression #_source.enabled: false @@ -101,7 +101,7 @@ setup.template.settings: #============================== Dashboards ===================================== # These settings control loading the sample dashboards to the Kibana index. Loading # the dashboards is disabled by default and can be enabled either by setting the -# options here, or by using the `-setup` CLI flag or the `setup` command. +# options here or by using the `setup` command. #setup.dashboards.enabled: false # The URL from where to download the dashboards archive. By default this URL @@ -149,9 +149,6 @@ output.elasticsearch: # Array of hosts to connect to. hosts: ["localhost:9200"] - # Enabled ilm (beta) to use index lifecycle management instead daily indices. - #ilm.enabled: false - # Optional protocol and basic auth credentials. #protocol: "https" #username: "elastic" diff --git a/x-pack/filebeat/include/list.go b/x-pack/filebeat/include/list.go index 529f23b9c1f..f4550a6e898 100644 --- a/x-pack/filebeat/include/list.go +++ b/x-pack/filebeat/include/list.go @@ -9,6 +9,7 @@ package include import ( // Import packages that need to register themselves. _ "github.com/elastic/beats/x-pack/filebeat/input/netflow" + _ "github.com/elastic/beats/x-pack/filebeat/module/iptables" _ "github.com/elastic/beats/x-pack/filebeat/module/suricata" _ "github.com/elastic/beats/x-pack/filebeat/module/zeek" ) diff --git a/x-pack/filebeat/input/netflow/_meta/kibana/6/dashboard/filebeat-network-flows-top-n.json b/x-pack/filebeat/input/netflow/_meta/kibana/7/dashboard/filebeat-network-flows-top-n.json similarity index 100% rename from x-pack/filebeat/input/netflow/_meta/kibana/6/dashboard/filebeat-network-flows-top-n.json rename to x-pack/filebeat/input/netflow/_meta/kibana/7/dashboard/filebeat-network-flows-top-n.json diff --git a/x-pack/filebeat/module/iptables/README.md b/x-pack/filebeat/module/iptables/README.md new file mode 100644 index 00000000000..10ca22ff33c --- /dev/null +++ b/x-pack/filebeat/module/iptables/README.md @@ -0,0 +1,6 @@ +# iptables module + +## Caveats + +* Module is to be considered _beta_. + diff --git a/x-pack/filebeat/module/iptables/_meta/config.yml b/x-pack/filebeat/module/iptables/_meta/config.yml new file mode 100644 index 00000000000..0de64687f6e --- /dev/null +++ b/x-pack/filebeat/module/iptables/_meta/config.yml @@ -0,0 +1,10 @@ +- module: iptables + log: + enabled: true + + # Set which input to use between syslog (default) or file. + #var.input: + + # Set custom paths for the log files. If left empty, + # Filebeat will choose the paths depending on your OS. + #var.paths: diff --git a/x-pack/filebeat/module/iptables/_meta/docs.asciidoc b/x-pack/filebeat/module/iptables/_meta/docs.asciidoc new file mode 100644 index 00000000000..c158cf3e1bc --- /dev/null +++ b/x-pack/filebeat/module/iptables/_meta/docs.asciidoc @@ -0,0 +1,72 @@ +[role="xpack"] + +:modulename: iptables +:has-dashboards: true + +== Iptables module + +This is a module for iptables and ip6tables logs. It parses logs received +over the network via syslog or from a file. Also, it understands the prefix added +by some Ubiquiti firewalls, which includes the rule set name, rule number and +the action performed on the traffic (allow/deny). + +When you run the module, it performs a few tasks under the hood: + +* Sets the default input to `syslog` and binds to `localhost` port `9001` + (but don’t worry, you can override the defaults). + +* Uses ingest node to parse and process the log lines, shaping the data into + a structure suitable for visualizing in Kibana. + +* Deploys dashboards for visualizing the log data. + +[float] +=== Compatibility + +This module requires the {elasticsearch-plugins}/ingest-geoip.html[ingest-geoip] +Elasticsearch plugins. + +include::../include/running-modules.asciidoc[] + +[float] +=== Example dashboard + +This module comes with sample dashboards showing geolocation and network +protocols used. One for all iptables logs: + +[role="screenshot"] +image::./images/kibana-iptables.png[] + +and one specific for Ubiquiti Firewall logs: + +[role="screenshot"] +image::./images/kibana-iptables-ubiquiti.png[] + +include::../include/configuring-intro.asciidoc[] + +The module is by default configured to run via syslog on port 9001. However +it can also be configured to read from a file path. See the following example. + +["source","yaml",subs="attributes"] +----- +- module: iptables + log: + enabled: true + var.paths: ["/var/log/iptables.log"] + var.input: "file" +----- + +:fileset_ex: log + +include::../include/config-option-intro.asciidoc[] + +[float] +==== `log` log fileset settings + +include::../include/var-paths.asciidoc[] + +:has-dashboards!: + +:fileset_ex!: + +:modulename!: diff --git a/x-pack/filebeat/module/iptables/_meta/fields.yml b/x-pack/filebeat/module/iptables/_meta/fields.yml new file mode 100644 index 00000000000..797e069697d --- /dev/null +++ b/x-pack/filebeat/module/iptables/_meta/fields.yml @@ -0,0 +1,10 @@ +- key: iptables + title: iptables + description: > + Module for handling the iptables logs. + fields: + - name: iptables + type: group + description: > + Fields from the iptables logs. + fields: diff --git a/x-pack/filebeat/module/iptables/_meta/kibana/6/dashboard/Filebeat-Iptables-Overview.json b/x-pack/filebeat/module/iptables/_meta/kibana/6/dashboard/Filebeat-Iptables-Overview.json new file mode 100644 index 00000000000..e10f783d764 --- /dev/null +++ b/x-pack/filebeat/module/iptables/_meta/kibana/6/dashboard/Filebeat-Iptables-Overview.json @@ -0,0 +1,759 @@ +{ + "objects": [ + { + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "query": { + "language": "kuery", + "query": "" + } + } + }, + "savedSearchId": "b3f1b010-1f26-11e9-8ec4-cf5d91a864b3", + "title": "Events Timeline [Filebeat Iptables]", + "uiStateJSON": {}, + "version": 1, + "visState": { + "aggs": [ + { + "enabled": true, + "id": "1", + "params": {}, + "schema": "metric", + "type": "count" + }, + { + "enabled": true, + "id": "2", + "params": { + "customInterval": "2h", + "drop_partials": false, + "extended_bounds": {}, + "field": "@timestamp", + "interval": "auto", + "min_doc_count": 1, + "time_zone": "Europe/Berlin", + "useNormalizedEsInterval": true + }, + "schema": "segment", + "type": "date_histogram" + } + ], + "params": { + "addLegend": true, + "addTimeMarker": false, + "addTooltip": true, + "categoryAxes": [ + { + "id": "CategoryAxis-1", + "labels": { + "show": true, + "truncate": 100 + }, + "position": "bottom", + "scale": { + "type": "linear" + }, + "show": true, + "style": {}, + "title": {}, + "type": "category" + } + ], + "grid": { + "categoryLines": false, + "style": { + "color": "#eee" + } + }, + "legendPosition": "right", + "seriesParams": [ + { + "data": { + "id": "1", + "label": "Count" + }, + "drawLinesBetweenPoints": true, + "interpolate": "linear", + "mode": "stacked", + "show": "true", + "showCircles": true, + "type": "area", + "valueAxis": "ValueAxis-1" + } + ], + "times": [], + "type": "area", + "valueAxes": [ + { + "id": "ValueAxis-1", + "labels": { + "filter": false, + "rotate": 0, + "show": true, + "truncate": 100 + }, + "name": "LeftAxis-1", + "position": "left", + "scale": { + "mode": "normal", + "type": "linear" + }, + "show": true, + "style": {}, + "title": { + "text": "Count" + }, + "type": "value" + } + ] + }, + "title": "Events Timeline [Filebeat Iptables]", + "type": "area" + } + }, + "id": "4c913eb0-1f51-11e9-93ed-f7e068f4aebb", + "type": "visualization", + "updated_at": "2019-01-23T20:56:04.891Z", + "version": 1 + }, + { + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "query": { + "language": "kuery", + "query": "iptables.length:*" + } + } + }, + "savedSearchId": "b3f1b010-1f26-11e9-8ec4-cf5d91a864b3", + "title": "Top Source Countries [Filebeat Iptables]", + "uiStateJSON": { + "vis": { + "params": { + "sort": { + "columnIndex": null, + "direction": null + } + } + } + }, + "version": 1, + "visState": { + "aggs": [ + { + "enabled": true, + "id": "1", + "params": {}, + "schema": "metric", + "type": "count" + }, + { + "enabled": true, + "id": "2", + "params": { + "customLabel": "Country", + "field": "source.geo.country_iso_code", + "missingBucket": false, + "missingBucketLabel": "Missing", + "order": "desc", + "orderBy": "1", + "otherBucket": false, + "otherBucketLabel": "Other", + "size": 5 + }, + "schema": "bucket", + "type": "terms" + } + ], + "params": { + "perPage": 10, + "showMetricsAtAllLevels": false, + "showPartialRows": false, + "showTotal": false, + "sort": { + "columnIndex": null, + "direction": null + }, + "totalFunc": "sum" + }, + "title": "Top Source Countries [Filebeat Iptables]", + "type": "table" + } + }, + "id": "2599f5e0-1e98-11e9-8ec4-cf5d91a864b3", + "type": "visualization", + "updated_at": "2019-01-23T20:51:02.293Z", + "version": 1 + }, + { + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "query": { + "language": "kuery", + "query": "iptables.length:*" + } + } + }, + "savedSearchId": "b3f1b010-1f26-11e9-8ec4-cf5d91a864b3", + "title": "Source Map [Filebeat Iptables]", + "uiStateJSON": { + "mapCenter": [ + 45.02695045318546, + -44.82421875000001 + ], + "mapZoom": 3 + }, + "version": 1, + "visState": { + "aggs": [ + { + "enabled": true, + "id": "1", + "params": {}, + "schema": "metric", + "type": "count" + }, + { + "enabled": true, + "id": "2", + "params": { + "autoPrecision": true, + "field": "source.geo.location", + "isFilteredByCollar": true, + "mapCenter": [ + 0, + 0 + ], + "mapZoom": 2, + "precision": 2, + "useGeocentroid": true + }, + "schema": "segment", + "type": "geohash_grid" + } + ], + "params": { + "addTooltip": true, + "colorSchema": "Yellow to Red", + "heatClusterSize": 1.5, + "isDesaturated": true, + "legendPosition": "bottomright", + "mapCenter": [ + 0, + 0 + ], + "mapType": "Scaled Circle Markers", + "mapZoom": 2, + "wms": { + "enabled": false, + "options": { + "format": "image/png", + "transparent": true + }, + "selectedTmsLayer": { + "attribution": "\u003cp\u003e\u0026#169; \u003ca href=\"http://www.openstreetmap.org/copyright\"\u003eOpenStreetMap\u003c/a\u003e contributors | \u003ca href=\"https://www.elastic.co/elastic-maps-service\"\u003eElastic Maps Service\u003c/a\u003e\u003c/p\u003e\u0026#10;", + "id": "road_map", + "maxZoom": 18, + "minZoom": 0, + "origin": "elastic_maps_service" + } + } + }, + "title": "Source Map [Filebeat Iptables]", + "type": "tile_map" + } + }, + "id": "c4394ec0-1efd-11e9-8ec4-cf5d91a864b3", + "type": "visualization", + "updated_at": "2019-01-23T20:51:02.293Z", + "version": 1 + }, + { + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "query": { + "language": "kuery", + "query": "iptables.length:*" + } + } + }, + "savedSearchId": "b3f1b010-1f26-11e9-8ec4-cf5d91a864b3", + "title": "Destination Map [Filebeat Iptables]", + "uiStateJSON": {}, + "version": 1, + "visState": { + "aggs": [ + { + "enabled": true, + "id": "1", + "params": {}, + "schema": "metric", + "type": "count" + }, + { + "enabled": true, + "id": "2", + "params": { + "autoPrecision": true, + "field": "destination.geo.location", + "isFilteredByCollar": true, + "mapCenter": [ + 0, + 0 + ], + "mapZoom": 2, + "precision": 2, + "useGeocentroid": true + }, + "schema": "segment", + "type": "geohash_grid" + } + ], + "params": { + "addTooltip": true, + "colorSchema": "Yellow to Red", + "heatClusterSize": 1.5, + "isDesaturated": true, + "legendPosition": "bottomright", + "mapCenter": [ + 0, + 0 + ], + "mapType": "Scaled Circle Markers", + "mapZoom": 2, + "wms": { + "enabled": false, + "options": { + "format": "image/png", + "transparent": true + }, + "selectedTmsLayer": { + "attribution": "\u003cp\u003e\u0026#169; \u003ca href=\"http://www.openstreetmap.org/copyright\"\u003eOpenStreetMap\u003c/a\u003e contributors | \u003ca href=\"https://www.elastic.co/elastic-maps-service\"\u003eElastic Maps Service\u003c/a\u003e\u003c/p\u003e\u0026#10;", + "id": "road_map", + "maxZoom": 18, + "minZoom": 0, + "origin": "elastic_maps_service" + } + } + }, + "title": "Destination Map [Filebeat Iptables]", + "type": "tile_map" + } + }, + "id": "d8cea010-1efd-11e9-8ec4-cf5d91a864b3", + "type": "visualization", + "updated_at": "2019-01-23T20:51:02.293Z", + "version": 1 + }, + { + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "query": { + "language": "kuery", + "query": "iptables.length:*" + } + } + }, + "savedSearchId": "b3f1b010-1f26-11e9-8ec4-cf5d91a864b3", + "title": "Network Type Breakdown [Filebeat Iptables]", + "uiStateJSON": { + "vis": { + "legendOpen": false + } + }, + "version": 1, + "visState": { + "aggs": [ + { + "enabled": true, + "id": "1", + "params": {}, + "schema": "metric", + "type": "count" + }, + { + "enabled": true, + "id": "2", + "params": { + "field": "network.type", + "missingBucket": true, + "missingBucketLabel": "Missing", + "order": "desc", + "orderBy": "1", + "otherBucket": true, + "otherBucketLabel": "Other", + "size": 5 + }, + "schema": "segment", + "type": "terms" + } + ], + "params": { + "addLegend": true, + "addTooltip": true, + "isDonut": true, + "labels": { + "last_level": true, + "show": true, + "truncate": 100, + "values": true + }, + "legendPosition": "right", + "type": "pie" + }, + "title": "Network Type Breakdown [Filebeat Iptables]", + "type": "pie" + } + }, + "id": "b57b7370-1f1d-11e9-8ec4-cf5d91a864b3", + "type": "visualization", + "updated_at": "2019-01-23T20:51:02.293Z", + "version": 1 + }, + { + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "query": { + "language": "kuery", + "query": "iptables.length:*" + } + } + }, + "savedSearchId": "b3f1b010-1f26-11e9-8ec4-cf5d91a864b3", + "title": "Network Transport Breakdown [Filebeat Iptables]", + "uiStateJSON": { + "vis": { + "legendOpen": false + } + }, + "version": 1, + "visState": { + "aggs": [ + { + "enabled": true, + "id": "1", + "params": {}, + "schema": "metric", + "type": "count" + }, + { + "enabled": true, + "id": "2", + "params": { + "field": "network.transport", + "missingBucket": true, + "missingBucketLabel": "Missing", + "order": "desc", + "orderBy": "1", + "otherBucket": true, + "otherBucketLabel": "Other", + "size": 5 + }, + "schema": "segment", + "type": "terms" + } + ], + "params": { + "addLegend": true, + "addTooltip": true, + "isDonut": true, + "labels": { + "last_level": true, + "show": true, + "truncate": 100, + "values": true + }, + "legendPosition": "right", + "type": "pie" + }, + "title": "Network Transport Breakdown [Filebeat Iptables]", + "type": "pie" + } + }, + "id": "35fe0910-1f26-11e9-8ec4-cf5d91a864b3", + "type": "visualization", + "updated_at": "2019-01-23T20:51:02.293Z", + "version": 1 + }, + { + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "query": { + "language": "kuery", + "query": "iptables.length:*" + } + } + }, + "savedSearchId": "b3f1b010-1f26-11e9-8ec4-cf5d91a864b3", + "title": "Top Destination Ports [Filebeat Iptables]", + "uiStateJSON": { + "vis": { + "params": { + "sort": { + "columnIndex": null, + "direction": null + } + } + } + }, + "version": 1, + "visState": { + "aggs": [ + { + "enabled": true, + "id": "1", + "params": {}, + "schema": "metric", + "type": "count" + }, + { + "enabled": true, + "id": "2", + "params": { + "customLabel": "Port", + "field": "destination.port", + "missingBucket": false, + "missingBucketLabel": "Missing", + "order": "desc", + "orderBy": "1", + "otherBucket": false, + "otherBucketLabel": "Other", + "size": 5 + }, + "schema": "bucket", + "type": "terms" + } + ], + "params": { + "perPage": 10, + "showMetricsAtAllLevels": false, + "showPartialRows": false, + "showTotal": false, + "sort": { + "columnIndex": null, + "direction": null + }, + "totalFunc": "sum" + }, + "title": "Top Destination Ports [Filebeat Iptables]", + "type": "table" + } + }, + "id": "683402b0-1f29-11e9-8ec4-cf5d91a864b3", + "type": "visualization", + "updated_at": "2019-01-23T20:51:02.293Z", + "version": 1 + }, + { + "attributes": { + "columns": [ + "_source" + ], + "description": "", + "hits": 0, + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "highlightAll": true, + "index": "filebeat-*", + "query": { + "language": "kuery", + "query": "iptables.length :*" + }, + "version": true + } + }, + "sort": [ + "@timestamp", + "desc" + ], + "title": "Events Search [Filebeat Iptables]", + "version": 1 + }, + "id": "b3f1b010-1f26-11e9-8ec4-cf5d91a864b3", + "type": "search", + "updated_at": "2019-01-23T20:51:02.293Z", + "version": 1 + }, + { + "attributes": { + "description": "Overview of the iptables events dashboard.", + "hits": 0, + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "query": { + "language": "kuery", + "query": "" + } + } + }, + "optionsJSON": { + "darkTheme": false, + "hidePanelTitles": false, + "useMargins": true + }, + "panelsJSON": [ + { + "embeddableConfig": { + "vis": { + "legendOpen": false + } + }, + "gridData": { + "h": 15, + "i": "1", + "w": 37, + "x": 0, + "y": 0 + }, + "id": "4c913eb0-1f51-11e9-93ed-f7e068f4aebb", + "panelIndex": "1", + "type": "visualization", + "version": "6.6.0" + }, + { + "embeddableConfig": {}, + "gridData": { + "h": 15, + "i": "2", + "w": 11, + "x": 37, + "y": 0 + }, + "id": "2599f5e0-1e98-11e9-8ec4-cf5d91a864b3", + "panelIndex": "2", + "type": "visualization", + "version": "6.6.0" + }, + { + "embeddableConfig": { + "mapCenter": [ + 47.15984001304432, + -47.02148437500001 + ], + "mapZoom": 2 + }, + "gridData": { + "h": 15, + "i": "3", + "w": 24, + "x": 0, + "y": 15 + }, + "id": "c4394ec0-1efd-11e9-8ec4-cf5d91a864b3", + "panelIndex": "3", + "type": "visualization", + "version": "6.6.0" + }, + { + "embeddableConfig": { + "mapCenter": [ + 49.15296965617042, + -27.949218750000004 + ], + "mapZoom": 2 + }, + "gridData": { + "h": 15, + "i": "4", + "w": 24, + "x": 24, + "y": 15 + }, + "id": "d8cea010-1efd-11e9-8ec4-cf5d91a864b3", + "panelIndex": "4", + "type": "visualization", + "version": "6.6.0" + }, + { + "embeddableConfig": {}, + "gridData": { + "h": 15, + "i": "5", + "w": 19, + "x": 0, + "y": 30 + }, + "id": "b57b7370-1f1d-11e9-8ec4-cf5d91a864b3", + "panelIndex": "5", + "type": "visualization", + "version": "6.6.0" + }, + { + "embeddableConfig": {}, + "gridData": { + "h": 15, + "i": "6", + "w": 18, + "x": 19, + "y": 30 + }, + "id": "35fe0910-1f26-11e9-8ec4-cf5d91a864b3", + "panelIndex": "6", + "type": "visualization", + "version": "6.6.0" + }, + { + "embeddableConfig": {}, + "gridData": { + "h": 15, + "i": "7", + "w": 11, + "x": 37, + "y": 30 + }, + "id": "683402b0-1f29-11e9-8ec4-cf5d91a864b3", + "panelIndex": "7", + "type": "visualization", + "version": "6.6.0" + }, + { + "embeddableConfig": {}, + "gridData": { + "h": 19, + "i": "8", + "w": 48, + "x": 0, + "y": 45 + }, + "id": "b3f1b010-1f26-11e9-8ec4-cf5d91a864b3", + "panelIndex": "8", + "type": "search", + "version": "6.6.0" + } + ], + "timeRestore": false, + "title": "[Filebeat Iptables] Overview", + "version": 1 + }, + "id": "ceefb9e0-1f51-11e9-93ed-f7e068f4aebb", + "type": "dashboard", + "updated_at": "2019-01-23T20:59:43.614Z", + "version": 1 + } + ], + "version": "6.6.0" +} diff --git a/x-pack/filebeat/module/iptables/_meta/kibana/6/dashboard/Filebeat-Iptables-Ubiquiti-Firewall-Overview.json b/x-pack/filebeat/module/iptables/_meta/kibana/6/dashboard/Filebeat-Iptables-Ubiquiti-Firewall-Overview.json new file mode 100644 index 00000000000..000a1bdcd93 --- /dev/null +++ b/x-pack/filebeat/module/iptables/_meta/kibana/6/dashboard/Filebeat-Iptables-Ubiquiti-Firewall-Overview.json @@ -0,0 +1,848 @@ +{ + "objects": [ + { + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "query": { + "language": "kuery", + "query": "" + } + } + }, + "savedSearchId": "c4e80aa0-1fd4-11e9-ae2a-939083c6a64e", + "title": "Ubiquiti Firewall Event Timeline [Filebeat Iptables]", + "uiStateJSON": { + "vis": { + "colors": { + "allow": "#64B0C8", + "deny": "#E24D42" + } + } + }, + "version": 1, + "visState": { + "aggs": [ + { + "enabled": true, + "id": "1", + "params": {}, + "schema": "metric", + "type": "count" + }, + { + "enabled": true, + "id": "2", + "params": { + "customInterval": "2h", + "drop_partials": false, + "extended_bounds": {}, + "field": "@timestamp", + "interval": "auto", + "min_doc_count": 1, + "timeRange": { + "from": "2019-01-24T15:47:12.171Z", + "mode": "absolute", + "to": "2019-01-24T15:47:52.785Z" + }, + "time_zone": "Europe/Berlin", + "useNormalizedEsInterval": true + }, + "schema": "segment", + "type": "date_histogram" + }, + { + "enabled": true, + "id": "3", + "params": { + "field": "event.outcome", + "missingBucket": true, + "missingBucketLabel": "Missing", + "order": "desc", + "orderBy": "_key", + "otherBucket": true, + "otherBucketLabel": "Other", + "size": 5 + }, + "schema": "group", + "type": "terms" + } + ], + "params": { + "addLegend": true, + "addTimeMarker": false, + "addTooltip": true, + "categoryAxes": [ + { + "id": "CategoryAxis-1", + "labels": { + "show": true, + "truncate": 100 + }, + "position": "bottom", + "scale": { + "type": "linear" + }, + "show": true, + "style": {}, + "title": {}, + "type": "category" + } + ], + "grid": { + "categoryLines": false, + "style": { + "color": "#eee" + } + }, + "legendPosition": "top", + "seriesParams": [ + { + "data": { + "id": "1", + "label": "Count" + }, + "drawLinesBetweenPoints": true, + "mode": "stacked", + "show": "true", + "showCircles": true, + "type": "histogram", + "valueAxis": "ValueAxis-1" + } + ], + "times": [], + "type": "histogram", + "valueAxes": [ + { + "id": "ValueAxis-1", + "labels": { + "filter": false, + "rotate": 0, + "show": true, + "truncate": 100 + }, + "name": "LeftAxis-1", + "position": "left", + "scale": { + "mode": "normal", + "type": "linear" + }, + "show": true, + "style": {}, + "title": { + "text": "Count" + }, + "type": "value" + } + ] + }, + "title": "Ubiquiti Firewall Event Timeline [Filebeat Iptables]", + "type": "histogram" + } + }, + "id": "758b3620-1fda-11e9-ae2a-939083c6a64e", + "type": "visualization", + "updated_at": "2019-01-24T16:37:11.788Z", + "version": 2 + }, + { + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "query": { + "language": "kuery", + "query": "" + } + } + }, + "savedSearchId": "9f7d97c0-1fe9-11e9-ae2a-939083c6a64e", + "title": "Ubiquiti Firewall Top Blocked IPs [Filebeat Iptables]", + "uiStateJSON": { + "vis": { + "params": { + "sort": { + "columnIndex": null, + "direction": null + } + } + } + }, + "version": 1, + "visState": { + "aggs": [ + { + "enabled": true, + "id": "1", + "params": {}, + "schema": "metric", + "type": "count" + }, + { + "enabled": true, + "id": "2", + "params": { + "customLabel": "Source IP", + "field": "source.ip", + "missingBucket": false, + "missingBucketLabel": "Missing", + "order": "desc", + "orderBy": "1", + "otherBucket": false, + "otherBucketLabel": "Other", + "size": 5 + }, + "schema": "bucket", + "type": "terms" + } + ], + "params": { + "perPage": 10, + "showMetricsAtAllLevels": false, + "showPartialRows": false, + "showTotal": false, + "sort": { + "columnIndex": null, + "direction": null + }, + "totalFunc": "sum" + }, + "title": "Ubiquiti Firewall Top Blocked IPs [Filebeat Iptables]", + "type": "table" + } + }, + "id": "1ba82fd0-1ff0-11e9-ae2a-939083c6a64e", + "type": "visualization", + "updated_at": "2019-01-24T16:06:20.635Z", + "version": 2 + }, + { + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "query": { + "language": "kuery", + "query": "" + } + } + }, + "savedSearchId": "7862cab0-1fdb-11e9-ae2a-939083c6a64e", + "title": "Ubiquiti Firewall Allowed Traffic Map [Filebeat Iptables]", + "uiStateJSON": {}, + "version": 1, + "visState": { + "aggs": [ + { + "enabled": true, + "id": "1", + "params": {}, + "schema": "metric", + "type": "count" + }, + { + "enabled": true, + "id": "2", + "params": { + "autoPrecision": true, + "field": "source.geo.location", + "isFilteredByCollar": true, + "mapCenter": [ + 0, + 0 + ], + "mapZoom": 2, + "precision": 2, + "useGeocentroid": true + }, + "schema": "segment", + "type": "geohash_grid" + } + ], + "params": { + "addTooltip": true, + "colorSchema": "Yellow to Red", + "heatClusterSize": 1.5, + "isDesaturated": true, + "legendPosition": "bottomright", + "mapCenter": [ + 0, + 0 + ], + "mapType": "Scaled Circle Markers", + "mapZoom": 2, + "wms": { + "enabled": false, + "options": { + "format": "image/png", + "transparent": true + }, + "selectedTmsLayer": { + "attribution": "\u003cp\u003e\u0026#169; \u003ca href=\"http://www.openstreetmap.org/copyright\"\u003eOpenStreetMap\u003c/a\u003e contributors | \u003ca href=\"https://www.elastic.co/elastic-maps-service\"\u003eElastic Maps Service\u003c/a\u003e\u003c/p\u003e\u0026#10;", + "id": "road_map", + "maxZoom": 18, + "minZoom": 0, + "origin": "elastic_maps_service" + } + } + }, + "title": "Ubiquiti Firewall Allowed Traffic Map [Filebeat Iptables]", + "type": "tile_map" + } + }, + "id": "5bd53050-1fe9-11e9-ae2a-939083c6a64e", + "type": "visualization", + "updated_at": "2019-01-24T15:04:34.005Z", + "version": 1 + }, + { + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "query": { + "language": "kuery", + "query": "" + } + } + }, + "savedSearchId": "9f7d97c0-1fe9-11e9-ae2a-939083c6a64e", + "title": "Ubiquiti Firewall Blocked Traffic Map [Filebeat Iptables]", + "uiStateJSON": { + "mapCenter": [ + 19.228176737766262, + -22.851562500000004 + ], + "mapZoom": 3 + }, + "version": 1, + "visState": { + "aggs": [ + { + "enabled": true, + "id": "1", + "params": {}, + "schema": "metric", + "type": "count" + }, + { + "enabled": true, + "id": "2", + "params": { + "autoPrecision": true, + "field": "source.geo.location", + "isFilteredByCollar": true, + "mapCenter": [ + 0, + 0 + ], + "mapZoom": 2, + "precision": 2, + "useGeocentroid": true + }, + "schema": "segment", + "type": "geohash_grid" + } + ], + "params": { + "addTooltip": true, + "colorSchema": "Yellow to Red", + "heatClusterSize": 1.5, + "isDesaturated": true, + "legendPosition": "bottomright", + "mapCenter": [ + 0, + 0 + ], + "mapType": "Scaled Circle Markers", + "mapZoom": 2, + "wms": { + "enabled": false, + "options": { + "format": "image/png", + "transparent": true + }, + "selectedTmsLayer": { + "attribution": "\u003cp\u003e\u0026#169; \u003ca href=\"http://www.openstreetmap.org/copyright\"\u003eOpenStreetMap\u003c/a\u003e contributors | \u003ca href=\"https://www.elastic.co/elastic-maps-service\"\u003eElastic Maps Service\u003c/a\u003e\u003c/p\u003e\u0026#10;", + "id": "road_map", + "maxZoom": 18, + "minZoom": 0, + "origin": "elastic_maps_service" + } + } + }, + "title": "Ubiquiti Firewall Blocked Traffic Map [Filebeat Iptables]", + "type": "tile_map" + } + }, + "id": "8853aa20-1fef-11e9-ae2a-939083c6a64e", + "type": "visualization", + "updated_at": "2019-01-24T15:50:31.689Z", + "version": 2 + }, + { + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "query": { + "language": "kuery", + "query": "" + } + } + }, + "savedSearchId": "c4e80aa0-1fd4-11e9-ae2a-939083c6a64e", + "title": "Ubiquiti Firewall Traffic Breakdown [Filebeat Iptables]", + "uiStateJSON": { + "vis": { + "colors": { + "deny": "#E24D42", + "icmp": "#F29191", + "ipv4": "#65C5DB", + "ipv6": "#D683CE", + "ipv6-icmp": "#EA6460", + "tcp": "#447EBC", + "udp": "#F2C96D" + } + } + }, + "version": 1, + "visState": { + "aggs": [ + { + "enabled": true, + "id": "1", + "params": {}, + "schema": "metric", + "type": "count" + }, + { + "enabled": true, + "id": "2", + "params": { + "field": "event.outcome", + "missingBucket": true, + "missingBucketLabel": "Missing", + "order": "desc", + "orderBy": "1", + "otherBucket": true, + "otherBucketLabel": "Other", + "size": 5 + }, + "schema": "segment", + "type": "terms" + }, + { + "enabled": true, + "id": "3", + "params": { + "field": "network.type", + "missingBucket": true, + "missingBucketLabel": "Missing", + "order": "desc", + "orderBy": "1", + "otherBucket": true, + "otherBucketLabel": "Other", + "size": 5 + }, + "schema": "segment", + "type": "terms" + }, + { + "enabled": true, + "id": "4", + "params": { + "field": "network.transport", + "missingBucket": true, + "missingBucketLabel": "Missing", + "order": "desc", + "orderBy": "1", + "otherBucket": true, + "otherBucketLabel": "Other", + "size": 5 + }, + "schema": "segment", + "type": "terms" + } + ], + "params": { + "addLegend": true, + "addTooltip": true, + "isDonut": true, + "labels": { + "last_level": false, + "show": true, + "truncate": 100, + "values": false + }, + "legendPosition": "top", + "type": "pie" + }, + "title": "Ubiquiti Firewall Traffic Breakdown [Filebeat Iptables]", + "type": "pie" + } + }, + "id": "fdea1ad0-1ff4-11e9-ae2a-939083c6a64e", + "type": "visualization", + "updated_at": "2019-01-24T16:27:50.397Z", + "version": 1 + }, + { + "attributes": { + "columns": [ + "_source" + ], + "description": "", + "hits": 0, + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "highlightAll": true, + "index": "filebeat-*", + "query": { + "language": "kuery", + "query": "iptables.ubiquiti.rule_set :*" + }, + "version": true + } + }, + "sort": [ + "@timestamp", + "desc" + ], + "title": "Ubiquiti Firewall Events [Filebeat Iptables]", + "version": 1 + }, + "id": "c4e80aa0-1fd4-11e9-ae2a-939083c6a64e", + "type": "search", + "updated_at": "2019-01-24T12:37:10.858Z", + "version": 1 + }, + { + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "query": { + "language": "kuery", + "query": "" + } + } + }, + "savedSearchId": "c4e80aa0-1fd4-11e9-ae2a-939083c6a64e", + "title": "Ubiquiti Firewall Traffic by Port [Filebeat Iptables]", + "uiStateJSON": { + "vis": { + "params": { + "sort": { + "columnIndex": null, + "direction": null + } + } + } + }, + "version": 1, + "visState": { + "aggs": [ + { + "enabled": true, + "id": "1", + "params": {}, + "schema": "metric", + "type": "count" + }, + { + "enabled": true, + "id": "2", + "params": { + "customLabel": "event.outcome", + "field": "event.outcome", + "missingBucket": false, + "missingBucketLabel": "Missing", + "order": "desc", + "orderBy": "1", + "otherBucket": false, + "otherBucketLabel": "Other", + "row": false, + "size": 5 + }, + "schema": "split", + "type": "terms" + }, + { + "enabled": true, + "id": "3", + "params": { + "customLabel": "Destination port", + "field": "destination.port", + "missingBucket": false, + "missingBucketLabel": "Missing", + "order": "desc", + "orderBy": "1", + "otherBucket": false, + "otherBucketLabel": "Other", + "size": 5 + }, + "schema": "bucket", + "type": "terms" + } + ], + "params": { + "perPage": 10, + "showMetricsAtAllLevels": false, + "showPartialRows": false, + "showTotal": false, + "sort": { + "columnIndex": null, + "direction": null + }, + "totalFunc": "sum" + }, + "title": "Ubiquiti Firewall Traffic by Port [Filebeat Iptables]", + "type": "table" + } + }, + "id": "190bcb50-1ff6-11e9-ae2a-939083c6a64e", + "type": "visualization", + "updated_at": "2019-01-24T16:35:45.413Z", + "version": 1 + }, + { + "attributes": { + "columns": [ + "_source" + ], + "description": "", + "hits": 0, + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "highlightAll": true, + "index": "filebeat-*", + "query": { + "language": "kuery", + "query": "iptables.ubiquiti.rule_set :* and event.outcome : \"deny\"" + }, + "version": true + } + }, + "sort": [ + "@timestamp", + "desc" + ], + "title": "Ubiquiti Firewall Blocked Events [Filebeat Iptables]", + "version": 1 + }, + "id": "9f7d97c0-1fe9-11e9-ae2a-939083c6a64e", + "type": "search", + "updated_at": "2019-01-24T15:35:33.942Z", + "version": 2 + }, + { + "attributes": { + "columns": [ + "_source" + ], + "description": "", + "hits": 0, + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "highlightAll": true, + "index": "filebeat-*", + "query": { + "language": "kuery", + "query": "iptables.ubiquiti.rule_set :* and event.outcome : \"allow\"" + }, + "version": true + } + }, + "sort": [ + "@timestamp", + "desc" + ], + "title": "Ubiquiti Firewall Allowed Events [Filebeat Iptables]", + "version": 1 + }, + "id": "7862cab0-1fdb-11e9-ae2a-939083c6a64e", + "type": "search", + "updated_at": "2019-01-24T15:04:12.010Z", + "version": 3 + }, + { + "attributes": { + "description": "Overview of the Ubiquiti Firewall iptables events dashboard.", + "hits": 0, + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "query": { + "language": "kuery", + "query": "" + } + } + }, + "optionsJSON": { + "darkTheme": false, + "hidePanelTitles": false, + "useMargins": true + }, + "panelsJSON": [ + { + "embeddableConfig": { + "vis": { + "colors": { + "allow": "#64B0C8", + "deny": "#E24D42" + }, + "legendOpen": true + } + }, + "gridData": { + "h": 15, + "i": "1", + "w": 33, + "x": 0, + "y": 0 + }, + "id": "758b3620-1fda-11e9-ae2a-939083c6a64e", + "panelIndex": "1", + "title": "Event Timeline", + "type": "visualization", + "version": "6.6.0" + }, + { + "embeddableConfig": {}, + "gridData": { + "h": 15, + "i": "2", + "w": 15, + "x": 33, + "y": 0 + }, + "id": "1ba82fd0-1ff0-11e9-ae2a-939083c6a64e", + "panelIndex": "2", + "title": "Top Blocked by source IP", + "type": "visualization", + "version": "6.6.0" + }, + { + "embeddableConfig": { + "mapCenter": [ + 39.095962936305476, + -22.148437500000004 + ], + "mapZoom": 2 + }, + "gridData": { + "h": 15, + "i": "3", + "w": 24, + "x": 0, + "y": 15 + }, + "id": "5bd53050-1fe9-11e9-ae2a-939083c6a64e", + "panelIndex": "3", + "title": "Allowed Traffic Map", + "type": "visualization", + "version": "6.6.0" + }, + { + "embeddableConfig": { + "mapCenter": [ + 46.31658418182218, + -34.10156250000001 + ], + "mapZoom": 2 + }, + "gridData": { + "h": 15, + "i": "4", + "w": 24, + "x": 24, + "y": 15 + }, + "id": "8853aa20-1fef-11e9-ae2a-939083c6a64e", + "panelIndex": "4", + "title": "Blocked Traffic Map", + "type": "visualization", + "version": "6.6.0" + }, + { + "embeddableConfig": { + "vis": { + "colors": { + "allow": "#7EB26D", + "deny": "#E24D42", + "icmp": "#F29191", + "ipv4": "#65C5DB", + "ipv6": "#D683CE", + "ipv6-icmp": "#EA6460", + "tcp": "#447EBC", + "udp": "#F2C96D" + } + } + }, + "gridData": { + "h": 18, + "i": "5", + "w": 24, + "x": 0, + "y": 30 + }, + "id": "fdea1ad0-1ff4-11e9-ae2a-939083c6a64e", + "panelIndex": "5", + "title": "Traffic Breakdown by Protocol", + "type": "visualization", + "version": "6.6.0" + }, + { + "embeddableConfig": {}, + "gridData": { + "h": 27, + "i": "6", + "w": 48, + "x": 0, + "y": 48 + }, + "id": "c4e80aa0-1fd4-11e9-ae2a-939083c6a64e", + "panelIndex": "6", + "title": "Event View", + "type": "search", + "version": "6.6.0" + }, + { + "embeddableConfig": {}, + "gridData": { + "h": 18, + "i": "7", + "w": 24, + "x": 24, + "y": 30 + }, + "id": "190bcb50-1ff6-11e9-ae2a-939083c6a64e", + "panelIndex": "7", + "title": "Traffic Breakdown by Port", + "type": "visualization", + "version": "6.6.0" + } + ], + "timeRestore": false, + "title": "[Filebeat Iptables] Ubiquiti Firewall Overview", + "version": 1 + }, + "id": "d39f0980-1ff3-11e9-ae2a-939083c6a64e", + "type": "dashboard", + "updated_at": "2019-01-24T16:38:35.174Z", + "version": 4 + } + ], + "version": "6.6.0" +} diff --git a/x-pack/filebeat/module/iptables/fields.go b/x-pack/filebeat/module/iptables/fields.go new file mode 100644 index 00000000000..3fe34ea1268 --- /dev/null +++ b/x-pack/filebeat/module/iptables/fields.go @@ -0,0 +1,23 @@ +// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +// or more contributor license agreements. Licensed under the Elastic License; +// you may not use this file except in compliance with the Elastic License. + +// Code generated by beats/dev-tools/cmd/asset/asset.go - DO NOT EDIT. + +package iptables + +import ( + "github.com/elastic/beats/libbeat/asset" +) + +func init() { + if err := asset.SetFields("filebeat", "iptables", asset.ModuleFieldsPri, AssetIptables); err != nil { + panic(err) + } +} + +// AssetIptables returns asset data. +// This is the base64 encoded gzipped contents of module/iptables. +func AssetIptables() string { + return "eJy0l09vozwQxu/5FPMB3ub4HnJYqWq2UqV2W2nTvUaOPYAVY1N7SEQ//comUBKIQ0OWI3+e38x4/Ay+gy1WC5AFsY1CNwMgSQqP7gh03MqCpNEL+DEDAHgxolQIibGQMS2U1ClQhu1XoEzq5jOARKISbhE+ugPN8mNpf1FV4AJSa8ricGcA6K/HoAWJNfkwzF9dYBeKlKFde1b7qEEro9POzTN0f/1hqkQwScAHRY0UVGouSIGaZFI19dBIe2O3oFiFFgpryHCj5q1oL8xEmf1asQ2qXphSE6Zox0X69Lb7P4hBEIsRLUtz1LROFEtdj7rFam+sGEtt5SDIzeEeuMk3UjP/vi/dw8//YPkITAt4eRwTl0kShzRh1V6DQLNsvLTWx9eJNRKF5HnRQ3d79VJFHl7eDj057zw47dLOo4bMjcAjqcG0o/AD3isNpNhJUtwG9LSMYgpmWY501MITaK1cFGpRSIucBpiy+Cax0QImhEXnomCHH7fJ0+FHiZoj6DLfXMj2xN8mUP2HsX0hJmzIN8a3SI1ZysGcWpDmJi8UEq43FWHfoMZjf4X6eR/4EoUgGuUXJa0F7iTvT45vmeMyaABljMAiR7lDERypCOWIxFD414XvgfVGUr8ELjOWRjv0lxp4tQiXzJRyB9jKz0aTwG+0IftgfBGkQp1SNr23ap0IyJT0Lxa2lh23rMQnjZbVw5WT5XTQn0/6wvqGCMKYjzuwQ7tDcdq757p3BLSRPNfAt3VhT/yOCTO+vQnznm+12SsU9Q/VCPJeamH2k+H3YoeWpEMR4qhVwcnP2EQgmvjLupI5wsrAs9xd9olSTNo778sr907Pn66r8HOQaX5JfTQZMoE2/BYXrFKGRbPfyI9SkpxSguY0ZSy8H+Ta40rtimNrczosP43Gk3zPG8yFQgE8eU3wmoOt33P029Jfaz8fgbelwnW9R6/E9+CrDIPsYevDXlImdWiZcNsNjpdeUMenp9tE5A9UHjGf/Q0AAP//R0INxw==" +} diff --git a/x-pack/filebeat/module/iptables/log/_meta/fields.yml b/x-pack/filebeat/module/iptables/log/_meta/fields.yml new file mode 100644 index 00000000000..6b1617ab450 --- /dev/null +++ b/x-pack/filebeat/module/iptables/log/_meta/fields.yml @@ -0,0 +1,163 @@ +- name: ether_type + type: long + description: > + Value of the ethernet type field identifying the network layer protocol. + +- name: flow_label + type: integer + description: > + IPv6 flow label. + +- name: fragment_flags + type: keyword + description: > + IP fragment flags. A combination of CE, DF and MF. + +- name: fragment_offset + type: long + description: > + Offset of the current IP fragment. + +- name: icmp + type: group + description: > + ICMP fields. + fields: + + - name: code + type: long + description: > + ICMP code. + + - name: id + type: long + description: > + ICMP ID. + + - name: parameter + type: long + description: > + ICMP parameter. + + - name: redirect + type: ip + description: > + ICMP redirect address. + + - name: seq + type: long + description: > + ICMP sequence number. + + - name: type + type: long + description: > + ICMP type. + +- name: id + type: long + description: > + Packet identifier. + +- name: incomplete_bytes + type: long + description: > + Number of incomplete bytes. + +- name: input_device + type: keyword + description: > + Device that received the packet. + +- name: precedence_bits + type: short + description: > + IP precedence bits. + +- name: tos + type: long + description: > + IP Type of Service field. + +- name: length + type: long + description: > + Packet length. + +- name: output_device + type: keyword + description: > + Device that output the packet. + +- name: tcp + type: group + description: > + TCP fields. + fields: + + - name: flags + type: keyword + description: > + TCP flags. + + - name: reserved_bits + type: short + description: > + TCP reserved bits. + + - name: seq + type: long + description: > + TCP sequence number. + + - name: ack + type: long + description: > + TCP Acknowledgment number. + + - name: window + type: long + description: > + Advertised TCP window size. + +- name: ttl + type: integer + description: > + Time To Live field. + +- name: udp + type: group + description: > + UDP fields. + fields: + + - name: length + type: long + description: > + Length of the UDP header and payload. + +- name: ubiquiti + type: group + description: > + Fields for Ubiquiti network devices. + fields: + + - name: input_zone + type: keyword + description: > + Input zone. + + - name: output_zone + type: keyword + description: > + Output zone. + + - name: rule_number + type: keyword + description: + The rule number within the rule set. + + - name: rule_set + type: keyword + description: + The rule set name. diff --git a/x-pack/filebeat/module/iptables/log/config/file.yml b/x-pack/filebeat/module/iptables/log/config/file.yml new file mode 100644 index 00000000000..21045d97393 --- /dev/null +++ b/x-pack/filebeat/module/iptables/log/config/file.yml @@ -0,0 +1,11 @@ +type: log +paths: +{{ range $i, $path := .paths }} + - {{$path}} +{{ end }} +exclude_files: [".gz$"] +tags: {{.tags}} +{{ if .convert_timezone }} +processors: + - add_locale: ~ +{{ end }} diff --git a/x-pack/filebeat/module/iptables/log/config/syslog.yml b/x-pack/filebeat/module/iptables/log/config/syslog.yml new file mode 100644 index 00000000000..896b4441de9 --- /dev/null +++ b/x-pack/filebeat/module/iptables/log/config/syslog.yml @@ -0,0 +1,8 @@ +type: syslog + {{ if .convert_timezone }} +processors: + - add_locale: ~ + {{ end }} + +protocol.udp: + host: "{{.syslog_host}}:{{.syslog_port}}" diff --git a/x-pack/filebeat/module/iptables/log/ingest/pipeline.json b/x-pack/filebeat/module/iptables/log/ingest/pipeline.json new file mode 100644 index 00000000000..1678df94c5e --- /dev/null +++ b/x-pack/filebeat/module/iptables/log/ingest/pipeline.json @@ -0,0 +1,180 @@ +{ + "description" : "Pipeline for IPTables", + "processors" : [ + { + "grok": { + "field": "message", + "patterns": [ + "%{SYSLOGTIMESTAMP:iptables.raw_date}%{GREEDYDATA}\\[%{UBIQUITI_LABEL}\\]%{IPTABLES}%{SPACE}", + "%{SYSLOGTIMESTAMP:iptables.raw_date}%{GREEDYDATA}%{IPTABLES}%{SPACE}", + "%{GREEDYDATA}\\[%{UBIQUITI_LABEL}\\]%{IPTABLES}%{SPACE}", + "%{GREEDYDATA}%{IPTABLES}%{SPACE}" + ], + "pattern_definitions": { + "UNSIGNED_INT": "[0-9]+", + "ETHTYPE": "(?:[A-Fa-f0-9]{2}):(?:[A-Fa-f0-9]{2})", + "ETHTYPE_DISCARD": "(?::[A-Fa-f0-9]{2})*", + "NETFILTERMAC": "(?:%{MAC:destination.mac}:%{MAC:source.mac}:%{ETHTYPE:iptables.ether_type}?%{ETHTYPE_DISCARD}|%{MAC:destination.mac}%{ETHTYPE_DISCARD}:%{ETHTYPE:iptables.ether_type}?)", + "IPTABLES_ETHERNET": "IN=%{DATA:iptables.input_device} OUT=%{DATA:iptables.output_device}?(?: MAC=%{NETFILTERMAC})?", + "IPTABLES_PORT_PAIR": "SPT=%{UNSIGNED_INT:source.port:int} DPT=%{UNSIGNED_INT:destination.port:int}", + "IPTABLES_TCP_FLAGS": "((?<= )(CWR|ECE|URG|ACK|PSH|RST|SYN|FIN))*", + "IPTABLES_TCP_SEQ": "SEQ=%{UNSIGNED_INT:iptables.tcp.seq:int} ACK=%{UNSIGNED_INT:iptables.tcp.ack:int}", + "IPTABLES_TCP_DETAILS": "(?:%{IPTABLES_TCP_SEQ} )?WINDOW=%{UNSIGNED_INT:iptables.tcp.window:int} RES=0x%{BASE16NUM:iptables.tcp_reserved_bits} %{IPTABLES_TCP_FLAGS:iptables.tcp.flags}", + "IPTABLES_INCOMPLETE_PACKET": "INCOMPLETE \\[%{UNSIGNED_INT:iptables.incomplete_bytes:int} bytes\\]", + "IPTABLES_UDP_DETAILS": "LEN=%{UNSIGNED_INT:iptables.udp.length:int}", + "IPTABLES_ICMP_EXTRA_ECHO": "ID=%{UNSIGNED_INT:iptables.icmp.id:int} SEQ=%{UNSIGNED_INT:iptables.icmp.seq:int}", + "IPTABLES_ICMP_EXTRA_PARAM": "PARAMETER=%{UNSIGNED_INT:iptables.icmp.parameter:int}", + "IPTABLES_ICMP_EXTRA_REDIRECT": "GATEWAY=%{IP:iptables.icmp.redirect}", + "IPTABLES_ICMP_EXTRA": "( (?:%{IPTABLES_ICMP_EXTRA_ECHO}|%{IPTABLES_ICMP_EXTRA_PARAM}|%{IPTABLES_ICMP_EXTRA_REDIRECT}))*", + "IPTABLES_ICMP_DETAILS": "TYPE=%{UNSIGNED_INT:iptables.icmp.type:int} CODE=%{UNSIGNED_INT:iptables.icmp.code:int}(( %{IPTABLES_INCOMPLETE_PACKET})|%{IPTABLES_ICMP_EXTRA})", + "IPTABLES_PROTOCOL": "PROTO=(?[a-zA-Z0-9]+)", + "IPTABLES_IP_PAYLOAD": "%{IPTABLES_PROTOCOL}( %{IPTABLES_PORT_PAIR})?( (%{IPTABLES_TCP_DETAILS}|%{IPTABLES_UDP_DETAILS}|%{IPTABLES_ICMP_DETAILS}|%{IPTABLES_INCOMPLETE_PACKET}))?", + "IPTABLES_IP_FRAGFLAG": "((?<= )(CE|DF|MF))*", + "IPTABLES_IP_START": "SRC=%{IPV4:source.ip} DST=%{IPV4:destination.ip} LEN=%{UNSIGNED_INT:iptables.length:int} TOS=0x%{BASE16NUM:iptables.tos} PREC=0x%{BASE16NUM:iptables.precedence_bits} TTL=%{UNSIGNED_INT:iptables.ttl:int} ID=%{UNSIGNED_INT:iptables.id:int}(?: %{IPTABLES_IP_FRAGFLAG:iptables.fragment_flags})?(?: FRAG: %{UNSIGNED_INT:iptables.fragment_offset:int})?", + "IPTABLES_IP": "%{IPTABLES_IP_START} %{IPTABLES_IP_PAYLOAD}", + "IPTABLES_IPV6_START": "SRC=%{IPV6:source.ip} DST=%{IPV6:destination.ip} LEN=%{UNSIGNED_INT:iptables.length:int} TC=%{UNSIGNED_INT:iptables.tos} HOPLIMIT=%{UNSIGNED_INT:iptables.ttl:int} FLOWLBL=%{UNSIGNED_INT:iptables.flow_label:int}", + "IPTABLES_IPV6": "%{IPTABLES_IPV6_START} %{IPTABLES_IP_PAYLOAD}", + "IPTABLES": "%{IPTABLES_ETHERNET} (:?%{IPTABLES_IP}|%{IPTABLES_IPV6})", + "UBIQUITI_FIELD": "[^-\\]]*", + "UBIQUITI_RULESET_NAME": "[^\\]]*", + "UBIQUITI_LABEL": "%{UBIQUITI_RULESET_NAME:iptables.ubiquiti.rule_set}-%{UBIQUITI_FIELD:iptables.ubiquiti.rule_number}-%{UBIQUITI_FIELD:event.outcome}" + } + } + }, + { + "rename": { + "field": "message", + "target_field": "log.original" + } + }, + { + "grok": { + "field": "iptables.ubiquiti.rule_set", + "ignore_missing": true, + "ignore_failure": true, + "patterns": [ + "%{UBIQUITI_FIELD:iptables.ubiquiti.input_zone}-%{UBIQUITI_FIELD:iptables.ubiquiti.output_zone}" + ], + "pattern_definitions": { + "UBIQUITI_FIELD": "[^-]*" + } + } + }, + { + "date": { + "field": "iptables.raw_date", + "ignore_failure": true, + {< if .convert_timezone >}"timezone": "{{ beat.timezone }}",{< end >} + "formats": [ + "MMM d HH:mm:ss", + "MMM dd HH:mm:ss" + ] + } + }, + { + "remove": { + "field": "iptables.raw_date", + "ignore_missing": true + } + }, + + { + "lowercase": { + "field": "network.transport", + "ignore_missing": true + } + }, + { + "geoip": { + "field": "source.ip", + "target_field": "source.geo", + "ignore_missing": true + } + }, + { + "geoip": { + "field": "destination.ip", + "target_field": "destination.geo", + "ignore_missing": true + } + }, + { + "script": { + "lang": "painless", + "params": { + "mappings": [ + { + "source": { + "object": "iptables", + "key": "ether_type" + }, + "destination": { + "object": "network", + "key": "type" + }, + "map": { + "08:00": "ipv4", + "86:dd": "ipv6" + } + }, + { + "source": { + "object": "event", + "key": "outcome" + }, + "destination": { + "object": "event", + "key": "outcome" + }, + "map": { + "D": "deny", + "A": "allow" + } + }, + { + "source": { + "object": "network", + "key": "transport" + }, + "destination": { + "object": "network", + "key": "transport" + }, + "map": { + "icmpv6": "ipv6-icmp" + } + } + ] + }, + "source": "for (action in params.mappings) { def src = ctx[action.source.object]; if (src != null) { Map map = action.map; String key = src[action.source.key]; String mapping = map[key]; if (mapping != null) { Map dst = ctx[action.destination.object]; if (dst == null) { dst = new HashMap(); ctx[action.destination.object] = dst;} dst[action.destination.key] = mapping; } } }" + } + }, + { + "script": { + "lang": "painless", + "params": { + "hex_fields_to_convert": [ + "ether_type", + "tos", + "precedence_bits", + "tcp_reserved_bits" + ] + }, + "source": "def iptables = ctx['iptables']; if (iptables != null) { for (key in params.hex_fields_to_convert) { long value = 0; def field = iptables[key]; if (field == null) continue; char[] hex = field.toLowerCase().toCharArray(); for (chr in hex) { long v = -1; if (chr >= (char)'a' && chr <= (char)'f') v = (long)chr - (char)'a' + 10; else if (chr >= (char)'0' && chr <= (char)'9') v = (long)chr - (char)'0'; if (v >= 0) {value = value*16 + v;} } iptables[key] = value; } }" + } + }, + { + "rename": { + "field": "iptables.tcp_reserved_bits", + "target_field": "iptables.tcp.reserved_bits", + "ignore_missing": true + } + } + ], + "on_failure" : [{ + "set" : { + "field" : "error.message", + "value" : "{{ _ingest.on_failure_message }}" + } + }] +} diff --git a/x-pack/filebeat/module/iptables/log/manifest.yml b/x-pack/filebeat/module/iptables/log/manifest.yml new file mode 100644 index 00000000000..0262a19f5fb --- /dev/null +++ b/x-pack/filebeat/module/iptables/log/manifest.yml @@ -0,0 +1,28 @@ +module_version: "1.0" + +var: + - name: paths + default: + - /var/log/iptables.log + - name: tags + default: [iptables] + - name: syslog_host + default: localhost + - name: syslog_port + default: 9001 + - name: input + default: syslog + - name: convert_timezone + default: false + # if ES < 6.1.0, this flag switches to false automatically when evaluating the + # pipeline + min_elasticsearch_version: + version: 6.1.0 + value: false + +ingest_pipeline: ingest/pipeline.json +input: config/{{.input}}.yml + +requires.processors: +- name: geoip + plugin: ingest-geoip diff --git a/x-pack/filebeat/module/iptables/log/test/geo.log b/x-pack/filebeat/module/iptables/log/test/geo.log new file mode 100644 index 00000000000..1755a7853c0 --- /dev/null +++ b/x-pack/filebeat/module/iptables/log/test/geo.log @@ -0,0 +1 @@ +Oct 10 07:25:12 Hostname kernel: [wan-lan-default-D]IN=eth0 OUT= MAC=90:10:20:76:8d:20:90:10:65:29:b6:2a:08:00 SRC=158.109.0.1 DST=10.4.0.5 LEN=52 TOS=0x00 PREC=0x00 TTL=63 ID=0 DF PROTO=TCP SPT=38842 DPT=443 WINDOW=2853 RES=0x00 ACK URGP=0 diff --git a/x-pack/filebeat/module/iptables/log/test/geo.log-expected.json b/x-pack/filebeat/module/iptables/log/test/geo.log-expected.json new file mode 100644 index 00000000000..b7c0182599c --- /dev/null +++ b/x-pack/filebeat/module/iptables/log/test/geo.log-expected.json @@ -0,0 +1,48 @@ +[ + { + "@timestamp": "2019-10-10T07:25:12.000Z", + "destination.ip": "10.4.0.5", + "destination.mac": "90:10:20:76:8d:20", + "destination.port": 443, + "ecs.version": "1.0.0-beta2", + "event.dataset": "iptables.log", + "event.module": "iptables", + "event.outcome": "deny", + "fileset.name": "log", + "input.type": "log", + "iptables.ether_type": 2048, + "iptables.fragment_flags": "DF", + "iptables.id": 0, + "iptables.input_device": "eth0", + "iptables.length": 52, + "iptables.output_device": "", + "iptables.precedence_bits": 0, + "iptables.tcp.flags": "ACK", + "iptables.tcp.reserved_bits": 0, + "iptables.tcp.window": 2853, + "iptables.tos": 0, + "iptables.ttl": 63, + "iptables.ubiquiti.input_zone": "wan", + "iptables.ubiquiti.output_zone": "lan", + "iptables.ubiquiti.rule_number": "default", + "iptables.ubiquiti.rule_set": "wan-lan", + "log.offset": 0, + "log.original": "Oct 10 07:25:12 Hostname kernel: [wan-lan-default-D]IN=eth0 OUT= MAC=90:10:20:76:8d:20:90:10:65:29:b6:2a:08:00 SRC=158.109.0.1 DST=10.4.0.5 LEN=52 TOS=0x00 PREC=0x00 TTL=63 ID=0 DF PROTO=TCP SPT=38842 DPT=443 WINDOW=2853 RES=0x00 ACK URGP=0 ", + "network.transport": "tcp", + "network.type": "ipv4", + "service.type": "iptables", + "source.geo.city_name": "Bellaterra", + "source.geo.continent_name": "Europe", + "source.geo.country_iso_code": "ES", + "source.geo.location.lat": 41.5026, + "source.geo.location.lon": 2.0875, + "source.geo.region_iso_code": "ES-B", + "source.geo.region_name": "Barcelona", + "source.ip": "158.109.0.1", + "source.mac": "90:10:65:29:b6:2a", + "source.port": 38842, + "tags": [ + "iptables" + ] + } +] \ No newline at end of file diff --git a/x-pack/filebeat/module/iptables/log/test/icmp.log b/x-pack/filebeat/module/iptables/log/test/icmp.log new file mode 100644 index 00000000000..6ab1f8c7ee6 --- /dev/null +++ b/x-pack/filebeat/module/iptables/log/test/icmp.log @@ -0,0 +1 @@ +Jan 8 03:37:09 DENY: IN=eth0 OUT= MAC=90:10:28:5f:62:24:90:10:18:5a:89:2a:08:00 SRC=192.0.2.71 DST=192.0.2.83 LEN=88 TOS=0x00 PREC=0x00 TTL=118 ID=21684 PROTO=ICMP TYPE=3 CODE=3 [SRC=192.0.2.83 DST=192.168.173.191 LEN=60 TOS=0x00 PREC=0x00 TTL=54 ID=0 DF PROTO=UDP SPT=21458 DPT=62936 LEN=40 ] diff --git a/x-pack/filebeat/module/iptables/log/test/icmp.log-expected.json b/x-pack/filebeat/module/iptables/log/test/icmp.log-expected.json new file mode 100644 index 00000000000..689ad154856 --- /dev/null +++ b/x-pack/filebeat/module/iptables/log/test/icmp.log-expected.json @@ -0,0 +1,32 @@ +[ + { + "@timestamp": "2019-01-08T03:37:09.000Z", + "destination.ip": "192.0.2.83", + "destination.mac": "90:10:28:5f:62:24", + "ecs.version": "1.0.0-beta2", + "event.dataset": "iptables.log", + "event.module": "iptables", + "fileset.name": "log", + "input.type": "log", + "iptables.ether_type": 2048, + "iptables.icmp.code": 3, + "iptables.icmp.type": 3, + "iptables.id": 21684, + "iptables.input_device": "eth0", + "iptables.length": 88, + "iptables.output_device": "", + "iptables.precedence_bits": 0, + "iptables.tos": 0, + "iptables.ttl": 118, + "log.offset": 0, + "log.original": "Jan 8 03:37:09 DENY: IN=eth0 OUT= MAC=90:10:28:5f:62:24:90:10:18:5a:89:2a:08:00 SRC=192.0.2.71 DST=192.0.2.83 LEN=88 TOS=0x00 PREC=0x00 TTL=118 ID=21684 PROTO=ICMP TYPE=3 CODE=3 [SRC=192.0.2.83 DST=192.168.173.191 LEN=60 TOS=0x00 PREC=0x00 TTL=54 ID=0 DF PROTO=UDP SPT=21458 DPT=62936 LEN=40 ]", + "network.transport": "icmp", + "network.type": "ipv4", + "service.type": "iptables", + "source.ip": "192.0.2.71", + "source.mac": "90:10:18:5a:89:2a", + "tags": [ + "iptables" + ] + } +] \ No newline at end of file diff --git a/x-pack/filebeat/module/iptables/log/test/iptables.log b/x-pack/filebeat/module/iptables/log/test/iptables.log new file mode 100644 index 00000000000..26a169f56ff --- /dev/null +++ b/x-pack/filebeat/module/iptables/log/test/iptables.log @@ -0,0 +1,10 @@ +Jan 8 03:37:09 example-host kernel: iptables DROP_INPUT: IN=eth0 OUT= MAC=90:10:35:5a:1e:3a:90:10:9e:ec:2c:71:08:00 SRC=203.0.113.36 DST=172.16.54.114 LEN=52 TOS=0x00 PREC=0x00 TTL=115 ID=15743 DF PROTO=TCP SPT=17805 DPT=445 WINDOW=8192 RES=0x00 SYN URGP=0 +Jan 8 03:37:57 example-host kernel: iptables DROP_INPUT: IN=eth0 OUT= MAC=90:10:35:5a:1e:3a:90:10:76:e0:e2:d5:08:00 SRC=198.51.100.198 DST=172.16.54.114 LEN=40 TOS=0x00 PREC=0x00 TTL=243 ID=17703 PROTO=TCP SPT=47091 DPT=1433 WINDOW=1024 RES=0x00 SYN URGP=0 +Jan 8 03:38:45 example-host kernel: iptables DROP_INPUT: IN=eth0 OUT= MAC=90:10:35:5a:1e:3a:90:10:9e:ec:2c:71:08:00 SRC=203.0.113.201 DST=172.16.54.114 LEN=52 TOS=0x00 PREC=0x00 TTL=115 ID=19619 DF PROTO=TCP SPT=59319 DPT=445 WINDOW=8192 RES=0x00 SYN URGP=0 +Jan 8 03:39:25 example-host kernel: iptables DROP_INPUT: IN=eth0 OUT= MAC=90:10:35:5a:1e:3a:90:10:9e:ec:2c:71:08:00 SRC=203.0.113.246 DST=172.16.54.114 LEN=40 TOS=0x00 PREC=0x00 TTL=240 ID=4255 DF PROTO=TCP SPT=44181 DPT=80 WINDOW=14600 RES=0x00 SYN URGP=0 +Jan 8 03:40:21 example-host kernel: iptables DROP_INPUT: IN=eth0 OUT= MAC=90:10:35:5a:1e:3a:90:10:76:e0:e2:d5:08:00 SRC=203.0.113.208 DST=172.16.54.114 LEN=52 TOS=0x00 PREC=0x00 TTL=110 ID=27150 DF PROTO=TCP SPT=64358 DPT=445 WINDOW=8192 RES=0x00 SYN URGP=0 +Jan 8 03:40:25 example-host kernel: iptables DROP_INPUT: IN=eth0 OUT= MAC=90:10:35:5a:1e:3a:90:10:9e:ec:2c:71:08:00 SRC=198.51.100.160 DST=172.16.54.114 LEN=40 TOS=0x00 PREC=0x00 TTL=242 ID=7264 PROTO=TCP SPT=58830 DPT=445 WINDOW=1024 RES=0x00 SYN URGP=0 +Jan 8 03:41:17 example-host kernel: iptables DROP_INPUT: IN=eth0 OUT= MAC=90:10:35:5a:1e:3a:90:10:76:e0:e2:d5:08:00 SRC=198.51.100.115 DST=172.16.54.114 LEN=52 TOS=0x00 PREC=0x00 TTL=117 ID=6101 DF PROTO=TCP SPT=51985 DPT=445 WINDOW=8192 RES=0x00 SYN URGP=0 +Jan 8 03:41:23 example-host kernel: iptables DROP_INPUT: IN=eth0 OUT= MAC=90:10:35:5a:1e:3a:90:10:76:e0:e2:d5:08:00 SRC=198.51.100.167 DST=172.16.54.114 LEN=52 TOS=0x00 PREC=0x00 TTL=45 ID=6319 DF PROTO=TCP SPT=4099 DPT=445 WINDOW=8192 RES=0x00 SYN URGP=0 +Jan 8 03:43:18 example-host kernel: iptables DROP_INPUT: IN=eth0 OUT= MAC=90:10:35:5a:1e:3a:90:10:9e:ec:2c:71:08:00 SRC=198.51.100.19 DST=172.16.54.114 LEN=40 TOS=0x00 PREC=0x00 TTL=245 ID=48624 PROTO=TCP SPT=59287 DPT=139 WINDOW=1024 RES=0x00 SYN URGP=0 +Jan 8 03:43:42 example-host kernel: iptables DROP_INPUT: IN=eth0 OUT= MAC=90:10:35:5a:1e:3a:90:10:76:e0:e2:d5:08:00:45:00:00:00:00 SRC=198.51.100.68 DST=172.16.54.114 LEN=40 TOS=0x00 PREC=0x00 TTL=250 ID=54321 PROTO=TCP SPT=53296 DPT=8088 WINDOW=65535 RES=0x00 SYN URGP=0 diff --git a/x-pack/filebeat/module/iptables/log/test/iptables.log-expected.json b/x-pack/filebeat/module/iptables/log/test/iptables.log-expected.json new file mode 100644 index 00000000000..ff646faf78b --- /dev/null +++ b/x-pack/filebeat/module/iptables/log/test/iptables.log-expected.json @@ -0,0 +1,338 @@ +[ + { + "@timestamp": "2019-01-08T03:37:09.000Z", + "destination.ip": "172.16.54.114", + "destination.mac": "90:10:35:5a:1e:3a", + "destination.port": 445, + "ecs.version": "1.0.0-beta2", + "event.dataset": "iptables.log", + "event.module": "iptables", + "fileset.name": "log", + "input.type": "log", + "iptables.ether_type": 2048, + "iptables.fragment_flags": "DF", + "iptables.id": 15743, + "iptables.input_device": "eth0", + "iptables.length": 52, + "iptables.output_device": "", + "iptables.precedence_bits": 0, + "iptables.tcp.flags": "SYN", + "iptables.tcp.reserved_bits": 0, + "iptables.tcp.window": 8192, + "iptables.tos": 0, + "iptables.ttl": 115, + "log.offset": 0, + "log.original": "Jan 8 03:37:09 example-host kernel: iptables DROP_INPUT: IN=eth0 OUT= MAC=90:10:35:5a:1e:3a:90:10:9e:ec:2c:71:08:00 SRC=203.0.113.36 DST=172.16.54.114 LEN=52 TOS=0x00 PREC=0x00 TTL=115 ID=15743 DF PROTO=TCP SPT=17805 DPT=445 WINDOW=8192 RES=0x00 SYN URGP=0 ", + "network.transport": "tcp", + "network.type": "ipv4", + "service.type": "iptables", + "source.ip": "203.0.113.36", + "source.mac": "90:10:9e:ec:2c:71", + "source.port": 17805, + "tags": [ + "iptables" + ] + }, + { + "@timestamp": "2019-01-08T03:37:57.000Z", + "destination.ip": "172.16.54.114", + "destination.mac": "90:10:35:5a:1e:3a", + "destination.port": 1433, + "ecs.version": "1.0.0-beta2", + "event.dataset": "iptables.log", + "event.module": "iptables", + "fileset.name": "log", + "input.type": "log", + "iptables.ether_type": 2048, + "iptables.id": 17703, + "iptables.input_device": "eth0", + "iptables.length": 40, + "iptables.output_device": "", + "iptables.precedence_bits": 0, + "iptables.tcp.flags": "SYN", + "iptables.tcp.reserved_bits": 0, + "iptables.tcp.window": 1024, + "iptables.tos": 0, + "iptables.ttl": 243, + "log.offset": 259, + "log.original": "Jan 8 03:37:57 example-host kernel: iptables DROP_INPUT: IN=eth0 OUT= MAC=90:10:35:5a:1e:3a:90:10:76:e0:e2:d5:08:00 SRC=198.51.100.198 DST=172.16.54.114 LEN=40 TOS=0x00 PREC=0x00 TTL=243 ID=17703 PROTO=TCP SPT=47091 DPT=1433 WINDOW=1024 RES=0x00 SYN URGP=0 ", + "network.transport": "tcp", + "network.type": "ipv4", + "service.type": "iptables", + "source.ip": "198.51.100.198", + "source.mac": "90:10:76:e0:e2:d5", + "source.port": 47091, + "tags": [ + "iptables" + ] + }, + { + "@timestamp": "2019-01-08T03:38:45.000Z", + "destination.ip": "172.16.54.114", + "destination.mac": "90:10:35:5a:1e:3a", + "destination.port": 445, + "ecs.version": "1.0.0-beta2", + "event.dataset": "iptables.log", + "event.module": "iptables", + "fileset.name": "log", + "input.type": "log", + "iptables.ether_type": 2048, + "iptables.fragment_flags": "DF", + "iptables.id": 19619, + "iptables.input_device": "eth0", + "iptables.length": 52, + "iptables.output_device": "", + "iptables.precedence_bits": 0, + "iptables.tcp.flags": "SYN", + "iptables.tcp.reserved_bits": 0, + "iptables.tcp.window": 8192, + "iptables.tos": 0, + "iptables.ttl": 115, + "log.offset": 518, + "log.original": "Jan 8 03:38:45 example-host kernel: iptables DROP_INPUT: IN=eth0 OUT= MAC=90:10:35:5a:1e:3a:90:10:9e:ec:2c:71:08:00 SRC=203.0.113.201 DST=172.16.54.114 LEN=52 TOS=0x00 PREC=0x00 TTL=115 ID=19619 DF PROTO=TCP SPT=59319 DPT=445 WINDOW=8192 RES=0x00 SYN URGP=0 ", + "network.transport": "tcp", + "network.type": "ipv4", + "service.type": "iptables", + "source.ip": "203.0.113.201", + "source.mac": "90:10:9e:ec:2c:71", + "source.port": 59319, + "tags": [ + "iptables" + ] + }, + { + "@timestamp": "2019-01-08T03:39:25.000Z", + "destination.ip": "172.16.54.114", + "destination.mac": "90:10:35:5a:1e:3a", + "destination.port": 80, + "ecs.version": "1.0.0-beta2", + "event.dataset": "iptables.log", + "event.module": "iptables", + "fileset.name": "log", + "input.type": "log", + "iptables.ether_type": 2048, + "iptables.fragment_flags": "DF", + "iptables.id": 4255, + "iptables.input_device": "eth0", + "iptables.length": 40, + "iptables.output_device": "", + "iptables.precedence_bits": 0, + "iptables.tcp.flags": "SYN", + "iptables.tcp.reserved_bits": 0, + "iptables.tcp.window": 14600, + "iptables.tos": 0, + "iptables.ttl": 240, + "log.offset": 778, + "log.original": "Jan 8 03:39:25 example-host kernel: iptables DROP_INPUT: IN=eth0 OUT= MAC=90:10:35:5a:1e:3a:90:10:9e:ec:2c:71:08:00 SRC=203.0.113.246 DST=172.16.54.114 LEN=40 TOS=0x00 PREC=0x00 TTL=240 ID=4255 DF PROTO=TCP SPT=44181 DPT=80 WINDOW=14600 RES=0x00 SYN URGP=0 ", + "network.transport": "tcp", + "network.type": "ipv4", + "service.type": "iptables", + "source.ip": "203.0.113.246", + "source.mac": "90:10:9e:ec:2c:71", + "source.port": 44181, + "tags": [ + "iptables" + ] + }, + { + "@timestamp": "2019-01-08T03:40:21.000Z", + "destination.ip": "172.16.54.114", + "destination.mac": "90:10:35:5a:1e:3a", + "destination.port": 445, + "ecs.version": "1.0.0-beta2", + "event.dataset": "iptables.log", + "event.module": "iptables", + "fileset.name": "log", + "input.type": "log", + "iptables.ether_type": 2048, + "iptables.fragment_flags": "DF", + "iptables.id": 27150, + "iptables.input_device": "eth0", + "iptables.length": 52, + "iptables.output_device": "", + "iptables.precedence_bits": 0, + "iptables.tcp.flags": "SYN", + "iptables.tcp.reserved_bits": 0, + "iptables.tcp.window": 8192, + "iptables.tos": 0, + "iptables.ttl": 110, + "log.offset": 1037, + "log.original": "Jan 8 03:40:21 example-host kernel: iptables DROP_INPUT: IN=eth0 OUT= MAC=90:10:35:5a:1e:3a:90:10:76:e0:e2:d5:08:00 SRC=203.0.113.208 DST=172.16.54.114 LEN=52 TOS=0x00 PREC=0x00 TTL=110 ID=27150 DF PROTO=TCP SPT=64358 DPT=445 WINDOW=8192 RES=0x00 SYN URGP=0 ", + "network.transport": "tcp", + "network.type": "ipv4", + "service.type": "iptables", + "source.ip": "203.0.113.208", + "source.mac": "90:10:76:e0:e2:d5", + "source.port": 64358, + "tags": [ + "iptables" + ] + }, + { + "@timestamp": "2019-01-08T03:40:25.000Z", + "destination.ip": "172.16.54.114", + "destination.mac": "90:10:35:5a:1e:3a", + "destination.port": 445, + "ecs.version": "1.0.0-beta2", + "event.dataset": "iptables.log", + "event.module": "iptables", + "fileset.name": "log", + "input.type": "log", + "iptables.ether_type": 2048, + "iptables.id": 7264, + "iptables.input_device": "eth0", + "iptables.length": 40, + "iptables.output_device": "", + "iptables.precedence_bits": 0, + "iptables.tcp.flags": "SYN", + "iptables.tcp.reserved_bits": 0, + "iptables.tcp.window": 1024, + "iptables.tos": 0, + "iptables.ttl": 242, + "log.offset": 1297, + "log.original": "Jan 8 03:40:25 example-host kernel: iptables DROP_INPUT: IN=eth0 OUT= MAC=90:10:35:5a:1e:3a:90:10:9e:ec:2c:71:08:00 SRC=198.51.100.160 DST=172.16.54.114 LEN=40 TOS=0x00 PREC=0x00 TTL=242 ID=7264 PROTO=TCP SPT=58830 DPT=445 WINDOW=1024 RES=0x00 SYN URGP=0 ", + "network.transport": "tcp", + "network.type": "ipv4", + "service.type": "iptables", + "source.ip": "198.51.100.160", + "source.mac": "90:10:9e:ec:2c:71", + "source.port": 58830, + "tags": [ + "iptables" + ] + }, + { + "@timestamp": "2019-01-08T03:41:17.000Z", + "destination.ip": "172.16.54.114", + "destination.mac": "90:10:35:5a:1e:3a", + "destination.port": 445, + "ecs.version": "1.0.0-beta2", + "event.dataset": "iptables.log", + "event.module": "iptables", + "fileset.name": "log", + "input.type": "log", + "iptables.ether_type": 2048, + "iptables.fragment_flags": "DF", + "iptables.id": 6101, + "iptables.input_device": "eth0", + "iptables.length": 52, + "iptables.output_device": "", + "iptables.precedence_bits": 0, + "iptables.tcp.flags": "SYN", + "iptables.tcp.reserved_bits": 0, + "iptables.tcp.window": 8192, + "iptables.tos": 0, + "iptables.ttl": 117, + "log.offset": 1554, + "log.original": "Jan 8 03:41:17 example-host kernel: iptables DROP_INPUT: IN=eth0 OUT= MAC=90:10:35:5a:1e:3a:90:10:76:e0:e2:d5:08:00 SRC=198.51.100.115 DST=172.16.54.114 LEN=52 TOS=0x00 PREC=0x00 TTL=117 ID=6101 DF PROTO=TCP SPT=51985 DPT=445 WINDOW=8192 RES=0x00 SYN URGP=0 ", + "network.transport": "tcp", + "network.type": "ipv4", + "service.type": "iptables", + "source.ip": "198.51.100.115", + "source.mac": "90:10:76:e0:e2:d5", + "source.port": 51985, + "tags": [ + "iptables" + ] + }, + { + "@timestamp": "2019-01-08T03:41:23.000Z", + "destination.ip": "172.16.54.114", + "destination.mac": "90:10:35:5a:1e:3a", + "destination.port": 445, + "ecs.version": "1.0.0-beta2", + "event.dataset": "iptables.log", + "event.module": "iptables", + "fileset.name": "log", + "input.type": "log", + "iptables.ether_type": 2048, + "iptables.fragment_flags": "DF", + "iptables.id": 6319, + "iptables.input_device": "eth0", + "iptables.length": 52, + "iptables.output_device": "", + "iptables.precedence_bits": 0, + "iptables.tcp.flags": "SYN", + "iptables.tcp.reserved_bits": 0, + "iptables.tcp.window": 8192, + "iptables.tos": 0, + "iptables.ttl": 45, + "log.offset": 1814, + "log.original": "Jan 8 03:41:23 example-host kernel: iptables DROP_INPUT: IN=eth0 OUT= MAC=90:10:35:5a:1e:3a:90:10:76:e0:e2:d5:08:00 SRC=198.51.100.167 DST=172.16.54.114 LEN=52 TOS=0x00 PREC=0x00 TTL=45 ID=6319 DF PROTO=TCP SPT=4099 DPT=445 WINDOW=8192 RES=0x00 SYN URGP=0 ", + "network.transport": "tcp", + "network.type": "ipv4", + "service.type": "iptables", + "source.ip": "198.51.100.167", + "source.mac": "90:10:76:e0:e2:d5", + "source.port": 4099, + "tags": [ + "iptables" + ] + }, + { + "@timestamp": "2019-01-08T03:43:18.000Z", + "destination.ip": "172.16.54.114", + "destination.mac": "90:10:35:5a:1e:3a", + "destination.port": 139, + "ecs.version": "1.0.0-beta2", + "event.dataset": "iptables.log", + "event.module": "iptables", + "fileset.name": "log", + "input.type": "log", + "iptables.ether_type": 2048, + "iptables.id": 48624, + "iptables.input_device": "eth0", + "iptables.length": 40, + "iptables.output_device": "", + "iptables.precedence_bits": 0, + "iptables.tcp.flags": "SYN", + "iptables.tcp.reserved_bits": 0, + "iptables.tcp.window": 1024, + "iptables.tos": 0, + "iptables.ttl": 245, + "log.offset": 2072, + "log.original": "Jan 8 03:43:18 example-host kernel: iptables DROP_INPUT: IN=eth0 OUT= MAC=90:10:35:5a:1e:3a:90:10:9e:ec:2c:71:08:00 SRC=198.51.100.19 DST=172.16.54.114 LEN=40 TOS=0x00 PREC=0x00 TTL=245 ID=48624 PROTO=TCP SPT=59287 DPT=139 WINDOW=1024 RES=0x00 SYN URGP=0 ", + "network.transport": "tcp", + "network.type": "ipv4", + "service.type": "iptables", + "source.ip": "198.51.100.19", + "source.mac": "90:10:9e:ec:2c:71", + "source.port": 59287, + "tags": [ + "iptables" + ] + }, + { + "@timestamp": "2019-01-08T03:43:42.000Z", + "destination.ip": "172.16.54.114", + "destination.mac": "90:10:35:5a:1e:3a", + "destination.port": 8088, + "ecs.version": "1.0.0-beta2", + "event.dataset": "iptables.log", + "event.module": "iptables", + "fileset.name": "log", + "input.type": "log", + "iptables.ether_type": 2048, + "iptables.id": 54321, + "iptables.input_device": "eth0", + "iptables.length": 40, + "iptables.output_device": "", + "iptables.precedence_bits": 0, + "iptables.tcp.flags": "SYN", + "iptables.tcp.reserved_bits": 0, + "iptables.tcp.window": 65535, + "iptables.tos": 0, + "iptables.ttl": 250, + "log.offset": 2329, + "log.original": "Jan 8 03:43:42 example-host kernel: iptables DROP_INPUT: IN=eth0 OUT= MAC=90:10:35:5a:1e:3a:90:10:76:e0:e2:d5:08:00:45:00:00:00:00 SRC=198.51.100.68 DST=172.16.54.114 LEN=40 TOS=0x00 PREC=0x00 TTL=250 ID=54321 PROTO=TCP SPT=53296 DPT=8088 WINDOW=65535 RES=0x00 SYN URGP=0 ", + "network.transport": "tcp", + "network.type": "ipv4", + "service.type": "iptables", + "source.ip": "198.51.100.68", + "source.mac": "90:10:76:e0:e2:d5", + "source.port": 53296, + "tags": [ + "iptables" + ] + } +] \ No newline at end of file diff --git a/x-pack/filebeat/module/iptables/log/test/ipv6.log b/x-pack/filebeat/module/iptables/log/test/ipv6.log new file mode 100644 index 00000000000..5541c810644 --- /dev/null +++ b/x-pack/filebeat/module/iptables/log/test/ipv6.log @@ -0,0 +1,11 @@ +Jan 22 09:05:05 ubuntu-bionic kernel: [16571.459614] IN= OUT=lo SRC=2001:0db8:0000:0000:0000:0000:0000:0001 DST=2001:0db8:0000:0000:0000:0000:0000:0002 LEN=104 TC=0 HOPLIMIT=64 FLOWLBL=868225 PROTO=ICMPv6 TYPE=128 CODE=0 ID=3427 SEQ=1 +Jan 22 09:05:05 ubuntu-bionic kernel: [16571.459695] IN= OUT=lo SRC=2001:0db8:0000:0000:0000:0000:0000:0001 DST=2001:0db8:0000:0000:0000:0000:0000:0002 LEN=104 TC=0 HOPLIMIT=64 FLOWLBL=770819 PROTO=ICMPv6 TYPE=129 CODE=0 ID=3427 SEQ=1 +Jan 22 09:05:06 ubuntu-bionic kernel: [16572.482458] IN= OUT=lo SRC=2001:0db8:0000:0000:0000:0000:0000:0001 DST=2001:0db8:0000:0000:0000:0000:0000:0002 LEN=104 TC=0 HOPLIMIT=64 FLOWLBL=868225 PROTO=ICMPv6 TYPE=128 CODE=0 ID=3427 SEQ=2 +Jan 22 09:05:06 ubuntu-bionic kernel: [16572.482476] IN= OUT=lo SRC=2001:0db8:0000:0000:0000:0000:0000:0001 DST=2001:0db8:0000:0000:0000:0000:0000:0002 LEN=104 TC=0 HOPLIMIT=64 FLOWLBL=770819 PROTO=ICMPv6 TYPE=129 CODE=0 ID=3427 SEQ=2 +Jan 22 09:05:07 ubuntu-bionic kernel: [16573.506336] IN= OUT=lo SRC=2001:0db8:0000:0000:0000:0000:0000:0001 DST=2001:0db8:0000:0000:0000:0000:0000:0002 LEN=104 TC=0 HOPLIMIT=64 FLOWLBL=868225 PROTO=ICMPv6 TYPE=128 CODE=0 ID=3427 SEQ=3 +Jan 22 09:05:07 ubuntu-bionic kernel: [16573.506356] IN= OUT=lo SRC=2001:0db8:0000:0000:0000:0000:0000:0001 DST=2001:0db8:0000:0000:0000:0000:0000:0002 LEN=104 TC=0 HOPLIMIT=64 FLOWLBL=770819 PROTO=ICMPv6 TYPE=129 CODE=0 ID=3427 SEQ=3 +Jan 22 09:05:08 ubuntu-bionic kernel: [16574.533989] IN= OUT=lo SRC=2001:0db8:0000:0000:0000:0000:0000:0001 DST=2001:0db8:0000:0000:0000:0000:0000:0002 LEN=104 TC=0 HOPLIMIT=64 FLOWLBL=868225 PROTO=ICMPv6 TYPE=128 CODE=0 ID=3427 SEQ=4 +Jan 22 09:05:08 ubuntu-bionic kernel: [16574.534007] IN= OUT=lo SRC=2001:0db8:0000:0000:0000:0000:0000:0001 DST=2001:0db8:0000:0000:0000:0000:0000:0002 LEN=104 TC=0 HOPLIMIT=64 FLOWLBL=770819 PROTO=ICMPv6 TYPE=129 CODE=0 ID=3427 SEQ=4 +Jan 22 09:05:09 ubuntu-bionic kernel: [16575.553704] IN= OUT=lo SRC=2001:0db8:0000:0000:0000:0000:0000:0001 DST=2001:0db8:0000:0000:0000:0000:0000:0002 LEN=104 TC=0 HOPLIMIT=64 FLOWLBL=868225 PROTO=ICMPv6 TYPE=128 CODE=0 ID=3427 SEQ=5 +Jan 22 09:05:09 ubuntu-bionic kernel: [16575.553722] IN= OUT=lo SRC=2001:0db8:0000:0000:0000:0000:0000:0001 DST=2001:0db8:0000:0000:0000:0000:0000:0002 LEN=104 TC=0 HOPLIMIT=64 FLOWLBL=770819 PROTO=ICMPv6 TYPE=129 CODE=0 ID=3427 SEQ=5 +Jan 22 10:52:34 ubuntu-bionic kernel: [ 307.757925] IN= OUT=enp0s3 MAC=90:10:12:34:56:78:90:10:aa:bb:cc:dd:86:dd:ff:ff SRC=fe80:0000:0000:0000:0084:88ff:feae:790a DST=ff02:0000:0000:0000:0000:0000:0000:0016 LEN=96 TC=0 HOPLIMIT=1 FLOWLBL=0 PROTO=ICMPv6 TYPE=143 CODE=0 MARK=0xd4 diff --git a/x-pack/filebeat/module/iptables/log/test/ipv6.log-expected.json b/x-pack/filebeat/module/iptables/log/test/ipv6.log-expected.json new file mode 100644 index 00000000000..5c662ddd178 --- /dev/null +++ b/x-pack/filebeat/module/iptables/log/test/ipv6.log-expected.json @@ -0,0 +1,301 @@ +[ + { + "@timestamp": "2019-01-22T09:05:05.000Z", + "destination.ip": "2001:0db8:0000:0000:0000:0000:0000:0002", + "ecs.version": "1.0.0-beta2", + "event.dataset": "iptables.log", + "event.module": "iptables", + "fileset.name": "log", + "input.type": "log", + "iptables.flow_label": 868225, + "iptables.icmp.code": 0, + "iptables.icmp.id": 3427, + "iptables.icmp.seq": 1, + "iptables.icmp.type": 128, + "iptables.input_device": "", + "iptables.length": 104, + "iptables.output_device": "lo", + "iptables.tos": 0, + "iptables.ttl": 64, + "log.offset": 0, + "log.original": "Jan 22 09:05:05 ubuntu-bionic kernel: [16571.459614] IN= OUT=lo SRC=2001:0db8:0000:0000:0000:0000:0000:0001 DST=2001:0db8:0000:0000:0000:0000:0000:0002 LEN=104 TC=0 HOPLIMIT=64 FLOWLBL=868225 PROTO=ICMPv6 TYPE=128 CODE=0 ID=3427 SEQ=1 ", + "network.transport": "ipv6-icmp", + "service.type": "iptables", + "source.ip": "2001:0db8:0000:0000:0000:0000:0000:0001", + "tags": [ + "iptables" + ] + }, + { + "@timestamp": "2019-01-22T09:05:05.000Z", + "destination.ip": "2001:0db8:0000:0000:0000:0000:0000:0002", + "ecs.version": "1.0.0-beta2", + "event.dataset": "iptables.log", + "event.module": "iptables", + "fileset.name": "log", + "input.type": "log", + "iptables.flow_label": 770819, + "iptables.icmp.code": 0, + "iptables.icmp.id": 3427, + "iptables.icmp.seq": 1, + "iptables.icmp.type": 129, + "iptables.input_device": "", + "iptables.length": 104, + "iptables.output_device": "lo", + "iptables.tos": 0, + "iptables.ttl": 64, + "log.offset": 236, + "log.original": "Jan 22 09:05:05 ubuntu-bionic kernel: [16571.459695] IN= OUT=lo SRC=2001:0db8:0000:0000:0000:0000:0000:0001 DST=2001:0db8:0000:0000:0000:0000:0000:0002 LEN=104 TC=0 HOPLIMIT=64 FLOWLBL=770819 PROTO=ICMPv6 TYPE=129 CODE=0 ID=3427 SEQ=1 ", + "network.transport": "ipv6-icmp", + "service.type": "iptables", + "source.ip": "2001:0db8:0000:0000:0000:0000:0000:0001", + "tags": [ + "iptables" + ] + }, + { + "@timestamp": "2019-01-22T09:05:06.000Z", + "destination.ip": "2001:0db8:0000:0000:0000:0000:0000:0002", + "ecs.version": "1.0.0-beta2", + "event.dataset": "iptables.log", + "event.module": "iptables", + "fileset.name": "log", + "input.type": "log", + "iptables.flow_label": 868225, + "iptables.icmp.code": 0, + "iptables.icmp.id": 3427, + "iptables.icmp.seq": 2, + "iptables.icmp.type": 128, + "iptables.input_device": "", + "iptables.length": 104, + "iptables.output_device": "lo", + "iptables.tos": 0, + "iptables.ttl": 64, + "log.offset": 472, + "log.original": "Jan 22 09:05:06 ubuntu-bionic kernel: [16572.482458] IN= OUT=lo SRC=2001:0db8:0000:0000:0000:0000:0000:0001 DST=2001:0db8:0000:0000:0000:0000:0000:0002 LEN=104 TC=0 HOPLIMIT=64 FLOWLBL=868225 PROTO=ICMPv6 TYPE=128 CODE=0 ID=3427 SEQ=2 ", + "network.transport": "ipv6-icmp", + "service.type": "iptables", + "source.ip": "2001:0db8:0000:0000:0000:0000:0000:0001", + "tags": [ + "iptables" + ] + }, + { + "@timestamp": "2019-01-22T09:05:06.000Z", + "destination.ip": "2001:0db8:0000:0000:0000:0000:0000:0002", + "ecs.version": "1.0.0-beta2", + "event.dataset": "iptables.log", + "event.module": "iptables", + "fileset.name": "log", + "input.type": "log", + "iptables.flow_label": 770819, + "iptables.icmp.code": 0, + "iptables.icmp.id": 3427, + "iptables.icmp.seq": 2, + "iptables.icmp.type": 129, + "iptables.input_device": "", + "iptables.length": 104, + "iptables.output_device": "lo", + "iptables.tos": 0, + "iptables.ttl": 64, + "log.offset": 708, + "log.original": "Jan 22 09:05:06 ubuntu-bionic kernel: [16572.482476] IN= OUT=lo SRC=2001:0db8:0000:0000:0000:0000:0000:0001 DST=2001:0db8:0000:0000:0000:0000:0000:0002 LEN=104 TC=0 HOPLIMIT=64 FLOWLBL=770819 PROTO=ICMPv6 TYPE=129 CODE=0 ID=3427 SEQ=2 ", + "network.transport": "ipv6-icmp", + "service.type": "iptables", + "source.ip": "2001:0db8:0000:0000:0000:0000:0000:0001", + "tags": [ + "iptables" + ] + }, + { + "@timestamp": "2019-01-22T09:05:07.000Z", + "destination.ip": "2001:0db8:0000:0000:0000:0000:0000:0002", + "ecs.version": "1.0.0-beta2", + "event.dataset": "iptables.log", + "event.module": "iptables", + "fileset.name": "log", + "input.type": "log", + "iptables.flow_label": 868225, + "iptables.icmp.code": 0, + "iptables.icmp.id": 3427, + "iptables.icmp.seq": 3, + "iptables.icmp.type": 128, + "iptables.input_device": "", + "iptables.length": 104, + "iptables.output_device": "lo", + "iptables.tos": 0, + "iptables.ttl": 64, + "log.offset": 944, + "log.original": "Jan 22 09:05:07 ubuntu-bionic kernel: [16573.506336] IN= OUT=lo SRC=2001:0db8:0000:0000:0000:0000:0000:0001 DST=2001:0db8:0000:0000:0000:0000:0000:0002 LEN=104 TC=0 HOPLIMIT=64 FLOWLBL=868225 PROTO=ICMPv6 TYPE=128 CODE=0 ID=3427 SEQ=3 ", + "network.transport": "ipv6-icmp", + "service.type": "iptables", + "source.ip": "2001:0db8:0000:0000:0000:0000:0000:0001", + "tags": [ + "iptables" + ] + }, + { + "@timestamp": "2019-01-22T09:05:07.000Z", + "destination.ip": "2001:0db8:0000:0000:0000:0000:0000:0002", + "ecs.version": "1.0.0-beta2", + "event.dataset": "iptables.log", + "event.module": "iptables", + "fileset.name": "log", + "input.type": "log", + "iptables.flow_label": 770819, + "iptables.icmp.code": 0, + "iptables.icmp.id": 3427, + "iptables.icmp.seq": 3, + "iptables.icmp.type": 129, + "iptables.input_device": "", + "iptables.length": 104, + "iptables.output_device": "lo", + "iptables.tos": 0, + "iptables.ttl": 64, + "log.offset": 1180, + "log.original": "Jan 22 09:05:07 ubuntu-bionic kernel: [16573.506356] IN= OUT=lo SRC=2001:0db8:0000:0000:0000:0000:0000:0001 DST=2001:0db8:0000:0000:0000:0000:0000:0002 LEN=104 TC=0 HOPLIMIT=64 FLOWLBL=770819 PROTO=ICMPv6 TYPE=129 CODE=0 ID=3427 SEQ=3 ", + "network.transport": "ipv6-icmp", + "service.type": "iptables", + "source.ip": "2001:0db8:0000:0000:0000:0000:0000:0001", + "tags": [ + "iptables" + ] + }, + { + "@timestamp": "2019-01-22T09:05:08.000Z", + "destination.ip": "2001:0db8:0000:0000:0000:0000:0000:0002", + "ecs.version": "1.0.0-beta2", + "event.dataset": "iptables.log", + "event.module": "iptables", + "fileset.name": "log", + "input.type": "log", + "iptables.flow_label": 868225, + "iptables.icmp.code": 0, + "iptables.icmp.id": 3427, + "iptables.icmp.seq": 4, + "iptables.icmp.type": 128, + "iptables.input_device": "", + "iptables.length": 104, + "iptables.output_device": "lo", + "iptables.tos": 0, + "iptables.ttl": 64, + "log.offset": 1416, + "log.original": "Jan 22 09:05:08 ubuntu-bionic kernel: [16574.533989] IN= OUT=lo SRC=2001:0db8:0000:0000:0000:0000:0000:0001 DST=2001:0db8:0000:0000:0000:0000:0000:0002 LEN=104 TC=0 HOPLIMIT=64 FLOWLBL=868225 PROTO=ICMPv6 TYPE=128 CODE=0 ID=3427 SEQ=4 ", + "network.transport": "ipv6-icmp", + "service.type": "iptables", + "source.ip": "2001:0db8:0000:0000:0000:0000:0000:0001", + "tags": [ + "iptables" + ] + }, + { + "@timestamp": "2019-01-22T09:05:08.000Z", + "destination.ip": "2001:0db8:0000:0000:0000:0000:0000:0002", + "ecs.version": "1.0.0-beta2", + "event.dataset": "iptables.log", + "event.module": "iptables", + "fileset.name": "log", + "input.type": "log", + "iptables.flow_label": 770819, + "iptables.icmp.code": 0, + "iptables.icmp.id": 3427, + "iptables.icmp.seq": 4, + "iptables.icmp.type": 129, + "iptables.input_device": "", + "iptables.length": 104, + "iptables.output_device": "lo", + "iptables.tos": 0, + "iptables.ttl": 64, + "log.offset": 1652, + "log.original": "Jan 22 09:05:08 ubuntu-bionic kernel: [16574.534007] IN= OUT=lo SRC=2001:0db8:0000:0000:0000:0000:0000:0001 DST=2001:0db8:0000:0000:0000:0000:0000:0002 LEN=104 TC=0 HOPLIMIT=64 FLOWLBL=770819 PROTO=ICMPv6 TYPE=129 CODE=0 ID=3427 SEQ=4 ", + "network.transport": "ipv6-icmp", + "service.type": "iptables", + "source.ip": "2001:0db8:0000:0000:0000:0000:0000:0001", + "tags": [ + "iptables" + ] + }, + { + "@timestamp": "2019-01-22T09:05:09.000Z", + "destination.ip": "2001:0db8:0000:0000:0000:0000:0000:0002", + "ecs.version": "1.0.0-beta2", + "event.dataset": "iptables.log", + "event.module": "iptables", + "fileset.name": "log", + "input.type": "log", + "iptables.flow_label": 868225, + "iptables.icmp.code": 0, + "iptables.icmp.id": 3427, + "iptables.icmp.seq": 5, + "iptables.icmp.type": 128, + "iptables.input_device": "", + "iptables.length": 104, + "iptables.output_device": "lo", + "iptables.tos": 0, + "iptables.ttl": 64, + "log.offset": 1888, + "log.original": "Jan 22 09:05:09 ubuntu-bionic kernel: [16575.553704] IN= OUT=lo SRC=2001:0db8:0000:0000:0000:0000:0000:0001 DST=2001:0db8:0000:0000:0000:0000:0000:0002 LEN=104 TC=0 HOPLIMIT=64 FLOWLBL=868225 PROTO=ICMPv6 TYPE=128 CODE=0 ID=3427 SEQ=5 ", + "network.transport": "ipv6-icmp", + "service.type": "iptables", + "source.ip": "2001:0db8:0000:0000:0000:0000:0000:0001", + "tags": [ + "iptables" + ] + }, + { + "@timestamp": "2019-01-22T09:05:09.000Z", + "destination.ip": "2001:0db8:0000:0000:0000:0000:0000:0002", + "ecs.version": "1.0.0-beta2", + "event.dataset": "iptables.log", + "event.module": "iptables", + "fileset.name": "log", + "input.type": "log", + "iptables.flow_label": 770819, + "iptables.icmp.code": 0, + "iptables.icmp.id": 3427, + "iptables.icmp.seq": 5, + "iptables.icmp.type": 129, + "iptables.input_device": "", + "iptables.length": 104, + "iptables.output_device": "lo", + "iptables.tos": 0, + "iptables.ttl": 64, + "log.offset": 2124, + "log.original": "Jan 22 09:05:09 ubuntu-bionic kernel: [16575.553722] IN= OUT=lo SRC=2001:0db8:0000:0000:0000:0000:0000:0001 DST=2001:0db8:0000:0000:0000:0000:0000:0002 LEN=104 TC=0 HOPLIMIT=64 FLOWLBL=770819 PROTO=ICMPv6 TYPE=129 CODE=0 ID=3427 SEQ=5 ", + "network.transport": "ipv6-icmp", + "service.type": "iptables", + "source.ip": "2001:0db8:0000:0000:0000:0000:0000:0001", + "tags": [ + "iptables" + ] + }, + { + "@timestamp": "2019-01-22T10:52:34.000Z", + "destination.ip": "ff02:0000:0000:0000:0000:0000:0000:0016", + "destination.mac": "90:10:12:34:56:78", + "ecs.version": "1.0.0-beta2", + "event.dataset": "iptables.log", + "event.module": "iptables", + "fileset.name": "log", + "input.type": "log", + "iptables.ether_type": 34525, + "iptables.flow_label": 0, + "iptables.icmp.code": 0, + "iptables.icmp.type": 143, + "iptables.input_device": "", + "iptables.length": 96, + "iptables.output_device": "enp0s3", + "iptables.tos": 0, + "iptables.ttl": 1, + "log.offset": 2360, + "log.original": "Jan 22 10:52:34 ubuntu-bionic kernel: [ 307.757925] IN= OUT=enp0s3 MAC=90:10:12:34:56:78:90:10:aa:bb:cc:dd:86:dd:ff:ff SRC=fe80:0000:0000:0000:0084:88ff:feae:790a DST=ff02:0000:0000:0000:0000:0000:0000:0016 LEN=96 TC=0 HOPLIMIT=1 FLOWLBL=0 PROTO=ICMPv6 TYPE=143 CODE=0 MARK=0xd4", + "network.transport": "ipv6-icmp", + "network.type": "ipv6", + "service.type": "iptables", + "source.ip": "fe80:0000:0000:0000:0084:88ff:feae:790a", + "source.mac": "90:10:aa:bb:cc:dd", + "tags": [ + "iptables" + ] + } +] \ No newline at end of file diff --git a/x-pack/filebeat/module/iptables/log/test/ubiquiti.log b/x-pack/filebeat/module/iptables/log/test/ubiquiti.log new file mode 100644 index 00000000000..c795c77a516 --- /dev/null +++ b/x-pack/filebeat/module/iptables/log/test/ubiquiti.log @@ -0,0 +1,5 @@ +Jan 5 20:17:05 MainFirewall kernel: [LAN_LOCAL-default-A]IN=eth0.90 OUT= MAC=90:10:92:6e:ea:a7:90:10:73:ba:d6:77:08:00:45:fc:02:1c SRC=192.168.48.137 DST=255.55.174.225 LEN=540 TOS=0x1C PREC=0xE0 TTL=64 ID=27223 PROTO=UDP SPT=48689 DPT=48689 LEN=520 +Jan 5 20:17:01 MainFirewall kernel: [WAN_OUT-2000-A]IN=eth0 OUT=eth2 MAC=90:10:20:76:8d:20:90:10:24:67:f4:89:08:00 SRC=192.168.134.158 DST=192.0.2.25 LEN=265 TOS=0x00 PREC=0x00 TTL=63 ID=51768 DF PROTO=TCP SPT=43189 DPT=443 WINDOW=159 RES=0x00 ACK PSH URGP=0 +Jan 5 20:17:01 MainFirewall kernel: [source-dest-default-D]IN=eth0 OUT=eth2 MAC=90:10:20:76:8d:20:90:10:65:29:b6:2a:08:00 SRC=192.168.110.116 DST=192.0.2.25 LEN=52 TOS=0x00 PREC=0x00 TTL=63 ID=0 DF PROTO=TCP SPT=50093 DPT=1443 WINDOW=2857 RES=0x00 ACK URGP=0 +Jan 5 20:17:01 MainFirewall kernel: [WAN_OUT-2000-A]IN=eth0 OUT=eth2 MAC=90:10:20:76:8d:20:90:10:65:29:b6:2a:08:00 SRC=192.168.110.116 DST=192.0.2.25 LEN=52 TOS=0x00 PREC=0x00 TTL=63 ID=0 DF PROTO=TCP SPT=50093 DPT=1443 WINDOW=2853 RES=0x00 ACK URGP=0 +Jan 5 20:17:01 MainFirewall kernel: [WAN_OUT-2000-A]IN=eth0 OUT=eth2 MAC=90:10:20:76:8d:20:90:10:65:29:b6:2a:08:00 SRC=192.168.110.116 DST=192.0.2.25 LEN=52 TOS=0x00 PREC=0x00 TTL=63 ID=0 DF PROTO=TCP SPT=50093 DPT=1443 WINDOW=2850 RES=0x00 ACK URGP=0 diff --git a/x-pack/filebeat/module/iptables/log/test/ubiquiti.log-expected.json b/x-pack/filebeat/module/iptables/log/test/ubiquiti.log-expected.json new file mode 100644 index 00000000000..f268a98822e --- /dev/null +++ b/x-pack/filebeat/module/iptables/log/test/ubiquiti.log-expected.json @@ -0,0 +1,186 @@ +[ + { + "@timestamp": "2019-01-05T20:17:05.000Z", + "destination.ip": "255.55.174.225", + "destination.mac": "90:10:92:6e:ea:a7", + "destination.port": 48689, + "ecs.version": "1.0.0-beta2", + "event.dataset": "iptables.log", + "event.module": "iptables", + "event.outcome": "allow", + "fileset.name": "log", + "input.type": "log", + "iptables.ether_type": 2048, + "iptables.id": 27223, + "iptables.input_device": "eth0.90", + "iptables.length": 540, + "iptables.output_device": "", + "iptables.precedence_bits": 224, + "iptables.tos": 28, + "iptables.ttl": 64, + "iptables.ubiquiti.rule_number": "default", + "iptables.ubiquiti.rule_set": "LAN_LOCAL", + "iptables.udp.length": 520, + "log.offset": 0, + "log.original": "Jan 5 20:17:05 MainFirewall kernel: [LAN_LOCAL-default-A]IN=eth0.90 OUT= MAC=90:10:92:6e:ea:a7:90:10:73:ba:d6:77:08:00:45:fc:02:1c SRC=192.168.48.137 DST=255.55.174.225 LEN=540 TOS=0x1C PREC=0xE0 TTL=64 ID=27223 PROTO=UDP SPT=48689 DPT=48689 LEN=520 ", + "network.transport": "udp", + "network.type": "ipv4", + "service.type": "iptables", + "source.ip": "192.168.48.137", + "source.mac": "90:10:73:ba:d6:77", + "source.port": 48689, + "tags": [ + "iptables" + ] + }, + { + "@timestamp": "2019-01-05T20:17:01.000Z", + "destination.ip": "192.0.2.25", + "destination.mac": "90:10:20:76:8d:20", + "destination.port": 443, + "ecs.version": "1.0.0-beta2", + "event.dataset": "iptables.log", + "event.module": "iptables", + "event.outcome": "allow", + "fileset.name": "log", + "input.type": "log", + "iptables.ether_type": 2048, + "iptables.fragment_flags": "DF", + "iptables.id": 51768, + "iptables.input_device": "eth0", + "iptables.length": 265, + "iptables.output_device": "eth2", + "iptables.precedence_bits": 0, + "iptables.tcp.flags": "ACK", + "iptables.tcp.reserved_bits": 0, + "iptables.tcp.window": 159, + "iptables.tos": 0, + "iptables.ttl": 63, + "iptables.ubiquiti.rule_number": "2000", + "iptables.ubiquiti.rule_set": "WAN_OUT", + "log.offset": 252, + "log.original": "Jan 5 20:17:01 MainFirewall kernel: [WAN_OUT-2000-A]IN=eth0 OUT=eth2 MAC=90:10:20:76:8d:20:90:10:24:67:f4:89:08:00 SRC=192.168.134.158 DST=192.0.2.25 LEN=265 TOS=0x00 PREC=0x00 TTL=63 ID=51768 DF PROTO=TCP SPT=43189 DPT=443 WINDOW=159 RES=0x00 ACK PSH URGP=0 ", + "network.transport": "tcp", + "network.type": "ipv4", + "service.type": "iptables", + "source.ip": "192.168.134.158", + "source.mac": "90:10:24:67:f4:89", + "source.port": 43189, + "tags": [ + "iptables" + ] + }, + { + "@timestamp": "2019-01-05T20:17:01.000Z", + "destination.ip": "192.0.2.25", + "destination.mac": "90:10:20:76:8d:20", + "destination.port": 1443, + "ecs.version": "1.0.0-beta2", + "event.dataset": "iptables.log", + "event.module": "iptables", + "event.outcome": "deny", + "fileset.name": "log", + "input.type": "log", + "iptables.ether_type": 2048, + "iptables.fragment_flags": "DF", + "iptables.id": 0, + "iptables.input_device": "eth0", + "iptables.length": 52, + "iptables.output_device": "eth2", + "iptables.precedence_bits": 0, + "iptables.tcp.flags": "ACK", + "iptables.tcp.reserved_bits": 0, + "iptables.tcp.window": 2857, + "iptables.tos": 0, + "iptables.ttl": 63, + "iptables.ubiquiti.input_zone": "source", + "iptables.ubiquiti.output_zone": "dest", + "iptables.ubiquiti.rule_number": "default", + "iptables.ubiquiti.rule_set": "source-dest", + "log.offset": 513, + "log.original": "Jan 5 20:17:01 MainFirewall kernel: [source-dest-default-D]IN=eth0 OUT=eth2 MAC=90:10:20:76:8d:20:90:10:65:29:b6:2a:08:00 SRC=192.168.110.116 DST=192.0.2.25 LEN=52 TOS=0x00 PREC=0x00 TTL=63 ID=0 DF PROTO=TCP SPT=50093 DPT=1443 WINDOW=2857 RES=0x00 ACK URGP=0 ", + "network.transport": "tcp", + "network.type": "ipv4", + "service.type": "iptables", + "source.ip": "192.168.110.116", + "source.mac": "90:10:65:29:b6:2a", + "source.port": 50093, + "tags": [ + "iptables" + ] + }, + { + "@timestamp": "2019-01-05T20:17:01.000Z", + "destination.ip": "192.0.2.25", + "destination.mac": "90:10:20:76:8d:20", + "destination.port": 1443, + "ecs.version": "1.0.0-beta2", + "event.dataset": "iptables.log", + "event.module": "iptables", + "event.outcome": "allow", + "fileset.name": "log", + "input.type": "log", + "iptables.ether_type": 2048, + "iptables.fragment_flags": "DF", + "iptables.id": 0, + "iptables.input_device": "eth0", + "iptables.length": 52, + "iptables.output_device": "eth2", + "iptables.precedence_bits": 0, + "iptables.tcp.flags": "ACK", + "iptables.tcp.reserved_bits": 0, + "iptables.tcp.window": 2853, + "iptables.tos": 0, + "iptables.ttl": 63, + "iptables.ubiquiti.rule_number": "2000", + "iptables.ubiquiti.rule_set": "WAN_OUT", + "log.offset": 774, + "log.original": "Jan 5 20:17:01 MainFirewall kernel: [WAN_OUT-2000-A]IN=eth0 OUT=eth2 MAC=90:10:20:76:8d:20:90:10:65:29:b6:2a:08:00 SRC=192.168.110.116 DST=192.0.2.25 LEN=52 TOS=0x00 PREC=0x00 TTL=63 ID=0 DF PROTO=TCP SPT=50093 DPT=1443 WINDOW=2853 RES=0x00 ACK URGP=0 ", + "network.transport": "tcp", + "network.type": "ipv4", + "service.type": "iptables", + "source.ip": "192.168.110.116", + "source.mac": "90:10:65:29:b6:2a", + "source.port": 50093, + "tags": [ + "iptables" + ] + }, + { + "@timestamp": "2019-01-05T20:17:01.000Z", + "destination.ip": "192.0.2.25", + "destination.mac": "90:10:20:76:8d:20", + "destination.port": 1443, + "ecs.version": "1.0.0-beta2", + "event.dataset": "iptables.log", + "event.module": "iptables", + "event.outcome": "allow", + "fileset.name": "log", + "input.type": "log", + "iptables.ether_type": 2048, + "iptables.fragment_flags": "DF", + "iptables.id": 0, + "iptables.input_device": "eth0", + "iptables.length": 52, + "iptables.output_device": "eth2", + "iptables.precedence_bits": 0, + "iptables.tcp.flags": "ACK", + "iptables.tcp.reserved_bits": 0, + "iptables.tcp.window": 2850, + "iptables.tos": 0, + "iptables.ttl": 63, + "iptables.ubiquiti.rule_number": "2000", + "iptables.ubiquiti.rule_set": "WAN_OUT", + "log.offset": 1028, + "log.original": "Jan 5 20:17:01 MainFirewall kernel: [WAN_OUT-2000-A]IN=eth0 OUT=eth2 MAC=90:10:20:76:8d:20:90:10:65:29:b6:2a:08:00 SRC=192.168.110.116 DST=192.0.2.25 LEN=52 TOS=0x00 PREC=0x00 TTL=63 ID=0 DF PROTO=TCP SPT=50093 DPT=1443 WINDOW=2850 RES=0x00 ACK URGP=0 ", + "network.transport": "tcp", + "network.type": "ipv4", + "service.type": "iptables", + "source.ip": "192.168.110.116", + "source.mac": "90:10:65:29:b6:2a", + "source.port": 50093, + "tags": [ + "iptables" + ] + } +] \ No newline at end of file diff --git a/x-pack/filebeat/module/iptables/module.yml b/x-pack/filebeat/module/iptables/module.yml new file mode 100644 index 00000000000..3129ecbdbc3 --- /dev/null +++ b/x-pack/filebeat/module/iptables/module.yml @@ -0,0 +1,5 @@ +dashboards: +- id: ceefb9e0-1f51-11e9-93ed-f7e068f4aebb + file: Filebeat-Iptables-Overview.json +- id: d39f0980-1ff3-11e9-ae2a-939083c6a64e + file: Filebeat-Iptables-Ubiquiti-Firewall-Overview.json diff --git a/x-pack/filebeat/module/suricata/_meta/kibana/7/dashboard/Filebeat-Suricata-Alert-Overview.json b/x-pack/filebeat/module/suricata/_meta/kibana/7/dashboard/Filebeat-Suricata-Alert-Overview.json index f6330c2d0d0..26be4f57616 100644 --- a/x-pack/filebeat/module/suricata/_meta/kibana/7/dashboard/Filebeat-Suricata-Alert-Overview.json +++ b/x-pack/filebeat/module/suricata/_meta/kibana/7/dashboard/Filebeat-Suricata-Alert-Overview.json @@ -226,10 +226,10 @@ "columns": [ "host.name", "suricata.eve.flow_id", - "suricata.eve.src_ip", - "suricata.eve.src_port", - "suricata.eve.dest_ip", - "suricata.eve.dest_port", + "source.ip", + "source.port", + "destination.ip", + "destination.port", "source.geo.country_iso_code", "destination.geo.country_iso_code" ], diff --git a/x-pack/filebeat/module/suricata/_meta/kibana/7/dashboard/Filebeat-Suricata-Overview.json b/x-pack/filebeat/module/suricata/_meta/kibana/7/dashboard/Filebeat-Suricata-Overview.json index 1b8597655ef..92a7047df8a 100644 --- a/x-pack/filebeat/module/suricata/_meta/kibana/7/dashboard/Filebeat-Suricata-Overview.json +++ b/x-pack/filebeat/module/suricata/_meta/kibana/7/dashboard/Filebeat-Suricata-Overview.json @@ -225,7 +225,7 @@ "enabled": true, "id": "2", "params": { - "field": "suricata.eve.app_proto", + "field": "network.protocol", "missingBucket": false, "missingBucketLabel": "Missing", "order": "desc", @@ -398,7 +398,7 @@ "host.name", "event.type", "suricata.eve.flow_id", - "suricata.eve.proto", + "network.transport", "source.ip", "source.port", "destination.ip", @@ -632,7 +632,7 @@ "enabled": true, "id": "2", "params": { - "field": "suricata.eve.proto", + "field": "network.transport", "missingBucket": false, "missingBucketLabel": "Missing", "order": "desc", @@ -916,4 +916,4 @@ } ], "version": "6.4.3" -} \ No newline at end of file +} diff --git a/x-pack/filebeat/module/suricata/eve/_meta/fields.yml b/x-pack/filebeat/module/suricata/eve/_meta/fields.yml index 4b481594ba3..eb2971b3888 100644 --- a/x-pack/filebeat/module/suricata/eve/_meta/fields.yml +++ b/x-pack/filebeat/module/suricata/eve/_meta/fields.yml @@ -4,7 +4,8 @@ Fields exported by the EVE JSON logs fields: - name: event_type - type: keyword + type: alias + path: event.type - name: app_proto_orig type: keyword @@ -46,7 +47,8 @@ type: keyword - name: filename - type: keyword + type: alias + path: file.path - name: tx_id type: long @@ -67,25 +69,30 @@ type: keyword - name: size - type: long + type: alias + path: file.size - name: icmp_type type: long - name: dest_port - type: long + type: alias + path: destination.port - name: src_port - type: long + type: alias + path: source.port - name: proto - type: keyword + type: alias + path: network.transport - name: pcap_cnt type: long - name: src_ip - type: ip + type: alias + path: source.ip - name: dns type: group @@ -124,7 +131,8 @@ type: keyword - name: dest_ip - type: ip + type: alias + path: destination.ip - name: icmp_code type: long @@ -133,37 +141,45 @@ type: group fields: - name: status - type: long + type: alias + path: http.response.status_code - name: redirect type: keyword - name: http_user_agent - type: keyword + type: alias + path: user_agent.original - name: protocol type: keyword - name: http_refer - type: keyword + type: alias + path: http.request.referrer - name: url - type: keyword + type: alias + path: url.original - name: hostname - type: keyword + type: alias + path: url.domain - name: length - type: long + type: alias + path: http.response.body.bytes - name: http_method - type: keyword + type: alias + path: http.request.method - name: http_content_type type: keyword - name: timestamp - type: date + type: alias + path: '@timestamp' - name: in_iface type: keyword @@ -175,7 +191,8 @@ type: keyword - name: severity - type: long + type: alias + path: event.severity - name: rev type: long @@ -187,7 +204,8 @@ type: keyword - name: action - type: keyword + type: alias + path: event.outcome - name: signature_id type: long @@ -655,13 +673,16 @@ type: group fields: - name: bytes_toclient - type: long + type: alias + path: destination.bytes - name: start - type: date + type: alias + path: event.start - name: pkts_toclient - type: long + type: alias + path: destination.packets - name: age type: long @@ -670,13 +691,15 @@ type: keyword - name: bytes_toserver - type: long + type: alias + path: source.bytes - name: reason type: keyword - name: pkts_toserver - type: long + type: alias + path: source.packets - name: end type: date @@ -685,7 +708,8 @@ type: boolean - name: app_proto - type: keyword + type: alias + path: network.protocol - name: tx_id type: long diff --git a/x-pack/filebeat/module/suricata/eve/ingest/pipeline.json b/x-pack/filebeat/module/suricata/eve/ingest/pipeline.json index d117ae4564a..00c59caa23f 100644 --- a/x-pack/filebeat/module/suricata/eve/ingest/pipeline.json +++ b/x-pack/filebeat/module/suricata/eve/ingest/pipeline.json @@ -1,283 +1,322 @@ -{ "description": "Pipeline for parsing Suricata EVE logs" -, "processors": - [ { "script": - { "lang": "painless" - , "source": "ctx['suricata'] = new HashMap(); ctx['suricata']['eve'] = ctx['json']; ctx.remove('json');" - } - } - , {"convert": - {"field": "suricata.eve.src_ip" - ,"target_field": "source.ip" - ,"type": "string" - ,"ignore_missing": true - } - } - , {"convert": - {"field": "suricata.eve.src_port" - ,"target_field": "source.port" - ,"type": "integer" - ,"ignore_missing": true - } - } - , {"convert": - {"field": "suricata.eve.dest_ip" - ,"target_field": "destination.ip" - ,"type": "string" - ,"ignore_missing": true - } - } - , {"convert": - {"field": "suricata.eve.dest_port" - ,"target_field": "destination.port" - ,"type": "integer" - ,"ignore_missing": true - } - } - , {"lowercase": - {"field": "suricata.eve.http.http_method" - ,"target_field": "http.request.method" - ,"ignore_missing": true - } - } - , {"convert": - {"field": "suricata.eve.http.status" - ,"target_field": "http.response.status_code" - ,"type": "string" - ,"ignore_missing": true - } - } - , {"convert": - {"field": "suricata.eve.http.hostname" - ,"target_field": "url.domain" - ,"type": "string" - ,"ignore_missing": true - } - } - , { "grok": - { "field": "suricata.eve.http.url" - , "patterns": ["%{PATH:url.path}(?:\\?%{QUERY:url.query})?(?:#%{ANY:url.fragment})?"] - , "ignore_missing": true - , "pattern_definitions": - { "PATH": "[^?#]*" - , "QUERY": "[^#]*" - , "ANY": ".*" +{ + "description": "Pipeline for parsing Suricata EVE logs", + "processors": [ + { + "script": { + "lang": "painless", + "source": "ctx['suricata'] = new HashMap(); ctx['suricata']['eve'] = ctx['json']; ctx.remove('json');" + } + }, + { + "rename": { + "field": "suricata.eve.src_ip", + "target_field": "source.ip", + "ignore_missing": true + } + }, + { + "rename": { + "field": "suricata.eve.src_port", + "target_field": "source.port", + "ignore_missing": true + } + }, + { + "rename": { + "field": "suricata.eve.dest_ip", + "target_field": "destination.ip", + "ignore_missing": true + } + }, + { + "rename": { + "field": "suricata.eve.dest_port", + "target_field": "destination.port", + "ignore_missing": true + } + }, + { + "lowercase": { + "field": "suricata.eve.http.http_method", + "target_field": "http.request.method", + "ignore_missing": true + } + }, + { + "rename": { + "field": "suricata.eve.http.status", + "target_field": "http.response.status_code", + "ignore_missing": true + } + }, + { + "rename": { + "field": "suricata.eve.http.hostname", + "target_field": "destination.domain", + "ignore_missing": true + } + }, + { + "set": { + "field": "url.domain", + "value": "{{destination.domain}}", + "if": "ctx?.destination?.domain != null" + } + }, + { + "grok": { + "field": "suricata.eve.http.url", + "patterns": [ + "%{PATH:url.path}(?:\\?%{QUERY:url.query})?(?:#%{ANY:url.fragment})?" + ], + "ignore_missing": true, + "pattern_definitions": { + "PATH": "[^?#]*", + "QUERY": "[^#]*", + "ANY": ".*" + } + } + }, + { + "rename": { + "field": "suricata.eve.http.url", + "target_field": "url.original", + "ignore_missing": true + } + }, + { + "rename": { + "field": "suricata.eve.http.http_refer", + "target_field": "http.request.referrer", + "ignore_missing": true + } + }, + { + "rename": { + "field": "suricata.eve.http.length", + "target_field": "http.response.body.bytes", + "ignore_missing": true + } + }, + { + "rename": { + "field": "suricata.eve.fileinfo.filename", + "target_field": "file.path", + "ignore_missing": true + } + }, + { + "rename": { + "field": "suricata.eve.fileinfo.size", + "target_field": "file.size", + "ignore_missing": true + } + }, + { + "date": { + "field": "suricata.eve.timestamp", + "target_field": "@timestamp", + "formats": [ + "ISO8601" + ] + } + }, + { + "lowercase": { + "field": "suricata.eve.event_type", + "target_field": "event.type", + "ignore_missing": true + } + }, + { + "convert": { + "field": "suricata.eve.alert.category", + "target_field": "message", + "type": "string", + "ignore_missing": true + } + }, + { + "rename": { + "field": "suricata.eve.alert.action", + "target_field": "event.outcome", + "ignore_missing": true + } + }, + { + "rename": { + "field": "suricata.eve.alert.severity", + "target_field": "event.severity", + "ignore_missing": true + } + }, + { + "rename": { + "field": "suricata.eve.flow.pkts_toclient", + "target_field": "destination.packets", + "ignore_missing": true + } + }, + { + "rename": { + "field": "suricata.eve.flow.pkts_toserver", + "target_field": "source.packets", + "ignore_missing": true + } + }, + { + "rename": { + "field": "suricata.eve.flow.bytes_toclient", + "target_field": "destination.bytes", + "ignore_missing": true + } + }, + { + "rename": { + "field": "suricata.eve.flow.bytes_toserver", + "target_field": "source.bytes", + "ignore_missing": true + } + }, + { + "script": { + "lang": "painless", + "source": "long getOrZero(def map, def key) { if(map!=null && map[key]!=null) { return map[key]; } return 0; } def network=ctx['network'], source=ctx['source'], dest=ctx['destination']; def sp=getOrZero(source,'packets'), sb=getOrZero(source,'bytes'), dp=getOrZero(dest,'packets'), db=getOrZero(dest,'bytes'); if(sb+db+sp+dp > 0){if (network==null){network=new HashMap(); ctx['network']=network; } if(sb+db>0) network['bytes'] = sb+db; if(sp+dp>0) network['packets'] = sp+dp; }" + } + }, + { + "date": { + "field": "suricata.eve.flow.start", + "target_field": "event.start", + "formats": [ + "ISO8601" + ], + "ignore_failure": true + } + }, + { + "set": { + "field": "event.end", + "value": "{{@timestamp}}" + } + }, + { + "script": { + "lang": "painless", + "source": "Instant ins(def d){try{return Instant.parse(d);}catch(Exception e){return null;}}def ev=ctx['event'];if(ev!=null){def start=ins(ev['start']); def end=ins(ev['end']); if(start!=null && end!=null && !start.isAfter(end)) {ev['duration'] = Duration.between(start,end).toNanos();}}" + } + }, + { + "lowercase": { + "field": "suricata.eve.proto", + "target_field": "network.transport", + "ignore_missing": true + } + }, + { + "lowercase": { + "field": "suricata.eve.app_proto", + "target_field": "network.protocol", + "ignore_missing": true + } + }, + { + "user_agent": { + "field": "suricata.eve.http.http_user_agent", + "ignore_missing": true + } + }, + { + "rename": { + "field": "suricata.eve.http.http_user_agent", + "target_field": "user_agent.original", + "ignore_missing": true + } + }, + { + "rename": { + "field": "user_agent.device", + "target_field": "user_agent.device.name", + "ignore_missing": true + } + }, + { + "rename": { + "field": "user_agent.os", + "target_field": "user_agent.temp_os", + "ignore_missing": true + } + }, + { + "rename": { + "field": "user_agent.temp_os", + "target_field": "user_agent.os.full_name", + "ignore_missing": true + } + }, + { + "rename": { + "field": "user_agent.os_name", + "target_field": "user_agent.os.name", + "ignore_missing": true + } + }, + { + "rename": { + "field": "user_agent.os_version", + "target_field": "user_agent.os.version", + "ignore_missing": true + } + }, + { + "rename": { + "field": "user_agent.os_major", + "target_field": "user_agent.os.major", + "ignore_missing": true + } + }, + { + "rename": { + "field": "user_agent.os_minor", + "target_field": "user_agent.os.minor", + "ignore_missing": true + } + }, + { + "geoip": { + "field": "source.ip", + "target_field": "source.geo", + "ignore_missing": true + } + }, + { + "geoip": { + "field": "destination.ip", + "target_field": "destination.geo", + "ignore_missing": true + } + }, + { + "remove": { + "field": [ + "suricata.eve.app_proto", + "suricata.eve.event_type", + "suricata.eve.flow.end", + "suricata.eve.flow.start", + "suricata.eve.http.http_method", + "suricata.eve.proto", + "suricata.eve.timestamp" + ], + "ignore_missing": true + } } - } - } - , {"convert": - {"field": "suricata.eve.http.hostname" - ,"target_field": "destination.domain" - ,"type": "string" - ,"ignore_missing": true - } - } - , {"convert": - {"field": "suricata.eve.http.http_refer" - ,"target_field": "http.request.referrer" - ,"type": "string" - ,"ignore_missing": true - } - } - , {"convert": - {"field": "suricata.eve.http.length" - ,"target_field": "http.response.body.bytes" - ,"type": "integer" - ,"ignore_missing": true - } - } - , {"convert": - {"field": "suricata.eve.fileinfo.filename" - ,"target_field": "file.path" - ,"type": "string" - ,"ignore_missing": true - } - } - , {"convert": - {"field": "suricata.eve.fileinfo.size" - ,"target_field": "file.size" - ,"type": "integer" - ,"ignore_missing": true - } - } - , {"convert": - {"field": "suricata.eve.http.http_user_agent" - ,"target_field": "user_agent.original" - ,"type": "string" - ,"ignore_missing": true - } - } - - , { "date": - { "field": "suricata.eve.timestamp" - , "target_field": "@timestamp" - , "formats": ["ISO8601"] - } - } - , {"remove": - {"field": "suricata.eve.timestamp" - } - } - , { "lowercase": - { "field": "suricata.eve.event_type" - , "target_field": "event.type" - , "ignore_missing": true - } - } - , {"convert": - {"field": "suricata.eve.alert.category" - ,"target_field": "message" - ,"type": "string" - ,"ignore_missing": true - } - } - , {"convert": - {"field": "suricata.eve.alert.action" - ,"target_field": "event.outcome" - ,"type": "string" - ,"ignore_missing": true - } - } - , {"convert": - {"field": "suricata.eve.alert.severity" - ,"target_field": "event.severity" - ,"type": "integer" - ,"ignore_missing": true - } - } - , {"convert": - {"field": "suricata.eve.flow.pkts_toclient" - ,"target_field": "destination.packets" - ,"type": "integer" - ,"ignore_missing": true - } - } - , {"convert": - {"field": "suricata.eve.flow.pkts_toserver" - ,"target_field": "source.packets" - ,"type": "integer" - ,"ignore_missing": true - } - } - , {"convert": - {"field": "suricata.eve.flow.bytes_toclient" - ,"target_field": "destination.bytes" - ,"type": "integer" - ,"ignore_missing": true - } - } - , {"convert": - {"field": "suricata.eve.flow.bytes_toserver" - ,"target_field": "source.bytes" - ,"type": "integer" - ,"ignore_missing": true - } - } - , { "script": - { "lang": "painless" - , "source": "long getOrZero(def map, def key) { if(map!=null && map[key]!=null) { return map[key]; } return 0; } def network=ctx['network'], source=ctx['source'], dest=ctx['destination']; def sp=getOrZero(source,'packets'), sb=getOrZero(source,'bytes'), dp=getOrZero(dest,'packets'), db=getOrZero(dest,'bytes'); if(sb+db+sp+dp > 0){if (network==null){network=new HashMap(); ctx['network']=network; } if(sb+db>0) network['bytes'] = sb+db; if(sp+dp>0) network['packets'] = sp+dp; }" - } - } - , {"date": - {"field": "suricata.eve.flow.start" - ,"target_field": "event.start" - ,"formats": ["ISO8601"] - ,"ignore_failure": true - } - } - , {"remove": - {"field": "suricata.eve.flow.start" - ,"ignore_missing": true - } - } - , {"remove": - {"field": "suricata.eve.flow.end" - ,"ignore_missing": true - } - } - , {"set": - {"field": "event.end" - ,"value": "{{@timestamp}}" - } - } - , { "script": - { "lang": "painless" - , "source": "Instant ins(def d){try{return Instant.parse(d);}catch(Exception e){return null;}}def ev=ctx['event'];if(ev!=null){def start=ins(ev['start']); def end=ins(ev['end']); if(start!=null && end!=null && !start.isAfter(end)) {ev['duration'] = Duration.between(start,end).toNanos();}}" - } - } - , { "lowercase": - { "field": "suricata.eve.proto" - , "target_field": "network.transport" - , "ignore_missing": true - } - } - , { "lowercase": - { "field": "suricata.eve.app_proto" - , "target_field": "network.protocol" - , "ignore_missing": true - } - } - , { "user_agent": - { "field": "user_agent.original" - , "target_field": "user_agent" - , "ignore_missing": true - } - } - , { "rename": - { "field": "user_agent.os" - , "target_field": "user_agent.temp_os" - , "ignore_missing": true - } - } - , { "rename": - { "field": "user_agent.temp_os" - , "target_field": "user_agent.os.full_name" - , "ignore_missing": true - } - } - , { "rename": - { "field": "user_agent.os_name" - , "target_field": "user_agent.os.name" - , "ignore_missing": true - } - } - , { "rename": - { "field": "user_agent.os_version" - , "target_field": "user_agent.os.version" - , "ignore_missing": true - } - } - , { "rename": - { "field": "user_agent.os_major" - , "target_field": "user_agent.os.major" - , "ignore_missing": true - } - } - , { "rename": - { "field": "user_agent.os_minor" - , "target_field": "user_agent.os.minor" - , "ignore_missing": true - } - } - - , { "geoip": - { "field": "source.ip" - , "target_field": "source.geo" - , "ignore_missing": true - } - } - , { "geoip": - { "field": "destination.ip" - , "target_field": "destination.geo" - , "ignore_missing": true - } - } - - ] -, "on_failure": - [ { "set" : - { "field" : "error.message" - , "value" : "{{ _ingest.on_failure_message }}" - } - } - ] + ], + "on_failure": [ + { + "set": { + "field": "error.message", + "value": "{{ _ingest.on_failure_message }}" + } + } + ] } diff --git a/x-pack/filebeat/module/suricata/eve/test/eve-alerts.log-expected.json b/x-pack/filebeat/module/suricata/eve/test/eve-alerts.log-expected.json index 46371b017d5..15317f5ad39 100644 --- a/x-pack/filebeat/module/suricata/eve/test/eve-alerts.log-expected.json +++ b/x-pack/filebeat/module/suricata/eve/test/eve-alerts.log-expected.json @@ -25,7 +25,7 @@ "fileset.name": "eve", "http.request.method": "get", "http.response.body.bytes": 1121, - "http.response.status_code": "200", + "http.response.status_code": 200, "input.type": "log", "log.offset": 0, "message": "Attempted Information Leak", @@ -38,44 +38,27 @@ "source.ip": "192.168.1.146", "source.packets": 4, "source.port": 32858, - "suricata.eve.alert.action": "allowed", "suricata.eve.alert.category": "Attempted Information Leak", "suricata.eve.alert.gid": 1, "suricata.eve.alert.rev": 4, - "suricata.eve.alert.severity": 2, "suricata.eve.alert.signature": "ET POLICY curl User-Agent Outbound", "suricata.eve.alert.signature_id": 2013028, - "suricata.eve.app_proto": "http", - "suricata.eve.dest_ip": "93.184.216.34", - "suricata.eve.dest_port": 80, - "suricata.eve.event_type": "alert", - "suricata.eve.flow.bytes_toclient": 1654, - "suricata.eve.flow.bytes_toserver": 347, - "suricata.eve.flow.pkts_toclient": 3, - "suricata.eve.flow.pkts_toserver": 4, "suricata.eve.flow_id": 2191386088856669, - "suricata.eve.http.hostname": "example.net", "suricata.eve.http.http_content_type": "text/html", - "suricata.eve.http.http_method": "GET", - "suricata.eve.http.http_user_agent": "curl/7.58.0", - "suricata.eve.http.length": 1121, "suricata.eve.http.protocol": "HTTP/1.1", - "suricata.eve.http.status": 200, - "suricata.eve.http.url": "/", "suricata.eve.in_iface": "enp0s3", - "suricata.eve.proto": "TCP", - "suricata.eve.src_ip": "192.168.1.146", - "suricata.eve.src_port": 32858, "suricata.eve.tx_id": 0, "tags": [ "suricata" ], "url.domain": "example.net", + "url.original": "/", "url.path": "/", - "user_agent.device": "Other", + "user_agent.device.name": "Other", "user_agent.major": "7", "user_agent.minor": "58", "user_agent.name": "curl", + "user_agent.original": "curl/7.58.0", "user_agent.os.full_name": "Other", "user_agent.os.name": "Other", "user_agent.patch": "0" @@ -106,7 +89,7 @@ "fileset.name": "eve", "http.request.method": "get", "http.response.body.bytes": 1121, - "http.response.status_code": "200", + "http.response.status_code": 200, "input.type": "log", "log.offset": 723, "message": "Attempted Information Leak", @@ -119,44 +102,27 @@ "source.ip": "192.168.1.146", "source.packets": 4, "source.port": 32864, - "suricata.eve.alert.action": "allowed", "suricata.eve.alert.category": "Attempted Information Leak", "suricata.eve.alert.gid": 1, "suricata.eve.alert.rev": 4, - "suricata.eve.alert.severity": 2, "suricata.eve.alert.signature": "ET POLICY curl User-Agent Outbound", "suricata.eve.alert.signature_id": 2013028, - "suricata.eve.app_proto": "http", - "suricata.eve.dest_ip": "93.184.216.34", - "suricata.eve.dest_port": 80, - "suricata.eve.event_type": "alert", - "suricata.eve.flow.bytes_toclient": 1654, - "suricata.eve.flow.bytes_toserver": 347, - "suricata.eve.flow.pkts_toclient": 3, - "suricata.eve.flow.pkts_toserver": 4, "suricata.eve.flow_id": 678269478904081, - "suricata.eve.http.hostname": "example.net", "suricata.eve.http.http_content_type": "text/html", - "suricata.eve.http.http_method": "GET", - "suricata.eve.http.http_user_agent": "curl/7.58.0", - "suricata.eve.http.length": 1121, "suricata.eve.http.protocol": "HTTP/1.1", - "suricata.eve.http.status": 200, - "suricata.eve.http.url": "/", "suricata.eve.in_iface": "enp0s3", - "suricata.eve.proto": "TCP", - "suricata.eve.src_ip": "192.168.1.146", - "suricata.eve.src_port": 32864, "suricata.eve.tx_id": 0, "tags": [ "suricata" ], "url.domain": "example.net", + "url.original": "/", "url.path": "/", - "user_agent.device": "Other", + "user_agent.device.name": "Other", "user_agent.major": "7", "user_agent.minor": "58", "user_agent.name": "curl", + "user_agent.original": "curl/7.58.0", "user_agent.os.full_name": "Other", "user_agent.os.name": "Other", "user_agent.patch": "0" @@ -187,7 +153,7 @@ "fileset.name": "eve", "http.request.method": "get", "http.response.body.bytes": 1126, - "http.response.status_code": "200", + "http.response.status_code": 200, "input.type": "log", "log.offset": 1445, "message": "Attempted Information Leak", @@ -200,44 +166,27 @@ "source.ip": "192.168.1.146", "source.packets": 4, "source.port": 32870, - "suricata.eve.alert.action": "allowed", "suricata.eve.alert.category": "Attempted Information Leak", "suricata.eve.alert.gid": 1, "suricata.eve.alert.rev": 4, - "suricata.eve.alert.severity": 2, "suricata.eve.alert.signature": "ET POLICY curl User-Agent Outbound", "suricata.eve.alert.signature_id": 2013028, - "suricata.eve.app_proto": "http", - "suricata.eve.dest_ip": "93.184.216.34", - "suricata.eve.dest_port": 80, - "suricata.eve.event_type": "alert", - "suricata.eve.flow.bytes_toclient": 1654, - "suricata.eve.flow.bytes_toserver": 347, - "suricata.eve.flow.pkts_toclient": 3, - "suricata.eve.flow.pkts_toserver": 4, "suricata.eve.flow_id": 1170030461115650, - "suricata.eve.http.hostname": "example.net", "suricata.eve.http.http_content_type": "text/html", - "suricata.eve.http.http_method": "GET", - "suricata.eve.http.http_user_agent": "curl/7.58.0", - "suricata.eve.http.length": 1126, "suricata.eve.http.protocol": "HTTP/1.1", - "suricata.eve.http.status": 200, - "suricata.eve.http.url": "/", "suricata.eve.in_iface": "enp0s3", - "suricata.eve.proto": "TCP", - "suricata.eve.src_ip": "192.168.1.146", - "suricata.eve.src_port": 32870, "suricata.eve.tx_id": 0, "tags": [ "suricata" ], "url.domain": "example.net", + "url.original": "/", "url.path": "/", - "user_agent.device": "Other", + "user_agent.device.name": "Other", "user_agent.major": "7", "user_agent.minor": "58", "user_agent.name": "curl", + "user_agent.original": "curl/7.58.0", "user_agent.os.full_name": "Other", "user_agent.os.name": "Other", "user_agent.patch": "0" @@ -268,7 +217,7 @@ "fileset.name": "eve", "http.request.method": "get", "http.response.body.bytes": 1121, - "http.response.status_code": "200", + "http.response.status_code": 200, "input.type": "log", "log.offset": 2168, "message": "Attempted Information Leak", @@ -281,44 +230,27 @@ "source.ip": "192.168.1.146", "source.packets": 4, "source.port": 32872, - "suricata.eve.alert.action": "allowed", "suricata.eve.alert.category": "Attempted Information Leak", "suricata.eve.alert.gid": 1, "suricata.eve.alert.rev": 4, - "suricata.eve.alert.severity": 2, "suricata.eve.alert.signature": "ET POLICY curl User-Agent Outbound", "suricata.eve.alert.signature_id": 2013028, - "suricata.eve.app_proto": "http", - "suricata.eve.dest_ip": "93.184.216.34", - "suricata.eve.dest_port": 80, - "suricata.eve.event_type": "alert", - "suricata.eve.flow.bytes_toclient": 1654, - "suricata.eve.flow.bytes_toserver": 347, - "suricata.eve.flow.pkts_toclient": 3, - "suricata.eve.flow.pkts_toserver": 4, "suricata.eve.flow_id": 49628113637132, - "suricata.eve.http.hostname": "example.org", "suricata.eve.http.http_content_type": "text/html", - "suricata.eve.http.http_method": "GET", - "suricata.eve.http.http_user_agent": "curl/7.58.0", - "suricata.eve.http.length": 1121, "suricata.eve.http.protocol": "HTTP/1.1", - "suricata.eve.http.status": 200, - "suricata.eve.http.url": "/", "suricata.eve.in_iface": "enp0s3", - "suricata.eve.proto": "TCP", - "suricata.eve.src_ip": "192.168.1.146", - "suricata.eve.src_port": 32872, "suricata.eve.tx_id": 0, "tags": [ "suricata" ], "url.domain": "example.org", + "url.original": "/", "url.path": "/", - "user_agent.device": "Other", + "user_agent.device.name": "Other", "user_agent.major": "7", "user_agent.minor": "58", "user_agent.name": "curl", + "user_agent.original": "curl/7.58.0", "user_agent.os.full_name": "Other", "user_agent.os.name": "Other", "user_agent.patch": "0" @@ -349,7 +281,7 @@ "fileset.name": "eve", "http.request.method": "get", "http.response.body.bytes": 1121, - "http.response.status_code": "200", + "http.response.status_code": 200, "input.type": "log", "log.offset": 2889, "message": "Attempted Information Leak", @@ -362,44 +294,27 @@ "source.ip": "192.168.1.146", "source.packets": 4, "source.port": 32876, - "suricata.eve.alert.action": "allowed", "suricata.eve.alert.category": "Attempted Information Leak", "suricata.eve.alert.gid": 1, "suricata.eve.alert.rev": 4, - "suricata.eve.alert.severity": 2, "suricata.eve.alert.signature": "ET POLICY curl User-Agent Outbound", "suricata.eve.alert.signature_id": 2013028, - "suricata.eve.app_proto": "http", - "suricata.eve.dest_ip": "93.184.216.34", - "suricata.eve.dest_port": 80, - "suricata.eve.event_type": "alert", - "suricata.eve.flow.bytes_toclient": 1654, - "suricata.eve.flow.bytes_toserver": 347, - "suricata.eve.flow.pkts_toclient": 3, - "suricata.eve.flow.pkts_toserver": 4, "suricata.eve.flow_id": 116307482565223, - "suricata.eve.http.hostname": "example.org", "suricata.eve.http.http_content_type": "text/html", - "suricata.eve.http.http_method": "GET", - "suricata.eve.http.http_user_agent": "curl/7.58.0", - "suricata.eve.http.length": 1121, "suricata.eve.http.protocol": "HTTP/1.1", - "suricata.eve.http.status": 200, - "suricata.eve.http.url": "/", "suricata.eve.in_iface": "enp0s3", - "suricata.eve.proto": "TCP", - "suricata.eve.src_ip": "192.168.1.146", - "suricata.eve.src_port": 32876, "suricata.eve.tx_id": 0, "tags": [ "suricata" ], "url.domain": "example.org", + "url.original": "/", "url.path": "/", - "user_agent.device": "Other", + "user_agent.device.name": "Other", "user_agent.major": "7", "user_agent.minor": "58", "user_agent.name": "curl", + "user_agent.original": "curl/7.58.0", "user_agent.os.full_name": "Other", "user_agent.os.name": "Other", "user_agent.patch": "0" @@ -430,7 +345,7 @@ "fileset.name": "eve", "http.request.method": "get", "http.response.body.bytes": 1126, - "http.response.status_code": "200", + "http.response.status_code": 200, "input.type": "log", "log.offset": 3611, "message": "Attempted Information Leak", @@ -443,44 +358,27 @@ "source.ip": "192.168.1.146", "source.packets": 4, "source.port": 32892, - "suricata.eve.alert.action": "allowed", "suricata.eve.alert.category": "Attempted Information Leak", "suricata.eve.alert.gid": 1, "suricata.eve.alert.rev": 4, - "suricata.eve.alert.severity": 2, "suricata.eve.alert.signature": "ET POLICY curl User-Agent Outbound", "suricata.eve.alert.signature_id": 2013028, - "suricata.eve.app_proto": "http", - "suricata.eve.dest_ip": "93.184.216.34", - "suricata.eve.dest_port": 80, - "suricata.eve.event_type": "alert", - "suricata.eve.flow.bytes_toclient": 1654, - "suricata.eve.flow.bytes_toserver": 347, - "suricata.eve.flow.pkts_toclient": 3, - "suricata.eve.flow.pkts_toserver": 4, "suricata.eve.flow_id": 1205867738178946, - "suricata.eve.http.hostname": "example.org", "suricata.eve.http.http_content_type": "text/html", - "suricata.eve.http.http_method": "GET", - "suricata.eve.http.http_user_agent": "curl/7.58.0", - "suricata.eve.http.length": 1126, "suricata.eve.http.protocol": "HTTP/1.1", - "suricata.eve.http.status": 200, - "suricata.eve.http.url": "/", "suricata.eve.in_iface": "enp0s3", - "suricata.eve.proto": "TCP", - "suricata.eve.src_ip": "192.168.1.146", - "suricata.eve.src_port": 32892, "suricata.eve.tx_id": 0, "tags": [ "suricata" ], "url.domain": "example.org", + "url.original": "/", "url.path": "/", - "user_agent.device": "Other", + "user_agent.device.name": "Other", "user_agent.major": "7", "user_agent.minor": "58", "user_agent.name": "curl", + "user_agent.original": "curl/7.58.0", "user_agent.os.full_name": "Other", "user_agent.os.name": "Other", "user_agent.patch": "0" @@ -511,7 +409,7 @@ "fileset.name": "eve", "http.request.method": "get", "http.response.body.bytes": 1138, - "http.response.status_code": "200", + "http.response.status_code": 200, "input.type": "log", "log.offset": 4334, "message": "Not Suspicious Traffic", @@ -524,43 +422,26 @@ "source.ip": "192.168.1.146", "source.packets": 4, "source.port": 37742, - "suricata.eve.alert.action": "allowed", "suricata.eve.alert.category": "Not Suspicious Traffic", "suricata.eve.alert.gid": 1, "suricata.eve.alert.rev": 5, - "suricata.eve.alert.severity": 3, "suricata.eve.alert.signature": "ET POLICY GNU/Linux APT User-Agent Outbound likely related to package management", "suricata.eve.alert.signature_id": 2013504, - "suricata.eve.app_proto": "http", - "suricata.eve.dest_ip": "91.189.88.152", - "suricata.eve.dest_port": 80, - "suricata.eve.event_type": "alert", - "suricata.eve.flow.bytes_toclient": 1654, - "suricata.eve.flow.bytes_toserver": 497, - "suricata.eve.flow.pkts_toclient": 3, - "suricata.eve.flow.pkts_toserver": 4, "suricata.eve.flow_id": 764842923400056, - "suricata.eve.http.hostname": "security.ubuntu.com", - "suricata.eve.http.http_method": "GET", - "suricata.eve.http.http_user_agent": "Debian APT-HTTP/1.3 (1.6.3ubuntu0.1)", - "suricata.eve.http.length": 1138, "suricata.eve.http.protocol": "HTTP/1.1", - "suricata.eve.http.status": 200, - "suricata.eve.http.url": "/ubuntu/dists/bionic-security/InRelease", "suricata.eve.in_iface": "enp0s3", - "suricata.eve.proto": "TCP", - "suricata.eve.src_ip": "192.168.1.146", - "suricata.eve.src_port": 37742, "suricata.eve.tx_id": 0, "tags": [ "suricata" ], "url.domain": "security.ubuntu.com", + "url.original": "/ubuntu/dists/bionic-security/InRelease", "url.path": "/ubuntu/dists/bionic-security/InRelease", - "user_agent.device": "Other", + "user_agent.device.name": "Other", "user_agent.major": "1", "user_agent.minor": "3", "user_agent.name": "Debian APT-HTTP", + "user_agent.original": "Debian APT-HTTP/1.3 (1.6.3ubuntu0.1)", "user_agent.os.full_name": "Debian", "user_agent.os.name": "Debian" }, @@ -590,7 +471,7 @@ "fileset.name": "eve", "http.request.method": "get", "http.response.body.bytes": 0, - "http.response.status_code": "304", + "http.response.status_code": 304, "input.type": "log", "log.offset": 5140, "message": "Not Suspicious Traffic", @@ -603,43 +484,26 @@ "source.ip": "192.168.1.146", "source.packets": 4, "source.port": 52340, - "suricata.eve.alert.action": "allowed", "suricata.eve.alert.category": "Not Suspicious Traffic", "suricata.eve.alert.gid": 1, "suricata.eve.alert.rev": 5, - "suricata.eve.alert.severity": 3, "suricata.eve.alert.signature": "ET POLICY GNU/Linux APT User-Agent Outbound likely related to package management", "suricata.eve.alert.signature_id": 2013504, - "suricata.eve.app_proto": "http", - "suricata.eve.dest_ip": "91.189.91.23", - "suricata.eve.dest_port": 80, - "suricata.eve.event_type": "alert", - "suricata.eve.flow.bytes_toclient": 417, - "suricata.eve.flow.bytes_toserver": 487, - "suricata.eve.flow.pkts_toclient": 3, - "suricata.eve.flow.pkts_toserver": 4, "suricata.eve.flow_id": 112424506237238, - "suricata.eve.http.hostname": "archive.ubuntu.com", - "suricata.eve.http.http_method": "GET", - "suricata.eve.http.http_user_agent": "Debian APT-HTTP/1.3 (1.6.3ubuntu0.1)", - "suricata.eve.http.length": 0, "suricata.eve.http.protocol": "HTTP/1.1", - "suricata.eve.http.status": 304, - "suricata.eve.http.url": "/ubuntu/dists/bionic/InRelease", "suricata.eve.in_iface": "enp0s3", - "suricata.eve.proto": "TCP", - "suricata.eve.src_ip": "192.168.1.146", - "suricata.eve.src_port": 52340, "suricata.eve.tx_id": 0, "tags": [ "suricata" ], "url.domain": "archive.ubuntu.com", + "url.original": "/ubuntu/dists/bionic/InRelease", "url.path": "/ubuntu/dists/bionic/InRelease", - "user_agent.device": "Other", + "user_agent.device.name": "Other", "user_agent.major": "1", "user_agent.minor": "3", "user_agent.name": "Debian APT-HTTP", + "user_agent.original": "Debian APT-HTTP/1.3 (1.6.3ubuntu0.1)", "user_agent.os.full_name": "Debian", "user_agent.os.name": "Debian" }, @@ -669,7 +533,7 @@ "fileset.name": "eve", "http.request.method": "get", "http.response.body.bytes": 2601, - "http.response.status_code": "200", + "http.response.status_code": 200, "input.type": "log", "log.offset": 5931, "message": "Not Suspicious Traffic", @@ -682,43 +546,26 @@ "source.ip": "192.168.1.146", "source.packets": 6, "source.port": 52340, - "suricata.eve.alert.action": "allowed", "suricata.eve.alert.category": "Not Suspicious Traffic", "suricata.eve.alert.gid": 1, "suricata.eve.alert.rev": 5, - "suricata.eve.alert.severity": 3, "suricata.eve.alert.signature": "ET POLICY GNU/Linux APT User-Agent Outbound likely related to package management", "suricata.eve.alert.signature_id": 2013504, - "suricata.eve.app_proto": "http", - "suricata.eve.dest_ip": "91.189.91.23", - "suricata.eve.dest_port": 80, - "suricata.eve.event_type": "alert", - "suricata.eve.flow.bytes_toclient": 3445, - "suricata.eve.flow.bytes_toserver": 842, - "suricata.eve.flow.pkts_toclient": 5, - "suricata.eve.flow.pkts_toserver": 6, "suricata.eve.flow_id": 112424506237238, - "suricata.eve.http.hostname": "archive.ubuntu.com", - "suricata.eve.http.http_method": "GET", - "suricata.eve.http.http_user_agent": "Debian APT-HTTP/1.3 (1.6.3ubuntu0.1)", - "suricata.eve.http.length": 2601, "suricata.eve.http.protocol": "HTTP/1.1", - "suricata.eve.http.status": 200, - "suricata.eve.http.url": "/ubuntu/dists/bionic-updates/InRelease", "suricata.eve.in_iface": "enp0s3", - "suricata.eve.proto": "TCP", - "suricata.eve.src_ip": "192.168.1.146", - "suricata.eve.src_port": 52340, "suricata.eve.tx_id": 1, "tags": [ "suricata" ], "url.domain": "archive.ubuntu.com", + "url.original": "/ubuntu/dists/bionic-updates/InRelease", "url.path": "/ubuntu/dists/bionic-updates/InRelease", - "user_agent.device": "Other", + "user_agent.device.name": "Other", "user_agent.major": "1", "user_agent.minor": "3", "user_agent.name": "Debian APT-HTTP", + "user_agent.original": "Debian APT-HTTP/1.3 (1.6.3ubuntu0.1)", "user_agent.os.full_name": "Debian", "user_agent.os.name": "Debian" }, @@ -748,7 +595,7 @@ "fileset.name": "eve", "http.request.method": "get", "http.response.body.bytes": 1241, - "http.response.status_code": "200", + "http.response.status_code": 200, "input.type": "log", "log.offset": 6734, "message": "Not Suspicious Traffic", @@ -761,43 +608,26 @@ "source.ip": "192.168.1.146", "source.packets": 64, "source.port": 37742, - "suricata.eve.alert.action": "allowed", "suricata.eve.alert.category": "Not Suspicious Traffic", "suricata.eve.alert.gid": 1, "suricata.eve.alert.rev": 5, - "suricata.eve.alert.severity": 3, "suricata.eve.alert.signature": "ET POLICY GNU/Linux APT User-Agent Outbound likely related to package management", "suricata.eve.alert.signature_id": 2013504, - "suricata.eve.app_proto": "http", - "suricata.eve.dest_ip": "91.189.88.152", - "suricata.eve.dest_port": 80, - "suricata.eve.event_type": "alert", - "suricata.eve.flow.bytes_toclient": 90543, - "suricata.eve.flow.bytes_toserver": 4810, - "suricata.eve.flow.pkts_toclient": 62, - "suricata.eve.flow.pkts_toserver": 64, "suricata.eve.flow_id": 764842923400056, - "suricata.eve.http.hostname": "security.ubuntu.com", - "suricata.eve.http.http_method": "GET", - "suricata.eve.http.http_user_agent": "Debian APT-HTTP/1.3 (1.6.3ubuntu0.1)", - "suricata.eve.http.length": 1241, "suricata.eve.http.protocol": "HTTP/1.1", - "suricata.eve.http.status": 200, - "suricata.eve.http.url": "/ubuntu/dists/bionic-security/main/source/by-hash/SHA256/f5ec03d97ca76c98162d9233c8b7c578c52897e2136428277baf2e7b633a8e72", "suricata.eve.in_iface": "enp0s3", - "suricata.eve.proto": "TCP", - "suricata.eve.src_ip": "192.168.1.146", - "suricata.eve.src_port": 37742, "suricata.eve.tx_id": 1, "tags": [ "suricata" ], "url.domain": "security.ubuntu.com", + "url.original": "/ubuntu/dists/bionic-security/main/source/by-hash/SHA256/f5ec03d97ca76c98162d9233c8b7c578c52897e2136428277baf2e7b633a8e72", "url.path": "/ubuntu/dists/bionic-security/main/source/by-hash/SHA256/f5ec03d97ca76c98162d9233c8b7c578c52897e2136428277baf2e7b633a8e72", - "user_agent.device": "Other", + "user_agent.device.name": "Other", "user_agent.major": "1", "user_agent.minor": "3", "user_agent.name": "Debian APT-HTTP", + "user_agent.original": "Debian APT-HTTP/1.3 (1.6.3ubuntu0.1)", "user_agent.os.full_name": "Debian", "user_agent.os.name": "Debian" }, @@ -827,7 +657,7 @@ "fileset.name": "eve", "http.request.method": "get", "http.response.body.bytes": 2687, - "http.response.status_code": "200", + "http.response.status_code": 200, "input.type": "log", "log.offset": 7630, "message": "Not Suspicious Traffic", @@ -840,43 +670,26 @@ "source.ip": "192.168.1.146", "source.packets": 87, "source.port": 37742, - "suricata.eve.alert.action": "allowed", "suricata.eve.alert.category": "Not Suspicious Traffic", "suricata.eve.alert.gid": 1, "suricata.eve.alert.rev": 5, - "suricata.eve.alert.severity": 3, "suricata.eve.alert.signature": "ET POLICY GNU/Linux APT User-Agent Outbound likely related to package management", "suricata.eve.alert.signature_id": 2013504, - "suricata.eve.app_proto": "http", - "suricata.eve.dest_ip": "91.189.88.152", - "suricata.eve.dest_port": 80, - "suricata.eve.event_type": "alert", - "suricata.eve.flow.bytes_toclient": 145014, - "suricata.eve.flow.bytes_toserver": 6591, - "suricata.eve.flow.pkts_toclient": 98, - "suricata.eve.flow.pkts_toserver": 87, "suricata.eve.flow_id": 764842923400056, - "suricata.eve.http.hostname": "security.ubuntu.com", - "suricata.eve.http.http_method": "GET", - "suricata.eve.http.http_user_agent": "Debian APT-HTTP/1.3 (1.6.3ubuntu0.1)", - "suricata.eve.http.length": 2687, "suricata.eve.http.protocol": "HTTP/1.1", - "suricata.eve.http.status": 200, - "suricata.eve.http.url": "/ubuntu/dists/bionic-security/main/binary-amd64/by-hash/SHA256/c5b8346a3221bc9a23a79ba4dc4e730a6319a77fc9d63872dfc56539a0810015", "suricata.eve.in_iface": "enp0s3", - "suricata.eve.proto": "TCP", - "suricata.eve.src_ip": "192.168.1.146", - "suricata.eve.src_port": 37742, "suricata.eve.tx_id": 2, "tags": [ "suricata" ], "url.domain": "security.ubuntu.com", + "url.original": "/ubuntu/dists/bionic-security/main/binary-amd64/by-hash/SHA256/c5b8346a3221bc9a23a79ba4dc4e730a6319a77fc9d63872dfc56539a0810015", "url.path": "/ubuntu/dists/bionic-security/main/binary-amd64/by-hash/SHA256/c5b8346a3221bc9a23a79ba4dc4e730a6319a77fc9d63872dfc56539a0810015", - "user_agent.device": "Other", + "user_agent.device.name": "Other", "user_agent.major": "1", "user_agent.minor": "3", "user_agent.name": "Debian APT-HTTP", + "user_agent.original": "Debian APT-HTTP/1.3 (1.6.3ubuntu0.1)", "user_agent.os.full_name": "Debian", "user_agent.os.name": "Debian" }, @@ -906,7 +719,7 @@ "fileset.name": "eve", "http.request.method": "get", "http.response.body.bytes": 2688, - "http.response.status_code": "200", + "http.response.status_code": 200, "input.type": "log", "log.offset": 8533, "message": "Not Suspicious Traffic", @@ -919,43 +732,26 @@ "source.ip": "192.168.1.146", "source.packets": 156, "source.port": 37742, - "suricata.eve.alert.action": "allowed", "suricata.eve.alert.category": "Not Suspicious Traffic", "suricata.eve.alert.gid": 1, "suricata.eve.alert.rev": 5, - "suricata.eve.alert.severity": 3, "suricata.eve.alert.signature": "ET POLICY GNU/Linux APT User-Agent Outbound likely related to package management", "suricata.eve.alert.signature_id": 2013504, - "suricata.eve.app_proto": "http", - "suricata.eve.dest_ip": "91.189.88.152", - "suricata.eve.dest_port": 80, - "suricata.eve.event_type": "alert", - "suricata.eve.flow.bytes_toclient": 330525, - "suricata.eve.flow.bytes_toserver": 11460, - "suricata.eve.flow.pkts_toclient": 221, - "suricata.eve.flow.pkts_toserver": 156, "suricata.eve.flow_id": 764842923400056, - "suricata.eve.http.hostname": "security.ubuntu.com", - "suricata.eve.http.http_method": "GET", - "suricata.eve.http.http_user_agent": "Debian APT-HTTP/1.3 (1.6.3ubuntu0.1)", - "suricata.eve.http.length": 2688, "suricata.eve.http.protocol": "HTTP/1.1", - "suricata.eve.http.status": 200, - "suricata.eve.http.url": "/ubuntu/dists/bionic-security/universe/binary-amd64/by-hash/SHA256/e5cc957139a25a0fee47cbf2c0fac8ad5cab50346d6a74abe031748924c5b558", "suricata.eve.in_iface": "enp0s3", - "suricata.eve.proto": "TCP", - "suricata.eve.src_ip": "192.168.1.146", - "suricata.eve.src_port": 37742, "suricata.eve.tx_id": 3, "tags": [ "suricata" ], "url.domain": "security.ubuntu.com", + "url.original": "/ubuntu/dists/bionic-security/universe/binary-amd64/by-hash/SHA256/e5cc957139a25a0fee47cbf2c0fac8ad5cab50346d6a74abe031748924c5b558", "url.path": "/ubuntu/dists/bionic-security/universe/binary-amd64/by-hash/SHA256/e5cc957139a25a0fee47cbf2c0fac8ad5cab50346d6a74abe031748924c5b558", - "user_agent.device": "Other", + "user_agent.device.name": "Other", "user_agent.major": "1", "user_agent.minor": "3", "user_agent.name": "Debian APT-HTTP", + "user_agent.original": "Debian APT-HTTP/1.3 (1.6.3ubuntu0.1)", "user_agent.os.full_name": "Debian", "user_agent.os.name": "Debian" }, @@ -985,7 +781,7 @@ "fileset.name": "eve", "http.request.method": "get", "http.response.body.bytes": 2601, - "http.response.status_code": "200", + "http.response.status_code": 200, "input.type": "log", "log.offset": 9443, "message": "Not Suspicious Traffic", @@ -998,43 +794,26 @@ "source.ip": "192.168.1.146", "source.packets": 64, "source.port": 52340, - "suricata.eve.alert.action": "allowed", "suricata.eve.alert.category": "Not Suspicious Traffic", "suricata.eve.alert.gid": 1, "suricata.eve.alert.rev": 5, - "suricata.eve.alert.severity": 3, "suricata.eve.alert.signature": "ET POLICY GNU/Linux APT User-Agent Outbound likely related to package management", "suricata.eve.alert.signature_id": 2013504, - "suricata.eve.app_proto": "http", - "suricata.eve.dest_ip": "91.189.91.23", - "suricata.eve.dest_port": 80, - "suricata.eve.event_type": "alert", - "suricata.eve.flow.bytes_toclient": 96554, - "suricata.eve.flow.bytes_toserver": 4895, - "suricata.eve.flow.pkts_toclient": 67, - "suricata.eve.flow.pkts_toserver": 64, "suricata.eve.flow_id": 112424506237238, - "suricata.eve.http.hostname": "archive.ubuntu.com", - "suricata.eve.http.http_method": "GET", - "suricata.eve.http.http_user_agent": "Debian APT-HTTP/1.3 (1.6.3ubuntu0.1)", - "suricata.eve.http.length": 2601, "suricata.eve.http.protocol": "HTTP/1.1", - "suricata.eve.http.status": 200, - "suricata.eve.http.url": "/ubuntu/dists/bionic-backports/InRelease", "suricata.eve.in_iface": "enp0s3", - "suricata.eve.proto": "TCP", - "suricata.eve.src_ip": "192.168.1.146", - "suricata.eve.src_port": 52340, "suricata.eve.tx_id": 2, "tags": [ "suricata" ], "url.domain": "archive.ubuntu.com", + "url.original": "/ubuntu/dists/bionic-backports/InRelease", "url.path": "/ubuntu/dists/bionic-backports/InRelease", - "user_agent.device": "Other", + "user_agent.device.name": "Other", "user_agent.major": "1", "user_agent.minor": "3", "user_agent.name": "Debian APT-HTTP", + "user_agent.original": "Debian APT-HTTP/1.3 (1.6.3ubuntu0.1)", "user_agent.os.full_name": "Debian", "user_agent.os.name": "Debian" }, @@ -1064,7 +843,7 @@ "fileset.name": "eve", "http.request.method": "get", "http.response.body.bytes": 2687, - "http.response.status_code": "200", + "http.response.status_code": 200, "input.type": "log", "log.offset": 10252, "message": "Not Suspicious Traffic", @@ -1077,43 +856,26 @@ "source.ip": "192.168.1.146", "source.packets": 91, "source.port": 52340, - "suricata.eve.alert.action": "allowed", "suricata.eve.alert.category": "Not Suspicious Traffic", "suricata.eve.alert.gid": 1, "suricata.eve.alert.rev": 5, - "suricata.eve.alert.severity": 3, "suricata.eve.alert.signature": "ET POLICY GNU/Linux APT User-Agent Outbound likely related to package management", "suricata.eve.alert.signature_id": 2013504, - "suricata.eve.app_proto": "http", - "suricata.eve.dest_ip": "91.189.91.23", - "suricata.eve.dest_port": 80, - "suricata.eve.event_type": "alert", - "suricata.eve.flow.bytes_toclient": 174843, - "suricata.eve.flow.bytes_toserver": 6932, - "suricata.eve.flow.pkts_toclient": 119, - "suricata.eve.flow.pkts_toserver": 91, "suricata.eve.flow_id": 112424506237238, - "suricata.eve.http.hostname": "archive.ubuntu.com", - "suricata.eve.http.http_method": "GET", - "suricata.eve.http.http_user_agent": "Debian APT-HTTP/1.3 (1.6.3ubuntu0.1)", - "suricata.eve.http.length": 2687, "suricata.eve.http.protocol": "HTTP/1.1", - "suricata.eve.http.status": 200, - "suricata.eve.http.url": "/ubuntu/dists/bionic-updates/main/source/by-hash/SHA256/65f2e3a4e9d89d9d4b5e3d42e586bc96f48a24466b0ad0b4a707255e44a26b03", "suricata.eve.in_iface": "enp0s3", - "suricata.eve.proto": "TCP", - "suricata.eve.src_ip": "192.168.1.146", - "suricata.eve.src_port": 52340, "suricata.eve.tx_id": 3, "tags": [ "suricata" ], "url.domain": "archive.ubuntu.com", + "url.original": "/ubuntu/dists/bionic-updates/main/source/by-hash/SHA256/65f2e3a4e9d89d9d4b5e3d42e586bc96f48a24466b0ad0b4a707255e44a26b03", "url.path": "/ubuntu/dists/bionic-updates/main/source/by-hash/SHA256/65f2e3a4e9d89d9d4b5e3d42e586bc96f48a24466b0ad0b4a707255e44a26b03", - "user_agent.device": "Other", + "user_agent.device.name": "Other", "user_agent.major": "1", "user_agent.minor": "3", "user_agent.name": "Debian APT-HTTP", + "user_agent.original": "Debian APT-HTTP/1.3 (1.6.3ubuntu0.1)", "user_agent.os.full_name": "Debian", "user_agent.os.name": "Debian" }, @@ -1143,7 +905,7 @@ "fileset.name": "eve", "http.request.method": "get", "http.response.body.bytes": 2688, - "http.response.status_code": "200", + "http.response.status_code": 200, "input.type": "log", "log.offset": 11147, "message": "Not Suspicious Traffic", @@ -1156,43 +918,26 @@ "source.ip": "192.168.1.146", "source.packets": 159, "source.port": 52340, - "suricata.eve.alert.action": "allowed", "suricata.eve.alert.category": "Not Suspicious Traffic", "suricata.eve.alert.gid": 1, "suricata.eve.alert.rev": 5, - "suricata.eve.alert.severity": 3, "suricata.eve.alert.signature": "ET POLICY GNU/Linux APT User-Agent Outbound likely related to package management", "suricata.eve.alert.signature_id": 2013504, - "suricata.eve.app_proto": "http", - "suricata.eve.dest_ip": "91.189.91.23", - "suricata.eve.dest_port": 80, - "suricata.eve.event_type": "alert", - "suricata.eve.flow.bytes_toclient": 376452, - "suricata.eve.flow.bytes_toserver": 11679, - "suricata.eve.flow.pkts_toclient": 253, - "suricata.eve.flow.pkts_toserver": 159, "suricata.eve.flow_id": 112424506237238, - "suricata.eve.http.hostname": "archive.ubuntu.com", - "suricata.eve.http.http_method": "GET", - "suricata.eve.http.http_user_agent": "Debian APT-HTTP/1.3 (1.6.3ubuntu0.1)", - "suricata.eve.http.length": 2688, "suricata.eve.http.protocol": "HTTP/1.1", - "suricata.eve.http.status": 200, - "suricata.eve.http.url": "/ubuntu/dists/bionic-updates/universe/source/by-hash/SHA256/56cfd9cc2efa61dff7428dddf921c3cd6047ab8e6484a7f1888e4c3f7252f1ef", "suricata.eve.in_iface": "enp0s3", - "suricata.eve.proto": "TCP", - "suricata.eve.src_ip": "192.168.1.146", - "suricata.eve.src_port": 52340, "suricata.eve.tx_id": 4, "tags": [ "suricata" ], "url.domain": "archive.ubuntu.com", + "url.original": "/ubuntu/dists/bionic-updates/universe/source/by-hash/SHA256/56cfd9cc2efa61dff7428dddf921c3cd6047ab8e6484a7f1888e4c3f7252f1ef", "url.path": "/ubuntu/dists/bionic-updates/universe/source/by-hash/SHA256/56cfd9cc2efa61dff7428dddf921c3cd6047ab8e6484a7f1888e4c3f7252f1ef", - "user_agent.device": "Other", + "user_agent.device.name": "Other", "user_agent.major": "1", "user_agent.minor": "3", "user_agent.name": "Debian APT-HTTP", + "user_agent.original": "Debian APT-HTTP/1.3 (1.6.3ubuntu0.1)", "user_agent.os.full_name": "Debian", "user_agent.os.name": "Debian" }, @@ -1222,7 +967,7 @@ "fileset.name": "eve", "http.request.method": "get", "http.response.body.bytes": 2687, - "http.response.status_code": "200", + "http.response.status_code": 200, "input.type": "log", "log.offset": 12048, "message": "Not Suspicious Traffic", @@ -1235,43 +980,26 @@ "source.ip": "192.168.1.146", "source.packets": 190, "source.port": 52340, - "suricata.eve.alert.action": "allowed", "suricata.eve.alert.category": "Not Suspicious Traffic", "suricata.eve.alert.gid": 1, "suricata.eve.alert.rev": 5, - "suricata.eve.alert.severity": 3, "suricata.eve.alert.signature": "ET POLICY GNU/Linux APT User-Agent Outbound likely related to package management", "suricata.eve.alert.signature_id": 2013504, - "suricata.eve.app_proto": "http", - "suricata.eve.dest_ip": "91.189.91.23", - "suricata.eve.dest_port": 80, - "suricata.eve.event_type": "alert", - "suricata.eve.flow.bytes_toclient": 468170, - "suricata.eve.flow.bytes_toserver": 13986, - "suricata.eve.flow.pkts_toclient": 314, - "suricata.eve.flow.pkts_toserver": 190, "suricata.eve.flow_id": 112424506237238, - "suricata.eve.http.hostname": "archive.ubuntu.com", - "suricata.eve.http.http_method": "GET", - "suricata.eve.http.http_user_agent": "Debian APT-HTTP/1.3 (1.6.3ubuntu0.1)", - "suricata.eve.http.length": 2687, "suricata.eve.http.protocol": "HTTP/1.1", - "suricata.eve.http.status": 200, - "suricata.eve.http.url": "/ubuntu/dists/bionic-updates/main/binary-amd64/by-hash/SHA256/4360137dc8f98b47648da1fef5472ef234fb02115bc2b29873bcaeee62637e70", "suricata.eve.in_iface": "enp0s3", - "suricata.eve.proto": "TCP", - "suricata.eve.src_ip": "192.168.1.146", - "suricata.eve.src_port": 52340, "suricata.eve.tx_id": 5, "tags": [ "suricata" ], "url.domain": "archive.ubuntu.com", + "url.original": "/ubuntu/dists/bionic-updates/main/binary-amd64/by-hash/SHA256/4360137dc8f98b47648da1fef5472ef234fb02115bc2b29873bcaeee62637e70", "url.path": "/ubuntu/dists/bionic-updates/main/binary-amd64/by-hash/SHA256/4360137dc8f98b47648da1fef5472ef234fb02115bc2b29873bcaeee62637e70", - "user_agent.device": "Other", + "user_agent.device.name": "Other", "user_agent.major": "1", "user_agent.minor": "3", "user_agent.name": "Debian APT-HTTP", + "user_agent.original": "Debian APT-HTTP/1.3 (1.6.3ubuntu0.1)", "user_agent.os.full_name": "Debian", "user_agent.os.name": "Debian" }, @@ -1301,7 +1029,7 @@ "fileset.name": "eve", "http.request.method": "get", "http.response.body.bytes": 2691, - "http.response.status_code": "200", + "http.response.status_code": 200, "input.type": "log", "log.offset": 12951, "message": "Not Suspicious Traffic", @@ -1314,43 +1042,26 @@ "source.ip": "192.168.1.146", "source.packets": 328, "source.port": 52340, - "suricata.eve.alert.action": "allowed", "suricata.eve.alert.category": "Not Suspicious Traffic", "suricata.eve.alert.gid": 1, "suricata.eve.alert.rev": 5, - "suricata.eve.alert.severity": 3, "suricata.eve.alert.signature": "ET POLICY GNU/Linux APT User-Agent Outbound likely related to package management", "suricata.eve.alert.signature_id": 2013504, - "suricata.eve.app_proto": "http", - "suricata.eve.dest_ip": "91.189.91.23", - "suricata.eve.dest_port": 80, - "suricata.eve.event_type": "alert", - "suricata.eve.flow.bytes_toclient": 880323, - "suricata.eve.flow.bytes_toserver": 23361, - "suricata.eve.flow.pkts_toclient": 588, - "suricata.eve.flow.pkts_toserver": 328, "suricata.eve.flow_id": 112424506237238, - "suricata.eve.http.hostname": "archive.ubuntu.com", - "suricata.eve.http.http_method": "GET", - "suricata.eve.http.http_user_agent": "Debian APT-HTTP/1.3 (1.6.3ubuntu0.1)", - "suricata.eve.http.length": 2691, "suricata.eve.http.protocol": "HTTP/1.1", - "suricata.eve.http.status": 200, - "suricata.eve.http.url": "/ubuntu/dists/bionic-updates/restricted/binary-amd64/by-hash/SHA256/c93fdc7f10cad1263349fd7b5bdd6a7f7163165b96ad263b3e12022e319d0d12", "suricata.eve.in_iface": "enp0s3", - "suricata.eve.proto": "TCP", - "suricata.eve.src_ip": "192.168.1.146", - "suricata.eve.src_port": 52340, "suricata.eve.tx_id": 6, "tags": [ "suricata" ], "url.domain": "archive.ubuntu.com", + "url.original": "/ubuntu/dists/bionic-updates/restricted/binary-amd64/by-hash/SHA256/c93fdc7f10cad1263349fd7b5bdd6a7f7163165b96ad263b3e12022e319d0d12", "url.path": "/ubuntu/dists/bionic-updates/restricted/binary-amd64/by-hash/SHA256/c93fdc7f10cad1263349fd7b5bdd6a7f7163165b96ad263b3e12022e319d0d12", - "user_agent.device": "Other", + "user_agent.device.name": "Other", "user_agent.major": "1", "user_agent.minor": "3", "user_agent.name": "Debian APT-HTTP", + "user_agent.original": "Debian APT-HTTP/1.3 (1.6.3ubuntu0.1)", "user_agent.os.full_name": "Debian", "user_agent.os.name": "Debian" }, @@ -1380,7 +1091,7 @@ "fileset.name": "eve", "http.request.method": "get", "http.response.body.bytes": 2687, - "http.response.status_code": "200", + "http.response.status_code": 200, "input.type": "log", "log.offset": 13860, "message": "Not Suspicious Traffic", @@ -1393,43 +1104,26 @@ "source.ip": "192.168.1.146", "source.packets": 330, "source.port": 52340, - "suricata.eve.alert.action": "allowed", "suricata.eve.alert.category": "Not Suspicious Traffic", "suricata.eve.alert.gid": 1, "suricata.eve.alert.rev": 5, - "suricata.eve.alert.severity": 3, "suricata.eve.alert.signature": "ET POLICY GNU/Linux APT User-Agent Outbound likely related to package management", "suricata.eve.alert.signature_id": 2013504, - "suricata.eve.app_proto": "http", - "suricata.eve.dest_ip": "91.189.91.23", - "suricata.eve.dest_port": 80, - "suricata.eve.event_type": "alert", - "suricata.eve.flow.bytes_toclient": 884342, - "suricata.eve.flow.bytes_toserver": 23758, - "suricata.eve.flow.pkts_toclient": 591, - "suricata.eve.flow.pkts_toserver": 330, "suricata.eve.flow_id": 112424506237238, - "suricata.eve.http.hostname": "archive.ubuntu.com", - "suricata.eve.http.http_method": "GET", - "suricata.eve.http.http_user_agent": "Debian APT-HTTP/1.3 (1.6.3ubuntu0.1)", - "suricata.eve.http.length": 2687, "suricata.eve.http.protocol": "HTTP/1.1", - "suricata.eve.http.status": 200, - "suricata.eve.http.url": "/ubuntu/dists/bionic-updates/universe/binary-amd64/by-hash/SHA256/5190f7afbee38b3cb32225db478fdbabd46f76eaa9c5921a13091891bf3e9bbc", "suricata.eve.in_iface": "enp0s3", - "suricata.eve.proto": "TCP", - "suricata.eve.src_ip": "192.168.1.146", - "suricata.eve.src_port": 52340, "suricata.eve.tx_id": 7, "tags": [ "suricata" ], "url.domain": "archive.ubuntu.com", + "url.original": "/ubuntu/dists/bionic-updates/universe/binary-amd64/by-hash/SHA256/5190f7afbee38b3cb32225db478fdbabd46f76eaa9c5921a13091891bf3e9bbc", "url.path": "/ubuntu/dists/bionic-updates/universe/binary-amd64/by-hash/SHA256/5190f7afbee38b3cb32225db478fdbabd46f76eaa9c5921a13091891bf3e9bbc", - "user_agent.device": "Other", + "user_agent.device.name": "Other", "user_agent.major": "1", "user_agent.minor": "3", "user_agent.name": "Debian APT-HTTP", + "user_agent.original": "Debian APT-HTTP/1.3 (1.6.3ubuntu0.1)", "user_agent.os.full_name": "Debian", "user_agent.os.name": "Debian" }, @@ -1471,42 +1165,26 @@ "source.ip": "192.168.1.146", "source.packets": 524, "source.port": 52340, - "suricata.eve.alert.action": "allowed", "suricata.eve.alert.category": "Not Suspicious Traffic", "suricata.eve.alert.gid": 1, "suricata.eve.alert.rev": 5, - "suricata.eve.alert.severity": 3, "suricata.eve.alert.signature": "ET POLICY GNU/Linux APT User-Agent Outbound likely related to package management", "suricata.eve.alert.signature_id": 2013504, - "suricata.eve.app_proto": "http", - "suricata.eve.dest_ip": "91.189.91.23", - "suricata.eve.dest_port": 80, - "suricata.eve.event_type": "alert", - "suricata.eve.flow.bytes_toclient": 1467603, - "suricata.eve.flow.bytes_toserver": 36819, - "suricata.eve.flow.pkts_toclient": 979, - "suricata.eve.flow.pkts_toserver": 524, "suricata.eve.flow_id": 112424506237238, - "suricata.eve.http.hostname": "archive.ubuntu.com", - "suricata.eve.http.http_method": "GET", - "suricata.eve.http.http_user_agent": "Debian APT-HTTP/1.3 (1.6.3ubuntu0.1)", - "suricata.eve.http.length": 0, "suricata.eve.http.protocol": "HTTP/1.1", - "suricata.eve.http.url": "/ubuntu/dists/bionic-updates/universe/i18n/by-hash/SHA256/9fe539b7036e51327cd85ca5e0a4dd4eb47f69168875de2ac9842a5e36ebd4a4", "suricata.eve.in_iface": "enp0s3", - "suricata.eve.proto": "TCP", - "suricata.eve.src_ip": "192.168.1.146", - "suricata.eve.src_port": 52340, "suricata.eve.tx_id": 8, "tags": [ "suricata" ], "url.domain": "archive.ubuntu.com", + "url.original": "/ubuntu/dists/bionic-updates/universe/i18n/by-hash/SHA256/9fe539b7036e51327cd85ca5e0a4dd4eb47f69168875de2ac9842a5e36ebd4a4", "url.path": "/ubuntu/dists/bionic-updates/universe/i18n/by-hash/SHA256/9fe539b7036e51327cd85ca5e0a4dd4eb47f69168875de2ac9842a5e36ebd4a4", - "user_agent.device": "Other", + "user_agent.device.name": "Other", "user_agent.major": "1", "user_agent.minor": "3", "user_agent.name": "Debian APT-HTTP", + "user_agent.original": "Debian APT-HTTP/1.3 (1.6.3ubuntu0.1)", "user_agent.os.full_name": "Debian", "user_agent.os.name": "Debian" }, @@ -1548,42 +1226,26 @@ "source.ip": "192.168.1.146", "source.packets": 575, "source.port": 52340, - "suricata.eve.alert.action": "allowed", "suricata.eve.alert.category": "Not Suspicious Traffic", "suricata.eve.alert.gid": 1, "suricata.eve.alert.rev": 5, - "suricata.eve.alert.severity": 3, "suricata.eve.alert.signature": "ET POLICY GNU/Linux APT User-Agent Outbound likely related to package management", "suricata.eve.alert.signature_id": 2013504, - "suricata.eve.app_proto": "http", - "suricata.eve.dest_ip": "91.189.91.23", - "suricata.eve.dest_port": 80, - "suricata.eve.event_type": "alert", - "suricata.eve.flow.bytes_toclient": 1618380, - "suricata.eve.flow.bytes_toserver": 40452, - "suricata.eve.flow.pkts_toclient": 1079, - "suricata.eve.flow.pkts_toserver": 575, "suricata.eve.flow_id": 112424506237238, - "suricata.eve.http.hostname": "archive.ubuntu.com", - "suricata.eve.http.http_method": "GET", - "suricata.eve.http.http_user_agent": "Debian APT-HTTP/1.3 (1.6.3ubuntu0.1)", - "suricata.eve.http.length": 0, "suricata.eve.http.protocol": "HTTP/1.1", - "suricata.eve.http.url": "/ubuntu/dists/bionic-updates/multiverse/binary-amd64/by-hash/SHA256/8ab8cb220c0e50521c589acc2bc2b43a3121210f0b035a0605972bcffd73dd16", "suricata.eve.in_iface": "enp0s3", - "suricata.eve.proto": "TCP", - "suricata.eve.src_ip": "192.168.1.146", - "suricata.eve.src_port": 52340, "suricata.eve.tx_id": 9, "tags": [ "suricata" ], "url.domain": "archive.ubuntu.com", + "url.original": "/ubuntu/dists/bionic-updates/multiverse/binary-amd64/by-hash/SHA256/8ab8cb220c0e50521c589acc2bc2b43a3121210f0b035a0605972bcffd73dd16", "url.path": "/ubuntu/dists/bionic-updates/multiverse/binary-amd64/by-hash/SHA256/8ab8cb220c0e50521c589acc2bc2b43a3121210f0b035a0605972bcffd73dd16", - "user_agent.device": "Other", + "user_agent.device.name": "Other", "user_agent.major": "1", "user_agent.minor": "3", "user_agent.name": "Debian APT-HTTP", + "user_agent.original": "Debian APT-HTTP/1.3 (1.6.3ubuntu0.1)", "user_agent.os.full_name": "Debian", "user_agent.os.name": "Debian" } diff --git a/x-pack/filebeat/module/suricata/eve/test/eve-small.log-expected.json b/x-pack/filebeat/module/suricata/eve/test/eve-small.log-expected.json index 5e648f23e62..66c1f46efba 100644 --- a/x-pack/filebeat/module/suricata/eve/test/eve-small.log-expected.json +++ b/x-pack/filebeat/module/suricata/eve/test/eve-small.log-expected.json @@ -15,14 +15,8 @@ "service.type": "suricata", "source.ip": "192.168.86.85", "source.port": 55406, - "suricata.eve.dest_ip": "192.168.253.112", - "suricata.eve.dest_port": 22, - "suricata.eve.event_type": "ssh", "suricata.eve.flow_id": 298824096901438, "suricata.eve.in_iface": "en0", - "suricata.eve.proto": "TCP", - "suricata.eve.src_ip": "192.168.86.85", - "suricata.eve.src_port": 55406, "suricata.eve.ssh.client.proto_version": "2.0", "suricata.eve.ssh.client.software_version": "OpenSSH_7.6", "suricata.eve.ssh.server.proto_version": "2.0", @@ -59,26 +53,13 @@ "source.ip": "192.168.86.85", "source.packets": 4, "source.port": 55641, - "suricata.eve.alert.action": "allowed", "suricata.eve.alert.category": "Potential Corporate Privacy Violation", "suricata.eve.alert.gid": 1, "suricata.eve.alert.rev": 3, - "suricata.eve.alert.severity": 1, "suricata.eve.alert.signature": "ET POLICY Observed IP Lookup Domain (l2 .io in TLS SNI)", "suricata.eve.alert.signature_id": 2024833, - "suricata.eve.app_proto": "tls", - "suricata.eve.dest_ip": "192.168.156.70", - "suricata.eve.dest_port": 443, - "suricata.eve.event_type": "alert", - "suricata.eve.flow.bytes_toclient": 343, - "suricata.eve.flow.bytes_toserver": 793, - "suricata.eve.flow.pkts_toclient": 3, - "suricata.eve.flow.pkts_toserver": 4, "suricata.eve.flow_id": 904992230150281, "suricata.eve.in_iface": "en0", - "suricata.eve.proto": "TCP", - "suricata.eve.src_ip": "192.168.86.85", - "suricata.eve.src_port": 55641, "suricata.eve.tls.session_resumed": true, "suricata.eve.tls.sni": "l2.io", "suricata.eve.tls.version": "TLS 1.2", @@ -100,39 +81,29 @@ "fileset.name": "eve", "http.request.method": "get", "http.response.body.bytes": 1155, - "http.response.status_code": "200", + "http.response.status_code": 200, "input.type": "log", "log.offset": 985, "network.transport": "tcp", "service.type": "suricata", "source.ip": "192.168.86.85", "source.port": 56119, - "suricata.eve.dest_ip": "192.168.86.28", - "suricata.eve.dest_port": 63963, - "suricata.eve.event_type": "http", "suricata.eve.flow_id": 2115002772430095, - "suricata.eve.http.hostname": "192.168.86.28", "suricata.eve.http.http_content_type": "text/xml", - "suricata.eve.http.http_method": "GET", - "suricata.eve.http.http_user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36", - "suricata.eve.http.length": 1155, "suricata.eve.http.protocol": "HTTP/1.1", - "suricata.eve.http.status": 200, - "suricata.eve.http.url": "/dd.xml", "suricata.eve.in_iface": "en0", - "suricata.eve.proto": "TCP", - "suricata.eve.src_ip": "192.168.86.85", - "suricata.eve.src_port": 56119, "suricata.eve.tx_id": 0, "tags": [ "suricata" ], "url.domain": "192.168.86.28", + "url.original": "/dd.xml", "url.path": "/dd.xml", - "user_agent.device": "Other", + "user_agent.device.name": "Other", "user_agent.major": "67", "user_agent.minor": "0", "user_agent.name": "Chrome", + "user_agent.original": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36", "user_agent.os.full_name": "Mac OS X 10.13.5", "user_agent.os.major": "10", "user_agent.os.minor": "13", @@ -154,7 +125,7 @@ "fileset.name": "eve", "http.request.method": "get", "http.response.body.bytes": 1071, - "http.response.status_code": "200", + "http.response.status_code": 200, "input.type": "log", "log.offset": 1507, "network.protocol": "http", @@ -162,41 +133,28 @@ "service.type": "suricata", "source.ip": "192.168.86.28", "source.port": 8008, - "suricata.eve.app_proto": "http", - "suricata.eve.dest_ip": "192.168.86.85", - "suricata.eve.dest_port": 56118, - "suricata.eve.event_type": "fileinfo", - "suricata.eve.fileinfo.filename": "/ssdp/device-desc.xml", "suricata.eve.fileinfo.gaps": false, "suricata.eve.fileinfo.md5": "427b7337ff37eeb24d74f47d8e04cf21", "suricata.eve.fileinfo.sha1": "313573490192c685e9e53abef25453ed0d5e2aee", "suricata.eve.fileinfo.sha256": "f610428ebddf6f8cf9e39322e672583c45fcdcf885efad0ab48fd53a3dfc2c4b", - "suricata.eve.fileinfo.size": 1071, "suricata.eve.fileinfo.state": "CLOSED", "suricata.eve.fileinfo.stored": false, "suricata.eve.fileinfo.tx_id": 0, "suricata.eve.flow_id": 2211411903323127, - "suricata.eve.http.hostname": "192.168.86.28", "suricata.eve.http.http_content_type": "application/xml", - "suricata.eve.http.http_method": "GET", - "suricata.eve.http.http_user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36", - "suricata.eve.http.length": 1071, "suricata.eve.http.protocol": "HTTP/1.1", - "suricata.eve.http.status": 200, - "suricata.eve.http.url": "/ssdp/device-desc.xml", "suricata.eve.in_iface": "en0", - "suricata.eve.proto": "TCP", - "suricata.eve.src_ip": "192.168.86.28", - "suricata.eve.src_port": 8008, "tags": [ "suricata" ], "url.domain": "192.168.86.28", + "url.original": "/ssdp/device-desc.xml", "url.path": "/ssdp/device-desc.xml", - "user_agent.device": "Other", + "user_agent.device.name": "Other", "user_agent.major": "67", "user_agent.minor": "0", "user_agent.name": "Chrome", + "user_agent.original": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36", "user_agent.os.full_name": "Mac OS X 10.13.5", "user_agent.os.major": "10", "user_agent.os.minor": "13", @@ -219,8 +177,6 @@ "service.type": "suricata", "source.ip": "192.168.86.1", "source.port": 53, - "suricata.eve.dest_ip": "192.168.86.85", - "suricata.eve.dest_port": 39464, "suricata.eve.dns.id": 12308, "suricata.eve.dns.rcode": "NOERROR", "suricata.eve.dns.rdata": "172.217.13.110", @@ -228,12 +184,8 @@ "suricata.eve.dns.rrtype": "A", "suricata.eve.dns.ttl": 299, "suricata.eve.dns.type": "answer", - "suricata.eve.event_type": "dns", "suricata.eve.flow_id": 1684780223079543, "suricata.eve.in_iface": "en0", - "suricata.eve.proto": "UDP", - "suricata.eve.src_ip": "192.168.86.1", - "suricata.eve.src_port": 53, "tags": [ "suricata" ] @@ -249,7 +201,6 @@ "input.type": "log", "log.offset": 2687, "service.type": "suricata", - "suricata.eve.event_type": "stats", "suricata.eve.stats.app_layer.flow.dcerpc_tcp": 0, "suricata.eve.stats.app_layer.flow.dcerpc_udp": 0, "suricata.eve.stats.app_layer.flow.dns_tcp": 0, @@ -390,14 +341,8 @@ "service.type": "suricata", "source.ip": "192.168.86.85", "source.port": 56187, - "suricata.eve.dest_ip": "17.142.164.13", - "suricata.eve.dest_port": 443, - "suricata.eve.event_type": "tls", "suricata.eve.flow_id": 89751777876473, "suricata.eve.in_iface": "en0", - "suricata.eve.proto": "TCP", - "suricata.eve.src_ip": "192.168.86.85", - "suricata.eve.src_port": 56187, "suricata.eve.tls.fingerprint": "6a:ff:ac:a6:5f:8a:05:e7:a9:8c:76:29:b9:08:c7:69:ad:dc:72:47", "suricata.eve.tls.issuerdn": "CN=Apple IST CA 2 - G1/OU=Certification Authority/O=Apple Inc./C=US", "suricata.eve.tls.notafter": "2019-03-29T17:54:31", @@ -435,22 +380,11 @@ "source.ip": "fe80:0000:0000:0000:fada:0cff:fedc:87f1", "source.packets": 1, "source.port": 546, - "suricata.eve.app_proto": "failed", - "suricata.eve.dest_ip": "ff02:0000:0000:0000:0000:0000:0001:0002", - "suricata.eve.dest_port": 547, - "suricata.eve.event_type": "flow", "suricata.eve.flow.age": 0, "suricata.eve.flow.alerted": false, - "suricata.eve.flow.bytes_toclient": 0, - "suricata.eve.flow.bytes_toserver": 110, - "suricata.eve.flow.pkts_toclient": 0, - "suricata.eve.flow.pkts_toserver": 1, "suricata.eve.flow.reason": "timeout", "suricata.eve.flow.state": "new", "suricata.eve.flow_id": 1828507008887644, - "suricata.eve.proto": "UDP", - "suricata.eve.src_ip": "fe80:0000:0000:0000:fada:0cff:fedc:87f1", - "suricata.eve.src_port": 546, "tags": [ "suricata" ] diff --git a/x-pack/filebeat/module/suricata/fields.go b/x-pack/filebeat/module/suricata/fields.go index 35e16147a4a..66e8b7e5c75 100644 --- a/x-pack/filebeat/module/suricata/fields.go +++ b/x-pack/filebeat/module/suricata/fields.go @@ -19,5 +19,5 @@ func init() { // AssetSuricata returns asset data. // This is the base64 encoded gzipped contents of module/suricata. func AssetSuricata() string { - return "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" + return "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" } diff --git a/x-pack/filebeat/module/zeek/http/ingest/pipeline.json b/x-pack/filebeat/module/zeek/http/ingest/pipeline.json index 2da6e89dffc..c0614b4f3a1 100644 --- a/x-pack/filebeat/module/zeek/http/ingest/pipeline.json +++ b/x-pack/filebeat/module/zeek/http/ingest/pipeline.json @@ -54,6 +54,13 @@ "ignore_missing": true } }, + { + "rename": { + "field": "user_agent.device", + "target_field": "user_agent.device.name", + "ignore_missing": true + } + }, { "rename": { "field": "user_agent.os", @@ -97,4 +104,4 @@ } } ] -} \ No newline at end of file +} diff --git a/x-pack/filebeat/module/zeek/http/test/http-json.log-expected.json b/x-pack/filebeat/module/zeek/http/test/http-json.log-expected.json index b6d7e0dca5c..57c220b4b85 100644 --- a/x-pack/filebeat/module/zeek/http/test/http-json.log-expected.json +++ b/x-pack/filebeat/module/zeek/http/test/http-json.log-expected.json @@ -30,7 +30,7 @@ "url.domain": "ocsp.apple.com", "url.original": "/ocsp04-aaica02/ME4wTKADAgEAMEUwQzBBMAkGBSsOAwIaBQAEFNqvF+Za6oA4ceFRLsAWwEInjUhJBBQx6napI3Sl39T97qDBpp7GEQ4R7AIIUP1IOZZ86ns=", "url.port": "80", - "user_agent.device": "Other", + "user_agent.device.name": "Other", "user_agent.name": "Other", "user_agent.os.full_name": "Other", "user_agent.os.name": "Other", diff --git a/x-pack/filebeat/modules.d/iptables.yml.disabled b/x-pack/filebeat/modules.d/iptables.yml.disabled new file mode 100644 index 00000000000..833fd91537b --- /dev/null +++ b/x-pack/filebeat/modules.d/iptables.yml.disabled @@ -0,0 +1,13 @@ +# Module: iptables +# Docs: https://www.elastic.co/guide/en/beats/filebeat/master/filebeat-module-iptables.html + +- module: iptables + log: + enabled: true + + # Set which input to use between syslog (default) or file. + #var.input: + + # Set custom paths for the log files. If left empty, + # Filebeat will choose the paths depending on your OS. + #var.paths: diff --git a/x-pack/functionbeat/_meta/beat.yml b/x-pack/functionbeat/_meta/beat.yml index 539fbdeb7cf..2274a5033c8 100644 --- a/x-pack/functionbeat/_meta/beat.yml +++ b/x-pack/functionbeat/_meta/beat.yml @@ -144,3 +144,10 @@ functionbeat.provider.aws.functions: # Starting position is where to start reading events from the Kinesis stream. # Default is trim_horizon. #starting_position: "trim_horizon" + +#==================== Elasticsearch template setting ========================== + +setup.template.settings: + index.number_of_shards: 1 + #index.codec: best_compression + #_source.enabled: false diff --git a/x-pack/functionbeat/core/makezip.go b/x-pack/functionbeat/core/makezip.go index 0d1a88d00d9..5ac5127c646 100644 --- a/x-pack/functionbeat/core/makezip.go +++ b/x-pack/functionbeat/core/makezip.go @@ -5,9 +5,16 @@ package core import ( + "fmt" + yaml "gopkg.in/yaml.v2" + "github.com/pkg/errors" + "github.com/elastic/beats/libbeat/cfgfile" + "github.com/elastic/beats/libbeat/cmd/instance" + "github.com/elastic/beats/libbeat/common" + "github.com/elastic/beats/libbeat/keystore" "github.com/elastic/beats/x-pack/functionbeat/config" "github.com/elastic/beats/x-pack/functionbeat/core/bundle" ) @@ -43,12 +50,29 @@ func MakeZip() ([]byte, error) { if err != nil { return nil, err } + + resources := []bundle.Resource{ + &bundle.MemoryFile{Path: "functionbeat.yml", Raw: rawConfig, FileMode: 0766}, + &bundle.LocalFile{Path: "pkg/functionbeat", FileMode: 0755}, + } + + rawKeystore, err := keystoreRaw() + if err != nil { + return nil, err + } + + if len(rawKeystore) > 0 { + resources = append(resources, &bundle.MemoryFile{ + Path: "data/functionbeat.keystore", + Raw: rawKeystore, + FileMode: 0600, + }) + } + bundle := bundle.NewZipWithLimits( packageUncompressedLimit, packageCompressedLimit, - &bundle.MemoryFile{Path: "functionbeat.yml", Raw: rawConfig, FileMode: 0766}, - &bundle.LocalFile{Path: "pkg/functionbeat", FileMode: 0755}, - ) + resources...) content, err := bundle.Bytes() if err != nil { @@ -56,3 +80,22 @@ func MakeZip() ([]byte, error) { } return content, nil } + +func keystoreRaw() ([]byte, error) { + cfg, err := cfgfile.Load("", common.NewConfig()) + if err != nil { + return nil, fmt.Errorf("error loading config file: %v", err) + } + + store, err := instance.LoadKeystore(cfg, "functionbeat") + if err != nil { + return nil, errors.Wrapf(err, "cannot load the keystore for packaging") + } + + packager, ok := store.(keystore.Packager) + if !ok { + return nil, fmt.Errorf("the configured keystore cannot be packaged") + } + + return packager.Package() +} diff --git a/x-pack/functionbeat/docs/fields.asciidoc b/x-pack/functionbeat/docs/fields.asciidoc index 2deda0fda83..b062de265fc 100644 --- a/x-pack/functionbeat/docs/fields.asciidoc +++ b/x-pack/functionbeat/docs/fields.asciidoc @@ -3284,16 +3284,6 @@ type: keyword Major version of the user agent. --- - -*`user_agent.device`*:: -+ --- -type: keyword - -Name of the physical device. - - -- *`user_agent.os.major`*:: @@ -3314,16 +3304,6 @@ type: long Minor version of the operating system. --- - -*`url.hostname`*:: -+ --- -type: keyword - -Hostname of the request, such as "elastic.co". - - -- [[exported-fields-functionbeat]] diff --git a/x-pack/functionbeat/functionbeat.reference.yml b/x-pack/functionbeat/functionbeat.reference.yml index 832009f360a..05feb66beec 100644 --- a/x-pack/functionbeat/functionbeat.reference.yml +++ b/x-pack/functionbeat/functionbeat.reference.yml @@ -391,11 +391,6 @@ output.elasticsearch: # IPv6 addresses should always be defined as: https://[2001:db8::1]:9200 hosts: ["localhost:9200"] - # Enabled ilm (beta) to use index lifecycle management instead daily indices. - #ilm.enabled: false - #ilm.rollover_alias: "functionbeat" - #ilm.pattern: "{now/d}-000001" - # Set gzip compression level. #compression_level: 0 @@ -1042,6 +1037,25 @@ setup.template.settings: #_source: #enabled: false +#============================== Setup ILM ===================================== + +# Configure Index Lifecycle Management Index Lifecycle Management creates a +# write alias and adds additional settings to the template. +# The elasticsearch.output.index setting will be replaced with the write alias +# if ILM is enabled. + +# Enabled ILM support. Valid values are true, false, and auto. The beat will +# detect availabilty of Index Lifecycle Management in Elasticsearch and enable +# or disable ILM support. +#setup.ilm.enabled: auto + +# Configure the ILM write alias name. +#setup.ilm.rollover_alias: "functionbeat" + +# Configure rollover index pattern. +#setup.ilm.pattern: "{now/d}-000001" + + #============================== Kibana ===================================== # Starting with Beats version 6.0.0, the dashboards are loaded via the Kibana API. diff --git a/x-pack/functionbeat/functionbeat.yml b/x-pack/functionbeat/functionbeat.yml index 1136f2d26ba..73ab5b1cca3 100644 --- a/x-pack/functionbeat/functionbeat.yml +++ b/x-pack/functionbeat/functionbeat.yml @@ -145,6 +145,13 @@ functionbeat.provider.aws.functions: # Default is trim_horizon. #starting_position: "trim_horizon" +#==================== Elasticsearch template setting ========================== + +setup.template.settings: + index.number_of_shards: 1 + #index.codec: best_compression + #_source.enabled: false + #================================ General ===================================== # The name of the shipper that publishes the network data. It can be used to group @@ -164,7 +171,7 @@ functionbeat.provider.aws.functions: #============================== Dashboards ===================================== # These settings control loading the sample dashboards to the Kibana index. Loading # the dashboards is disabled by default and can be enabled either by setting the -# options here, or by using the `-setup` CLI flag or the `setup` command. +# options here or by using the `setup` command. #setup.dashboards.enabled: false # The URL from where to download the dashboards archive. By default this URL @@ -212,9 +219,6 @@ output.elasticsearch: # Array of hosts to connect to. hosts: ["localhost:9200"] - # Enabled ilm (beta) to use index lifecycle management instead daily indices. - #ilm.enabled: false - # Optional protocol and basic auth credentials. #protocol: "https" #username: "elastic" diff --git a/x-pack/functionbeat/include/fields.go b/x-pack/functionbeat/include/fields.go index 62113755e71..737aef0a691 100644 --- a/x-pack/functionbeat/include/fields.go +++ b/x-pack/functionbeat/include/fields.go @@ -19,5 +19,5 @@ func init() { // AssetFieldsYml returns asset data. // This is the base64 encoded gzipped contents of fields.yml. func AssetFieldsYml() string { - return "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" + return "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" } diff --git a/x-pack/libbeat/management/api/configuration.go b/x-pack/libbeat/management/api/configuration.go index 3dd851391f8..214a40336da 100644 --- a/x-pack/libbeat/management/api/configuration.go +++ b/x-pack/libbeat/management/api/configuration.go @@ -5,6 +5,7 @@ package api import ( + "encoding/json" "fmt" "net/http" "reflect" @@ -50,16 +51,40 @@ func (c *ConfigBlock) ConfigWithMeta() (*reload.ConfigWithMeta, error) { }, nil } +type configResponse struct { + Type string + Raw map[string]interface{} +} + +func (c *configResponse) UnmarshalJSON(b []byte) error { + var resp = struct { + Type string `json:"type"` + Raw map[string]interface{} `json:"config"` + }{} + + if err := json.Unmarshal(b, &resp); err != nil { + return err + } + + converter := selectConverter(resp.Type) + newMap, err := converter(resp.Raw) + if err != nil { + return err + } + *c = configResponse{ + Type: resp.Type, + Raw: newMap, + } + return nil +} + // Configuration retrieves the list of configuration blocks from Kibana func (c *Client) Configuration(accessToken string, beatUUID uuid.UUID, configOK bool) (ConfigBlocks, error) { headers := http.Header{} headers.Set("kbn-beats-access-token", accessToken) resp := struct { - ConfigBlocks []*struct { - Type string `json:"type"` - Raw map[string]interface{} `json:"config"` - } `json:"configuration_blocks"` + ConfigBlocks []*configResponse `json:"configuration_blocks"` }{} url := fmt.Sprintf("/api/beats/agent/%s/configuration?validSetting=%t", beatUUID, configOK) statusCode, err := c.request("GET", url, nil, headers, &resp) diff --git a/x-pack/libbeat/management/api/configuration_test.go b/x-pack/libbeat/management/api/configuration_test.go index 1df04cf3437..b9eafc6ef61 100644 --- a/x-pack/libbeat/management/api/configuration_test.go +++ b/x-pack/libbeat/management/api/configuration_test.go @@ -28,7 +28,7 @@ func TestConfiguration(t *testing.T) { assert.Equal(t, "false", r.URL.Query().Get("validSetting")) - fmt.Fprintf(w, `{"configuration_blocks":[{"type":"filebeat.modules","config":{"module":"apache2"}},{"type":"metricbeat.modules","config":{"module":"system","period":"10s"}}]}`) + fmt.Fprintf(w, `{"configuration_blocks":[{"type":"filebeat.modules","config":{"_sub_type":"apache2"}},{"type":"metricbeat.modules","config":{"_sub_type":"system","period":"10s"}}]}`) })) defer server.Close() diff --git a/x-pack/libbeat/management/api/convert.go b/x-pack/libbeat/management/api/convert.go new file mode 100644 index 00000000000..af7da53be46 --- /dev/null +++ b/x-pack/libbeat/management/api/convert.go @@ -0,0 +1,79 @@ +// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +// or more contributor license agreements. Licensed under the Elastic License; +// you may not use this file except in compliance with the Elastic License. + +package api + +import ( + "fmt" + "strings" +) + +type converter func(map[string]interface{}) (map[string]interface{}, error) + +var mapper = map[string]converter{ + ".inputs": noopConvert, + ".modules": convertMultiple, + "output": convertSingle, +} + +var errSubTypeNotFound = fmt.Errorf("'%s' key not found", subTypeKey) + +var ( + subTypeKey = "_sub_type" + moduleKey = "module" +) + +func selectConverter(t string) converter { + for k, v := range mapper { + if strings.Index(t, k) > -1 { + return v + } + } + return noopConvert +} + +func convertSingle(m map[string]interface{}) (map[string]interface{}, error) { + subType, err := extractSubType(m) + if err != nil { + return nil, err + } + + delete(m, subTypeKey) + newMap := map[string]interface{}{subType: m} + return newMap, nil +} + +func convertMultiple(m map[string]interface{}) (map[string]interface{}, error) { + subType, err := extractSubType(m) + if err != nil { + return nil, err + } + + v, ok := m[moduleKey] + + if ok && v != subType { + return nil, fmt.Errorf("module key already exist in the raw document and doesn't match the 'sub_type', expecting '%s' and received '%s", subType, v) + } + + m[moduleKey] = subType + delete(m, subTypeKey) + return m, nil +} + +func noopConvert(m map[string]interface{}) (map[string]interface{}, error) { + return m, nil +} + +func extractSubType(m map[string]interface{}) (string, error) { + subType, ok := m[subTypeKey] + if !ok { + return "", errSubTypeNotFound + } + + k, ok := subType.(string) + if !ok { + return "", fmt.Errorf("invalid type for `sub_type`, expecting a string received %T", subType) + } + return k, nil +} diff --git a/x-pack/libbeat/management/api/convert_test.go b/x-pack/libbeat/management/api/convert_test.go new file mode 100644 index 00000000000..e8f32e90494 --- /dev/null +++ b/x-pack/libbeat/management/api/convert_test.go @@ -0,0 +1,111 @@ +// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +// or more contributor license agreements. Licensed under the Elastic License; +// you may not use this file except in compliance with the Elastic License. + +package api + +import ( + "reflect" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestConvertAPI(t *testing.T) { + tests := map[string]struct { + t string + config map[string]interface{} + expected map[string]interface{} + err bool + }{ + "output": { + t: "output", + config: map[string]interface{}{ + "_sub_type": "elasticsearch", + "username": "foobar", + }, + expected: map[string]interface{}{ + "elasticsearch": map[string]interface{}{ + "username": "foobar", + }, + }, + }, + "filebeat inputs": { + t: "filebeat.inputs", + config: map[string]interface{}{ + "type": "log", + "paths": []string{ + "/var/log/message.log", + "/var/log/system.log", + }, + }, + expected: map[string]interface{}{ + "type": "log", + "paths": []string{ + "/var/log/message.log", + "/var/log/system.log", + }, + }, + }, + "filebeat modules": { + t: "filebeat.modules", + config: map[string]interface{}{ + "_sub_type": "system", + }, + expected: map[string]interface{}{ + "module": "system", + }, + }, + "metricbeat modules": { + t: "metricbeat.modules", + config: map[string]interface{}{ + "_sub_type": "logstash", + }, + expected: map[string]interface{}{ + "module": "logstash", + }, + }, + "badly formed output": { + err: true, + t: "output", + config: map[string]interface{}{ + "nosubtype": "logstash", + }, + }, + "badly formed filebeat module": { + err: true, + t: "filebeat.modules", + config: map[string]interface{}{ + "nosubtype": "logstash", + }, + }, + "badly formed metricbeat module": { + err: true, + t: "metricbeat.modules", + config: map[string]interface{}{ + "nosubtype": "logstash", + }, + }, + "unknown type is passthrough": { + t: "unkown", + config: map[string]interface{}{ + "nosubtype": "logstash", + }, + expected: map[string]interface{}{ + "nosubtype": "logstash", + }, + }, + } + + for name, test := range tests { + test := test + t.Run(name, func(t *testing.T) { + converter := selectConverter(test.t) + newMap, err := converter(test.config) + if !assert.Equal(t, test.err, err != nil) { + return + } + assert.True(t, reflect.DeepEqual(newMap, test.expected)) + }) + } +} diff --git a/x-pack/libbeat/management/api/doc.go b/x-pack/libbeat/management/api/doc.go new file mode 100644 index 00000000000..3bcdfee78ad --- /dev/null +++ b/x-pack/libbeat/management/api/doc.go @@ -0,0 +1,69 @@ +// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +// or more contributor license agreements. Licensed under the Elastic License; +// you may not use this file except in compliance with the Elastic License. + +/* +The Kibana CM Api returns a configuration format which cannot be ingested directly by our +configuration parser, it need to be transformed from the generic format into an adapted format +which is dependant on the type of configuration. + + +Translations: + +Type: output + +{ + "configuration_blocks": [ + + { + "config": { + "_sub_type": "elasticsearch" + "_id": "12312341231231" + "hosts": [ "localhost" ], + "password": "foobar" + "username": "elastic" + }, + "type": "output" + } + ] +} + +YAML representation: + +{ + "elasticsearch": { + "hosts": [ "localhost" ], + "password": "foobar" + "username": "elastic" + } +} + + +Type: *.modules + +{ + "configuration_blocks": [ + + { + "config": { + "_sub_type": "system" + "_id": "12312341231231" + "path" "foobar" + }, + "type": "filebeat.module" + } + ] +} + +YAML representation: + +[ +{ + "module": "system" + "path": "foobar" +} +] + +*/ + +package api diff --git a/x-pack/libbeat/tests/system/test_management.py b/x-pack/libbeat/tests/system/test_management.py index 21fea610102..edab65dfe64 100644 --- a/x-pack/libbeat/tests/system/test_management.py +++ b/x-pack/libbeat/tests/system/test_management.py @@ -26,8 +26,9 @@ def setUp(self): self.es_pass = "changeme" self.es = Elasticsearch([self.get_elasticsearch_url()], verify_certs=True) - @unittest.skipIf(not INTEGRATION_TESTS, - "integration tests are disabled, run with INTEGRATION_TESTS=1 to enable them.") + # @unittest.skipIf(not INTEGRATION_TESTS, + # "integration tests are disabled, run with INTEGRATION_TESTS=1 to enable them.") + @unittest.skip("Skipping because snapshot is not ready yet. see #10481") def test_enroll(self): """ Enroll the beat in Kibana Central Management @@ -60,8 +61,9 @@ def test_enroll(self): backup_content = open(config_path + ".bak", 'r').read() assert config_content == backup_content - @unittest.skipIf(not INTEGRATION_TESTS, - "integration tests are disabled, run with INTEGRATION_TESTS=1 to enable them.") + # @unittest.skipIf(not INTEGRATION_TESTS, + # "integration tests are disabled, run with INTEGRATION_TESTS=1 to enable them.") + @unittest.skip("Skipping because snapshot is not ready yet. see #10481") def test_enroll_bad_pw(self): """ Try to enroll the beat in Kibana Central Management with a bad password @@ -85,8 +87,9 @@ def test_enroll_bad_pw(self): new_content = open(config_path, 'r').read() assert config_content == new_content - @unittest.skipIf(not INTEGRATION_TESTS, - "integration tests are disabled, run with INTEGRATION_TESTS=1 to enable them.") + # @unittest.skipIf(not INTEGRATION_TESTS, + # "integration tests are disabled, run with INTEGRATION_TESTS=1 to enable them.") + @unittest.skip("Skipping because snapshot is not ready yet. see #10446") def test_fetch_configs(self): """ Config is retrieved from Central Management and updates are applied @@ -152,8 +155,9 @@ def test_fetch_configs(self): proc.check_kill_and_wait() - @unittest.skipIf(not INTEGRATION_TESTS, - "integration tests are disabled, run with INTEGRATION_TESTS=1 to enable them.") + # @unittest.skipIf(not INTEGRATION_TESTS, + # "integration tests are disabled, run with INTEGRATION_TESTS=1 to enable them.") + @unittest.skip("Skipping because snapshot is not ready yet. see #10446") def test_configs_cache(self): """ Config cache is used if Kibana is not available @@ -255,6 +259,7 @@ def create_and_assing_tag(self, blocks): url = self.get_kibana_url() + "/api/beats/agents_tags/assignments" r = requests.post(url, json=data, headers=headers, auth=(self.es_user, self.es_pass)) + assert r.status_code == 200 def get_elasticsearch_url(self): diff --git a/x-pack/metricbeat/magefile.go b/x-pack/metricbeat/magefile.go index 1f4635fcc6a..75aaaad5eeb 100644 --- a/x-pack/metricbeat/magefile.go +++ b/x-pack/metricbeat/magefile.go @@ -16,6 +16,7 @@ import ( "time" "github.com/magefile/mage/mg" + "github.com/pkg/errors" "github.com/elastic/beats/dev-tools/mage" ) @@ -64,7 +65,9 @@ func Package() { start := time.Now() defer func() { fmt.Println("package ran for", time.Since(start)) }() - mage.LoadLocalNamedSpec("xpack") + mage.UseElasticBeatXPackPackaging() + customizePackaging() + mage.PackageKibanaDashboardsFromBuildDir() mg.Deps(Update, prepareModulePackaging) mg.Deps(CrossBuild, CrossBuildGoDaemon) @@ -97,7 +100,7 @@ func Dashboards() error { // Config generates both the short and reference configs. func Config() { - mg.Deps(shortConfig, referenceConfig, createDirModulesD) + mg.Deps(shortConfig, referenceConfig, dockerConfig, createDirModulesD) } // Update is an alias for running fields, dashboards, config. @@ -244,6 +247,15 @@ func referenceConfig() error { return nil } +func dockerConfig() error { + var configParts = []string{ + mage.OSSBeatDir("_meta/beat.docker.yml"), + mage.LibbeatDir("_meta/config.docker.yml"), + } + + return mage.FileConcat(mage.BeatName+".docker.yml", 0600, configParts...) +} + func createDirModulesD() error { if err := os.RemoveAll("modules.d"); err != nil { return err @@ -272,3 +284,65 @@ func createDirModulesD() error { } return nil } + +func customizePackaging() { + var ( + archiveModulesDir = "modules.d" + unixModulesDir = "/etc/{{.BeatName}}/modules.d" + + modulesDir = mage.PackageFile{ + Mode: 0644, + Source: dirModulesDGenerated, + Config: true, + Modules: true, + } + windowsModulesDir = mage.PackageFile{ + Mode: 0644, + Source: "{{.PackageDir}}/modules.d", + Config: true, + Modules: true, + Dep: func(spec mage.PackageSpec) error { + if err := mage.Copy(dirModulesDGenerated, spec.MustExpand("{{.PackageDir}}/modules.d")); err != nil { + return errors.Wrap(err, "failed to copy modules.d dir") + } + + return mage.FindReplace( + spec.MustExpand("{{.PackageDir}}/modules.d/system.yml"), + regexp.MustCompile(`- load`), `#- load`) + }, + } + windowsReferenceConfig = mage.PackageFile{ + Mode: 0644, + Source: "{{.PackageDir}}/metricbeat.reference.yml", + Dep: func(spec mage.PackageSpec) error { + err := mage.Copy("metricbeat.reference.yml", + spec.MustExpand("{{.PackageDir}}/metricbeat.reference.yml")) + if err != nil { + return errors.Wrap(err, "failed to copy reference config") + } + + return mage.FindReplace( + spec.MustExpand("{{.PackageDir}}/metricbeat.reference.yml"), + regexp.MustCompile(`- load`), `#- load`) + }, + } + ) + + for _, args := range mage.Packages { + switch args.OS { + case "windows": + args.Spec.Files[archiveModulesDir] = windowsModulesDir + args.Spec.ReplaceFile("{{.BeatName}}.reference.yml", windowsReferenceConfig) + default: + pkgType := args.Types[0] + switch pkgType { + case mage.TarGz, mage.Zip, mage.Docker: + args.Spec.Files[archiveModulesDir] = modulesDir + case mage.Deb, mage.RPM, mage.DMG: + args.Spec.Files[unixModulesDir] = modulesDir + default: + panic(errors.Errorf("unhandled package type: %v", pkgType)) + } + } + } +} diff --git a/x-pack/metricbeat/metricbeat.docker.yml b/x-pack/metricbeat/metricbeat.docker.yml new file mode 100644 index 00000000000..982018eefc7 --- /dev/null +++ b/x-pack/metricbeat/metricbeat.docker.yml @@ -0,0 +1,11 @@ +metricbeat.config.modules: + path: ${path.config}/modules.d/*.yml + reload.enabled: false + +processors: +- add_cloud_metadata: ~ + +output.elasticsearch: + hosts: '${ELASTICSEARCH_HOSTS:elasticsearch:9200}' + username: '${ELASTICSEARCH_USERNAME:}' + password: '${ELASTICSEARCH_PASSWORD:}' diff --git a/x-pack/metricbeat/metricbeat.reference.yml b/x-pack/metricbeat/metricbeat.reference.yml index 9b9675b01fc..6eed891a52a 100644 --- a/x-pack/metricbeat/metricbeat.reference.yml +++ b/x-pack/metricbeat/metricbeat.reference.yml @@ -525,7 +525,15 @@ metricbeat.modules: enabled: true period: 10s hosts: ["localhost:4949"] - node.namespace: node + + # List of plugins to collect metrics from, by default it collects from + # all the available ones. + #munin.plugins: [] + + # If set to true, it sanitizes fields names in concordance with munin + # implementation (all characters that are not alphanumeric, or underscore + # are replaced by underscores). + #munin.sanitize: false #-------------------------------- MySQL Module -------------------------------- - module: mysql @@ -966,11 +974,6 @@ output.elasticsearch: # IPv6 addresses should always be defined as: https://[2001:db8::1]:9200 hosts: ["localhost:9200"] - # Enabled ilm (beta) to use index lifecycle management instead daily indices. - #ilm.enabled: false - #ilm.rollover_alias: "metricbeat" - #ilm.pattern: "{now/d}-000001" - # Set gzip compression level. #compression_level: 0 @@ -1617,6 +1620,25 @@ setup.template.settings: #_source: #enabled: false +#============================== Setup ILM ===================================== + +# Configure Index Lifecycle Management Index Lifecycle Management creates a +# write alias and adds additional settings to the template. +# The elasticsearch.output.index setting will be replaced with the write alias +# if ILM is enabled. + +# Enabled ILM support. Valid values are true, false, and auto. The beat will +# detect availabilty of Index Lifecycle Management in Elasticsearch and enable +# or disable ILM support. +#setup.ilm.enabled: auto + +# Configure the ILM write alias name. +#setup.ilm.rollover_alias: "metricbeat" + +# Configure rollover index pattern. +#setup.ilm.pattern: "{now/d}-000001" + + #============================== Kibana ===================================== # Starting with Beats version 6.0.0, the dashboards are loaded via the Kibana API. diff --git a/x-pack/metricbeat/metricbeat.yml b/x-pack/metricbeat/metricbeat.yml index 9732d9907b1..52916c295de 100644 --- a/x-pack/metricbeat/metricbeat.yml +++ b/x-pack/metricbeat/metricbeat.yml @@ -45,7 +45,7 @@ setup.template.settings: #============================== Dashboards ===================================== # These settings control loading the sample dashboards to the Kibana index. Loading # the dashboards is disabled by default and can be enabled either by setting the -# options here, or by using the `-setup` CLI flag or the `setup` command. +# options here or by using the `setup` command. #setup.dashboards.enabled: false # The URL from where to download the dashboards archive. By default this URL @@ -93,9 +93,6 @@ output.elasticsearch: # Array of hosts to connect to. hosts: ["localhost:9200"] - # Enabled ilm (beta) to use index lifecycle management instead daily indices. - #ilm.enabled: false - # Optional protocol and basic auth credentials. #protocol: "https" #username: "elastic" diff --git a/x-pack/metricbeat/module/aws/_meta/docs.asciidoc b/x-pack/metricbeat/module/aws/_meta/docs.asciidoc index 735254f1a8a..a510dfd570c 100644 --- a/x-pack/metricbeat/module/aws/_meta/docs.asciidoc +++ b/x-pack/metricbeat/module/aws/_meta/docs.asciidoc @@ -25,8 +25,8 @@ see https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp.html[Te aws> sts get-session-token --serial-number arn:aws:iam::1234:mfa/your-email@example.com --token-code 456789 --duration-seconds 129600 ---- -Specific permissions needs to be added into the IAM user's policy to allow Metricbeat collecting AWS monitoring metrics. Please -see documentation under each metric set for required permissions. +Specific permissions needs to be added into the IAM user's policy to authorize Metricbeat to collect AWS monitoring metrics. Please +see documentation under each metricset for required permissions. By default, Amazon EC2 sends metric data to CloudWatch every 5 minutes. With this basic monitoring, `period` in aws module configuration should be larger or equal than `300s`. If `period` is set to be less than `300s`, the same cloudwatch metrics diff --git a/x-pack/metricbeat/module/aws/_meta/kibana/7/dashboard/Metricbeat-aws-ec2-overview.json b/x-pack/metricbeat/module/aws/_meta/kibana/7/dashboard/Metricbeat-aws-ec2-overview.json index 23087ee392a..28396b84e0d 100644 --- a/x-pack/metricbeat/module/aws/_meta/kibana/7/dashboard/Metricbeat-aws-ec2-overview.json +++ b/x-pack/metricbeat/module/aws/_meta/kibana/7/dashboard/Metricbeat-aws-ec2-overview.json @@ -478,7 +478,7 @@ }, { "attributes": { - "description": "", + "description": "Overview of AWS EC2 Metrics", "hits": 0, "kibanaSavedObjectMeta": { "searchSourceJSON": { diff --git a/x-pack/metricbeat/module/aws/aws.go b/x-pack/metricbeat/module/aws/aws.go index 3ff2465a71a..9b2f23cc43c 100644 --- a/x-pack/metricbeat/module/aws/aws.go +++ b/x-pack/metricbeat/module/aws/aws.go @@ -4,7 +4,17 @@ package aws -import "github.com/elastic/beats/metricbeat/mb" +import ( + "strconv" + + awssdk "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/aws/defaults" + "github.com/aws/aws-sdk-go-v2/service/ec2" + "github.com/aws/aws-sdk-go-v2/service/ec2/ec2iface" + "github.com/pkg/errors" + + "github.com/elastic/beats/metricbeat/mb" +) // Config defines all required and optional parameters for aws metricsets type Config struct { @@ -18,6 +28,10 @@ type Config struct { // MetricSet is the base metricset for all aws metricsets type MetricSet struct { mb.BaseMetricSet + RegionsList []string + DurationString string + PeriodInSec int + AwsConfig *awssdk.Config } // ModuleName is the name of this module. @@ -44,5 +58,79 @@ func NewMetricSet(base mb.BaseMetricSet) (*MetricSet, error) { if err != nil { return nil, err } - return &MetricSet{BaseMetricSet: base}, nil + + awsConfig := defaults.Config() + awsCreds := awssdk.Credentials{ + AccessKeyID: config.AccessKeyID, + SecretAccessKey: config.SecretAccessKey, + } + if config.SessionToken != "" { + awsCreds.SessionToken = config.SessionToken + } + + awsConfig.Credentials = awssdk.StaticCredentialsProvider{ + Value: awsCreds, + } + + awsConfig.Region = config.DefaultRegion + + svcEC2 := ec2.New(awsConfig) + regionsList, err := getRegions(svcEC2) + if err != nil { + return nil, err + } + + // Calculate duration based on period + durationString, periodSec, err := convertPeriodToDuration(config.Period) + if err != nil { + return nil, err + } + + // Construct MetricSet + metricSet := MetricSet{ + BaseMetricSet: base, + RegionsList: regionsList, + DurationString: durationString, + PeriodInSec: periodSec, + AwsConfig: &awsConfig, + } + return &metricSet, nil +} + +func getRegions(svc ec2iface.EC2API) (regionsList []string, err error) { + input := &ec2.DescribeRegionsInput{} + req := svc.DescribeRegionsRequest(input) + output, err := req.Send() + if err != nil { + err = errors.Wrap(err, "Failed DescribeRegions") + return + } + for _, region := range output.Regions { + regionsList = append(regionsList, *region.RegionName) + } + return +} + +func convertPeriodToDuration(period string) (string, int, error) { + // Set starttime double the default frequency earlier than the endtime in order to make sure + // GetMetricDataRequest gets the latest data point for each metric. + numberPeriod, err := strconv.Atoi(period[0 : len(period)-1]) + if err != nil { + return "", 0, err + } + + unitPeriod := period[len(period)-1:] + switch unitPeriod { + case "s": + duration := "-" + strconv.Itoa(numberPeriod*2) + unitPeriod + return duration, numberPeriod, nil + case "m": + duration := "-" + strconv.Itoa(numberPeriod*2) + unitPeriod + periodInSec := numberPeriod * 60 + return duration, periodInSec, nil + default: + err = errors.New("invalid period in config. Please reset period in config") + duration := "-" + strconv.Itoa(numberPeriod*2) + "s" + return duration, numberPeriod, err + } } diff --git a/x-pack/metricbeat/module/aws/aws_test.go b/x-pack/metricbeat/module/aws/aws_test.go new file mode 100644 index 00000000000..b503690464a --- /dev/null +++ b/x-pack/metricbeat/module/aws/aws_test.go @@ -0,0 +1,81 @@ +// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +// or more contributor license agreements. Licensed under the Elastic License; +// you may not use this file except in compliance with the Elastic License. + +// +build !integration + +package aws + +import ( + "fmt" + "testing" + + awssdk "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/ec2" + "github.com/aws/aws-sdk-go-v2/service/ec2/ec2iface" + "github.com/stretchr/testify/assert" +) + +// MockEC2Client struct is used for unit tests. +type MockEC2Client struct { + ec2iface.EC2API +} + +var regionName = "us-west-1" + +func (m *MockEC2Client) DescribeRegionsRequest(input *ec2.DescribeRegionsInput) ec2.DescribeRegionsRequest { + return ec2.DescribeRegionsRequest{ + Request: &awssdk.Request{ + Data: &ec2.DescribeRegionsOutput{ + Regions: []ec2.Region{ + { + RegionName: ®ionName, + }, + }, + }, + }, + } +} + +func TestGetRegions(t *testing.T) { + mockSvc := &MockEC2Client{} + regionsList, err := getRegions(mockSvc) + if err != nil { + fmt.Println("failed getRegions: ", err) + t.FailNow() + } + assert.Equal(t, 1, len(regionsList)) + assert.Equal(t, regionName, regionsList[0]) +} + +func TestConvertPeriodToDuration(t *testing.T) { + period1 := "300s" + duration1, periodSec1, err := convertPeriodToDuration(period1) + assert.NoError(t, nil, err) + assert.Equal(t, "-600s", duration1) + assert.Equal(t, 300, periodSec1) + + period2 := "30ss" + duration2, periodSec2, err := convertPeriodToDuration(period2) + assert.Error(t, err) + assert.Equal(t, "", duration2) + assert.Equal(t, 0, periodSec2) + + period3 := "10m" + duration3, periodSec3, err := convertPeriodToDuration(period3) + assert.NoError(t, nil, err) + assert.Equal(t, "-20m", duration3) + assert.Equal(t, 600, periodSec3) + + period4 := "30s" + duration4, periodSec4, err := convertPeriodToDuration(period4) + assert.NoError(t, nil, err) + assert.Equal(t, "-60s", duration4) + assert.Equal(t, 30, periodSec4) + + period5 := "60s" + duration5, periodSec5, err := convertPeriodToDuration(period5) + assert.NoError(t, nil, err) + assert.Equal(t, "-120s", duration5) + assert.Equal(t, 60, periodSec5) +} diff --git a/x-pack/metricbeat/module/aws/ec2/_meta/data.json b/x-pack/metricbeat/module/aws/ec2/_meta/data.json index 0a12fc20283..d753f58ef13 100644 --- a/x-pack/metricbeat/module/aws/ec2/_meta/data.json +++ b/x-pack/metricbeat/module/aws/ec2/_meta/data.json @@ -7,28 +7,56 @@ "aws": { "ec2": { "cpu": { + "credit_balance": 169.089216, + "credit_usage": 0.002926, + "surplus_credit_balance": 0, + "surplus_credits_charged": 0, "total": { - "pct": 3.34375 + "pct": 0.033333333333322 } }, "diskio": { "read": { "bytes": 0, - "ops": 0 + "count": 0 }, "write": { - "bytes": 14036172.8, - "ops": 1320.4 + "bytes": 0, + "count": 0 } }, + "instance": { + "core": { + "count": 1 + }, + "image": { + "id": "ami-01e24be29428c15b2" + }, + "monitoring": { + "state": "disabled" + }, + "private": { + "dns_name": "ip-172-31-26-12.us-west-2.compute.internal", + "ip": "172.31.26.12" + }, + "public": { + "dns_name": "ec2-34-217-213-210.us-west-2.compute.amazonaws.com", + "ip": "34.217.213.210" + }, + "state": { + "code": 16, + "name": "running" + }, + "threads_per_core": 1 + }, "network": { "in": { - "bytes": 112.8, - "packets": 2 + "bytes": 296.6, + "packets": 3 }, "out": { - "bytes": 84.8, - "packets": 2 + "bytes": 269, + "packets": 3.6 } }, "status": { @@ -39,18 +67,15 @@ } }, "cloud": { - "availability_zone": "eu-central-1b", - "image": { - "id": "ami-556ef13a" - }, + "availability_zone": "us-west-2a", "instance": { - "id": "i-08027f3dcddb9a976" + "id": "i-077bdaf7e5d81bba3" }, "machine": { - "type": "i3.4xlarge" + "type": "t2.micro" }, "provider": "ec2", - "region": "eu-central-1" + "region": "us-west-2" }, "event": { "dataset": "aws.ec2", diff --git a/x-pack/metricbeat/module/aws/ec2/_meta/fields.yml b/x-pack/metricbeat/module/aws/ec2/_meta/fields.yml index e3e86cf883c..1aaefab58f7 100644 --- a/x-pack/metricbeat/module/aws/ec2/_meta/fields.yml +++ b/x-pack/metricbeat/module/aws/ec2/_meta/fields.yml @@ -68,3 +68,39 @@ type: long description: > Reports whether the instance has passed the instance status check in the last minute. + - name: instance.core.count + type: integer + description: > + The number of CPU cores for the instance. + - name: instance.image.id + type: keyword + description: > + The ID of the image used to launch the instance. + - name: instance.monitoring.state + type: keyword + description: > + Indicates whether detailed monitoring is enabled. + - name: instance.private.dns_name + type: keyword + description: > + The private DNS name of the network interface. + - name: instance.private.ip + type: ip + description: > + The private IPv4 address associated with the network interface. + - name: instance.public.dns_name + type: keyword + description: > + The public DNS name of the instance. + - name: instance.public.ip + type: ip + description: > + The address of the Elastic IP address (IPv4) bound to the network interface. + - name: instance.state.code + type: integer + description: > + The state of the instance, as a 16-bit unsigned integer. + - name: instance.threads_per_core + type: integer + description: > + The state of the instance (pending | running | shutting-down | terminated | stopping | stopped). diff --git a/x-pack/metricbeat/module/aws/ec2/data.go b/x-pack/metricbeat/module/aws/ec2/data.go index 20cf12ce9ba..e73e8309c1f 100644 --- a/x-pack/metricbeat/module/aws/ec2/data.go +++ b/x-pack/metricbeat/module/aws/ec2/data.go @@ -24,11 +24,11 @@ var ( "diskio": s.Object{ "read": s.Object{ "bytes": c.Float("diskio.read.bytes", s.Optional), - "ops": c.Float("diskio.read.ops", s.Optional), + "count": c.Float("diskio.read.count", s.Optional), }, "write": s.Object{ "bytes": c.Float("diskio.write.bytes", s.Optional), - "ops": c.Float("diskio.write.ops", s.Optional), + "count": c.Float("diskio.write.count", s.Optional), }, }, "network": s.Object{ @@ -46,6 +46,30 @@ var ( "check_failed_instance": c.Int("status.check_failed_instance", s.Optional), "check_failed_system": c.Int("status.check_failed_system", s.Optional), }, + "instance": s.Object{ + "image": s.Object{ + "id": c.Str("instance.image.id", s.Optional), + }, + "state": s.Object{ + "name": c.Str("instance.state.name", s.Optional), + "code": c.Int("instance.state.code", s.Optional), + }, + "monitoring": s.Object{ + "state": c.Str("instance.monitoring.state", s.Optional), + }, + "core": s.Object{ + "count": c.Int("instance.core.count", s.Optional), + }, + "threads_per_core": c.Int("instance.threads_per_core", s.Optional), + "public": s.Object{ + "ip": c.Str("instance.public.ip", s.Optional), + "dns_name": c.Str("instance.public.dns_name", s.Optional), + }, + "private": s.Object{ + "ip": c.Str("instance.private.ip", s.Optional), + "dns_name": c.Str("instance.private.dns_name", s.Optional), + }, + }, } ) @@ -55,18 +79,16 @@ var ( "name": c.Str("service.name", s.Optional), }, "cloud": s.Object{ - "provider": c.Str("cloud.provider", s.Optional), + "provider": c.Str("cloud.provider", s.Optional), + "availability_zone": c.Str("cloud.availability_zone", s.Optional), + "region": c.Str("cloud.region", s.Optional), "instance": s.Object{ - "id": c.Str("cloud.instance.id", s.Optional), + "id": c.Str("cloud.instance.id", s.Optional), + "name": c.Str("cloud.instance.name", s.Optional), }, "machine": s.Object{ "type": c.Str("cloud.machine.type", s.Optional), }, - "availability_zone": c.Str("cloud.availability_zone", s.Optional), - "image": s.Object{ - "id": c.Str("cloud.image.id", s.Optional), - }, - "region": c.Str("cloud.region", s.Optional), }, } ) diff --git a/x-pack/metricbeat/module/aws/ec2/ec2.go b/x-pack/metricbeat/module/aws/ec2/ec2.go index f45274ee939..e690cfc4003 100644 --- a/x-pack/metricbeat/module/aws/ec2/ec2.go +++ b/x-pack/metricbeat/module/aws/ec2/ec2.go @@ -6,11 +6,9 @@ package ec2 import ( "fmt" - "strconv" "time" awssdk "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go-v2/aws/defaults" "github.com/aws/aws-sdk-go-v2/service/cloudwatch" "github.com/aws/aws-sdk-go-v2/service/cloudwatch/cloudwatchiface" "github.com/aws/aws-sdk-go-v2/service/ec2" @@ -41,12 +39,7 @@ func init() { // interface methods except for Fetch. type MetricSet struct { *aws.MetricSet - moduleConfig *aws.Config - awsConfig *awssdk.Config - regionsList []string - durationString string - periodInSec int - logger *logp.Logger + logger *logp.Logger } // metricIDNameMap is a translating map between createMetricDataQuery id @@ -63,8 +56,8 @@ var metricIDNameMap = map[string][]string{ "network4": {"network.out.bytes", "NetworkOut"}, "disk1": {"diskio.read.bytes", "DiskReadBytes"}, "disk2": {"diskio.write.bytes", "DiskWriteBytes"}, - "disk3": {"diskio.read.ops", "DiskReadOps"}, - "disk4": {"diskio.write.ops", "DiskWriteOps"}, + "disk3": {"diskio.read.count", "DiskReadOps"}, + "disk4": {"diskio.write.count", "DiskWriteOps"}, "status1": {"status.check_failed", "StatusCheckFailed"}, "status2": {"status.check_failed_system", "StatusCheckFailed_System"}, "status3": {"status.check_failed_instance", "StatusCheckFailed_Instance"}, @@ -90,38 +83,9 @@ func New(base mb.BaseMetricSet) (mb.MetricSet, error) { return nil, errors.Wrap(err, "error creating aws metricset") } - // Get a list of regions - awsConfig := defaults.Config() - awsCreds := awssdk.Credentials{ - AccessKeyID: moduleConfig.AccessKeyID, - SecretAccessKey: moduleConfig.SecretAccessKey, - } - if moduleConfig.SessionToken != "" { - awsCreds.SessionToken = moduleConfig.SessionToken - } - - awsConfig.Credentials = awssdk.StaticCredentialsProvider{ - Value: awsCreds, - } - - awsConfig.Region = moduleConfig.DefaultRegion - svcEC2 := ec2.New(awsConfig) - regionsList, err := getRegions(svcEC2) - if err != nil { - err = errors.Wrap(err, "getRegions failed") - ec2Logger.Error(err.Error()) - } - - // Calculate duration based on period - durationString, periodSec, err := convertPeriodToDuration(moduleConfig.Period) - if err != nil { - ec2Logger.Error(err.Error()) - return nil, err - } - // Check if period is set to be multiple of 60s or 300s - remainder300 := periodSec % 300 - remainder60 := periodSec % 60 + remainder300 := metricSet.PeriodInSec % 300 + remainder60 := metricSet.PeriodInSec % 60 if remainder300 != 0 || remainder60 != 0 { err := errors.New("period needs to be set to 60s (or a multiple of 60s) if detailed monitoring is " + "enabled for EC2 instances or set to 300s (or a multiple of 300s) if EC2 instances has basic monitoring. " + @@ -130,13 +94,8 @@ func New(base mb.BaseMetricSet) (mb.MetricSet, error) { } return &MetricSet{ - MetricSet: metricSet, - moduleConfig: &moduleConfig, - awsConfig: &awsConfig, - regionsList: regionsList, - durationString: durationString, - periodInSec: periodSec, - logger: ec2Logger, + MetricSet: metricSet, + logger: ec2Logger, }, nil } @@ -144,9 +103,9 @@ func New(base mb.BaseMetricSet) (mb.MetricSet, error) { // format. It publishes the event which is then forwarded to the output. In case // of an error set the Error field of mb.Event or simply call report.Error(). func (m *MetricSet) Fetch(report mb.ReporterV2) { - for _, regionName := range m.regionsList { - m.awsConfig.Region = regionName - svcEC2 := ec2.New(*m.awsConfig) + for _, regionName := range m.MetricSet.RegionsList { + m.MetricSet.AwsConfig.Region = regionName + svcEC2 := ec2.New(*m.MetricSet.AwsConfig) instanceIDs, instancesOutputs, err := getInstancesPerRegion(svcEC2) if err != nil { err = errors.Wrap(err, "getInstancesPerRegion failed, skipping region "+regionName) @@ -155,13 +114,13 @@ func (m *MetricSet) Fetch(report mb.ReporterV2) { continue } - svcCloudwatch := cloudwatch.New(*m.awsConfig) + svcCloudwatch := cloudwatch.New(*m.MetricSet.AwsConfig) for _, instanceID := range instanceIDs { init := true getMetricDataOutput := &cloudwatch.GetMetricDataOutput{NextToken: nil} for init || getMetricDataOutput.NextToken != nil { init = false - output, err := getMetricDataPerRegion(m.durationString, m.periodInSec, instanceID, getMetricDataOutput.NextToken, svcCloudwatch) + output, err := getMetricDataPerRegion(m.MetricSet.DurationString, m.MetricSet.PeriodInSec, instanceID, getMetricDataOutput.NextToken, svcCloudwatch) if err != nil { err = errors.Wrap(err, "getMetricDataPerRegion failed, skipping region "+regionName+" for instance "+instanceID) m.logger.Error(err.Error()) @@ -187,38 +146,23 @@ func (m *MetricSet) Fetch(report mb.ReporterV2) { } } -func getRegions(svc ec2iface.EC2API) (regionsList []string, err error) { - input := &ec2.DescribeRegionsInput{} - req := svc.DescribeRegionsRequest(input) - output, err := req.Send() - if err != nil { - err = errors.Wrap(err, "Failed DescribeRegions") - return - } - for _, region := range output.Regions { - regionsList = append(regionsList, *region.RegionName) - } - return -} - func createCloudWatchEvents(getMetricDataOutput *cloudwatch.GetMetricDataOutput, instanceID string, instanceOutput ec2.Instance, regionName string) (event mb.Event, info string, err error) { event.Service = metricsetName event.RootFields = common.MapStr{} mapOfRootFieldsResults := make(map[string]interface{}) mapOfRootFieldsResults["service.name"] = metricsetName + + // Cloud fields in ECS mapOfRootFieldsResults["cloud.provider"] = metricsetName + mapOfRootFieldsResults["cloud.availability_zone"] = *instanceOutput.Placement.AvailabilityZone + mapOfRootFieldsResults["cloud.region"] = regionName mapOfRootFieldsResults["cloud.instance.id"] = instanceID - machineType, err := instanceOutput.InstanceType.MarshalValue() if err != nil { err = errors.Wrap(err, "instance.InstanceType.MarshalValue failed") return } - mapOfRootFieldsResults["cloud.machine.type"] = machineType - mapOfRootFieldsResults["cloud.availability_zone"] = *instanceOutput.Placement.AvailabilityZone - mapOfRootFieldsResults["cloud.image.id"] = *instanceOutput.ImageId - mapOfRootFieldsResults["cloud.region"] = regionName resultRootFields, err := eventMapping(mapOfRootFieldsResults, schemaRootFields) if err != nil { @@ -227,7 +171,38 @@ func createCloudWatchEvents(getMetricDataOutput *cloudwatch.GetMetricDataOutput, } event.RootFields = resultRootFields + // AWS EC2 Metrics mapOfMetricSetFieldResults := make(map[string]interface{}) + mapOfMetricSetFieldResults["instance.image.id"] = *instanceOutput.ImageId + instanceStateName, err := instanceOutput.State.Name.MarshalValue() + if err != nil { + err = errors.Wrap(err, "instance.State.Name.MarshalValue failed") + return + } + + monitoringState, err := instanceOutput.Monitoring.State.MarshalValue() + if err != nil { + err = errors.Wrap(err, "instance.Monitoring.State.MarshalValue failed") + return + } + + mapOfMetricSetFieldResults["instance.state.name"] = instanceStateName + mapOfMetricSetFieldResults["instance.state.code"] = fmt.Sprint(*instanceOutput.State.Code) + mapOfMetricSetFieldResults["instance.monitoring.state"] = monitoringState + mapOfMetricSetFieldResults["instance.core.count"] = fmt.Sprint(*instanceOutput.CpuOptions.CoreCount) + mapOfMetricSetFieldResults["instance.threads_per_core"] = fmt.Sprint(*instanceOutput.CpuOptions.ThreadsPerCore) + publicIP := instanceOutput.PublicIpAddress + if publicIP != nil { + mapOfMetricSetFieldResults["instance.public.ip"] = *publicIP + } + + mapOfMetricSetFieldResults["instance.public.dns_name"] = *instanceOutput.PublicDnsName + mapOfMetricSetFieldResults["instance.private.dns_name"] = *instanceOutput.PrivateDnsName + privateIP := instanceOutput.PrivateIpAddress + if privateIP != nil { + mapOfMetricSetFieldResults["instance.private.ip"] = *privateIP + } + for _, output := range getMetricDataOutput.MetricDataResults { if len(output.Values) == 0 { continue @@ -236,7 +211,7 @@ func createCloudWatchEvents(getMetricDataOutput *cloudwatch.GetMetricDataOutput, mapOfMetricSetFieldResults[metricKey[0]] = fmt.Sprint(output.Values[0]) } - if len(mapOfMetricSetFieldResults) <= 3 { + if len(mapOfMetricSetFieldResults) <= 11 { info = "Missing Cloudwatch data for instance " + instanceID + ". This is expected for a new instance during the " + "first data collection. If this shows up multiple times, please recheck the period setting in config." return @@ -283,32 +258,6 @@ func getInstancesPerRegion(svc ec2iface.EC2API) (instanceIDs []string, instances return } -func convertPeriodToDuration(period string) (string, int, error) { - // Amazon EC2 sends metrics to Amazon CloudWatch with 5-minute default frequency. - // If detailed monitoring is enabled, then data will be available in 1-minute period. - // Set starttime double the default frequency earlier than the endtime in order to make sure - // GetMetricDataRequest gets the latest data point for each metric. - numberPeriod, err := strconv.Atoi(period[0 : len(period)-1]) - if err != nil { - return "", 0, err - } - - unitPeriod := period[len(period)-1:] - switch unitPeriod { - case "s": - duration := "-" + strconv.Itoa(numberPeriod*2) + unitPeriod - return duration, numberPeriod, nil - case "m": - duration := "-" + strconv.Itoa(numberPeriod*2) + unitPeriod - periodInSec := numberPeriod * 60 - return duration, periodInSec, nil - default: - err = errors.New("invalid period in config. Please reset period in config") - duration := "-" + strconv.Itoa(numberPeriod*2) + "s" - return duration, numberPeriod, err - } -} - func getMetricDataPerRegion(durationString string, periodInSec int, instanceID string, nextToken *string, svc cloudwatchiface.CloudWatchAPI) (*cloudwatch.GetMetricDataOutput, error) { endTime := time.Now() duration, err := time.ParseDuration(durationString) diff --git a/x-pack/metricbeat/module/aws/ec2/ec2_integration_test.go b/x-pack/metricbeat/module/aws/ec2/ec2_integration_test.go index 863b4f6be46..1c75f450886 100644 --- a/x-pack/metricbeat/module/aws/ec2/ec2_integration_test.go +++ b/x-pack/metricbeat/module/aws/ec2/ec2_integration_test.go @@ -40,8 +40,8 @@ func TestFetch(t *testing.T) { tempCreds["session_token"] = sessionToken } - awsMetricSet := mbtest.NewReportingMetricSetV2(t, tempCreds) - events, errs := mbtest.ReportingFetchV2(awsMetricSet) + ec2MetricSet := mbtest.NewReportingMetricSetV2(t, tempCreds) + events, errs := mbtest.ReportingFetchV2(ec2MetricSet) if errs != nil { t.Skip("Skipping TestFetch: failed to make api calls. Please check $AWS_ACCESS_KEY_ID, " + "$AWS_SECRET_ACCESS_KEY and $AWS_SESSION_TOKEN in config.yml") @@ -51,7 +51,7 @@ func TestFetch(t *testing.T) { if !assert.NotEmpty(t, events) { t.FailNow() } - t.Logf("Module: %s Metricset: %s", awsMetricSet.Module().Name(), awsMetricSet.Name()) + t.Logf("Module: %s Metricset: %s", ec2MetricSet.Module().Name(), ec2MetricSet.Name()) for _, event := range events { // RootField @@ -82,7 +82,7 @@ func TestFetch(t *testing.T) { checkEventField("status.check_failed_instance", "int", event, t) } - err := mbtest.WriteEventsReporterV2(awsMetricSet, t, "") + err := mbtest.WriteEventsReporterV2(ec2MetricSet, t, "/") if err != nil { t.Fatal("write", err) } diff --git a/x-pack/metricbeat/module/aws/ec2/ec2_test.go b/x-pack/metricbeat/module/aws/ec2/ec2_test.go index c563ed18567..3353dedaf15 100644 --- a/x-pack/metricbeat/module/aws/ec2/ec2_test.go +++ b/x-pack/metricbeat/module/aws/ec2/ec2_test.go @@ -15,7 +15,6 @@ import ( "github.com/aws/aws-sdk-go-v2/service/cloudwatch/cloudwatchiface" "github.com/aws/aws-sdk-go-v2/service/ec2" "github.com/aws/aws-sdk-go-v2/service/ec2/ec2iface" - "github.com/pkg/errors" "github.com/stretchr/testify/assert" "github.com/elastic/beats/metricbeat/mb" @@ -51,6 +50,14 @@ func (m *MockEC2Client) DescribeRegionsRequest(input *ec2.DescribeRegionsInput) } func (m *MockEC2Client) DescribeInstancesRequest(input *ec2.DescribeInstancesInput) ec2.DescribeInstancesRequest { + runningCode := int64(16) + coreCount := int64(1) + threadsPerCore := int64(1) + publicDNSName := "ec2-1-2-3-4.us-west-1.compute.amazonaws.com" + publicIP := "1.2.3.4" + privateDNSName := "ip-5-6-7-8.us-west-1.compute.internal" + privateIP := "5.6.7.8" + instance := ec2.Instance{ InstanceId: awssdk.String("i-123"), InstanceType: ec2.InstanceTypeT2Medium, @@ -58,6 +65,21 @@ func (m *MockEC2Client) DescribeInstancesRequest(input *ec2.DescribeInstancesInp AvailabilityZone: awssdk.String("us-west-1a"), }, ImageId: awssdk.String("image-123"), + State: &ec2.InstanceState{ + Name: ec2.InstanceStateNameRunning, + Code: &runningCode, + }, + Monitoring: &ec2.Monitoring{ + State: ec2.MonitoringStateDisabled, + }, + CpuOptions: &ec2.CpuOptions{ + CoreCount: &coreCount, + ThreadsPerCore: &threadsPerCore, + }, + PublicDnsName: &publicDNSName, + PublicIpAddress: &publicIP, + PrivateDnsName: &privateDNSName, + PrivateIpAddress: &privateIP, } return ec2.DescribeInstancesRequest{ Request: &awssdk.Request{ @@ -117,17 +139,6 @@ func (m *MockCloudWatchClient) GetMetricDataRequest(input *cloudwatch.GetMetricD } } -func TestGetRegions(t *testing.T) { - mockSvc := &MockEC2Client{} - regionsList, err := getRegions(mockSvc) - if err != nil { - fmt.Println("failed getRegions: ", err) - t.FailNow() - } - assert.Equal(t, 1, len(regionsList)) - assert.Equal(t, regionName, regionsList[0]) -} - func TestGetInstanceIDs(t *testing.T) { mockSvc := &MockEC2Client{} instanceIDs, instancesOutputs, err := getInstancesPerRegion(mockSvc) @@ -170,39 +181,6 @@ func TestGetMetricDataPerRegion(t *testing.T) { assert.Equal(t, 0.0, getMetricDataOutput.MetricDataResults[3].Values[0]) } -func TestConvertPeriodToDuration(t *testing.T) { - period1 := "300s" - duration1, periodSec1, err := convertPeriodToDuration(period1) - assert.NoError(t, nil, err) - assert.Equal(t, "-600s", duration1) - assert.Equal(t, 300, periodSec1) - - period2 := "30ss" - duration2, periodSec2, err := convertPeriodToDuration(period2) - expectedErr := errors.New("Invaid period in config. Please reset period in config.") - assert.Error(t, expectedErr, err) - assert.Equal(t, "", duration2) - assert.Equal(t, 0, periodSec2) - - period3 := "10m" - duration3, periodSec3, err := convertPeriodToDuration(period3) - assert.NoError(t, nil, err) - assert.Equal(t, "-20m", duration3) - assert.Equal(t, 600, periodSec3) - - period4 := "30s" - duration4, periodSec4, err := convertPeriodToDuration(period4) - assert.NoError(t, nil, err) - assert.Equal(t, "-60s", duration4) - assert.Equal(t, 30, periodSec4) - - period5 := "60s" - duration5, periodSec5, err := convertPeriodToDuration(period5) - assert.NoError(t, nil, err) - assert.Equal(t, "-120s", duration5) - assert.Equal(t, 60, periodSec5) -} - func TestCreateCloudWatchEvents(t *testing.T) { mockModuleConfig := aws.Config{ Period: "300s", @@ -213,7 +191,6 @@ func TestCreateCloudWatchEvents(t *testing.T) { RootFields: common.MapStr{ "service": common.MapStr{"name": "ec2"}, "cloud": common.MapStr{ - "image": common.MapStr{"id": "image-123"}, "region": regionName, "provider": "ec2", "instance": common.MapStr{"id": "i-123"}, @@ -225,6 +202,21 @@ func TestCreateCloudWatchEvents(t *testing.T) { "cpu": common.MapStr{ "total": common.MapStr{"pct": 0.25}, }, + "instance": common.MapStr{ + "image": common.MapStr{"id": "image-123"}, + "core": common.MapStr{"count": int64(1)}, + "threads_per_core": int64(1), + "state": common.MapStr{"code": int64(16), "name": "running"}, + "monitoring": common.MapStr{"state": "disabled"}, + "public": common.MapStr{ + "dns_name": "ec2-1-2-3-4.us-west-1.compute.amazonaws.com", + "ip": "1.2.3.4", + }, + "private": common.MapStr{ + "dns_name": "ip-5-6-7-8.us-west-1.compute.internal", + "ip": "5.6.7.8", + }, + }, }, } svcEC2Mock := &MockEC2Client{} @@ -235,13 +227,7 @@ func TestCreateCloudWatchEvents(t *testing.T) { assert.Equal(t, "i-123", instanceID) svcCloudwatchMock := &MockCloudWatchClient{} - //Calculate duration based on period - durationString, periodSec, err := convertPeriodToDuration(mockModuleConfig.Period) - assert.NoError(t, nil, err) - assert.Equal(t, "-600s", durationString) - assert.Equal(t, 300, periodSec) - - getMetricDataOutput, err := getMetricDataPerRegion(durationString, periodSec, instanceID, nil, svcCloudwatchMock) + getMetricDataOutput, err := getMetricDataPerRegion("-600s", 300, instanceID, nil, svcCloudwatchMock) assert.NoError(t, err) assert.Equal(t, 4, len(getMetricDataOutput.MetricDataResults)) assert.Equal(t, "cpu1", *getMetricDataOutput.MetricDataResults[0].Id) @@ -253,4 +239,5 @@ func TestCreateCloudWatchEvents(t *testing.T) { assert.Equal(t, "", info) assert.Equal(t, expectedEvent.RootFields, event.RootFields) assert.Equal(t, expectedEvent.MetricSetFields["cpu"], event.MetricSetFields["cpu"]) + assert.Equal(t, expectedEvent.MetricSetFields["instance"], event.MetricSetFields["instance"]) } diff --git a/x-pack/metricbeat/module/aws/fields.go b/x-pack/metricbeat/module/aws/fields.go index b5db704d62d..36cc75e590f 100644 --- a/x-pack/metricbeat/module/aws/fields.go +++ b/x-pack/metricbeat/module/aws/fields.go @@ -19,5 +19,5 @@ func init() { // AssetAws returns asset data. // This is the base64 encoded gzipped contents of module/aws. func AssetAws() string { - return "eJzElsFuIzcMQO/5CmLP3Tnk6EOBrtH7ou1ijw4t0R7CGlIQKQ+8X19obCd26mzTIJPqONKQ75Ezkj7Djg4LwNHuAJw90QI+4Wif7gAiWSicnVUW8OsdAMADjvYAg8aaCIKmRMENfvv+Jwwq7FpYtjCQFw4Gm6LDNLdMWuOIHvruDqBQIjRawBbvADZMKdpiiv4ZBAc607Thh9wWFq359OQG1HWQy0AU7h+f3Qr2YsDjeKBw/wBBxZHFwHt6dPMeHUYqBBYKZorPbL83Wxh7Dv1TgBs1MhKH9WF68fflfXeR/7pO5/Fc9VI35Nq5OqYuB79acZa3gIniapMUny/4SR3a+KsnyFQCieOWQDeAKWlAp9jAIeiQqxNUYT+VBwtBqKWQeDoAC1QjUJnqyGKOEqh7USQUiuyrarilGVykDmsqzWP59RsckxlYPvXjkhE2WqZV1TnxD2xh/5V7jam9Oys5YRGKVwLHwssTe48GGEKpFMG4PWGHEQ0SVgk9RdAC5lic4stSVktO1VYfKHdKeW3W455gTSRPnUKBKokHbl/io/bYk0B7bfn123KK8OXIDHtMlYANflDR1xrbKvRYthTnVZ6cboq3f0nUISNHiDpKU/9n/38BlHjadryvBiyhllYjjJEbBSY4qtxWF/JRy65j6TKGHbnNanzKAYUC8b59jNL2lTMGsDiVDQay5z/lz/G1+ofyT9u4Vn8vfpZufXCaF37KMEvpP4r9vcoe2XasXSGMs7B/OVUaT9eExvq4VZlrIdhrqgMZ4B454ToRuL6efCzsNCN6i+8kjend2aeqa35v8KUOOVE7FKa6a6Yyndz29ha0Owy2XTrwhim2+xBrbJ+j8/CaBs1pOWW41Hxjr94iaY5erQs9hd1qg5xeOCiTyva/+f1BWYtbO8+9p3JN2u42Gc0owlq9v548MsHENJ2KbdYO5jRcz/HxRprQHAaW6q+XXB3jfbDrHCLnPP+Dyu2O3ZL5OwAA//+GdF+p" + return "eJzEl0FvGzkPhu/5FURPLdAM8BUf9pDDAtu0h1wWwXaLHh1aoj1ENNJApDxw0R+/oDx2bNdpnKydnYNhjDTU876SKPES7ml5BTjIBYCyBrqCNzjImwsAT+Iy98opXsHvFwAAdzjIHXTJl0DgUgjkVOCPb1+gS5E1ZY5z6EgzO4FZTl1tuw6p+AHVtc0FQKZAKHQFc7wAmDEFL1c1+iVE7GhNY48ue+uYU+nHNwegdoNsByL3YfPuULBHA66eO3If7sClqMhRQFvaaNMWFQbKBOIy9uT31H4ztTC07NqHAAc8EooK02X98PP1h2Zr/F2f1s++1G25ri+NJsXQ9E53eqzFi8NAfjILCfc7/MIHe/5uCXrKjqLinCDNAENIDpW8gYNLXV+UoETW0R7MBK7kTFHDEjhCEYIUq48cRTE6ah4V4jJ51kkRnNMZtMTSTSmbjuvbr7AaTED6cT62GWGWcu1VlAN/Rwv7JPcUg317VnLCHMnvCFgZHx/YWxRA53IhD8L2hhUGFAhYomvJQ8ogilnJPy5KSu5DkckrihuH3FXW4oJgShQfZgojlBi4Y1uJG9lDSxHss+vbr9c1wscVMywwFAIW+E45HatYJq7FPCd/XslV00HhtpdiUuiRPfg0RJP+8/y/B4x+TDvaFgGOrmTzCL1no8AAKymHpUfSIeX7hmPTo7snlbMqHseATI54YYsxWl5ZYwBHpTxDR7K/KX+Nn4q+Kn9N46noqfg5NtOl0nnh6whnsf612E9lu2e559RkQn8W9o+j0zheE4x1k6pEUyZYpFA6EsAFcsBpINB0PPmQWemM6BZfKRrTydmr66k/Nfh16vpAdihU31NPuZ7c8vIpsDsMWpZ2PGPydh/i5G05KnfHTNA5VdYRtmW+cK5eIlIUtUjjWnL3kxlyeOSgDCnOn6fvL+pTVrHzXFvKu6R2t+lRhDxMk7a7jSsmqEz1VLRWWYpSt9vGqxtpQFHoOBY9XuRkFe+VtZ5DyHqc/0DK4Rk7VswmxbiU7afEw5WPHQlzyv+6UEiZpBYET+e3TSt3OKeGD++Je1oOKe+3HQF286luSsOw+FZdedvMq4v9c/ge6tLG5uDwIngR5030bDXiw0rwpHXFbRfDLEDRctEjFcgGtM+8QKXGR5lY02kNHaPDpz+/1IHX9v50qziSkvvDK3H/9TPQbm4X/7erfCYRQJHkuFbgA4/p79msZRrYncvQGvwnP49clSPaCV1cGzdyfLbkwg5ubjctb83gdzBNJfr1wfhcS+sWalzyh918cSKqcfc9fA9W38P/frucskKJwvNY6+A6yBOk2trVSCY95Ykltlfghbc9RW+b/gfkEuPqn7RFleP8sla2P0Apdxzryv5h95a+H/vZX/Lvmot/AgAA//+h+RBa" } diff --git a/x-pack/metricbeat/packages.yml b/x-pack/metricbeat/packages.yml deleted file mode 100644 index a0cf5267533..00000000000 --- a/x-pack/metricbeat/packages.yml +++ /dev/null @@ -1,90 +0,0 @@ -specs: - xpack: - - os: windows - types: [zip] - spec: - <<: *windows_binary_spec - <<: *elastic_license_for_binaries - files: - modules.d: - mode: 0644 - source: build/package/modules.d - config: true - kibana: - source: build/kibana - mode: 0644 - - - os: darwin - types: [tgz] - spec: - <<: *binary_spec - <<: *elastic_license_for_binaries - files: - modules.d: - mode: 0644 - source: build/package/modules.d - config: true - kibana: - source: build/kibana - mode: 0644 - - - os: darwin - types: [dmg] - spec: - <<: *macos_beat_pkg_spec - <<: *elastic_license_for_macos_pkg - files: - /etc/{{.BeatName}}/modules.d: - mode: 0644 - source: build/package/modules.d - config: true - '/Library/Application Support/{{.BeatVendor}}/{{.BeatName}}/kibana': - source: build/kibana - mode: 0644 - - - os: linux - types: [tgz] - spec: - <<: *binary_spec - <<: *elastic_license_for_binaries - files: - modules.d: - mode: 0644 - source: build/package/modules.d - config: true - kibana: - source: build/kibana - mode: 0644 - - - os: linux - types: [deb, rpm] - spec: - <<: *deb_rpm_spec - <<: *elastic_license_for_deb_rpm - files: - '/etc/{{.BeatName}}/modules.d': - mode: 0644 - source: build/package/modules.d - config: true - '/usr/share/{{.BeatName}}/kibana': - source: build/kibana - mode: 0644 - - - os: linux - types: [docker] - spec: - <<: *docker_spec - <<: *elastic_docker_spec - <<: *elastic_license_for_binaries - files: - '{{.BeatName}}.yml': - source: '../../metricbeat/metricbeat.docker.yml' - mode: 0600 - config: true - modules.d: - mode: 0644 - source: build/package/modules.d - config: true - kibana: - source: build/kibana - mode: 0644