Skip to content

Commit

Permalink
Update audit events with additional fields. (#2645)
Browse files Browse the repository at this point in the history
  • Loading branch information
r0mant authored Apr 16, 2019
1 parent fbd6b38 commit b95d51f
Show file tree
Hide file tree
Showing 35 changed files with 526 additions and 102 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ endif

.PHONY: test-package
test-package: remove-temp-files
go test -v -test.parallel=0 ./$(p)
go test -v ./$(p)

.PHONY: test-grep-package
test-grep-package: remove-temp-files
Expand Down
2 changes: 1 addition & 1 deletion e
Submodule e updated from 6378c7 to 4b3c87
20 changes: 17 additions & 3 deletions lib/auth/apiserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -1787,17 +1787,31 @@ func (s *APIServer) searchSessionEvents(auth ClientI, w http.ResponseWriter, r *
}

type auditEventReq struct {
Type string `json:"type"`
// Event is the event that's being emitted.
Event events.Event `json:"event"`
// Fields is the additional event fields.
Fields events.EventFields `json:"fields"`
// Type is the event type.
//
// This field is obsolete and kept for backwards compatibility.
Type string `json:"type"`
}

// HTTP POST /:version/events
func (s *APIServer) emitAuditEvent(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
var req auditEventReq
if err := httplib.ReadJSON(r, &req); err != nil {
err := httplib.ReadJSON(r, &req)
if err != nil {
return nil, trace.Wrap(err)
}
if err := auth.EmitAuditEvent(req.Type, req.Fields); err != nil {
// For backwards compatibility, check if the full event struct has
// been sent in the request or just the event type.
if req.Event.Name != "" {
err = auth.EmitAuditEvent(req.Event, req.Fields)
} else {
err = auth.EmitAuditEvent(events.Event{Name: req.Type}, req.Fields)
}
if err != nil {
return nil, trace.Wrap(err)
}
return message("ok"), nil
Expand Down
4 changes: 2 additions & 2 deletions lib/auth/auth_with_roles.go
Original file line number Diff line number Diff line change
Expand Up @@ -905,14 +905,14 @@ func (a *AuthWithRoles) ValidateGithubAuthCallback(q url.Values) (*GithubAuthRes
return a.authServer.ValidateGithubAuthCallback(q)
}

func (a *AuthWithRoles) EmitAuditEvent(eventType string, fields events.EventFields) error {
func (a *AuthWithRoles) EmitAuditEvent(event events.Event, fields events.EventFields) error {
if err := a.action(defaults.Namespace, services.KindEvent, services.VerbCreate); err != nil {
return trace.Wrap(err)
}
if err := a.action(defaults.Namespace, services.KindEvent, services.VerbUpdate); err != nil {
return trace.Wrap(err)
}
return a.alog.EmitAuditEvent(eventType, fields)
return a.alog.EmitAuditEvent(event, fields)
}

func (a *AuthWithRoles) PostSessionSlice(slice events.SessionSlice) error {
Expand Down
8 changes: 5 additions & 3 deletions lib/auth/clt.go
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ func NewClient(addr string, dialer Dialer, params ...roundtrip.ClientParam) (*Cl
dialer = net.Dial
}
transport := &http.Transport{
Dial: dialer,
Dial: dialer,
ResponseHeaderTimeout: defaults.DefaultDialTimeout,
}
params = append(params,
Expand Down Expand Up @@ -1642,10 +1642,12 @@ func (c *Client) ValidateGithubAuthCallback(q url.Values) (*GithubAuthResponse,
}

// EmitAuditEvent sends an auditable event to the auth server (part of evets.IAuditLog interface)
func (c *Client) EmitAuditEvent(eventType string, fields events.EventFields) error {
func (c *Client) EmitAuditEvent(event events.Event, fields events.EventFields) error {
_, err := c.PostJSON(c.Endpoint("events"), &auditEventReq{
Type: eventType,
Event: event,
Fields: fields,
// Send "type" as well for backwards compatibility.
Type: event.Name,
})
if err != nil {
return trace.Wrap(err)
Expand Down
6 changes: 3 additions & 3 deletions lib/auth/github.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,13 +84,13 @@ type GithubAuthResponse struct {
func (a *AuthServer) ValidateGithubAuthCallback(q url.Values) (*GithubAuthResponse, error) {
re, err := a.validateGithubAuthCallback(q)
if err != nil {
a.EmitAuditEvent(events.UserLoginEvent, events.EventFields{
a.EmitAuditEvent(events.UserSSOLoginFailure, events.EventFields{
events.LoginMethod: events.LoginMethodGithub,
events.AuthAttemptSuccess: false,
events.AuthAttemptErr: err.Error(),
})
} else {
a.EmitAuditEvent(events.UserLoginEvent, events.EventFields{
a.EmitAuditEvent(events.UserSSOLogin, events.EventFields{
events.EventUser: re.Username,
events.AuthAttemptSuccess: true,
events.LoginMethod: events.LoginMethodGithub,
Expand Down Expand Up @@ -502,7 +502,7 @@ func (c *githubAPIClient) getTeams() ([]teamResponse, error) {

// Print warning to Teleport logs as well as the Audit Log.
log.Warnf(warningMessage)
c.authServer.EmitAuditEvent(events.UserLoginEvent, events.EventFields{
c.authServer.EmitAuditEvent(events.UserSSOLoginFailure, events.EventFields{
events.LoginMethod: events.LoginMethodGithub,
events.AuthAttemptMessage: warningMessage,
})
Expand Down
4 changes: 2 additions & 2 deletions lib/auth/methods.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,14 +85,14 @@ type SessionCreds struct {
func (s *AuthServer) AuthenticateUser(req AuthenticateUserRequest) error {
err := s.authenticateUser(req)
if err != nil {
s.EmitAuditEvent(events.UserLoginEvent, events.EventFields{
s.EmitAuditEvent(events.UserLocalLoginFailure, events.EventFields{
events.EventUser: req.Username,
events.LoginMethod: events.LoginMethodLocal,
events.AuthAttemptSuccess: false,
events.AuthAttemptErr: err.Error(),
})
} else {
s.EmitAuditEvent(events.UserLoginEvent, events.EventFields{
s.EmitAuditEvent(events.UserLocalLogin, events.EventFields{
events.EventUser: req.Username,
events.LoginMethod: events.LoginMethodLocal,
events.AuthAttemptSuccess: true,
Expand Down
6 changes: 3 additions & 3 deletions lib/auth/oidc.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,14 +128,14 @@ func (s *AuthServer) CreateOIDCAuthRequest(req services.OIDCAuthRequest) (*servi
func (a *AuthServer) ValidateOIDCAuthCallback(q url.Values) (*OIDCAuthResponse, error) {
re, err := a.validateOIDCAuthCallback(q)
if err != nil {
a.EmitAuditEvent(events.UserLoginEvent, events.EventFields{
a.EmitAuditEvent(events.UserSSOLoginFailure, events.EventFields{
events.LoginMethod: events.LoginMethodOIDC,
events.AuthAttemptSuccess: false,
// log the original internal error in audit log
events.AuthAttemptErr: trace.Unwrap(err).Error(),
})
} else {
a.EmitAuditEvent(events.UserLoginEvent, events.EventFields{
a.EmitAuditEvent(events.UserSSOLogin, events.EventFields{
events.EventUser: re.Username,
events.AuthAttemptSuccess: true,
events.LoginMethod: events.LoginMethodOIDC,
Expand Down Expand Up @@ -549,7 +549,7 @@ collect:

// Print warning to Teleport logs as well as the Audit Log.
log.Warnf(warningMessage)
g.auditLog.EmitAuditEvent(events.UserLoginEvent, events.EventFields{
g.auditLog.EmitAuditEvent(events.UserSSOLoginFailure, events.EventFields{
events.LoginMethod: events.LoginMethodOIDC,
events.AuthAttemptMessage: warningMessage,
})
Expand Down
4 changes: 2 additions & 2 deletions lib/auth/saml.go
Original file line number Diff line number Diff line change
Expand Up @@ -278,13 +278,13 @@ type SAMLAuthResponse struct {
func (a *AuthServer) ValidateSAMLResponse(samlResponse string) (*SAMLAuthResponse, error) {
re, err := a.validateSAMLResponse(samlResponse)
if err != nil {
a.EmitAuditEvent(events.UserLoginEvent, events.EventFields{
a.EmitAuditEvent(events.UserSSOLoginFailure, events.EventFields{
events.LoginMethod: events.LoginMethodSAML,
events.AuthAttemptSuccess: false,
events.AuthAttemptErr: err.Error(),
})
} else {
a.EmitAuditEvent(events.UserLoginEvent, events.EventFields{
a.EmitAuditEvent(events.UserSSOLogin, events.EventFields{
events.EventUser: re.Username,
events.AuthAttemptSuccess: true,
events.LoginMethod: events.LoginMethodSAML,
Expand Down
42 changes: 35 additions & 7 deletions lib/events/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@ const (
EventType = "event"
// EventID is a unique event identifier
EventID = "uid"
// EventCode is a code that uniquely identifies a particular event type
EventCode = "code"
// EventSeverity contains event severity (info, warning, error)
EventSeverity = "severity"
// EventMessage contains human-friendly event message
EventMessage = "message"
// EventTime is event time
EventTime = "time"
// EventLogin is OS login
Expand All @@ -49,6 +55,13 @@ const (
// EventCursor is an event ID (used as cursor value for enumeration, not stored)
EventCursor = "id"

// SeverityInfo represents severity for informational events.
SeverityInfo = "info"
// SeverityWarning represents severity for events that need attention.
SeverityWarning = "warning"
// SeverityError represents severity for events caused by an error.
SeverityError = "error"

// EventIndex is an event index as received from the logging server
EventIndex = "ei"

Expand Down Expand Up @@ -137,12 +150,12 @@ const (
AuthAttemptMessage = "message"

// SCPEvent means data transfer that occurred on the server
SCPEvent = "scp"
SCPPath = "path"
SCPLengh = "len"
SCPAction = "action"
SCPUpload = "upload"
SCPDownload = "download"
SCPEvent = "scp"
SCPPath = "path"
SCPLengh = "len"
SCPAction = "action"
SCPActionUpload = "upload"
SCPActionDownload = "download"

// ResizeEvent means that some user resized PTY on the client
ResizeEvent = "resize"
Expand Down Expand Up @@ -176,7 +189,7 @@ type IAuditLog interface {
io.Closer

// EmitAuditEvent emits audit event
EmitAuditEvent(eventType string, fields EventFields) error
EmitAuditEvent(Event, EventFields) error

// DELETE IN: 2.7.0
// This method is no longer necessary as nodes and proxies >= 2.7.0
Expand Down Expand Up @@ -244,6 +257,21 @@ func (f EventFields) GetID() string {
return f.GetString(EventID)
}

// GetCode returns the event code
func (f EventFields) GetCode() string {
return f.GetString(EventCode)
}

// GetTimestamp returns the event timestamp (when it was emitted)
func (f EventFields) GetTimestamp() time.Time {
return f.GetTime(EventTime)
}

// GetMessage returns the event user message
func (f EventFields) GetMessage() string {
return f.GetString(EventMessage)
}

// GetString returns a string representation of a logged field
func (f EventFields) GetString(key string) string {
val, found := f[key]
Expand Down
43 changes: 37 additions & 6 deletions lib/events/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,20 @@ limitations under the License.

package events

import "gopkg.in/check.v1"
import "time"
import (
"time"

type AuditApiTestSuite struct {
}
"github.com/gravitational/teleport/lib/fixtures"
"github.com/gravitational/teleport/lib/utils"
"github.com/jonboulle/clockwork"
"gopkg.in/check.v1"
)

type AuditAPITestSuite struct{}

var _ = check.Suite(&AuditApiTestSuite{})
var _ = check.Suite(&AuditAPITestSuite{})

func (a *AuditApiTestSuite) TestFields(c *check.C) {
func (a *AuditAPITestSuite) TestFields(c *check.C) {
now := time.Now().Round(time.Minute)

f := EventFields{
Expand All @@ -48,3 +53,29 @@ func (a *AuditApiTestSuite) TestFields(c *check.C) {
t := f.GetTime("time")
c.Assert(t, check.Equals, now)
}

func (a *AuditAPITestSuite) TestUpdateFields(c *check.C) {
event := Event{
Name: "test.event",
Code: "TEST0001I",
Severity: SeverityInfo,
Message: "User {{.user}} logged in via {{.method}}",
}
fields := EventFields{
EventUser: "[email protected]",
LoginMethod: LoginMethodOIDC,
}
c.Assert(UpdateEventFields(event, fields, clockwork.NewFakeClock(), utils.NewFakeUID()), check.IsNil)

// Check the fields have been updated appropriately.
c.Assert(fields, check.DeepEquals, EventFields{
EventType: event.Name,
EventID: fixtures.UUID,
EventCode: event.Code,
EventTime: time.Date(1984, time.April, 4, 0, 0, 0, 0, time.UTC),
EventSeverity: SeverityInfo,
EventUser: "[email protected]",
EventMessage: "User [email protected] logged in via oidc",
LoginMethod: LoginMethodOIDC,
})
}
10 changes: 5 additions & 5 deletions lib/events/auditlog.go
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,7 @@ func (l *AuditLog) UploadSessionRecording(r SessionRecording) error {
return trace.Wrap(err)
}
l.WithFields(log.Fields{"duration": time.Now().Sub(start), "session-id": r.SessionID}).Debugf("Session upload completed.")
return l.EmitAuditEvent(SessionUploadEvent, EventFields{
return l.EmitAuditEvent(SessionUpload, EventFields{
SessionEventID: string(r.SessionID),
URL: url,
EventIndex: math.MaxInt32,
Expand Down Expand Up @@ -349,7 +349,7 @@ func (l *AuditLog) processSlice(sl SessionLogger, slice *SessionSlice) error {
if err != nil {
return trace.Wrap(err)
}
if err := l.EmitAuditEvent(chunk.EventType, fields); err != nil {
if err := l.EmitAuditEvent(Event{Name: chunk.EventType}, fields); err != nil {
return trace.Wrap(err)
}
}
Expand Down Expand Up @@ -842,11 +842,11 @@ func (l *AuditLog) fetchSessionEvents(fileName string, afterN int) ([]EventField
}

// EmitAuditEvent adds a new event to the log. Part of auth.IAuditLog interface.
func (l *AuditLog) EmitAuditEvent(eventType string, fields EventFields) error {
func (l *AuditLog) EmitAuditEvent(event Event, fields EventFields) error {
if l.ExternalLog != nil {
return l.ExternalLog.EmitAuditEvent(eventType, fields)
return l.ExternalLog.EmitAuditEvent(event, fields)
}
return l.localLog.EmitAuditEvent(eventType, fields)
return l.localLog.EmitAuditEvent(event, fields)
}

// emitEvent emits event for test purposes
Expand Down
4 changes: 2 additions & 2 deletions lib/events/auditlog_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,7 @@ func (a *AuditTestSuite) TestBasicLogging(c *check.C) {
alog.Clock = clockwork.NewFakeClockAt(now)

// emit regular event:
err = alog.EmitAuditEvent("user.joined", EventFields{"apples?": "yes"})
err = alog.EmitAuditEvent(Event{Name: "user.joined"}, EventFields{"apples?": "yes"})
c.Assert(err, check.IsNil)
logfile := alog.localLog.file.Name()
c.Assert(alog.Close(), check.IsNil)
Expand Down Expand Up @@ -348,7 +348,7 @@ func (a *AuditTestSuite) TestLogRotation(c *check.C) {
clock.Advance(duration)

// emit regular event:
err = alog.EmitAuditEvent("user.joined", EventFields{"apples?": "yes"})
err = alog.EmitAuditEvent(Event{Name: "user.joined"}, EventFields{"apples?": "yes"})
c.Assert(err, check.IsNil)
logfile := alog.localLog.file.Name()

Expand Down
Loading

0 comments on commit b95d51f

Please sign in to comment.