diff --git a/CHANGELOG.next.asciidoc b/CHANGELOG.next.asciidoc index f309326613d8..f6827fd9548e 100644 --- a/CHANGELOG.next.asciidoc +++ b/CHANGELOG.next.asciidoc @@ -269,6 +269,7 @@ https://github.com/elastic/beats/compare/v7.0.0-alpha2...master[Check the HEAD d - Add `entity_id` fields. {pull}10500[10500] - Add seven dashboards for the system module. {pull}10511[10511] - Move System module to beta. {pull}10800[10800] +- Login dataset: Add event category and type. {pull}11339[11339] *Filebeat* diff --git a/x-pack/auditbeat/module/system/login/_meta/data.json b/x-pack/auditbeat/module/system/login/_meta/data.json index 3518c24535ad..ea9482e9d76e 100644 --- a/x-pack/auditbeat/module/system/login/_meta/data.json +++ b/x-pack/auditbeat/module/system/login/_meta/data.json @@ -1,20 +1,18 @@ { "@timestamp": "2017-10-12T08:05:34.853Z", - "agent": { - "hostname": "host.example.com", - "name": "host.example.com" - }, "event": { "action": "user_login", + "category": "authentication", "dataset": "login", "kind": "event", "module": "system", - "origin": "/var/log/wtmp.1", - "outcome": "success" + "origin": "/var/log/wtmp", + "outcome": "success", + "type": "authentication_success" }, - "message": "Login by user vagrant (UID: 1000) on pts/1 (PID: 17559) from 10.0.2.2 (IP: 10.0.2.2)", + "message": "Login by user vagrant (UID: 1000) on pts/2 (PID: 14962) from 10.0.2.2 (IP: 10.0.2.2)", "process": { - "pid": 17559 + "pid": 14962 }, "service": { "type": "system" @@ -25,6 +23,6 @@ "user": { "id": 1000, "name": "vagrant", - "terminal": "pts/1" + "terminal": "pts/2" } -} +} \ No newline at end of file diff --git a/x-pack/auditbeat/module/system/login/login.go b/x-pack/auditbeat/module/system/login/login.go index 064157e7d3a1..698911822101 100644 --- a/x-pack/auditbeat/module/system/login/login.go +++ b/x-pack/auditbeat/module/system/login/login.go @@ -202,9 +202,13 @@ func (ms *MetricSet) loginEvent(loginRecord *LoginRecord) mb.Event { switch loginRecord.Type { case userLoginRecord: + event.RootFields.Put("event.category", "authentication") event.RootFields.Put("event.outcome", "success") + event.RootFields.Put("event.type", "authentication_success") case userLoginFailedRecord: + event.RootFields.Put("event.category", "authentication") event.RootFields.Put("event.outcome", "failure") + event.RootFields.Put("event.type", "authentication_failure") } return event diff --git a/x-pack/auditbeat/module/system/login/login_test.go b/x-pack/auditbeat/module/system/login/login_test.go index 30926cbf7be1..6634c39e0a61 100644 --- a/x-pack/auditbeat/module/system/login/login_test.go +++ b/x-pack/auditbeat/module/system/login/login_test.go @@ -31,6 +31,7 @@ func TestData(t *testing.T) { config["login.wtmp_file_pattern"] = "../../../tests/files/wtmp" config["login.btmp_file_pattern"] = "" f := mbtest.NewReportingMetricSetV2(t, config) + defer f.(*MetricSet).utmpReader.bucket.DeleteBucket() events, errs := mbtest.ReportingFetchV2(f) if len(errs) > 0 { @@ -43,11 +44,50 @@ func TestData(t *testing.T) { t.Fatalf("only one event expected, got %d", len(events)) } + events[0].RootFields.Put("event.origin", "/var/log/wtmp") fullEvent := mbtest.StandardizeEvent(f, events[0], core.AddDatasetToEvent) mbtest.WriteEventToDataJSON(t, fullEvent, "") } -func TestFailedLogins(t *testing.T) { +func TestWtmp(t *testing.T) { + if byteOrder != binary.LittleEndian { + t.Skip("Test only works on little-endian systems - skipping.") + } + + defer abtest.SetupDataDir(t)() + + config := getBaseConfig() + config["login.wtmp_file_pattern"] = "../../../tests/files/wtmp" + config["login.btmp_file_pattern"] = "" + f := mbtest.NewReportingMetricSetV2(t, config) + defer f.(*MetricSet).utmpReader.bucket.DeleteBucket() + + 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.Fatalf("only one event expected, got %d", len(events)) + } + + // utmpdump: [7] [14962] [ts/2] [vagrant ] [pts/2 ] [10.0.2.2 ] [10.0.2.2 ] [2019-01-24T09:51:51,367964+00:00] + checkFieldValue(t, events[0].RootFields, "event.kind", "event") + checkFieldValue(t, events[0].RootFields, "event.category", "authentication") + checkFieldValue(t, events[0].RootFields, "event.action", "user_login") + checkFieldValue(t, events[0].RootFields, "event.outcome", "success") + checkFieldValue(t, events[0].RootFields, "event.type", "authentication_success") + checkFieldValue(t, events[0].RootFields, "process.pid", 14962) + checkFieldValue(t, events[0].RootFields, "source.ip", "10.0.2.2") + checkFieldValue(t, events[0].RootFields, "user.name", "vagrant") + checkFieldValue(t, events[0].RootFields, "user.terminal", "pts/2") + assert.True(t, events[0].Timestamp.Equal(time.Date(2019, 1, 24, 9, 51, 51, 367964000, time.UTC)), + "Timestamp is not equal: %+v", events[0].Timestamp) +} + +func TestBtmp(t *testing.T) { if byteOrder != binary.LittleEndian { t.Skip("Test only works on little-endian systems - skipping.") } @@ -58,6 +98,7 @@ func TestFailedLogins(t *testing.T) { config["login.wtmp_file_pattern"] = "" config["login.btmp_file_pattern"] = "../../../tests/files/btmp_ubuntu1804" f := mbtest.NewReportingMetricSetV2(t, config) + defer f.(*MetricSet).utmpReader.bucket.DeleteBucket() events, errs := mbtest.ReportingFetchV2(f) if len(errs) > 0 { @@ -72,8 +113,10 @@ func TestFailedLogins(t *testing.T) { // utmpdump: [6] [03307] [ ] [root ] [ssh:notty ] [10.0.2.2 ] [10.0.2.2 ] [2019-02-20T17:42:26,000000+0000] checkFieldValue(t, events[0].RootFields, "event.kind", "event") + checkFieldValue(t, events[0].RootFields, "event.category", "authentication") checkFieldValue(t, events[0].RootFields, "event.action", "user_login") checkFieldValue(t, events[0].RootFields, "event.outcome", "failure") + checkFieldValue(t, events[0].RootFields, "event.type", "authentication_failure") checkFieldValue(t, events[0].RootFields, "process.pid", 3307) checkFieldValue(t, events[0].RootFields, "source.ip", "10.0.2.2") checkFieldValue(t, events[0].RootFields, "user.id", 0) @@ -85,8 +128,10 @@ func TestFailedLogins(t *testing.T) { // The second UTMP entry in the btmp test file is a duplicate of the first, this is what Ubuntu 18.04 generates. // utmpdump: [6] [03307] [ ] [root ] [ssh:notty ] [10.0.2.2 ] [10.0.2.2 ] [2019-02-20T17:42:26,000000+0000] checkFieldValue(t, events[1].RootFields, "event.kind", "event") + checkFieldValue(t, events[0].RootFields, "event.category", "authentication") checkFieldValue(t, events[1].RootFields, "event.action", "user_login") checkFieldValue(t, events[1].RootFields, "event.outcome", "failure") + checkFieldValue(t, events[0].RootFields, "event.type", "authentication_failure") checkFieldValue(t, events[1].RootFields, "process.pid", 3307) checkFieldValue(t, events[1].RootFields, "source.ip", "10.0.2.2") checkFieldValue(t, events[1].RootFields, "user.id", 0) @@ -97,8 +142,10 @@ func TestFailedLogins(t *testing.T) { // utmpdump: [7] [03788] [/0 ] [elastic ] [pts/0 ] [ ] [0.0.0.0 ] [2019-02-20T17:45:08,447344+0000] checkFieldValue(t, events[2].RootFields, "event.kind", "event") + checkFieldValue(t, events[0].RootFields, "event.category", "authentication") checkFieldValue(t, events[2].RootFields, "event.action", "user_login") checkFieldValue(t, events[2].RootFields, "event.outcome", "failure") + checkFieldValue(t, events[0].RootFields, "event.type", "authentication_failure") checkFieldValue(t, events[2].RootFields, "process.pid", 3788) checkFieldValue(t, events[2].RootFields, "source.ip", "0.0.0.0") checkFieldValue(t, events[2].RootFields, "user.name", "elastic") @@ -108,8 +155,10 @@ func TestFailedLogins(t *testing.T) { // utmpdump: [7] [03788] [/0 ] [UNKNOWN ] [pts/0 ] [ ] [0.0.0.0 ] [2019-02-20T17:45:15,765318+0000] checkFieldValue(t, events[3].RootFields, "event.kind", "event") + checkFieldValue(t, events[0].RootFields, "event.category", "authentication") checkFieldValue(t, events[3].RootFields, "event.action", "user_login") checkFieldValue(t, events[3].RootFields, "event.outcome", "failure") + checkFieldValue(t, events[0].RootFields, "event.type", "authentication_failure") checkFieldValue(t, events[3].RootFields, "process.pid", 3788) checkFieldValue(t, events[3].RootFields, "source.ip", "0.0.0.0") contains, err := events[3].RootFields.HasKey("user.id")