Skip to content

Commit

Permalink
GitHub proxy: emit rbac failure event and add prometheus counters (#5…
Browse files Browse the repository at this point in the history
…1878)

* GitHub proxy: emit rbac failure event and add prometheus counters

* move device metadata to sshca.identity
  • Loading branch information
greedy52 committed Feb 24, 2025
1 parent 937c306 commit 789da57
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 18 deletions.
2 changes: 1 addition & 1 deletion lib/srv/authhandlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -367,7 +367,7 @@ func (h *AuthHandlers) UserKeyAuth(conn ssh.ConnMetadata, key ssh.PublicKey) (*s
UserMetadata: apievents.UserMetadata{
Login: principal,
User: ident.Username,
TrustedDevice: eventDeviceMetadataFromIdentity(ident),
TrustedDevice: ident.GetDeviceMetadata(),
},
ConnectionMetadata: apievents.ConnectionMetadata{
LocalAddr: conn.LocalAddr().String(),
Expand Down
18 changes: 1 addition & 17 deletions lib/srv/ctx.go
Original file line number Diff line number Diff line change
Expand Up @@ -1060,22 +1060,6 @@ func (c *ServerContext) ExecCommand() (*ExecCommand, error) {
}, nil
}

func eventDeviceMetadataFromIdentity(ident *sshca.Identity) *apievents.DeviceMetadata {
if ident == nil {
return nil
}

if ident.DeviceID == "" && ident.DeviceAssetTag == "" && ident.DeviceCredentialID == "" {
return nil
}

return &apievents.DeviceMetadata{
DeviceId: ident.DeviceID,
AssetTag: ident.DeviceAssetTag,
CredentialId: ident.DeviceCredentialID,
}
}

func (id *IdentityContext) GetUserMetadata() apievents.UserMetadata {
userKind := apievents.UserKind_USER_KIND_HUMAN
if id.BotName != "" {
Expand All @@ -1087,7 +1071,7 @@ func (id *IdentityContext) GetUserMetadata() apievents.UserMetadata {
User: id.TeleportUser,
Impersonator: id.Impersonator,
AccessRequests: id.ActiveRequests,
TrustedDevice: eventDeviceMetadataFromIdentity(id.UnmappedIdentity),
TrustedDevice: id.UnmappedIdentity.GetDeviceMetadata(),
UserKind: userKind,
BotName: id.BotName,
BotInstanceID: id.BotInstanceID,
Expand Down
62 changes: 62 additions & 0 deletions lib/srv/git/forward.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (
"github.com/google/uuid"
"github.com/gravitational/trace"
"github.com/jonboulle/clockwork"
"github.com/prometheus/client_golang/prometheus"
"golang.org/x/crypto/ssh"

"github.com/gravitational/teleport"
Expand All @@ -37,6 +38,7 @@ import (
"github.com/gravitational/teleport/lib/auth/authclient"
"github.com/gravitational/teleport/lib/bpf"
"github.com/gravitational/teleport/lib/events"
"github.com/gravitational/teleport/lib/observability/metrics"
"github.com/gravitational/teleport/lib/service/servicecfg"
"github.com/gravitational/teleport/lib/services"
"github.com/gravitational/teleport/lib/srv"
Expand All @@ -46,6 +48,38 @@ import (
logutils "github.com/gravitational/teleport/lib/utils/log"
)

var (
execSessionCounter = prometheus.NewCounter(prometheus.CounterOpts{
Namespace: teleport.MetricNamespace,
Subsystem: teleport.ComponentGit,
Name: "exec_sessions_total",
})

execFailureCounter = prometheus.NewCounter(prometheus.CounterOpts{
Namespace: teleport.MetricNamespace,
Subsystem: teleport.ComponentGit,
Name: "exec_failures_total",
})

userKeyAuthFailureCounter = prometheus.NewCounter(prometheus.CounterOpts{
Namespace: teleport.MetricNamespace,
Subsystem: teleport.ComponentGit,
Name: "userkeyauth_failures_total",
})

rbacFailureCounter = prometheus.NewCounter(prometheus.CounterOpts{
Namespace: teleport.MetricNamespace,
Subsystem: teleport.ComponentGit,
Name: "rbac_failures_total",
})

forwardServerPrometheusCollectors = []prometheus.Collector{
execSessionCounter,
userKeyAuthFailureCounter,
rbacFailureCounter,
}
)

// ForwardServerConfig is the configuration for the ForwardServer.
type ForwardServerConfig struct {
// ParentContext is a parent context, used to signal global
Expand Down Expand Up @@ -172,6 +206,10 @@ func NewForwardServer(cfg *ForwardServerConfig) (*ForwardServer, error) {
return nil, trace.Wrap(err)
}

if err := metrics.RegisterPrometheusCollectors(forwardServerPrometheusCollectors...); err != nil {
return nil, trace.Wrap(err)
}

serverConn, clientConn, err := utils.DualPipeNetConn(cfg.SrcAddr, cfg.DstAddr)
if err != nil {
return nil, trace.Wrap(err)
Expand Down Expand Up @@ -282,6 +320,7 @@ func (s *ForwardServer) userKeyAuth(conn ssh.ConnMetadata, key ssh.PublicKey) (*
// Use auth.UserKeyAuth to verify user cert is signed by UserCA.
permissions, err := s.auth.UserKeyAuth(conn, key)
if err != nil {
userKeyAuthFailureCounter.Inc()
return nil, trace.Wrap(err)
}

Expand All @@ -295,6 +334,26 @@ func (s *ForwardServer) userKeyAuth(conn ssh.ConnMetadata, key ssh.PublicKey) (*
"fingerprint", sshutils.Fingerprint(key),
"user", cert.KeyId,
)
rbacFailureCounter.Inc()
s.emitEvent(&apievents.AuthAttempt{
Metadata: apievents.Metadata{
Type: events.AuthAttemptEvent,
Code: events.AuthAttemptFailureCode,
},
UserMetadata: apievents.UserMetadata{
Login: gitUser,
User: ident.Username,
TrustedDevice: ident.GetDeviceMetadata(),
},
ConnectionMetadata: apievents.ConnectionMetadata{
LocalAddr: conn.LocalAddr().String(),
RemoteAddr: conn.RemoteAddr().String(),
},
Status: apievents.Status{
Success: false,
Error: err.Error(),
},
})
return nil, trace.Wrap(err)
}
return permissions, nil
Expand Down Expand Up @@ -445,7 +504,10 @@ func (s *ForwardServer) handleExec(ctx context.Context, sctx *sessionContext, re
var r sshutils.ExecReq
defer func() {
if err != nil {
execFailureCounter.Inc()
s.emitEvent(s.makeGitCommandEvent(sctx, r.Command, err))
} else {
execSessionCounter.Inc()
}
}()

Expand Down
8 changes: 8 additions & 0 deletions lib/srv/git/forward_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,14 @@ func TestForwardServer(t *testing.T) {
clientLogin: "git",
verifyRemoteHost: ssh.InsecureIgnoreHostKey(),
wantNewClientError: true,
verifyEvent: func(t *testing.T, event apievents.AuditEvent) {
authFailureEvent, ok := event.(*apievents.AuthAttempt)
require.True(t, ok)
assert.Equal(t, libevents.AuthAttemptEvent, authFailureEvent.Metadata.Type)
assert.Equal(t, libevents.AuthAttemptFailureCode, authFailureEvent.Metadata.Code)
assert.Equal(t, "alice", authFailureEvent.User)
assert.Contains(t, authFailureEvent.Error, "access denied")
},
},
{
name: "failed client login check",
Expand Down
17 changes: 17 additions & 0 deletions lib/sshca/identity.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import (
"github.com/gravitational/teleport"
"github.com/gravitational/teleport/api/constants"
"github.com/gravitational/teleport/api/types"
apievents "github.com/gravitational/teleport/api/types/events"
"github.com/gravitational/teleport/api/types/wrappers"
"github.com/gravitational/teleport/api/utils/keys"
"github.com/gravitational/teleport/lib/utils"
Expand Down Expand Up @@ -306,6 +307,22 @@ func (i *Identity) Encode(certFormat string) (*ssh.Certificate, error) {
return cert, nil
}

// GetDeviceMetadata returns information about user's trusted device.
func (i *Identity) GetDeviceMetadata() *apievents.DeviceMetadata {
if i == nil {
return nil
}
if i.DeviceID == "" && i.DeviceAssetTag == "" && i.DeviceCredentialID == "" {
return nil
}

return &apievents.DeviceMetadata{
DeviceId: i.DeviceID,
AssetTag: i.DeviceAssetTag,
CredentialId: i.DeviceCredentialID,
}
}

// DecodeIdentity decodes an ssh certificate into an identity.
func DecodeIdentity(cert *ssh.Certificate) (*Identity, error) {
ident := &Identity{
Expand Down

0 comments on commit 789da57

Please sign in to comment.