mirror of
https://github.com/gravitational/teleport.git
synced 2026-10-11 22:49:54 +00:00
emit audit events when SFTP or SCP commands are blocked (#36977)
* emit audit events when SFTP or SCP commands are blocked * include user name in SCP web audit message * use 'an' instead of 'a' * make SCP audit message use consistent language, fix type check error * fix unused var
This commit is contained in:
@@ -290,6 +290,8 @@ const (
|
||||
// Note: some requests (like exec into a pod) use other codes (like
|
||||
// ExecCode).
|
||||
KubeRequestCode = "T3009I"
|
||||
// SCPDisallowedCode is the SCP disallowed event code.
|
||||
SCPDisallowedCode = "T3010E"
|
||||
|
||||
// KubernetesClusterCreateCode is the kube.create event code.
|
||||
KubernetesClusterCreateCode = "T3010I"
|
||||
@@ -337,6 +339,7 @@ const (
|
||||
SFTPSymlinkFailureCode = "TS018E"
|
||||
SFTPLinkCode = "TS019I"
|
||||
SFTPLinkFailureCode = "TS019E"
|
||||
SFTPDisallowedCode = "TS020E"
|
||||
|
||||
// SessionCommandCode is a session command code.
|
||||
SessionCommandCode = "T4000I"
|
||||
|
||||
@@ -261,6 +261,16 @@ func (e *localExec) String() string {
|
||||
func (e *localExec) transformSecureCopy() error {
|
||||
isSCPCmd, err := checkSCPAllowed(e.Ctx, e.GetCommand())
|
||||
if err != nil {
|
||||
e.Ctx.GetServer().EmitAuditEvent(context.WithoutCancel(e.Ctx.Context), &apievents.SFTP{
|
||||
Metadata: apievents.Metadata{
|
||||
Code: events.SCPDisallowedCode,
|
||||
Type: events.SCPEvent,
|
||||
Time: time.Now(),
|
||||
},
|
||||
UserMetadata: e.Ctx.Identity.GetUserMetadata(),
|
||||
ServerMetadata: e.Ctx.GetServerMetadata(),
|
||||
Error: err.Error(),
|
||||
})
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
if !isSCPCmd {
|
||||
@@ -356,6 +366,16 @@ func (e *remoteExec) SetCommand(command string) {
|
||||
// ExecResult is only used to communicate an error while launching.
|
||||
func (e *remoteExec) Start(ctx context.Context, ch ssh.Channel) (*ExecResult, error) {
|
||||
if _, err := checkSCPAllowed(e.ctx, e.GetCommand()); err != nil {
|
||||
e.ctx.GetServer().EmitAuditEvent(context.WithoutCancel(ctx), &apievents.SFTP{
|
||||
Metadata: apievents.Metadata{
|
||||
Code: events.SCPDisallowedCode,
|
||||
Type: events.SCPEvent,
|
||||
Time: time.Now(),
|
||||
},
|
||||
UserMetadata: e.ctx.Identity.GetUserMetadata(),
|
||||
ServerMetadata: e.ctx.GetServerMetadata(),
|
||||
Error: err.Error(),
|
||||
})
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/gravitational/trace"
|
||||
@@ -1361,7 +1362,18 @@ func (s *Server) handleSubsystem(ctx context.Context, ch ssh.Channel, req *ssh.R
|
||||
|
||||
// if SFTP was requested, check that
|
||||
if subsystem.subsytemName == teleport.SFTPSubsystem {
|
||||
if err := serverContext.CheckSFTPAllowed(s.sessionRegistry); err != nil {
|
||||
err := serverContext.CheckSFTPAllowed(s.sessionRegistry)
|
||||
if err != nil {
|
||||
s.EmitAuditEvent(context.WithoutCancel(ctx), &apievents.SFTP{
|
||||
Metadata: apievents.Metadata{
|
||||
Code: events.SFTPDisallowedCode,
|
||||
Type: events.SFTPEvent,
|
||||
Time: time.Now(),
|
||||
},
|
||||
UserMetadata: serverContext.Identity.GetUserMetadata(),
|
||||
ServerMetadata: serverContext.GetServerMetadata(),
|
||||
Error: err.Error(),
|
||||
})
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ import (
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"time"
|
||||
|
||||
"github.com/gogo/protobuf/jsonpb"
|
||||
"github.com/gravitational/trace"
|
||||
@@ -33,6 +34,7 @@ import (
|
||||
|
||||
"github.com/gravitational/teleport"
|
||||
apievents "github.com/gravitational/teleport/api/types/events"
|
||||
"github.com/gravitational/teleport/lib/events"
|
||||
"github.com/gravitational/teleport/lib/srv"
|
||||
"github.com/gravitational/teleport/lib/utils"
|
||||
)
|
||||
@@ -66,6 +68,16 @@ func (s *sftpSubsys) Start(ctx context.Context,
|
||||
// this connection was proxied, the proxy doesn't know if file copying
|
||||
// is allowed for certain Nodes.
|
||||
if !serverCtx.AllowFileCopying {
|
||||
serverCtx.GetServer().EmitAuditEvent(context.WithoutCancel(ctx), &apievents.SFTP{
|
||||
Metadata: apievents.Metadata{
|
||||
Code: events.SFTPDisallowedCode,
|
||||
Type: events.SFTPEvent,
|
||||
Time: time.Now(),
|
||||
},
|
||||
UserMetadata: serverCtx.Identity.GetUserMetadata(),
|
||||
ServerMetadata: serverCtx.GetServerMetadata(),
|
||||
Error: srv.ErrNodeFileCopyingNotPermitted.Error(),
|
||||
})
|
||||
return srv.ErrNodeFileCopyingNotPermitted
|
||||
}
|
||||
|
||||
|
||||
@@ -2154,7 +2154,18 @@ func (s *Server) parseSubsystemRequest(req *ssh.Request, ctx *srv.ServerContext)
|
||||
case r.Name == teleport.GetHomeDirSubsystem:
|
||||
return newHomeDirSubsys(), nil
|
||||
case r.Name == teleport.SFTPSubsystem:
|
||||
if err := ctx.CheckSFTPAllowed(s.reg); err != nil {
|
||||
err := ctx.CheckSFTPAllowed(s.reg)
|
||||
if err != nil {
|
||||
s.EmitAuditEvent(context.Background(), &apievents.SFTP{
|
||||
Metadata: apievents.Metadata{
|
||||
Code: events.SFTPDisallowedCode,
|
||||
Type: events.SFTPEvent,
|
||||
Time: time.Now(),
|
||||
},
|
||||
UserMetadata: ctx.Identity.GetUserMetadata(),
|
||||
ServerMetadata: ctx.GetServerMetadata(),
|
||||
Error: err.Error(),
|
||||
})
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
|
||||
|
||||
@@ -47,6 +47,7 @@ const EventIconMap: Record<EventCode, any> = {
|
||||
[eventCodes.SCP_DOWNLOAD]: Icons.Download,
|
||||
[eventCodes.SCP_UPLOAD_FAILURE]: Icons.Upload,
|
||||
[eventCodes.SCP_UPLOAD]: Icons.Upload,
|
||||
[eventCodes.SCP_DISALLOWED]: Icons.FolderPlus,
|
||||
[eventCodes.SFTP_OPEN_FAILURE]: Icons.FolderPlus,
|
||||
[eventCodes.SFTP_OPEN]: Icons.FolderPlus,
|
||||
[eventCodes.SFTP_CLOSE_FAILURE]: Icons.FolderPlus,
|
||||
@@ -83,6 +84,9 @@ const EventIconMap: Record<EventCode, any> = {
|
||||
[eventCodes.SFTP_READLINK]: Icons.FolderPlus,
|
||||
[eventCodes.SFTP_SYMLINK_FAILURE]: Icons.FolderPlus,
|
||||
[eventCodes.SFTP_SYMLINK]: Icons.FolderPlus,
|
||||
[eventCodes.SFTP_LINK]: Icons.FolderPlus,
|
||||
[eventCodes.SFTP_LINK_FAILURE]: Icons.FolderPlus,
|
||||
[eventCodes.SFTP_DISALLOWED]: Icons.FolderPlus,
|
||||
[eventCodes.APP_SESSION_CHUNK]: Icons.Info,
|
||||
[eventCodes.APP_SESSION_START]: Icons.Info,
|
||||
[eventCodes.APP_SESSION_END]: Icons.Info,
|
||||
|
||||
@@ -281,6 +281,14 @@ export const formatters: Formatters = {
|
||||
rest['server_hostname'] || rest['addr.local']
|
||||
}] failed [${exitError}]`,
|
||||
},
|
||||
[eventCodes.SCP_DISALLOWED]: {
|
||||
type: 'scp',
|
||||
desc: 'SCP Disallowed',
|
||||
format: ({ user, ...rest }) =>
|
||||
`User [${user}] SCP file transfer on node [${
|
||||
rest['server_hostname'] || rest['addr.local']
|
||||
}] blocked`,
|
||||
},
|
||||
[eventCodes.SFTP_OPEN]: {
|
||||
type: 'sftp',
|
||||
desc: 'SFTP Open',
|
||||
@@ -569,6 +577,30 @@ export const formatters: Formatters = {
|
||||
rest['server_hostname'] || rest['addr.local']
|
||||
}]: [${error}]`,
|
||||
},
|
||||
[eventCodes.SFTP_LINK]: {
|
||||
type: 'sftp',
|
||||
desc: 'SFTP Link',
|
||||
format: ({ user, path, ...rest }) =>
|
||||
`User [${user}] created hard link [${path}] on node [${
|
||||
rest['server_hostname'] || rest['addr.local']
|
||||
}]`,
|
||||
},
|
||||
[eventCodes.SFTP_LINK_FAILURE]: {
|
||||
type: 'sftp',
|
||||
desc: 'SFTP Link Failed',
|
||||
format: ({ user, path, error, ...rest }) =>
|
||||
`User [${user}] failed to create hard link [${path}] on node [${
|
||||
rest['server_hostname'] || rest['addr.local']
|
||||
}]: [${error}]`,
|
||||
},
|
||||
[eventCodes.SFTP_DISALLOWED]: {
|
||||
type: 'sftp',
|
||||
desc: 'SFTP Disallowed',
|
||||
format: ({ user, ...rest }) =>
|
||||
`User [${user}] was blocked from creating an SFTP session on node [${
|
||||
rest['server_hostname'] || rest['addr.local']
|
||||
}]`,
|
||||
},
|
||||
[eventCodes.SESSION_JOIN]: {
|
||||
type: 'session.join',
|
||||
desc: 'User Joined',
|
||||
|
||||
@@ -152,6 +152,7 @@ export const eventCodes = {
|
||||
SCP_DOWNLOAD: 'T3004I',
|
||||
SCP_UPLOAD_FAILURE: 'T3005E',
|
||||
SCP_UPLOAD: 'T3005I',
|
||||
SCP_DISALLOWED: 'T3010E',
|
||||
SFTP_OPEN_FAILURE: 'TS001E',
|
||||
SFTP_OPEN: 'TS001I',
|
||||
SFTP_CLOSE_FAILURE: 'TS002E',
|
||||
@@ -188,6 +189,9 @@ export const eventCodes = {
|
||||
SFTP_READLINK: 'TS017I',
|
||||
SFTP_SYMLINK_FAILURE: 'TS018E',
|
||||
SFTP_SYMLINK: 'TS018I',
|
||||
SFTP_LINK: 'TS019I',
|
||||
SFTP_LINK_FAILURE: 'TS019E',
|
||||
SFTP_DISALLOWED: 'TS020E',
|
||||
SESSION_COMMAND: 'T4000I',
|
||||
SESSION_DATA: 'T2006I',
|
||||
SESSION_DISK: 'T4001I',
|
||||
@@ -405,6 +409,12 @@ export type RawEvents = {
|
||||
exitError: string;
|
||||
}
|
||||
>;
|
||||
[eventCodes.SCP_DISALLOWED]: RawEvent<
|
||||
typeof eventCodes.SCP_DISALLOWED,
|
||||
{
|
||||
user: string;
|
||||
}
|
||||
>;
|
||||
[eventCodes.SFTP_OPEN]: RawEventSFTP<typeof eventCodes.SFTP_OPEN>;
|
||||
[eventCodes.SFTP_OPEN_FAILURE]: RawEventSFTP<
|
||||
typeof eventCodes.SFTP_OPEN_FAILURE
|
||||
@@ -477,6 +487,11 @@ export type RawEvents = {
|
||||
[eventCodes.SFTP_SYMLINK_FAILURE]: RawEventSFTP<
|
||||
typeof eventCodes.SFTP_SYMLINK_FAILURE
|
||||
>;
|
||||
[eventCodes.SFTP_LINK]: RawEventSFTP<typeof eventCodes.SFTP_LINK>;
|
||||
[eventCodes.SFTP_LINK_FAILURE]: RawEventSFTP<
|
||||
typeof eventCodes.SFTP_LINK_FAILURE
|
||||
>;
|
||||
[eventCodes.SFTP_DISALLOWED]: RawEventSFTP<typeof eventCodes.SFTP_DISALLOWED>;
|
||||
[eventCodes.SESSION_COMMAND]: RawEventCommand<
|
||||
typeof eventCodes.SESSION_COMMAND
|
||||
>;
|
||||
|
||||
Reference in New Issue
Block a user