Skip to content

Commit

Permalink
Merge pull request #218 from weaveworks/211-policy-exclusions-fix
Browse files Browse the repository at this point in the history
use labels as map only instead of list of maps
  • Loading branch information
Samra10 committed Dec 29, 2023
2 parents a51ffe4 + ad1c908 commit 57e5af9
Show file tree
Hide file tree
Showing 8 changed files with 188 additions and 40 deletions.
2 changes: 1 addition & 1 deletion api/v2beta3/policy_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ type PolicyExclusions struct {
// +optional
// Labels is a list of Kubernetes labels that are needed to excluded the policy against a resource
// this filter is statisfied if only one label existed, using * for value make it so it will match if the key exists regardless of its value
Labels []map[string]string `json:"labels"`
Labels map[string]string `json:"labels"`
}

// PolicySpec defines the desired state of Policy
Expand Down
12 changes: 3 additions & 9 deletions api/v2beta3/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 3 additions & 5 deletions config/crd/bases/pac.weave.works_policies.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -533,15 +533,13 @@ spec:
list
properties:
labels:
additionalProperties:
type: string
description: Labels is a list of Kubernetes labels that are needed
to excluded the policy against a resource this filter is statisfied
if only one label existed, using * for value make it so it will
match if the key exists regardless of its value
items:
additionalProperties:
type: string
type: object
type: array
type: object
namespaces:
description: Namespaces is a list of Kubernetes namespaces that
a resource needs to be a part of to excluded from this policy
Expand Down
8 changes: 3 additions & 5 deletions helm/crds/pac.weave.works_policies.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -533,15 +533,13 @@ spec:
list
properties:
labels:
additionalProperties:
type: string
description: Labels is a list of Kubernetes labels that are needed
to excluded the policy against a resource this filter is statisfied
if only one label existed, using * for value make it so it will
match if the key exists regardless of its value
items:
additionalProperties:
type: string
type: object
type: array
type: object
namespaces:
description: Namespaces is a list of Kubernetes namespaces that
a resource needs to be a part of to excluded from this policy
Expand Down
6 changes: 3 additions & 3 deletions pkg/policy-core/domain/policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@ type PolicyStandard struct {

// PolicyExclusions are the structure which resources should not be evaluated against the policy
type PolicyExclusions struct {
Namespaces []string `json:"namespaces"`
Resources []string `json:"resources"`
Labels []map[string]string `json:"labels"`
Namespaces []string `json:"namespaces"`
Resources []string `json:"resources"`
Labels map[string]string `json:"labels"`
}

// Policy represents a policy
Expand Down
14 changes: 6 additions & 8 deletions pkg/policy-core/validation/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,15 +73,13 @@ func isExcluded(entity domain.Entity, policy domain.Policy) bool {
}
}

for _, obj := range policy.Exclude.Labels {
for key, val := range obj {
entityVal, ok := entity.Labels[key]
if ok {
if val != "*" && val != entityVal {
continue
}
return true
for key, val := range policy.Exclude.Labels {
entityVal, ok := entity.Labels[key]
if ok {
if val != "*" && val != entityVal {
continue
}
return true
}
}

Expand Down
6 changes: 3 additions & 3 deletions pkg/policy-core/validation/opa_validation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -674,7 +674,7 @@ func TestOpaValidator_Validate(t *testing.T) {
init: init{
writeCompliance: false,
loadStubs: func(policiesSource *mock.MockPoliciesSource, sink *mock.MockPolicyValidationSink) {
imageTag := testdata.Policies["imageTagExcluded"]
imageTag := testdata.Policies["imageTagExcludedNamespaces"]
policiesSource.EXPECT().GetAll(gomock.Any()).
Times(1).Return([]domain.Policy{
imageTag,
Expand All @@ -694,7 +694,7 @@ func TestOpaValidator_Validate(t *testing.T) {
init: init{
writeCompliance: false,
loadStubs: func(policiesSource *mock.MockPoliciesSource, sink *mock.MockPolicyValidationSink) {
imageTag := testdata.Policies["imageTagExcluded"]
imageTag := testdata.Policies["imageTagExcludedLabels"]
policiesSource.EXPECT().GetAll(gomock.Any()).
Times(1).Return([]domain.Policy{
imageTag,
Expand All @@ -715,7 +715,7 @@ func TestOpaValidator_Validate(t *testing.T) {
writeCompliance: false,
loadStubs: func(policiesSource *mock.MockPoliciesSource, sink *mock.MockPolicyValidationSink) {
missingOwner := testdata.Policies["missingOwner"]
imageTag := testdata.Policies["imageTag"]
imageTag := testdata.Policies["imageTagExcludedResources"]
imageTag.Exclude.Resources = []string{"unit-testing/nginx-deployment"}
policiesSource.EXPECT().GetAll(gomock.Any()).
Times(1).Return([]domain.Policy{
Expand Down
172 changes: 166 additions & 6 deletions pkg/policy-core/validation/testdata/testdata.go
Original file line number Diff line number Diff line change
Expand Up @@ -544,15 +544,175 @@ var (
},
},
},
"imageTagExcluded": {
"imageTagExcludedNamespaces": {
Name: "Using latest image tag in container",
Exclude: domain.PolicyExclusions{
Namespaces: []string{"unit-testing", "flux-system"},
Resources: []string{"unit-testing/nginx-deployment"},
Labels: []map[string]string{
{
"app": "nginx",
},
},
Enforce: true,
ID: uuid.NewV4().String(),
Code: `
package magalix.advisor.images.image_tag_enforce
image_tag := input.parameters.image_tag
violation[result] {
some i
containers = controller_spec.containers[i]
splittedUrl = split(containers.image, "/")
image = splittedUrl[count(splittedUrl)-1]
not contains(image, ":")
result = {
"issue detected": true,
"msg": "Image is not tagged",
"violating_key": sprintf("spec.template.spec.containers[%v].image", [i])
}
}
violation[result] {
some i
containers = controller_spec.containers[i]
splittedUrl = split(containers.image, "/")
image = splittedUrl[count(splittedUrl)-1]
count(split(image, ":")) == 2
[image_name, tag] = split(image, ":")
tag == image_tag
result = {
"issue detected": true,
"msg": sprintf("Image contains unapproved tag '%v'", [image_tag]),
"image": image,
"violating_key": sprintf("spec.template.spec.containers[%v].image", [i])
}
}
violation[result] {
some i
containers = controller_spec.containers[i]
splittedUrl = split(containers.image, "/")
image = splittedUrl[count(splittedUrl)-1]
count(split(image, ":")) == 3
[image_name, port, tag] = split(image, ":")
tag == image_tag
result = {
"issue detected": true,
"msg": sprintf("Image contains unapproved tag:'%v'", [image_tag]),
"image": image,
"violating_key": sprintf("spec.template.spec.containers[%v].image", [i])
}
}
# Controller input
controller_input = input.review.object
# controller_container acts as an iterator to get containers from the template
controller_spec = controller_input.spec.template.spec {
contains_kind(controller_input.kind, {"StatefulSet" , "DaemonSet", "Deployment", "Job"})
} else = controller_input.spec {
controller_input.kind == "Pod"
} else = controller_input.spec.jobTemplate.spec.template.spec {
controller_input.kind == "CronJob"
}
contains_kind(kind, kinds) {
kinds[_] = kind
}`,
Mutate: true,
Parameters: []domain.PolicyParameters{
{
Name: "image_tag",
Type: "string",
Required: true,
Value: "latest",
},
},
},
"imageTagExcludedResources": {
Name: "Using latest image tag in container",
Exclude: domain.PolicyExclusions{
Resources: []string{"unit-testing/nginx-deployment"},
},
Enforce: true,
ID: uuid.NewV4().String(),
Code: `
package magalix.advisor.images.image_tag_enforce
image_tag := input.parameters.image_tag
violation[result] {
some i
containers = controller_spec.containers[i]
splittedUrl = split(containers.image, "/")
image = splittedUrl[count(splittedUrl)-1]
not contains(image, ":")
result = {
"issue detected": true,
"msg": "Image is not tagged",
"violating_key": sprintf("spec.template.spec.containers[%v].image", [i])
}
}
violation[result] {
some i
containers = controller_spec.containers[i]
splittedUrl = split(containers.image, "/")
image = splittedUrl[count(splittedUrl)-1]
count(split(image, ":")) == 2
[image_name, tag] = split(image, ":")
tag == image_tag
result = {
"issue detected": true,
"msg": sprintf("Image contains unapproved tag '%v'", [image_tag]),
"image": image,
"violating_key": sprintf("spec.template.spec.containers[%v].image", [i])
}
}
violation[result] {
some i
containers = controller_spec.containers[i]
splittedUrl = split(containers.image, "/")
image = splittedUrl[count(splittedUrl)-1]
count(split(image, ":")) == 3
[image_name, port, tag] = split(image, ":")
tag == image_tag
result = {
"issue detected": true,
"msg": sprintf("Image contains unapproved tag:'%v'", [image_tag]),
"image": image,
"violating_key": sprintf("spec.template.spec.containers[%v].image", [i])
}
}
# Controller input
controller_input = input.review.object
# controller_container acts as an iterator to get containers from the template
controller_spec = controller_input.spec.template.spec {
contains_kind(controller_input.kind, {"StatefulSet" , "DaemonSet", "Deployment", "Job"})
} else = controller_input.spec {
controller_input.kind == "Pod"
} else = controller_input.spec.jobTemplate.spec.template.spec {
controller_input.kind == "CronJob"
}
contains_kind(kind, kinds) {
kinds[_] = kind
}`,
Mutate: true,
Parameters: []domain.PolicyParameters{
{
Name: "image_tag",
Type: "string",
Required: true,
Value: "latest",
},
},
},
"imageTagExcludedLabels": {
Name: "Using latest image tag in container",
Exclude: domain.PolicyExclusions{
Labels: map[string]string{
"app": "nginx",
},
},
Enforce: true,
Expand Down

0 comments on commit 57e5af9

Please sign in to comment.