diff --git a/charts/kyverno/crds/crds.yaml b/charts/kyverno/crds/crds.yaml index d67b93c8a5..d9733d9c12 100644 --- a/charts/kyverno/crds/crds.yaml +++ b/charts/kyverno/crds/crds.yaml @@ -411,6 +411,269 @@ spec: --- apiVersion: apiextensions.k8s.io/v1beta1 kind: CustomResourceDefinition +metadata: + name: policies.kyverno.io +spec: + group: kyverno.io + names: + kind: Policy + plural: policies + shortNames: + - pol + singular: policy + scope: Namespaced + subresources: + status: {} + validation: + openAPIV3Schema: + properties: + spec: + properties: + background: + type: boolean + rules: + items: + properties: + exclude: + properties: + clusterRoles: + items: + type: string + type: array + resources: + properties: + kinds: + items: + type: string + type: array + name: + type: string + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + roles: + items: + type: string + type: array + subjects: + items: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + namespace: + type: string + required: + - kind + - name + type: object + type: array + type: object + generate: + properties: + apiVersion: + type: string + clone: + properties: + name: + type: string + namespace: + type: string + required: + - namespace + - name + type: object + data: + AnyValue: {} + kind: + type: string + name: + type: string + namespace: + type: string + synchronize: + type: boolean + required: + - kind + - name + type: object + match: + properties: + clusterRoles: + items: + type: string + type: array + resources: + minProperties: 1 + properties: + kinds: + items: + type: string + type: array + name: + type: string + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + roles: + items: + type: string + type: array + subjects: + items: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + namespace: + type: string + required: + - kind + - name + type: object + type: array + required: + - resources + type: object + mutate: + properties: + overlay: + AnyValue: {} + patchStrategicMerge: + AnyValue: {} + patches: + items: + properties: + op: + enum: + - add + - replace + - remove + type: string + path: + type: string + value: + AnyValue: {} + required: + - path + - op + type: object + type: array + patchesJson6902: + type: string + type: object + name: + type: string + preconditions: + items: + required: + - key + - operator + - value + type: object + type: array + validate: + properties: + anyPattern: + AnyValue: {} + deny: + properties: + conditions: + items: + properties: + key: + type: string + operator: + enum: + - Equal + - Equals + - NotEqual + - NotEquals + - In + - NotIn + type: string + value: + anyOf: + - type: string + - items: {} + type: array + required: + - key + - operator + - value + type: object + type: array + message: + type: string + pattern: + AnyValue: {} + type: object + required: + - name + - match + type: object + type: array + validationFailureAction: + enum: + - enforce + - audit + type: string + required: + - rules + status: {} + versions: + - name: v1 + served: true + storage: true +--- +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition metadata: name: policyviolations.kyverno.io spec: diff --git a/cmd/kyverno/main.go b/cmd/kyverno/main.go index e452884261..d8b87f11dc 100644 --- a/cmd/kyverno/main.go +++ b/cmd/kyverno/main.go @@ -171,7 +171,8 @@ func main() { // Policy Status Handler - deals with all logic related to policy status statusSync := policystatus.NewSync( pclient, - pInformer.Kyverno().V1().ClusterPolicies().Lister()) + pInformer.Kyverno().V1().ClusterPolicies().Lister(), + pInformer.Kyverno().V1().Policies().Lister()) // POLICY VIOLATION GENERATOR // -- generate policy violation @@ -190,6 +191,7 @@ func main() { policyCtrl, err := policy.NewPolicyController(pclient, client, pInformer.Kyverno().V1().ClusterPolicies(), + pInformer.Kyverno().V1().Policies(), pInformer.Kyverno().V1().ClusterPolicyViolations(), pInformer.Kyverno().V1().PolicyViolations(), configData, @@ -235,6 +237,7 @@ func main() { pCacheController := policycache.NewPolicyCacheController( pInformer.Kyverno().V1().ClusterPolicies(), + pInformer.Kyverno().V1().Policies(), log.Log.WithName("PolicyCacheController"), ) diff --git a/definitions/crds/crds.yaml b/definitions/crds/crds.yaml index c5d4406ad0..425862bc05 100644 --- a/definitions/crds/crds.yaml +++ b/definitions/crds/crds.yaml @@ -274,6 +274,270 @@ spec: --- apiVersion: apiextensions.k8s.io/v1beta1 kind: CustomResourceDefinition +metadata: + name: policies.kyverno.io +spec: + group: kyverno.io + versions: + - name: v1 + served: true + storage: true + scope: Namespaced + names: + kind: Policy + plural: policies + singular: policy + shortNames: + - pol + subresources: + status: {} + validation: + openAPIV3Schema: + properties: + status: {} + spec: + required: + - rules + properties: + # default values to be handled by user + validationFailureAction: + type: string + enum: + - enforce # blocks the resorce api-reques if a rule fails. + - audit # allows resource creation and reports the failed validation rules as violations. Default + background: + type: boolean + rules: + type: array + items: + type: object + required: + - name + - match + properties: + name: + type: string + match: + type: object + required: + - resources + properties: + roles: + type: array + items: + type: string + clusterRoles: + type: array + items: + type: string + subjects: + type: array + items: + type: object + required: + - kind + - name + properties: + kind: + type: string + apiGroup: + type: string + name: + type: string + namespace: + type: string + resources: + type: object + minProperties: 1 + properties: + kinds: + type: array + items: + type: string + name: + type: string + selector: + properties: + matchLabels: + type: object + additionalProperties: + type: string + 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 + properties: + roles: + type: array + items: + type: string + clusterRoles: + type: array + items: + type: string + subjects: + type: array + items: + type: object + required: + - kind + - name + properties: + kind: + type: string + apiGroup: + type: string + name: + type: string + namespace: + type: string + resources: + type: object + properties: + kinds: + type: array + items: + type: string + name: + type: string + selector: + properties: + matchLabels: + type: object + additionalProperties: + type: string + matchExpressions: + type: array + items: + type: object + required: + - key + - operator + properties: + key: + type: string + operator: + type: string + values: + type: array + items: + type: string + preconditions: + type: array + items: + type: object + required: + - key # can be of any type + - operator # typed + - value # can be of any type + mutate: + type: object + properties: + overlay: + AnyValue: {} + patchStrategicMerge: + AnyValue: {} + patchesJson6902: + type: string + patches: + type: array + items: + type: object + required: + - path + - op + properties: + path: + type: string + op: + type: string + enum: + - add + - replace + - remove + value: + AnyValue: {} + validate: + type: object + properties: + message: + type: string + pattern: + AnyValue: {} + anyPattern: + AnyValue: {} + deny: + properties: + conditions: + type: array + items: + type: object + required: + - key # can be of any type + - operator # typed + - value # can be of any type + properties: + operator: + type: string + enum: + - Equal + - Equals + - NotEqual + - NotEquals + - In + - NotIn + key: + type: string + value: + anyOf: + - type: string + - type: array + items: {} + generate: + type: object + required: + - kind + - name + properties: + apiVersion: + type: string + kind: + type: string + name: + type: string + namespace: + type: string + synchronize: + type: boolean + clone: + type: object + required: + - namespace + - name + properties: + namespace: + type: string + name: + type: string + data: + AnyValue: {} +--- +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition metadata: name: clusterpolicyviolations.kyverno.io spec: diff --git a/definitions/install.yaml b/definitions/install.yaml index 11a25a7288..1f3d8b7c2f 100644 --- a/definitions/install.yaml +++ b/definitions/install.yaml @@ -416,6 +416,269 @@ spec: --- apiVersion: apiextensions.k8s.io/v1beta1 kind: CustomResourceDefinition +metadata: + name: policies.kyverno.io +spec: + group: kyverno.io + names: + kind: Policy + plural: policies + shortNames: + - pol + singular: policy + scope: Namespaced + subresources: + status: {} + validation: + openAPIV3Schema: + properties: + spec: + properties: + background: + type: boolean + rules: + items: + properties: + exclude: + properties: + clusterRoles: + items: + type: string + type: array + resources: + properties: + kinds: + items: + type: string + type: array + name: + type: string + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + roles: + items: + type: string + type: array + subjects: + items: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + namespace: + type: string + required: + - kind + - name + type: object + type: array + type: object + generate: + properties: + apiVersion: + type: string + clone: + properties: + name: + type: string + namespace: + type: string + required: + - namespace + - name + type: object + data: + AnyValue: {} + kind: + type: string + name: + type: string + namespace: + type: string + synchronize: + type: boolean + required: + - kind + - name + type: object + match: + properties: + clusterRoles: + items: + type: string + type: array + resources: + minProperties: 1 + properties: + kinds: + items: + type: string + type: array + name: + type: string + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + roles: + items: + type: string + type: array + subjects: + items: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + namespace: + type: string + required: + - kind + - name + type: object + type: array + required: + - resources + type: object + mutate: + properties: + overlay: + AnyValue: {} + patchStrategicMerge: + AnyValue: {} + patches: + items: + properties: + op: + enum: + - add + - replace + - remove + type: string + path: + type: string + value: + AnyValue: {} + required: + - path + - op + type: object + type: array + patchesJson6902: + type: string + type: object + name: + type: string + preconditions: + items: + required: + - key + - operator + - value + type: object + type: array + validate: + properties: + anyPattern: + AnyValue: {} + deny: + properties: + conditions: + items: + properties: + key: + type: string + operator: + enum: + - Equal + - Equals + - NotEqual + - NotEquals + - In + - NotIn + type: string + value: + anyOf: + - type: string + - items: {} + type: array + required: + - key + - operator + - value + type: object + type: array + message: + type: string + pattern: + AnyValue: {} + type: object + required: + - name + - match + type: object + type: array + validationFailureAction: + enum: + - enforce + - audit + type: string + required: + - rules + status: {} + versions: + - name: v1 + served: true + storage: true +--- +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition metadata: name: policyviolations.kyverno.io spec: @@ -650,17 +913,51 @@ rules: - list - watch --- + apiVersion: rbac.authorization.k8s.io/v1beta1 kind: ClusterRole metadata: labels: - rbac.authorization.k8s.io/aggregate-to-view: "true" - name: kyverno:view-policyviolations + rbac.authorization.k8s.io/aggregate-to-admin: "true" + name: kyverno:admin-policies +rules: +- apiGroups: + - kyverno.io + resources: + - policies + verbs: + - "*" +--- +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: ClusterRole +metadata: + labels: + rbac.authorization.k8s.io/aggregate-to-edit: "true" + name: kyverno:edit-policies-policyviolations rules: - apiGroups: - kyverno.io resources: - policyviolations + - policies + verbs: + - get + - list + - watch +--- + +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: ClusterRole +metadata: + labels: + rbac.authorization.k8s.io/aggregate-to-view: "true" + name: kyverno:view-policies-policyviolations +rules: +- apiGroups: + - kyverno.io + resources: + - policyviolations + - policies verbs: - get - list diff --git a/definitions/install_debug.yaml b/definitions/install_debug.yaml index 6271438ec2..f6ae4e384b 100644 --- a/definitions/install_debug.yaml +++ b/definitions/install_debug.yaml @@ -416,6 +416,269 @@ spec: --- apiVersion: apiextensions.k8s.io/v1beta1 kind: CustomResourceDefinition +metadata: + name: policies.kyverno.io +spec: + group: kyverno.io + names: + kind: Policy + plural: policies + shortNames: + - pol + singular: policy + scope: Namespaced + subresources: + status: {} + validation: + openAPIV3Schema: + properties: + spec: + properties: + background: + type: boolean + rules: + items: + properties: + exclude: + properties: + clusterRoles: + items: + type: string + type: array + resources: + properties: + kinds: + items: + type: string + type: array + name: + type: string + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + roles: + items: + type: string + type: array + subjects: + items: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + namespace: + type: string + required: + - kind + - name + type: object + type: array + type: object + generate: + properties: + apiVersion: + type: string + clone: + properties: + name: + type: string + namespace: + type: string + required: + - namespace + - name + type: object + data: + AnyValue: {} + kind: + type: string + name: + type: string + namespace: + type: string + synchronize: + type: boolean + required: + - kind + - name + type: object + match: + properties: + clusterRoles: + items: + type: string + type: array + resources: + minProperties: 1 + properties: + kinds: + items: + type: string + type: array + name: + type: string + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + roles: + items: + type: string + type: array + subjects: + items: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + namespace: + type: string + required: + - kind + - name + type: object + type: array + required: + - resources + type: object + mutate: + properties: + overlay: + AnyValue: {} + patchStrategicMerge: + AnyValue: {} + patches: + items: + properties: + op: + enum: + - add + - replace + - remove + type: string + path: + type: string + value: + AnyValue: {} + required: + - path + - op + type: object + type: array + patchesJson6902: + type: string + type: object + name: + type: string + preconditions: + items: + required: + - key + - operator + - value + type: object + type: array + validate: + properties: + anyPattern: + AnyValue: {} + deny: + properties: + conditions: + items: + properties: + key: + type: string + operator: + enum: + - Equal + - Equals + - NotEqual + - NotEquals + - In + - NotIn + type: string + value: + anyOf: + - type: string + - items: {} + type: array + required: + - key + - operator + - value + type: object + type: array + message: + type: string + pattern: + AnyValue: {} + type: object + required: + - name + - match + type: object + type: array + validationFailureAction: + enum: + - enforce + - audit + type: string + required: + - rules + status: {} + versions: + - name: v1 + served: true + storage: true +--- +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition metadata: name: policyviolations.kyverno.io spec: diff --git a/go.sum b/go.sum index eaf422e247..5dd86690dc 100644 --- a/go.sum +++ b/go.sum @@ -497,6 +497,7 @@ github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm4 github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= @@ -747,6 +748,8 @@ github.com/ncw/directio v1.0.5/go.mod h1:rX/pKEYkOXBGOggmcyJeJGloCkleSvphPx2eV3t github.com/nicksnyder/go-i18n v1.10.0/go.mod h1:HrK7VCrbOvQoUAQ7Vpy7i87N7JZZZ7R2xBGjv0j365Q= github.com/nirmata/client-go v0.17.5-0.20200625181911-7e81180b291e h1:EZ4Yi82Z8uK7OgebBoAQvQaEYoQy0OV4KdYFXoXdDgU= github.com/nirmata/client-go v0.17.5-0.20200625181911-7e81180b291e/go.mod h1:ouF6o5pz3is8qU0/qYL2RnoxOPqgfuidYLowytyLJmc= +github.com/nirmata/client-go v1.5.1 h1:yBfS5sTI18MbRYagdc0Fl8qIl/F9dlYt73WNbEUDfA4= +github.com/nirmata/client-go v11.0.0+incompatible h1:BMHYEFCsTSicG2qImOMox+ophXWdNNcq250eCO2ZDio= github.com/nsqio/go-nsq v1.0.7/go.mod h1:XP5zaUs3pqf+Q71EqUJs3HYfBIqfK6G83WQMdNN+Ito= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= @@ -756,6 +759,7 @@ github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.11.0 h1:JAKSXpt1YjtLA7YpPiqO9ss6sNXEsPfSGdwN0UHqzrw= github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v0.0.0-20190113212917-5533ce8a0da3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= @@ -763,6 +767,7 @@ github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5 github.com/onsi/gomega v1.4.2/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.8.1 h1:C5Dqfs/LeauYDX0jJXIe2SWmwCbGzx9yF8C8xy3Lh34= github.com/onsi/gomega v1.8.1/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= @@ -796,6 +801,7 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= @@ -803,19 +809,23 @@ github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXP github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM= github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= +github.com/prometheus/client_golang v1.0.0 h1:vrDKnkGzuGvhNAL56c7DBz29ZL+KxnoR0x7enabFceM= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 h1:S/YWwWx/RA8rT8tKFRuGUZhuA90OyIBpPCXkcbwU8DE= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.4.1 h1:K0MGApIoQvMw27RTdJkPbr3JZ7DNbtxQNyi5STVM6Kw= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.2 h1:6LJUbpNm42llc4HRCuvApCSWB/WfhuNo9K98Q9sNGfs= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/qri-io/starlib v0.4.2-0.20200213133954-ff2e8cd5ef8d/go.mod h1:7DPO4domFU579Ga6E61sB9VFNaniPVwJP5C4bBCu3wA= @@ -905,6 +915,7 @@ github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRci github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.3-0.20181224173747-660f15d67dbb/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/subosito/gotenv v1.1.1/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/tevino/abool v0.0.0-20170917061928-9b9efcf221b5 h1:hNna6Fi0eP1f2sMBe/rJicDmaHmoXGe1Ta84FPYHLuE= @@ -936,6 +947,7 @@ github.com/uudashr/gocognit v0.0.0-20190926065955-1655d0de0517/go.mod h1:j44Ayx2 github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasthttp v1.2.0/go.mod h1:4vX61m6KN+xDduDNwXrhIAVZaZaZiQ1luJk8LWSxF3s= github.com/valyala/quicktemplate v1.2.0/go.mod h1:EH+4AkTd43SvgIbQHYu59/cJyxDoOVRUAfrukLPuGJ4= +github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a h1:0R4NLDRDZX6JcmhJgXi5E4b8Wg84ihbmUKp/GvSPEzc= github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio= github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw= github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I= @@ -1174,7 +1186,9 @@ golang.org/x/tools v0.0.0-20190930201159-7c411dea38b0/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20191010075000-0337d82405ff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200226224502-204d844ad48d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898 h1:/atklqdjdhuosWIl6AIbOeHJjicWYPqR9bpxqxYG2pA= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gomodules.xyz/jsonpatch/v2 v2.0.1 h1:xyiBuvkD2g5n7cYzx6u2sxQvsAy4QJsZFCzGVdzOXZ0= gomodules.xyz/jsonpatch/v2 v2.0.1/go.mod h1:IhYNNY4jnS53ZnfE4PAmpKtDpTCj1JFXc+3mwe7XcUU= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.5.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= @@ -1217,6 +1231,7 @@ gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/cheggaaa/pb.v1 v1.0.28/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df/go.mod h1:LRQQ+SO6ZHR7tOkpBDuZnXENFzX8qRjMDMyPD6BRkCw= @@ -1239,6 +1254,7 @@ gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/square/go-jose.v2 v2.1.9/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/urfave/cli.v1 v1.20.0/go.mod h1:vuBzUtMdQeixQj8LVd+/98pzhxNGQoyuPBlsXHOQNO0= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= @@ -1250,6 +1266,7 @@ gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20191120175047-4206685974f2 h1:XZx7nhd5GMaZpmDaEHFVafUZC7ya0fuo7cSJ3UCKYmM= gopkg.in/yaml.v3 v3.0.0-20191120175047-4206685974f2/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -1268,6 +1285,7 @@ k8s.io/apimachinery v0.17.0/go.mod h1:b9qmWdKlLuU9EBh+06BtLcSf/Mu89rWL33naRxs1uZ k8s.io/apimachinery v0.17.2/go.mod h1:b9qmWdKlLuU9EBh+06BtLcSf/Mu89rWL33naRxs1uZg= k8s.io/apimachinery v0.17.4 h1:UzM+38cPUJnzqSQ+E1PY4YxMHIzQyCg29LOoGfo79Zw= k8s.io/apimachinery v0.17.4/go.mod h1:gxLnyZcGNdZTCLnq3fgzyg2A5BVCHTNDFrw8AmuJ+0g= +k8s.io/apimachinery v0.18.6 h1:RtFHnfGNfd1N0LeSrKCUznz5xtUP1elRGvHJbL3Ntag= k8s.io/apiserver v0.17.2/go.mod h1:lBmw/TtQdtxvrTk0e2cgtOxHizXI+d0mmGQURIHQZlo= k8s.io/apiserver v0.17.4 h1:bYc9LvDPEF9xAL3fhbDzqNOQOAnNF2ZYCrMW8v52/mE= k8s.io/apiserver v0.17.4/go.mod h1:5ZDQ6Xr5MNBxyi3iUZXS84QOhZl+W7Oq2us/29c0j9I= diff --git a/pkg/api/kyverno/v1/register.go b/pkg/api/kyverno/v1/register.go index fa275a59e5..9a9323c772 100644 --- a/pkg/api/kyverno/v1/register.go +++ b/pkg/api/kyverno/v1/register.go @@ -40,6 +40,8 @@ func addKnownTypes(scheme *runtime.Scheme) error { &PolicyViolationList{}, &GenerateRequest{}, &GenerateRequestList{}, + &Policy{}, + &PolicyList{}, ) metav1.AddToGroupVersion(scheme, SchemeGroupVersion) return nil diff --git a/pkg/api/kyverno/v1/types.go b/pkg/api/kyverno/v1/types.go index f331d6a5d7..39c3656cca 100644 --- a/pkg/api/kyverno/v1/types.go +++ b/pkg/api/kyverno/v1/types.go @@ -93,6 +93,14 @@ type ClusterPolicyList struct { Items []ClusterPolicy `json:"items" yaml:"items"` } +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +// PolicyList ... +type PolicyList struct { + metav1.TypeMeta `json:",inline" yaml:",inline"` + metav1.ListMeta `json:"metadata" yaml:"metadata"` + Items []Policy `json:"items" yaml:"items"` +} + // +genclient // +genclient:nonNamespaced // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object @@ -124,6 +132,8 @@ type PolicyViolationList struct { Items []PolicyViolation `json:"items" yaml:"items"` } +// +genclient +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object // Policy contains rules to be applied to created resources type Policy struct { metav1.TypeMeta `json:",inline,omitempty" yaml:",inline,omitempty"` diff --git a/pkg/api/kyverno/v1/zz_generated.deepcopy.go b/pkg/api/kyverno/v1/zz_generated.deepcopy.go index ff1a7c63e4..ddcde51e34 100644 --- a/pkg/api/kyverno/v1/zz_generated.deepcopy.go +++ b/pkg/api/kyverno/v1/zz_generated.deepcopy.go @@ -174,6 +174,29 @@ func (in *Condition) DeepCopy() *Condition { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Deny) DeepCopyInto(out *Deny) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Deny. +func (in *Deny) DeepCopy() *Deny { + if in == nil { + return nil + } + out := new(Deny) + in.DeepCopyInto(out) + 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 @@ -367,6 +390,47 @@ func (in *Policy) DeepCopy() *Policy { return out } +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *Policy) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PolicyList) DeepCopyInto(out *PolicyList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]Policy, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PolicyList. +func (in *PolicyList) DeepCopy() *PolicyList { + if in == nil { + return nil + } + out := new(PolicyList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *PolicyList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *PolicyStatus) DeepCopyInto(out *PolicyStatus) { *out = *in diff --git a/pkg/client/clientset/versioned/fake/register.go b/pkg/client/clientset/versioned/fake/register.go index 482e66ffc7..4a90cf5ba8 100644 --- a/pkg/client/clientset/versioned/fake/register.go +++ b/pkg/client/clientset/versioned/fake/register.go @@ -29,8 +29,7 @@ import ( var scheme = runtime.NewScheme() var codecs = serializer.NewCodecFactory(scheme) - -// var parameterCodec = runtime.NewParameterCodec(scheme) +var parameterCodec = runtime.NewParameterCodec(scheme) var localSchemeBuilder = runtime.SchemeBuilder{ kyvernov1.AddToScheme, } diff --git a/pkg/client/clientset/versioned/typed/kyverno/v1/fake/fake_kyverno_client.go b/pkg/client/clientset/versioned/typed/kyverno/v1/fake/fake_kyverno_client.go index b8114f5779..044fcd7200 100644 --- a/pkg/client/clientset/versioned/typed/kyverno/v1/fake/fake_kyverno_client.go +++ b/pkg/client/clientset/versioned/typed/kyverno/v1/fake/fake_kyverno_client.go @@ -40,6 +40,10 @@ func (c *FakeKyvernoV1) GenerateRequests(namespace string) v1.GenerateRequestInt return &FakeGenerateRequests{c, namespace} } +func (c *FakeKyvernoV1) Policies(namespace string) v1.PolicyInterface { + return &FakePolicies{c, namespace} +} + func (c *FakeKyvernoV1) PolicyViolations(namespace string) v1.PolicyViolationInterface { return &FakePolicyViolations{c, namespace} } diff --git a/pkg/client/clientset/versioned/typed/kyverno/v1/fake/fake_policy.go b/pkg/client/clientset/versioned/typed/kyverno/v1/fake/fake_policy.go new file mode 100644 index 0000000000..8b939055cd --- /dev/null +++ b/pkg/client/clientset/versioned/typed/kyverno/v1/fake/fake_policy.go @@ -0,0 +1,140 @@ +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + kyvernov1 "github.com/nirmata/kyverno/pkg/api/kyverno/v1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + labels "k8s.io/apimachinery/pkg/labels" + schema "k8s.io/apimachinery/pkg/runtime/schema" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + testing "k8s.io/client-go/testing" +) + +// FakePolicies implements PolicyInterface +type FakePolicies struct { + Fake *FakeKyvernoV1 + ns string +} + +var policiesResource = schema.GroupVersionResource{Group: "kyverno.io", Version: "v1", Resource: "policies"} + +var policiesKind = schema.GroupVersionKind{Group: "kyverno.io", Version: "v1", Kind: "Policy"} + +// Get takes name of the policy, and returns the corresponding policy object, and an error if there is any. +func (c *FakePolicies) Get(name string, options v1.GetOptions) (result *kyvernov1.Policy, err error) { + obj, err := c.Fake. + Invokes(testing.NewGetAction(policiesResource, c.ns, name), &kyvernov1.Policy{}) + + if obj == nil { + return nil, err + } + return obj.(*kyvernov1.Policy), err +} + +// List takes label and field selectors, and returns the list of Policies that match those selectors. +func (c *FakePolicies) List(opts v1.ListOptions) (result *kyvernov1.PolicyList, err error) { + obj, err := c.Fake. + Invokes(testing.NewListAction(policiesResource, policiesKind, c.ns, opts), &kyvernov1.PolicyList{}) + + if obj == nil { + return nil, err + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + list := &kyvernov1.PolicyList{ListMeta: obj.(*kyvernov1.PolicyList).ListMeta} + for _, item := range obj.(*kyvernov1.PolicyList).Items { + if label.Matches(labels.Set(item.Labels)) { + list.Items = append(list.Items, item) + } + } + return list, err +} + +// Watch returns a watch.Interface that watches the requested policies. +func (c *FakePolicies) Watch(opts v1.ListOptions) (watch.Interface, error) { + return c.Fake. + InvokesWatch(testing.NewWatchAction(policiesResource, c.ns, opts)) + +} + +// Create takes the representation of a policy and creates it. Returns the server's representation of the policy, and an error, if there is any. +func (c *FakePolicies) Create(policy *kyvernov1.Policy) (result *kyvernov1.Policy, err error) { + obj, err := c.Fake. + Invokes(testing.NewCreateAction(policiesResource, c.ns, policy), &kyvernov1.Policy{}) + + if obj == nil { + return nil, err + } + return obj.(*kyvernov1.Policy), err +} + +// Update takes the representation of a policy and updates it. Returns the server's representation of the policy, and an error, if there is any. +func (c *FakePolicies) Update(policy *kyvernov1.Policy) (result *kyvernov1.Policy, err error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateAction(policiesResource, c.ns, policy), &kyvernov1.Policy{}) + + if obj == nil { + return nil, err + } + return obj.(*kyvernov1.Policy), err +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *FakePolicies) UpdateStatus(policy *kyvernov1.Policy) (*kyvernov1.Policy, error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateSubresourceAction(policiesResource, "status", c.ns, policy), &kyvernov1.Policy{}) + + if obj == nil { + return nil, err + } + return obj.(*kyvernov1.Policy), err +} + +// Delete takes name of the policy and deletes it. Returns an error if one occurs. +func (c *FakePolicies) Delete(name string, options *v1.DeleteOptions) error { + _, err := c.Fake. + Invokes(testing.NewDeleteAction(policiesResource, c.ns, name), &kyvernov1.Policy{}) + + return err +} + +// DeleteCollection deletes a collection of objects. +func (c *FakePolicies) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { + action := testing.NewDeleteCollectionAction(policiesResource, c.ns, listOptions) + + _, err := c.Fake.Invokes(action, &kyvernov1.PolicyList{}) + return err +} + +// Patch applies the patch and returns the patched policy. +func (c *FakePolicies) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *kyvernov1.Policy, err error) { + obj, err := c.Fake. + Invokes(testing.NewPatchSubresourceAction(policiesResource, c.ns, name, pt, data, subresources...), &kyvernov1.Policy{}) + + if obj == nil { + return nil, err + } + return obj.(*kyvernov1.Policy), err +} diff --git a/pkg/client/clientset/versioned/typed/kyverno/v1/generated_expansion.go b/pkg/client/clientset/versioned/typed/kyverno/v1/generated_expansion.go index 79d25f6f55..e5717f5ba4 100644 --- a/pkg/client/clientset/versioned/typed/kyverno/v1/generated_expansion.go +++ b/pkg/client/clientset/versioned/typed/kyverno/v1/generated_expansion.go @@ -24,4 +24,6 @@ type ClusterPolicyViolationExpansion interface{} type GenerateRequestExpansion interface{} +type PolicyExpansion interface{} + type PolicyViolationExpansion interface{} diff --git a/pkg/client/clientset/versioned/typed/kyverno/v1/kyverno_client.go b/pkg/client/clientset/versioned/typed/kyverno/v1/kyverno_client.go index 8c1b5a0ea8..62d63d7baa 100644 --- a/pkg/client/clientset/versioned/typed/kyverno/v1/kyverno_client.go +++ b/pkg/client/clientset/versioned/typed/kyverno/v1/kyverno_client.go @@ -29,6 +29,7 @@ type KyvernoV1Interface interface { ClusterPoliciesGetter ClusterPolicyViolationsGetter GenerateRequestsGetter + PoliciesGetter PolicyViolationsGetter } @@ -49,6 +50,10 @@ func (c *KyvernoV1Client) GenerateRequests(namespace string) GenerateRequestInte return newGenerateRequests(c, namespace) } +func (c *KyvernoV1Client) Policies(namespace string) PolicyInterface { + return newPolicies(c, namespace) +} + func (c *KyvernoV1Client) PolicyViolations(namespace string) PolicyViolationInterface { return newPolicyViolations(c, namespace) } diff --git a/pkg/client/clientset/versioned/typed/kyverno/v1/policy.go b/pkg/client/clientset/versioned/typed/kyverno/v1/policy.go new file mode 100644 index 0000000000..6dc55b69f1 --- /dev/null +++ b/pkg/client/clientset/versioned/typed/kyverno/v1/policy.go @@ -0,0 +1,191 @@ +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1 + +import ( + "time" + + v1 "github.com/nirmata/kyverno/pkg/api/kyverno/v1" + scheme "github.com/nirmata/kyverno/pkg/client/clientset/versioned/scheme" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + rest "k8s.io/client-go/rest" +) + +// PoliciesGetter has a method to return a PolicyInterface. +// A group's client should implement this interface. +type PoliciesGetter interface { + Policies(namespace string) PolicyInterface +} + +// PolicyInterface has methods to work with Policy resources. +type PolicyInterface interface { + Create(*v1.Policy) (*v1.Policy, error) + Update(*v1.Policy) (*v1.Policy, error) + UpdateStatus(*v1.Policy) (*v1.Policy, error) + Delete(name string, options *metav1.DeleteOptions) error + DeleteCollection(options *metav1.DeleteOptions, listOptions metav1.ListOptions) error + Get(name string, options metav1.GetOptions) (*v1.Policy, error) + List(opts metav1.ListOptions) (*v1.PolicyList, error) + Watch(opts metav1.ListOptions) (watch.Interface, error) + Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1.Policy, err error) + PolicyExpansion +} + +// policies implements PolicyInterface +type policies struct { + client rest.Interface + ns string +} + +// newPolicies returns a Policies +func newPolicies(c *KyvernoV1Client, namespace string) *policies { + return &policies{ + client: c.RESTClient(), + ns: namespace, + } +} + +// Get takes name of the policy, and returns the corresponding policy object, and an error if there is any. +func (c *policies) Get(name string, options metav1.GetOptions) (result *v1.Policy, err error) { + result = &v1.Policy{} + err = c.client.Get(). + Namespace(c.ns). + Resource("policies"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of Policies that match those selectors. +func (c *policies) List(opts metav1.ListOptions) (result *v1.PolicyList, err error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &v1.PolicyList{} + err = c.client.Get(). + Namespace(c.ns). + Resource("policies"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested policies. +func (c *policies) Watch(opts metav1.ListOptions) (watch.Interface, error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + opts.Watch = true + return c.client.Get(). + Namespace(c.ns). + Resource("policies"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch() +} + +// Create takes the representation of a policy and creates it. Returns the server's representation of the policy, and an error, if there is any. +func (c *policies) Create(policy *v1.Policy) (result *v1.Policy, err error) { + result = &v1.Policy{} + err = c.client.Post(). + Namespace(c.ns). + Resource("policies"). + Body(policy). + Do(). + Into(result) + return +} + +// Update takes the representation of a policy and updates it. Returns the server's representation of the policy, and an error, if there is any. +func (c *policies) Update(policy *v1.Policy) (result *v1.Policy, err error) { + result = &v1.Policy{} + err = c.client.Put(). + Namespace(c.ns). + Resource("policies"). + Name(policy.Name). + Body(policy). + Do(). + Into(result) + return +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). + +func (c *policies) UpdateStatus(policy *v1.Policy) (result *v1.Policy, err error) { + result = &v1.Policy{} + err = c.client.Put(). + Namespace(c.ns). + Resource("policies"). + Name(policy.Name). + SubResource("status"). + Body(policy). + Do(). + Into(result) + return +} + +// Delete takes name of the policy and deletes it. Returns an error if one occurs. +func (c *policies) Delete(name string, options *metav1.DeleteOptions) error { + return c.client.Delete(). + Namespace(c.ns). + Resource("policies"). + Name(name). + Body(options). + Do(). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *policies) DeleteCollection(options *metav1.DeleteOptions, listOptions metav1.ListOptions) error { + var timeout time.Duration + if listOptions.TimeoutSeconds != nil { + timeout = time.Duration(*listOptions.TimeoutSeconds) * time.Second + } + return c.client.Delete(). + Namespace(c.ns). + Resource("policies"). + VersionedParams(&listOptions, scheme.ParameterCodec). + Timeout(timeout). + Body(options). + Do(). + Error() +} + +// Patch applies the patch and returns the patched policy. +func (c *policies) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1.Policy, err error) { + result = &v1.Policy{} + err = c.client.Patch(pt). + Namespace(c.ns). + Resource("policies"). + SubResource(subresources...). + Name(name). + Body(data). + Do(). + Into(result) + return +} diff --git a/pkg/client/informers/externalversions/generic.go b/pkg/client/informers/externalversions/generic.go index a9cd2d844b..5a5b303d02 100644 --- a/pkg/client/informers/externalversions/generic.go +++ b/pkg/client/informers/externalversions/generic.go @@ -59,6 +59,8 @@ func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource return &genericInformer{resource: resource.GroupResource(), informer: f.Kyverno().V1().ClusterPolicyViolations().Informer()}, nil case v1.SchemeGroupVersion.WithResource("generaterequests"): return &genericInformer{resource: resource.GroupResource(), informer: f.Kyverno().V1().GenerateRequests().Informer()}, nil + case v1.SchemeGroupVersion.WithResource("policies"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Kyverno().V1().Policies().Informer()}, nil case v1.SchemeGroupVersion.WithResource("policyviolations"): return &genericInformer{resource: resource.GroupResource(), informer: f.Kyverno().V1().PolicyViolations().Informer()}, nil diff --git a/pkg/client/informers/externalversions/kyverno/v1/interface.go b/pkg/client/informers/externalversions/kyverno/v1/interface.go index cbcc150a44..da449426a9 100644 --- a/pkg/client/informers/externalversions/kyverno/v1/interface.go +++ b/pkg/client/informers/externalversions/kyverno/v1/interface.go @@ -30,6 +30,8 @@ type Interface interface { ClusterPolicyViolations() ClusterPolicyViolationInformer // GenerateRequests returns a GenerateRequestInformer. GenerateRequests() GenerateRequestInformer + // Policies returns a PolicyInformer. + Policies() PolicyInformer // PolicyViolations returns a PolicyViolationInformer. PolicyViolations() PolicyViolationInformer } @@ -60,6 +62,11 @@ func (v *version) GenerateRequests() GenerateRequestInformer { return &generateRequestInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} } +// Policies returns a PolicyInformer. +func (v *version) Policies() PolicyInformer { + return &policyInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} +} + // PolicyViolations returns a PolicyViolationInformer. func (v *version) PolicyViolations() PolicyViolationInformer { return &policyViolationInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} diff --git a/pkg/client/informers/externalversions/kyverno/v1/policy.go b/pkg/client/informers/externalversions/kyverno/v1/policy.go new file mode 100644 index 0000000000..50aea4c1b4 --- /dev/null +++ b/pkg/client/informers/externalversions/kyverno/v1/policy.go @@ -0,0 +1,89 @@ +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by informer-gen. DO NOT EDIT. + +package v1 + +import ( + time "time" + + kyvernov1 "github.com/nirmata/kyverno/pkg/api/kyverno/v1" + versioned "github.com/nirmata/kyverno/pkg/client/clientset/versioned" + internalinterfaces "github.com/nirmata/kyverno/pkg/client/informers/externalversions/internalinterfaces" + v1 "github.com/nirmata/kyverno/pkg/client/listers/kyverno/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + watch "k8s.io/apimachinery/pkg/watch" + cache "k8s.io/client-go/tools/cache" +) + +// PolicyInformer provides access to a shared informer and lister for +// Policies. +type PolicyInformer interface { + Informer() cache.SharedIndexInformer + Lister() v1.PolicyLister +} + +type policyInformer struct { + factory internalinterfaces.SharedInformerFactory + tweakListOptions internalinterfaces.TweakListOptionsFunc + namespace string +} + +// NewPolicyInformer constructs a new informer for Policy type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewPolicyInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredPolicyInformer(client, namespace, resyncPeriod, indexers, nil) +} + +// NewFilteredPolicyInformer constructs a new informer for Policy type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewFilteredPolicyInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { + return cache.NewSharedIndexInformer( + &cache.ListWatch{ + ListFunc: func(options metav1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.KyvernoV1().Policies(namespace).List(options) + }, + WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.KyvernoV1().Policies(namespace).Watch(options) + }, + }, + &kyvernov1.Policy{}, + resyncPeriod, + indexers, + ) +} + +func (f *policyInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredPolicyInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +} + +func (f *policyInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&kyvernov1.Policy{}, f.defaultInformer) +} + +func (f *policyInformer) Lister() v1.PolicyLister { + return v1.NewPolicyLister(f.Informer().GetIndexer()) +} diff --git a/pkg/client/listers/kyverno/v1/expansion_generated.go b/pkg/client/listers/kyverno/v1/expansion_generated.go index 328ff5bd31..86eb7aeff0 100644 --- a/pkg/client/listers/kyverno/v1/expansion_generated.go +++ b/pkg/client/listers/kyverno/v1/expansion_generated.go @@ -190,3 +190,43 @@ func (s generateRequestNamespaceLister) GetGenerateRequestsForClusterPolicy(poli } return list, err } + +type PolicyListerExpansion interface { + GetPolicyForPolicyViolation(pv *kyvernov1.PolicyViolation) ([]*kyvernov1.Policy, error) +} + +func (p *policyLister) GetPolicyForPolicyViolation(pv *kyvernov1.PolicyViolation) ([]*kyvernov1.Policy, error) { + if len(pv.Labels) == 0 { + return nil, fmt.Errorf("no Policy found for PolicyViolation %v because it has no labels", pv.Name) + } + + pList, err := p.List(labels.Everything()) + if err != nil { + return nil, err + } + + var policies []*kyvernov1.Policy + for _, p := range pList { + policyLabelmap := map[string]string{"policy": p.Name} + + ls := &metav1.LabelSelector{} + err = metav1.Convert_Map_string_To_string_To_v1_LabelSelector(&policyLabelmap, ls, nil) + if err != nil { + return nil, fmt.Errorf("failed to generate label sector of Policy name %s: %v", p.Name, err) + } + selector, err := metav1.LabelSelectorAsSelector(ls) + if err != nil { + return nil, fmt.Errorf("invalid label selector: %v", err) + } + // If a policy with a nil or empty selector creeps in, it should match nothing, not everything. + if selector.Empty() || !selector.Matches(labels.Set(pv.Labels)) { + continue + } + if p.Namespace != pv.Namespace { + continue + } + policies = append(policies, p) + } + + return policies, err +} diff --git a/pkg/client/listers/kyverno/v1/policy.go b/pkg/client/listers/kyverno/v1/policy.go new file mode 100644 index 0000000000..cb10b84ab8 --- /dev/null +++ b/pkg/client/listers/kyverno/v1/policy.go @@ -0,0 +1,93 @@ +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by lister-gen. DO NOT EDIT. + +package v1 + +import ( + v1 "github.com/nirmata/kyverno/pkg/api/kyverno/v1" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/tools/cache" +) + +// PolicyLister helps list Policies. +type PolicyLister interface { + // List lists all Policies in the indexer. + List(selector labels.Selector) (ret []*v1.Policy, err error) + // Policies returns an object that can list and get Policies. + Policies(namespace string) PolicyNamespaceLister + PolicyListerExpansion +} + +// policyLister implements the PolicyLister interface. +type policyLister struct { + indexer cache.Indexer +} + +// NewPolicyLister returns a new PolicyLister. +func NewPolicyLister(indexer cache.Indexer) PolicyLister { + return &policyLister{indexer: indexer} +} + +// List lists all Policies in the indexer. +func (s *policyLister) List(selector labels.Selector) (ret []*v1.Policy, err error) { + err = cache.ListAll(s.indexer, selector, func(m interface{}) { + ret = append(ret, m.(*v1.Policy)) + }) + return ret, err +} + +// Policies returns an object that can list and get Policies. +func (s *policyLister) Policies(namespace string) PolicyNamespaceLister { + return policyNamespaceLister{indexer: s.indexer, namespace: namespace} +} + +// PolicyNamespaceLister helps list and get Policies. +type PolicyNamespaceLister interface { + // List lists all Policies in the indexer for a given namespace. + List(selector labels.Selector) (ret []*v1.Policy, err error) + // Get retrieves the Policy from the indexer for a given namespace and name. + Get(name string) (*v1.Policy, error) +} + +// policyNamespaceLister implements the PolicyNamespaceLister +// interface. +type policyNamespaceLister struct { + indexer cache.Indexer + namespace string +} + +// List lists all Policies in the indexer for a given namespace. +func (s policyNamespaceLister) List(selector labels.Selector) (ret []*v1.Policy, err error) { + err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { + ret = append(ret, m.(*v1.Policy)) + }) + return ret, err +} + +// Get retrieves the Policy from the indexer for a given namespace and name. +func (s policyNamespaceLister) Get(name string) (*v1.Policy, error) { + obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) + if err != nil { + return nil, err + } + if !exists { + return nil, errors.NewNotFound(v1.Resource("policy"), name) + } + return obj.(*v1.Policy), nil +} diff --git a/pkg/dclient/client.go b/pkg/dclient/client.go index 6d9e6429b0..0a82641d6c 100644 --- a/pkg/dclient/client.go +++ b/pkg/dclient/client.go @@ -237,6 +237,11 @@ func (c *Client) SetDiscovery(discoveryClient IDiscovery) { c.DiscoveryClient = discoveryClient } +// GetDiscoveryCache gets the discovery client cache +func (c *Client) GetDiscoveryCache() discovery.CachedDiscoveryInterface { + return memory.NewMemCacheClient(c.kclient.Discovery()) +} + //ServerPreferredResources stores the cachedClient instance for discovery client type ServerPreferredResources struct { cachedClient discovery.CachedDiscoveryInterface diff --git a/pkg/policy/common.go b/pkg/policy/common.go index cb99b9dd2f..88697176e3 100644 --- a/pkg/policy/common.go +++ b/pkg/policy/common.go @@ -2,7 +2,9 @@ package policy import ( "fmt" + "strings" + kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/labels" @@ -32,3 +34,30 @@ func transformResource(resource unstructured.Unstructured) []byte { } return data } + +// convertPoliciesToClusterPolicies - convert array of Policy to array of ClusterPolicy +func convertPoliciesToClusterPolicies(nsPolicies []*kyverno.Policy) []*kyverno.ClusterPolicy { + var cpols []*kyverno.ClusterPolicy + for _, pol := range nsPolicies { + cpol := kyverno.ClusterPolicy(*pol) + cpols = append(cpols, &cpol) + } + return cpols +} + +// convertPolicyToClusterPolicy - convert Policy to ClusterPolicy +func convertPolicyToClusterPolicy(nsPolicies *kyverno.Policy) *kyverno.ClusterPolicy { + cpol := kyverno.ClusterPolicy(*nsPolicies) + return &cpol +} + +func getIsNamespacedPolicy(key string) (string, string, bool) { + namespace := "" + index := strings.Index(key, "/") + if index != -1 { + namespace = key[:index] + key = key[index+1:] + return namespace, key, true + } + return namespace, key, false +} diff --git a/pkg/policy/controller.go b/pkg/policy/controller.go index 0fc91fcfc1..8cfc6c18f0 100644 --- a/pkg/policy/controller.go +++ b/pkg/policy/controller.go @@ -55,6 +55,9 @@ type PolicyController struct { // pLister can list/get policy from the shared informer's store pLister kyvernolister.ClusterPolicyLister + // npLister can list/get namespace policy from the shared informer's store + npLister kyvernolister.PolicyLister + // pvLister can list/get policy violation from the shared informer's store cpvLister kyvernolister.ClusterPolicyViolationLister @@ -67,6 +70,9 @@ type PolicyController struct { // pListerSynced returns true if the Policy store has been synced at least once pListerSynced cache.InformerSynced + // npListerSynced returns true if the Policy store has been synced at least once + npListerSynced cache.InformerSynced + // pvListerSynced returns true if the Policy store has been synced at least once cpvListerSynced cache.InformerSynced @@ -95,6 +101,7 @@ type PolicyController struct { func NewPolicyController(kyvernoClient *kyvernoclient.Clientset, client *client.Client, pInformer kyvernoinformer.ClusterPolicyInformer, + npInformer kyvernoinformer.PolicyInformer, cpvInformer kyvernoinformer.ClusterPolicyViolationInformer, nspvInformer kyvernoinformer.PolicyViolationInformer, configHandler config.Interface, eventGen event.Interface, @@ -131,6 +138,12 @@ func NewPolicyController(kyvernoClient *kyvernoclient.Clientset, UpdateFunc: pc.updatePolicy, DeleteFunc: pc.deletePolicy, }) + // Policy informer event handler + npInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ + AddFunc: pc.addNsPolicy, + UpdateFunc: pc.updateNsPolicy, + DeleteFunc: pc.deleteNsPolicy, + }) cpvInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ AddFunc: pc.addClusterPolicyViolation, @@ -145,11 +158,13 @@ func NewPolicyController(kyvernoClient *kyvernoclient.Clientset, }) pc.pLister = pInformer.Lister() + pc.npLister = npInformer.Lister() pc.cpvLister = cpvInformer.Lister() pc.nspvLister = nspvInformer.Lister() pc.nsLister = namespaces.Lister() pc.pListerSynced = pInformer.Informer().HasSynced + pc.npListerSynced = npInformer.Informer().HasSynced pc.cpvListerSynced = cpvInformer.Informer().HasSynced pc.nspvListerSynced = nspvInformer.Informer().HasSynced pc.nsListerSynced = namespaces.Informer().HasSynced @@ -225,6 +240,54 @@ func (pc *PolicyController) deletePolicy(obj interface{}) { pc.enqueuePolicy(p) } +func (pc *PolicyController) addNsPolicy(obj interface{}) { + logger := pc.log + p := obj.(*kyverno.Policy) + pol := convertPolicyToClusterPolicy(p) + if !pc.canBackgroundProcess(pol) { + return + } + logger.V(4).Info("queuing policy for background processing", "namespace", pol.Namespace, "name", pol.Name) + pc.enqueuePolicy(pol) +} + +func (pc *PolicyController) updateNsPolicy(old, cur interface{}) { + logger := pc.log + oldP := old.(*kyverno.Policy) + curP := cur.(*kyverno.Policy) + ncurP := convertPolicyToClusterPolicy(curP) + if !pc.canBackgroundProcess(ncurP) { + return + } + + logger.V(4).Info("updating namespace policy", "namespace", oldP.Namespace, "name", oldP.Name) + pc.enqueuePolicy(ncurP) +} + +func (pc *PolicyController) deleteNsPolicy(obj interface{}) { + logger := pc.log + p, ok := obj.(*kyverno.Policy) + if !ok { + tombstone, ok := obj.(cache.DeletedFinalStateUnknown) + if !ok { + logger.Info("couldnt get object from tomstone", "obj", obj) + return + } + + p, ok = tombstone.Obj.(*kyverno.Policy) + if !ok { + logger.Info("tombstone container object that is not a policy", "obj", obj) + return + } + } + pol := convertPolicyToClusterPolicy(p) + logger.V(4).Info("deleting namespace policy", "namespace", pol.Namespace, "name", pol.Name) + + // we process policies that are not set of background processing as we need to perform policy violation + // cleanup when a policy is deleted. + pc.enqueuePolicy(pol) +} + func (pc *PolicyController) enqueuePolicy(policy *kyverno.ClusterPolicy) { logger := pc.log key, err := cache.MetaNamespaceKeyFunc(policy) @@ -307,7 +370,16 @@ func (pc *PolicyController) syncPolicy(key string) error { logger.V(4).Info("finished syncing policy", "key", key, "processingTime", time.Since(startTime).String()) }() - policy, err := pc.pLister.Get(key) + namespace, key, isNamespacedPolicy := getIsNamespacedPolicy(key) + var policy *kyverno.ClusterPolicy + var err error + if !isNamespacedPolicy { + policy, err = pc.pLister.Get(key) + } else { + var nspolicy *kyverno.Policy + nspolicy, err = pc.npLister.Policies(namespace).Get(key) + policy = convertPolicyToClusterPolicy(nspolicy) + } if errors.IsNotFound(err) { go pc.deletePolicyViolations(key) @@ -327,7 +399,6 @@ func (pc *PolicyController) syncPolicy(key string) error { engineResponses := pc.processExistingResources(policy) pc.cleanupAndReport(engineResponses) - return nil } diff --git a/pkg/policy/namespacedpv.go b/pkg/policy/namespacedpv.go index 05c887225e..171683755c 100644 --- a/pkg/policy/namespacedpv.go +++ b/pkg/policy/namespacedpv.go @@ -18,6 +18,7 @@ func (pc *PolicyController) addNamespacedPolicyViolation(obj interface{}) { } ps := pc.getPolicyForNamespacedPolicyViolation(pv) + if len(ps) == 0 { // there is no cluster policy for this violation, so we can delete this cluster policy violation logger.V(4).Info("namespaced policy violation does not belong to an active policy, will be cleaned up") @@ -46,6 +47,7 @@ func (pc *PolicyController) updateNamespacedPolicyViolation(old, cur interface{} logger := pc.log.WithValues("kind", curPV.Kind, "namespace", curPV.Namespace, "name", curPV.Name) ps := pc.getPolicyForNamespacedPolicyViolation(curPV) + if len(ps) == 0 { // there is no namespaced policy for this violation, so we can delete this cluster policy violation logger.V(4).Info("nameapced policy violation does not belong to an active policy, will be cleanedup") @@ -107,7 +109,17 @@ func (pc *PolicyController) deleteNamespacedPolicyViolation(obj interface{}) { func (pc *PolicyController) getPolicyForNamespacedPolicyViolation(pv *kyverno.PolicyViolation) []*kyverno.ClusterPolicy { logger := pc.log.WithValues("kind", pv.Kind, "namespace", pv.Namespace, "name", pv.Name) + // Check for NamespacePolicies + nspol, err := pc.npLister.GetPolicyForPolicyViolation(pv) + if err != nil { + logger.V(4).Info("missing namespace policy for namespaced policy violation", "reason", err.Error()) + return nil + } + if len(nspol) > 0 { + return convertPoliciesToClusterPolicies(nspol) + } policies, err := pc.pLister.GetPolicyForNamespacedPolicyViolation(pv) + if err != nil || len(policies) == 0 { logger.V(4).Info("missing policy for namespaced policy violation", "reason", err.Error()) return nil diff --git a/pkg/policy/validate.go b/pkg/policy/validate.go index de7b791f04..a914de6882 100644 --- a/pkg/policy/validate.go +++ b/pkg/policy/validate.go @@ -47,6 +47,32 @@ func Validate(policyRaw []byte, client *dclient.Client, mock bool, openAPIContro // as there are more than 1 operation in rule, not need to evaluate it further return fmt.Errorf("path: spec.rules[%d]: %v", i, err) } + // validate Cluster Resources in namespaced cluster policy + // For namespaced cluster policy, ClusterResource type field and values are not allowed in match and exclude + if p.ObjectMeta.Namespace != "" { + // check unique kind + isUnique := func(kind string, resources []string) bool { + for _, k := range resources { + if kind == k { + return false + } + } + return true + } + clusterResources := make([]string, 0) + // Get all the cluster type kind supported by cluster + res, _ := client.GetDiscoveryCache().ServerPreferredResources() + for _, resList := range res { + for _, r := range resList.APIResources { + if r.Namespaced == false { + if isUnique(r.Kind, clusterResources) { + clusterResources = append(clusterResources, r.Kind) + } + } + } + } + return checkClusterResourceInMatchAndExclude(rule, clusterResources) + } if doesMatchAndExcludeConflict(rule) { return fmt.Errorf("path: spec.rules[%v]: rule is matching an empty set", rule.Name) @@ -401,3 +427,34 @@ func validateResourceDescription(rd kyverno.ResourceDescription) error { } return nil } + +// checkClusterResourceInMatchAndExclude returns false if namespaced ClusterPolicy contains cluster wide resources in +// Match and Exclude block +func checkClusterResourceInMatchAndExclude(rule kyverno.Rule, clusterResources []string) error { + // Contains Namespaces in Match->ResourceDescription + if len(rule.MatchResources.ResourceDescription.Namespaces) > 0 { + return fmt.Errorf("namespaced cluster policy : field namespaces not allowed in match.resources") + } + // Contains Namespaces in Exclude->ResourceDescription + if len(rule.ExcludeResources.ResourceDescription.Namespaces) > 0 { + return fmt.Errorf("namespaced cluster policy : field namespaces not allowed in exclude.resources") + } + // Contains "Cluster Wide Resources" in Match->ResourceDescription->Kinds + for _, kind := range rule.MatchResources.ResourceDescription.Kinds { + for _, k := range clusterResources { + if kind == k { + return fmt.Errorf("namespaced policy : cluster type value '%s' not allowed in match.resources.kinds", kind) + } + } + } + // Contains "Cluster Wide Resources" in Exclude->ResourceDescription->Kinds + for _, kind := range rule.ExcludeResources.ResourceDescription.Kinds { + for _, k := range clusterResources { + if kind == k { + return fmt.Errorf("namespaced policy : cluster type value '%s' not allowed in exclude.resources.kinds", kind) + } + } + + } + return nil +} diff --git a/pkg/policycache/cache.go b/pkg/policycache/cache.go index 8cd77948fb..67aa43a19e 100644 --- a/pkg/policycache/cache.go +++ b/pkg/policycache/cache.go @@ -1,7 +1,5 @@ package policycache -// package main - import ( "sync" @@ -11,9 +9,17 @@ import ( type pMap struct { sync.RWMutex + // dataMap field stores ClusterPolicies dataMap map[PolicyType][]*kyverno.ClusterPolicy + // nsDataMap field stores Namespaced Policies for each namespaces. + // The Policy is converted internally to ClusterPolicy and stored as a ClusterPolicy + // Since both the policy use same type (i.e. Policy), Both policies can be differentiated based on + // "Kind" or "namespace". When the Policy is converted it will retain the value of kind as "Policy". + // Cluster policy will be having namespace as Blank (""), but Policy will always be having namespace field and "default" value by default + nsDataMap map[string]map[PolicyType][]*kyverno.ClusterPolicy // nameCacheMap stores the names of all existing policies in dataMap + // Policy names are stored as / nameCacheMap map[PolicyType]map[string]bool } @@ -27,7 +33,7 @@ type policyCache struct { type Interface interface { Add(policy *kyverno.ClusterPolicy) Remove(policy *kyverno.ClusterPolicy) - Get(pkey PolicyType) []*kyverno.ClusterPolicy + Get(pkey PolicyType, nspace *string) []*kyverno.ClusterPolicy } // newPolicyCache ... @@ -42,6 +48,7 @@ func newPolicyCache(log logr.Logger) Interface { return &policyCache{ pMap{ dataMap: make(map[PolicyType][]*kyverno.ClusterPolicy), + nsDataMap: make(map[string]map[PolicyType][]*kyverno.ClusterPolicy), nameCacheMap: namesCache, }, log, @@ -51,12 +58,13 @@ func newPolicyCache(log logr.Logger) Interface { // Add a policy to cache func (pc *policyCache) Add(policy *kyverno.ClusterPolicy) { pc.pMap.add(policy) + pc.Logger.V(4).Info("policy is added to cache", "name", policy.GetName()) } // Get the list of matched policies -func (pc *policyCache) Get(pkey PolicyType) []*kyverno.ClusterPolicy { - return pc.pMap.get(pkey) +func (pc *policyCache) Get(pkey PolicyType, nspace *string) []*kyverno.ClusterPolicy { + return pc.pMap.get(pkey, nspace) } // Remove a policy from cache @@ -74,13 +82,28 @@ func (m *pMap) add(policy *kyverno.ClusterPolicy) { validateEnforceMap := m.nameCacheMap[ValidateEnforce] validateAuditMap := m.nameCacheMap[ValidateAudit] generateMap := m.nameCacheMap[Generate] + var pName = policy.GetName() + pSpace := policy.GetNamespace() + isNamespacedPolicy := false + if pSpace != "" { + pName = pSpace + "/" + pName + isNamespacedPolicy = true + // Initialize Namespace Cache Map + _, ok := m.nsDataMap[policy.GetNamespace()] + if !ok { + m.nsDataMap[policy.GetNamespace()] = make(map[PolicyType][]*kyverno.ClusterPolicy) + } + } - pName := policy.GetName() for _, rule := range policy.Spec.Rules { if rule.HasMutate() { if !mutateMap[pName] { mutateMap[pName] = true - + if isNamespacedPolicy { + mutatePolicy := m.nsDataMap[policy.GetNamespace()][Mutate] + m.nsDataMap[policy.GetNamespace()][Mutate] = append(mutatePolicy, policy) + continue + } mutatePolicy := m.dataMap[Mutate] m.dataMap[Mutate] = append(mutatePolicy, policy) } @@ -91,7 +114,11 @@ func (m *pMap) add(policy *kyverno.ClusterPolicy) { if enforcePolicy { if !validateEnforceMap[pName] { validateEnforceMap[pName] = true - + if isNamespacedPolicy { + validatePolicy := m.nsDataMap[policy.GetNamespace()][ValidateEnforce] + m.nsDataMap[policy.GetNamespace()][ValidateEnforce] = append(validatePolicy, policy) + continue + } validatePolicy := m.dataMap[ValidateEnforce] m.dataMap[ValidateEnforce] = append(validatePolicy, policy) } @@ -101,7 +128,11 @@ func (m *pMap) add(policy *kyverno.ClusterPolicy) { // ValidateAudit if !validateAuditMap[pName] { validateAuditMap[pName] = true - + if isNamespacedPolicy { + validatePolicy := m.nsDataMap[policy.GetNamespace()][ValidateAudit] + m.nsDataMap[policy.GetNamespace()][ValidateAudit] = append(validatePolicy, policy) + continue + } validatePolicy := m.dataMap[ValidateAudit] m.dataMap[ValidateAudit] = append(validatePolicy, policy) } @@ -111,7 +142,11 @@ func (m *pMap) add(policy *kyverno.ClusterPolicy) { if rule.HasGenerate() { if !generateMap[pName] { generateMap[pName] = true - + if isNamespacedPolicy { + generatePolicy := m.nsDataMap[policy.GetNamespace()][Generate] + m.nsDataMap[policy.GetNamespace()][Generate] = append(generatePolicy, policy) + continue + } generatePolicy := m.dataMap[Generate] m.dataMap[Generate] = append(generatePolicy, policy) } @@ -125,30 +160,55 @@ func (m *pMap) add(policy *kyverno.ClusterPolicy) { m.nameCacheMap[Generate] = generateMap } -func (m *pMap) get(key PolicyType) []*kyverno.ClusterPolicy { +func (m *pMap) get(key PolicyType, nspace *string) []*kyverno.ClusterPolicy { m.RLock() defer m.RUnlock() + if nspace == nil || *nspace == "" { + return m.dataMap[key] + } + return m.nsDataMap[*nspace][key] - return m.dataMap[key] } func (m *pMap) remove(policy *kyverno.ClusterPolicy) { m.Lock() defer m.Unlock() - pName := policy.GetName() - dataMap := m.dataMap - for k, policies := range dataMap { + var pName = policy.GetName() + pSpace := policy.GetNamespace() + isNamespacedPolicy := false + if pSpace != "" { + pName = pSpace + "/" + pName + isNamespacedPolicy = true + } + if !isNamespacedPolicy { + dataMap := m.dataMap + for k, policies := range dataMap { - var newPolicies []*kyverno.ClusterPolicy - for _, p := range policies { - if p.GetName() == pName { - continue + var newPolicies []*kyverno.ClusterPolicy + for _, p := range policies { + if p.GetName() == pName { + continue + } + newPolicies = append(newPolicies, p) } - newPolicies = append(newPolicies, p) - } - m.dataMap[k] = newPolicies + m.dataMap[k] = newPolicies + } + } else { + dataMap := m.nsDataMap[pSpace] + for k, policies := range dataMap { + + var newPolicies []*kyverno.ClusterPolicy + for _, p := range policies { + if (p.GetNamespace() + "/" + p.GetName()) == pName { + continue + } + newPolicies = append(newPolicies, p) + } + + m.nsDataMap[pSpace][k] = newPolicies + } } for _, nameCache := range m.nameCacheMap { diff --git a/pkg/policycache/cache_test.go b/pkg/policycache/cache_test.go index beed77cfaf..4c952291ce 100644 --- a/pkg/policycache/cache_test.go +++ b/pkg/policycache/cache_test.go @@ -17,21 +17,21 @@ func Test_All(t *testing.T) { pCache.Add(policy) // get - if len(pCache.Get(Mutate)) != 1 { - t.Errorf("expected 1 mutate policy, found %v", len(pCache.Get(Mutate))) + if len(pCache.Get(Mutate, nil)) != 1 { + t.Errorf("expected 1 mutate policy, found %v", len(pCache.Get(Mutate, nil))) } - if len(pCache.Get(ValidateEnforce)) != 1 { - t.Errorf("expected 1 validate enforce policy, found %v", len(pCache.Get(ValidateEnforce))) + if len(pCache.Get(ValidateEnforce, nil)) != 1 { + t.Errorf("expected 1 validate enforce policy, found %v", len(pCache.Get(ValidateEnforce, nil))) } - if len(pCache.Get(Generate)) != 1 { - t.Errorf("expected 1 generate policy, found %v", len(pCache.Get(Generate))) + if len(pCache.Get(Generate, nil)) != 1 { + t.Errorf("expected 1 generate policy, found %v", len(pCache.Get(Generate, nil))) } // remove pCache.Remove(policy) - assert.Assert(t, len(pCache.Get(ValidateEnforce)) == 0) + assert.Assert(t, len(pCache.Get(ValidateEnforce, nil)) == 0) } func Test_Add_Duplicate_Policy(t *testing.T) { @@ -42,16 +42,16 @@ func Test_Add_Duplicate_Policy(t *testing.T) { pCache.Add(policy) pCache.Add(policy) - if len(pCache.Get(Mutate)) != 1 { - t.Errorf("expected 1 mutate policy, found %v", len(pCache.Get(Mutate))) + if len(pCache.Get(Mutate, nil)) != 1 { + t.Errorf("expected 1 mutate policy, found %v", len(pCache.Get(Mutate, nil))) } - if len(pCache.Get(ValidateEnforce)) != 1 { - t.Errorf("expected 1 validate enforce policy, found %v", len(pCache.Get(ValidateEnforce))) + if len(pCache.Get(ValidateEnforce, nil)) != 1 { + t.Errorf("expected 1 validate enforce policy, found %v", len(pCache.Get(ValidateEnforce, nil))) } - if len(pCache.Get(Generate)) != 1 { - t.Errorf("expected 1 generate policy, found %v", len(pCache.Get(Generate))) + if len(pCache.Get(Generate, nil)) != 1 { + t.Errorf("expected 1 generate policy, found %v", len(pCache.Get(Generate, nil))) } } @@ -66,12 +66,12 @@ func Test_Add_Validate_Audit(t *testing.T) { pCache.Add(policy) pCache.Add(policy) - if len(pCache.Get(ValidateEnforce)) != 1 { - t.Errorf("expected 1 validate enforce policy, found %v", len(pCache.Get(ValidateEnforce))) + if len(pCache.Get(ValidateEnforce, nil)) != 1 { + t.Errorf("expected 1 validate enforce policy, found %v", len(pCache.Get(ValidateEnforce, nil))) } - if len(pCache.Get(ValidateAudit)) != 1 { - t.Errorf("expected 1 validate audit policy, found %v", len(pCache.Get(ValidateAudit))) + if len(pCache.Get(ValidateAudit, nil)) != 1 { + t.Errorf("expected 1 validate audit policy, found %v", len(pCache.Get(ValidateAudit, nil))) } } @@ -80,18 +80,18 @@ func Test_Add_Remove(t *testing.T) { policy := newPolicy(t) pCache.Add(policy) - if len(pCache.Get(ValidateEnforce)) != 1 { - t.Errorf("expected 1 validate enforce policy, found %v", len(pCache.Get(ValidateEnforce))) + if len(pCache.Get(ValidateEnforce, nil)) != 1 { + t.Errorf("expected 1 validate enforce policy, found %v", len(pCache.Get(ValidateEnforce, nil))) } pCache.Remove(policy) - if len(pCache.Get(ValidateEnforce)) != 0 { - t.Errorf("expected 1 validate enforce policy, found %v", len(pCache.Get(ValidateEnforce))) + if len(pCache.Get(ValidateEnforce, nil)) != 0 { + t.Errorf("expected 1 validate enforce policy, found %v", len(pCache.Get(ValidateEnforce, nil))) } pCache.Add(policy) - if len(pCache.Get(ValidateEnforce)) != 1 { - t.Errorf("expected 1 validate enforce policy, found %v", len(pCache.Get(ValidateEnforce))) + if len(pCache.Get(ValidateEnforce, nil)) != 1 { + t.Errorf("expected 1 validate enforce policy, found %v", len(pCache.Get(ValidateEnforce, nil))) } } @@ -205,3 +205,196 @@ func newPolicy(t *testing.T) *kyverno.ClusterPolicy { return policy } + +func newNsPolicy(t *testing.T) *kyverno.ClusterPolicy { + rawPolicy := []byte(`{ + "metadata": { + "name": "test-policy", + "namespace": "test" + }, + "spec": { + "validationFailureAction": "enforce", + "rules": [ + { + "name": "deny-privileged-disallowpriviligedescalation", + "match": { + "resources": { + "kinds": [ + "Pod" + ] + } + }, + "validate": { + "deny": { + "conditions": [ + { + "key": "a", + "operator": "Equals", + "value": "a" + } + ] + } + } + }, + { + "name": "deny-privileged-disallowpriviligedescalation", + "match": { + "resources": { + "kinds": [ + "Pod" + ] + } + }, + "validate": { + "pattern": { + "spec": { + "containers": [ + { + "image": "!*:latest" + } + ] + } + } + } + }, + { + "name": "annotate-host-path", + "match": { + "resources": { + "kinds": [ + "Pod" + ] + } + }, + "mutate": { + "overlay": { + "metadata": { + "annotations": { + "+(cluster-autoscaler.kubernetes.io/safe-to-evict)": true + } + } + } + } + }, + { + "name": "default-deny-ingress", + "match": { + "resources": { + "kinds": [ + "Namespace" + ] + } + }, + "generate": { + "kind": "NetworkPolicy", + "name": "default-deny-ingress", + "namespace": "{{request.object.metadata.name}}", + "data": { + "spec": { + "podSelector": { + }, + "policyTypes": [ + "Ingress" + ] + } + } + } + } + ] + } + }`) + + var policy *kyverno.Policy + err := json.Unmarshal(rawPolicy, &policy) + assert.NilError(t, err) + + return convertPolicyToClusterPolicy(policy) +} + +func Test_Ns_All(t *testing.T) { + pCache := newPolicyCache(log.Log) + policy := newNsPolicy(t) + + // add + pCache.Add(policy) + nspace := policy.GetNamespace() + // get + if len(pCache.Get(Mutate, &nspace)) != 1 { + t.Errorf("expected 1 mutate policy, found %v", len(pCache.Get(Mutate, &nspace))) + } + + if len(pCache.Get(ValidateEnforce, &nspace)) != 1 { + t.Errorf("expected 1 validate enforce policy, found %v", len(pCache.Get(ValidateEnforce, &nspace))) + } + + if len(pCache.Get(Generate, &nspace)) != 1 { + t.Errorf("expected 1 generate policy, found %v", len(pCache.Get(Generate, &nspace))) + } + + // remove + pCache.Remove(policy) + assert.Assert(t, len(pCache.Get(ValidateEnforce, &nspace)) == 0) +} + +func Test_Ns_Add_Duplicate_Policy(t *testing.T) { + pCache := newPolicyCache(log.Log) + policy := newNsPolicy(t) + + pCache.Add(policy) + pCache.Add(policy) + pCache.Add(policy) + nspace := policy.GetNamespace() + if len(pCache.Get(Mutate, &nspace)) != 1 { + t.Errorf("expected 1 mutate policy, found %v", len(pCache.Get(Mutate, &nspace))) + } + + if len(pCache.Get(ValidateEnforce, &nspace)) != 1 { + t.Errorf("expected 1 validate enforce policy, found %v", len(pCache.Get(ValidateEnforce, &nspace))) + } + + if len(pCache.Get(Generate, &nspace)) != 1 { + t.Errorf("expected 1 generate policy, found %v", len(pCache.Get(Generate, &nspace))) + } +} + +func Test_Ns_Add_Validate_Audit(t *testing.T) { + pCache := newPolicyCache(log.Log) + policy := newNsPolicy(t) + nspace := policy.GetNamespace() + + pCache.Add(policy) + pCache.Add(policy) + + policy.Spec.ValidationFailureAction = "audit" + pCache.Add(policy) + pCache.Add(policy) + + if len(pCache.Get(ValidateEnforce, &nspace)) != 1 { + t.Errorf("expected 1 validate enforce policy, found %v", len(pCache.Get(ValidateEnforce, &nspace))) + } + + if len(pCache.Get(ValidateAudit, &nspace)) != 1 { + t.Errorf("expected 1 validate audit policy, found %v", len(pCache.Get(ValidateAudit, &nspace))) + } +} + +func Test_Ns_Add_Remove(t *testing.T) { + pCache := newPolicyCache(log.Log) + policy := newNsPolicy(t) + + pCache.Add(policy) + nspace := policy.GetNamespace() + if len(pCache.Get(ValidateEnforce, &nspace)) != 1 { + t.Errorf("expected 1 validate enforce policy, found %v", len(pCache.Get(ValidateEnforce, &nspace))) + } + + pCache.Remove(policy) + if len(pCache.Get(ValidateEnforce, &nspace)) != 0 { + t.Errorf("expected 1 validate enforce policy, found %v", len(pCache.Get(ValidateEnforce, &nspace))) + } + + pCache.Add(policy) + if len(pCache.Get(ValidateEnforce, &nspace)) != 1 { + t.Errorf("expected 1 validate enforce policy, found %v", len(pCache.Get(ValidateEnforce, &nspace))) + } +} diff --git a/pkg/policycache/informer.go b/pkg/policycache/informer.go index 5070c0fdcf..1ba73fbd93 100644 --- a/pkg/policycache/informer.go +++ b/pkg/policycache/informer.go @@ -15,14 +15,16 @@ import ( // This cache is only used in the admission webhook to fast retrieve // policies based on types (Mutate/ValidateEnforce/Generate). type Controller struct { - pSynched cache.InformerSynced - Cache Interface - log logr.Logger + pSynched cache.InformerSynced + nspSynched cache.InformerSynced + Cache Interface + log logr.Logger } // NewPolicyCacheController create a new PolicyController func NewPolicyCacheController( pInformer kyvernoinformer.ClusterPolicyInformer, + nspInformer kyvernoinformer.PolicyInformer, log logr.Logger) *Controller { pc := Controller{ @@ -30,17 +32,33 @@ func NewPolicyCacheController( log: log, } + // ClusterPolicy Informer pInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ AddFunc: pc.addPolicy, UpdateFunc: pc.updatePolicy, DeleteFunc: pc.deletePolicy, }) + // Policy Informer + nspInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ + AddFunc: pc.addNsPolicy, + UpdateFunc: pc.updateNsPolicy, + DeleteFunc: pc.deleteNsPolicy, + }) + pc.pSynched = pInformer.Informer().HasSynced + pc.nspSynched = nspInformer.Informer().HasSynced return &pc } +// convertPolicyToClusterPolicy - convert Policy to ClusterPolicy +// This will retain the kind of Policy and convert type to ClusterPolicy +func convertPolicyToClusterPolicy(nsPolicies *kyverno.Policy) *kyverno.ClusterPolicy { + cpol := kyverno.ClusterPolicy(*nsPolicies) + return &cpol +} + func (c *Controller) addPolicy(obj interface{}) { p := obj.(*kyverno.ClusterPolicy) c.Cache.Add(p) @@ -53,7 +71,6 @@ func (c *Controller) updatePolicy(old, cur interface{}) { if reflect.DeepEqual(pOld.Spec, pNew.Spec) { return } - c.Cache.Remove(pOld) c.Cache.Add(pNew) } @@ -63,6 +80,29 @@ func (c *Controller) deletePolicy(obj interface{}) { c.Cache.Remove(p) } +// addNsPolicy - Add Policy to cache +func (c *Controller) addNsPolicy(obj interface{}) { + p := obj.(*kyverno.Policy) + c.Cache.Add(convertPolicyToClusterPolicy(p)) +} + +// updateNsPolicy - Update Policy of cache +func (c *Controller) updateNsPolicy(old, cur interface{}) { + npOld := old.(*kyverno.Policy) + npNew := cur.(*kyverno.Policy) + if reflect.DeepEqual(npOld.Spec, npNew.Spec) { + return + } + c.Cache.Remove(convertPolicyToClusterPolicy(npOld)) + c.Cache.Add(convertPolicyToClusterPolicy(npNew)) +} + +// deleteNsPolicy - Delete Policy from cache +func (c *Controller) deleteNsPolicy(obj interface{}) { + p := obj.(*kyverno.Policy) + c.Cache.Remove(convertPolicyToClusterPolicy(p)) +} + // Run waits until policy informer to be synced func (c *Controller) Run(workers int, stopCh <-chan struct{}) { logger := c.log diff --git a/pkg/policystatus/main.go b/pkg/policystatus/main.go index e18d2aab70..ffc2003a10 100644 --- a/pkg/policystatus/main.go +++ b/pkg/policystatus/main.go @@ -3,11 +3,11 @@ package policystatus import ( "encoding/json" "fmt" + kyvernolister "github.com/nirmata/kyverno/pkg/client/listers/kyverno/v1" + "strings" "sync" "time" - kyvernolister "github.com/nirmata/kyverno/pkg/client/listers/kyverno/v1" - "k8s.io/apimachinery/pkg/util/wait" "github.com/nirmata/kyverno/pkg/client/clientset/versioned" @@ -52,6 +52,7 @@ type Sync struct { Listener Listener client *versioned.Clientset lister kyvernolister.ClusterPolicyLister + nsLister kyvernolister.PolicyLister } type cache struct { @@ -60,7 +61,7 @@ type cache struct { keyToMutex *keyToMutex } -func NewSync(c *versioned.Clientset, lister kyvernolister.ClusterPolicyLister) *Sync { +func NewSync(c *versioned.Clientset, lister kyvernolister.ClusterPolicyLister, nsLister kyvernolister.PolicyLister) *Sync { return &Sync{ cache: &cache{ dataMu: sync.RWMutex{}, @@ -69,6 +70,7 @@ func NewSync(c *versioned.Clientset, lister kyvernolister.ClusterPolicyLister) * }, client: c, lister: lister, + nsLister: nsLister, Listener: make(chan statusUpdater, 20), } } @@ -99,7 +101,6 @@ func (s *Sync) updateStatusCache(stopCh <-chan struct{}) { status = policy.Status } } - updatedStatus := statusUpdater.UpdateStatus(status) s.cache.dataMu.Lock() @@ -127,18 +128,49 @@ func (s *Sync) updatePolicyStatus() { s.cache.dataMu.Unlock() for policyName, status := range nameToStatus { - policy, err := s.lister.Get(policyName) - if err != nil { - continue + // Identify Policy and ClusterPolicy based on namespace in key + // key = / for namespacepolicy and key = for clusterpolicy + // and update the respective policies + namespace := "" + isNamespacedPolicy := false + key := policyName + index := strings.Index(policyName, "/") + if index != -1 { + namespace = policyName[:index] + isNamespacedPolicy = true + policyName = policyName[index+1:] + } + if !isNamespacedPolicy { + policy, err := s.lister.Get(policyName) + if err != nil { + continue + } + + policy.Status = status + _, err = s.client.KyvernoV1().ClusterPolicies().UpdateStatus(policy) + if err != nil { + s.cache.dataMu.Lock() + delete(s.cache.data, policyName) + s.cache.dataMu.Unlock() + log.Log.Error(err, "failed to update policy status") + } + } else { + policy, err := s.nsLister.Policies(namespace).Get(policyName) + if err != nil { + s.cache.dataMu.Lock() + delete(s.cache.data, key) + s.cache.dataMu.Unlock() + continue + } + policy.Status = status + _, err = s.client.KyvernoV1().Policies(namespace).UpdateStatus(policy) + if err != nil { + s.cache.dataMu.Lock() + delete(s.cache.data, key) + s.cache.dataMu.Unlock() + log.Log.Error(err, "failed to update namespace policy status") + } } - policy.Status = status - _, err = s.client.KyvernoV1().ClusterPolicies().UpdateStatus(policy) - if err != nil { - s.cache.dataMu.Lock() - delete(s.cache.data, policyName) - s.cache.dataMu.Unlock() - log.Log.Error(err, "failed to update policy status") - } } } diff --git a/pkg/policystatus/status_test.go b/pkg/policystatus/status_test.go index c69383b328..595f87f593 100644 --- a/pkg/policystatus/status_test.go +++ b/pkg/policystatus/status_test.go @@ -8,6 +8,7 @@ import ( "time" v1 "github.com/nirmata/kyverno/pkg/api/kyverno/v1" + lv1 "github.com/nirmata/kyverno/pkg/client/listers/kyverno/v1" ) type dummyStore struct { @@ -52,11 +53,32 @@ func (dl dummyLister) ListResources(selector labels.Selector) (ret []*v1.Cluster return nil, fmt.Errorf("not implemented") } +// type dymmyNsNamespace struct {} + +type dummyNsLister struct { +} + +func (dl dummyNsLister) Policies(name string) lv1.PolicyNamespaceLister { + return dummyNsLister{} +} + +func (dl dummyNsLister) List(selector labels.Selector) (ret []*v1.Policy, err error) { + return nil, fmt.Errorf("not implemented") +} + +func (dl dummyNsLister) Get(name string) (*v1.Policy, error) { + return nil, fmt.Errorf("not implemented") +} + +func (dl dummyNsLister) GetPolicyForPolicyViolation(pv *v1.PolicyViolation) ([]*v1.Policy, error) { + return nil, fmt.Errorf("not implemented") +} + func TestKeyToMutex(t *testing.T) { expectedCache := `{"policy1":{"rulesAppliedCount":100}}` stopCh := make(chan struct{}) - s := NewSync(nil, dummyLister{}) + s := NewSync(nil, dummyLister{}, dummyNsLister{}) for i := 0; i < 100; i++ { go s.updateStatusCache(stopCh) } diff --git a/pkg/webhookconfig/checker.go b/pkg/webhookconfig/checker.go index 7fea9922e7..9275ca5d79 100644 --- a/pkg/webhookconfig/checker.go +++ b/pkg/webhookconfig/checker.go @@ -25,7 +25,7 @@ func (wrc *WebhookRegistrationClient) constructVerifyMutatingWebhookConfig(caDat caData, true, wrc.timeoutSeconds, - "deployments/*", + []string{"deployments/*"}, "apps", "v1", []admregapi.OperationType{admregapi.Update}, @@ -49,7 +49,7 @@ func (wrc *WebhookRegistrationClient) constructDebugVerifyMutatingWebhookConfig( caData, true, wrc.timeoutSeconds, - "deployments/*", + []string{"deployments/*"}, "apps", "v1", []admregapi.OperationType{admregapi.Update}, diff --git a/pkg/webhookconfig/common.go b/pkg/webhookconfig/common.go index ae83cb49e3..253b6685f3 100644 --- a/pkg/webhookconfig/common.go +++ b/pkg/webhookconfig/common.go @@ -63,7 +63,7 @@ func (wrc *WebhookRegistrationClient) constructOwner() v1.OwnerReference { } // debug mutating webhook -func generateDebugMutatingWebhook(name, url string, caData []byte, validate bool, timeoutSeconds int32, resource, apiGroups, apiVersions string, operationTypes []admregapi.OperationType) admregapi.MutatingWebhook { +func generateDebugMutatingWebhook(name, url string, caData []byte, validate bool, timeoutSeconds int32, resources []string, apiGroups, apiVersions string, operationTypes []admregapi.OperationType) admregapi.MutatingWebhook { sideEffect := admregapi.SideEffectClassNoneOnDryRun failurePolicy := admregapi.Ignore reinvocationPolicy := admregapi.NeverReinvocationPolicy @@ -86,9 +86,7 @@ func generateDebugMutatingWebhook(name, url string, caData []byte, validate bool APIVersions: []string{ apiVersions, }, - Resources: []string{ - resource, - }, + Resources: resources, }, }, }, @@ -98,7 +96,7 @@ func generateDebugMutatingWebhook(name, url string, caData []byte, validate bool } } -func generateDebugValidatingWebhook(name, url string, caData []byte, validate bool, timeoutSeconds int32, resource, apiGroups, apiVersions string, operationTypes []admregapi.OperationType) admregapi.ValidatingWebhook { +func generateDebugValidatingWebhook(name, url string, caData []byte, validate bool, timeoutSeconds int32, resources []string, apiGroups, apiVersions string, operationTypes []admregapi.OperationType) admregapi.ValidatingWebhook { sideEffect := admregapi.SideEffectClassNoneOnDryRun failurePolicy := admregapi.Ignore return admregapi.ValidatingWebhook{ @@ -118,9 +116,7 @@ func generateDebugValidatingWebhook(name, url string, caData []byte, validate bo APIVersions: []string{ apiVersions, }, - Resources: []string{ - resource, - }, + Resources: resources, }, }, }, @@ -167,7 +163,7 @@ func generateDebugValidatingWebhook(name, url string, caData []byte, validate bo // } // mutating webhook -func generateMutatingWebhook(name, servicePath string, caData []byte, validation bool, timeoutSeconds int32, resource, apiGroups, apiVersions string, operationTypes []admregapi.OperationType) admregapi.MutatingWebhook { +func generateMutatingWebhook(name, servicePath string, caData []byte, validation bool, timeoutSeconds int32, resources []string, apiGroups, apiVersions string, operationTypes []admregapi.OperationType) admregapi.MutatingWebhook { sideEffect := admregapi.SideEffectClassNoneOnDryRun failurePolicy := admregapi.Ignore reinvocationPolicy := admregapi.NeverReinvocationPolicy @@ -194,9 +190,7 @@ func generateMutatingWebhook(name, servicePath string, caData []byte, validation APIVersions: []string{ apiVersions, }, - Resources: []string{ - resource, - }, + Resources: resources, }, }, }, @@ -207,7 +201,7 @@ func generateMutatingWebhook(name, servicePath string, caData []byte, validation } // validating webhook -func generateValidatingWebhook(name, servicePath string, caData []byte, validation bool, timeoutSeconds int32, resource, apiGroups, apiVersions string, operationTypes []admregapi.OperationType) admregapi.ValidatingWebhook { +func generateValidatingWebhook(name, servicePath string, caData []byte, validation bool, timeoutSeconds int32, resources []string, apiGroups, apiVersions string, operationTypes []admregapi.OperationType) admregapi.ValidatingWebhook { sideEffect := admregapi.SideEffectClassNoneOnDryRun failurePolicy := admregapi.Ignore return admregapi.ValidatingWebhook{ @@ -231,9 +225,7 @@ func generateValidatingWebhook(name, servicePath string, caData []byte, validati APIVersions: []string{ apiVersions, }, - Resources: []string{ - resource, - }, + Resources: resources, }, }, }, diff --git a/pkg/webhookconfig/policy.go b/pkg/webhookconfig/policy.go index da9fa0fa8a..f60a67290b 100644 --- a/pkg/webhookconfig/policy.go +++ b/pkg/webhookconfig/policy.go @@ -24,7 +24,7 @@ func (wrc *WebhookRegistrationClient) contructPolicyValidatingWebhookConfig(caDa caData, true, wrc.timeoutSeconds, - "clusterpolicies/*", + []string{"clusterpolicies/*", "policies/*"}, "kyverno.io", "v1", []admregapi.OperationType{admregapi.Create, admregapi.Update}, @@ -49,7 +49,7 @@ func (wrc *WebhookRegistrationClient) contructDebugPolicyValidatingWebhookConfig caData, true, wrc.timeoutSeconds, - "clusterpolicies/*", + []string{"clusterpolicies/*", "policies/*"}, "kyverno.io", "v1", []admregapi.OperationType{admregapi.Create, admregapi.Update}, @@ -73,7 +73,7 @@ func (wrc *WebhookRegistrationClient) contructPolicyMutatingWebhookConfig(caData caData, true, wrc.timeoutSeconds, - "clusterpolicies/*", + []string{"clusterpolicies/*", "policies/*"}, "kyverno.io", "v1", []admregapi.OperationType{admregapi.Create, admregapi.Update}, @@ -97,7 +97,7 @@ func (wrc *WebhookRegistrationClient) contructDebugPolicyMutatingWebhookConfig(c caData, true, wrc.timeoutSeconds, - "clusterpolicies/*", + []string{"clusterpolicies/*", "policies/*"}, "kyverno.io", "v1", []admregapi.OperationType{admregapi.Create, admregapi.Update}, diff --git a/pkg/webhookconfig/resource.go b/pkg/webhookconfig/resource.go index d513888618..df7e943bbc 100644 --- a/pkg/webhookconfig/resource.go +++ b/pkg/webhookconfig/resource.go @@ -24,7 +24,7 @@ func (wrc *WebhookRegistrationClient) constructDebugMutatingWebhookConfig(caData caData, true, wrc.timeoutSeconds, - "*/*", + []string{"*/*"}, "*", "*", []admregapi.OperationType{admregapi.Create, admregapi.Update}, @@ -48,7 +48,7 @@ func (wrc *WebhookRegistrationClient) constructMutatingWebhookConfig(caData []by caData, false, wrc.timeoutSeconds, - "*/*", + []string{"*/*"}, "*", "*", []admregapi.OperationType{admregapi.Create, admregapi.Update}, @@ -98,7 +98,7 @@ func (wrc *WebhookRegistrationClient) constructDebugValidatingWebhookConfig(caDa caData, true, wrc.timeoutSeconds, - "*/*", + []string{"*/*"}, "*", "*", []admregapi.OperationType{admregapi.Create, admregapi.Update, admregapi.Delete}, @@ -122,7 +122,7 @@ func (wrc *WebhookRegistrationClient) constructValidatingWebhookConfig(caData [] caData, false, wrc.timeoutSeconds, - "*/*", + []string{"*/*"}, "*", "*", []admregapi.OperationType{admregapi.Create, admregapi.Update, admregapi.Delete}, diff --git a/pkg/webhooks/common.go b/pkg/webhooks/common.go index 6b2c0401c7..9a8edb6d79 100644 --- a/pkg/webhooks/common.go +++ b/pkg/webhooks/common.go @@ -167,7 +167,7 @@ func convertResource(raw []byte, group, version, kind, namespace string) (unstru func excludeKyvernoResources(kind string) bool { switch kind { - case "ClusterPolicy", "ClusterPolicyViolation", "PolicyViolation", "GenerateRequest": + case "ClusterPolicy", "ClusterPolicyViolation", "PolicyViolation", "GenerateRequest", "Policy": return true default: return false diff --git a/pkg/webhooks/mutation.go b/pkg/webhooks/mutation.go index 9a6763d914..275e819bcb 100644 --- a/pkg/webhooks/mutation.go +++ b/pkg/webhooks/mutation.go @@ -56,7 +56,7 @@ func (ws *WebhookServer) HandleMutation( policyContext.Policy = *policy engineResponse := engine.Mutate(policyContext) - ws.statusListener.Send(mutateStats{resp: engineResponse}) + ws.statusListener.Send(mutateStats{resp: engineResponse, namespace: policy.Namespace}) if !engineResponse.IsSuccessful() { logger.Info("failed to apply policy", "policy", policy.Name, "failed rules", engineResponse.GetFailedRules()) continue @@ -118,11 +118,15 @@ func (ws *WebhookServer) HandleMutation( } type mutateStats struct { - resp response.EngineResponse + resp response.EngineResponse + namespace string } func (ms mutateStats) PolicyName() string { - return ms.resp.PolicyResponse.Policy + if ms.namespace == "" { + return ms.resp.PolicyResponse.Policy + } + return ms.namespace + "/" + ms.resp.PolicyResponse.Policy } func (ms mutateStats) UpdateStatus(status kyverno.PolicyStatus) kyverno.PolicyStatus { diff --git a/pkg/webhooks/server.go b/pkg/webhooks/server.go index b964b705de..0af3eb2bbc 100644 --- a/pkg/webhooks/server.go +++ b/pkg/webhooks/server.go @@ -272,12 +272,14 @@ func (ws *WebhookServer) resourceMutation(request *v1beta1.AdmissionRequest) *v1 }, } } - logger.V(6).Info("received an admission request in mutating webhook") + mutatePolicies := ws.pCache.Get(policycache.Mutate, nil) + validatePolicies := ws.pCache.Get(policycache.ValidateEnforce, nil) + generatePolicies := ws.pCache.Get(policycache.Generate, nil) - mutatePolicies := ws.pCache.Get(policycache.Mutate) - validatePolicies := ws.pCache.Get(policycache.ValidateEnforce) - generatePolicies := ws.pCache.Get(policycache.Generate) + // Get namespace policies from the cache for the requested resource namespace + nsMutatePolicies := ws.pCache.Get(policycache.Mutate, &request.Namespace) + mutatePolicies = append(mutatePolicies, nsMutatePolicies...) // getRoleRef only if policy has roles/clusterroles defined var roles, clusterRoles []string @@ -420,7 +422,10 @@ func (ws *WebhookServer) resourceValidation(request *v1beta1.AdmissionRequest) * // push admission request to audit handler, this won't block the admission request ws.auditHandler.Add(request.DeepCopy()) - policies := ws.pCache.Get(policycache.ValidateEnforce) + policies := ws.pCache.Get(policycache.ValidateEnforce, nil) + // Get namespace policies from the cache for the requested resource namespace + nsPolicies := ws.pCache.Get(policycache.ValidateEnforce, &request.Namespace) + policies = append(policies, nsPolicies...) if len(policies) == 0 { logger.V(4).Info("No enforce Validation policy found, returning") return &v1beta1.AdmissionResponse{Allowed: true} diff --git a/pkg/webhooks/validate_audit.go b/pkg/webhooks/validate_audit.go index d60b276f99..cf3867d73e 100644 --- a/pkg/webhooks/validate_audit.go +++ b/pkg/webhooks/validate_audit.go @@ -134,8 +134,10 @@ func (h *auditHandler) process(request *v1beta1.AdmissionRequest) error { var err error logger := h.log.WithName("process") - policies := h.pCache.Get(policycache.ValidateAudit) - + policies := h.pCache.Get(policycache.ValidateAudit, nil) + // Get namespace policies from the cache for the requested resource namespace + nsPolicies := h.pCache.Get(policycache.ValidateAudit, &request.Namespace) + policies = append(policies, nsPolicies...) // getRoleRef only if policy has roles/clusterroles defined if containRBACinfo(policies) { roles, clusterRoles, err = userinfo.GetRoleRef(h.rbLister, h.crbLister, request, h.configHandler) diff --git a/pkg/webhooks/validation.go b/pkg/webhooks/validation.go index d03b8d9db8..6426aab1b5 100644 --- a/pkg/webhooks/validation.go +++ b/pkg/webhooks/validation.go @@ -87,7 +87,8 @@ func HandleValidation( } engineResponses = append(engineResponses, engineResponse) statusListener.Send(validateStats{ - resp: engineResponse, + resp: engineResponse, + namespace: policy.Namespace, }) if !engineResponse.IsSuccessful() { logger.V(4).Info("failed to apply policy", "policy", policy.Name, "failed rules", engineResponse.GetFailedRules()) @@ -126,11 +127,16 @@ func HandleValidation( } type validateStats struct { - resp response.EngineResponse + resp response.EngineResponse + namespace string } func (vs validateStats) PolicyName() string { - return vs.resp.PolicyResponse.Policy + if vs.namespace == "" { + return vs.resp.PolicyResponse.Policy + } + return vs.namespace + "/" + vs.resp.PolicyResponse.Policy + } func (vs validateStats) UpdateStatus(status kyverno.PolicyStatus) kyverno.PolicyStatus {