Skip to content
Open
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
2 changes: 1 addition & 1 deletion central/policy/service/validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ func (s *policyValidator) validateEventSource(policy *storage.Policy) error {
}

func validateNoLabelsInScopeForAuditEvent(scope *storage.Scope, context string) error {
if scope.GetLabel() != nil || (features.LabelBasedPolicyScoping.Enabled() && (scope.GetClusterLabel() != nil || scope.GetNamespaceLabel() != nil)) {
if scope.GetLabel() != nil {
return errors.Errorf("labels in `%s` section are not permitted for audit log events based policies", context)
}
return nil
Expand Down
26 changes: 16 additions & 10 deletions pkg/scopecomp/scope.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,35 +95,35 @@ func CompileScope(scope *storage.Scope, clusterLabelProvider ClusterLabelProvide
return cs, nil
}

// MatchesClusterLabels evaluates cluster label matchers against a deployment's cluster
func (c *CompiledScope) MatchesClusterLabels(ctx context.Context, deployment *storage.Deployment) bool {
// MatchesClusterLabels evaluates cluster label matchers against a cluster's labels
func (c *CompiledScope) MatchesClusterLabels(ctx context.Context, clusterID string) bool {
if c.ClusterLabelKey == nil {
return true
}
if c.clusterLabelProvider == nil {
log.Error("Cluster label matcher defined but provider is nil - failing closed")
return false
}
clusterLabels, err := c.clusterLabelProvider.GetClusterLabels(ctx, deployment.GetClusterId())
clusterLabels, err := c.clusterLabelProvider.GetClusterLabels(ctx, clusterID)
if err != nil {
log.Errorf("Failed to fetch cluster labels for cluster %s: %v", deployment.GetClusterId(), err)
log.Errorf("Failed to fetch cluster labels for cluster %s: %v", clusterID, err)
return false
}
return c.MatchesLabels(c.ClusterLabelKey, c.ClusterLabelValue, clusterLabels)
}

// MatchesNamespaceLabels evaluates namespace label matchers against a deployment's namespace
func (c *CompiledScope) MatchesNamespaceLabels(ctx context.Context, deployment *storage.Deployment) bool {
// MatchesNamespaceLabels evaluates namespace label matchers against a namespace's labels
func (c *CompiledScope) MatchesNamespaceLabels(ctx context.Context, clusterID string, namespace string) bool {
if c.NamespaceLabelKey == nil {
return true
}
if c.namespaceLabelProvider == nil {
log.Error("Namespace label matcher defined but provider is nil - failing closed")
return false
}
namespaceLabels, err := c.namespaceLabelProvider.GetNamespaceLabels(ctx, deployment.GetClusterId(), deployment.GetNamespace())
namespaceLabels, err := c.namespaceLabelProvider.GetNamespaceLabels(ctx, clusterID, namespace)
if err != nil {
log.Errorf("Failed to fetch namespace labels for namespace %s in cluster %s: %v", deployment.GetNamespace(), deployment.GetClusterId(), err)
log.Errorf("Failed to fetch namespace labels for namespace %s in cluster %s: %v", namespace, clusterID, err)
return false
}
return c.MatchesLabels(c.NamespaceLabelKey, c.NamespaceLabelValue, namespaceLabels)
Expand All @@ -137,13 +137,13 @@ func (c *CompiledScope) MatchesDeployment(ctx context.Context, deployment *stora
if !c.MatchesCluster(deployment.GetClusterId()) {
return false
}
if !c.MatchesClusterLabels(ctx, deployment) {
if !c.MatchesClusterLabels(ctx, deployment.GetClusterId()) {
return false
}
if !c.MatchesNamespace(deployment.GetNamespace()) {
return false
}
if !c.MatchesNamespaceLabels(ctx, deployment) {
if !c.MatchesNamespaceLabels(ctx, deployment.GetClusterId(), deployment.GetNamespace()) {
return false
}
if !c.MatchesLabels(c.LabelKey, c.LabelValue, deployment.GetLabels()) {
Expand Down Expand Up @@ -188,8 +188,14 @@ func (c *CompiledScope) MatchesAuditEvent(ctx context.Context, auditEvent *stora
if !c.MatchesCluster(auditEvent.GetObject().GetClusterId()) {
return false
}
if !c.MatchesClusterLabels(ctx, auditEvent.GetObject().GetClusterId()) {
return false
}
if !c.MatchesNamespace(auditEvent.GetObject().GetNamespace()) {
return false
}
if !c.MatchesNamespaceLabels(ctx, auditEvent.GetObject().GetClusterId(), auditEvent.GetObject().GetNamespace()) {
return false
}
return true
}
161 changes: 161 additions & 0 deletions pkg/scopecomp/scope_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -355,3 +355,164 @@ func TestWithinScope(t *testing.T) {
assert.Equalf(t, test.result, cs.MatchesDeployment(context.Background(), test.deployment), "Failed test '%s'", test.name)
}
}

func TestMatchesAuditEventWithLabels(t *testing.T) {
testutils.MustUpdateFeature(t, features.LabelBasedPolicyScoping, true)

subtests := map[string]struct {
scope *storage.Scope
auditEvent *storage.KubernetesEvent
clusterLabels map[string]string
namespaceLabels map[string]string
result bool
}{
"cluster label match": {
scope: &storage.Scope{
ClusterLabel: &storage.Scope_Label{
Key: "env",
Value: "prod",
},
},
auditEvent: &storage.KubernetesEvent{
Object: &storage.KubernetesEvent_Object{
ClusterId: "cluster1",
Namespace: "default",
},
},
clusterLabels: map[string]string{"env": "prod"},
result: true,
},
"cluster label mismatch": {
scope: &storage.Scope{
ClusterLabel: &storage.Scope_Label{
Key: "env",
Value: "prod",
},
},
auditEvent: &storage.KubernetesEvent{
Object: &storage.KubernetesEvent_Object{
ClusterId: "cluster1",
Namespace: "default",
},
},
clusterLabels: map[string]string{"env": "dev"},
result: false,
},
"namespace label match": {
scope: &storage.Scope{
NamespaceLabel: &storage.Scope_Label{
Key: "team",
Value: "backend",
},
},
auditEvent: &storage.KubernetesEvent{
Object: &storage.KubernetesEvent_Object{
ClusterId: "cluster1",
Namespace: "test-backend",
},
},
namespaceLabels: map[string]string{"team": "backend"},
result: true,
},
"namespace label mismatch": {
scope: &storage.Scope{
NamespaceLabel: &storage.Scope_Label{
Key: "team",
Value: "backend",
},
},
auditEvent: &storage.KubernetesEvent{
Object: &storage.KubernetesEvent_Object{
ClusterId: "cluster1",
Namespace: "test-frontend",
},
},
namespaceLabels: map[string]string{"team": "frontend"},
result: false,
},
"combined cluster and namespace label match": {
scope: &storage.Scope{
ClusterLabel: &storage.Scope_Label{
Key: "env",
Value: "prod",
},
NamespaceLabel: &storage.Scope_Label{
Key: "team",
Value: "backend",
},
},
auditEvent: &storage.KubernetesEvent{
Object: &storage.KubernetesEvent_Object{
ClusterId: "cluster1",
Namespace: "test-backend",
},
},
clusterLabels: map[string]string{"env": "prod"},
namespaceLabels: map[string]string{"team": "backend"},
result: true,
},
"cluster matches but namespace does not": {
scope: &storage.Scope{
ClusterLabel: &storage.Scope_Label{
Key: "env",
Value: "prod",
},
NamespaceLabel: &storage.Scope_Label{
Key: "team",
Value: "backend",
},
},
auditEvent: &storage.KubernetesEvent{
Object: &storage.KubernetesEvent_Object{
ClusterId: "cluster1",
Namespace: "test-frontend",
},
},
clusterLabels: map[string]string{"env": "prod"},
namespaceLabels: map[string]string{"team": "frontend"},
result: false,
},
"no label scope matches any audit event": {
scope: &storage.Scope{
Cluster: "cluster1",
Namespace: "default",
},
auditEvent: &storage.KubernetesEvent{
Object: &storage.KubernetesEvent_Object{
ClusterId: "cluster1",
Namespace: "default",
},
},
result: true,
},
}
for name, test := range subtests {
t.Run(name, func(_ *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()

var clusterProvider ClusterLabelProvider
var namespaceProvider NamespaceLabelProvider
if test.clusterLabels != nil {
mockCluster := mocks.NewMockClusterLabelProvider(ctrl)
mockCluster.EXPECT().
GetClusterLabels(gomock.Any(), gomock.Eq(test.auditEvent.GetObject().GetClusterId())).
Return(test.clusterLabels, nil).
AnyTimes()
clusterProvider = mockCluster
}
if test.namespaceLabels != nil {
mockNamespace := mocks.NewMockNamespaceLabelProvider(ctrl)
mockNamespace.EXPECT().
GetNamespaceLabels(gomock.Any(), gomock.Eq(test.auditEvent.GetObject().GetClusterId()), gomock.Eq(test.auditEvent.GetObject().GetNamespace())).
Return(test.namespaceLabels, nil).
AnyTimes()
namespaceProvider = mockNamespace
}

cs, err := CompileScope(test.scope, clusterProvider, namespaceProvider)
require.NoError(t, err)
assert.Equalf(t, test.result, cs.MatchesAuditEvent(context.Background(), test.auditEvent), "Failed test '%s'", name)
})
}
}
Loading