Skip to content

Commit

Permalink
emit app.session.start when creating an app session
Browse files Browse the repository at this point in the history
  • Loading branch information
rudream committed Jul 12, 2024
1 parent 324f46f commit e5fc2b1
Show file tree
Hide file tree
Showing 16 changed files with 2,204 additions and 1,913 deletions.
2,020 changes: 1,116 additions & 904 deletions api/client/proto/authservice.pb.go

Large diffs are not rendered by default.

8 changes: 8 additions & 0 deletions api/proto/teleport/legacy/client/proto/authservice.proto
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,8 @@ message RouteToApp {
string AzureIdentity = 6 [(gogoproto.jsontag) = "azure_identity,omitempty"];
// GCPServiceAccount is the GCP service account to assume when accessing GCP API.
string GCPServiceAccount = 7 [(gogoproto.jsontag) = "gcp_service_account,omitempty"];
// URI is the URI of the application.
string URI = 8 [(gogoproto.jsontag) = "uri,omitempty"];
}

// GetUserRequest specifies parameters for the GetUser method.
Expand Down Expand Up @@ -810,6 +812,12 @@ message CreateAppSessionRequest {
// An optional field, that when provided, the response will be validated and
// the ID of the validated MFA device will be stored in session's certificate.
MFAAuthenticateResponse MFAResponse = 8 [(gogoproto.jsontag) = "mfa_response,omitempty"];
// AppName is the name of the application.
string AppName = 9 [(gogoproto.jsontag) = "app_name"];
// URI is the URI of the application.
string URI = 10 [(gogoproto.jsontag) = "app_name"];
// RemoteAddr is a client (user's) address.
string RemoteAddr = 11 [(gogoproto.jsontag) = "remote_addr,omitempty"];
}

// CreateAppSessionResponse contains the requested application web session.
Expand Down
2 changes: 2 additions & 0 deletions api/proto/teleport/legacy/types/events/events.proto
Original file line number Diff line number Diff line change
Expand Up @@ -4434,6 +4434,8 @@ message RouteToApp {
string AzureIdentity = 6 [(gogoproto.jsontag) = "azure_identity,omitempty"];
// GCPServiceAccount is the GCP service account to assume when accessing GCP API.
string GCPServiceAccount = 7 [(gogoproto.jsontag) = "gcp_service_account,omitempty"];
// URI is the application URI.
string URI = 8 [(gogoproto.jsontag) = "uri,omitempty"];
}

// RouteToDatabase combines parameters for database service routing information.
Expand Down
1,958 changes: 1,002 additions & 956 deletions api/types/events/events.pb.go

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,8 @@ message RouteToApp {
string AzureIdentity = 6 [(gogoproto.jsontag) = "azure_identity,omitempty"];
// GCPServiceAccount is the GCP service account to assume when accessing GCP API.
string GCPServiceAccount = 7 [(gogoproto.jsontag) = "gcp_service_account,omitempty"];
// URI is the application URI.
string URI = 8 [(gogoproto.jsontag) = "uri,omitempty"];
}

// GetUserRequest specifies parameters for the GetUser method.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3562,6 +3562,8 @@ message RouteToApp {
string AzureIdentity = 6 [(gogoproto.jsontag) = "azure_identity,omitempty"];
// GCPServiceAccount is the GCP service account to assume when accessing GCP API.
string GCPServiceAccount = 7 [(gogoproto.jsontag) = "gcp_service_account,omitempty"];
// URI is the application URI.
string URI = 8 [(gogoproto.jsontag) = "uri,omitempty"];
}

// RouteToDatabase combines parameters for database service routing information.
Expand Down
3 changes: 3 additions & 0 deletions lib/auth/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -1983,6 +1983,8 @@ type certRequest struct {
appClusterName string
// appName is the name of the application to generate cert for.
appName string
// appURI is the URI of the application.
appURI string
// awsRoleARN is the role ARN to generate certificate for.
awsRoleARN string
// azureIdentity is the Azure identity to generate certificate for.
Expand Down Expand Up @@ -3057,6 +3059,7 @@ func generateCert(ctx context.Context, a *Server, req certRequest, caType types.
KubernetesUsers: kubeUsers,
RouteToApp: tlsca.RouteToApp{
SessionID: req.appSessionID,
URI: req.appURI,
PublicAddr: req.appPublicAddr,
ClusterName: req.appClusterName,
Name: req.appName,
Expand Down
8 changes: 6 additions & 2 deletions lib/auth/auth_with_roles.go
Original file line number Diff line number Diff line change
Expand Up @@ -3165,8 +3165,9 @@ func (a *ServerWithRoles) generateUserCerts(ctx context.Context, req proto.UserC
if req.RouteToApp.Name != "" {
// Create a new app session using the same cert request. The user certs
// generated below will be linked to this session by the session ID.
var ws types.WebSession
if req.RouteToApp.SessionID == "" {
ws, err := a.authServer.CreateAppSessionFromReq(ctx, NewAppSessionRequest{
ws, err = a.authServer.CreateAppSessionFromReq(ctx, NewAppSessionRequest{
NewWebSessionRequest: NewWebSessionRequest{
User: req.Username,
LoginIP: a.context.Identity.GetIdentity().LoginIP,
Expand All @@ -3187,6 +3188,8 @@ func (a *ServerWithRoles) generateUserCerts(ctx context.Context, req proto.UserC
GCPServiceAccount: req.RouteToApp.GCPServiceAccount,
MFAVerified: verifiedMFADeviceID,
DeviceExtensions: DeviceExtensions(a.context.Identity.GetIdentity().DeviceExtensions),
AppName: req.RouteToApp.Name,
AppURI: req.RouteToApp.URI,
})
if err != nil {
return nil, trace.Wrap(err)
Expand All @@ -3197,7 +3200,7 @@ func (a *ServerWithRoles) generateUserCerts(ctx context.Context, req proto.UserC
// Old clients will pass a session ID of an existing App session instead of a
// single GenerateUserCerts call. This is allowed, but ensure new clients cannot
// generate certs for MFA verified app sessions without an additional MFA check.
ws, err := a.GetAppSession(ctx, types.GetAppSessionRequest{
ws, err = a.GetAppSession(ctx, types.GetAppSessionRequest{
SessionID: req.RouteToApp.SessionID,
})
if err != nil {
Expand Down Expand Up @@ -3254,6 +3257,7 @@ func (a *ServerWithRoles) generateUserCerts(ctx context.Context, req proto.UserC
appSessionID: appSessionID,
appName: req.RouteToApp.Name,
appPublicAddr: req.RouteToApp.PublicAddr,
appURI: req.RouteToApp.URI,
appClusterName: req.RouteToApp.ClusterName,
awsRoleARN: req.RouteToApp.AWSRoleARN,
azureIdentity: req.RouteToApp.AzureIdentity,
Expand Down
2 changes: 2 additions & 0 deletions lib/auth/authclient/clt.go
Original file line number Diff line number Diff line change
Expand Up @@ -1735,6 +1735,8 @@ func TryCreateAppSessionForClientCertV15(ctx context.Context, client CreateAppSe
AWSRoleARN: routeToApp.AWSRoleARN,
AzureIdentity: routeToApp.AzureIdentity,
GCPServiceAccount: routeToApp.GCPServiceAccount,
URI: routeToApp.URI,
AppName: routeToApp.Name,
})
if err != nil {
return "", trace.Wrap(err)
Expand Down
48 changes: 48 additions & 0 deletions lib/auth/sessions.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,12 @@ import (
apidefaults "github.com/gravitational/teleport/api/defaults"
mfav1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/mfa/v1"
"github.com/gravitational/teleport/api/types"
apievents "github.com/gravitational/teleport/api/types/events"
"github.com/gravitational/teleport/api/utils/keys"
"github.com/gravitational/teleport/entitlements"
"github.com/gravitational/teleport/lib/auth/native"
"github.com/gravitational/teleport/lib/defaults"
"github.com/gravitational/teleport/lib/events"
"github.com/gravitational/teleport/lib/jwt"
"github.com/gravitational/teleport/lib/modules"
"github.com/gravitational/teleport/lib/services"
Expand Down Expand Up @@ -277,6 +279,14 @@ type NewAppSessionRequest struct {
MFAVerified string
// DeviceExtensions holds device-aware user certificate extensions.
DeviceExtensions DeviceExtensions
// AppName is the name of the app.
AppName string
// AppURI is the URI of the app.
AppURI string
// Identity is the identity of the user.
Identity tlsca.Identity
// RemoteAddr is a client (user's) address.
RemoteAddr string
}

// CreateAppSession creates and inserts a services.WebSession into the
Expand Down Expand Up @@ -335,6 +345,8 @@ func (a *Server) CreateAppSession(ctx context.Context, req *proto.CreateAppSessi
AzureIdentity: req.AzureIdentity,
GCPServiceAccount: req.GCPServiceAccount,
MFAVerified: verifiedMFADeviceID,
AppName: req.AppName,
AppURI: req.URI,
DeviceExtensions: DeviceExtensions(identity.DeviceExtensions),
})
if err != nil {
Expand Down Expand Up @@ -448,6 +460,42 @@ func (a *Server) CreateAppSessionFromReq(ctx context.Context, req NewAppSessionR
}
log.Debugf("Generated application web session for %v with TTL %v.", req.User, req.SessionTTL)
UserLoginCount.Inc()

userMetadata := req.Identity.GetUserMetadata()
userMetadata.User = session.GetUser()
userMetadata.AWSRoleARN = req.AWSRoleARN

err = a.emitter.EmitAuditEvent(a.closeCtx, &apievents.AppSessionStart{
Metadata: apievents.Metadata{
Type: events.AppSessionStartEvent,
Code: events.AppSessionStartCode,
ClusterName: req.ClusterName,
},
ServerMetadata: apievents.ServerMetadata{
ServerVersion: teleport.Version,
ServerID: a.ServerID,
ServerNamespace: apidefaults.Namespace,
},
SessionMetadata: apievents.SessionMetadata{
SessionID: session.GetName(),
WithMFA: req.MFAVerified,
PrivateKeyPolicy: string(req.Identity.PrivateKeyPolicy),
},
UserMetadata: userMetadata,
ConnectionMetadata: apievents.ConnectionMetadata{
RemoteAddr: req.RemoteAddr,
},
PublicAddr: req.PublicAddr,
AppMetadata: apievents.AppMetadata{
AppURI: req.AppURI,
AppPublicAddr: req.PublicAddr,
AppName: req.AppName,
},
})
if err != nil {
log.WithError(err).Warn("Failed to emit app session start event")
}

return session, nil
}

Expand Down
1 change: 1 addition & 0 deletions lib/teleterm/clusters/cluster_apps.go
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ func (c *Cluster) ReissueAppCert(ctx context.Context, clusterClient *client.Clus
AWSRoleARN: "",
AzureIdentity: "",
GCPServiceAccount: "",
URI: app.GetURI(),
}

// TODO (Joerger): DELETE IN v17.0.0
Expand Down
4 changes: 4 additions & 0 deletions lib/tlsca/ca.go
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,9 @@ type RouteToApp struct {

// GCPServiceAccount is the GCP service account to assume when accessing GCP API.
GCPServiceAccount string

// URI is the URI of the application.
URI string
}

// RouteToDatabase contains routing information for databases.
Expand Down Expand Up @@ -307,6 +310,7 @@ func (id *Identity) GetEventIdentity() events.Identity {
AWSRoleARN: id.RouteToApp.AWSRoleARN,
AzureIdentity: id.RouteToApp.AzureIdentity,
GCPServiceAccount: id.RouteToApp.GCPServiceAccount,
URI: id.RouteToApp.URI,
}
}
var routeToDatabase *events.RouteToDatabase
Expand Down
54 changes: 3 additions & 51 deletions lib/web/apps.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,17 +29,13 @@ import (
"github.com/gravitational/trace"
"github.com/julienschmidt/httprouter"

"github.com/gravitational/teleport"
apiclient "github.com/gravitational/teleport/api/client"
"github.com/gravitational/teleport/api/client/proto"
apidefaults "github.com/gravitational/teleport/api/defaults"
"github.com/gravitational/teleport/api/types"
apievents "github.com/gravitational/teleport/api/types/events"
wantypes "github.com/gravitational/teleport/lib/auth/webauthntypes"
"github.com/gravitational/teleport/lib/events"
"github.com/gravitational/teleport/lib/httplib"
"github.com/gravitational/teleport/lib/reversetunnelclient"
"github.com/gravitational/teleport/lib/tlsca"
"github.com/gravitational/teleport/lib/utils"
"github.com/gravitational/teleport/lib/web/app"
"github.com/gravitational/teleport/lib/web/ui"
Expand Down Expand Up @@ -260,58 +256,14 @@ func (h *Handler) createAppSession(w http.ResponseWriter, r *http.Request, p htt
ClusterName: result.ClusterName,
AWSRoleARN: req.AWSRole,
MFAResponse: mfaProtoResponse,
AppName: result.App.GetName(),
URI: result.App.GetURI(),
RemoteAddr: r.RemoteAddr,
})
if err != nil {
return nil, trace.Wrap(err)
}

// Extract the identity of the user.
certificate, err := tlsca.ParseCertificatePEM(ws.GetTLSCert())
if err != nil {
return nil, trace.Wrap(err)
}
identity, err := tlsca.FromSubject(certificate.Subject, certificate.NotAfter)
if err != nil {
return nil, trace.Wrap(err)
}

userMetadata := identity.GetUserMetadata()
userMetadata.User = ws.GetUser()
userMetadata.AWSRoleARN = req.AWSRole

// Now that the certificate has been issued, emit a "new session created"
// for all events associated with this certificate.
appSessionStartEvent := &apievents.AppSessionStart{
Metadata: apievents.Metadata{
Type: events.AppSessionStartEvent,
Code: events.AppSessionStartCode,
ClusterName: identity.RouteToApp.ClusterName,
},
ServerMetadata: apievents.ServerMetadata{
ServerVersion: teleport.Version,
ServerID: h.cfg.HostUUID,
ServerNamespace: apidefaults.Namespace,
},
SessionMetadata: apievents.SessionMetadata{
SessionID: identity.RouteToApp.SessionID,
WithMFA: identity.MFAVerified,
PrivateKeyPolicy: string(identity.PrivateKeyPolicy),
},
UserMetadata: userMetadata,
ConnectionMetadata: apievents.ConnectionMetadata{
RemoteAddr: r.RemoteAddr,
},
PublicAddr: identity.RouteToApp.PublicAddr,
AppMetadata: apievents.AppMetadata{
AppURI: result.App.GetURI(),
AppPublicAddr: result.App.GetPublicAddr(),
AppName: result.App.GetName(),
},
}
if err := h.cfg.Emitter.EmitAuditEvent(h.cfg.Context, appSessionStartEvent); err != nil {
return nil, trace.Wrap(err)
}

return &CreateAppSessionResponse{
CookieValue: ws.GetName(),
SubjectCookieValue: ws.GetBearerToken(),
Expand Down
1 change: 1 addition & 0 deletions tool/tctl/common/auth_command.go
Original file line number Diff line number Diff line change
Expand Up @@ -895,6 +895,7 @@ func (a *AuthCommand) generateUserKeys(ctx context.Context, clusterAPI certifica
Name: a.appName,
PublicAddr: server.GetApp().GetPublicAddr(),
ClusterName: a.leafCluster,
URI: server.GetApp().GetURI(),
}

// TODO (Joerger): DELETE IN v17.0.0
Expand Down
3 changes: 3 additions & 0 deletions tool/tsh/common/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,7 @@ func onAppConfig(cf *CLIConf) error {
AWSRoleARN: app.AWSRoleARN,
AzureIdentity: app.AzureIdentity,
GCPServiceAccount: app.GCPServiceAccount,
URI: app.GetURI(),
}
conf, err := formatAppConfig(tc, profile, routeToApp, cf.Format)
if err != nil {
Expand Down Expand Up @@ -524,6 +525,7 @@ func getAppInfo(cf *CLIConf, tc *client.TeleportClient, matchRouteToApp func(tls
Name: app.GetName(),
PublicAddr: app.GetPublicAddr(),
ClusterName: tc.SiteName,
URI: app.GetURI(),
},
app: app,
}
Expand Down Expand Up @@ -683,5 +685,6 @@ func tlscaRouteToAppToProto(route tlsca.RouteToApp) proto.RouteToApp {
AWSRoleARN: route.AWSRoleARN,
AzureIdentity: route.AzureIdentity,
GCPServiceAccount: route.GCPServiceAccount,
URI: route.URI,
}
}
1 change: 1 addition & 0 deletions tool/tsh/common/vnet_common.go
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@ func (p *vnetAppProvider) reissueAppCert(ctx context.Context, tc *client.Telepor
Name: app.GetName(),
PublicAddr: app.GetPublicAddr(),
ClusterName: tc.SiteName,
URI: app.GetURI(),
}

profile, err := tc.ProfileStatus()
Expand Down

0 comments on commit e5fc2b1

Please sign in to comment.