diff --git a/changes/issue-8982-ingest-encrypted-linux b/changes/issue-8982-ingest-encrypted-linux new file mode 100644 index 0000000000..9bd59c40a5 --- /dev/null +++ b/changes/issue-8982-ingest-encrypted-linux @@ -0,0 +1 @@ +* Fix how we are querying and ingesting disk encryption in linux to workaround an osquery bug. diff --git a/cmd/osquery-perf/agent.go b/cmd/osquery-perf/agent.go index 116b7f78ee..d4522fb745 100644 --- a/cmd/osquery-perf/agent.go +++ b/cmd/osquery-perf/agent.go @@ -991,6 +991,22 @@ func (a *agent) diskEncryption() []map[string]string { return []map[string]string{} } +func (a *agent) diskEncryptionLinux() []map[string]string { + // 50% of results have encryption enabled + enabled := rand.Intn(2) == 1 + if enabled { + return []map[string]string{ + {"path": "/etc", "encrypted": "0"}, + {"path": "/tmp", "encrypted": "0"}, + {"path": "/", "encrypted": "1"}, + } + } + return []map[string]string{ + {"path": "/etc", "encrypted": "0"}, + {"path": "/tmp", "encrypted": "0"}, + } +} + func (a *agent) processQuery(name, query string) (handled bool, results []map[string]string, status *fleet.OsqueryStatus) { const ( hostPolicyQueryPrefix = "fleet_policy_query_" @@ -1067,6 +1083,13 @@ func (a *agent) processQuery(name, query string) (handled bool, results []map[st results = a.diskSpace() } return true, results, &ss + + case strings.HasPrefix(name, hostDetailQueryPrefix+"disk_encryption_linux"): + ss := fleet.OsqueryStatus(rand.Intn(2)) + if ss == fleet.StatusOK { + results = a.diskEncryptionLinux() + } + return true, results, &ss case strings.HasPrefix(name, hostDetailQueryPrefix+"disk_encryption_"): ss := fleet.OsqueryStatus(rand.Intn(2)) if ss == fleet.StatusOK { diff --git a/server/service/osquery_utils/queries.go b/server/service/osquery_utils/queries.go index 1f8b276b68..3d41c177d7 100644 --- a/server/service/osquery_utils/queries.go +++ b/server/service/osquery_utils/queries.go @@ -585,9 +585,11 @@ FROM // osquery table on darwin and linux, it is always present. }, "disk_encryption_linux": { - Query: `SELECT 1 FROM (SELECT encrypted, path FROM disk_encryption FULL OUTER JOIN mounts ON mounts.device_alias = disk_encryption.name) WHERE encrypted = 1 AND path = '/';`, + // This query doesn't do any filtering as we've seen what's possibly an osquery bug because it's returning bad + // results if we filter further, so we'll do the filtering in Go. + Query: `SELECT de.encrypted, m.path FROM disk_encryption de JOIN mounts m ON m.device_alias = de.name;`, Platforms: fleet.HostLinuxOSs, - DirectIngestFunc: directIngestDiskEncryption, + DirectIngestFunc: directIngestDiskEncryptionLinux, // the "disk_encryption" table doesn't need a Discovery query as it is an official // osquery table on darwin and linux, it is always present. }, @@ -1289,6 +1291,23 @@ func directIngestMunkiInfo(ctx context.Context, logger log.Logger, host *fleet.H return ds.SetOrUpdateMunkiInfo(ctx, host.ID, rows[0]["version"], errList, warnList) } +func directIngestDiskEncryptionLinux(ctx context.Context, logger log.Logger, host *fleet.Host, ds fleet.Datastore, rows []map[string]string, failed bool) error { + if failed { + level.Error(logger).Log("op", "directIngestDiskEncryptionLinux", "err", "failed") + return nil + } + + encrypted := false + for _, row := range rows { + if row["path"] == "/" && row["encrypted"] == "1" { + encrypted = true + break + } + } + + return ds.SetOrUpdateHostDisksEncryption(ctx, host.ID, encrypted) +} + func directIngestDiskEncryption(ctx context.Context, logger log.Logger, host *fleet.Host, ds fleet.Datastore, rows []map[string]string, failed bool) error { if failed { level.Error(logger).Log("op", "directIngestDiskEncryption", "err", "failed") diff --git a/server/service/osquery_utils/queries_test.go b/server/service/osquery_utils/queries_test.go index 924d7a2859..ae6a690821 100644 --- a/server/service/osquery_utils/queries_test.go +++ b/server/service/osquery_utils/queries_test.go @@ -831,3 +831,30 @@ func TestDirectDiskEncryption(t *testing.T) { require.NoError(t, err) require.False(t, ds.SetOrUpdateHostDisksEncryptionFuncInvoked) } + +func TestDirectIngestDiskEncryptionLinux(t *testing.T) { + ds := new(mock.Store) + var expectEncrypted bool + ds.SetOrUpdateHostDisksEncryptionFunc = func(ctx context.Context, id uint, encrypted bool) error { + assert.Equal(t, expectEncrypted, encrypted) + return nil + } + host := fleet.Host{ + ID: 1, + } + + expectEncrypted = false + err := directIngestDiskEncryptionLinux(context.Background(), log.NewNopLogger(), &host, ds, []map[string]string{}, false) + require.NoError(t, err) + require.True(t, ds.SetOrUpdateHostDisksEncryptionFuncInvoked) + ds.SetOrUpdateHostDisksEncryptionFuncInvoked = false + + expectEncrypted = true + err = directIngestDiskEncryptionLinux(context.Background(), log.NewNopLogger(), &host, ds, []map[string]string{ + {"path": "/etc/hosts", "encrypted": "0"}, + {"path": "/tmp", "encrypted": "0"}, + {"path": "/", "encrypted": "1"}, + }, false) + require.NoError(t, err) + require.True(t, ds.SetOrUpdateHostDisksEncryptionFuncInvoked) +}