Add audit events for CreatePairedDeviceEnrollToken (#68835)

* Add audit events for CreatePairedDeviceEnrollToken

* Add Web UI formatter for new audit events

* Update audit events reference

* Use `OSType` from generated protos
This commit is contained in:
Maja Cieślak
2026-07-24 10:20:49 +00:00
committed by GitHub
parent f7eba299d7
commit e71a23f144
12 changed files with 238 additions and 12 deletions
+1
View File
@@ -5,6 +5,7 @@ inputs:
paths:
- api/proto/teleport/userpreferences/
- api/proto/teleport/desktop/
- api/proto/teleport/devicetrust/v1/os_type.proto
- proto/prehog/
- proto/teleport/lib/teleterm/
- proto/teleport/lib/vnet/diag/
+52
View File
@@ -3633,6 +3633,58 @@ Example:
}
```
## device.enroll_pairing.request
There are multiple events with the `device.enroll_pairing.request` type.
### TV010I
Device Enroll Pairing Requested
Example:
```json
{
"cluster_name": "im-a-cluster-name",
"code": "TV010I",
"device": {
"asset_tag": "M2CQVQV64R",
"device_id": "",
"os_type": 4
},
"ei": 0,
"event": "device.enroll_pairing.request",
"success": true,
"time": "2024-04-08T19:37:48.1Z",
"uid": "a7b3e9f1-4d5c-6a8b-9012-def345678901",
"user": "llama"
}
```
### TV010W
Device Enroll Pairing Request Failed
Example:
```json
{
"cluster_name": "im-a-cluster-name",
"code": "TV010W",
"device": {
"asset_tag": "M2CQVQV64R",
"device_id": "",
"os_type": 4
},
"ei": 0,
"error": "invalid pairing token",
"event": "device.enroll_pairing.request",
"success": false,
"time": "2024-04-08T19:38:48.1Z",
"uid": "a7b3e9f1-4d5c-6a8b-9012-def345678902"
}
```
## device.token.create
Device Enroll Token Created
+61
View File
@@ -0,0 +1,61 @@
/* eslint-disable */
// @generated by protobuf-ts 2.9.3 with parameter add_pb_suffix,eslint_disable,generate_dependencies,server_grpc1,ts_nocheck
// @generated from protobuf file "teleport/devicetrust/v1/os_type.proto" (package "teleport.devicetrust.v1", syntax proto3)
// tslint:disable
// @ts-nocheck
//
// Copyright 2022 Gravitational, Inc
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
/**
* OSType represents the operating system of a device.
*
* @generated from protobuf enum teleport.devicetrust.v1.OSType
*/
export enum OSType {
/**
* @generated from protobuf enum value: OS_TYPE_UNSPECIFIED = 0;
*/
OS_TYPE_UNSPECIFIED = 0,
/**
* Linux.
*
* @generated from protobuf enum value: OS_TYPE_LINUX = 1;
*/
OS_TYPE_LINUX = 1,
/**
* macOS.
*
* @generated from protobuf enum value: OS_TYPE_MACOS = 2;
*/
OS_TYPE_MACOS = 2,
/**
* Windows.
*
* @generated from protobuf enum value: OS_TYPE_WINDOWS = 3;
*/
OS_TYPE_WINDOWS = 3,
/**
* iOS.
*
* @generated from protobuf enum value: OS_TYPE_IOS = 4;
*/
OS_TYPE_IOS = 4,
/**
* iPadOS.
*
* @generated from protobuf enum value: OS_TYPE_IPADOS = 5;
*/
OS_TYPE_IPADOS = 5
}
+5
View File
@@ -707,6 +707,11 @@ const (
// A confirmed web authentication means the WebSession itself now holds
// augmented TLS and SSH certificates.
DeviceAuthenticateConfirmEvent = "device.authenticate.confirm"
// DeviceEnrollPairingRequestEvent is emitted when a device presents an enroll
// pairing token to request enrollment. On success the pairing transitions to
// awaiting approval. It is also emitted on failure (e.g. an invalid token),
// before any transition, in which case there is no associated user.
DeviceEnrollPairingRequestEvent = "device.enroll_pairing.request"
// BotJoinEvent is emitted when a bot joins
BotJoinEvent = "bot.join"
+5
View File
@@ -525,6 +525,11 @@ const (
DeviceWebTokenCreateCode = "TV008I"
// DeviceAuthenticateConfirmCode is the device authentication confirm code.
DeviceAuthenticateConfirmCode = "TV009I"
// DeviceEnrollPairingRequestCode is the device enroll pairing request code.
DeviceEnrollPairingRequestCode = "TV010I"
// DeviceEnrollPairingRequestFailureCode is the device enroll pairing request
// failure code.
DeviceEnrollPairingRequestFailureCode = "TV010W"
// LoginRuleCreateCode is the login rule create code.
LoginRuleCreateCode = "TLR00I"
+2 -1
View File
@@ -296,7 +296,8 @@ func FromEventFields(fields EventFields) (events.AuditEvent, error) {
DeviceAuthenticateEvent,
DeviceEnrollTokenCreateEvent,
DeviceWebTokenCreateEvent,
DeviceAuthenticateConfirmEvent:
DeviceAuthenticateConfirmEvent,
DeviceEnrollPairingRequestEvent:
e = &events.DeviceEvent2{}
case LockCreatedEvent:
e = &events.LockCreate{}
+1
View File
@@ -155,6 +155,7 @@ var eventsMap = map[string]apievents.AuditEvent{
DeviceEnrollTokenCreateEvent: &apievents.DeviceEvent2{},
DeviceWebTokenCreateEvent: &apievents.DeviceEvent2{},
DeviceAuthenticateConfirmEvent: &apievents.DeviceEvent2{},
DeviceEnrollPairingRequestEvent: &apievents.DeviceEvent2{},
LockCreatedEvent: &apievents.LockCreate{},
LockDeletedEvent: &apievents.LockDelete{},
RecoveryCodeGeneratedEvent: &apievents.RecoveryCodeGenerate{},
@@ -206,6 +206,8 @@ export const EventIconMap: Record<EventCode, any> = {
[eventCodes.DEVICE_UPDATE]: Icons.Info,
[eventCodes.DEVICE_WEB_TOKEN_CREATE]: Icons.Info,
[eventCodes.DEVICE_AUTHENTICATE_CONFIRM]: Icons.Info,
[eventCodes.DEVICE_ENROLL_PAIRING_REQUEST]: Icons.Info,
[eventCodes.DEVICE_ENROLL_PAIRING_REQUEST_FAILURE]: Icons.Info,
[eventCodes.MFA_DEVICE_ADD]: Icons.Info,
[eventCodes.MFA_DEVICE_DELETE]: Icons.Info,
[eventCodes.BILLING_CARD_CREATE]: Icons.CreditCard,
@@ -3291,6 +3291,36 @@ export const events = [
user: 'llama',
user_kind: 1,
},
{
cluster_name: 'im-a-cluster-name',
code: 'TV010I',
device: {
asset_tag: 'M2CQVQV64R',
device_id: '',
os_type: 4,
},
ei: 0,
event: 'device.enroll_pairing.request',
success: true,
time: '2024-04-08T19:37:48.1Z',
uid: 'a7b3e9f1-4d5c-6a8b-9012-def345678901',
user: 'llama',
},
{
cluster_name: 'im-a-cluster-name',
code: 'TV010W',
device: {
asset_tag: 'M2CQVQV64R',
device_id: '',
os_type: 4,
},
ei: 0,
error: 'invalid pairing token',
event: 'device.enroll_pairing.request',
success: false,
time: '2024-04-08T19:38:48.1Z',
uid: 'a7b3e9f1-4d5c-6a8b-9012-def345678902',
},
{
cluster_name: 'im-a-cluster-name',
code: 'TLR00I',
@@ -17,6 +17,7 @@
*/
import { PagerPosition } from 'design/DataTable/types';
import { OSType } from 'gen-proto-ts/teleport/devicetrust/v1/os_type_pb';
export type TrustedDevice = {
id: string;
@@ -56,6 +57,28 @@ export type DeviceSource = {
*/
export type TrustedDeviceOSType = TrustedDevice['osType'];
export function osTypeLabel(
osType: OSType
): TrustedDevice['osType'] | undefined {
switch (osType) {
case OSType.OS_TYPE_LINUX:
return 'Linux';
case OSType.OS_TYPE_MACOS:
return 'macOS';
case OSType.OS_TYPE_WINDOWS:
return 'Windows';
case OSType.OS_TYPE_IOS:
return 'iOS';
case OSType.OS_TYPE_IPADOS:
return 'iPadOS';
case OSType.OS_TYPE_UNSPECIFIED:
return undefined;
default:
osType satisfies never;
return undefined;
}
}
export type TrustedDeviceResponse = {
items: TrustedDevice[];
startKey: string;
@@ -20,6 +20,8 @@ import { formatDistanceStrict } from 'date-fns';
import { pluralize } from 'shared/utils/text';
import { osTypeLabel } from 'teleport/DeviceTrust/types';
import {
Event,
EventCode,
@@ -1491,6 +1493,20 @@ export const formatters: Formatters = {
? `User [${user}] has confirmed device web authentication`
: `User [${user}] has failed to confirm device web authentication`,
},
[eventCodes.DEVICE_ENROLL_PAIRING_REQUEST]: {
type: 'device.enroll_pairing.request',
desc: 'Device Enroll Pairing Requested',
format: ({ user, device }) =>
`Device enrollment was requested for user [${user}]${formatDevice(device)}`,
},
[eventCodes.DEVICE_ENROLL_PAIRING_REQUEST_FAILURE]: {
type: 'device.enroll_pairing.request',
desc: 'Device Enroll Pairing Request Failed',
format: ({ device, error }) =>
error
? `Device enrollment request failed${formatDevice(device)}: ${error}`
: `Device enrollment request failed${formatDevice(device)}`,
},
[eventCodes.X11_FORWARD]: {
type: 'x11-forward',
desc: 'X11 Forwarding Requested',
@@ -2989,3 +3005,22 @@ export function formatSqn({
}
return `${scope}::${name}`;
}
// formatDevice renders the OS type and serial number (carried as asset_tag) of a device audit
// event's device metadata, e.g. " (OS [iOS], serial number [ABC123])". Returns an empty string when
// neither is present.
const formatDevice = (device?: {
asset_tag?: string;
os_type?: number;
}): string => {
const parts: string[] = [];
// Account for os_type possibly being 0 (UNSPECIFIED) with os_type != null.
const os = device?.os_type != null ? osTypeLabel(device.os_type) : undefined;
if (os) {
parts.push(`OS [${os}]`);
}
if (device?.asset_tag) {
parts.push(`serial number [${device.asset_tag}]`);
}
return parts.length ? ` (${parts.join(', ')})` : '';
};
@@ -30,6 +30,7 @@ export const eventGroupTypes = {
subsystem: 'Subsystem Request',
'user.login': 'User Logins',
'spiffe.svid.issued': 'SPIFFE SVID Issuance',
'device.enroll_pairing.request': 'Device Enroll Pairing Request',
};
/**
@@ -43,7 +44,6 @@ export const eventGroupTypes = {
* These duplicated event types needs to be defined in `eventGroupTypes` object
* 3: Define icons for events under `EventTypeCell.tsx` file
* 4: Add an actual JSON event to the fixtures file in `src/Audit/fixtures/index.ts`.
* 5: Check fixture is rendered in storybook, then update snapshot for `Audit.story.test.tsx`
*/
export const eventCodes = {
ACCESS_REQUEST_CREATED: 'T5000I',
@@ -138,6 +138,8 @@ export const eventCodes = {
DEVICE_UPDATE: 'TV007I',
DEVICE_WEB_TOKEN_CREATE: 'TV008I',
DEVICE_AUTHENTICATE_CONFIRM: 'TV009I',
DEVICE_ENROLL_PAIRING_REQUEST: 'TV010I',
DEVICE_ENROLL_PAIRING_REQUEST_FAILURE: 'TV010W',
EXEC_FAILURE: 'T3002E',
EXEC: 'T3002I',
GITHUB_CONNECTOR_CREATED: 'T8000I',
@@ -1445,6 +1447,15 @@ export type RawEvents = {
[eventCodes.DEVICE_AUTHENTICATE_CONFIRM]: RawDeviceEvent<
typeof eventCodes.DEVICE_AUTHENTICATE_CONFIRM
>;
[eventCodes.DEVICE_ENROLL_PAIRING_REQUEST]: RawDeviceEvent<
typeof eventCodes.DEVICE_ENROLL_PAIRING_REQUEST
>;
// The failure event carries no user (the pairing lookup that resolves the user
// is what failed) and surfaces the failure reason via the error field.
[eventCodes.DEVICE_ENROLL_PAIRING_REQUEST_FAILURE]: RawEvent<
typeof eventCodes.DEVICE_ENROLL_PAIRING_REQUEST_FAILURE,
Merge<DeviceEventFields, { error?: string }>
>;
[eventCodes.UNKNOWN]: RawEvent<
typeof eventCodes.UNKNOWN,
{
@@ -2597,16 +2608,15 @@ type RawEventData<T extends EventCode> = RawEvent<
}
>;
type RawDeviceEvent<T extends EventCode> = RawEvent<
T,
{
device: { asset_tag: string; device_id: string; os_type: number };
success?: boolean;
user?: string;
// status from "legacy" event format.
status?: { success: boolean };
}
>;
type DeviceEventFields = {
device: { asset_tag: string; device_id: string; os_type: number };
success?: boolean;
user?: string;
// status from "legacy" event format.
status?: { success: boolean };
};
type RawDeviceEvent<T extends EventCode> = RawEvent<T, DeviceEventFields>;
type RawEventCommand<T extends EventCode> = RawEvent<
T,