Skip to content

Commit

Permalink
Add the cert.create event
Browse files Browse the repository at this point in the history
For now, this is only emitted for user certificate issuance.
  • Loading branch information
espadolini committed Feb 2, 2022
1 parent bd01152 commit c6fe31f
Show file tree
Hide file tree
Showing 8 changed files with 3,237 additions and 821 deletions.
3,870 changes: 3,049 additions & 821 deletions api/types/events/events.pb.go

Large diffs are not rendered by default.

109 changes: 109 additions & 0 deletions api/types/events/events.proto
Original file line number Diff line number Diff line change
Expand Up @@ -1678,6 +1678,25 @@ message WindowsDesktopSessionEnd {
map<string, string> DesktopLabels = 8 [ (gogoproto.jsontag) = "desktop_labels" ];
}

// CertificateType represents the type of a certificate (user, host, CA, client...).
enum CertificateType {
UNKNOWN = 0;
USER = 1;
}

// CertificateCreate is emitted when a certificate is issued.
message CertificateCreate {
// Metadata is a common event metadata.
Metadata Metadata = 1
[ (gogoproto.nullable) = false, (gogoproto.embed) = true, (gogoproto.jsontag) = "" ];

// CertificateType is the type of certificate that was just issued.
CertificateType CertificateType = 2 [ (gogoproto.jsontag) = "cert_type,omitempty" ];

// Identity is the identity associated with the certificate, as interpreted by Teleport.
Identity Identity = 3 [ (gogoproto.jsontag) = "identity" ];
}

// OneOf is a union of one of audit events submitted to the auth service
message OneOf {
// Event is one of the audit events
Expand Down Expand Up @@ -1749,6 +1768,7 @@ message OneOf {
events.PostgresFunctionCall PostgresFunctionCall = 65;
events.AccessRequestDelete AccessRequestDelete = 66;
events.SessionConnect SessionConnect = 67;
events.CertificateCreate CertificateCreate = 68;
}
}

Expand Down Expand Up @@ -1779,3 +1799,92 @@ message SessionUpload {
// URL is where the url the session event data upload is at
string SessionURL = 5 [ (gogoproto.jsontag) = "url" ];
}

// Identity matches github.com/gravitational/teleport/lib/tlsca.Identity except
// for RouteToApp and RouteToDatabase which are nullable and Traits which is
// represented as a google.protobuf.Struct (still containing a map from string
// to strings).
message Identity {
// Username is a username or name of the node connection
string Username = 1 [ (gogoproto.jsontag) = "user,omitempty" ];
// Impersonator is a username of a user impersonating this user
string Impersonator = 2 [ (gogoproto.jsontag) = "impersonator,omitempty" ];
// Groups is a list of groups (Teleport roles) encoded in the identity
repeated string Groups = 3 [ (gogoproto.jsontag) = "roles,omitempty" ];
// Usage is a list of usage restrictions encoded in the identity
repeated string Usage = 4 [ (gogoproto.jsontag) = "usage,omitempty" ];
// Principals is a list of Unix logins allowed.
repeated string Principals = 5 [ (gogoproto.jsontag) = "logins,omitempty" ];
// KubernetesGroups is a list of Kubernetes groups allowed
repeated string KubernetesGroups = 6 [ (gogoproto.jsontag) = "kubernetes_groups,omitempty" ];
// KubernetesUsers is a list of Kubernetes users allowed
repeated string KubernetesUsers = 7 [ (gogoproto.jsontag) = "kubernetes_users,omitempty" ];
// Expires specifies whenever the session will expire
google.protobuf.Timestamp Expires = 8 [
(gogoproto.stdtime) = true,
(gogoproto.nullable) = false,
(gogoproto.jsontag) = "expires"
];
// RouteToCluster specifies the target cluster
// if present in the session
string RouteToCluster = 9 [ (gogoproto.jsontag) = "route_to_cluster,omitempty" ];
// KubernetesCluster specifies the target kubernetes cluster for TLS
// identities. This can be empty on older Teleport clients.
string KubernetesCluster = 10 [ (gogoproto.jsontag) = "kubernetes_cluster,omitempty" ];
// Traits hold claim data used to populate a role at runtime.
google.protobuf.Struct Traits = 11
[ (gogoproto.jsontag) = "traits,omitempty", (gogoproto.casttype) = "Struct" ];
// RouteToApp holds routing information for applications. Routing metadata
// allows Teleport web proxy to route HTTP requests to the appropriate
// cluster and Teleport application proxy within the cluster.
RouteToApp RouteToApp = 12 [ (gogoproto.jsontag) = "route_to_app,omitempty" ];
// TeleportCluster is the name of the teleport cluster that this identity
// originated from. For TLS certs this may not be the same as cert issuer,
// in case of multi-hop requests that originate from a remote cluster.
string TeleportCluster = 13 [ (gogoproto.jsontag) = "teleport_cluster,omitempty" ];
// RouteToDatabase contains routing information for databases.
RouteToDatabase RouteToDatabase = 14 [ (gogoproto.jsontag) = "route_to_database,omitempty" ];
// DatabaseNames is a list of allowed database names.
repeated string DatabaseNames = 15 [ (gogoproto.jsontag) = "database_names,omitempty" ];
// DatabaseUsers is a list of allowed database users.
repeated string DatabaseUsers = 16 [ (gogoproto.jsontag) = "database_users,omitempty" ];
// MFAVerified is the UUID of an MFA device when this Identity was
// confirmed immediately after an MFA check.
string MFAVerified = 17 [ (gogoproto.jsontag) = "mfa_device,omitempty" ];
// ClientIP is an observed IP of the client that this Identity represents.
string ClientIP = 18 [ (gogoproto.jsontag) = "client_ip,omitempty" ];
// AWSRoleARNs is a list of allowed AWS role ARNs user can assume.
repeated string AWSRoleARNs = 19 [ (gogoproto.jsontag) = "aws_role_arns,omitempty" ];
// ActiveRequests is a list of UUIDs of active requests for this Identity.
repeated string ActiveRequests = 20 [ (gogoproto.jsontag) = "access_requests,omitempty" ];
// DisallowReissue is a flag that, if set, instructs the auth server to
// deny any attempts to reissue new certificates while authenticated with
// this certificate.
bool DisallowReissue = 21 [ (gogoproto.jsontag) = "disallow_reissue,omitempty" ];
}

// RouteToApp contains parameters for application access certificate requests.
message RouteToApp {
// Name is the application name certificate is being requested for.
string Name = 1 [ (gogoproto.jsontag) = "name" ];
// SessionID is the ID of the application session.
string SessionID = 2 [ (gogoproto.jsontag) = "session_id" ];
// PublicAddr is the application public address.
string PublicAddr = 3 [ (gogoproto.jsontag) = "public_addr" ];
// ClusterName is the cluster where the application resides.
string ClusterName = 4 [ (gogoproto.jsontag) = "cluster_name" ];
// AWSRoleARN is the AWS role to assume when accessing AWS API.
string AWSRoleARN = 5 [ (gogoproto.jsontag) = "aws_role_arn,omitempty" ];
}

// RouteToDatabase combines parameters for database service routing information.
message RouteToDatabase {
// ServiceName is the Teleport database proxy service name the cert is for.
string ServiceName = 1 [ (gogoproto.jsontag) = "service_name" ];
// Protocol is the type of the database the cert is for.
string Protocol = 2 [ (gogoproto.jsontag) = "protocol" ];
// Username is an optional database username to embed.
string Username = 3 [ (gogoproto.jsontag) = "username,omitempty" ];
// Database is an optional database name to embed.
string Database = 4 [ (gogoproto.jsontag) = "database,omitempty" ];
}
4 changes: 4 additions & 0 deletions api/types/events/oneof.go
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,10 @@ func ToOneOf(in AuditEvent) (*OneOf, error) {
out.Event = &OneOf_AccessRequestDelete{
AccessRequestDelete: e,
}
case *CertificateCreate:
out.Event = &OneOf_CertificateCreate{
CertificateCreate: e,
}
default:
return nil, trace.BadParameter("event type %T is not supported", in)
}
Expand Down
14 changes: 14 additions & 0 deletions lib/auth/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -1019,6 +1019,20 @@ func (a *Server) generateUserCert(req certRequest) (*proto.Certs, error) {
if err != nil {
return nil, trace.Wrap(err)
}

eventIdentity := identity.GetEventIdentity()
eventIdentity.Expires = certRequest.NotAfter
if a.emitter.EmitAuditEvent(a.closeCtx, &apievents.CertificateCreate{
Metadata: apievents.Metadata{
Type: events.CertificateCreateEvent,
Code: events.CertificateCreateCode,
},
CertificateType: apievents.CertificateType_USER,
Identity: &eventIdentity,
}); err != nil {
log.WithError(err).Warn("Failed to emit certificate create event.")
}

return &proto.Certs{
SSH: sshCert,
TLS: tlsCert,
Expand Down
3 changes: 3 additions & 0 deletions lib/events/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -456,6 +456,9 @@ const (
// WindowsDesktopSessionEndEvent is emitted when a user disconnects
// from a desktop.
WindowsDesktopSessionEndEvent = "windows.desktop.session.end"

// CertificateCreateEvent is emitted when a certificate is issued.
CertificateCreateEvent = "cert.create"
)

const (
Expand Down
3 changes: 3 additions & 0 deletions lib/events/codes.go
Original file line number Diff line number Diff line change
Expand Up @@ -474,4 +474,7 @@ const (
LockCreatedCode = "TLK00I"
// LockDeletedCode is the lock deleted event code.
LockDeletedCode = "TLK01I"

// CertificateCreateCode is the certificate issuance event code.
CertificateCreateCode = "T1010I"
)
2 changes: 2 additions & 0 deletions lib/events/dynamic.go
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,8 @@ func FromEventFields(fields EventFields) (apievents.AuditEvent, error) {
e = &events.SessionConnect{}
case AccessRequestDeleteEvent:
e = &events.AccessRequestDelete{}
case CertificateCreateEvent:
e = &events.CertificateCreate{}
default:
return nil, trace.BadParameter("unknown event type: %q", eventType)
}
Expand Down
53 changes: 53 additions & 0 deletions lib/tlsca/ca.go
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,59 @@ func (id *Identity) GetRouteToApp() (RouteToApp, error) {
return id.RouteToApp, nil
}

func (id *Identity) GetEventIdentity() events.Identity {
// leave a nil instead of a zero struct so the field doesn't appear when
// serialized as json
var routeToApp *events.RouteToApp
if id.RouteToApp != (RouteToApp{}) {
routeToApp = &events.RouteToApp{
Name: id.RouteToApp.Name,
SessionID: id.RouteToApp.SessionID,
PublicAddr: id.RouteToApp.PublicAddr,
ClusterName: id.RouteToApp.ClusterName,
AWSRoleARN: id.RouteToApp.AWSRoleARN,
}
}
var routeToDatabase *events.RouteToDatabase
if id.RouteToDatabase != (RouteToDatabase{}) {
routeToDatabase = &events.RouteToDatabase{
ServiceName: id.RouteToDatabase.ServiceName,
Protocol: id.RouteToDatabase.Protocol,
Username: id.RouteToDatabase.Username,
Database: id.RouteToDatabase.Database,
}
}

traits, err := events.EncodeMapStrings(id.Traits)
if err != nil {
log.WithError(err).Debug("Failed to encode traits.")
}

return events.Identity{
Username: id.Username,
Impersonator: id.Impersonator,
Groups: id.Groups,
Usage: id.Usage,
Principals: id.Principals,
KubernetesGroups: id.KubernetesGroups,
KubernetesUsers: id.KubernetesUsers,
Expires: id.Expires,
RouteToCluster: id.RouteToCluster,
KubernetesCluster: id.KubernetesCluster,
Traits: traits,
RouteToApp: routeToApp,
TeleportCluster: id.TeleportCluster,
RouteToDatabase: routeToDatabase,
DatabaseNames: id.DatabaseNames,
DatabaseUsers: id.DatabaseUsers,
MFAVerified: id.MFAVerified,
ClientIP: id.ClientIP,
AWSRoleARNs: id.AWSRoleARNs,
ActiveRequests: id.ActiveRequests,
DisallowReissue: id.DisallowReissue,
}
}

// CheckAndSetDefaults checks and sets default values
func (id *Identity) CheckAndSetDefaults() error {
if id.Username == "" {
Expand Down

0 comments on commit c6fe31f

Please sign in to comment.