Skip to content
6 changes: 5 additions & 1 deletion api/types/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,11 @@ func (a *AppV3) IsAWSConsole() bool {

// IsTCP returns true if this app represents a TCP endpoint.
func (a *AppV3) IsTCP() bool {
return strings.HasPrefix(a.Spec.URI, "tcp://")
return IsAppTCP(a.Spec.URI)
}

func IsAppTCP(uri string) bool {
return strings.HasPrefix(uri, "tcp://")
}

// GetProtocol returns the application protocol.
Expand Down
56 changes: 56 additions & 0 deletions lib/auth/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ import (
"go.opentelemetry.io/otel/exporters/otlp/otlptrace"
"golang.org/x/crypto/ssh"
"golang.org/x/exp/slices"
"google.golang.org/protobuf/types/known/durationpb"

"github.com/gravitational/teleport"
"github.com/gravitational/teleport/api/client"
Expand Down Expand Up @@ -1277,6 +1278,59 @@ func (a *Server) GenerateDatabaseTestCert(req DatabaseTestCertRequest) ([]byte,
return certs.TLS, nil
}

// submitCertificateIssuedEvent submits a certificate issued usage event to the
// usage reporting service.
func (a *Server) submitCertificateIssuedEvent(req *certRequest) {
var database, app, kubernetes, desktop bool

if req.dbService != "" {
database = true
}

if req.appName != "" {
app = true
}

if req.kubernetesCluster != "" {
kubernetes = true
}

// Bot users are regular Teleport users, but have a special internal label.
bot := false
if _, ok := req.user.GetMetadata().Labels[types.BotLabel]; ok {
bot = true
}

// Unfortunately the only clue we have about Windows certs is the usage
// restriction: `RouteToWindowsDesktop` isn't actually passed along to the
// certRequest.
for _, usage := range req.usage {
switch usage {
case teleport.UsageWindowsDesktopOnly:
desktop = true
}
}

// For usage reporting, we care about the impersonator rather than the user
// being impersonated (if any).
user := req.user.GetName()
if req.impersonator != "" {
user = req.impersonator
}

if err := a.SubmitAnonymizedUsageEvents(&services.UsageCertificateIssued{
UserName: user,
Ttl: durationpb.New(req.ttl),
IsBot: bot,
UsageDatabase: database,
UsageApp: app,
UsageKubernetes: kubernetes,
UsageDesktop: desktop,
}); err != nil {
log.Debugf("Unable to submit certificate issued usage event: %v", err)
}
}

// generateUserCert generates user certificates
func (a *Server) generateUserCert(req certRequest) (*proto.Certs, error) {
ctx := context.TODO()
Expand Down Expand Up @@ -1585,6 +1639,8 @@ func (a *Server) generateUserCert(req certRequest) (*proto.Certs, error) {
certs.SSHCACerts = append(certs.SSHCACerts, services.GetSSHCheckingKeys(ca)...)
}

a.submitCertificateIssuedEvent(&req)

return certs, nil
}

Expand Down
36 changes: 34 additions & 2 deletions lib/events/usageevents/usageevents.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@ import (
"github.com/gravitational/teleport/lib/services"
)

const (
// tcpSessionType is the session_type in tp.session.start for TCP
// Application Access
tcpSessionType = "app_tcp"
)

// UsageLogger is a trivial audit log sink that forwards an anonymized subset of
// audit log events to Teleport.
type UsageLogger struct {
Expand Down Expand Up @@ -66,12 +72,38 @@ func (u *UsageLogger) reportAuditEvent(ctx context.Context, event apievents.Audi
UserName: e.User,
ConnectorType: e.Method,
}))

case *apievents.SessionStart:
// Note: session.start is only SSH.
// Note: session.start is only SSH and Kubernetes.
sessionType := types.SSHSessionKind
if e.KubernetesCluster != "" {
sessionType = types.KubernetesSessionKind
}

return trace.Wrap(u.report(&services.UsageSessionStart{
UserName: e.User,
SessionType: string(sessionType),
}))
case *apievents.DatabaseSessionStart:
return trace.Wrap(u.report(&services.UsageSessionStart{
UserName: e.User,
SessionType: string(types.SSHSessionKind),
SessionType: string(types.DatabaseSessionKind),
}))
case *apievents.AppSessionStart:
sessionType := string(types.AppSessionKind)
if types.IsAppTCP(e.AppURI) {
sessionType = tcpSessionType
}
return trace.Wrap(u.report(&services.UsageSessionStart{
UserName: e.User,
SessionType: sessionType,
}))
case *apievents.WindowsDesktopSessionStart:
return trace.Wrap(u.report(&services.UsageSessionStart{
UserName: e.User,
SessionType: string(types.WindowsDesktopSessionKind),
}))

case *apievents.GithubConnectorCreate:
return trace.Wrap(u.report(&services.UsageSSOCreate{
ConnectorType: types.KindGithubConnector,
Expand Down
Loading