Skip to content

Commit

Permalink
Allow querying for audit events in either an ascending or descending …
Browse files Browse the repository at this point in the history
  • Loading branch information
xacrimon authored Jul 12, 2021
1 parent 0b0f631 commit 01dd806
Show file tree
Hide file tree
Showing 26 changed files with 741 additions and 452 deletions.
6 changes: 4 additions & 2 deletions api/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -1365,14 +1365,15 @@ func (c *Client) DeleteAllNodes(ctx context.Context, namespace string) error {
}

// SearchEvents allows searching for events with a full pagination support.
func (c *Client) SearchEvents(ctx context.Context, fromUTC, toUTC time.Time, namespace string, eventTypes []string, limit int, startKey string) ([]events.AuditEvent, string, error) {
func (c *Client) SearchEvents(ctx context.Context, fromUTC, toUTC time.Time, namespace string, eventTypes []string, limit int, order types.EventOrder, startKey string) ([]events.AuditEvent, string, error) {
request := &proto.GetEventsRequest{
Namespace: namespace,
StartDate: fromUTC,
EndDate: toUTC,
EventTypes: eventTypes,
Limit: int32(limit),
StartKey: startKey,
Order: proto.Order(order),
}

response, err := c.grpc.GetEvents(ctx, request, c.callOpts...)
Expand All @@ -1393,12 +1394,13 @@ func (c *Client) SearchEvents(ctx context.Context, fromUTC, toUTC time.Time, nam
}

// SearchSessionEvents allows searching for session events with a full pagination support.
func (c *Client) SearchSessionEvents(ctx context.Context, fromUTC time.Time, toUTC time.Time, limit int, startKey string) ([]events.AuditEvent, string, error) {
func (c *Client) SearchSessionEvents(ctx context.Context, fromUTC time.Time, toUTC time.Time, limit int, order types.EventOrder, startKey string) ([]events.AuditEvent, string, error) {
request := &proto.GetSessionEventsRequest{
StartDate: fromUTC,
EndDate: toUTC,
Limit: int32(limit),
StartKey: startKey,
Order: proto.Order(order),
}

response, err := c.grpc.GetSessionEvents(ctx, request, c.callOpts...)
Expand Down
842 changes: 474 additions & 368 deletions api/client/proto/authservice.pb.go

Large diffs are not rendered by default.

13 changes: 13 additions & 0 deletions api/client/proto/authservice.proto
Original file line number Diff line number Diff line change
Expand Up @@ -826,6 +826,13 @@ message SingleUseUserCert {
}
}

// Order specifies any ordering of some objects as returned in regards to some aspect
// of said objects which may be trivially ordered such as a timestamp.
enum Order {
DESCENDING = 0;
ASCENDING = 1;
}

message GetEventsRequest {
// Namespace, if not set, defaults to 'default'
string Namespace = 1;
Expand All @@ -843,6 +850,9 @@ message GetEventsRequest {
// If the previous response had LastKey set then this should be
// set to its value. Otherwise leave empty.
string StartKey = 6;
// Order specifies an ascending or descending order of events.
// A value of 0 means a descending order and a value of 1 means an ascending order.
Order Order = 7;
}

message GetSessionEventsRequest {
Expand All @@ -858,6 +868,9 @@ message GetSessionEventsRequest {
// If the previous response had LastKey set then this should be
// set to its value. Otherwise leave empty.
string StartKey = 4;
// Order specifies an ascending or descending order of events.
// A value of 0 means a descending order and a value of 1 means an ascending order.
Order Order = 5;
}

message Events {
Expand Down
28 changes: 28 additions & 0 deletions api/types/order.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
Copyright 2021 Gravitational, 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 types

// EventOrder is an ordering of events, either ascending or descending.
type EventOrder int

// EventOrderAscending is an ascending event order.
// In essence, events go from oldest to newest.
const EventOrderAscending = 0

// EventOrderDescending is an descending event order.
// In this ordering events go from newest to oldest.
const EventOrderDescending = 1
2 changes: 1 addition & 1 deletion e
Submodule e updated from 4040d0 to d0361b
2 changes: 1 addition & 1 deletion integration/db_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -330,7 +330,7 @@ func waitForAuditEventTypeWithBackoff(t *testing.T, cli *auth.Server, startTime
t.Fatalf("failed to create linear backoff: %v", err)
}
for {
events, _, err := cli.SearchEvents(startTime, time.Now().Add(time.Hour), apidefaults.Namespace, []string{eventType}, 100, "")
events, _, err := cli.SearchEvents(startTime, time.Now().Add(time.Hour), apidefaults.Namespace, []string{eventType}, 100, types.EventOrderAscending, "")
if err != nil {
t.Fatalf("failed to call SearchEvents: %v", err)
}
Expand Down
2 changes: 1 addition & 1 deletion integration/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1513,7 +1513,7 @@ func twoClustersTunnel(t *testing.T, suite *integrationTestSuite, now time.Time,
for {
select {
case <-tickCh:
eventsInSite, _, err := site.SearchEvents(now, now.Add(1*time.Hour), apidefaults.Namespace, eventTypes, 0, "")
eventsInSite, _, err := site.SearchEvents(now, now.Add(1*time.Hour), apidefaults.Namespace, eventTypes, 0, types.EventOrderAscending, "")
if err != nil {
return trace.Wrap(err)
}
Expand Down
4 changes: 2 additions & 2 deletions lib/auth/apiserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -1814,7 +1814,7 @@ func (s *APIServer) searchEvents(auth ClientI, w http.ResponseWriter, r *http.Re
}

eventTypes := query[events.EventType]
eventsList, _, err := auth.SearchEvents(from, to, apidefaults.Namespace, eventTypes, limit, "")
eventsList, _, err := auth.SearchEvents(from, to, apidefaults.Namespace, eventTypes, limit, types.EventOrderDescending, "")
if err != nil {
return nil, trace.Wrap(err)
}
Expand Down Expand Up @@ -1854,7 +1854,7 @@ func (s *APIServer) searchSessionEvents(auth ClientI, w http.ResponseWriter, r *
}
}
// only pull back start and end events to build list of completed sessions
eventsList, _, err := auth.SearchSessionEvents(from, to, limit, "")
eventsList, _, err := auth.SearchSessionEvents(from, to, limit, types.EventOrderDescending, "")
if err != nil {
return nil, trace.Wrap(err)
}
Expand Down
8 changes: 4 additions & 4 deletions lib/auth/auth_with_roles.go
Original file line number Diff line number Diff line change
Expand Up @@ -3078,12 +3078,12 @@ func (a *ServerWithRoles) IsMFARequired(ctx context.Context, req *proto.IsMFAReq
}

// SearchEvents allows searching audit events with pagination support.
func (a *ServerWithRoles) SearchEvents(fromUTC, toUTC time.Time, namespace string, eventTypes []string, limit int, startKey string) (events []apievents.AuditEvent, lastKey string, err error) {
func (a *ServerWithRoles) SearchEvents(fromUTC, toUTC time.Time, namespace string, eventTypes []string, limit int, order types.EventOrder, startKey string) (events []apievents.AuditEvent, lastKey string, err error) {
if err := a.action(apidefaults.Namespace, types.KindEvent, types.VerbList); err != nil {
return nil, "", trace.Wrap(err)
}

events, lastKey, err = a.alog.SearchEvents(fromUTC, toUTC, namespace, eventTypes, limit, startKey)
events, lastKey, err = a.alog.SearchEvents(fromUTC, toUTC, namespace, eventTypes, limit, order, startKey)
if err != nil {
return nil, "", trace.Wrap(err)
}
Expand All @@ -3092,12 +3092,12 @@ func (a *ServerWithRoles) SearchEvents(fromUTC, toUTC time.Time, namespace strin
}

// SearchSessionEvents allows searching session audit events with pagination support.
func (a *ServerWithRoles) SearchSessionEvents(fromUTC, toUTC time.Time, limit int, startKey string) (events []apievents.AuditEvent, lastKey string, err error) {
func (a *ServerWithRoles) SearchSessionEvents(fromUTC, toUTC time.Time, limit int, order types.EventOrder, startKey string) (events []apievents.AuditEvent, lastKey string, err error) {
if err := a.action(apidefaults.Namespace, types.KindSession, types.VerbList); err != nil {
return nil, "", trace.Wrap(err)
}

events, lastKey, err = a.alog.SearchSessionEvents(fromUTC, toUTC, limit, startKey)
events, lastKey, err = a.alog.SearchSessionEvents(fromUTC, toUTC, limit, order, startKey)
if err != nil {
return nil, "", trace.Wrap(err)
}
Expand Down
8 changes: 4 additions & 4 deletions lib/auth/clt.go
Original file line number Diff line number Diff line change
Expand Up @@ -1448,8 +1448,8 @@ func (c *Client) GetSessionEvents(namespace string, sid session.ID, afterN int,
}

// SearchEvents allows searching for audit events with pagination support.
func (c *Client) SearchEvents(fromUTC, toUTC time.Time, namespace string, eventTypes []string, limit int, startKey string) ([]apievents.AuditEvent, string, error) {
events, lastKey, err := c.APIClient.SearchEvents(context.TODO(), fromUTC, toUTC, namespace, eventTypes, limit, startKey)
func (c *Client) SearchEvents(fromUTC, toUTC time.Time, namespace string, eventTypes []string, limit int, order types.EventOrder, startKey string) ([]apievents.AuditEvent, string, error) {
events, lastKey, err := c.APIClient.SearchEvents(context.TODO(), fromUTC, toUTC, namespace, eventTypes, limit, order, startKey)
if err != nil {
return nil, "", trace.Wrap(err)
}
Expand All @@ -1458,8 +1458,8 @@ func (c *Client) SearchEvents(fromUTC, toUTC time.Time, namespace string, eventT
}

// SearchSessionEvents returns session related events to find completed sessions.
func (c *Client) SearchSessionEvents(fromUTC time.Time, toUTC time.Time, limit int, startKey string) ([]apievents.AuditEvent, string, error) {
events, lastKey, err := c.APIClient.SearchSessionEvents(context.TODO(), fromUTC, toUTC, limit, startKey)
func (c *Client) SearchSessionEvents(fromUTC time.Time, toUTC time.Time, limit int, order types.EventOrder, startKey string) ([]apievents.AuditEvent, string, error) {
events, lastKey, err := c.APIClient.SearchSessionEvents(context.TODO(), fromUTC, toUTC, limit, order, startKey)
if err != nil {
return nil, "", trace.Wrap(err)
}
Expand Down
4 changes: 2 additions & 2 deletions lib/auth/grpcserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -2670,7 +2670,7 @@ func (g *GRPCServer) GetEvents(ctx context.Context, req *proto.GetEventsRequest)
return nil, trace.Wrap(err)
}

rawEvents, lastkey, err := auth.ServerWithRoles.SearchEvents(req.StartDate, req.EndDate, req.Namespace, req.EventTypes, int(req.Limit), req.StartKey)
rawEvents, lastkey, err := auth.ServerWithRoles.SearchEvents(req.StartDate, req.EndDate, req.Namespace, req.EventTypes, int(req.Limit), types.EventOrder(req.Order), req.StartKey)
if err != nil {
return nil, trace.Wrap(err)
}
Expand Down Expand Up @@ -2699,7 +2699,7 @@ func (g *GRPCServer) GetSessionEvents(ctx context.Context, req *proto.GetSession
return nil, trace.Wrap(err)
}

rawEvents, lastkey, err := auth.ServerWithRoles.SearchSessionEvents(req.StartDate, req.EndDate, int(req.Limit), req.StartKey)
rawEvents, lastkey, err := auth.ServerWithRoles.SearchSessionEvents(req.StartDate, req.EndDate, int(req.Limit), types.EventOrder(req.Order), req.StartKey)
if err != nil {
return nil, trace.Wrap(err)
}
Expand Down
4 changes: 2 additions & 2 deletions lib/auth/tls_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1349,14 +1349,14 @@ func (s *TLSSuite) TestSharedSessions(c *check.C) {
// try searching for events with no filter (empty query) - should get all 3 events:
to := time.Now().In(time.UTC).Add(time.Hour)
from := to.Add(-time.Hour * 2)
history, _, err := clt.SearchEvents(from, to, apidefaults.Namespace, nil, 0, "")
history, _, err := clt.SearchEvents(from, to, apidefaults.Namespace, nil, 0, types.EventOrderDescending, "")
c.Assert(err, check.IsNil)
c.Assert(history, check.NotNil)
// Extra event is the upload event
c.Assert(len(history), check.Equals, 5)

// try searching for only "session.end" events (real query)
history, _, err = clt.SearchEvents(from, to, apidefaults.Namespace, []string{events.SessionEndEvent}, 0, "")
history, _, err = clt.SearchEvents(from, to, apidefaults.Namespace, []string{events.SessionEndEvent}, 0, types.EventOrderDescending, "")
c.Assert(err, check.IsNil)
c.Assert(history, check.NotNil)
c.Assert(len(history), check.Equals, 2)
Expand Down
8 changes: 4 additions & 4 deletions lib/events/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"math"
"time"

"github.com/gravitational/teleport/api/types"
apievents "github.com/gravitational/teleport/api/types/events"
"github.com/gravitational/teleport/lib/session"

Expand Down Expand Up @@ -595,11 +596,10 @@ type IAuditLog interface {
// Event types to filter can be specified and pagination is handled by an iterator key that allows
// a query to be resumed.
//
// The only mandatory requirement is a date range (UTC). Results must always
// show up sorted by date (newest first)
// The only mandatory requirement is a date range (UTC).
//
// This function may never return more than 1 MiB of event data.
SearchEvents(fromUTC, toUTC time.Time, namespace string, eventTypes []string, limit int, startKey string) ([]apievents.AuditEvent, string, error)
SearchEvents(fromUTC, toUTC time.Time, namespace string, eventTypes []string, limit int, order types.EventOrder, startKey string) ([]apievents.AuditEvent, string, error)

// SearchSessionEvents is a flexible way to find session events.
// Only session events are returned by this function.
Expand All @@ -609,7 +609,7 @@ type IAuditLog interface {
// a query to be resumed.
//
// This function may never return more than 1 MiB of event data.
SearchSessionEvents(fromUTC time.Time, toUTC time.Time, limit int, startKey string) ([]apievents.AuditEvent, string, error)
SearchSessionEvents(fromUTC time.Time, toUTC time.Time, limit int, order types.EventOrder, startKey string) ([]apievents.AuditEvent, string, error)

// WaitForDelivery waits for resources to be released and outstanding requests to
// complete after calling Close method
Expand Down
17 changes: 9 additions & 8 deletions lib/events/auditlog.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import (

"github.com/gravitational/teleport"
apidefaults "github.com/gravitational/teleport/api/defaults"
"github.com/gravitational/teleport/api/types"
apievents "github.com/gravitational/teleport/api/types/events"
"github.com/gravitational/teleport/lib/defaults"
"github.com/gravitational/teleport/lib/session"
Expand Down Expand Up @@ -1017,7 +1018,7 @@ func (l *AuditLog) auditDirs() ([]string, error) {
return out, nil
}

func (l *AuditLog) SearchEvents(fromUTC, toUTC time.Time, namespace string, eventType []string, limit int, startKey string) ([]apievents.AuditEvent, string, error) {
func (l *AuditLog) SearchEvents(fromUTC, toUTC time.Time, namespace string, eventType []string, limit int, order types.EventOrder, startKey string) ([]apievents.AuditEvent, string, error) {
g := l.log.WithFields(log.Fields{"namespace": namespace, "eventType": eventType, "limit": limit})
g.Debugf("SearchEvents(%v, %v)", fromUTC, toUTC)
if limit <= 0 {
Expand All @@ -1027,18 +1028,18 @@ func (l *AuditLog) SearchEvents(fromUTC, toUTC time.Time, namespace string, even
return nil, "", trace.BadParameter("limit %v exceeds max iteration limit %v", limit, defaults.MaxIterationLimit)
}
if l.ExternalLog != nil {
return l.ExternalLog.SearchEvents(fromUTC, toUTC, namespace, eventType, limit, startKey)
return l.ExternalLog.SearchEvents(fromUTC, toUTC, namespace, eventType, limit, order, startKey)
}
return l.localLog.SearchEvents(fromUTC, toUTC, namespace, eventType, limit, startKey)
return l.localLog.SearchEvents(fromUTC, toUTC, namespace, eventType, limit, order, startKey)
}

func (l *AuditLog) SearchSessionEvents(fromUTC, toUTC time.Time, limit int, startKey string) ([]apievents.AuditEvent, string, error) {
func (l *AuditLog) SearchSessionEvents(fromUTC, toUTC time.Time, limit int, order types.EventOrder, startKey string) ([]apievents.AuditEvent, string, error) {
l.log.Debugf("SearchSessionEvents(%v, %v, %v)", fromUTC, toUTC, limit)

if l.ExternalLog != nil {
return l.ExternalLog.SearchSessionEvents(fromUTC, toUTC, limit, startKey)
return l.ExternalLog.SearchSessionEvents(fromUTC, toUTC, limit, order, startKey)
}
return l.localLog.SearchSessionEvents(fromUTC, toUTC, limit, startKey)
return l.localLog.SearchSessionEvents(fromUTC, toUTC, limit, order, startKey)
}

// getLocalLog returns the local (file based) audit log.
Expand Down Expand Up @@ -1220,11 +1221,11 @@ func (a *closedLogger) GetSessionEvents(namespace string, sid session.ID, after
return nil, trace.NotImplemented(loggerClosedMessage)
}

func (a *closedLogger) SearchEvents(fromUTC, toUTC time.Time, namespace string, eventType []string, limit int, startKey string) ([]apievents.AuditEvent, string, error) {
func (a *closedLogger) SearchEvents(fromUTC, toUTC time.Time, namespace string, eventType []string, limit int, order types.EventOrder, startKey string) ([]apievents.AuditEvent, string, error) {
return nil, "", trace.NotImplemented(loggerClosedMessage)
}

func (a *closedLogger) SearchSessionEvents(fromUTC time.Time, toUTC time.Time, limit int, startKey string) ([]apievents.AuditEvent, string, error) {
func (a *closedLogger) SearchSessionEvents(fromUTC time.Time, toUTC time.Time, limit int, order types.EventOrder, startKey string) ([]apievents.AuditEvent, string, error) {
return nil, "", trace.NotImplemented(loggerClosedMessage)
}

Expand Down
5 changes: 3 additions & 2 deletions lib/events/auditlog_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import (

"github.com/gravitational/teleport"
apidefaults "github.com/gravitational/teleport/api/defaults"
"github.com/gravitational/teleport/api/types"
"github.com/gravitational/teleport/api/types/events"
apievents "github.com/gravitational/teleport/api/types/events"
"github.com/gravitational/teleport/lib/fixtures"
Expand Down Expand Up @@ -297,7 +298,7 @@ func (a *AuditTestSuite) TestSessionRecordingOff(c *check.C) {
upload(c, uploadDir, fakeClock, alog)

// get all events from the audit log, should have two session event and one upload event
found, _, err := alog.SearchEvents(now.Add(-time.Hour), now.Add(time.Hour), apidefaults.Namespace, nil, 0, "")
found, _, err := alog.SearchEvents(now.Add(-time.Hour), now.Add(time.Hour), apidefaults.Namespace, nil, 0, types.EventOrderAscending, "")
c.Assert(err, check.IsNil)
c.Assert(found, check.HasLen, 3)
eventA, okA := found[0].(*apievents.SessionStart)
Expand Down Expand Up @@ -382,7 +383,7 @@ func (a *AuditTestSuite) TestLogRotation(c *check.C) {
c.Assert(err, check.IsNil)
c.Assert(string(bytes), check.Equals, string(contents))

found, _, err := alog.SearchEvents(now.Add(-time.Hour), now.Add(time.Hour), apidefaults.Namespace, nil, 0, "")
found, _, err := alog.SearchEvents(now.Add(-time.Hour), now.Add(time.Hour), apidefaults.Namespace, nil, 0, types.EventOrderAscending, "")
c.Assert(err, check.IsNil)
c.Assert(found, check.HasLen, 1)
}
Expand Down
5 changes: 3 additions & 2 deletions lib/events/discard.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"context"
"time"

"github.com/gravitational/teleport/api/types"
apievents "github.com/gravitational/teleport/api/types/events"
"github.com/gravitational/teleport/lib/session"
)
Expand Down Expand Up @@ -52,10 +53,10 @@ func (d *DiscardAuditLog) GetSessionChunk(namespace string, sid session.ID, offs
func (d *DiscardAuditLog) GetSessionEvents(namespace string, sid session.ID, after int, includePrintEvents bool) ([]EventFields, error) {
return make([]EventFields, 0), nil
}
func (d *DiscardAuditLog) SearchEvents(fromUTC, toUTC time.Time, namespace string, eventType []string, limit int, startKey string) ([]apievents.AuditEvent, string, error) {
func (d *DiscardAuditLog) SearchEvents(fromUTC, toUTC time.Time, namespace string, eventType []string, limit int, order types.EventOrder, startKey string) ([]apievents.AuditEvent, string, error) {
return make([]apievents.AuditEvent, 0), "", nil
}
func (d *DiscardAuditLog) SearchSessionEvents(fromUTC time.Time, toUTC time.Time, limit int, startKey string) ([]apievents.AuditEvent, string, error) {
func (d *DiscardAuditLog) SearchSessionEvents(fromUTC time.Time, toUTC time.Time, limit int, order types.EventOrder, startKey string) ([]apievents.AuditEvent, string, error) {
return make([]apievents.AuditEvent, 0), "", nil
}

Expand Down
Loading

0 comments on commit 01dd806

Please sign in to comment.