Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[v14] Add client remote address to some audit events #36567

Merged
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
75 changes: 75 additions & 0 deletions api/proto/teleport/legacy/types/events/events.proto
Expand Up @@ -1280,6 +1280,13 @@ message UserCreate {

// Connector is the connector used to create the user.
string Connector = 5 [(gogoproto.jsontag) = "connector"];

// ConnectionMetadata holds information about the connection
ConnectionMetadata Connection = 6 [
(gogoproto.nullable) = false,
(gogoproto.embed) = true,
(gogoproto.jsontag) = ""
];
}

// UserDelete is emitted when a user gets deleted
Expand All @@ -1304,6 +1311,13 @@ message UserDelete {
(gogoproto.embed) = true,
(gogoproto.jsontag) = ""
];

// ConnectionMetadata holds information about the connection
ConnectionMetadata Connection = 4 [
(gogoproto.nullable) = false,
(gogoproto.embed) = true,
(gogoproto.jsontag) = ""
];
}

// UserPasswordChange is emitted when the user changes their own password.
Expand All @@ -1321,6 +1335,13 @@ message UserPasswordChange {
(gogoproto.embed) = true,
(gogoproto.jsontag) = ""
];

// ConnectionMetadata holds information about the connection
ConnectionMetadata Connection = 3 [
(gogoproto.nullable) = false,
(gogoproto.embed) = true,
(gogoproto.jsontag) = ""
];
}

// AccessRequestCreate is emitted when access request has been created or updated
Expand Down Expand Up @@ -1900,6 +1921,13 @@ message RoleCreate {
(gogoproto.embed) = true,
(gogoproto.jsontag) = ""
];

// ConnectionMetadata holds information about the connection
ConnectionMetadata Connection = 4 [
(gogoproto.nullable) = false,
(gogoproto.embed) = true,
(gogoproto.jsontag) = ""
];
}

// RoleDelete is emitted when a role is deleted
Expand All @@ -1924,6 +1952,13 @@ message RoleDelete {
(gogoproto.embed) = true,
(gogoproto.jsontag) = ""
];

// ConnectionMetadata holds information about the connection
ConnectionMetadata Connection = 4 [
(gogoproto.nullable) = false,
(gogoproto.embed) = true,
(gogoproto.jsontag) = ""
];
}

// TrustedClusterCreate is the event for creating a trusted cluster.
Expand All @@ -1948,6 +1983,13 @@ message TrustedClusterCreate {
(gogoproto.embed) = true,
(gogoproto.jsontag) = ""
];

// ConnectionMetadata holds information about the connection
ConnectionMetadata Connection = 4 [
(gogoproto.nullable) = false,
(gogoproto.embed) = true,
(gogoproto.jsontag) = ""
];
}

// TrustedClusterDelete is the event for removing a trusted cluster.
Expand All @@ -1972,6 +2014,13 @@ message TrustedClusterDelete {
(gogoproto.embed) = true,
(gogoproto.jsontag) = ""
];

// ConnectionMetadata holds information about the connection
ConnectionMetadata Connection = 4 [
(gogoproto.nullable) = false,
(gogoproto.embed) = true,
(gogoproto.jsontag) = ""
];
}

// ProvisionTokenCreate event is emitted when a provisioning token (a.k.a. join
Expand Down Expand Up @@ -2055,6 +2104,13 @@ message GithubConnectorCreate {
(gogoproto.embed) = true,
(gogoproto.jsontag) = ""
];

// ConnectionMetadata holds information about the connection
ConnectionMetadata Connection = 4 [
(gogoproto.nullable) = false,
(gogoproto.embed) = true,
(gogoproto.jsontag) = ""
];
}

// GithubConnectorDelete fires when a Github connector is deleted.
Expand All @@ -2079,6 +2135,13 @@ message GithubConnectorDelete {
(gogoproto.embed) = true,
(gogoproto.jsontag) = ""
];

// ConnectionMetadata holds information about the connection
ConnectionMetadata Connection = 4 [
(gogoproto.nullable) = false,
(gogoproto.embed) = true,
(gogoproto.jsontag) = ""
];
}

// OIDCConnectorCreate fires when OIDC connector is created/updated.
Expand Down Expand Up @@ -2988,6 +3051,12 @@ message MFADeviceAdd {
(gogoproto.embed) = true,
(gogoproto.jsontag) = ""
];
// ConnectionMetadata holds information about the connection
ConnectionMetadata Connection = 4 [
(gogoproto.nullable) = false,
(gogoproto.embed) = true,
(gogoproto.jsontag) = ""
];
}

// MFADeviceDelete is emitted when a user deletes an MFA device.
Expand All @@ -3010,6 +3079,12 @@ message MFADeviceDelete {
(gogoproto.embed) = true,
(gogoproto.jsontag) = ""
];
// ConnectionMetadata holds information about the connection
ConnectionMetadata Connection = 4 [
(gogoproto.nullable) = false,
(gogoproto.embed) = true,
(gogoproto.jsontag) = ""
];
}

// BillingInformationUpdate is emitted when a user updates the billing information.
Expand Down
2,319 changes: 1,419 additions & 900 deletions api/types/events/events.pb.go

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions lib/auth/access.go
Expand Up @@ -46,6 +46,7 @@ func (a *Server) UpsertRole(ctx context.Context, role types.Role) error {
ResourceMetadata: apievents.ResourceMetadata{
Name: role.GetName(),
},
ConnectionMetadata: authz.ConnectionMetadata(ctx),
}); err != nil {
log.WithError(err).Warnf("Failed to emit role create event.")
}
Expand Down Expand Up @@ -132,6 +133,7 @@ func (a *Server) DeleteRole(ctx context.Context, name string) error {
ResourceMetadata: apievents.ResourceMetadata{
Name: name,
},
ConnectionMetadata: authz.ConnectionMetadata(ctx),
}); err != nil {
log.WithError(err).Warnf("Failed to emit role delete event.")
}
Expand Down
17 changes: 12 additions & 5 deletions lib/auth/access_test.go
Expand Up @@ -18,6 +18,7 @@ package auth

import (
"context"
"net"
"testing"
"time"

Expand All @@ -28,12 +29,14 @@ import (
"github.com/gravitational/teleport/api/types/accesslist"
apievents "github.com/gravitational/teleport/api/types/events"
"github.com/gravitational/teleport/api/types/header"
"github.com/gravitational/teleport/lib/authz"
"github.com/gravitational/teleport/lib/events"
)

func TestUpsertDeleteRoleEventsEmitted(t *testing.T) {
t.Parallel()
ctx := context.Background()
clientAddr := &net.TCPAddr{IP: net.IPv4(10, 255, 0, 0)}
ctx := authz.ContextWithClientSrcAddr(context.Background(), clientAddr)
p, err := newTestPack(ctx, t.TempDir())
require.NoError(t, err)

Expand All @@ -47,8 +50,10 @@ func TestUpsertDeleteRoleEventsEmitted(t *testing.T) {
// Creating a role should emit a RoleCreatedEvent.
err = p.a.UpsertRole(ctx, role)
require.NoError(t, err)
require.Equal(t, p.mockEmitter.LastEvent().GetType(), events.RoleCreatedEvent)
require.Equal(t, p.mockEmitter.LastEvent().(*apievents.RoleCreate).Name, role.GetName())
require.Equal(t, events.RoleCreatedEvent, p.mockEmitter.LastEvent().GetType())
createEvt := p.mockEmitter.LastEvent().(*apievents.RoleCreate)
require.Equal(t, role.GetName(), createEvt.Name)
require.Equal(t, clientAddr.String(), createEvt.ConnectionMetadata.RemoteAddr)
p.mockEmitter.Reset()

// Updating a role should emit a RoleCreatedEvent.
Expand All @@ -61,8 +66,10 @@ func TestUpsertDeleteRoleEventsEmitted(t *testing.T) {
// Deleting a role should emit a RoleDeletedEvent.
err = p.a.DeleteRole(ctx, role.GetName())
require.NoError(t, err)
require.Equal(t, p.mockEmitter.LastEvent().GetType(), events.RoleDeletedEvent)
require.Equal(t, p.mockEmitter.LastEvent().(*apievents.RoleDelete).Name, role.GetName())
require.Equal(t, events.RoleDeletedEvent, p.mockEmitter.LastEvent().GetType())
deleteEvt := p.mockEmitter.LastEvent().(*apievents.RoleDelete)
require.Equal(t, role.GetName(), deleteEvt.Name)
require.Equal(t, clientAddr.String(), deleteEvt.ConnectionMetadata.RemoteAddr)
p.mockEmitter.Reset()

// When deleting a nonexistent role, no event should be emitted.
Expand Down
10 changes: 6 additions & 4 deletions lib/auth/auth.go
Expand Up @@ -3223,8 +3223,9 @@ func (a *Server) deleteMFADeviceSafely(ctx context.Context, user, deviceName str
Code: events.MFADeviceDeleteEventCode,
ClusterName: clusterName.GetClusterName(),
},
UserMetadata: authz.ClientUserMetadataWithUser(ctx, user),
MFADeviceMetadata: mfaDeviceEventMetadata(deviceToDelete),
UserMetadata: authz.ClientUserMetadataWithUser(ctx, user),
MFADeviceMetadata: mfaDeviceEventMetadata(deviceToDelete),
ConnectionMetadata: authz.ConnectionMetadata(ctx),
}); err != nil {
return nil, trace.Wrap(err)
}
Expand Down Expand Up @@ -3324,8 +3325,9 @@ func (a *Server) verifyMFARespAndAddDevice(ctx context.Context, req *newMFADevic
Code: events.MFADeviceAddEventCode,
ClusterName: clusterName.GetClusterName(),
},
UserMetadata: authz.ClientUserMetadataWithUser(ctx, req.username),
MFADeviceMetadata: mfaDeviceEventMetadata(dev),
UserMetadata: authz.ClientUserMetadataWithUser(ctx, req.username),
MFADeviceMetadata: mfaDeviceEventMetadata(dev),
ConnectionMetadata: authz.ConnectionMetadata(ctx),
}); err != nil {
log.WithError(err).Warn("Failed to emit add mfa device event.")
}
Expand Down
29 changes: 23 additions & 6 deletions lib/auth/auth_test.go
Expand Up @@ -28,6 +28,7 @@ import (
"errors"
"fmt"
mathrand "math/rand"
"net"
"os"
"sort"
"strings"
Expand Down Expand Up @@ -767,7 +768,8 @@ func TestTrustedClusterCRUDEventEmitted(t *testing.T) {
t.Parallel()
s := newAuthSuite(t)

ctx := context.Background()
clientAddr := &net.TCPAddr{IP: net.IPv4(10, 255, 0, 0)}
ctx := authz.ContextWithClientSrcAddr(context.Background(), clientAddr)
s.a.emitter = s.mockEmitter

// set up existing cluster to bypass switch cases that
Expand Down Expand Up @@ -795,6 +797,8 @@ func TestTrustedClusterCRUDEventEmitted(t *testing.T) {
_, err = s.a.UpsertTrustedCluster(ctx, tc)
require.NoError(t, err)
require.Equal(t, s.mockEmitter.LastEvent().GetType(), events.TrustedClusterCreateEvent)
require.Equal(t, events.TrustedClusterCreateEvent, s.mockEmitter.LastEvent().GetType())
require.Equal(t, clientAddr.String(), s.mockEmitter.LastEvent().(*apievents.TrustedClusterCreate).ConnectionMetadata.RemoteAddr)
s.mockEmitter.Reset()

// test create event for switch case: when tc exists but enabled is true
Expand All @@ -803,19 +807,24 @@ func TestTrustedClusterCRUDEventEmitted(t *testing.T) {
_, err = s.a.UpsertTrustedCluster(ctx, tc)
require.NoError(t, err)
require.Equal(t, s.mockEmitter.LastEvent().GetType(), events.TrustedClusterCreateEvent)
require.Equal(t, events.TrustedClusterCreateEvent, s.mockEmitter.LastEvent().GetType())
require.Equal(t, clientAddr.String(), s.mockEmitter.LastEvent().(*apievents.TrustedClusterCreate).ConnectionMetadata.RemoteAddr)
s.mockEmitter.Reset()

// test delete event
err = s.a.DeleteTrustedCluster(ctx, "test")
require.NoError(t, err)
require.Equal(t, s.mockEmitter.LastEvent().GetType(), events.TrustedClusterDeleteEvent)
require.Equal(t, events.TrustedClusterDeleteEvent, s.mockEmitter.LastEvent().GetType())
require.Equal(t, clientAddr.String(), s.mockEmitter.LastEvent().(*apievents.TrustedClusterDelete).ConnectionMetadata.RemoteAddr)
}

func TestGithubConnectorCRUDEventsEmitted(t *testing.T) {
t.Parallel()
s := newAuthSuite(t)

ctx := context.Background()
clientAddr := &net.TCPAddr{IP: net.IPv4(10, 255, 0, 0)}
ctx := authz.ContextWithClientSrcAddr(context.Background(), clientAddr)
// test github create event
github, err := types.NewGithubConnector("test", types.GithubConnectorSpecV3{
TeamsToLogins: []types.TeamMapping{
Expand All @@ -830,21 +839,25 @@ func TestGithubConnectorCRUDEventsEmitted(t *testing.T) {
err = s.a.upsertGithubConnector(ctx, github)
require.NoError(t, err)
require.IsType(t, &apievents.GithubConnectorCreate{}, s.mockEmitter.LastEvent())
require.Equal(t, s.mockEmitter.LastEvent().GetType(), events.GithubConnectorCreatedEvent)
require.Equal(t, events.GithubConnectorCreatedEvent, s.mockEmitter.LastEvent().GetType())
require.Equal(t, clientAddr.String(), s.mockEmitter.LastEvent().(*apievents.GithubConnectorCreate).ConnectionMetadata.RemoteAddr)
s.mockEmitter.Reset()

// test github update event
err = s.a.upsertGithubConnector(ctx, github)
require.NoError(t, err)
require.IsType(t, &apievents.GithubConnectorCreate{}, s.mockEmitter.LastEvent())
require.Equal(t, s.mockEmitter.LastEvent().GetType(), events.GithubConnectorCreatedEvent)
require.Equal(t, events.GithubConnectorCreatedEvent, s.mockEmitter.LastEvent().GetType())
require.Equal(t, clientAddr.String(), s.mockEmitter.LastEvent().(*apievents.GithubConnectorCreate).ConnectionMetadata.RemoteAddr)
s.mockEmitter.Reset()

// test github delete event
err = s.a.deleteGithubConnector(ctx, "test")
require.NoError(t, err)
require.IsType(t, &apievents.GithubConnectorDelete{}, s.mockEmitter.LastEvent())
require.Equal(t, s.mockEmitter.LastEvent().GetType(), events.GithubConnectorDeletedEvent)
require.Equal(t, events.GithubConnectorDeletedEvent, s.mockEmitter.LastEvent().GetType())
require.Equal(t, clientAddr.String(), s.mockEmitter.LastEvent().(*apievents.GithubConnectorDelete).ConnectionMetadata.RemoteAddr)
}

func TestOIDCConnectorCRUDEventsEmitted(t *testing.T) {
Expand Down Expand Up @@ -2064,7 +2077,8 @@ func TestDeleteMFADeviceSync(t *testing.T) {
mockEmitter := &eventstest.MockRecorderEmitter{}
authServer.emitter = mockEmitter

ctx := context.Background()
clientAddr := &net.TCPAddr{IP: net.IPv4(10, 255, 0, 0)}
ctx := authz.ContextWithClientSrcAddr(context.Background(), clientAddr)

username := "llama@goteleport.com"
_, _, err := CreateUserAndRole(authServer, username, []string{username}, nil /* allowRules */)
Expand Down Expand Up @@ -2150,6 +2164,7 @@ func TestDeleteMFADeviceSync(t *testing.T) {
require.IsType(t, &apievents.MFADeviceDelete{}, event, "underlying event type")
deleteEvent := event.(*apievents.MFADeviceDelete) // asserted above
assert.Equal(t, username, deleteEvent.User, "event.User")
assert.Equal(t, clientAddr.String(), deleteEvent.ConnectionMetadata.RemoteAddr)
})
}
}
Expand Down Expand Up @@ -2499,7 +2514,9 @@ func TestAddMFADeviceSync(t *testing.T) {
event := mockEmitter.LastEvent()
require.Equal(t, events.MFADeviceAddEvent, event.GetType())
require.Equal(t, events.MFADeviceAddEventCode, event.GetCode())
require.Equal(t, event.(*apievents.MFADeviceAdd).UserMetadata.User, u.username)
addEvt := event.(*apievents.MFADeviceAdd)
require.Equal(t, u.username, addEvt.UserMetadata.User)
assert.Contains(t, addEvt.ConnectionMetadata.RemoteAddr, "127.0.0.1", "client remote addr must be localhost")

// Check it's been added.
res, err := clt.GetMFADevices(ctx, &proto.GetMFADevicesRequest{})
Expand Down