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

Added activity item for fleetd enrollment with host serial and display name. (#23790) #23926

Merged
merged 1 commit into from
Nov 18, 2024
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
1 change: 1 addition & 0 deletions changes/22810-fleetd-enroll-activity
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Added activity item for fleetd enrollment with host serial and display name.
1 change: 1 addition & 0 deletions cmd/osquery-perf/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -1413,6 +1413,7 @@ func (a *agent) orbitEnroll() error {
EnrollSecret: a.EnrollSecret,
HardwareUUID: a.UUID,
HardwareSerial: a.SerialNumber,
Hostname: a.CachedString("hostname"),
}
jsonBytes, err := json.Marshal(params)
if err != nil {
Expand Down
17 changes: 17 additions & 0 deletions docs/Contributing/Audit-logs.md
Original file line number Diff line number Diff line change
Expand Up @@ -521,6 +521,23 @@ This activity contains the following fields:
}
```

## fleet_enrolled

Generated when a host is enrolled to Fleet (Fleet's agent fleetd is installed).

This activity contains the following fields:
- "host_serial": Serial number of the host.
- "host_display_name": Display name of the host.

#### Example

```json
{
"host_serial": "B04FL3ALPT21",
"host_display_name": "WIN-DESKTOP-JGS78KJ7C"
}
```

## mdm_enrolled

Generated when a host is enrolled in Fleet's MDM.
Expand Down
1 change: 1 addition & 0 deletions frontend/interfaces/activity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export enum ActivityType {
UserDeletedGlobalRole = "deleted_user_global_role",
UserChangedTeamRole = "changed_user_team_role",
UserDeletedTeamRole = "deleted_user_team_role",
FleetEnrolled = "fleet_enrolled",
MdmEnrolled = "mdm_enrolled",
MdmUnenrolled = "mdm_unenrolled",
EditedMacosMinVersion = "edited_macos_min_version",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,14 @@ const TAGGED_TEMPLATES = {
</>
);
},
fleetEnrolled: (activity: IActivity) => {
const hostDisplayName = activity.details?.host_display_name ? (
<b>{activity.details.host_display_name}</b>
) : (
"A host"
);
return <>{hostDisplayName} enrolled in Fleet.</>;
},
mdmEnrolled: (activity: IActivity) => {
if (activity.details?.mdm_platform === "microsoft") {
return (
Expand Down Expand Up @@ -1167,6 +1175,9 @@ const getDetail = (
case ActivityType.UserDeletedTeamRole: {
return TAGGED_TEMPLATES.userDeletedTeamRole(activity);
}
case ActivityType.FleetEnrolled: {
return TAGGED_TEMPLATES.fleetEnrolled(activity);
}
case ActivityType.MdmEnrolled: {
return TAGGED_TEMPLATES.mdmEnrolled(activity);
}
Expand Down
2 changes: 2 additions & 0 deletions orbit/changes/22810-fleetd-enroll-activity
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Added computer_name and hardware_model for fleetd enrollment.
Added serial number for fleetd enrollment for Windows hosts (already present for macOS and Linux).
26 changes: 18 additions & 8 deletions orbit/cmd/orbit/orbit.go
Original file line number Diff line number Diff line change
Expand Up @@ -694,6 +694,8 @@ func main() {
HardwareUUID: osqueryHostInfo.HardwareUUID,
Hostname: osqueryHostInfo.Hostname,
Platform: osqueryHostInfo.Platform,
ComputerName: osqueryHostInfo.ComputerName,
HardwareModel: osqueryHostInfo.HardwareModel,
}

if runtime.GOOS == "darwin" {
Expand Down Expand Up @@ -737,13 +739,6 @@ func main() {
orbitHostInfo.OsqueryIdentifier = osqueryHostInfo.InstanceID
}

// The hardware serial was not sent when Windows MDM was implemented,
// thus we clear its value here to not break any existing enroll functionality
// on the server.
if runtime.GOOS == "windows" {
orbitHostInfo.HardwareSerial = ""
}

var (
options []osquery.Option
// optionsAfterFlagfile is populated with options that will be set after the '--flagfile' argument
Expand Down Expand Up @@ -1697,6 +1692,10 @@ type osqueryHostInfo struct {
HardwareSerial string `json:"hardware_serial"`
// Hostname is the device's hostname (extracted from `system_info` osquery table).
Hostname string `json:"hostname"`
// ComputerName is the friendly computer name (optional) (extracted from `system_info` osquery table).
ComputerName string `json:"computer_name"`
// HardwareModel is the device's hardware model (extracted from `system_info` osquery table).
HardwareModel string `json:"hardware_model"`
// Platform is the device's platform as defined by osquery (extracted from `os_version` osquery table).
Platform string `json:"platform"`
// InstanceID is the osquery's randomly generated instance ID
Expand All @@ -1714,7 +1713,18 @@ func getHostInfo(osqueryPath string, osqueryDBPath string) (*osqueryHostInfo, er
if err := os.MkdirAll(filepath.Dir(osqueryDBPath), constant.DefaultDirMode); err != nil {
return nil, err
}
const systemQuery = "SELECT si.uuid, si.hardware_serial, si.hostname, os.platform, os.version as os_version, oi.instance_id, oi.version as osquery_version FROM system_info si, os_version os, osquery_info oi"
const systemQuery = `
SELECT
si.uuid,
si.hardware_serial,
si.hostname,
si.computer_name,
si.hardware_model,
os.platform,
os.version as os_version,
oi.instance_id,
oi.version as osquery_version
FROM system_info si, os_version os, osquery_info oi`
args := []string{
"-S",
"--database_path", osqueryDBPath,
Expand Down
29 changes: 24 additions & 5 deletions server/datastore/mysql/hosts.go
Original file line number Diff line number Diff line change
Expand Up @@ -1904,9 +1904,20 @@ func (ds *Datastore) EnrollOrbit(ctx context.Context, isMDMEnabled bool, hostInf
}
// NOTE: allow an empty serial, currently it is empty for Windows.

var host fleet.Host
host := fleet.Host{
ComputerName: hostInfo.ComputerName,
Hostname: hostInfo.Hostname,
HardwareModel: hostInfo.HardwareModel,
HardwareSerial: hostInfo.HardwareSerial,
}
err := ds.withRetryTxx(ctx, func(tx sqlx.ExtContext) error {
enrolledHostInfo, err := matchHostDuringEnrollment(ctx, tx, orbitEnroll, isMDMEnabled, hostInfo.OsqueryIdentifier, hostInfo.HardwareUUID, hostInfo.HardwareSerial)
serialToMatch := hostInfo.HardwareSerial
if hostInfo.Platform == "windows" {
// For Windows, don't match by serial number to retain legacy functionality.
serialToMatch = ""
}
enrolledHostInfo, err := matchHostDuringEnrollment(ctx, tx, orbitEnroll, isMDMEnabled, hostInfo.OsqueryIdentifier,
hostInfo.HardwareUUID, serialToMatch)

// If the osquery identifier that osqueryd will use was not sent by Orbit, then use the hardware UUID as identifier
// (using the hardware UUID is Orbit's default behavior).
Expand Down Expand Up @@ -1936,13 +1947,17 @@ func (ds *Datastore) EnrollOrbit(ctx context.Context, isMDMEnabled bool, hostInf
uuid = COALESCE(NULLIF(uuid, ''), ?),
osquery_host_id = COALESCE(NULLIF(osquery_host_id, ''), ?),
hardware_serial = COALESCE(NULLIF(hardware_serial, ''), ?),
computer_name = COALESCE(NULLIF(computer_name, ''), ?),
hardware_model = COALESCE(NULLIF(hardware_model, ''), ?),
team_id = ?
WHERE id = ?`
_, err := tx.ExecContext(ctx, sqlUpdate,
orbitNodeKey,
hostInfo.HardwareUUID,
osqueryIdentifier,
hostInfo.HardwareSerial,
hostInfo.ComputerName,
hostInfo.HardwareModel,
teamID,
enrolledHostInfo.ID,
)
Expand Down Expand Up @@ -1977,8 +1992,10 @@ func (ds *Datastore) EnrollOrbit(ctx context.Context, isMDMEnabled bool, hostInf
orbit_node_key,
hardware_serial,
hostname,
computer_name,
hardware_model,
platform
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, 1, ?, ?, ?, ?)
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, 1, ?, ?, ?, ?, ?, ?)
`
result, err := tx.ExecContext(ctx, sqlInsert,
zeroTime,
Expand All @@ -1992,16 +2009,18 @@ func (ds *Datastore) EnrollOrbit(ctx context.Context, isMDMEnabled bool, hostInf
orbitNodeKey,
hostInfo.HardwareSerial,
hostInfo.Hostname,
hostInfo.ComputerName,
hostInfo.HardwareModel,
hostInfo.Platform,
)
if err != nil {
return ctxerr.Wrap(ctx, err, "orbit enroll error inserting host details")
}
hostID, _ := result.LastInsertId()
const sqlHostDisplayName = `
INSERT INTO host_display_names (host_id, display_name) VALUES (?, '')
INSERT INTO host_display_names (host_id, display_name) VALUES (?, ?)
`
_, err = tx.ExecContext(ctx, sqlHostDisplayName, hostID)
_, err = tx.ExecContext(ctx, sqlHostDisplayName, hostID, host.DisplayName())
if err != nil {
return ctxerr.Wrap(ctx, err, "insert host_display_names")
}
Expand Down
28 changes: 25 additions & 3 deletions server/datastore/mysql/hosts_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8061,6 +8061,11 @@ func testHostsGetUnverifiedDiskEncryptionKeys(t *testing.T, ds *Datastore) {
func testHostsEnrollOrbit(t *testing.T, ds *Datastore) {
ctx := context.Background()

const (
computerName = "My computer"
hardwareModel = "CMP-1000"
)

createHost := func(osqueryID, serial string) *fleet.Host {
dbZeroTime := time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC)
var osqueryIDPtr *string
Expand All @@ -8075,6 +8080,8 @@ func testHostsEnrollOrbit(t *testing.T, ds *Datastore) {
DetailUpdatedAt: dbZeroTime,
OsqueryHostID: osqueryIDPtr,
RefetchRequested: true,
ComputerName: computerName,
HardwareModel: hardwareModel,
})
require.NoError(t, err)
return h
Expand Down Expand Up @@ -8112,10 +8119,19 @@ func testHostsEnrollOrbit(t *testing.T, ds *Datastore) {
h, err = ds.EnrollOrbit(ctx, true, fleet.OrbitHostInfo{
HardwareUUID: *hBoth.OsqueryHostID,
HardwareSerial: hBoth.HardwareSerial,
ComputerName: hBoth.ComputerName,
HardwareModel: hBoth.HardwareModel,
}, uuid.New().String(), nil)
require.NoError(t, err)
require.Equal(t, hBoth.ID, h.ID)
require.Empty(t, h.HardwareSerial) // this is just to prove that it was loaded based on osquery_host_id, the serial was not set in the lookup
assert.Equal(t, hBoth.HardwareSerial, h.HardwareSerial)
assert.Equal(t, hBoth.ComputerName, h.ComputerName)
assert.Equal(t, hBoth.HardwareModel, h.HardwareModel)
h, err = ds.Host(ctx, h.ID)
require.NoError(t, err)
assert.Equal(t, hBoth.HardwareSerial, h.HardwareSerial)
assert.Equal(t, hBoth.ComputerName, h.ComputerName)
assert.Equal(t, hBoth.HardwareModel, h.HardwareModel)

// enroll with osquery id from hBoth and serial from hSerialNoOsquery (should
// use the osquery match)
Expand All @@ -8125,14 +8141,17 @@ func testHostsEnrollOrbit(t *testing.T, ds *Datastore) {
}, uuid.New().String(), nil)
require.NoError(t, err)
require.Equal(t, hBoth.ID, h.ID)
require.Empty(t, h.HardwareSerial)
assert.Equal(t, hSerialNoOsquery.HardwareSerial, h.HardwareSerial)

// enroll with no match, will create a new one
newSerial := uuid.NewString()
h, err = ds.EnrollOrbit(ctx, true, fleet.OrbitHostInfo{
HardwareUUID: uuid.New().String(),
HardwareSerial: uuid.New().String(),
HardwareSerial: newSerial,
Hostname: "foo2",
Platform: "darwin",
ComputerName: "New computer",
HardwareModel: "ABC-3000",
}, uuid.New().String(), nil)
require.NoError(t, err)
require.Greater(t, h.ID, hBoth.ID)
Expand All @@ -8141,6 +8160,9 @@ func testHostsEnrollOrbit(t *testing.T, ds *Datastore) {
require.NoError(t, err)
require.Equal(t, "foo2", h.Hostname)
require.Equal(t, "darwin", h.Platform)
assert.Equal(t, "New computer", h.ComputerName)
assert.Equal(t, "ABC-3000", h.HardwareModel)
assert.Equal(t, newSerial, h.HardwareSerial)

// simulate a "corrupt database" where two hosts have the same serial and
// enroll by serial should always use the same (the smaller ID)
Expand Down
20 changes: 20 additions & 0 deletions server/fleet/activities.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ var ActivityDetailsList = []ActivityDetails{
ActivityTypeChangedUserTeamRole{},
ActivityTypeDeletedUserTeamRole{},

ActivityTypeFleetEnrolled{},
ActivityTypeMDMEnrolled{},
ActivityTypeMDMUnenrolled{},

Expand Down Expand Up @@ -795,6 +796,25 @@ func (a ActivityTypeDeletedUserTeamRole) Documentation() (activity string, detai
}`
}

type ActivityTypeFleetEnrolled struct {
HostSerial string `json:"host_serial"`
HostDisplayName string `json:"host_display_name"`
}

func (a ActivityTypeFleetEnrolled) ActivityName() string {
return "fleet_enrolled"
}

func (a ActivityTypeFleetEnrolled) Documentation() (activity string, details string, detailsExample string) {
return `Generated when a host is enrolled to Fleet (Fleet's agent fleetd is installed).`,
`This activity contains the following fields:
- "host_serial": Serial number of the host.
- "host_display_name": Display name of the host.`, `{
"host_serial": "B04FL3ALPT21",
"host_display_name": "WIN-DESKTOP-JGS78KJ7C"
}`
}

type ActivityTypeMDMEnrolled struct {
HostSerial string `json:"host_serial"`
HostDisplayName string `json:"host_display_name"`
Expand Down
4 changes: 4 additions & 0 deletions server/fleet/orbit.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,10 @@ type OrbitHostInfo struct {
//
// If not set, then the HardwareUUID is used/set as the osquery identifier.
OsqueryIdentifier string
// ComputerName is the device's friendly name (optional).
ComputerName string
// HardwareModel is the device's hardware model. For example: Standard PC (Q35 + ICH9, 2009)
HardwareModel string
}

// ExtensionInfo holds the data of a osquery extension to apply to an Orbit client.
Expand Down
14 changes: 14 additions & 0 deletions server/service/integration_mdm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2661,10 +2661,14 @@ func (s *integrationMDMTestSuite) TestEnrollOrbitAfterDEPSync() {
// enroll the host from orbit, it should match the host above via the serial
var resp EnrollOrbitResponse
hostUUID := uuid.New().String()
h.ComputerName = "My Mac"
h.HardwareModel = "MacBook Pro"
s.DoJSON("POST", "/api/fleet/orbit/enroll", EnrollOrbitRequest{
EnrollSecret: secret,
HardwareUUID: hostUUID, // will not match any existing host
HardwareSerial: h.HardwareSerial,
ComputerName: h.ComputerName,
HardwareModel: h.HardwareModel,
}, http.StatusOK, &resp)
require.NotEmpty(t, resp.OrbitNodeKey)

Expand All @@ -2674,11 +2678,21 @@ func (s *integrationMDMTestSuite) TestEnrollOrbitAfterDEPSync() {
s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d", h.ID), nil, http.StatusOK, &hostResp)
require.Equal(t, h.ID, hostResp.Host.ID)
require.NotEqual(t, dbZeroTime, hostResp.Host.LastEnrolledAt)
assert.Equal(t, h.ComputerName, hostResp.Host.ComputerName)
assert.Equal(t, h.HardwareModel, hostResp.Host.HardwareModel)
assert.Equal(t, h.HardwareSerial, hostResp.Host.HardwareSerial)
assert.Equal(t, h.DisplayName(), hostResp.Host.DisplayName)

got, err := s.ds.LoadHostByOrbitNodeKey(ctx, resp.OrbitNodeKey)
require.NoError(t, err)
require.Equal(t, h.ID, got.ID)

s.lastActivityMatches(
"fleet_enrolled",
fmt.Sprintf(`{"host_display_name": "%s", "host_serial": "%s"}`, h.DisplayName(), h.HardwareSerial),
0,
)

// enroll the host from osquery, it should match the same host
var osqueryResp enrollAgentResponse
osqueryID := uuid.New().String()
Expand Down
Loading
Loading