Skip to content

Commit

Permalink
Update aucoalesce to normalize events
Browse files Browse the repository at this point in the history
  • Loading branch information
andrewkroh committed Apr 27, 2017
1 parent c57b59c commit 666ff1c
Show file tree
Hide file tree
Showing 22 changed files with 3,734 additions and 214 deletions.
497 changes: 454 additions & 43 deletions aucoalesce/coalesce.go

Large diffs are not rendered by default.

143 changes: 109 additions & 34 deletions aucoalesce/coalesce_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,81 +21,152 @@ import (
"io/ioutil"
"os"
"path/filepath"
"sort"
"strings"
"testing"

"github.com/elastic/go-libaudit/auparse"
"github.com/stretchr/testify/assert"
"gopkg.in/yaml.v2"

"github.com/elastic/go-libaudit/auparse"
)

var update = flag.Bool("update", false, "update .golden files")

func TestCoalesceMessages(t *testing.T) {
files, err := filepath.Glob("testdata/*.log")
testFiles, err := filepath.Glob("testdata/*.yaml")
if err != nil {
t.Fatal("glob failed", err)
t.Fatal("glob", err)
}

for _, name := range files {
testCoalesce(t, name)
for _, file := range testFiles {
t.Run(file, func(t *testing.T) {
testCoalesceEvent(t, file)
})
}
}

func testCoalesce(t testing.TB, file string) {
msgs := readMessages(t, file)
type testEvent struct {
name string
messages []*auparse.AuditMessage
}

event, err := CoalesceMessages(msgs)
if err != nil {
t.Fatal(err)
type testEventOutput struct {
TestName string `json:"test_name"`
Event *Event `json:"event"`
Warnings []string `json:"warnings,omitempty"`
}

func newTestEventOutput(testName string, event *Event) testEventOutput {
var errs []string
for _, err := range event.Warnings {
errs = append(errs, err.Error())
}
return testEventOutput{testName, event, errs}
}

func testCoalesceEvent(t *testing.T, file string) {
testEvents := readEventsFromYAML(t, file)

var events []testEventOutput
for _, te := range testEvents {
event, err := CoalesceMessages(te.messages)
if err != nil {
t.Fatal(err)
}

events = append(events, newTestEventOutput(te.name, event))
}

// Update golden files on -update.
if *update {
if err = writeGoldenFile(file, event); err != nil {
if err := writeGoldenFile(file, events); err != nil {
t.Fatal(err)
}
}

// Compare events to golden events.
goldenEvent, err := readGoldenFile(file + ".golden")
goldenEvents, err := readGoldenFile(file)
if err != nil {
t.Fatal(err)
}

assert.EqualValues(t, goldenEvent, normalizeEvent(t, event), "file: %v", file)
// Compare events to golden events.
for i, observed := range events {
if i >= len(goldenEvents) {
t.Errorf("golden file has fewer events that there are test cases (run with -update): file=%v", file)
continue
}
expected := goldenEvents[i]

t.Run(testEvents[i].name, func(t *testing.T) {
assert.EqualValues(t, expected, normalizeEvent(t, observed), "file=%v test_case=%v", file, testEvents[i].name)
})
}
}

func readMessages(t testing.TB, name string) []*auparse.AuditMessage {
f, err := os.Open(name)
func readEventsFromYAML(t testing.TB, name string) []testEvent {
file, err := ioutil.ReadFile(name)
if err != nil {
t.Fatal(err)
}
defer f.Close()

var msgs []*auparse.AuditMessage
var data map[string]interface{}
if err := yaml.Unmarshal(file, &data); err != nil {
t.Fatal(err)
}

// Read logs and parse events.
s := bufio.NewScanner(bufio.NewReader(f))
for s.Scan() {
line := s.Text()
msg, err := auparse.ParseLogLine(line)
if err != nil {
t.Fatal("invalid message:", line)
tests, ok := data["tests"]
if !ok {
t.Fatal("failed to find 'tests' in yaml")
}

cases, ok := tests.(map[interface{}]interface{})
if !ok {
t.Fatalf("unexpected type %T for 'tests'", tests)
}

// Create test cases from YAML file.
var testEvents []testEvent
for name, messages := range cases {
var msgs []*auparse.AuditMessage

s := bufio.NewScanner(strings.NewReader(messages.(string)))
for s.Scan() {
line := s.Text()
msg, err := auparse.ParseLogLine(line)
if err != nil {
t.Fatal("invalid message:", line)
}

msgs = append(msgs, msg)
}

msgs = append(msgs, msg)
testEvents = append(testEvents, testEvent{
name: name.(string),
messages: msgs,
})
}

return msgs
// Sort the test cases by their key to ensure ordering.
sort.Slice(testEvents, func(i, j int) bool {
return testEvents[i].name < testEvents[j].name
})

return testEvents
}

func writeGoldenFile(sourceName string, event map[string]interface{}) error {
f, err := os.Create(sourceName + ".golden")
func writeGoldenFile(name string, events []testEventOutput) error {
if strings.HasSuffix(name, ".yaml") {
name = name[:len(name)-len(".yaml")]
}

f, err := os.Create(name + ".json.golden")
if err != nil {
return err
}
defer f.Close()

b, err := json.MarshalIndent(event, "", " ")
b, err := json.MarshalIndent(events, "", " ")
if err != nil {
return err
}
Expand All @@ -107,21 +178,25 @@ func writeGoldenFile(sourceName string, event map[string]interface{}) error {
return nil
}

func readGoldenFile(name string) (map[string]interface{}, error) {
data, err := ioutil.ReadFile(name)
func readGoldenFile(name string) ([]map[string]interface{}, error) {
if strings.HasSuffix(name, ".yaml") {
name = name[:len(name)-len(".yaml")]
}

data, err := ioutil.ReadFile(name + ".json.golden")
if err != nil {
return nil, err
}

var out map[string]interface{}
var out []map[string]interface{}
if err := json.Unmarshal(data, &out); err != nil {
return nil, err
}

return out, nil
}

func normalizeEvent(t testing.TB, event map[string]interface{}) map[string]interface{} {
func normalizeEvent(t testing.TB, event testEventOutput) map[string]interface{} {
b, err := json.Marshal(event)
if err != nil {
t.Fatal(err)
Expand Down
141 changes: 141 additions & 0 deletions aucoalesce/event_type.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
// Copyright 2017 Elasticsearch Inc.
//
// Licensed 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 aucoalesce

import (
. "github.com/elastic/go-libaudit/auparse"
)

// AuditEventType is a categorization of a simple or compound audit event.
type AuditEventType uint16

const (
EventTypeUnknown AuditEventType = iota
EventTypeUserspace
EventTypeSystemServices
EventTypeConfig
EventTypeTTY
EventTypeUserAccount
EventTypeUserLogin
EventTypeAuditDaemon
EventTypeMACDecision
EventTypeAnomoly
EventTypeIntegrity
EventTypeAnomolyResponse
EventTypeMAC
EventTypeCrypto
EventTypeVirt
EventTypeAuditRule
EventTypeDACDecision
EventTypeGroupChange
)

var auditEventTypeNames = map[AuditEventType]string{
EventTypeUnknown: "unknown",
EventTypeUserspace: "user-space",
EventTypeSystemServices: "system-services",
EventTypeConfig: "configuration",
EventTypeTTY: "TTY",
EventTypeUserAccount: "user-account",
EventTypeUserLogin: "user-login",
EventTypeAuditDaemon: "audit-daemon",
EventTypeMACDecision: "mac-decision",
EventTypeAnomoly: "anomoly",
EventTypeIntegrity: "integrity",
EventTypeAnomolyResponse: "anomaly-response",
EventTypeMAC: "mac",
EventTypeCrypto: "crypto",
EventTypeVirt: "virt",
EventTypeAuditRule: "audit-rule",
EventTypeDACDecision: "dac-decision",
EventTypeGroupChange: "group-change",
}

func (t AuditEventType) String() string {
name, found := auditEventTypeNames[t]
if found {
return name
}
return auditEventTypeNames[EventTypeUnknown]
}

func (t AuditEventType) MarshalText() (text []byte, err error) {
return []byte(t.String()), nil
}

func GetAuditEventType(t AuditMessageType) AuditEventType {
// Ported from: https://github.com/linux-audit/audit-userspace/blob/v2.7.5/auparse/normalize.c#L681
switch {
case t >= AUDIT_USER_AUTH && t <= AUDIT_USER_END,
t >= AUDIT_USER_CHAUTHTOK && t <= AUDIT_CRED_REFR,
t >= AUDIT_USER_LOGIN && t <= AUDIT_USER_LOGOUT,
t == AUDIT_GRP_AUTH:
return EventTypeUserLogin
case t >= AUDIT_ADD_USER && t <= AUDIT_DEL_GROUP,
t >= AUDIT_GRP_MGMT && t <= AUDIT_GRP_CHAUTHTOK,
t >= AUDIT_ACCT_LOCK && t <= AUDIT_ACCT_UNLOCK:
return EventTypeUserAccount
case t == AUDIT_KERNEL,
t >= AUDIT_SYSTEM_BOOT && t <= AUDIT_SERVICE_STOP:
return EventTypeSystemServices
case t == AUDIT_USYS_CONFIG,
t == AUDIT_CONFIG_CHANGE,
t == AUDIT_NETFILTER_CFG,
t >= AUDIT_FEATURE_CHANGE && t <= AUDIT_REPLACE:
return EventTypeConfig
case t == AUDIT_SECCOMP:
return EventTypeDACDecision
case t >= AUDIT_CHGRP_ID && t <= AUDIT_TRUSTED_APP,
t == AUDIT_USER_CMD,
t == AUDIT_CHUSER_ID:
return EventTypeUserspace
case t == AUDIT_USER_TTY, t == AUDIT_TTY:
return EventTypeTTY
case t >= AUDIT_DAEMON_START && t <= AUDIT_LAST_DAEMON:
return EventTypeAuditDaemon
case t == AUDIT_USER_SELINUX_ERR,
t == AUDIT_USER_AVC,
t >= AUDIT_APPARMOR_ALLOWED && t <= AUDIT_APPARMOR_DENIED,
t == AUDIT_APPARMOR_ERROR,
t >= AUDIT_AVC && t <= AUDIT_AVC_PATH:
return EventTypeMACDecision
case t >= AUDIT_INTEGRITY_DATA && t <= AUDIT_INTEGRITY_LAST_MSG,
t == AUDIT_ANOM_RBAC_INTEGRITY_FAIL:
return EventTypeIntegrity
case t >= AUDIT_ANOM_PROMISCUOUS && t <= AUDIT_LAST_KERN_ANOM_MSG,
t >= AUDIT_ANOM_LOGIN_FAILURES && t <= AUDIT_ANOM_RBAC_FAIL,
t >= AUDIT_ANOM_CRYPTO_FAIL && t <= AUDIT_LAST_ANOM_MSG:
return EventTypeAnomoly
case t >= AUDIT_RESP_ANOMALY && t <= AUDIT_LAST_ANOM_RESP:
return EventTypeAnomolyResponse
case t >= AUDIT_MAC_POLICY_LOAD && t <= AUDIT_LAST_SELINUX,
t >= AUDIT_AA && t <= AUDIT_APPARMOR_AUDIT,
t >= AUDIT_APPARMOR_HINT && t <= AUDIT_APPARMOR_STATUS,
t >= AUDIT_USER_ROLE_CHANGE && t <= AUDIT_LAST_USER_LSPP_MSG:
return EventTypeMAC
case t >= AUDIT_FIRST_KERN_CRYPTO_MSG && t <= AUDIT_LAST_KERN_CRYPTO_MSG,
t >= AUDIT_CRYPTO_TEST_USER && t <= AUDIT_LAST_CRYPTO_MSG:
return EventTypeCrypto
case t >= AUDIT_VIRT_CONTROL && t <= AUDIT_LAST_VIRT_MSG:
return EventTypeVirt
case t >= AUDIT_SYSCALL && t <= AUDIT_SOCKETCALL,
t >= AUDIT_SOCKADDR && t <= AUDIT_MQ_GETSETATTR,
t >= AUDIT_FD_PAIR && t <= AUDIT_OBJ_PID,
t >= AUDIT_BPRM_FCAPS && t <= AUDIT_NETFILTER_PKT:
return EventTypeAuditRule
default:
return EventTypeUnknown
}
}
Loading

0 comments on commit 666ff1c

Please sign in to comment.