Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
aa62991
X-Smart-Branch-Parent: master
JoukoVirtanen Aug 20, 2025
9deb4e8
X-Smart-Squash: Squashed 7 commits:
JoukoVirtanen Aug 6, 2025
1f86ff5
Revert "X-Smart-Squash: Squashed 7 commits:"
JoukoVirtanen Aug 6, 2025
0635c2f
Sends locked processes baselines to sensor when deployments leave obs…
JoukoVirtanen Aug 7, 2025
512c84b
Added a feature flag
JoukoVirtanen Aug 7, 2025
289c301
Fixed unit test
JoukoVirtanen Aug 7, 2025
02a73ee
Changed the name of the feature flag
JoukoVirtanen Aug 10, 2025
38b6e0d
Setting connectionManager for lifecycle manager
JoukoVirtanen Aug 11, 2025
67c98fa
Manual testing works
JoukoVirtanen Aug 14, 2025
9c74bf7
Fixed style
JoukoVirtanen Aug 14, 2025
db758ac
Not upserting the baseline if it doesn't need to be upserted
JoukoVirtanen Aug 14, 2025
19f5cb1
Added unit test
JoukoVirtanen Aug 14, 2025
05f90b8
Remved connection manager from process baseline service
JoukoVirtanen Aug 15, 2025
21ea3b2
make style
JoukoVirtanen Aug 15, 2025
e7a7dbd
Apply suggestions from code review
JoukoVirtanen Aug 15, 2025
3160d5d
Made feature flag consistent
JoukoVirtanen Aug 15, 2025
e1f1c2e
Added test for UpdateProcessBaselineElements where autolock is true
JoukoVirtanen Aug 15, 2025
2c2a470
Clarified which lock is being used
JoukoVirtanen Aug 15, 2025
5a2422d
Further clarified which lock is being used
JoukoVirtanen Aug 15, 2025
d39ef31
stackroxLock will be set to true if userLock is set to true. userLock…
JoukoVirtanen Aug 15, 2025
5c81356
SendBaselineToSensor returns error. Variable renamed from pw to baseline
JoukoVirtanen Aug 15, 2025
a6b3fc7
Added more tests for .checkAndUpdateBaseline with autolocking
JoukoVirtanen Aug 15, 2025
bc4b730
Added another test. UserLock should be unlocked when needed
JoukoVirtanen Aug 16, 2025
7c0662e
Moved a check into its own function
JoukoVirtanen Aug 16, 2025
c8e2eda
Refactored case where the baseline already exists
JoukoVirtanen Aug 16, 2025
1686877
Corrected comment
JoukoVirtanen Aug 16, 2025
b5bc597
Saving feature flag to env var, because it can be more complex in the…
JoukoVirtanen Aug 18, 2025
88f881c
Renamed checkIfBaselineDoesntNeedUpdate to checkIfBaselineCanBeSkipped
JoukoVirtanen Aug 20, 2025
2000de1
Not passing userLock to updateProcessBaselineElements
JoukoVirtanen Aug 20, 2025
c8eba1a
X-Smart-Branch-Parent: jv-ROX-30135-send-baselines-to-sensor-when-dep…
JoukoVirtanen Aug 20, 2025
e32687d
Added AutolockProcessBaseline to cluster config
JoukoVirtanen Aug 7, 2025
90dbcff
Autolocking of process baselines is done at cluster level
JoukoVirtanen Aug 8, 2025
18a5a50
Updated unit tests
JoukoVirtanen Aug 18, 2025
4c09a78
Updated unit tests
JoukoVirtanen Aug 18, 2025
168c7df
Using both feature flag and cluster config to gate feature
JoukoVirtanen Aug 18, 2025
233c33e
Renamed field from autolock to enabled
JoukoVirtanen Aug 18, 2025
2832993
Added cluster permission to detection lifecycle manager
JoukoVirtanen Aug 19, 2025
27e1369
make update-storage-protolock
JoukoVirtanen Aug 19, 2025
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
2 changes: 1 addition & 1 deletion central/alert/service/service_impl.go
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,7 @@ func (s *serviceImpl) ResolveAlert(ctx context.Context, req *v1.ResolveAlertRequ
ClusterId: alert.GetDeployment().GetClusterId(),
Namespace: alert.GetDeployment().GetNamespace(),
}
baseline, err := s.baselines.UpdateProcessBaselineElements(ctx, key, items, nil, false)
baseline, err := s.baselines.UpdateProcessBaselineElements(ctx, key, items, nil, false, false)
if err != nil {
return nil, err
}
Expand Down
12 changes: 9 additions & 3 deletions central/detection/lifecycle/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"time"

"github.com/stackrox/rox/central/activecomponent/updater/aggregator"
clusterDatastore "github.com/stackrox/rox/central/cluster/datastore"
"github.com/stackrox/rox/central/deployment/cache"
deploymentDatastore "github.com/stackrox/rox/central/deployment/datastore"
"github.com/stackrox/rox/central/deployment/queue"
Expand All @@ -14,6 +15,7 @@ import (
baselineDataStore "github.com/stackrox/rox/central/processbaseline/datastore"
processDatastore "github.com/stackrox/rox/central/processindicator/datastore"
"github.com/stackrox/rox/central/reprocessor"
"github.com/stackrox/rox/central/sensor/service/connection"
"github.com/stackrox/rox/generated/storage"
"github.com/stackrox/rox/pkg/logging"
"github.com/stackrox/rox/pkg/process/filter"
Expand Down Expand Up @@ -42,18 +44,20 @@ type Manager interface {
DeploymentRemoved(deploymentID string) error
RemovePolicy(policyID string) error
RemoveDeploymentFromObservation(deploymentID string)
SendBaselineToSensor(baseline *storage.ProcessBaseline) error
}

// newManager returns a new manager with the injected dependencies.
func newManager(buildTimeDetector buildtime.Detector, deployTimeDetector deploytime.Detector, runtimeDetector runtime.Detector,
deploymentDatastore deploymentDatastore.DataStore, processesDataStore processDatastore.DataStore, baselines baselineDataStore.DataStore,
alertManager alertmanager.AlertManager, reprocessor reprocessor.Loop, deletedDeploymentsCache cache.DeletedDeployments, filter filter.Filter,
processAggregator aggregator.ProcessAggregator) *managerImpl {
clusterDatastore clusterDatastore.DataStore, deploymentDatastore deploymentDatastore.DataStore, processesDataStore processDatastore.DataStore,
baselines baselineDataStore.DataStore, alertManager alertmanager.AlertManager, reprocessor reprocessor.Loop, deletedDeploymentsCache cache.DeletedDeployments,
filter filter.Filter, processAggregator aggregator.ProcessAggregator, connectionManager connection.Manager) *managerImpl {
m := &managerImpl{
buildTimeDetector: buildTimeDetector,
deployTimeDetector: deployTimeDetector,
runtimeDetector: runtimeDetector,
alertManager: alertManager,
clusterDataStore: clusterDatastore,
deploymentDataStore: deploymentDatastore,
processesDataStore: processesDataStore,
baselines: baselines,
Expand All @@ -70,6 +74,8 @@ func newManager(buildTimeDetector buildtime.Detector, deployTimeDetector deployt

removedOrDisabledPolicies: set.NewStringSet(),
processAggregator: processAggregator,

connectionManager: connectionManager,
}

go m.flushQueuePeriodically()
Expand Down
99 changes: 91 additions & 8 deletions central/detection/lifecycle/manager_impl.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (

"github.com/pkg/errors"
"github.com/stackrox/rox/central/activecomponent/updater/aggregator"
clusterDatastore "github.com/stackrox/rox/central/cluster/datastore"
"github.com/stackrox/rox/central/deployment/cache"
deploymentDatastore "github.com/stackrox/rox/central/deployment/datastore"
"github.com/stackrox/rox/central/deployment/queue"
Expand All @@ -20,9 +21,12 @@ import (
baselineDataStore "github.com/stackrox/rox/central/processbaseline/datastore"
processIndicatorDatastore "github.com/stackrox/rox/central/processindicator/datastore"
"github.com/stackrox/rox/central/reprocessor"
"github.com/stackrox/rox/central/sensor/service/connection"
"github.com/stackrox/rox/generated/internalapi/central"
"github.com/stackrox/rox/generated/storage"
"github.com/stackrox/rox/pkg/concurrency"
"github.com/stackrox/rox/pkg/env"
"github.com/stackrox/rox/pkg/features"
"github.com/stackrox/rox/pkg/policies"
"github.com/stackrox/rox/pkg/postgres/pgutils"
"github.com/stackrox/rox/pkg/process/filter"
Expand All @@ -40,7 +44,7 @@ import (
var (
lifecycleMgrCtx = sac.WithGlobalAccessScopeChecker(context.Background(),
sac.AllowFixedScopes(sac.AccessModeScopeKeys(storage.Access_READ_ACCESS, storage.Access_READ_WRITE_ACCESS),
sac.ResourceScopeKeys(resources.Alert, resources.Deployment, resources.Image,
sac.ResourceScopeKeys(resources.Alert, resources.Deployment, resources.Image, resources.Cluster,
resources.DeploymentExtension, resources.WorkflowAdministration, resources.Namespace)))

genDuration = env.BaselineGenerationDuration.DurationSetting()
Expand All @@ -62,6 +66,7 @@ type managerImpl struct {

alertManager alertmanager.AlertManager

clusterDataStore clusterDatastore.DataStore
deploymentDataStore deploymentDatastore.DataStore
processesDataStore processIndicatorDatastore.DataStore
baselines baselineDataStore.DataStore
Expand All @@ -81,6 +86,8 @@ type managerImpl struct {
removedOrDisabledPolicies set.StringSet

processAggregator aggregator.ProcessAggregator

connectionManager connection.Manager
}

func (m *managerImpl) copyAndResetIndicatorQueue() map[string]*storage.ProcessIndicator {
Expand Down Expand Up @@ -248,6 +255,37 @@ func (m *managerImpl) buildMapAndCheckBaseline(indicatorSlice []*storage.Process
}
}

func (m *managerImpl) SendBaselineToSensor(baseline *storage.ProcessBaseline) error {
err := m.connectionManager.SendMessage(baseline.GetKey().GetClusterId(), &central.MsgToSensor{
Msg: &central.MsgToSensor_BaselineSync{
BaselineSync: &central.BaselineSync{
Baselines: []*storage.ProcessBaseline{baseline},
}},
})
if err != nil {
log.Errorf("Error sending process baseline to cluster %q: %v", baseline.GetKey().GetClusterId(), err)
return err
}
log.Infof("Successfully sent process baseline to cluster %q: %s", baseline.GetKey().GetClusterId(), baseline.GetId())

return nil
}

// Perhaps the cluster config should be kept in memory and calling the database should not be needed
func (m *managerImpl) isAutolockEnabledForCluster(clusterId string) bool {
cluster, found, err := m.clusterDataStore.GetCluster(lifecycleMgrCtx, clusterId)

if !found || err != nil {
return false
}

return cluster.GetDynamicConfig().GetAutolockProcessBaseline().GetEnabled()
}

func checkIfBaselineCanBeSkipped(elements []*storage.BaselineItem, inObservation bool, baseline *storage.ProcessBaseline, autolockEnabled bool) bool {
return len(elements) == 0 && (inObservation || !autolockEnabled || processbaseline.IsUserLocked(baseline))
}

func (m *managerImpl) checkAndUpdateBaseline(baselineKey processBaselineKey, indicators []*storage.ProcessIndicator) (bool, error) {
key := &storage.ProcessBaselineKey{
DeploymentId: baselineKey.deploymentID,
Expand All @@ -256,15 +294,19 @@ func (m *managerImpl) checkAndUpdateBaseline(baselineKey processBaselineKey, ind
Namespace: baselineKey.namespace,
}

autolockEnabled := features.AutoLockProcessBaselines.Enabled() && m.isAutolockEnabledForCluster(baselineKey.clusterID)

// TODO joseph what to do if exclusions ("baseline" in the old non-inclusive language) doesn't exist? Always create for now?
baseline, exists, err := m.baselines.GetProcessBaseline(lifecycleMgrCtx, key)
if err != nil {
return false, err
}

inObservation := m.deploymentObservationQueue.InObservation(key.GetDeploymentId())

// If the baseline does not exist AND this deployment is in the observation period, we
// need not process further at this time.
if !exists && m.deploymentObservationQueue.InObservation(key.GetDeploymentId()) {
if !exists && inObservation {
return false, nil
}

Expand All @@ -286,22 +328,63 @@ func (m *managerImpl) checkAndUpdateBaseline(baselineKey processBaselineKey, ind
insertableElement := &storage.BaselineItem{Item: &storage.BaselineItem_ProcessName{ProcessName: baselineItem}}
elements = append(elements, insertableElement)
}
if len(elements) == 0 {

if checkIfBaselineCanBeSkipped(elements, inObservation, baseline, autolockEnabled) {
return false, nil
}

if !exists {
_, err = m.baselines.UpsertProcessBaseline(lifecycleMgrCtx, key, elements, true, true)
userLock := autolockEnabled && !inObservation
upsertedBaseline, err := m.baselines.UpsertProcessBaseline(lifecycleMgrCtx, key, elements, true, true, userLock)
if err != nil {
return false, err
}
if userLock {
err = m.SendBaselineToSensor(upsertedBaseline)
}
return false, err
}

userBaseline := processbaseline.IsUserLocked(baseline)
roxBaseline := processbaseline.IsRoxLocked(baseline) && hasNonStartupProcess
if userBaseline || roxBaseline {
reprocessRisk := userBaseline || roxBaseline

if reprocessRisk {
// We already checked if it's in the baseline and it is not, so reprocess risk to mark the results are suspicious if necessary
m.reprocessor.ReprocessRiskForDeployments(baselineKey.deploymentID)
} else {
// So we have a baseline, but not locked. Now we need to add these elements to the unlocked baseline
_, err = m.baselines.UpdateProcessBaselineElements(lifecycleMgrCtx, key, elements, nil, true)
}

if !autolockEnabled {
if !reprocessRisk {
_, err := m.baselines.UpdateProcessBaselineElements(lifecycleMgrCtx, key, elements, nil, true, false)
if err != nil {
return false, err
}
}
return userBaseline, nil
}

// If this point is reached AutoLockProcessBaselines is enabled.
// When AutoLockProcessBaselines we don't need to do anything if the baseline is user or stackrox locked.
// However, if the feature is enabled we need to user lock it if it is not user locked. It also needs to be
// stackrox locked if it is neither user locked or stackrox locked.
if !userBaseline {
// If the baseline is out of observation it needs to be user locked.
// Since we are here the baseline is not user locked and if it isn't stackrox locked either,
// it needs to be updated.
userLock := !inObservation
if userLock || !roxBaseline {
upsertedBaseline, err := m.baselines.UpdateProcessBaselineElements(lifecycleMgrCtx, key, elements, nil, true, userLock)
if err != nil {
return false, err
}
if userLock {
err := m.SendBaselineToSensor(upsertedBaseline)
if err != nil {
return false, err
}
}
}
}

return userBaseline, err
Expand Down
Loading
Loading