From 8d27d324d92a5165aab514e7ec3a13d1a4956229 Mon Sep 17 00:00:00 2001 From: James Pickett Date: Mon, 16 Dec 2024 14:24:39 -0800 Subject: [PATCH 1/3] add min interval between presence detections --- ee/localserver/server.go | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/ee/localserver/server.go b/ee/localserver/server.go index ce4f6a917..0f053f66b 100644 --- a/ee/localserver/server.go +++ b/ee/localserver/server.go @@ -60,8 +60,9 @@ type localServer struct { serverKey *rsa.PublicKey serverEcKey *ecdsa.PublicKey - presenceDetector presenceDetector - presenceDetectionMutex sync.Mutex + presenceDetector presenceDetector + presenceDetectionMutex sync.Mutex + lastPresenceDetectionAttempt time.Time } const ( @@ -422,6 +423,21 @@ func (ls *localServer) presenceDetectionHandler(next http.Handler) http.Handler ls.presenceDetectionMutex.Lock() defer ls.presenceDetectionMutex.Unlock() + // if the user fails or cancels the presence detection, we want to wait a bit before trying again + // so that if there are several queued up requests, we don't prompt the user multiple times in a row + // if they keep hitting cancel + + const minTimeBetweenPresenceDetection = 3 * time.Second + if time.Since(ls.lastPresenceDetectionAttempt) < minTimeBetweenPresenceDetection { + ls.slogger.Log(r.Context(), slog.LevelInfo, + "presence detection attempted too soon", + ) + + w.Header().Add(kolideDurationSinceLastPresenceDetectionHeaderKey, presencedetection.DetectionFailedDurationValue.String()) + next.ServeHTTP(w, r) + return + } + // can test this by adding an unauthed endpoint to the mux and running, for example: // curl -i -H "X-Kolide-Presence-Detection-Interval: 10s" -H "X-Kolide-Presence-Detection-Reason: my reason" localhost:12519/id detectionIntervalStr := r.Header.Get(kolidePresenceDetectionIntervalHeaderKey) @@ -461,7 +477,6 @@ func (ls *localServer) presenceDetectionHandler(next http.Handler) http.Handler } durationSinceLastDetection, err := ls.presenceDetector.DetectPresence(reason, detectionIntervalDuration) - if err != nil { ls.slogger.Log(r.Context(), slog.LevelInfo, "presence_detection", @@ -472,6 +487,8 @@ func (ls *localServer) presenceDetectionHandler(next http.Handler) http.Handler ) } + ls.lastPresenceDetectionAttempt = time.Now() + // if there was an error, we still want to return a 200 status code // and send the request through // allow the server to decide what to do based on last detection duration From 492d21e0e1ad7673067400a045a872157203942b Mon Sep 17 00:00:00 2001 From: James Pickett Date: Mon, 16 Dec 2024 14:32:49 -0800 Subject: [PATCH 2/3] more logging --- ee/localserver/server.go | 1 + 1 file changed, 1 insertion(+) diff --git a/ee/localserver/server.go b/ee/localserver/server.go index 0f053f66b..72738ed89 100644 --- a/ee/localserver/server.go +++ b/ee/localserver/server.go @@ -431,6 +431,7 @@ func (ls *localServer) presenceDetectionHandler(next http.Handler) http.Handler if time.Since(ls.lastPresenceDetectionAttempt) < minTimeBetweenPresenceDetection { ls.slogger.Log(r.Context(), slog.LevelInfo, "presence detection attempted too soon", + "min_interval", minTimeBetweenPresenceDetection, ) w.Header().Add(kolideDurationSinceLastPresenceDetectionHeaderKey, presencedetection.DetectionFailedDurationValue.String()) From 81b14892080a4c8f966f05bd7a2b41b186a216f4 Mon Sep 17 00:00:00 2001 From: James Pickett Date: Tue, 17 Dec 2024 08:31:33 -0800 Subject: [PATCH 3/3] add test for min interval --- ee/localserver/presence-detection-middleware_test.go | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/ee/localserver/presence-detection-middleware_test.go b/ee/localserver/presence-detection-middleware_test.go index c6206dcff..6fcb8c88e 100644 --- a/ee/localserver/presence-detection-middleware_test.go +++ b/ee/localserver/presence-detection-middleware_test.go @@ -79,7 +79,7 @@ func TestPresenceDetectionHandler(t *testing.T) { mockPresenceDetector := mocks.NewPresenceDetector(t) if tt.expectDetectPresenceCall { - mockPresenceDetector.On("DetectPresence", mock.AnythingOfType("string"), mock.AnythingOfType("Duration")).Return(tt.durationSinceLastDetection, tt.presenceDetectionError) + mockPresenceDetector.On("DetectPresence", mock.AnythingOfType("string"), mock.AnythingOfType("Duration")).Return(tt.durationSinceLastDetection, tt.presenceDetectionError).Once() } server := &localServer{ @@ -112,6 +112,14 @@ func TestPresenceDetectionHandler(t *testing.T) { require.NotEmpty(t, rr.Header().Get(kolideDurationSinceLastPresenceDetectionHeaderKey)) } require.Equal(t, tt.expectedStatusCode, rr.Code) + + // fire the request one more time, it should not call presence detector again + // because it's less than the minimum interval + mockPresenceDetector = mocks.NewPresenceDetector(t) + server.presenceDetector = mockPresenceDetector + + handlerToTest.ServeHTTP(rr, req) + mockPresenceDetector.AssertNotCalled(t, "DetectPresence", mock.Anything, mock.Anything) }) } }