Add validation to reject requests to enqueue scripts for plain osquery hosts (#16893)

This commit is contained in:
Sarah Gillespie
2024-02-15 14:34:16 -06:00
committed by GitHub
parent 119d1df76f
commit 96bd31dc1f
6 changed files with 52 additions and 4 deletions
@@ -0,0 +1,2 @@
- Added validation to reject requests to enqueue scripts for hosts that do not have fleetd installed
(i.e. plain osquery hosts).
+2 -2
View File
@@ -215,13 +215,13 @@ Fleet records the last 10,000 characters to prevent downtime.
if ident != "host1" || c.expectNotFound {
return nil, &notFoundError{}
}
return &fleet.Host{ID: 42, SeenTime: time.Now()}, nil
return &fleet.Host{ID: 42, SeenTime: time.Now(), OrbitNodeKey: ptr.String("abc")}, nil
}
ds.HostFunc = func(ctx context.Context, hid uint) (*fleet.Host, error) {
if hid != 42 || c.expectNotFound {
return nil, &notFoundError{}
}
h := fleet.Host{ID: hid, SeenTime: time.Now()}
h := fleet.Host{ID: hid, SeenTime: time.Now(), OrbitNodeKey: ptr.String("abc")}
if c.expectOffline {
h.SeenTime = time.Now().Add(-time.Hour)
}
+1
View File
@@ -577,6 +577,7 @@ SELECT
h.updated_at,
h.detail_updated_at,
h.node_key,
h.orbit_node_key,
h.hostname,
h.uuid,
h.platform,
@@ -4696,6 +4696,25 @@ func (s *integrationEnterpriseTestSuite) TestRunHostScript() {
// attempt to create an async script execution request, succeeds because script is added to queue.
s.Do("POST", "/api/latest/fleet/scripts/run", fleet.HostScriptRequestPayload{HostID: host.ID, ScriptContents: "echo"}, http.StatusAccepted)
// attempt to run a script on a plain osquery host
plainOsqueryHost, err := s.ds.NewHost(context.Background(), &fleet.Host{
DetailUpdatedAt: time.Now(),
LabelUpdatedAt: time.Now(),
PolicyUpdatedAt: time.Now(),
SeenTime: time.Now().Add(-time.Minute),
OsqueryHostID: ptr.String("plain-osquery-host"),
NodeKey: ptr.String("plain-osquery-host"),
UUID: uuid.New().String(),
Hostname: fmt.Sprintf("%s.local", "plain-osquery-host"),
HardwareSerial: uuid.New().String(),
Platform: "linux",
})
require.NoError(t, err)
res = s.Do("POST", "/api/latest/fleet/scripts/run", fleet.HostScriptRequestPayload{HostID: plainOsqueryHost.ID, ScriptContents: "echo"}, http.StatusUnprocessableEntity)
require.Contains(t, extractServerErrorText(res.Body), fleet.RunScriptDisabledErrMsg)
res = s.Do("POST", "/api/latest/fleet/scripts/run/sync", fleet.HostScriptRequestPayload{HostID: plainOsqueryHost.ID, ScriptContents: "echo"}, http.StatusUnprocessableEntity)
require.Contains(t, extractServerErrorText(res.Body), fleet.RunScriptDisabledErrMsg)
}
func (s *integrationEnterpriseTestSuite) TestRunHostSavedScript() {
@@ -4857,6 +4876,25 @@ func (s *integrationEnterpriseTestSuite) TestRunHostSavedScript() {
require.NoError(t, err)
s.DoJSON("POST", "/api/latest/fleet/scripts/run", fleet.HostScriptRequestPayload{HostID: host.ID, ScriptID: &script.ID}, http.StatusConflict, &runResp)
// attempt to run a script on a plain osquery host
plainOsqueryHost, err := s.ds.NewHost(context.Background(), &fleet.Host{
DetailUpdatedAt: time.Now(),
LabelUpdatedAt: time.Now(),
PolicyUpdatedAt: time.Now(),
SeenTime: time.Now().Add(-time.Minute),
OsqueryHostID: ptr.String("plain-osquery-host-2"),
NodeKey: ptr.String("plain-osquery-host-2"),
UUID: uuid.New().String(),
Hostname: fmt.Sprintf("%s.local", "plain-osquery-host-2"),
HardwareSerial: uuid.New().String(),
Platform: "linux",
})
require.NoError(t, err)
res = s.Do("POST", "/api/latest/fleet/scripts/run", fleet.HostScriptRequestPayload{HostID: plainOsqueryHost.ID, ScriptID: &script.ID}, http.StatusUnprocessableEntity)
require.Contains(t, extractServerErrorText(res.Body), fleet.RunScriptDisabledErrMsg)
res = s.Do("POST", "/api/latest/fleet/scripts/run/sync", fleet.HostScriptRequestPayload{HostID: plainOsqueryHost.ID, ScriptID: &script.ID}, http.StatusUnprocessableEntity)
require.Contains(t, extractServerErrorText(res.Body), fleet.RunScriptDisabledErrMsg)
}
func (s *integrationEnterpriseTestSuite) TestEnqueueSameScriptTwice() {
+7
View File
@@ -142,6 +142,13 @@ func (svc *Service) RunHostScript(ctx context.Context, request *fleet.HostScript
return nil, ctxerr.Wrap(ctx, err, "get host lite")
}
if host.OrbitNodeKey == nil || *host.OrbitNodeKey == "" {
// fleetd is required to run scripts so if the host is enrolled via plain osquery we return
// an error
svc.authz.SkipAuthorization(ctx)
return nil, fleet.NewUserMessageError(errors.New(fleet.RunScriptDisabledErrMsg), http.StatusUnprocessableEntity)
}
maxPending := maxPendingScripts
// must check that only one of script id or contents is provided before
+2 -2
View File
@@ -32,8 +32,8 @@ func TestHostRunScript(t *testing.T) {
}
}
teamHost := &fleet.Host{ID: 1, Hostname: "host-team", TeamID: ptr.Uint(1), SeenTime: time.Now()}
noTeamHost := &fleet.Host{ID: 2, Hostname: "host-no-team", TeamID: nil, SeenTime: time.Now()}
teamHost := &fleet.Host{ID: 1, Hostname: "host-team", TeamID: ptr.Uint(1), SeenTime: time.Now(), OrbitNodeKey: ptr.String("abc")}
noTeamHost := &fleet.Host{ID: 2, Hostname: "host-no-team", TeamID: nil, SeenTime: time.Now(), OrbitNodeKey: ptr.String("def")}
nonExistingHost := &fleet.Host{ID: 3, Hostname: "no-such-host", TeamID: nil}
ds.AppConfigFunc = func(ctx context.Context) (*fleet.AppConfig, error) {
return &fleet.AppConfig{}, nil