diff --git a/PROJECT b/PROJECT index ad995a7..59fdc71 100644 --- a/PROJECT +++ b/PROJECT @@ -7,4 +7,13 @@ layout: - go.kubebuilder.io/v4 projectName: kyverno-policy-operator repo: github.com/giantswarm/kyverno-policy-operator +resources: +- api: + crdVersion: v1 + namespaced: true + domain: giantswarm.io + group: policy + kind: PolicyException + path: github.com/giantswarm/kyverno-policy-operator/api/v1alpha1 + version: v1alpha1 version: "3" diff --git a/api/v1alpha1/groupversion_info.go b/api/v1alpha1/groupversion_info.go new file mode 100644 index 0000000..9920cb8 --- /dev/null +++ b/api/v1alpha1/groupversion_info.go @@ -0,0 +1,36 @@ +/* +Copyright 2023. + +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. +*/ + +// Package v1alpha1 contains API Schema definitions for the policy.giantswarm.io v1alpha1 API group +// +kubebuilder:object:generate=true +// +groupName=policy.giantswarm.io +package v1alpha1 + +import ( + "k8s.io/apimachinery/pkg/runtime/schema" + "sigs.k8s.io/controller-runtime/pkg/scheme" +) + +var ( + // GroupVersion is group version used to register these objects + GroupVersion = schema.GroupVersion{Group: "policy.giantswarm.io", Version: "v1alpha1"} + + // SchemeBuilder is used to add go types to the GroupVersionKind scheme + SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} + + // AddToScheme adds the types in this group-version to the given scheme. + AddToScheme = SchemeBuilder.AddToScheme +) diff --git a/api/v1alpha1/policyexception_types.go b/api/v1alpha1/policyexception_types.go new file mode 100644 index 0000000..eaafc70 --- /dev/null +++ b/api/v1alpha1/policyexception_types.go @@ -0,0 +1,62 @@ +/* +Copyright 2023. + +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. +*/ + +package v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// PolicyExceptionSpec defines the desired state of PolicyException +type PolicyExceptionSpec struct { + // Policies defines the list of policies to be excluded + Policies []string `json:"policies"` + + // Targes defines the list of target workloads where the exceptions will be applied + Targets []Target `json:"targets"` +} + +//+kubebuilder:object:root=true +//+kubebuilder:resource:shortName=gspolex +//+kubebuilder:subresource:status + +// PolicyException is the Schema for the policyexceptions API +type PolicyException struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec PolicyExceptionSpec `json:"spec,omitempty"` +} + +// Target defines a resource to which a PolicyException applies +type Target struct { + Namespaces []string `json:"namespaces"` + Names []string `json:"names"` + Kind string `json:"kind"` +} + +//+kubebuilder:object:root=true + +// PolicyExceptionList contains a list of PolicyException +type PolicyExceptionList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []PolicyException `json:"items"` +} + +func init() { + SchemeBuilder.Register(&PolicyException{}, &PolicyExceptionList{}) +} diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go new file mode 100644 index 0000000..a50f0e1 --- /dev/null +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -0,0 +1,136 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +/* +Copyright 2023. + +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 controller-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + runtime "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PolicyException) DeepCopyInto(out *PolicyException) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PolicyException. +func (in *PolicyException) DeepCopy() *PolicyException { + if in == nil { + return nil + } + out := new(PolicyException) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *PolicyException) 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 *PolicyExceptionList) DeepCopyInto(out *PolicyExceptionList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]PolicyException, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PolicyExceptionList. +func (in *PolicyExceptionList) DeepCopy() *PolicyExceptionList { + if in == nil { + return nil + } + out := new(PolicyExceptionList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *PolicyExceptionList) 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 *PolicyExceptionSpec) DeepCopyInto(out *PolicyExceptionSpec) { + *out = *in + if in.Policies != nil { + in, out := &in.Policies, &out.Policies + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Targets != nil { + in, out := &in.Targets, &out.Targets + *out = make([]Target, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PolicyExceptionSpec. +func (in *PolicyExceptionSpec) DeepCopy() *PolicyExceptionSpec { + if in == nil { + return nil + } + out := new(PolicyExceptionSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Target) DeepCopyInto(out *Target) { + *out = *in + if in.Namespaces != nil { + in, out := &in.Namespaces, &out.Namespaces + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Names != nil { + in, out := &in.Names, &out.Names + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Target. +func (in *Target) DeepCopy() *Target { + if in == nil { + return nil + } + out := new(Target) + in.DeepCopyInto(out) + return out +} diff --git a/config/crd/bases/policy.giantswarm.io_policyexceptions.yaml b/config/crd/bases/policy.giantswarm.io_policyexceptions.yaml new file mode 100644 index 0000000..07095e7 --- /dev/null +++ b/config/crd/bases/policy.giantswarm.io_policyexceptions.yaml @@ -0,0 +1,75 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.12.0 + name: policyexceptions.policy.giantswarm.io +spec: + group: policy.giantswarm.io + names: + kind: PolicyException + listKind: PolicyExceptionList + plural: policyexceptions + shortNames: + - gspolex + singular: policyexception + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: PolicyException is the Schema for the policyexceptions API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: PolicyExceptionSpec defines the desired state of PolicyException + properties: + policies: + description: Policies defines the list of policies to be excluded + items: + type: string + type: array + targets: + description: Targes defines the list of target workloads where the + exceptions will be applied + items: + description: Target defines a resource to which a PolicyException + applies + properties: + kind: + type: string + names: + items: + type: string + type: array + namespaces: + items: + type: string + type: array + required: + - kind + - names + - namespaces + type: object + type: array + required: + - policies + - targets + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/config/default/kustomization.yaml b/config/default/kustomization.yaml deleted file mode 100644 index 6f67e3a..0000000 --- a/config/default/kustomization.yaml +++ /dev/null @@ -1,144 +0,0 @@ -# Adds namespace to all resources. -namespace: kyverno-policy-operator-system - -# Value of this field is prepended to the -# names of all resources, e.g. a deployment named -# "wordpress" becomes "alices-wordpress". -# Note that it should also match with the prefix (text before '-') of the namespace -# field above. -namePrefix: kyverno-policy-operator- - -# Labels to add to all resources and selectors. -#labels: -#- includeSelectors: true -# pairs: -# someName: someValue - -resources: -- ../crd -- ../rbac -- ../manager -# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in -# crd/kustomization.yaml -#- ../webhook -# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'. 'WEBHOOK' components are required. -#- ../certmanager -# [PROMETHEUS] To enable prometheus monitor, uncomment all sections with 'PROMETHEUS'. -#- ../prometheus - -patchesStrategicMerge: -# Protect the /metrics endpoint by putting it behind auth. -# If you want your controller-manager to expose the /metrics -# endpoint w/o any authn/z, please comment the following line. -- manager_auth_proxy_patch.yaml - - - -# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in -# crd/kustomization.yaml -#- manager_webhook_patch.yaml - -# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'. -# Uncomment 'CERTMANAGER' sections in crd/kustomization.yaml to enable the CA injection in the admission webhooks. -# 'CERTMANAGER' needs to be enabled to use ca injection -#- webhookcainjection_patch.yaml - -# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER' prefix. -# Uncomment the following replacements to add the cert-manager CA injection annotations -#replacements: -# - source: # Add cert-manager annotation to ValidatingWebhookConfiguration, MutatingWebhookConfiguration and CRDs -# kind: Certificate -# group: cert-manager.io -# version: v1 -# name: serving-cert # this name should match the one in certificate.yaml -# fieldPath: .metadata.namespace # namespace of the certificate CR -# targets: -# - select: -# kind: ValidatingWebhookConfiguration -# fieldPaths: -# - .metadata.annotations.[cert-manager.io/inject-ca-from] -# options: -# delimiter: '/' -# index: 0 -# create: true -# - select: -# kind: MutatingWebhookConfiguration -# fieldPaths: -# - .metadata.annotations.[cert-manager.io/inject-ca-from] -# options: -# delimiter: '/' -# index: 0 -# create: true -# - select: -# kind: CustomResourceDefinition -# fieldPaths: -# - .metadata.annotations.[cert-manager.io/inject-ca-from] -# options: -# delimiter: '/' -# index: 0 -# create: true -# - source: -# kind: Certificate -# group: cert-manager.io -# version: v1 -# name: serving-cert # this name should match the one in certificate.yaml -# fieldPath: .metadata.name -# targets: -# - select: -# kind: ValidatingWebhookConfiguration -# fieldPaths: -# - .metadata.annotations.[cert-manager.io/inject-ca-from] -# options: -# delimiter: '/' -# index: 1 -# create: true -# - select: -# kind: MutatingWebhookConfiguration -# fieldPaths: -# - .metadata.annotations.[cert-manager.io/inject-ca-from] -# options: -# delimiter: '/' -# index: 1 -# create: true -# - select: -# kind: CustomResourceDefinition -# fieldPaths: -# - .metadata.annotations.[cert-manager.io/inject-ca-from] -# options: -# delimiter: '/' -# index: 1 -# create: true -# - source: # Add cert-manager annotation to the webhook Service -# kind: Service -# version: v1 -# name: webhook-service -# fieldPath: .metadata.name # namespace of the service -# targets: -# - select: -# kind: Certificate -# group: cert-manager.io -# version: v1 -# fieldPaths: -# - .spec.dnsNames.0 -# - .spec.dnsNames.1 -# options: -# delimiter: '.' -# index: 0 -# create: true -# - source: -# kind: Service -# version: v1 -# name: webhook-service -# fieldPath: .metadata.namespace # namespace of the service -# targets: -# - select: -# kind: Certificate -# group: cert-manager.io -# version: v1 -# fieldPaths: -# - .spec.dnsNames.0 -# - .spec.dnsNames.1 -# options: -# delimiter: '.' -# index: 1 -# create: true diff --git a/config/default/manager_auth_proxy_patch.yaml b/config/default/manager_auth_proxy_patch.yaml deleted file mode 100644 index 73fad2a..0000000 --- a/config/default/manager_auth_proxy_patch.yaml +++ /dev/null @@ -1,39 +0,0 @@ -# This patch inject a sidecar container which is a HTTP proxy for the -# controller manager, it performs RBAC authorization against the Kubernetes API using SubjectAccessReviews. -apiVersion: apps/v1 -kind: Deployment -metadata: - name: controller-manager - namespace: system -spec: - template: - spec: - containers: - - name: kube-rbac-proxy - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - "ALL" - image: gcr.io/kubebuilder/kube-rbac-proxy:v0.14.1 - args: - - "--secure-listen-address=0.0.0.0:8443" - - "--upstream=http://127.0.0.1:8080/" - - "--logtostderr=true" - - "--v=0" - ports: - - containerPort: 8443 - protocol: TCP - name: https - resources: - limits: - cpu: 500m - memory: 128Mi - requests: - cpu: 5m - memory: 64Mi - - name: manager - args: - - "--health-probe-bind-address=:8081" - - "--metrics-bind-address=127.0.0.1:8080" - - "--leader-elect" diff --git a/config/default/manager_config_patch.yaml b/config/default/manager_config_patch.yaml deleted file mode 100644 index f6f5891..0000000 --- a/config/default/manager_config_patch.yaml +++ /dev/null @@ -1,10 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: controller-manager - namespace: system -spec: - template: - spec: - containers: - - name: manager diff --git a/config/manager/kustomization.yaml b/config/manager/kustomization.yaml deleted file mode 100644 index 5c5f0b8..0000000 --- a/config/manager/kustomization.yaml +++ /dev/null @@ -1,2 +0,0 @@ -resources: -- manager.yaml diff --git a/config/manager/manager.yaml b/config/manager/manager.yaml deleted file mode 100644 index 123159e..0000000 --- a/config/manager/manager.yaml +++ /dev/null @@ -1,102 +0,0 @@ -apiVersion: v1 -kind: Namespace -metadata: - labels: - control-plane: controller-manager - app.kubernetes.io/name: namespace - app.kubernetes.io/instance: system - app.kubernetes.io/component: manager - app.kubernetes.io/created-by: kyverno-policy-operator - app.kubernetes.io/part-of: kyverno-policy-operator - app.kubernetes.io/managed-by: kustomize - name: system ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: controller-manager - namespace: system - labels: - control-plane: controller-manager - app.kubernetes.io/name: deployment - app.kubernetes.io/instance: controller-manager - app.kubernetes.io/component: manager - app.kubernetes.io/created-by: kyverno-policy-operator - app.kubernetes.io/part-of: kyverno-policy-operator - app.kubernetes.io/managed-by: kustomize -spec: - selector: - matchLabels: - control-plane: controller-manager - replicas: 1 - template: - metadata: - annotations: - kubectl.kubernetes.io/default-container: manager - labels: - control-plane: controller-manager - spec: - # TODO(user): Uncomment the following code to configure the nodeAffinity expression - # according to the platforms which are supported by your solution. - # It is considered best practice to support multiple architectures. You can - # build your manager image using the makefile target docker-buildx. - # affinity: - # nodeAffinity: - # requiredDuringSchedulingIgnoredDuringExecution: - # nodeSelectorTerms: - # - matchExpressions: - # - key: kubernetes.io/arch - # operator: In - # values: - # - amd64 - # - arm64 - # - ppc64le - # - s390x - # - key: kubernetes.io/os - # operator: In - # values: - # - linux - securityContext: - runAsNonRoot: true - # TODO(user): For common cases that do not require escalating privileges - # it is recommended to ensure that all your Pods/Containers are restrictive. - # More info: https://kubernetes.io/docs/concepts/security/pod-security-standards/#restricted - # Please uncomment the following code if your project does NOT have to work on old Kubernetes - # versions < 1.19 or on vendors versions which do NOT support this field by default (i.e. Openshift < 4.11 ). - # seccompProfile: - # type: RuntimeDefault - containers: - - command: - - /manager - args: - - --leader-elect - image: controller:latest - name: manager - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - "ALL" - livenessProbe: - httpGet: - path: /healthz - port: 8081 - initialDelaySeconds: 15 - periodSeconds: 20 - readinessProbe: - httpGet: - path: /readyz - port: 8081 - initialDelaySeconds: 5 - periodSeconds: 10 - # TODO(user): Configure the resources accordingly based on the project requirements. - # More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ - resources: - limits: - cpu: 500m - memory: 128Mi - requests: - cpu: 10m - memory: 64Mi - serviceAccountName: controller-manager - terminationGracePeriodSeconds: 10 diff --git a/config/prometheus/kustomization.yaml b/config/prometheus/kustomization.yaml deleted file mode 100644 index ed13716..0000000 --- a/config/prometheus/kustomization.yaml +++ /dev/null @@ -1,2 +0,0 @@ -resources: -- monitor.yaml diff --git a/config/prometheus/monitor.yaml b/config/prometheus/monitor.yaml deleted file mode 100644 index 0858c78..0000000 --- a/config/prometheus/monitor.yaml +++ /dev/null @@ -1,26 +0,0 @@ - -# Prometheus Monitor Service (Metrics) -apiVersion: monitoring.coreos.com/v1 -kind: ServiceMonitor -metadata: - labels: - control-plane: controller-manager - app.kubernetes.io/name: servicemonitor - app.kubernetes.io/instance: controller-manager-metrics-monitor - app.kubernetes.io/component: metrics - app.kubernetes.io/created-by: kyverno-policy-operator - app.kubernetes.io/part-of: kyverno-policy-operator - app.kubernetes.io/managed-by: kustomize - name: controller-manager-metrics-monitor - namespace: system -spec: - endpoints: - - path: /metrics - port: https - scheme: https - bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token - tlsConfig: - insecureSkipVerify: true - selector: - matchLabels: - control-plane: controller-manager diff --git a/config/rbac/auth_proxy_client_clusterrole.yaml b/config/rbac/auth_proxy_client_clusterrole.yaml deleted file mode 100644 index fe77c9e..0000000 --- a/config/rbac/auth_proxy_client_clusterrole.yaml +++ /dev/null @@ -1,16 +0,0 @@ -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - labels: - app.kubernetes.io/name: clusterrole - app.kubernetes.io/instance: metrics-reader - app.kubernetes.io/component: kube-rbac-proxy - app.kubernetes.io/created-by: kyverno-policy-operator - app.kubernetes.io/part-of: kyverno-policy-operator - app.kubernetes.io/managed-by: kustomize - name: metrics-reader -rules: -- nonResourceURLs: - - "/metrics" - verbs: - - get diff --git a/config/rbac/auth_proxy_role.yaml b/config/rbac/auth_proxy_role.yaml deleted file mode 100644 index daf709e..0000000 --- a/config/rbac/auth_proxy_role.yaml +++ /dev/null @@ -1,24 +0,0 @@ -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - labels: - app.kubernetes.io/name: clusterrole - app.kubernetes.io/instance: proxy-role - app.kubernetes.io/component: kube-rbac-proxy - app.kubernetes.io/created-by: kyverno-policy-operator - app.kubernetes.io/part-of: kyverno-policy-operator - app.kubernetes.io/managed-by: kustomize - name: proxy-role -rules: -- apiGroups: - - authentication.k8s.io - resources: - - tokenreviews - verbs: - - create -- apiGroups: - - authorization.k8s.io - resources: - - subjectaccessreviews - verbs: - - create diff --git a/config/rbac/auth_proxy_role_binding.yaml b/config/rbac/auth_proxy_role_binding.yaml deleted file mode 100644 index dbfdd63..0000000 --- a/config/rbac/auth_proxy_role_binding.yaml +++ /dev/null @@ -1,19 +0,0 @@ -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - labels: - app.kubernetes.io/name: clusterrolebinding - app.kubernetes.io/instance: proxy-rolebinding - app.kubernetes.io/component: kube-rbac-proxy - app.kubernetes.io/created-by: kyverno-policy-operator - app.kubernetes.io/part-of: kyverno-policy-operator - app.kubernetes.io/managed-by: kustomize - name: proxy-rolebinding -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: proxy-role -subjects: -- kind: ServiceAccount - name: controller-manager - namespace: system diff --git a/config/rbac/auth_proxy_service.yaml b/config/rbac/auth_proxy_service.yaml deleted file mode 100644 index e03ecd1..0000000 --- a/config/rbac/auth_proxy_service.yaml +++ /dev/null @@ -1,21 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - labels: - control-plane: controller-manager - app.kubernetes.io/name: service - app.kubernetes.io/instance: controller-manager-metrics-service - app.kubernetes.io/component: kube-rbac-proxy - app.kubernetes.io/created-by: kyverno-policy-operator - app.kubernetes.io/part-of: kyverno-policy-operator - app.kubernetes.io/managed-by: kustomize - name: controller-manager-metrics-service - namespace: system -spec: - ports: - - name: https - port: 8443 - protocol: TCP - targetPort: https - selector: - control-plane: controller-manager diff --git a/config/rbac/kustomization.yaml b/config/rbac/kustomization.yaml deleted file mode 100644 index 731832a..0000000 --- a/config/rbac/kustomization.yaml +++ /dev/null @@ -1,18 +0,0 @@ -resources: -# All RBAC will be applied under this service account in -# the deployment namespace. You may comment out this resource -# if your manager will use a service account that exists at -# runtime. Be sure to update RoleBinding and ClusterRoleBinding -# subjects if changing service account names. -- service_account.yaml -- role.yaml -- role_binding.yaml -- leader_election_role.yaml -- leader_election_role_binding.yaml -# Comment the following 4 lines if you want to disable -# the auth proxy (https://github.com/brancz/kube-rbac-proxy) -# which protects your /metrics endpoint. -- auth_proxy_service.yaml -- auth_proxy_role.yaml -- auth_proxy_role_binding.yaml -- auth_proxy_client_clusterrole.yaml diff --git a/config/rbac/leader_election_role.yaml b/config/rbac/leader_election_role.yaml deleted file mode 100644 index 799d0dd..0000000 --- a/config/rbac/leader_election_role.yaml +++ /dev/null @@ -1,44 +0,0 @@ -# permissions to do leader election. -apiVersion: rbac.authorization.k8s.io/v1 -kind: Role -metadata: - labels: - app.kubernetes.io/name: role - app.kubernetes.io/instance: leader-election-role - app.kubernetes.io/component: rbac - app.kubernetes.io/created-by: kyverno-policy-operator - app.kubernetes.io/part-of: kyverno-policy-operator - app.kubernetes.io/managed-by: kustomize - name: leader-election-role -rules: -- apiGroups: - - "" - resources: - - configmaps - verbs: - - get - - list - - watch - - create - - update - - patch - - delete -- apiGroups: - - coordination.k8s.io - resources: - - leases - verbs: - - get - - list - - watch - - create - - update - - patch - - delete -- apiGroups: - - "" - resources: - - events - verbs: - - create - - patch diff --git a/config/rbac/leader_election_role_binding.yaml b/config/rbac/leader_election_role_binding.yaml deleted file mode 100644 index d4b24f0..0000000 --- a/config/rbac/leader_election_role_binding.yaml +++ /dev/null @@ -1,19 +0,0 @@ -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - labels: - app.kubernetes.io/name: rolebinding - app.kubernetes.io/instance: leader-election-rolebinding - app.kubernetes.io/component: rbac - app.kubernetes.io/created-by: kyverno-policy-operator - app.kubernetes.io/part-of: kyverno-policy-operator - app.kubernetes.io/managed-by: kustomize - name: leader-election-rolebinding -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: Role - name: leader-election-role -subjects: -- kind: ServiceAccount - name: controller-manager - namespace: system diff --git a/config/rbac/role_binding.yaml b/config/rbac/role_binding.yaml deleted file mode 100644 index 2f57c8d..0000000 --- a/config/rbac/role_binding.yaml +++ /dev/null @@ -1,19 +0,0 @@ -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - labels: - app.kubernetes.io/name: clusterrolebinding - app.kubernetes.io/instance: manager-rolebinding - app.kubernetes.io/component: rbac - app.kubernetes.io/created-by: kyverno-policy-operator - app.kubernetes.io/part-of: kyverno-policy-operator - app.kubernetes.io/managed-by: kustomize - name: manager-rolebinding -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: manager-role -subjects: -- kind: ServiceAccount - name: controller-manager - namespace: system diff --git a/config/rbac/service_account.yaml b/config/rbac/service_account.yaml deleted file mode 100644 index e68734c..0000000 --- a/config/rbac/service_account.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: v1 -kind: ServiceAccount -metadata: - labels: - app.kubernetes.io/name: serviceaccount - app.kubernetes.io/instance: controller-manager-sa - app.kubernetes.io/component: rbac - app.kubernetes.io/created-by: kyverno-policy-operator - app.kubernetes.io/part-of: kyverno-policy-operator - app.kubernetes.io/managed-by: kustomize - name: controller-manager - namespace: system diff --git a/go.mod b/go.mod index 64801fa..01492b4 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,6 @@ module github.com/giantswarm/kyverno-policy-operator go 1.20 require ( - github.com/giantswarm/exception-recommender v0.0.0-20230915173943-4e62cdb03869 github.com/go-logr/logr v1.2.4 github.com/kyverno/kyverno v1.10.3 k8s.io/apimachinery v0.28.2 @@ -170,6 +169,8 @@ require ( github.com/oklog/ulid v1.3.1 // indirect github.com/oleiade/reflections v1.0.1 // indirect github.com/oliveagle/jsonpath v0.0.0-20180606110733-2e52cf6e6852 // indirect + github.com/onsi/ginkgo/v2 v2.11.0 // indirect + github.com/onsi/gomega v1.27.10 // indirect github.com/open-policy-agent/gatekeeper v0.0.0-20210824170141-dd97b8a7e966 // indirect github.com/open-policy-agent/opa v0.56.0 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect @@ -180,10 +181,10 @@ require ( github.com/pelletier/go-toml/v2 v2.1.0 // indirect github.com/philhofer/fwd v1.1.2 // indirect github.com/pkg/errors v0.9.1 // indirect - github.com/prometheus/client_golang v1.16.0 // indirect - github.com/prometheus/client_model v0.4.0 // indirect + github.com/prometheus/client_golang v1.17.0 // indirect + github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16 // indirect github.com/prometheus/common v0.44.0 // indirect - github.com/prometheus/procfs v0.11.1 // indirect + github.com/prometheus/procfs v0.12.0 // indirect github.com/protocolbuffers/txtpbfmt v0.0.0-20230830074515-d9d085e6be90 // indirect github.com/puzpuzpuz/xsync/v2 v2.5.0 // indirect github.com/r3labs/diff v1.1.0 // indirect @@ -267,7 +268,7 @@ require ( k8s.io/apiextensions-apiserver v0.28.2 // indirect k8s.io/component-base v0.28.2 // indirect k8s.io/klog/v2 v2.100.1 // indirect - k8s.io/kube-openapi v0.0.0-20230905202853-d090da108d2f // indirect + k8s.io/kube-openapi v0.0.0-20230928205116-a78145627833 // indirect k8s.io/kubectl v0.28.2 // indirect k8s.io/pod-security-admission v0.28.2 // indirect k8s.io/utils v0.0.0-20230726121419-3b25d923346b // indirect diff --git a/go.sum b/go.sum index ab68502..c2b4198 100644 --- a/go.sum +++ b/go.sum @@ -427,8 +427,6 @@ github.com/ghodss/yaml v0.0.0-20180820084758-c7ce16629ff4/go.mod h1:4dBDuWmgqj2H github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32 h1:Mn26/9ZMNWSw9C9ERFA1PUxfmGpolnw2v0bKOREu5ew= github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32/go.mod h1:GIjDIg/heH5DOkXY3YJ/wNhfHsQHoXGjl8G8amsYQ1I= -github.com/giantswarm/exception-recommender v0.0.0-20230915173943-4e62cdb03869 h1:QIkuctY0lZmZip24wekfMxd2eBxss1PctcVntALTg9w= -github.com/giantswarm/exception-recommender v0.0.0-20230915173943-4e62cdb03869/go.mod h1:KD6b9LtO1mYFHSLmNZJ780Ep0Dgv2brtzKQhHnYEopI= github.com/go-chi/chi v4.1.2+incompatible h1:fGFk2Gmi/YKXk0OmGfBh0WgmN3XB8lVnEyNz34tQRec= github.com/go-chi/chi v4.1.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= github.com/go-critic/go-critic v0.5.6/go.mod h1:cVjj0DfqewQVIlIAGexPCaGaZDAqGE29PYDDADIVNEo= @@ -1029,6 +1027,7 @@ github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= github.com/onsi/ginkgo/v2 v2.11.0 h1:WgqUCUt/lT6yXoQ8Wef0fsNn5cAuMK7+KT9UFRz2tcU= +github.com/onsi/ginkgo/v2 v2.11.0/go.mod h1:ZhrRA5XmEE3x3rhlzamx/JJvujdZoJ2uvgI7kR0iZvM= github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= 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= @@ -1039,6 +1038,7 @@ github.com/onsi/gomega v1.10.5/go.mod h1:gza4q3jKQJijlu05nKWRCW/GavJumGt8aNRxWg7 github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= +github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= github.com/open-policy-agent/cert-controller v0.2.0/go.mod h1:SWS7Ame8oKHF11cDsQCFlULrrOMV5Z59FIGEAF/M6YI= github.com/open-policy-agent/frameworks/constraint v0.0.0-20210701194838-1dbe2618668d/go.mod h1:y8wOVfZ6+bEmbhBMnLnFlQrJB9eQpVk+dIDa7YrtocI= @@ -1107,16 +1107,16 @@ github.com/prometheus/client_golang v1.6.0/go.mod h1:ZLOG9ck3JLRdB5MgO8f+lLTe83A github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/client_golang v1.9.0/go.mod h1:FqZLKOZnGdFAhOK4nqGHa7D66IdsO+O441Eve7ptJDU= github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= -github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= +github.com/prometheus/client_golang v1.17.0 h1:rl2sfwZMtSthVU752MqfjQozy7blglC+1SOtjMAMh+Q= +github.com/prometheus/client_golang v1.17.0/go.mod h1:VeL+gMmOAxkS2IqfCq0ZmHSL+LjWfWDUmp1mBz9JgUY= 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/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= -github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= +github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16 h1:v7DLqVdK4VrYkVD5diGdl4sxJurKJEMnODWRJlxV9oM= +github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= github.com/prometheus/common v0.0.0-20181020173914-7e9e6cabbd39/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= @@ -1142,8 +1142,8 @@ github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4O github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.7.1/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.11.1 h1:xRC8Iq1yyca5ypa9n1EZnWZkt7dwcoRPQwX/5gwaUuI= -github.com/prometheus/procfs v0.11.1/go.mod h1:eesXgaPo1q7lBpVMoMy0ZOFTth9hBn4W/y0/p/ScXhY= +github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= +github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= github.com/prometheus/statsd_exporter v0.20.0/go.mod h1:YL3FWCG8JBBtaUSxAg4Gz2ZYu22bS84XM89ZQXXTWmQ= github.com/prometheus/statsd_exporter v0.21.0/go.mod h1:rbT83sZq2V+p73lHhPZfMc3MLCHmSHelCh9hSGYNLTQ= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= @@ -2106,8 +2106,8 @@ k8s.io/klog/v2 v2.100.1 h1:7WCHKK6K8fNhTqfBhISHQ97KrnJNFZMcQvKp7gP/tmg= k8s.io/klog/v2 v2.100.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAGcJo0Tvi+dK12EcqSLqcWsryKMpfM= k8s.io/kube-openapi v0.0.0-20210305001622-591a79e4bda7/go.mod h1:wXW5VT87nVfh/iLV8FpR2uDvrFyomxbtb1KivDbvPTE= -k8s.io/kube-openapi v0.0.0-20230905202853-d090da108d2f h1:eeEUOoGYWhOz7EyXqhlR2zHKNw2mNJ9vzJmub6YN6kk= -k8s.io/kube-openapi v0.0.0-20230905202853-d090da108d2f/go.mod h1:AsvuZPBlUDVuCdzJ87iajxtXuR9oktsTctW/R9wwouA= +k8s.io/kube-openapi v0.0.0-20230928205116-a78145627833 h1:iFFEmmB7szQhJP42AvRD2+gzdVP7EuIKY1rJgxf0JZY= +k8s.io/kube-openapi v0.0.0-20230928205116-a78145627833/go.mod h1:AsvuZPBlUDVuCdzJ87iajxtXuR9oktsTctW/R9wwouA= k8s.io/kubectl v0.28.2 h1:fOWOtU6S0smdNjG1PB9WFbqEIMlkzU5ahyHkc7ESHgM= k8s.io/kubectl v0.28.2/go.mod h1:6EQWTPySF1fn7yKoQZHYf9TPwIl2AygHEcJoxFekr64= k8s.io/pod-security-admission v0.28.2 h1:3kiOL+gc6auNTGHuQ0hVsGxYu2YO/7DZb0xYR84GxiQ= diff --git a/internal/controller/policyexception_controller.go b/internal/controller/policyexception_controller.go new file mode 100644 index 0000000..42417cf --- /dev/null +++ b/internal/controller/policyexception_controller.go @@ -0,0 +1,240 @@ +/* +Copyright 2023. + +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. +*/ + +package controller + +import ( + "context" + "fmt" + + apierrors "k8s.io/apimachinery/pkg/api/errors" + + kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1" + kyvernov2alpha1 "github.com/kyverno/kyverno/api/kyverno/v2alpha1" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + + giantswarmPolicy "github.com/giantswarm/kyverno-policy-operator/api/v1alpha1" + + "github.com/go-logr/logr" + "k8s.io/apimachinery/pkg/runtime" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/log" +) + +// PolicyExceptionReconciler reconciles a PolicyException object +type PolicyExceptionReconciler struct { + client.Client + Scheme *runtime.Scheme + Log logr.Logger + DestinationNamespace string +} + +//+kubebuilder:rbac:groups=policy.giantswarm.io,resources=policyexceptions,verbs=get;list;watch;create;update;patch;delete +//+kubebuilder:rbac:groups=policy.giantswarm.io,resources=policyexceptions/status,verbs=get;update;patch +//+kubebuilder:rbac:groups=policy.giantswarm.io,resources=policyexceptions/finalizers,verbs=update + +func (r *PolicyExceptionReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + _ = log.FromContext(ctx) + _ = r.Log.WithValues("policyexception", req.NamespacedName) + + var gsPolicyException giantswarmPolicy.PolicyException + // This should be a flag + background := false + + if err := r.Get(ctx, req.NamespacedName, &gsPolicyException); err != nil { + // Error fetching the report + + // Check if the PolicyException was deleted + if apierrors.IsNotFound(err) { + // Ignore + return ctrl.Result{}, nil + } + + log.Log.Error(err, "unable to fetch PolicyException") + return ctrl.Result{}, client.IgnoreNotFound(err) + } + + // Define namespace + var namespace string + if r.DestinationNamespace == "" { + namespace = gsPolicyException.Namespace + } else { + namespace = r.DestinationNamespace + } + + // Create Kyverno exception + // Create a policy map for storing cluster policies to extract rules later + policyMap := make(map[string]kyvernov1.ClusterPolicy) + for _, policy := range gsPolicyException.Spec.Policies { + var kyvernoPolicy kyvernov1.ClusterPolicy + if err := r.Get(ctx, types.NamespacedName{Namespace: "", Name: policy}, &kyvernoPolicy); err != nil { + // Error fetching the report + log.Log.Error(err, "unable to fetch Kyverno Policy") + return ctrl.Result{}, client.IgnoreNotFound(err) + } + + policyMap[policy] = kyvernoPolicy + } + // Translate GiantSwarm PolicyException to Kyverno's PolicyException schema + policyException := kyvernov2alpha1.PolicyException{} + // Set namespace + policyException.Namespace = namespace + // Set name + policyException.Name = gsPolicyException.Name + // Set Background behaviour + policyException.Spec.Background = &background + // Set labels + policyException.Labels = make(map[string]string) + policyException.Labels["app.kubernetes.io/managed-by"] = "kyverno-policy-operator" + // Set ownerReferences + if err := controllerutil.SetControllerReference(&gsPolicyException, &policyException, r.Scheme); err != nil { + return ctrl.Result{}, err + } + + // Create PolicyException + if op, err := controllerutil.CreateOrUpdate(ctx, r.Client, &policyException, func() error { + + // Set .Spec.Match.Any targets + policyException.Spec.Match.Any = translateTargetsToResourceFilters(gsPolicyException) + + // Set .Spec.Exceptions + newExceptions := translatePoliciesToExceptions(policyMap) + if !deepEquals(policyException.Spec.Exceptions, newExceptions) { + policyException.Spec.Exceptions = newExceptions + } + + return nil + }); err != nil { + log.Log.Error(err, fmt.Sprintf("Reconciliation failed for PolicyException %s", policyException.Name)) + return ctrl.Result{}, err + } else { + log.Log.Info(fmt.Sprintf("PolicyException %s: %s", policyException.Name, op)) + } + + return ctrl.Result{}, nil +} + +func deepEquals(got []kyvernov2alpha1.Exception, want []kyvernov2alpha1.Exception) bool { + // Check Length size first + if len(got) != len(want) { + return false + } + // Create an exceptions map with the new desired Exceptions + exceptionMap := make(map[string][]string) + for _, exception := range want { + exceptionMap[exception.PolicyName] = exception.RuleNames + } + for _, exception := range got { + // Check if the Policy Name is still present in the new Exceptions + if _, exists := exceptionMap[exception.PolicyName]; !exists { + // The Policy is not present in the new array + // Arrays are not equals, exit + return false + } else { + // Check if the same RuleNames are still present in the new Exceptions + for _, oldRule := range exception.RuleNames { + found := false + // Check against every rule, exit if found + for _, newRule := range exceptionMap[exception.PolicyName] { + if newRule == oldRule { + // Found, break for + found = true + break + } + } + if !found { + // The arrays are not equals, exit + return false + } + // Rules are equals, continue + } + } + } + // Arrays are equals + return true +} + +func translateTargetsToResourceFilters(polex giantswarmPolicy.PolicyException) kyvernov1.ResourceFilters { + resourceFilters := kyvernov1.ResourceFilters{} + for _, target := range polex.Spec.Targets { + trasnlatedResourceFilter := kyvernov1.ResourceFilter{ + ResourceDescription: kyvernov1.ResourceDescription{ + Namespaces: target.Namespaces, + Names: target.Names, + Kinds: generateExceptionKinds(target.Kind), + }, + } + resourceFilters = append(resourceFilters, trasnlatedResourceFilter) + } + return resourceFilters +} + +// generateKinds creates the subresources necessary for top level controllers like Deployment or StatefulSet +func generateExceptionKinds(resourceKind string) []string { + // Adds the subresources to the exception list for each Kind + var exceptionKinds []string + exceptionKinds = append(exceptionKinds, resourceKind) + // Append ReplicaSets + if resourceKind == "Deployment" { + exceptionKinds = append(exceptionKinds, "ReplicaSet") + // Append Jobs + } else if resourceKind == "CronJob" { + exceptionKinds = append(exceptionKinds, "Job") + } + // Always append Pods except if they are the initial resource Kind + if resourceKind != "Pod" { + exceptionKinds = append(exceptionKinds, "Pod") + } + + return exceptionKinds +} + +// translatePoliciesToExceptions takes a Giant Swarm Policies array and transforms it into a Kyverno Exception array +func translatePoliciesToExceptions(policies map[string]kyvernov1.ClusterPolicy) []kyvernov2alpha1.Exception { + var exceptionArray []kyvernov2alpha1.Exception + for policyName, kyvernoPolicy := range policies { + kyvernoException := kyvernov2alpha1.Exception{ + PolicyName: policyName, + RuleNames: generatePolicyRules(kyvernoPolicy), + } + exceptionArray = append(exceptionArray, kyvernoException) + } + + return exceptionArray +} + +// generatePolicyRules takes a Kyverno Policy name and generates a list of rules owned by that policy +func generatePolicyRules(kyvernoPolicy kyvernov1.ClusterPolicy) []string { + var rulesArray []string + for _, rule := range kyvernoPolicy.Spec.Rules { + rulesArray = append(rulesArray, rule.Name) + } + for _, autogenRule := range kyvernoPolicy.Status.Autogen.Rules { + rulesArray = append(rulesArray, autogenRule.Name) + } + + return rulesArray +} + +// SetupWithManager sets up the controller with the Manager. +func (r *PolicyExceptionReconciler) SetupWithManager(mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). + For(&giantswarmPolicy.PolicyException{}). + Owns(&kyvernov2alpha1.PolicyException{}). + Complete(r) +} diff --git a/internal/controller/policyexceptiondraft_controller.go b/internal/controller/policyexceptiondraft_controller.go deleted file mode 100644 index 59106eb..0000000 --- a/internal/controller/policyexceptiondraft_controller.go +++ /dev/null @@ -1,186 +0,0 @@ -/* -Copyright 2023. - -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. -*/ - -package controller - -import ( - "context" - - "fmt" - - giantswarmExceptions "github.com/giantswarm/exception-recommender/api/v1alpha1" - kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1" - kyvernov2alpha1 "github.com/kyverno/kyverno/api/kyverno/v2alpha1" - apierrors "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/types" - "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" - - "github.com/go-logr/logr" - "k8s.io/apimachinery/pkg/runtime" - ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/log" -) - -// PolicyExceptionDraftReconciler reconciles a PolicyExceptionDraft object -type PolicyExceptionDraftReconciler struct { - client.Client - Scheme *runtime.Scheme - Log logr.Logger - DestinationNamespace string -} - -//+kubebuilder:rbac:groups=policy.giantswarm.io.giantswarm.io,resources=policyexceptiondrafts,verbs=get;list;watch;create;update;patch;delete -//+kubebuilder:rbac:groups=policy.giantswarm.io.giantswarm.io,resources=policyexceptiondrafts/status,verbs=get;update;patch -//+kubebuilder:rbac:groups=policy.giantswarm.io.giantswarm.io,resources=policyexceptiondrafts/finalizers,verbs=update - -func (r *PolicyExceptionDraftReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { - _ = log.FromContext(ctx) - _ = r.Log.WithValues("policyexceptiondraft", req.NamespacedName) - - var exceptionDraft giantswarmExceptions.PolicyExceptionDraft - // This should be a flag - background := false - - if err := r.Get(ctx, req.NamespacedName, &exceptionDraft); err != nil { - // Error fetching the report - log.Log.Error(err, "unable to fetch PolicyExceptionDraft") - return ctrl.Result{}, client.IgnoreNotFound(err) - } - - // Define namespace - var namespace string - if r.DestinationNamespace == "" { - namespace = exceptionDraft.Namespace - } else { - namespace = r.DestinationNamespace - } - - // Check if the draft hast been reconciled before - if _, exists := exceptionDraft.Labels["kyverno-policy-operator/reconciled"]; !exists { - // Label doesn't exist, add it - if exceptionDraft.Labels == nil { - exceptionDraft.Labels = make(map[string]string) - } - exceptionDraft.Labels["kyverno-policy-operator/reconciled"] = "true" - - // Update Kubernetes object - if err := r.Client.Update(ctx, &exceptionDraft, &client.UpdateOptions{}); err != nil { - r.Log.Error(err, "unable to update PolicyExceptionDraft") - } - - // Create Kyverno exception - // Translate GiantSwarm PolicyExceptionDraft to Kyverno's PolicyException schema - policyException := translateDraftToPolex(exceptionDraft) - // Set namespace - policyException.Namespace = namespace - // Set name - policyException.Name = exceptionDraft.Name - // Set labels - policyException.Labels = make(map[string]string) - policyException.Labels["app.kubernetes.io/managed-by"] = "kyverno-policy-operator" - // Set Background behaviour - policyException.Spec.Background = &background - - // Set ownerReferences - if err := controllerutil.SetControllerReference(&exceptionDraft, &policyException, r.Scheme); err != nil { - return ctrl.Result{}, err - } - - // Create PolicyException - if err := r.Create(ctx, &policyException); err != nil { - if apierrors.IsAlreadyExists(err) { - log.Log.Info(fmt.Sprintf("PolicyException %s/%s already exists", namespace, policyException.Name)) - return ctrl.Result{}, nil - } else { - log.Log.Error(err, "unable to create PolicyException") - } - } else { - log.Log.Info(fmt.Sprintf("Created PolicyException %s/%s", namespace, policyException.Name)) - } - } else { - // Exception must exist since draft was previously reconciled - var policyException kyvernov2alpha1.PolicyException - - if err := r.Get(ctx, types.NamespacedName{Namespace: namespace, Name: exceptionDraft.Name}, &policyException); err != nil { - // Error fetching the report - if apierrors.IsNotFound(err) { - // The PolEx doesn't exist, remove the reconciled label from the PolicyExceptionDraft to trigger recreation - delete(exceptionDraft.Labels, "kyverno-policy-operator/reconciled") - - // Update Kubernetes object - if draftErr := r.Client.Update(ctx, &exceptionDraft, &client.UpdateOptions{}); draftErr != nil { - r.Log.Error(draftErr, "unable to update PolicyExceptionDraft") - } - - return ctrl.Result{}, nil - } - log.Log.Error(err, "unable to fetch PolicyException") - return ctrl.Result{}, client.IgnoreNotFound(err) - } - - // Set new exceptions - newExceptions := translateExceptions(exceptionDraft.Spec.Exceptions) - if len(newExceptions) == len(policyException.Spec.Exceptions) { - // We can assume that if the lengths are the same, the exceptions have not changed, but this can be wrong. - // This log message is unnecesary, we can delete it after testing - r.Log.Info("Exceptions are the same, no need to update") - return ctrl.Result{}, nil - } - policyException.Spec.Exceptions = newExceptions - // Update Kubernetes object - if err := r.Client.Update(ctx, &policyException, &client.UpdateOptions{}); err != nil { - r.Log.Error(err, "unable to update PolicyException") - } else { - log.Log.Info(fmt.Sprintf("Updated PolicyException %s/%s", namespace, policyException.Name)) - } - } - - return ctrl.Result{}, nil -} - -// translateDraftToPolex takes a Giant Swarm PolicyExceptionDraft object and transforms it into a Kyverno Policy Exception object -func translateDraftToPolex(draft giantswarmExceptions.PolicyExceptionDraft) kyvernov2alpha1.PolicyException { - polex := kyvernov2alpha1.PolicyException{} - - polex.Spec.Match.All = kyvernov1.ResourceFilters{kyvernov1.ResourceFilter{ - ResourceDescription: kyvernov1.ResourceDescription{ - Namespaces: draft.Spec.Match.Namespaces, - Names: draft.Spec.Match.Names, - Kinds: draft.Spec.Match.Kinds, - }}} - polex.Spec.Exceptions = translateExceptions(draft.Spec.Exceptions) - - return polex -} - -// translateExceptions takes a Giant Swarm Exception array and transforms it into a Kyverno Exception array -func translateExceptions(exceptions []giantswarmExceptions.Exception) []kyvernov2alpha1.Exception { - var kyvernoExceptions []kyvernov2alpha1.Exception - for _, exception := range exceptions { - kyvernoExceptions = append(kyvernoExceptions, kyvernov2alpha1.Exception(exception)) - } - - return kyvernoExceptions -} - -// SetupWithManager sets up the controller with the Manager. -func (r *PolicyExceptionDraftReconciler) SetupWithManager(mgr ctrl.Manager) error { - return ctrl.NewControllerManagedBy(mgr). - For(&giantswarmExceptions.PolicyExceptionDraft{}). - Owns(&kyvernov2alpha1.PolicyException{}). - Complete(r) -} diff --git a/main.go b/main.go index cd3b6de..de0f8a6 100644 --- a/main.go +++ b/main.go @@ -22,7 +22,12 @@ import ( // Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.) // to ensure that exec-entrypoint and run can make use of them. - giantswarmExceptions "github.com/giantswarm/exception-recommender/api/v1alpha1" + + kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1" + + giantswarmPolicy "github.com/giantswarm/kyverno-policy-operator/api/v1alpha1" + "github.com/giantswarm/kyverno-policy-operator/internal/controller" + kyvernov2alpha1 "github.com/kyverno/kyverno/api/kyverno/v2alpha1" _ "k8s.io/client-go/plugin/pkg/client/auth" @@ -33,8 +38,6 @@ import ( ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/healthz" "sigs.k8s.io/controller-runtime/pkg/log/zap" - - "github.com/giantswarm/kyverno-policy-operator/internal/controller" //+kubebuilder:scaffold:imports ) @@ -49,13 +52,13 @@ func init() { setupLog.Error(err, "unable to register kyverno schema") } - err = giantswarmExceptions.AddToScheme(scheme) + err = kyvernov1.AddToScheme(scheme) if err != nil { - setupLog.Error(err, "unable to register giantswarm policy schema") + setupLog.Error(err, "unable to register kyverno schema") } + utilruntime.Must(giantswarmPolicy.AddToScheme(scheme)) utilruntime.Must(clientgoscheme.AddToScheme(scheme)) - //+kubebuilder:scaffold:scheme } @@ -65,7 +68,7 @@ func main() { var probeAddr string var destinationNamespace string // Flags - flag.StringVar(&destinationNamespace, "destination-namespace", "", "The namespace where the PolicyExceptions will be created. Defaults to Drafts namespace.") + flag.StringVar(&destinationNamespace, "destination-namespace", "", "The namespace where the Kyverno PolicyExceptions will be created. Defaults to GS PolicyException namespace.") flag.StringVar(&metricsAddr, "metrics-bind-address", ":8080", "The address the metric endpoint binds to.") flag.StringVar(&probeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.") flag.BoolVar(&enableLeaderElection, "leader-elect", false, @@ -103,12 +106,12 @@ func main() { os.Exit(1) } - if err = (&controller.PolicyExceptionDraftReconciler{ + if err = (&controller.PolicyExceptionReconciler{ Client: mgr.GetClient(), Scheme: mgr.GetScheme(), DestinationNamespace: destinationNamespace, }).SetupWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create controller", "controller", "PolicyExceptionDraft") + setupLog.Error(err, "unable to create controller", "controller", "PolicyException") os.Exit(1) } //+kubebuilder:scaffold:builder