Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add the cert.create event #9822

Merged
merged 5 commits into from
Feb 8, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3,820 changes: 3,017 additions & 803 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 @@ -19,6 +19,8 @@ import "gogoproto/gogo.proto";
import "google/protobuf/timestamp.proto";
import "google/protobuf/struct.proto";

import "github.com/gravitational/teleport/api/types/wrappers/wrappers.proto";

option (gogoproto.marshaler_all) = true;
option (gogoproto.unmarshaler_all) = true;
option (gogoproto.goproto_getters_all) = false;
Expand Down Expand Up @@ -1678,6 +1680,19 @@ message WindowsDesktopSessionEnd {
map<string, string> DesktopLabels = 8 [ (gogoproto.jsontag) = "desktop_labels" ];
}

// 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.
string 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 +1764,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 +1795,96 @@ 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). Field names match other names already used in other events
// rather than the field names in tlsca.Identity.
message Identity {
// User is a username or name of the node connection
string User = 1 [ (gogoproto.jsontag) = "user,omitempty" ];
// Impersonator is a username of a user impersonating this user
string Impersonator = 2 [ (gogoproto.jsontag) = "impersonator,omitempty" ];
// Roles is a list of groups (Teleport roles) encoded in the identity
repeated string Roles = 3 [ (gogoproto.jsontag) = "roles,omitempty" ];
// Usage is a list of usage restrictions encoded in the identity
repeated string Usage = 4 [ (gogoproto.jsontag) = "usage,omitempty" ];
// Logins is a list of Unix logins allowed.
repeated string Logins = 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.
wrappers.LabelValues Traits = 11 [
(gogoproto.nullable) = false,
(gogoproto.jsontag) = "traits,omitempty",
(gogoproto.customtype) = "github.com/gravitational/teleport/api/types/wrappers.Traits"
];
// 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" ];
// MFADeviceUUID is the UUID of an MFA device when this Identity was
// confirmed immediately after an MFA check.
string MFADeviceUUID = 17 [ (gogoproto.jsontag) = "mfa_device_uuid,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" ];
// AccessRequests is a list of UUIDs of active requests for this Identity.
repeated string AccessRequests = 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 @@ -1138,6 +1138,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: events.CertificateTypeUser,
Identity: &eventIdentity,
}); err != nil {
log.WithError(err).Warn("Failed to emit certificate create event.")
}

return &proto.Certs{
SSH: sshCert,
TLS: tlsCert,
Expand Down
6 changes: 6 additions & 0 deletions lib/events/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -456,6 +456,12 @@ 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"

// CertificateTypeUser is the CertificateType for certificate events pertaining to user certificates.
CertificateTypeUser = "user"
)

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 = "TC000I"
)
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
48 changes: 48 additions & 0 deletions lib/tlsca/ca.go
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,54 @@ 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,
}
}

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

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