1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2025-03-06 16:06:56 +00:00

Merge pull request #238 from nirmata/221_resources_provide_exception

221 resources provide exception
This commit is contained in:
Shivkumar Dudhani 2019-07-26 07:35:31 -04:00 committed by GitHub
commit 96393ed6e7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
72 changed files with 981 additions and 605 deletions

View file

@ -34,45 +34,88 @@ spec:
type: object
required:
- name
- resource
- match
properties:
name:
type: string
resource:
match:
type: object
required:
- kinds
- resources
properties:
kinds:
type: array
items:
type: string
name:
type: string
namespace:
type: string
selector:
resources:
type: object
required:
- kinds
properties:
matchLabels:
type: object
additionalProperties:
type: string
matchExpressions:
kinds:
type: array
items:
type: object
required:
- key
- operator
properties:
key:
type: string
name:
type: string
namespace:
type: string
selector:
properties:
matchLabels:
type: object
additionalProperties:
type: string
operator:
matchExpressions:
type: array
items:
type: object
required:
- key
- operator
properties:
key:
type: string
operator:
type: string
values:
type: array
items:
type: string
exclude:
type: object
required:
- resources
properties:
resources:
type: object
properties:
kinds:
type: array
items:
type: string
name:
type: string
namespace:
type: string
selector:
properties:
matchLabels:
type: object
additionalProperties:
type: string
values:
type: array
items:
type: string
matchExpressions:
type: array
items:
type: object
required:
- key
- operator
properties:
key:
type: string
operator:
type: string
values:
type: array
items:
type: string
mutate:
type: object
properties:

View file

@ -26,51 +26,96 @@ spec:
validationFailureAction:
type: string
enum:
- block
- report
- enforce # blocks the resorce api-reques if a rule fails. Default behavior
- audit # allows resource creationg and reports the failed validation rules as violations
rules:
type: array
items:
type: object
required:
- name
- resource
- match
properties:
name:
type: string
resource:
match:
type: object
required:
- kinds
- resources
properties:
kinds:
type: array
items:
type: string
name:
type: string
selector:
resources:
type: object
required:
- kinds
properties:
matchLabels:
type: object
additionalProperties:
type: string
matchExpressions:
kinds:
type: array
items:
type: object
required:
- key
- operator
properties:
key:
type: string
name:
type: string
namespace:
type: string
selector:
properties:
matchLabels:
type: object
additionalProperties:
type: string
operator:
matchExpressions:
type: array
items:
type: object
required:
- key
- operator
properties:
key:
type: string
operator:
type: string
values:
type: array
items:
type: string
exclude:
type: object
required:
- resources
properties:
resources:
type: object
properties:
kinds:
type: array
items:
type: string
name:
type: string
namespace:
type: string
selector:
properties:
matchLabels:
type: object
additionalProperties:
type: string
values:
type: array
items:
type: string
matchExpressions:
type: array
items:
type: object
required:
- key
- operator
properties:
key:
type: string
operator:
type: string
values:
type: array
items:
type: string
mutate:
type: object
properties:

View file

@ -5,12 +5,13 @@ metadata :
spec :
rules:
- name: add-label
resource:
kinds :
- Deployment
selector :
matchLabels :
cli: test
match:
resources:
kinds :
- Deployment
selector :
matchLabels :
cli: test
mutate:
patches:
- path: /metadata/labels/isMutated
@ -20,12 +21,13 @@ spec :
op: replace
value: "nginx_is_mutated"
- name: check-image
resource:
kinds :
- Deployment
selector :
matchLabels :
cli: test
match:
resources:
kinds :
- Deployment
selector :
matchLabels :
cli: test
validate:
message: "The imagePullPolicy must be Always when using image nginx"
pattern:

View file

@ -5,15 +5,17 @@ metadata:
spec:
rules:
- name: image-pull-policy
resource:
kinds:
- Deployment
# - StatefulSet
# name: "my-deployment"
# selector :
# matchLabels:
# app.type: prod
# namespace: "my-namespace"
match:
resources:
kinds:
- Deployment
exclude:
resources:
name: nginx-deployment1
selector :
matchLabels:
app: nginx1
namespace: "default"
mutate:
overlay:
spec:

View file

@ -5,11 +5,12 @@ metadata:
spec:
rules:
- name: check-registries
resource:
kinds:
- Deployment
- StatefulSet
namespace: default
match:
resources:
kinds:
- Deployment
- StatefulSet
namespace: default
validate:
message: "Registry is not allowed"
pattern:

View file

@ -5,22 +5,19 @@ metadata:
spec:
rules:
- name: "deny-ingress-traffic"
resource:
kinds:
- Namespace
name: "devtest"
match:
resources:
kinds:
- Namespace
name: "devtest"
generate:
kind: NetworkPolicy
name: deny-ingress-traffic
data:
spec:
podSelector:
matchLabels: {}
matchExpressions: []
policyTypes:
- Ingress
policyTypes:
- Ingress
metadata:
annotations: {}
labels:
policyname: "default"
# kind: ConfigMap

View file

@ -5,12 +5,13 @@ metadata:
spec:
rules:
- name: validate-runAsNonRoot
resource:
kinds:
- Deployment
selector :
matchLabels:
app.type: prod
match:
resources:
kinds:
- Deployment
selector :
matchLabels:
app.type: prod
validate:
message: "security context 'runAsNonRoot' shoud be set to true"
pattern:

View file

@ -5,9 +5,10 @@ metadata :
spec:
rules:
- name: check-readinessProbe-exists
resource:
kinds :
- Pod
match:
resources:
kinds :
- Pod
validate:
message: "readinessProbe is required"
pattern:
@ -17,9 +18,10 @@ spec:
readinessProbe:
successThreshold: ">1"
- name: check-livenessProbe-exists
resource:
kinds :
- Pod
match:
resources:
kinds :
- Pod
validate:
message: "livenessProbe is required"
pattern:

View file

@ -3,12 +3,13 @@ kind: Policy
metadata:
name: policy-qos
spec:
validationFailureAction: "audit"
# validationFailureAction: "audit"
rules:
- name: add-memory-limit
resource:
kinds:
- Deployment
match:
resources:
kinds:
- Deployment
mutate:
overlay:
spec:
@ -21,10 +22,12 @@ spec:
limits:
# add memory limit if it is not exist
"+(memory)": "300Mi"
"+(cpu)": "100"
- name: check-cpu-memory-limits
resource:
kinds:
- Deployment
match:
resources:
kinds:
- Deployment
validate:
message: "Resource limits are required for CPU and memory"
pattern:

View file

@ -5,12 +5,13 @@ metadata:
spec:
rules:
- name: validate-user-privilege
resource:
kinds:
- Deployment
selector :
matchLabels:
app.type: prod
match:
resources:
kinds:
- Deployment
selector :
matchLabels:
app.type: prod
validate:
message: "validate container security contexts"
pattern:

View file

@ -5,12 +5,13 @@ metadata :
spec :
rules:
- name: "Basic clone config generator for all namespaces"
resource:
kinds:
- Namespace
selector:
matchLabels:
LabelForSelector : "namespace2"
match:
resources:
kinds:
- Namespace
selector:
matchLabels:
LabelForSelector : "namespace2"
generate:
kind: ConfigMap
name: default-config
@ -18,12 +19,13 @@ spec :
namespace: default
name: config-template
- name: "Basic config generator for all namespaces"
resource:
kinds:
- Namespace
selector:
matchLabels:
LabelForSelector : "namespace2"
match:
resources:
kinds:
- Namespace
selector:
matchLabels:
LabelForSelector : "namespace2"
generate:
kind: Secret
name: mongo-creds

View file

@ -5,12 +5,13 @@ metadata:
spec:
rules:
- name: "copy-comfigmap"
resource :
kinds :
- Namespace
selector:
matchLabels:
LabelForSelector : "namespace2"
match:
resources:
kinds :
- Namespace
selector:
matchLabels:
LabelForSelector : "namespace2"
generate :
kind: ConfigMap
name : copied-cm
@ -18,12 +19,13 @@ spec:
namespace : default
name : game-config
- name: "zk-kafka-address"
resource:
kinds:
- Namespace
selector:
matchExpressions:
- {key: LabelForSelector, operator: In, values: [namespace2]}
match:
resources:
kinds:
- Namespace
selector:
matchExpressions:
- {key: LabelForSelector, operator: In, values: [namespace2]}
generate:
kind: ConfigMap
name: zk-kafka-address

View file

@ -5,10 +5,11 @@ metadata:
spec:
rules:
- name: "deny-all-traffic"
resource:
kinds:
- Namespace
name: "*"
match:
resources:
kinds:
- Namespace
name: "*"
generate:
kind: NetworkPolicy
name: deny-all-traffic

View file

@ -5,9 +5,10 @@ metadata:
spec:
rules:
- name: set-image-pull-policy
resource:
kinds:
- Deployment
match:
resources:
kinds:
- Deployment
mutate:
overlay:
spec:

View file

@ -5,12 +5,13 @@ metadata :
spec :
rules:
- name: pEP
resource:
kinds :
- Endpoints
selector:
matchLabels:
label : test
match:
resources:
kinds :
- Endpoints
selector:
matchLabels:
label : test
mutate:
patches:
- path : "/subsets/0/ports/0/port"

View file

@ -5,9 +5,10 @@ metadata:
spec:
rules:
- name: check-defined
resource:
kinds:
- Deployment
match:
resources:
kinds:
- Deployment
validate:
message: "Resource limits are required for CPU and memory"
pattern:
@ -22,9 +23,10 @@ spec:
cpu: "?*"
- name: check-cpu
resource:
kinds:
- Deployment
match:
resources:
kinds:
- Deployment
validate:
message: "CPU request should be less than 4"
pattern:

View file

@ -5,9 +5,10 @@ metadata:
spec:
rules:
- name: check-host-path
resource:
kinds:
- Pod
match:
resources:
kinds:
- Pod
validate:
message: "Host path is not allowed"
pattern:

View file

@ -5,9 +5,10 @@ metadata:
spec:
rules:
- name: image-pull-policy
resource:
kinds:
- Deployment
match:
resources:
kinds:
- Deployment
validate:
message: "Image tag ':latest' requires imagePullPolicy 'Always'"
pattern:

View file

@ -5,14 +5,15 @@ metadata :
spec :
rules:
- name: check-memory_requests_link_in_yaml
resource:
# Kind specifies one or more resource types to match
kinds:
- Deployment
# Name is optional and can use wildcards
name: "*"
# Selector is optional
selector:
match:
resources:
# Kind specifies one or more resource types to match
kinds:
- Deployment
# Name is optional and can use wildcards
name: "*"
# Selector is optional
selector:
validate:
pattern:
spec:

View file

@ -5,14 +5,15 @@ metadata :
spec :
rules:
- name: check-memory_requests_link_in_yaml_relative
resource:
# Kind specifies one or more resource types to match
kinds:
- Deployment
# Name is optional and can use wildcards
name: "*"
# Selector is optional
selector:
match:
resources:
# Kind specifies one or more resource types to match
kinds:
- Deployment
# Name is optional and can use wildcards
name: "*"
# Selector is optional
selector:
validate:
pattern:
spec:

View file

@ -5,9 +5,10 @@ metadata:
spec:
rules:
- name: check-node-port
resource:
kinds:
- Service
match:
resources:
kinds:
- Service
validate:
message: "NodePort type is not allowed"
pattern:

View file

@ -5,11 +5,12 @@ metadata :
spec :
rules:
- name: check-non-root
resource:
kinds:
- Deployment
- StatefuleSet
- DaemonSet
match:
resources:
kinds:
- Deployment
- StatefuleSet
- DaemonSet
validate:
message: "Root user is not allowed"
pattern:

View file

@ -5,9 +5,10 @@ metadata :
spec:
rules:
- name: check-liveness-probe-exists
resource:
kinds :
- StatefulSet
match:
resources:
kinds :
- StatefulSet
validate:
message: "a livenessProbe is required"
pattern:
@ -19,9 +20,10 @@ spec:
livenessProbe:
periodSeconds: ">0"
- name: check-readiness-probe-exists
resource:
kinds :
- StatefulSet
match:
resources:
kinds :
- StatefulSet
validate:
message: "a readinessProbe is required"
pattern:

View file

@ -5,9 +5,10 @@ metadata :
spec:
rules:
- name: check-probe-intervals
resource:
kinds :
- Deployment
match:
resources:
kinds :
- Deployment
validate:
message: "livenessProbe must be > 10s"
pattern:
@ -19,9 +20,10 @@ spec:
livenessProbe:
periodSeconds: ">10"
- name: check-probe-intervals
resource:
kinds :
- Deployment
match:
resources:
kinds :
- Deployment
validate:
message: "readinessProbe must be > 10s"
pattern:

View file

@ -5,10 +5,11 @@ metadata:
spec:
rules:
- name: check-registries
resource:
kinds:
- Deployment
- StatefulSet
match:
resources:
kinds:
- Deployment
- StatefulSet
validate:
message: "Registry is not allowed"
pattern:

View file

@ -210,10 +210,23 @@ func ParseAnnotationsFromObject(bytes []byte) map[string]string {
//AddPolicyJSONPatch generate JSON Patch to add policy informatino JSON patch
func AddPolicyJSONPatch(ann map[string]string, pi *pinfo.PolicyInfo, ruleType pinfo.RuleType) (map[string]string, []byte, error) {
if ann == nil {
ann = make(map[string]string, 0)
if !pi.ContainsRuleType(ruleType) {
return nil, nil, nil
}
PolicyObj := newAnnotationForPolicy(pi)
if ann == nil {
ann = make(map[string]string, 0)
PolicyByte, err := json.Marshal(PolicyObj)
if err != nil {
return nil, nil, err
}
// create a json patch to add annotation object
ann[BuildKey(pi.Name)] = string(PolicyByte)
// create add JSON patch
jsonPatch, err := createAddJSONPatch(BuildKey(pi.Name), string(PolicyByte))
return ann, jsonPatch, err
}
// if the annotations map is present then we
cPolicy, ok := ann[BuildKey(pi.Name)]
if !ok {
PolicyByte, err := json.Marshal(PolicyObj)
@ -223,7 +236,7 @@ func AddPolicyJSONPatch(ann map[string]string, pi *pinfo.PolicyInfo, ruleType pi
// insert policy information
ann[BuildKey(pi.Name)] = string(PolicyByte)
// create add JSON patch
jsonPatch, err := createAddJSONPatch(ann)
jsonPatch, err := createAddJSONPatch(BuildKey(pi.Name), string(PolicyByte))
return ann, jsonPatch, err
}
@ -244,7 +257,7 @@ func AddPolicyJSONPatch(ann map[string]string, pi *pinfo.PolicyInfo, ruleType pi
// update policy information
ann[BuildKey(pi.Name)] = string(cPolicyByte)
// create update JSON patch
jsonPatch, err := createReplaceJSONPatch(ann)
jsonPatch, err := createReplaceJSONPatch(BuildKey(pi.Name), string(cPolicyByte))
return ann, jsonPatch, err
}
@ -253,12 +266,7 @@ func RemovePolicyJSONPatch(ann map[string]string, policy string) (map[string]str
if ann == nil {
return nil, nil, nil
}
delete(ann, policy)
if len(ann) == 0 {
jsonPatch, err := createRemoveJSONPatch(ann)
return nil, jsonPatch, err
}
jsonPatch, err := createReplaceJSONPatch(ann)
jsonPatch, err := createRemoveJSONPatchKey(policy)
return ann, jsonPatch, err
}
@ -268,7 +276,13 @@ type patchMapValue struct {
Value map[string]string `json:"value"`
}
func createRemoveJSONPatch(ann map[string]string) ([]byte, error) {
type patchStringValue struct {
Op string `json:"op"`
Path string `json:"path"`
Value string `json:"value"`
}
func createRemoveJSONPatchMap() ([]byte, error) {
payload := []patchMapValue{{
Op: "remove",
Path: "/metadata/annotations",
@ -276,11 +290,8 @@ func createRemoveJSONPatch(ann map[string]string) ([]byte, error) {
return json.Marshal(payload)
}
func createAddJSONPatchMap(ann map[string]string) ([]byte, error) {
func createAddJSONPatch(ann map[string]string) ([]byte, error) {
if ann == nil {
ann = make(map[string]string, 0)
}
payload := []patchMapValue{{
Op: "add",
Path: "/metadata/annotations",
@ -289,14 +300,33 @@ func createAddJSONPatch(ann map[string]string) ([]byte, error) {
return json.Marshal(payload)
}
func createReplaceJSONPatch(ann map[string]string) ([]byte, error) {
if ann == nil {
ann = make(map[string]string, 0)
}
payload := []patchMapValue{{
Op: "replace",
Path: "/metadata/annotations",
Value: ann,
func createAddJSONPatch(key, value string) ([]byte, error) {
payload := []patchStringValue{{
Op: "add",
Path: "/metadata/annotations/" + key,
Value: value,
}}
return json.Marshal(payload)
}
func createReplaceJSONPatch(key, value string) ([]byte, error) {
// if ann == nil {
// ann = make(map[string]string, 0)
// }
payload := []patchStringValue{{
Op: "replace",
Path: "/metadata/annotations/" + key,
Value: value,
}}
return json.Marshal(payload)
}
func createRemoveJSONPatchKey(key string) ([]byte, error) {
payload := []patchStringValue{{
Op: "remove",
Path: "/metadata/annotations/" + key,
}}
return json.Marshal(payload)
}

View file

@ -34,7 +34,7 @@ func NewAnnotationControler(client *client.Client) Controller {
}
func (c *controller) Add(rkind, rns, rname string, patch []byte) {
c.queue.Add(newInfo(rkind, rns, rname, patch))
c.queue.Add(newInfo(rkind, rns, rname, &patch))
}
func (c *controller) Run(stopCh <-chan struct{}) {
@ -99,14 +99,7 @@ func (c *controller) syncHandler(obj interface{}) error {
}
var err error
// check if the resource is created
_, err = c.client.GetResource(key.RKind, key.RNs, key.RName)
if err != nil {
glog.Errorf("Error creating annotation: unable to get resource %s/%s/%s, will retry: %s ", key.RKind, key.RNs, key.RName, err)
return err
}
// if it is patch the resource
_, err = c.client.PatchResource(key.RKind, key.RNs, key.RName, *key.Patch)
_, err = c.client.PatchResource(key.RKind, key.RNs, key.RName, *key.patch)
if err != nil {
glog.Errorf("Error creating annotation: unable to get resource %s/%s/%s, will retry: %s", key.RKind, key.RNs, key.RName, err)
return err

View file

@ -5,14 +5,14 @@ type info struct {
RNs string
RName string
//TODO:Hack as slice makes the struct unhasable
Patch *[]byte
patch *[]byte
}
func newInfo(rkind, rns, rname string, patch []byte) info {
func newInfo(rkind, rns, rname string, patch *[]byte) info {
return info{
RKind: rkind,
RNs: rns,
RName: rname,
Patch: &patch,
patch: patch,
}
}

View file

@ -2,7 +2,7 @@ package annotations
const annotationQueueName = "annotation-queue"
const workerThreadCount = 1
const workQueueRetryLimit = 3
const workQueueRetryLimit = 5
func getStatus(status bool) string {
if status {
@ -12,5 +12,10 @@ func getStatus(status bool) string {
}
func BuildKey(policyName string) string {
//JSON Pointers
return "policies.kyverno.io~1" + policyName
}
func BuildKeyString(policyName string) string {
return "policies.kyverno.io/" + policyName
}

View file

@ -25,11 +25,22 @@ type Spec struct {
// Rule is set of mutation, validation and generation actions
// for the single resource description
type Rule struct {
Name string `json:"name"`
ResourceDescription `json:"resource"`
Mutation *Mutation `json:"mutate"`
Validation *Validation `json:"validate"`
Generation *Generation `json:"generate"`
Name string `json:"name"`
MatchResources MatchResources `json:"match"`
ExcludeResources ExcludeResources `json:"exclude,omitempty"`
Mutation *Mutation `json:"mutate"`
Validation *Validation `json:"validate"`
Generation *Generation `json:"generate"`
}
//MatchResources contains resource description of the resources that the rule is to apply on
type MatchResources struct {
ResourceDescription `json:"resources"`
}
//ExcludeResources container resource description of the resources that are to be excluded from the applying the policy rule
type ExcludeResources struct {
ResourceDescription `json:"resources"`
}
// ResourceDescription describes the resource to which the PolicyRule will be applied.

View file

@ -9,7 +9,8 @@ import (
// Validate checks if rule is not empty and all substructures are valid
func (r *Rule) Validate() error {
err := r.ResourceDescription.Validate()
// check matches Resoource Description of match resource
err := r.MatchResources.ResourceDescription.Validate()
if err != nil {
return err
}

View file

@ -8,6 +8,7 @@ import (
)
var defaultResourceDescriptionName = "defaultResourceDescription"
var defaultResourceDescription = ResourceDescription{
Kinds: []string{"Deployment"},
Name: &defaultResourceDescriptionName,
@ -18,8 +19,8 @@ var defaultResourceDescription = ResourceDescription{
func Test_EmptyRule(t *testing.T) {
emptyRule := Rule{
Name: "defaultRule",
ResourceDescription: defaultResourceDescription,
Name: "defaultRule",
MatchResources: MatchResources{ResourceDescription: defaultResourceDescription},
}
err := emptyRule.Validate()
assert.Assert(t, err != nil)

View file

@ -41,6 +41,23 @@ func (in *CloneFrom) DeepCopy() *CloneFrom {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ExcludeResources) DeepCopyInto(out *ExcludeResources) {
*out = *in
in.ResourceDescription.DeepCopyInto(&out.ResourceDescription)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExcludeResources.
func (in *ExcludeResources) DeepCopy() *ExcludeResources {
if in == nil {
return nil
}
out := new(ExcludeResources)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *FailedRule) DeepCopyInto(out *FailedRule) {
*out = *in
@ -67,6 +84,23 @@ func (in *Generation) DeepCopy() *Generation {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *MatchResources) DeepCopyInto(out *MatchResources) {
*out = *in
in.ResourceDescription.DeepCopyInto(&out.ResourceDescription)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MatchResources.
func (in *MatchResources) DeepCopy() *MatchResources {
if in == nil {
return nil
}
out := new(MatchResources)
in.DeepCopyInto(out)
return out
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Mutation.
func (in *Mutation) DeepCopy() *Mutation {
if in == nil {
@ -177,7 +211,8 @@ func (in *ResourceDescription) DeepCopy() *ResourceDescription {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Rule) DeepCopyInto(out *Rule) {
*out = *in
in.ResourceDescription.DeepCopyInto(&out.ResourceDescription)
in.MatchResources.DeepCopyInto(&out.MatchResources)
in.ExcludeResources.DeepCopyInto(&out.ExcludeResources)
if in.Mutation != nil {
in, out := &in.Mutation, &out.Mutation
*out = (*in).DeepCopy()

View file

@ -2,11 +2,10 @@ package controller
import (
"github.com/golang/glog"
"github.com/minio/minio/pkg/wildcard"
"github.com/nirmata/kyverno/pkg/annotations"
v1alpha1 "github.com/nirmata/kyverno/pkg/apis/policy/v1alpha1"
client "github.com/nirmata/kyverno/pkg/dclient"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"github.com/nirmata/kyverno/pkg/engine"
"k8s.io/apimachinery/pkg/runtime"
)
@ -23,44 +22,12 @@ func cleanAnnotations(client *client.Client, obj interface{}) {
return
}
// Get the resources that apply to the policy
// key uid
resourceMap := map[string]unstructured.Unstructured{}
for _, rule := range policy.Spec.Rules {
for _, k := range rule.Kinds {
if k == "Namespace" {
continue
}
// kind -> resource
gvr := client.DiscoveryClient.GetGVRFromKind(k)
// label selectors
// namespace ? should it be default or allow policy to specify it
namespace := "default"
if rule.ResourceDescription.Namespace != nil {
namespace = *rule.ResourceDescription.Namespace
}
list, err := client.ListResource(k, namespace, rule.ResourceDescription.Selector)
if err != nil {
glog.Errorf("unable to list resource for %s with label selector %s", gvr.Resource, rule.Selector.String())
glog.Errorf("unable to apply policy %s rule %s. err: %s", policy.Name, rule.Name, err)
continue
}
for _, res := range list.Items {
name := rule.ResourceDescription.Name
if name != nil {
// wild card matching
if !wildcard.Match(*name, res.GetName()) {
continue
}
}
resourceMap[string(res.GetUID())] = res
}
}
}
resourceMap := engine.ListResourcesThatApplyToPolicy(client, &policy)
// remove annotations for the resources
for _, obj := range resourceMap {
// get annotations
ann := obj.GetAnnotations()
ann := obj.Resource.GetAnnotations()
_, patch, err := annotations.RemovePolicyJSONPatch(ann, annotations.BuildKey(policy.Name))
if err != nil {
@ -68,7 +35,7 @@ func cleanAnnotations(client *client.Client, obj interface{}) {
continue
}
// patch the resource
_, err = client.PatchResource(obj.GetKind(), obj.GetNamespace(), obj.GetName(), patch)
_, err = client.PatchResource(obj.Resource.GetKind(), obj.Resource.GetNamespace(), obj.Resource.GetName(), patch)
if err != nil {
glog.Error(err)
continue

View file

@ -227,7 +227,7 @@ func (pc *PolicyController) createAnnotations(policyInfos []*info.PolicyInfo) {
}
// Generation rules
ann, gpatch, err := annotations.AddPolicyJSONPatch(ann, pi, info.Validation)
ann, gpatch, err := annotations.AddPolicyJSONPatch(ann, pi, info.Generation)
if err != nil {
glog.Error(err)
}
@ -251,14 +251,23 @@ func (pc *PolicyController) createAnnotations(policyInfos []*info.PolicyInfo) {
patch = mpatch
}
// generation
if gpatch != nil {
patch, err = jsonpatch.MergePatch(patch, gpatch)
if err != nil {
glog.Error(err)
continue
// generation
if patch != nil {
patch, err = jsonpatch.MergePatch(patch, gpatch)
if err != nil {
glog.Error(err)
continue
}
} else {
patch = gpatch
}
}
if patch == nil {
return
}
// add the anotation to the resource
_, err = pc.client.PatchResource(pi.RKind, pi.RNamespace, pi.RName, patch)
if err != nil {

View file

@ -8,44 +8,80 @@ import (
client "github.com/nirmata/kyverno/pkg/dclient"
"github.com/nirmata/kyverno/pkg/info"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
v1helper "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
)
// ProcessExisting checks for mutation and validation violations of existing resources
func ProcessExisting(client *client.Client, policy *types.Policy) []*info.PolicyInfo {
glog.Infof("Applying policy %s on existing resources", policy.Name)
func ListResourcesThatApplyToPolicy(client *client.Client, policy *types.Policy) map[string]resourceInfo {
// key uid
resourceMap := map[string]resourceInfo{}
for _, rule := range policy.Spec.Rules {
for _, k := range rule.Kinds {
// Match
for _, k := range rule.MatchResources.Kinds {
// kind -> resource
gvr := client.DiscoveryClient.GetGVRFromKind(k)
// label selectors
// namespace ? should it be default or allow policy to specify it
// Namespace
namespace := "default"
if rule.ResourceDescription.Namespace != nil {
namespace = *rule.ResourceDescription.Namespace
if rule.MatchResources.Namespace != nil {
namespace = *rule.MatchResources.Namespace
}
if k == "Namespace" {
namespace = ""
}
// Check if exclude namespace is not clashing
if rule.ExcludeResources.Namespace != nil && *rule.ExcludeResources.Namespace == namespace {
// as the namespace is excluded
continue
}
list, err := client.ListResource(k, namespace, rule.ResourceDescription.Selector)
// List resources
list, err := client.ListResource(k, namespace, rule.MatchResources.Selector)
if err != nil {
glog.Errorf("unable to list resource for %s with label selector %s", gvr.Resource, rule.Selector.String())
glog.Errorf("unable to list resource for %s with label selector %s", gvr.Resource, rule.MatchResources.Selector.String())
glog.Errorf("unable to apply policy %s rule %s. err: %s", policy.Name, rule.Name, err)
continue
}
var selector labels.Selector
// exclude label selector
if rule.ExcludeResources.Selector != nil {
selector, err = v1helper.LabelSelectorAsSelector(rule.ExcludeResources.Selector)
if err != nil {
glog.Error(err)
}
}
for _, res := range list.Items {
name := rule.ResourceDescription.Name
gvk := res.GroupVersionKind()
// exclude label selectors
if selector != nil {
set := labels.Set(res.GetLabels())
if selector.Matches(set) {
// if matches
continue
}
}
var name *string
// match
// name
// wild card matching
name = rule.MatchResources.Name
if name != nil {
// wild card matching
// if does not match then we skip
if !wildcard.Match(*name, res.GetName()) {
continue
}
}
ri := resourceInfo{resource: res, gvk: &metav1.GroupVersionKind{Group: gvk.Group,
// exclude
// name
// wild card matching
name = rule.ExcludeResources.Name
if name != nil {
// if matches then we skip
if wildcard.Match(*name, res.GetName()) {
continue
}
}
gvk := res.GroupVersionKind()
ri := resourceInfo{Resource: res, Gvk: &metav1.GroupVersionKind{Group: gvk.Group,
Version: gvk.Version,
Kind: gvk.Kind}}
@ -54,13 +90,60 @@ func ProcessExisting(client *client.Client, policy *types.Policy) []*info.Policy
}
}
}
return resourceMap
}
// ProcessExisting checks for mutation and validation violations of existing resources
func ProcessExisting(client *client.Client, policy *types.Policy) []*info.PolicyInfo {
glog.Infof("Applying policy %s on existing resources", policy.Name)
// key uid
resourceMap := ListResourcesThatApplyToPolicy(client, policy)
// for _, rule := range policy.Spec.Rules {
// for _, k := range rule.Kinds {
// // kind -> resource
// gvr := client.DiscoveryClient.GetGVRFromKind(k)
// // label selectors
// // namespace ? should it be default or allow policy to specify it
// namespace := "default"
// if rule.ResourceDescription.Namespace != nil {
// namespace = *rule.ResourceDescription.Namespace
// }
// if k == "Namespace" {
// namespace = ""
// }
// list, err := client.ListResource(k, namespace, rule.ResourceDescription.Selector)
// if err != nil {
// glog.Errorf("unable to list resource for %s with label selector %s", gvr.Resource, rule.Selector.String())
// glog.Errorf("unable to apply policy %s rule %s. err: %s", policy.Name, rule.Name, err)
// continue
// }
// for _, res := range list.Items {
// name := rule.ResourceDescription.Name
// gvk := res.GroupVersionKind()
// if name != nil {
// // wild card matching
// if !wildcard.Match(*name, res.GetName()) {
// continue
// }
// }
// ri := resourceInfo{resource: res, gvk: &metav1.GroupVersionKind{Group: gvk.Group,
// Version: gvk.Version,
// Kind: gvk.Kind}}
// resourceMap[string(res.GetUID())] = ri
// }
// }
// }
policyInfos := []*info.PolicyInfo{}
// for the filtered resource apply policy
for _, v := range resourceMap {
policyInfo, err := applyPolicy(client, policy, v)
if err != nil {
glog.Errorf("unable to apply policy %s on resource %s/%s", policy.Name, v.resource.GetName(), v.resource.GetNamespace())
glog.Errorf("unable to apply policy %s on resource %s/%s", policy.Name, v.Resource.GetName(), v.Resource.GetNamespace())
glog.Error(err)
continue
}
@ -71,28 +154,28 @@ func ProcessExisting(client *client.Client, policy *types.Policy) []*info.Policy
}
func applyPolicy(client *client.Client, policy *types.Policy, res resourceInfo) (*info.PolicyInfo, error) {
policyInfo := info.NewPolicyInfo(policy.Name, res.gvk.Kind, res.resource.GetName(), res.resource.GetNamespace(), policy.Spec.ValidationFailureAction)
policyInfo := info.NewPolicyInfo(policy.Name, res.Gvk.Kind, res.Resource.GetName(), res.Resource.GetNamespace(), policy.Spec.ValidationFailureAction)
glog.Infof("Applying policy %s with %d rules\n", policy.ObjectMeta.Name, len(policy.Spec.Rules))
rawResource, err := res.resource.MarshalJSON()
rawResource, err := res.Resource.MarshalJSON()
if err != nil {
return nil, err
}
// Mutate
mruleInfos, err := mutation(policy, rawResource, res.gvk)
mruleInfos, err := mutation(policy, rawResource, res.Gvk)
policyInfo.AddRuleInfos(mruleInfos)
if err != nil {
return nil, err
}
// Validation
vruleInfos, err := Validate(*policy, rawResource, *res.gvk)
vruleInfos, err := Validate(*policy, rawResource, *res.Gvk)
policyInfo.AddRuleInfos(vruleInfos)
if err != nil {
return nil, err
}
if res.gvk.Kind == "Namespace" {
if res.Gvk.Kind == "Namespace" {
// Generation
gruleInfos := GenerateNew(client, policy, res.resource)
gruleInfos := GenerateNew(client, policy, res.Resource)
policyInfo.AddRuleInfos(gruleInfos)
}

View file

@ -21,7 +21,7 @@ func Generate(client *client.Client, policy kubepolicy.Policy, rawResource []byt
ri := info.NewRuleInfo(rule.Name, info.Generation)
ok := ResourceMeetsDescription(rawResource, rule.ResourceDescription, gvk)
ok := ResourceMeetsDescription(rawResource, rule.MatchResources.ResourceDescription, rule.ExcludeResources.ResourceDescription, gvk)
if !ok {
glog.Infof("Rule is not applicable to the request: rule name = %s in policy %s \n", rule.Name, policy.ObjectMeta.Name)
continue

View file

@ -19,7 +19,7 @@ func Mutate(policy kubepolicy.Policy, rawResource []byte, gvk metav1.GroupVersio
}
ri := info.NewRuleInfo(rule.Name, info.Mutation)
ok := ResourceMeetsDescription(rawResource, rule.ResourceDescription, gvk)
ok := ResourceMeetsDescription(rawResource, rule.MatchResources.ResourceDescription, rule.ExcludeResources.ResourceDescription, gvk)
if !ok {
glog.V(3).Infof("Not applicable on specified resource kind%s", gvk.Kind)
continue
@ -27,12 +27,18 @@ func Mutate(policy kubepolicy.Policy, rawResource []byte, gvk metav1.GroupVersio
// Process Overlay
if rule.Mutation.Overlay != nil {
overlayPatches, err := ProcessOverlay(rule, rawResource, gvk)
if err != nil {
ri.Fail()
ri.Addf("overlay application has failed, err %v.", err)
} else {
if err == nil {
if len(overlayPatches) == 0{
// if array elements dont match then we skip(nil patch, no error)
// or if acnohor is defined and doenst match
// policy is not applicable
continue
}
ri.Addf("Rule %s: Overlay succesfully applied.", rule.Name)
allPatches = append(allPatches, overlayPatches...)
} else {
ri.Fail()
ri.Addf("overlay application has failed, err %v.", err)
}
}

View file

@ -275,16 +275,43 @@ func processSubtree(overlay interface{}, path string, op string) ([]byte, error)
// converts overlay to JSON string to be inserted into the JSON Patch
func prepareJSONValue(overlay interface{}) string {
jsonOverlay, err := json.Marshal(overlay)
var err error
if err != nil || hasOnlyAnchors(overlay) {
glog.V(3).Info(err)
return ""
}
// Need to remove anchors from the overlay struct
overlayWithoutAnchors := removeAnchorFromSubTree(overlay)
jsonOverlay, err := json.Marshal(overlayWithoutAnchors)
return string(jsonOverlay)
}
func removeAnchorFromSubTree(overlay interface{}) interface{} {
var result interface{}
switch typed := overlay.(type) {
case map[string]interface{}:
// assuming only keys have anchors
result = removeAnchroFromMap(typed)
case []interface{}:
arrayResult := make([]interface{}, 0)
for _, value := range typed {
arrayResult = append(arrayResult, removeAnchorFromSubTree(value))
}
result = arrayResult
default:
result = overlay
}
return result
}
func removeAnchroFromMap(overlay map[string]interface{}) map[string]interface{} {
result := make(map[string]interface{}, 0)
for k, v := range overlay {
result[getRawKeyIfWrappedWithAttributes(k)] = removeAnchorFromSubTree(v)
}
return result
}
// Anchor has pattern value, so resource shouldn't be mutated with it
// If entire subtree has only anchor keys - we should skip inserting it
func hasOnlyAnchors(overlay interface{}) bool {

View file

@ -6,8 +6,10 @@ import (
"strconv"
"strings"
"github.com/golang/glog"
"github.com/minio/minio/pkg/wildcard"
kubepolicy "github.com/nirmata/kyverno/pkg/apis/policy/v1alpha1"
v1alpha1 "github.com/nirmata/kyverno/pkg/apis/policy/v1alpha1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@ -15,8 +17,8 @@ import (
)
// ResourceMeetsDescription checks requests kind, name and labels to fit the policy rule
func ResourceMeetsDescription(resourceRaw []byte, description kubepolicy.ResourceDescription, gvk metav1.GroupVersionKind) bool {
if !findKind(description.Kinds, gvk.Kind) {
func ResourceMeetsDescription(resourceRaw []byte, matches v1alpha1.ResourceDescription, exclude v1alpha1.ResourceDescription, gvk metav1.GroupVersionKind) bool {
if !findKind(matches.Kinds, gvk.Kind) {
return false
}
@ -25,31 +27,58 @@ func ResourceMeetsDescription(resourceRaw []byte, description kubepolicy.Resourc
name := ParseNameFromObject(resourceRaw)
namespace := ParseNamespaceFromObject(resourceRaw)
if description.Name != nil {
if !wildcard.Match(*description.Name, name) {
if matches.Name != nil {
// Matches
if !wildcard.Match(*matches.Name, name) {
return false
}
}
if description.Namespace != nil && *description.Namespace != namespace {
// Exclude
// the resource name matches the exclude resource name then reject
if exclude.Name != nil {
if wildcard.Match(*exclude.Name, name) {
return false
}
}
// Matches
if matches.Namespace != nil && *matches.Namespace != namespace {
return false
}
if description.Selector != nil {
selector, err := metav1.LabelSelectorAsSelector(description.Selector)
if err != nil {
return false
}
labelMap := parseLabelsFromMetadata(meta)
if !selector.Matches(labelMap) {
return false
}
// Exclude
if exclude.Namespace != nil && *exclude.Namespace == namespace {
return false
}
// Matches
if matches.Selector != nil {
selector, err := metav1.LabelSelectorAsSelector(matches.Selector)
if err != nil {
glog.Error(err)
return false
}
if meta != nil {
labelMap := parseLabelsFromMetadata(meta)
if !selector.Matches(labelMap) {
return false
}
}
}
// Exclude
if exclude.Selector != nil {
selector, err := metav1.LabelSelectorAsSelector(exclude.Selector)
// if the label selector is incorrect, should be fail or
if err != nil {
glog.Error(err)
return false
}
if meta != nil {
labelMap := parseLabelsFromMetadata(meta)
if selector.Matches(labelMap) {
return false
}
}
}
}
return true
}
@ -57,8 +86,11 @@ func ResourceMeetsDescription(resourceRaw []byte, description kubepolicy.Resourc
func parseMetadataFromObject(bytes []byte) map[string]interface{} {
var objectJSON map[string]interface{}
json.Unmarshal(bytes, &objectJSON)
return objectJSON["metadata"].(map[string]interface{})
meta, ok := objectJSON["metadata"].(map[string]interface{})
if !ok {
return nil
}
return meta
}
//ParseKindFromObject get kind from resource
@ -85,10 +117,16 @@ func parseLabelsFromMetadata(meta map[string]interface{}) labels.Set {
func ParseNameFromObject(bytes []byte) string {
var objectJSON map[string]interface{}
json.Unmarshal(bytes, &objectJSON)
meta, ok := objectJSON["metadata"]
if !ok {
return ""
}
meta := objectJSON["metadata"].(map[string]interface{})
if name, ok := meta["name"].(string); ok {
metaMap, ok := meta.(map[string]interface{})
if !ok {
return ""
}
if name, ok := metaMap["name"].(string); ok {
return name
}
return ""
@ -98,12 +136,19 @@ func ParseNameFromObject(bytes []byte) string {
func ParseNamespaceFromObject(bytes []byte) string {
var objectJSON map[string]interface{}
json.Unmarshal(bytes, &objectJSON)
meta := objectJSON["metadata"].(map[string]interface{})
if namespace, ok := meta["namespace"].(string); ok {
return namespace
meta, ok := objectJSON["metadata"]
if !ok {
return ""
}
metaMap, ok := meta.(map[string]interface{})
if !ok {
return ""
}
if name, ok := metaMap["namespace"].(string); ok {
return name
}
return ""
}
@ -253,6 +298,6 @@ func convertToFloat(value interface{}) (float64, error) {
}
type resourceInfo struct {
resource unstructured.Unstructured
gvk *metav1.GroupVersionKind
Resource unstructured.Unstructured
Gvk *metav1.GroupVersionKind
}

View file

@ -18,6 +18,7 @@ func TestResourceMeetsDescription_Kind(t *testing.T) {
MatchExpressions: nil,
},
}
excludeResourcesResourceDesc := types.ResourceDescription{}
groupVersionKind := metav1.GroupVersionKind{Kind: "ConfigMap"}
rawResource := []byte(`{
@ -32,12 +33,12 @@ func TestResourceMeetsDescription_Kind(t *testing.T) {
}
}`)
assert.Assert(t, ResourceMeetsDescription(rawResource, resourceDescription, groupVersionKind))
assert.Assert(t, ResourceMeetsDescription(rawResource, resourceDescription, excludeResourcesResourceDesc, groupVersionKind))
resourceDescription.Kinds[0] = "Deployment"
assert.Assert(t, false == ResourceMeetsDescription(rawResource, resourceDescription, groupVersionKind))
assert.Assert(t, false == ResourceMeetsDescription(rawResource, resourceDescription, excludeResourcesResourceDesc, groupVersionKind))
resourceDescription.Kinds[0] = "ConfigMap"
groupVersionKind.Kind = "Deployment"
assert.Assert(t, false == ResourceMeetsDescription(rawResource, resourceDescription, groupVersionKind))
assert.Assert(t, false == ResourceMeetsDescription(rawResource, resourceDescription, excludeResourcesResourceDesc, groupVersionKind))
}
func TestResourceMeetsDescription_Name(t *testing.T) {
@ -50,6 +51,8 @@ func TestResourceMeetsDescription_Name(t *testing.T) {
MatchExpressions: nil,
},
}
excludeResourcesResourceDesc := types.ResourceDescription{}
groupVersionKind := metav1.GroupVersionKind{Kind: "ConfigMap"}
rawResource := []byte(`{
@ -64,9 +67,9 @@ func TestResourceMeetsDescription_Name(t *testing.T) {
}
}`)
assert.Assert(t, ResourceMeetsDescription(rawResource, resourceDescription, groupVersionKind))
assert.Assert(t, ResourceMeetsDescription(rawResource, resourceDescription, excludeResourcesResourceDesc, groupVersionKind))
resourceName = "test-config-map-new"
assert.Assert(t, false == ResourceMeetsDescription(rawResource, resourceDescription, groupVersionKind))
assert.Assert(t, false == ResourceMeetsDescription(rawResource, resourceDescription, excludeResourcesResourceDesc, groupVersionKind))
rawResource = []byte(`{
"metadata":{
@ -79,7 +82,7 @@ func TestResourceMeetsDescription_Name(t *testing.T) {
}
}
}`)
assert.Assert(t, ResourceMeetsDescription(rawResource, resourceDescription, groupVersionKind))
assert.Assert(t, ResourceMeetsDescription(rawResource, resourceDescription, excludeResourcesResourceDesc, groupVersionKind))
rawResource = []byte(`{
"metadata":{
@ -92,7 +95,7 @@ func TestResourceMeetsDescription_Name(t *testing.T) {
}
}
}`)
assert.Assert(t, false == ResourceMeetsDescription(rawResource, resourceDescription, groupVersionKind))
assert.Assert(t, false == ResourceMeetsDescription(rawResource, resourceDescription, excludeResourcesResourceDesc, groupVersionKind))
}
func TestResourceMeetsDescription_MatchExpressions(t *testing.T) {
@ -134,6 +137,8 @@ func TestResourceMeetsDescription_MatchExpressions(t *testing.T) {
},
},
}
excludeResourcesResourceDesc := types.ResourceDescription{}
groupVersionKind := metav1.GroupVersionKind{Kind: "ConfigMap"}
rawResource := []byte(`{
"metadata":{
@ -147,7 +152,7 @@ func TestResourceMeetsDescription_MatchExpressions(t *testing.T) {
}
}`)
assert.Assert(t, ResourceMeetsDescription(rawResource, resourceDescription, groupVersionKind))
assert.Assert(t, ResourceMeetsDescription(rawResource, resourceDescription, excludeResourcesResourceDesc, groupVersionKind))
rawResource = []byte(`{
"metadata":{
@ -161,7 +166,7 @@ func TestResourceMeetsDescription_MatchExpressions(t *testing.T) {
}
}`)
assert.Assert(t, false == ResourceMeetsDescription(rawResource, resourceDescription, groupVersionKind))
assert.Assert(t, false == ResourceMeetsDescription(rawResource, resourceDescription, excludeResourcesResourceDesc, groupVersionKind))
}
func TestResourceMeetsDescription_MatchLabels(t *testing.T) {
@ -178,6 +183,7 @@ func TestResourceMeetsDescription_MatchLabels(t *testing.T) {
},
}
groupVersionKind := metav1.GroupVersionKind{Kind: "ConfigMap"}
excludeResourcesResourceDesc := types.ResourceDescription{}
rawResource := []byte(`{
"metadata":{
@ -190,7 +196,7 @@ func TestResourceMeetsDescription_MatchLabels(t *testing.T) {
}
}
}`)
assert.Assert(t, ResourceMeetsDescription(rawResource, resourceDescription, groupVersionKind))
assert.Assert(t, ResourceMeetsDescription(rawResource, resourceDescription, excludeResourcesResourceDesc, groupVersionKind))
rawResource = []byte(`{
"metadata":{
@ -203,7 +209,7 @@ func TestResourceMeetsDescription_MatchLabels(t *testing.T) {
}
}
}`)
assert.Assert(t, false == ResourceMeetsDescription(rawResource, resourceDescription, groupVersionKind))
assert.Assert(t, false == ResourceMeetsDescription(rawResource, resourceDescription, excludeResourcesResourceDesc, groupVersionKind))
resourceDescription = types.ResourceDescription{
Kinds: []string{"ConfigMap"},
@ -217,7 +223,7 @@ func TestResourceMeetsDescription_MatchLabels(t *testing.T) {
},
}
assert.Assert(t, ResourceMeetsDescription(rawResource, resourceDescription, groupVersionKind))
assert.Assert(t, ResourceMeetsDescription(rawResource, resourceDescription, excludeResourcesResourceDesc, groupVersionKind))
}
func TestResourceMeetsDescription_MatchLabelsAndMatchExpressions(t *testing.T) {
@ -241,6 +247,7 @@ func TestResourceMeetsDescription_MatchLabelsAndMatchExpressions(t *testing.T) {
},
}
groupVersionKind := metav1.GroupVersionKind{Kind: "ConfigMap"}
excludeResourcesResourceDesc := types.ResourceDescription{}
rawResource := []byte(`{
"metadata":{
@ -254,7 +261,7 @@ func TestResourceMeetsDescription_MatchLabelsAndMatchExpressions(t *testing.T) {
}
}`)
assert.Assert(t, ResourceMeetsDescription(rawResource, resourceDescription, groupVersionKind))
assert.Assert(t, ResourceMeetsDescription(rawResource, resourceDescription, excludeResourcesResourceDesc, groupVersionKind))
resourceDescription = types.ResourceDescription{
Kinds: []string{"ConfigMap"},
@ -286,7 +293,7 @@ func TestResourceMeetsDescription_MatchLabelsAndMatchExpressions(t *testing.T) {
}
}
}`)
assert.Assert(t, ResourceMeetsDescription(rawResource, resourceDescription, groupVersionKind))
assert.Assert(t, ResourceMeetsDescription(rawResource, resourceDescription, excludeResourcesResourceDesc, groupVersionKind))
resourceDescription = types.ResourceDescription{
Kinds: []string{"ConfigMap"},
@ -307,7 +314,7 @@ func TestResourceMeetsDescription_MatchLabelsAndMatchExpressions(t *testing.T) {
},
}
assert.Assert(t, false == ResourceMeetsDescription(rawResource, resourceDescription, groupVersionKind))
assert.Assert(t, false == ResourceMeetsDescription(rawResource, resourceDescription, excludeResourcesResourceDesc, groupVersionKind))
resourceDescription = types.ResourceDescription{
Kinds: []string{"ConfigMap"},
@ -329,7 +336,7 @@ func TestResourceMeetsDescription_MatchLabelsAndMatchExpressions(t *testing.T) {
},
}
assert.Assert(t, false == ResourceMeetsDescription(rawResource, resourceDescription, groupVersionKind))
assert.Assert(t, false == ResourceMeetsDescription(rawResource, resourceDescription, excludeResourcesResourceDesc, groupVersionKind))
}
func TestWrappedWithParentheses_StringIsWrappedWithParentheses(t *testing.T) {

View file

@ -32,7 +32,7 @@ func Validate(policy kubepolicy.Policy, rawResource []byte, gvk metav1.GroupVers
}
ri := info.NewRuleInfo(rule.Name, info.Validation)
ok := ResourceMeetsDescription(rawResource, rule.ResourceDescription, gvk)
ok := ResourceMeetsDescription(rawResource, rule.MatchResources.ResourceDescription, rule.ExcludeResources.ResourceDescription, gvk)
if !ok {
glog.V(3).Infof("Not applicable on specified resource kind%s", gvk.Kind)
continue

View file

@ -6,7 +6,7 @@ const eventWorkQueueName = "policy-controller-events"
const eventWorkerThreadCount = 1
const workQueueRetryLimit = 1
const workQueueRetryLimit = 5
//Info defines the event details
type Info struct {

View file

@ -1,10 +1,12 @@
package gencontroller
import (
"encoding/json"
"fmt"
"strings"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
"github.com/golang/glog"
"github.com/nirmata/kyverno/pkg/annotations"
@ -14,6 +16,7 @@ import (
"github.com/nirmata/kyverno/pkg/info"
violation "github.com/nirmata/kyverno/pkg/violation"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
)
@ -44,7 +47,16 @@ func (c *Controller) listPolicies(ns *corev1.Namespace) ([]*v1alpha1.Policy, err
for _, r := range p.Spec.Rules {
if r.Generation != nil {
// Check if the resource meets the description
if namespaceMeetsRuleDescription(ns, r.ResourceDescription) {
data, err := json.Marshal(ns)
if err != nil {
glog.Error(err)
continue
}
// convert types of GVK
nsGvk := schema.FromAPIVersionAndKind("v1", "Namespace")
// Hardcode as we have a informer on specified gvk
gvk := metav1.GroupVersionKind{Group: nsGvk.Group, Kind: nsGvk.Kind, Version: nsGvk.Version}
if engine.ResourceMeetsDescription(data, r.MatchResources.ResourceDescription, r.ExcludeResources.ResourceDescription, gvk) {
fpolicies = append(fpolicies, p)
break
}
@ -76,8 +88,9 @@ func (c *Controller) processPolicy(ns *corev1.Namespace, p *v1alpha1.Policy) {
ruleInfos := engine.GenerateNew(c.client, p, unstObj)
policyInfo.AddRuleInfos(ruleInfos)
// generate annotations
// generate annotations on namespace
c.createAnnotations(policyInfo)
//TODO generate namespace on created resources
if !policyInfo.IsSuccessful() {
glog.Infof("Failed to apply policy %s on resource %s %s", p.Name, ns.Kind, ns.Name)
@ -127,7 +140,7 @@ func (c *Controller) createAnnotations(pi *info.PolicyInfo) {
// add annotation for policy application
ann := obj.GetAnnotations()
// Generation rules
ann, gpatch, err := annotations.AddPolicyJSONPatch(ann, pi, info.Mutation)
ann, gpatch, err := annotations.AddPolicyJSONPatch(ann, pi, info.Generation)
if err != nil {
glog.Error(err)
return
@ -136,7 +149,6 @@ func (c *Controller) createAnnotations(pi *info.PolicyInfo) {
// nothing to patch
return
}
// add the anotation to the resource
_, err = c.client.PatchResource(pi.RKind, pi.RNamespace, pi.RName, gpatch)
if err != nil {

View file

@ -199,3 +199,13 @@ func (pi *PolicyInfo) GetRuleNames(onSuccess bool) string {
return strings.Join(ruleNames, ",")
}
//ContainsRuleType checks if a policy info contains a rule type
func (pi *PolicyInfo) ContainsRuleType(ruleType RuleType) bool {
for _, r := range pi.Rules {
if r.RuleType == ruleType {
return true
}
}
return false
}

View file

@ -26,7 +26,7 @@ func (ws *WebhookServer) removePolicyViolation(request *v1beta1.AdmissionRequest
rkind := request.Kind.Kind
// check if the resource meets the policy Resource description
for _, rule := range policy.Spec.Rules {
ok := engine.ResourceMeetsDescription(request.Object.Raw, rule.ResourceDescription, request.Kind)
ok := engine.ResourceMeetsDescription(request.Object.Raw, rule.MatchResources.ResourceDescription, rule.ExcludeResources.ResourceDescription, request.Kind)
if ok {
// Check if the policy has a violation for this resource
err := ws.violationBuilder.ResourceRemoval(policy.Name, rkind, rns, rname)

View file

@ -1,7 +1,6 @@
package webhooks
import (
jsonpatch "github.com/evanphx/json-patch"
"github.com/golang/glog"
engine "github.com/nirmata/kyverno/pkg/engine"
"github.com/nirmata/kyverno/pkg/info"
@ -33,7 +32,6 @@ func (ws *WebhookServer) HandleMutation(request *v1beta1.AdmissionRequest) *v1be
}
var allPatches [][]byte
var annPatches []byte
policyInfos := []*info.PolicyInfo{}
for _, policy := range policies {
// check if policy has a rule for the admission request kind
@ -79,14 +77,8 @@ func (ws *WebhookServer) HandleMutation(request *v1beta1.AdmissionRequest) *v1be
annPatch := addAnnotationsToResource(request.Object.Raw, policyInfo, info.Mutation)
if annPatch != nil {
if annPatches == nil {
annPatches = annPatch
} else {
annPatches, err = jsonpatch.MergePatch(annPatches, annPatch)
if err != nil {
glog.Error(err)
}
}
// add annotations
ws.annotationsController.Add(rkind, rns, rname, annPatch)
}
}
@ -94,12 +86,6 @@ func (ws *WebhookServer) HandleMutation(request *v1beta1.AdmissionRequest) *v1be
eventsInfo, _ := newEventInfoFromPolicyInfo(policyInfos, (request.Operation == v1beta1.Update), info.Mutation)
ws.eventController.Add(eventsInfo...)
}
// add annotations
if annPatches != nil {
// fmt.Println(string(annPatches))
ws.annotationsController.Add(rkind, rns, rname, annPatches)
}
ok, msg := isAdmSuccesful(policyInfos)
if ok {
patchType := v1beta1.PatchTypeJSONPatch

View file

@ -77,10 +77,18 @@ func getApplicableKindsForPolicy(p *v1alpha1.Policy) []string {
kindsMap := map[string]interface{}{}
kinds := []string{}
// iterate over the rules an identify all kinds
// Matching
for _, rule := range p.Spec.Rules {
for _, k := range rule.ResourceDescription.Kinds {
for _, k := range rule.MatchResources.Kinds {
kindsMap[k] = nil
}
// remove excluded ones
for _, k := range rule.ExcludeResources.Kinds {
if _, ok := kindsMap[k]; ok {
// delete kind
delete(kindsMap, k)
}
}
}
// get the kinds

View file

@ -1,7 +1,6 @@
package webhooks
import (
jsonpatch "github.com/evanphx/json-patch"
"github.com/golang/glog"
engine "github.com/nirmata/kyverno/pkg/engine"
"github.com/nirmata/kyverno/pkg/info"
@ -34,7 +33,6 @@ func (ws *WebhookServer) HandleValidation(request *v1beta1.AdmissionRequest) *v1
glog.Errorf("failed to parse KIND from request: Namespace=%s Name=%s UID=%s patchOperation=%s\n", request.Namespace, request.Name, request.UID, request.Operation)
}
var annPatches []byte
for _, policy := range policies {
if !StringInSlice(request.Kind.Kind, getApplicableKindsForPolicy(policy)) {
@ -88,14 +86,7 @@ func (ws *WebhookServer) HandleValidation(request *v1beta1.AdmissionRequest) *v1
// annotations
annPatch := addAnnotationsToResource(request.Object.Raw, policyInfo, info.Validation)
if annPatch != nil {
if annPatches == nil {
annPatches = annPatch
} else {
annPatches, err = jsonpatch.MergePatch(annPatches, annPatch)
if err != nil {
glog.Error(err)
}
}
ws.annotationsController.Add(rkind, rns, rname, annPatch)
}
}
@ -106,10 +97,6 @@ func (ws *WebhookServer) HandleValidation(request *v1beta1.AdmissionRequest) *v1
ws.violationBuilder.Add(violations...)
ws.eventController.Add(eventsInfo...)
}
// add annotations
if annPatches != nil {
ws.annotationsController.Add(rkind, rns, rname, annPatches)
}
// If Validation fails then reject the request
ok, msg := isAdmSuccesful(policyInfos)
// violations are created if "audit" flag is set

View file

@ -5,10 +5,11 @@ metadata :
spec :
rules:
- name: pCM1
resource:
kinds :
- ConfigMap
name: "game-config"
match:
resources:
kinds :
- ConfigMap
name: "game-config"
mutate:
overlay:
data:
@ -25,10 +26,11 @@ spec :
op : add
value : newValue
- name: pCM2
resource:
kinds :
- ConfigMap
name: "game-config"
match:
resources:
kinds :
- ConfigMap
name: "game-config"
mutate:
patches:
- path : "/data/secretData"
@ -37,10 +39,11 @@ spec :
op : replace
value : "data is replaced"
- name: pCM3
resource:
kinds :
- ConfigMap
name: "game-config"
match:
resources:
kinds :
- ConfigMap
name: "game-config"
mutate:
patches:
- path : "/data/secretData"
@ -52,10 +55,11 @@ spec :
data:
game.properties: "*enemies=aliens*"
- name: pCM4
resource:
kinds :
- ConfigMap
name: "game-config"
match:
resources:
kinds :
- ConfigMap
name: "game-config"
validate:
message: "This CM data is broken because it does not have ui.properties"
pattern:

View file

@ -5,12 +5,13 @@ metadata :
spec:
rules:
- name: "copyCM"
resource :
kinds :
- Namespace
selector:
matchLabels:
LabelForSelector : "namespace2"
match:
resources:
kinds :
- Namespace
selector:
matchLabels:
LabelForSelector : "namespace2"
generate :
kind: ConfigMap
name : copied-cm

View file

@ -10,12 +10,13 @@ metadata :
spec :
rules:
- name: "patchNamespace2"
resource :
kinds :
- Namespace
selector:
matchLabels:
LabelForSelector : "namespace2"
match:
resources:
kinds :
- Namespace
selector:
matchLabels:
LabelForSelector : "namespace2"
mutate:
patches:
- path: "/metadata/labels/isMutatedByPolicy"
@ -23,12 +24,13 @@ spec :
value: "true"
- name: "copyCM"
resource :
kinds :
- Namespace
selector:
matchLabels:
LabelForSelector : "namespace2"
match:
resources:
kinds :
- Namespace
selector:
matchLabels:
LabelForSelector : "namespace2"
generate :
kind: ConfigMap
name : copied-cm
@ -37,12 +39,13 @@ spec :
name : game-config
- name: "generateCM"
resource :
kinds :
- Namespace
selector:
matchLabels:
LabelForSelector : "namespace2"
match:
resources:
kinds :
- Namespace
selector:
matchLabels:
LabelForSelector : "namespace2"
generate :
kind: ConfigMap
name : generated-cm
@ -56,10 +59,11 @@ spec :
rsa.public.key=42
- name: "generateSecret"
resource :
kinds :
- Namespace
name: ns2
match:
resources:
kinds :
- Namespace
name: ns2
generate :
kind: Secret
name : generated-secrets
@ -73,10 +77,11 @@ spec :
foo2=bar2
- name: "copySecret"
resource :
kinds :
- Namespace
name: ns2
match:
resources:
kinds :
- Namespace
name: ns2
generate :
kind: Secret
name : copied-secrets

View file

@ -5,10 +5,11 @@ metadata:
spec:
rules:
- name: pCJ
resource:
kinds :
- CronJob
name: "?ell*"
match:
resources:
kinds :
- CronJob
name: "?ell*"
mutate:
patches:
- path: "/metadata/labels/isMutated"

View file

@ -5,10 +5,11 @@ metadata:
spec:
rules:
- name: "Patch and Volume validation"
resource:
kinds:
- DaemonSet
name: fluentd-elasticsearch
match:
resources:
kinds:
- DaemonSet
name: fluentd-elasticsearch
mutate:
patches:
- path: "/metadata/labels/isMutated"

View file

@ -5,9 +5,10 @@ metadata :
spec :
rules:
- name: "First policy v2"
resource:
kinds :
- Deployment
match:
resources:
kinds :
- Deployment
mutate:
patches:
- path: /metadata/labels/isMutated
@ -16,7 +17,6 @@ spec :
- path: /metadata/labels/app
op: replace
value: "nginx_is_mutated"
validate:
message: "Because I like only mutated resources"
pattern:

View file

@ -5,12 +5,13 @@ metadata :
spec :
rules:
- name: pEP
resource:
kinds :
- Endpoints
selector:
matchLabels:
label : test
match:
resources:
kinds :
- Endpoints
selector:
matchLabels:
label : test
mutate:
patches:
- path : "/subsets/0/ports/0/port"

View file

@ -5,12 +5,13 @@ metadata:
spec :
rules:
- name: hpa1
resource:
kinds :
- HorizontalPodAutoscaler
selector:
matchLabels:
originalLabel: isHere
match:
resources:
kinds :
- HorizontalPodAutoscaler
selector:
matchLabels:
originalLabel: isHere
mutate:
patches:
- path: "/metadata/labels/isMutated"

View file

@ -5,12 +5,13 @@ metadata :
spec :
rules:
- name: ingress1
resource:
kinds :
- Ingress
selector:
matchLabels:
originalLabel: isHere
match:
resources:
kinds :
- Ingress
selector:
matchLabels:
originalLabel: isHere
mutate:
patches:
- path: "/metadata/labels/isMutated"

View file

@ -5,10 +5,11 @@ metadata:
spec :
rules:
- name: job2
resource:
kinds:
- Job
name: pi
match:
resources:
kinds:
- Job
name: pi
mutate:
overlay:
spec:
@ -20,10 +21,11 @@ spec :
- containerPort: 80
protocol: TCP
- name: job1
resource:
kinds:
- Job
name: pi
match:
resources:
kinds:
- Job
name: pi
mutate:
overlay:
metadata:

View file

@ -5,12 +5,13 @@ metadata :
spec :
rules:
- name: "rule"
resource:
kinds :
- LimitRange
selector:
matchLabels:
containerSize: minimal
match:
resources:
kinds :
- LimitRange
selector:
matchLabels:
containerSize: minimal
mutate:
patches:
- path : "/spec/limits/0/default/memory"

View file

@ -6,12 +6,13 @@ metadata :
spec :
rules:
- name: ns1
resource:
kinds :
- Namespace
selector:
matchLabels:
LabelForSelector : "namespace"
match:
resources:
kinds :
- Namespace
selector:
matchLabels:
LabelForSelector : "namespace"
mutate:
patches:
- path: "/metadata/labels/replaced"

View file

@ -5,12 +5,13 @@ metadata:
spec:
rules:
- name: np1
resource:
kinds :
- NetworkPolicy
selector:
matchLabels:
originalLabel: isHere
match:
resources:
kinds :
- NetworkPolicy
selector:
matchLabels:
originalLabel: isHere
mutate:
patches:
- path: "/metadata/labels/isMutated"

View file

@ -5,11 +5,12 @@ metadata:
spec:
rules:
- name: pvc1
resource:
kinds :
- PersistentVolumeClaim
matchLabels:
originalLabel: isHere
match:
resources:
kinds :
- PersistentVolumeClaim
matchLabels:
originalLabel: isHere
mutate:
patches:
- path: "/metadata/labels/originalLabel"

View file

@ -5,10 +5,11 @@ metadata:
spec:
rules:
- name: pdb1
resource:
kinds :
- PodDisruptionBudget
name: "game-pdb"
match:
resources:
kinds :
- PodDisruptionBudget
name: "game-pdb"
mutate:
patches:
- path: "/metadata/labels/isMutated"

View file

@ -5,12 +5,13 @@ metadata:
spec:
rules:
- name: podtemplate1
resource:
kinds :
- PodTemplate
selector:
matchLabels:
originalLabel: isHere
match:
resources:
kinds :
- PodTemplate
selector:
matchLabels:
originalLabel: isHere
mutate:
overlay:
template:

View file

@ -5,12 +5,13 @@ metadata :
spec :
rules:
- name: "rule1"
resource:
kinds :
- ResourceQuota
selector:
matchLabels:
quota: low
match:
resources:
kinds :
- ResourceQuota
selector:
matchLabels:
quota: low
validate:
message: "This RQ requests too many RAM"
pattern:
@ -18,12 +19,13 @@ spec :
hard:
memory: "8Gi|12Gi"
- name: "rule2"
resource:
kinds :
- ResourceQuota
selector:
matchLabels:
quota: low
match:
resources:
kinds :
- ResourceQuota
selector:
matchLabels:
quota: low
validate:
message: "This RQ requests too many CPUs"
pattern:
@ -31,12 +33,13 @@ spec :
hard:
cpu: <3
- name: "rule3"
resource:
kinds :
- ResourceQuota
selector:
matchLabels:
quota: low
match:
resources:
kinds :
- ResourceQuota
selector:
matchLabels:
quota: low
validate:
message: "This RQ requests too many PODs"
pattern:

View file

@ -5,12 +5,13 @@ metadata :
spec :
rules:
- name: "rule"
resource:
kinds :
- ResourceQuota
selector:
matchLabels:
quota: low
match:
resources:
kinds :
- ResourceQuota
selector:
matchLabels:
quota: low
mutate:
patches:
- path : "/spec/scopeSelector/matchExpressions/1"

View file

@ -5,10 +5,11 @@ metadata:
spec:
rules:
- name: secret1
resource:
kinds :
- Secret
name: "mysecret"
match:
resources:
kinds :
- Secret
name: "mysecret"
mutate:
patches:
- path: "/metadata/labels/isMutated"

View file

@ -5,12 +5,13 @@ metadata:
spec:
rules:
- name: set-userID
resource:
kinds:
- Deployment
selector :
matchLabels:
app.type: prod
match:
resources:
kinds:
- Deployment
selector :
matchLabels:
app.type: prod
mutate:
overlay:
spec:

View file

@ -5,10 +5,11 @@ metadata :
spec :
rules:
- name: ps1
resource:
kinds:
- Service
name: "game-service*"
match:
resources:
kinds:
- Service
name: "game-service*"
mutate:
patches:
- path: "/metadata/labels/isMutated"

View file

@ -5,12 +5,13 @@ metadata:
spec:
rules:
- name: statefulset1
resource:
kinds :
- StatefulSet
selector:
matchLabels:
originalLabel: isHere
match:
resources:
kinds :
- StatefulSet
selector:
matchLabels:
originalLabel: isHere
mutate:
patches:
- path: "/spec/template/metadata/labels/isMutated"

View file

@ -5,12 +5,13 @@ metadata :
spec :
rules:
- name: add-label
resource:
kinds :
- Deployment
selector :
matchLabels :
cli: test
match:
resources:
kinds :
- Deployment
selector :
matchLabels :
cli: test
mutate:
patches:
- path: /metadata/labels/isMutated
@ -25,36 +26,39 @@ spec :
- (image): "*nginx*"
imagePullPolicy: "Always"
- name: add-label2
resource:
kinds :
- Deployment
selector :
matchLabels :
cli: test
match:
resources:
kinds :
- Deployment
selector :
matchLabels :
cli: test
mutate:
patches:
- path: /metadata/labels/app1
op: replace
value: "nginx_is_mutated"
- name: add-label3
resource:
kinds :
- Deployment
selector :
matchLabels :
cli: test
match:
resources:
kinds :
- Deployment
selector :
matchLabels :
cli: test
mutate:
patches:
- path: /metadata/labels/app2
op: add
value: "nginx_is_mutated2"
- name: check-image
resource:
kinds :
- Deployment
selector :
matchLabels :
cli: test
match:
resources:
kinds :
- Deployment
selector :
matchLabels :
cli: test
validate:
message: "The imagePullPolicy must be Always when using image nginx"
pattern:
@ -65,10 +69,11 @@ spec :
- (image): "*nginx*"
imagePullPolicy: "Always"
- name: check-registries
resource:
kinds:
- Deployment
- StatefulSet
match:
resources:
kinds:
- Deployment
- StatefulSet
validate:
message: "Registry is not allowed"
pattern: