From f4a12be8ce57237dd5bfbde488e731e545ef7e45 Mon Sep 17 00:00:00 2001 From: Venkata Reddy Date: Fri, 9 Jul 2021 06:04:49 +0000 Subject: [PATCH] Added security policy validator in custome controller by [VenkataReddy] --- KubeArmor/core/kubeUpdate.go | 10 +- KubeArmor/types/types.go | 9 +- .../controllers/kubearmor-controller.yaml | 694 +++++++++++++++++ .../kubearmor-host-controller.yaml | 697 ++++++++++++++++++ ...y.kubearmor.com_kubearmorhostpolicies.yaml | 5 + ...urity.kubearmor.com_kubearmorpolicies.yaml | 5 + .../api/v1/kubearmorhostpolicy_types.go | 4 +- ...y.kubearmor.com_kubearmorhostpolicies.yaml | 5 + .../config/manager/kustomization.yaml | 6 + .../config/manager/manager.yaml | 3 +- .../kubearmorhostpolicy_controller.go | 154 +++- .../api/v1/kubearmorpolicy_types.go | 4 +- ...urity.kubearmor.com_kubearmorpolicies.yaml | 5 + .../config/manager/kustomization.yaml | 6 + .../config/manager/manager.yaml | 3 +- .../controllers/kubearmorpolicy_controller.go | 156 +++- 16 files changed, 1745 insertions(+), 21 deletions(-) create mode 100644 deployments/controllers/kubearmor-controller.yaml create mode 100644 deployments/controllers/kubearmor-host-controller.yaml diff --git a/KubeArmor/core/kubeUpdate.go b/KubeArmor/core/kubeUpdate.go index ddb05411b5..7c55f33486 100644 --- a/KubeArmor/core/kubeUpdate.go +++ b/KubeArmor/core/kubeUpdate.go @@ -589,7 +589,7 @@ func (dm *KubeArmorDaemon) GetSecurityPolicies(identities []string) []tp.Securit } // UpdateSecurityPolicy Function -func (dm *KubeArmorDaemon) UpdateSecurityPolicy(action string, secPolicy tp.SecurityPolicy) { +func (dm *KubeArmorDaemon) UpdateSecurityPolicy(action string, secPolicy tp.SecurityPolicy, status string) { dm.ContainerGroupsLock.Lock() defer dm.ContainerGroupsLock.Unlock() @@ -626,7 +626,9 @@ func (dm *KubeArmorDaemon) UpdateSecurityPolicy(action string, secPolicy tp.Secu dm.LogFeeder.UpdateSecurityPolicies("UPDATED", dm.ContainerGroups[idx]) // enforce security policies - dm.RuntimeEnforcer.UpdateSecurityPolicies(dm.ContainerGroups[idx]) + if status == "OK" { + dm.RuntimeEnforcer.UpdateSecurityPolicies(dm.ContainerGroups[idx]) + } } } } @@ -670,7 +672,7 @@ func (dm *KubeArmorDaemon) WatchSecurityPolicies() { if policy.Metadata["policyName"] == secPolicy.Metadata["policyName"] && policy.Metadata["namespaceName"] == secPolicy.Metadata["namespaceName"] && policy.Metadata["generation"] == secPolicy.Metadata["generation"] { - exist = true + //exist = true break } } @@ -1042,7 +1044,7 @@ func (dm *KubeArmorDaemon) WatchSecurityPolicies() { dm.LogFeeder.Printf("Detected a Security Policy (%s/%s/%s)", strings.ToLower(event.Type), secPolicy.Metadata["namespaceName"], secPolicy.Metadata["policyName"]) // apply security policies to containers - dm.UpdateSecurityPolicy(event.Type, secPolicy) + dm.UpdateSecurityPolicy(event.Type, secPolicy, event.Object.Status.PolicyStatus) } } } diff --git a/KubeArmor/types/types.go b/KubeArmor/types/types.go index 9aee902198..9fa06e0680 100644 --- a/KubeArmor/types/types.go +++ b/KubeArmor/types/types.go @@ -92,10 +92,15 @@ type K8sKubeArmorPolicyEvent struct { Object K8sKubeArmorPolicy `json:"object"` } +type SecurityPolicyStatus struct { + PolicyStatus string `json:"status,omitempty"` +} + // K8sKubeArmorPolicy Structure type K8sKubeArmorPolicy struct { - Metadata metav1.ObjectMeta `json:"metadata"` - Spec SecuritySpec `json:"spec"` + Metadata metav1.ObjectMeta `json:"metadata"` + Spec SecuritySpec `json:"spec"` + Status SecurityPolicyStatus `json:"status,omitempty"` } // K8sKubeArmorPolicies Structure diff --git a/deployments/controllers/kubearmor-controller.yaml b/deployments/controllers/kubearmor-controller.yaml new file mode 100644 index 0000000000..5ee16f69e1 --- /dev/null +++ b/deployments/controllers/kubearmor-controller.yaml @@ -0,0 +1,694 @@ +apiVersion: v1 +kind: Namespace +metadata: + labels: + control-plane: controller-manager + name: k8s-system +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.2.5 + creationTimestamp: null + name: kubearmorpolicies.security.kubearmor.com +spec: + group: security.kubearmor.com + names: + kind: KubeArmorPolicy + listKind: KubeArmorPolicyList + plural: kubearmorpolicies + shortNames: + - ksp + singular: kubearmorpolicy + scope: Namespaced + versions: + - name: v1 + schema: + openAPIV3Schema: + description: KubeArmorPolicy is the Schema for the kubearmorpolicies 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: KubeArmorPolicySpec defines the desired state of KubeArmorPolicy + properties: + action: + enum: + - Audit + - Allow + - Block + - AllowWithAudit + - BlockWithAudit + type: string + apparmor: + type: string + capabilities: + properties: + action: + enum: + - Audit + - Allow + - Block + - AllowWithAudit + - BlockWithAudit + type: string + matchCapabilities: + items: + properties: + action: + enum: + - Audit + - Allow + - Block + - AllowWithAudit + - BlockWithAudit + type: string + capability: + pattern: (chown|dac_override|dac_read_search|fowner|fsetid|kill|setgid|setuid|setpcap|linux_immutable|net_bind_service|net_broadcast|net_admin|net_raw|ipc_lock|ipc_owner|sys_module|sys_rawio|sys_chroot|sys_ptrace|sys_pacct|sys_admin|sys_boot|sys_nice|sys_resource|sys_time|sys_tty_config|mknod|lease|audit_write|audit_control|setfcap|mac_override|mac_admin)$ + type: string + fromSource: + items: + properties: + dir: + pattern: ^\/([A-z0-9-_.]+\/)*([A-z0-9-_.]+)+\/$ + type: string + path: + pattern: ^\/([A-z0-9-_.]+\/)*([A-z0-9-_.]+)$ + type: string + recursive: + type: boolean + type: object + type: array + message: + type: string + severity: + maximum: 10 + minimum: 1 + type: integer + tags: + items: + type: string + type: array + required: + - capability + type: object + type: array + message: + type: string + severity: + maximum: 10 + minimum: 1 + type: integer + tags: + items: + type: string + type: array + required: + - matchCapabilities + type: object + file: + properties: + action: + enum: + - Audit + - Allow + - Block + - AllowWithAudit + - BlockWithAudit + type: string + matchDirectories: + items: + properties: + action: + enum: + - Audit + - Allow + - Block + - AllowWithAudit + - BlockWithAudit + type: string + dir: + pattern: ^\/([A-z0-9-_.]+\/)*([A-z0-9-_.]+)+\/$ + type: string + fromSource: + items: + properties: + dir: + pattern: ^\/([A-z0-9-_.]+\/)*([A-z0-9-_.]+)+\/$ + type: string + path: + pattern: ^\/([A-z0-9-_.]+\/)*([A-z0-9-_.]+)$ + type: string + recursive: + type: boolean + type: object + type: array + message: + type: string + ownerOnly: + type: boolean + readOnly: + type: boolean + recursive: + type: boolean + severity: + maximum: 10 + minimum: 1 + type: integer + tags: + items: + type: string + type: array + required: + - dir + type: object + type: array + matchPaths: + items: + properties: + action: + enum: + - Audit + - Allow + - Block + - AllowWithAudit + - BlockWithAudit + type: string + fromSource: + items: + properties: + dir: + pattern: ^\/([A-z0-9-_.]+\/)*([A-z0-9-_.]+)+\/$ + type: string + path: + pattern: ^\/([A-z0-9-_.]+\/)*([A-z0-9-_.]+)$ + type: string + recursive: + type: boolean + type: object + type: array + message: + type: string + ownerOnly: + type: boolean + path: + pattern: ^\/([A-z0-9-_.]+\/)*([A-z0-9-_.]+)$ + type: string + readOnly: + type: boolean + severity: + maximum: 10 + minimum: 1 + type: integer + tags: + items: + type: string + type: array + required: + - path + type: object + type: array + matchPatterns: + items: + properties: + action: + enum: + - Audit + - Allow + - Block + - AllowWithAudit + - BlockWithAudit + type: string + message: + type: string + ownerOnly: + type: boolean + pattern: + type: string + readOnly: + type: boolean + severity: + maximum: 10 + minimum: 1 + type: integer + tags: + items: + type: string + type: array + required: + - pattern + type: object + type: array + message: + type: string + severity: + maximum: 10 + minimum: 1 + type: integer + tags: + items: + type: string + type: array + type: object + message: + type: string + network: + properties: + action: + enum: + - Audit + - Allow + - Block + - AllowWithAudit + - BlockWithAudit + type: string + matchProtocols: + items: + properties: + action: + enum: + - Audit + - Allow + - Block + - AllowWithAudit + - BlockWithAudit + type: string + fromSource: + items: + properties: + dir: + pattern: ^\/([A-z0-9-_.]+\/)*([A-z0-9-_.]+)+\/$ + type: string + path: + pattern: ^\/([A-z0-9-_.]+\/)*([A-z0-9-_.]+)$ + type: string + recursive: + type: boolean + type: object + type: array + message: + type: string + protocol: + pattern: (icmp|ICMP|tcp|TCP|udp|UDP)$ + type: string + severity: + maximum: 10 + minimum: 1 + type: integer + tags: + items: + type: string + type: array + required: + - protocol + type: object + type: array + message: + type: string + severity: + maximum: 10 + minimum: 1 + type: integer + tags: + items: + type: string + type: array + required: + - matchProtocols + type: object + process: + properties: + action: + enum: + - Audit + - Allow + - Block + - AllowWithAudit + - BlockWithAudit + type: string + matchDirectories: + items: + properties: + action: + enum: + - Audit + - Allow + - Block + - AllowWithAudit + - BlockWithAudit + type: string + dir: + pattern: ^\/([A-z0-9-_.]+\/)*([A-z0-9-_.]+)+\/$ + type: string + fromSource: + items: + properties: + dir: + pattern: ^\/([A-z0-9-_.]+\/)*([A-z0-9-_.]+)+\/$ + type: string + path: + pattern: ^\/([A-z0-9-_.]+\/)*([A-z0-9-_.]+)$ + type: string + recursive: + type: boolean + type: object + type: array + message: + type: string + ownerOnly: + type: boolean + recursive: + type: boolean + severity: + maximum: 10 + minimum: 1 + type: integer + tags: + items: + type: string + type: array + required: + - dir + type: object + type: array + matchPaths: + items: + properties: + action: + enum: + - Audit + - Allow + - Block + - AllowWithAudit + - BlockWithAudit + type: string + fromSource: + items: + properties: + dir: + pattern: ^\/([A-z0-9-_.]+\/)*([A-z0-9-_.]+)+\/$ + type: string + path: + pattern: ^\/([A-z0-9-_.]+\/)*([A-z0-9-_.]+)$ + type: string + recursive: + type: boolean + type: object + type: array + message: + type: string + ownerOnly: + type: boolean + path: + pattern: ^\/([A-z0-9-_.]+\/)*([A-z0-9-_.]+)$ + type: string + severity: + maximum: 10 + minimum: 1 + type: integer + tags: + items: + type: string + type: array + required: + - path + type: object + type: array + matchPatterns: + items: + properties: + action: + enum: + - Audit + - Allow + - Block + - AllowWithAudit + - BlockWithAudit + type: string + message: + type: string + ownerOnly: + type: boolean + pattern: + type: string + severity: + maximum: 10 + minimum: 1 + type: integer + tags: + items: + type: string + type: array + required: + - pattern + type: object + type: array + message: + type: string + severity: + maximum: 10 + minimum: 1 + type: integer + tags: + items: + type: string + type: array + type: object + selector: + properties: + matchLabels: + additionalProperties: + type: string + type: object + matchNames: + additionalProperties: + type: string + type: object + type: object + severity: + maximum: 10 + minimum: 1 + type: integer + tags: + items: + type: string + type: array + required: + - selector + type: object + status: + description: KubeArmorPolicyStatus defines the observed state of KubeArmorPolicy + properties: + status: + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: k8s-leader-election-role + namespace: k8s-system +rules: +- apiGroups: + - "" + resources: + - configmaps + verbs: + - get + - list + - watch + - create + - update + - patch + - delete +- apiGroups: + - "" + resources: + - configmaps/status + verbs: + - get + - update + - patch +- apiGroups: + - "" + resources: + - events + verbs: + - create +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + creationTimestamp: null + name: k8s-manager-role +rules: +- apiGroups: + - security.kubearmor.com + resources: + - kubearmorpolicies + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - security.kubearmor.com + resources: + - kubearmorpolicies/status + verbs: + - get + - patch + - update +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: k8s-proxy-role +rules: +- apiGroups: + - authentication.k8s.io + resources: + - tokenreviews + verbs: + - create +- apiGroups: + - authorization.k8s.io + resources: + - subjectaccessreviews + verbs: + - create +--- +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: ClusterRole +metadata: + name: k8s-metrics-reader +rules: +- nonResourceURLs: + - /metrics + verbs: + - get +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: k8s-leader-election-rolebinding + namespace: k8s-system +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: k8s-leader-election-role +subjects: +- kind: ServiceAccount + name: default + namespace: k8s-system +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: k8s-manager-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: k8s-manager-role +subjects: +- kind: ServiceAccount + name: default + namespace: k8s-system +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: k8s-proxy-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: k8s-proxy-role +subjects: +- kind: ServiceAccount + name: default + namespace: k8s-system +--- +apiVersion: v1 +kind: Service +metadata: + labels: + control-plane: controller-manager + name: k8s-controller-manager-metrics-service + namespace: k8s-system +spec: + ports: + - name: https + port: 8443 + targetPort: https + selector: + control-plane: controller-manager +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + control-plane: controller-manager + name: k8s-controller-manager + namespace: k8s-system +spec: + replicas: 1 + selector: + matchLabels: + control-plane: controller-manager + template: + metadata: + labels: + control-plane: controller-manager + spec: + containers: + - args: + - --secure-listen-address=0.0.0.0:8443 + - --upstream=http://127.0.0.1:8080/ + - --logtostderr=true + - --v=10 + image: gcr.io/kubebuilder/kube-rbac-proxy:v0.5.0 + name: kube-rbac-proxy + ports: + - containerPort: 8443 + name: https + - args: + - --metrics-addr=127.0.0.1:8080 + - --enable-leader-election + command: + - /manager + image: kubearmor/kubearmor-controller:latest + imagePullPolicy: Never + name: manager + resources: + limits: + cpu: 100m + memory: 30Mi + requests: + cpu: 100m + memory: 20Mi + terminationGracePeriodSeconds: 10 diff --git a/deployments/controllers/kubearmor-host-controller.yaml b/deployments/controllers/kubearmor-host-controller.yaml new file mode 100644 index 0000000000..2b171d2b91 --- /dev/null +++ b/deployments/controllers/kubearmor-host-controller.yaml @@ -0,0 +1,697 @@ +apiVersion: v1 +kind: Namespace +metadata: + labels: + control-plane: controller-manager + name: k8s-system +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.2.5 + creationTimestamp: null + name: kubearmorhostpolicies.security.kubearmor.com +spec: + group: security.kubearmor.com + names: + kind: KubeArmorHostPolicy + listKind: KubeArmorHostPolicyList + plural: kubearmorhostpolicies + shortNames: + - khp + singular: kubearmorhostpolicy + scope: Cluster + versions: + - name: v1 + schema: + openAPIV3Schema: + description: KubeArmorHostPolicy is the Schema for the kubearmorhostpolicies + 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: KubeArmorHostPolicySpec defines the desired state of KubeArmorHostPolicy + properties: + action: + enum: + - Audit + - Allow + - Block + - AllowWithAudit + - BlockWithAudit + type: string + apparmor: + type: string + capabilities: + properties: + action: + enum: + - Audit + - Allow + - Block + - AllowWithAudit + - BlockWithAudit + type: string + matchCapabilities: + items: + properties: + action: + enum: + - Audit + - Allow + - Block + - AllowWithAudit + - BlockWithAudit + type: string + capability: + pattern: (chown|dac_override|dac_read_search|fowner|fsetid|kill|setgid|setuid|setpcap|linux_immutable|net_bind_service|net_broadcast|net_admin|net_raw|ipc_lock|ipc_owner|sys_module|sys_rawio|sys_chroot|sys_ptrace|sys_pacct|sys_admin|sys_boot|sys_nice|sys_resource|sys_time|sys_tty_config|mknod|lease|audit_write|audit_control|setfcap|mac_override|mac_admin)$ + type: string + fromSource: + items: + properties: + dir: + pattern: ^\/([A-z0-9-_.]+\/)*([A-z0-9-_.]+)+\/$ + type: string + path: + pattern: ^\/([A-z0-9-_.]+\/)*([A-z0-9-_.]+)$ + type: string + recursive: + type: boolean + type: object + type: array + message: + type: string + severity: + maximum: 10 + minimum: 1 + type: integer + tags: + items: + type: string + type: array + required: + - capability + - fromSource + type: object + type: array + message: + type: string + severity: + maximum: 10 + minimum: 1 + type: integer + tags: + items: + type: string + type: array + required: + - matchCapabilities + type: object + file: + properties: + action: + enum: + - Audit + - Allow + - Block + - AllowWithAudit + - BlockWithAudit + type: string + matchDirectories: + items: + properties: + action: + enum: + - Audit + - Allow + - Block + - AllowWithAudit + - BlockWithAudit + type: string + dir: + pattern: ^\/([A-z0-9-_.]+\/)*([A-z0-9-_.]+)+\/$ + type: string + fromSource: + items: + properties: + dir: + pattern: ^\/([A-z0-9-_.]+\/)*([A-z0-9-_.]+)+\/$ + type: string + path: + pattern: ^\/([A-z0-9-_.]+\/)*([A-z0-9-_.]+)$ + type: string + recursive: + type: boolean + type: object + type: array + message: + type: string + ownerOnly: + type: boolean + readOnly: + type: boolean + recursive: + type: boolean + severity: + maximum: 10 + minimum: 1 + type: integer + tags: + items: + type: string + type: array + required: + - dir + type: object + type: array + matchPaths: + items: + properties: + action: + enum: + - Audit + - Allow + - Block + - AllowWithAudit + - BlockWithAudit + type: string + fromSource: + items: + properties: + dir: + pattern: ^\/([A-z0-9-_.]+\/)*([A-z0-9-_.]+)+\/$ + type: string + path: + pattern: ^\/([A-z0-9-_.]+\/)*([A-z0-9-_.]+)$ + type: string + recursive: + type: boolean + type: object + type: array + message: + type: string + ownerOnly: + type: boolean + path: + pattern: ^\/([A-z0-9-_.]+\/)*([A-z0-9-_.]+)$ + type: string + readOnly: + type: boolean + severity: + maximum: 10 + minimum: 1 + type: integer + tags: + items: + type: string + type: array + required: + - path + type: object + type: array + matchPatterns: + items: + properties: + action: + enum: + - Audit + - Allow + - Block + - AllowWithAudit + - BlockWithAudit + type: string + message: + type: string + ownerOnly: + type: boolean + pattern: + type: string + readOnly: + type: boolean + severity: + maximum: 10 + minimum: 1 + type: integer + tags: + items: + type: string + type: array + required: + - pattern + type: object + type: array + message: + type: string + severity: + maximum: 10 + minimum: 1 + type: integer + tags: + items: + type: string + type: array + type: object + message: + type: string + network: + properties: + action: + enum: + - Audit + - Allow + - Block + - AllowWithAudit + - BlockWithAudit + type: string + matchProtocols: + items: + properties: + action: + enum: + - Audit + - Allow + - Block + - AllowWithAudit + - BlockWithAudit + type: string + fromSource: + items: + properties: + dir: + pattern: ^\/([A-z0-9-_.]+\/)*([A-z0-9-_.]+)+\/$ + type: string + path: + pattern: ^\/([A-z0-9-_.]+\/)*([A-z0-9-_.]+)$ + type: string + recursive: + type: boolean + type: object + type: array + message: + type: string + protocol: + pattern: (icmp|ICMP|tcp|TCP|udp|UDP)$ + type: string + severity: + maximum: 10 + minimum: 1 + type: integer + tags: + items: + type: string + type: array + required: + - fromSource + - protocol + type: object + type: array + message: + type: string + severity: + maximum: 10 + minimum: 1 + type: integer + tags: + items: + type: string + type: array + required: + - matchProtocols + type: object + nodeSelector: + properties: + matchLabels: + additionalProperties: + type: string + type: object + matchNames: + additionalProperties: + type: string + type: object + type: object + process: + properties: + action: + enum: + - Audit + - Allow + - Block + - AllowWithAudit + - BlockWithAudit + type: string + matchDirectories: + items: + properties: + action: + enum: + - Audit + - Allow + - Block + - AllowWithAudit + - BlockWithAudit + type: string + dir: + pattern: ^\/([A-z0-9-_.]+\/)*([A-z0-9-_.]+)+\/$ + type: string + fromSource: + items: + properties: + dir: + pattern: ^\/([A-z0-9-_.]+\/)*([A-z0-9-_.]+)+\/$ + type: string + path: + pattern: ^\/([A-z0-9-_.]+\/)*([A-z0-9-_.]+)$ + type: string + recursive: + type: boolean + type: object + type: array + message: + type: string + ownerOnly: + type: boolean + recursive: + type: boolean + severity: + maximum: 10 + minimum: 1 + type: integer + tags: + items: + type: string + type: array + required: + - dir + type: object + type: array + matchPaths: + items: + properties: + action: + enum: + - Audit + - Allow + - Block + - AllowWithAudit + - BlockWithAudit + type: string + fromSource: + items: + properties: + dir: + pattern: ^\/([A-z0-9-_.]+\/)*([A-z0-9-_.]+)+\/$ + type: string + path: + pattern: ^\/([A-z0-9-_.]+\/)*([A-z0-9-_.]+)$ + type: string + recursive: + type: boolean + type: object + type: array + message: + type: string + ownerOnly: + type: boolean + path: + pattern: ^\/([A-z0-9-_.]+\/)*([A-z0-9-_.]+)$ + type: string + severity: + maximum: 10 + minimum: 1 + type: integer + tags: + items: + type: string + type: array + required: + - path + type: object + type: array + matchPatterns: + items: + properties: + action: + enum: + - Audit + - Allow + - Block + - AllowWithAudit + - BlockWithAudit + type: string + message: + type: string + ownerOnly: + type: boolean + pattern: + type: string + severity: + maximum: 10 + minimum: 1 + type: integer + tags: + items: + type: string + type: array + required: + - pattern + type: object + type: array + message: + type: string + severity: + maximum: 10 + minimum: 1 + type: integer + tags: + items: + type: string + type: array + type: object + severity: + maximum: 10 + minimum: 1 + type: integer + tags: + items: + type: string + type: array + required: + - nodeSelector + type: object + status: + description: KubeArmorHostPolicyStatus defines the observed state of KubeArmorHostPolicy + properties: + message: + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: k8s-leader-election-role + namespace: k8s-system +rules: +- apiGroups: + - "" + resources: + - configmaps + verbs: + - get + - list + - watch + - create + - update + - patch + - delete +- apiGroups: + - "" + resources: + - configmaps/status + verbs: + - get + - update + - patch +- apiGroups: + - "" + resources: + - events + verbs: + - create +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + creationTimestamp: null + name: k8s-manager-role +rules: +- apiGroups: + - security.kubearmor.com + resources: + - kubearmorhostpolicies + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - security.kubearmor.com + resources: + - kubearmorhostpolicies/status + verbs: + - get + - patch + - update +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: k8s-proxy-role +rules: +- apiGroups: + - authentication.k8s.io + resources: + - tokenreviews + verbs: + - create +- apiGroups: + - authorization.k8s.io + resources: + - subjectaccessreviews + verbs: + - create +--- +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: ClusterRole +metadata: + name: k8s-metrics-reader +rules: +- nonResourceURLs: + - /metrics + verbs: + - get +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: k8s-leader-election-rolebinding + namespace: k8s-system +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: k8s-leader-election-role +subjects: +- kind: ServiceAccount + name: default + namespace: k8s-system +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: k8s-manager-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: k8s-manager-role +subjects: +- kind: ServiceAccount + name: default + namespace: k8s-system +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: k8s-proxy-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: k8s-proxy-role +subjects: +- kind: ServiceAccount + name: default + namespace: k8s-system +--- +apiVersion: v1 +kind: Service +metadata: + labels: + control-plane: controller-manager + name: k8s-controller-manager-metrics-service + namespace: k8s-system +spec: + ports: + - name: https + port: 8443 + targetPort: https + selector: + control-plane: controller-manager +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + control-plane: controller-manager + name: k8s-controller-manager + namespace: k8s-system +spec: + replicas: 1 + selector: + matchLabels: + control-plane: controller-manager + template: + metadata: + labels: + control-plane: controller-manager + spec: + containers: + - args: + - --secure-listen-address=0.0.0.0:8443 + - --upstream=http://127.0.0.1:8080/ + - --logtostderr=true + - --v=10 + image: gcr.io/kubebuilder/kube-rbac-proxy:v0.5.0 + name: kube-rbac-proxy + ports: + - containerPort: 8443 + name: https + - args: + - --metrics-addr=127.0.0.1:8080 + - --enable-leader-election + command: + - /manager + image: kubearmor/kubearmor-host-controller:latest + imagePullPolicy: Never + name: manager + resources: + limits: + cpu: 100m + memory: 30Mi + requests: + cpu: 100m + memory: 20Mi + terminationGracePeriodSeconds: 10 diff --git a/helm/templates/security.kubearmor.com_kubearmorhostpolicies.yaml b/helm/templates/security.kubearmor.com_kubearmorhostpolicies.yaml index ef4c33ecc3..afb7fe16c6 100644 --- a/helm/templates/security.kubearmor.com_kubearmorhostpolicies.yaml +++ b/helm/templates/security.kubearmor.com_kubearmorhostpolicies.yaml @@ -487,10 +487,15 @@ spec: type: object status: description: KubeArmorHostPolicyStatus defines the observed state of KubeArmorHostPolicy + properties: + message: + type: string type: object type: object served: true storage: true + subresources: + status: {} status: acceptedNames: kind: "" diff --git a/helm/templates/security.kubearmor.com_kubearmorpolicies.yaml b/helm/templates/security.kubearmor.com_kubearmorpolicies.yaml index 29f7d95f20..54b85307f5 100644 --- a/helm/templates/security.kubearmor.com_kubearmorpolicies.yaml +++ b/helm/templates/security.kubearmor.com_kubearmorpolicies.yaml @@ -484,10 +484,15 @@ spec: type: object status: description: KubeArmorPolicyStatus defines the observed state of KubeArmorPolicy + properties: + status: + type: string type: object type: object served: true storage: true + subresources: + status: {} status: acceptedNames: kind: "" diff --git a/pkg/KubeArmorHostPolicy/api/v1/kubearmorhostpolicy_types.go b/pkg/KubeArmorHostPolicy/api/v1/kubearmorhostpolicy_types.go index 3837b11ea8..253a267bb7 100644 --- a/pkg/KubeArmorHostPolicy/api/v1/kubearmorhostpolicy_types.go +++ b/pkg/KubeArmorHostPolicy/api/v1/kubearmorhostpolicy_types.go @@ -283,14 +283,14 @@ type KubeArmorHostPolicySpec struct { // KubeArmorHostPolicyStatus defines the observed state of KubeArmorHostPolicy type KubeArmorHostPolicyStatus struct { - // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster - // Important: Run "make" to regenerate code after modifying this file + PolicyStatus string `json:"message,omitempty"` } // +kubebuilder:object:root=true // KubeArmorHostPolicy is the Schema for the kubearmorhostpolicies API // +kubebuilder:resource:shortName=khp +// +kubebuilder:subresource:status type KubeArmorHostPolicy struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` diff --git a/pkg/KubeArmorHostPolicy/config/crd/bases/security.kubearmor.com_kubearmorhostpolicies.yaml b/pkg/KubeArmorHostPolicy/config/crd/bases/security.kubearmor.com_kubearmorhostpolicies.yaml index ef4c33ecc3..afb7fe16c6 100644 --- a/pkg/KubeArmorHostPolicy/config/crd/bases/security.kubearmor.com_kubearmorhostpolicies.yaml +++ b/pkg/KubeArmorHostPolicy/config/crd/bases/security.kubearmor.com_kubearmorhostpolicies.yaml @@ -487,10 +487,15 @@ spec: type: object status: description: KubeArmorHostPolicyStatus defines the observed state of KubeArmorHostPolicy + properties: + message: + type: string type: object type: object served: true storage: true + subresources: + status: {} status: acceptedNames: kind: "" diff --git a/pkg/KubeArmorHostPolicy/config/manager/kustomization.yaml b/pkg/KubeArmorHostPolicy/config/manager/kustomization.yaml index 5c5f0b84cb..c82c4051eb 100644 --- a/pkg/KubeArmorHostPolicy/config/manager/kustomization.yaml +++ b/pkg/KubeArmorHostPolicy/config/manager/kustomization.yaml @@ -1,2 +1,8 @@ resources: - manager.yaml +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +images: +- name: controller + newName: kubearmor/kubearmor-host-controller + newTag: latest diff --git a/pkg/KubeArmorHostPolicy/config/manager/manager.yaml b/pkg/KubeArmorHostPolicy/config/manager/manager.yaml index b6c85a52d5..75c035c4ea 100644 --- a/pkg/KubeArmorHostPolicy/config/manager/manager.yaml +++ b/pkg/KubeArmorHostPolicy/config/manager/manager.yaml @@ -27,8 +27,9 @@ spec: - /manager args: - --enable-leader-election - image: controller:latest + image: kubearmor/kubearmor-host-controller:latest name: manager + imagePullPolicy: Never resources: limits: cpu: 100m diff --git a/pkg/KubeArmorHostPolicy/controllers/kubearmorhostpolicy_controller.go b/pkg/KubeArmorHostPolicy/controllers/kubearmorhostpolicy_controller.go index 2035c990e4..c8b4d91795 100644 --- a/pkg/KubeArmorHostPolicy/controllers/kubearmorhostpolicy_controller.go +++ b/pkg/KubeArmorHostPolicy/controllers/kubearmorhostpolicy_controller.go @@ -18,12 +18,14 @@ package controllers import ( "context" + "fmt" "github.com/go-logr/logr" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" securityv1 "github.com/kubearmor/KubeArmor/pkg/KubeArmorHostPolicy/api/v1" + "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" ) @@ -41,14 +43,54 @@ func (r *KubeArmorHostPolicyReconciler) Reconcile(req ctrl.Request) (ctrl.Result ctx := context.Background() log := r.Log.WithValues("kubearmorhostpolicy", req.NamespacedName) - policy := securityv1.KubeArmorHostPolicy{} + policy := &securityv1.KubeArmorHostPolicy{} - if err := r.Get(ctx, req.NamespacedName, &policy); err != nil { - return ctrl.Result{}, client.IgnoreNotFound(err) + if err := r.Get(ctx, req.NamespacedName, policy); err != nil { + if errors.IsNotFound(err) { + return ctrl.Result{}, client.IgnoreNotFound(err) + } + return ctrl.Result{}, err } - log.Info("Fetched KubeArmorHostPolicy") + var policyErr error + + // Validate KubeArmorPolicy + // if there are some issues in the policy the delete the policy and return failure code + policyErr = validateProcessSchema(policy, req) + if policyErr != nil { + goto POLICYERROR + } + + policyErr = validateFileSchema(policy, req) + if policyErr != nil { + goto POLICYERROR + } + + policyErr = validateNetworkSchema(policy, req) + if policyErr != nil { + goto POLICYERROR + } + policyErr = validateCapabilitiesSchema(policy, req) + if policyErr != nil { + goto POLICYERROR + } + +POLICYERROR: + if policyErr != nil { + //Update PolicyStatus + policy.Status.PolicyStatus = "Not OK" + err := r.Status().Update(ctx, policy) + log.Info("Invalid KubeArmorHostPolicy") + // delete the invalid KubeArmorPolicy + // err := r.Delete(ctx, policy) + return ctrl.Result{}, err + } + + //Update PolicyStatus + policy.Status.PolicyStatus = "OK" + _ = r.Status().Update(ctx, policy) + log.Info("Fetched KubeArmorHostPolicy") return ctrl.Result{}, nil } @@ -57,3 +99,107 @@ func (r *KubeArmorHostPolicyReconciler) SetupWithManager(mgr ctrl.Manager) error For(&securityv1.KubeArmorHostPolicy{}). Complete(r) } + +func validateProcessSchema(policy *securityv1.KubeArmorHostPolicy, req ctrl.Request) error { + var policyErr error + for _, matchPaths := range policy.Spec.Process.MatchPaths { + if policy.Spec.Action != "Allow" { + if matchPaths.OwnerOnly { + policyErr = fmt.Errorf("ownerOnly works with the Allow action %v", req.NamespacedName) + return policyErr + } + } + for _, fromSource := range matchPaths.FromSource { + if fromSource.Path != "" { + if fromSource.Recursive || !fromSource.Recursive { + policyErr = fmt.Errorf("recursive is only effective with directories, not paths %v", req.NamespacedName) + return policyErr + } + } + } + } + for _, matchDirectories := range policy.Spec.Process.MatchDirectories { + if policy.Spec.Action != "Allow" { + if matchDirectories.OwnerOnly { + policyErr = fmt.Errorf("ownerOnly works with the Allow action %v", req.NamespacedName) + return policyErr + } + } + } + for _, matchPatterns := range policy.Spec.Process.MatchPatterns { + if policy.Spec.Action != "Allow" { + if matchPatterns.OwnerOnly { + policyErr = fmt.Errorf("ownerOnly works with the Allow action %v", req.NamespacedName) + return policyErr + } + } + } + return policyErr +} + +func validateFileSchema(policy *securityv1.KubeArmorHostPolicy, req ctrl.Request) error { + var policyErr error + for _, matchPaths := range policy.Spec.File.MatchPaths { + if policy.Spec.Action != "Allow" { + if matchPaths.OwnerOnly { + policyErr = fmt.Errorf("ownerOnly works with the Allow action %v", req.NamespacedName) + return policyErr + } + } + for _, fromSource := range matchPaths.FromSource { + if fromSource.Path != "" { + if fromSource.Recursive { + policyErr = fmt.Errorf("recursive is only effective with directories, not paths %v", req.NamespacedName) + return policyErr + } + } + } + } + for _, matchDirectories := range policy.Spec.File.MatchDirectories { + if policy.Spec.Action != "Allow" { + if matchDirectories.OwnerOnly { + policyErr = fmt.Errorf("ownerOnly works with the Allow action %v", req.NamespacedName) + return policyErr + } + } + } + for _, matchPatterns := range policy.Spec.File.MatchPatterns { + if policy.Spec.Action != "Allow" { + if matchPatterns.OwnerOnly { + policyErr = fmt.Errorf("ownerOnly works with the Allow action %v", req.NamespacedName) + return policyErr + } + } + } + return policyErr +} + +func validateNetworkSchema(policy *securityv1.KubeArmorHostPolicy, req ctrl.Request) error { + var policyErr error + for _, matchProtocols := range policy.Spec.Network.MatchProtocols { + for _, fromSource := range matchProtocols.FromSource { + if fromSource.Path != "" { + if fromSource.Recursive || !fromSource.Recursive { + policyErr = fmt.Errorf("recursive is only effective with directories, not paths %v", req.NamespacedName) + return policyErr + } + } + } + } + return policyErr +} + +func validateCapabilitiesSchema(policy *securityv1.KubeArmorHostPolicy, req ctrl.Request) error { + var policyErr error + for _, matchCapabilities := range policy.Spec.Capabilities.MatchCapabilities { + for _, fromSource := range matchCapabilities.FromSource { + if fromSource.Path != "" { + if fromSource.Recursive || !fromSource.Recursive { + policyErr = fmt.Errorf("recursive is only effective with directories, not paths %v", req.NamespacedName) + return policyErr + } + } + } + } + return policyErr +} diff --git a/pkg/KubeArmorPolicy/api/v1/kubearmorpolicy_types.go b/pkg/KubeArmorPolicy/api/v1/kubearmorpolicy_types.go index a208c778f7..852e6e3148 100644 --- a/pkg/KubeArmorPolicy/api/v1/kubearmorpolicy_types.go +++ b/pkg/KubeArmorPolicy/api/v1/kubearmorpolicy_types.go @@ -287,14 +287,14 @@ type KubeArmorPolicySpec struct { // KubeArmorPolicyStatus defines the observed state of KubeArmorPolicy type KubeArmorPolicyStatus struct { - // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster - // Important: Run "make" to regenerate code after modifying this file + PolicyStatus string `json:"status,omitempty"` } // +kubebuilder:object:root=true // KubeArmorPolicy is the Schema for the kubearmorpolicies API // +kubebuilder:resource:shortName=ksp +// +kubebuilder:subresource:status type KubeArmorPolicy struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` diff --git a/pkg/KubeArmorPolicy/config/crd/bases/security.kubearmor.com_kubearmorpolicies.yaml b/pkg/KubeArmorPolicy/config/crd/bases/security.kubearmor.com_kubearmorpolicies.yaml index 29f7d95f20..54b85307f5 100644 --- a/pkg/KubeArmorPolicy/config/crd/bases/security.kubearmor.com_kubearmorpolicies.yaml +++ b/pkg/KubeArmorPolicy/config/crd/bases/security.kubearmor.com_kubearmorpolicies.yaml @@ -484,10 +484,15 @@ spec: type: object status: description: KubeArmorPolicyStatus defines the observed state of KubeArmorPolicy + properties: + status: + type: string type: object type: object served: true storage: true + subresources: + status: {} status: acceptedNames: kind: "" diff --git a/pkg/KubeArmorPolicy/config/manager/kustomization.yaml b/pkg/KubeArmorPolicy/config/manager/kustomization.yaml index 5c5f0b84cb..339a7cca9d 100644 --- a/pkg/KubeArmorPolicy/config/manager/kustomization.yaml +++ b/pkg/KubeArmorPolicy/config/manager/kustomization.yaml @@ -1,2 +1,8 @@ resources: - manager.yaml +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +images: +- name: controller + newName: kubearmor/kubearmor-controller + newTag: latest diff --git a/pkg/KubeArmorPolicy/config/manager/manager.yaml b/pkg/KubeArmorPolicy/config/manager/manager.yaml index b6c85a52d5..02c42ce819 100644 --- a/pkg/KubeArmorPolicy/config/manager/manager.yaml +++ b/pkg/KubeArmorPolicy/config/manager/manager.yaml @@ -27,8 +27,9 @@ spec: - /manager args: - --enable-leader-election - image: controller:latest + image: kubearmor/kubearmor-controller:latest name: manager + imagePullPolicy: Never resources: limits: cpu: 100m diff --git a/pkg/KubeArmorPolicy/controllers/kubearmorpolicy_controller.go b/pkg/KubeArmorPolicy/controllers/kubearmorpolicy_controller.go index be6824c98b..3afb081716 100644 --- a/pkg/KubeArmorPolicy/controllers/kubearmorpolicy_controller.go +++ b/pkg/KubeArmorPolicy/controllers/kubearmorpolicy_controller.go @@ -18,12 +18,14 @@ package controllers import ( "context" + "fmt" "github.com/go-logr/logr" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" securityv1 "github.com/kubearmor/KubeArmor/pkg/KubeArmorPolicy/api/v1" + "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" ) @@ -41,15 +43,55 @@ func (r *KubeArmorPolicyReconciler) Reconcile(req ctrl.Request) (ctrl.Result, er ctx := context.Background() log := r.Log.WithValues("kubearmorpolicy", req.NamespacedName) - policy := securityv1.KubeArmorPolicy{} + policy := &securityv1.KubeArmorPolicy{} - if err := r.Get(ctx, req.NamespacedName, &policy); err != nil { - return ctrl.Result{}, client.IgnoreNotFound(err) + if err := r.Get(ctx, req.NamespacedName, policy); err != nil { + if errors.IsNotFound(err) { + return ctrl.Result{}, client.IgnoreNotFound(err) + } + return ctrl.Result{}, err } - log.Info("Fetched KubeArmorPolicy") + var policyErr error + + // Validate KubeArmorPolicy + // if there are some issues in the policy the delete the policy and return failure code + policyErr = validateProcessSchema(policy, req) + if policyErr != nil { + goto POLICYERROR + } + + policyErr = validateFileSchema(policy, req) + if policyErr != nil { + goto POLICYERROR + } + + policyErr = validateNetworkSchema(policy, req) + if policyErr != nil { + goto POLICYERROR + } - return ctrl.Result{}, nil + policyErr = validateCapabilitiesSchema(policy, req) + if policyErr != nil { + goto POLICYERROR + } + +POLICYERROR: + if policyErr != nil { + //Update PolicyStatus + policy.Status.PolicyStatus = "Not OK" + err := r.Status().Update(ctx, policy) + log.Info("Invalid KubeArmorPolicy") + // delete the invalid KubeArmorPolicy + // err := r.Delete(ctx, policy) + return ctrl.Result{}, err + } + + //Update PolicyStatus + policy.Status.PolicyStatus = "OK" + err := r.Status().Update(ctx, policy) + log.Info("Fetched KubeArmorPolicy") + return ctrl.Result{}, err } func (r *KubeArmorPolicyReconciler) SetupWithManager(mgr ctrl.Manager) error { @@ -57,3 +99,107 @@ func (r *KubeArmorPolicyReconciler) SetupWithManager(mgr ctrl.Manager) error { For(&securityv1.KubeArmorPolicy{}). Complete(r) } + +func validateProcessSchema(policy *securityv1.KubeArmorPolicy, req ctrl.Request) error { + var policyErr error + for _, matchPaths := range policy.Spec.Process.MatchPaths { + if policy.Spec.Action != "Allow" { + if matchPaths.OwnerOnly { + policyErr = fmt.Errorf("ownerOnly works with the Allow action %v", req.NamespacedName) + return policyErr + } + } + for _, fromSource := range matchPaths.FromSource { + if fromSource.Path != "" { + if fromSource.Recursive || !fromSource.Recursive { + policyErr = fmt.Errorf("recursive is only effective with directories, not paths %v", req.NamespacedName) + return policyErr + } + } + } + } + for _, matchDirectories := range policy.Spec.Process.MatchDirectories { + if policy.Spec.Action != "Allow" { + if matchDirectories.OwnerOnly { + policyErr = fmt.Errorf("ownerOnly works with the Allow action %v", req.NamespacedName) + return policyErr + } + } + } + for _, matchPatterns := range policy.Spec.Process.MatchPatterns { + if policy.Spec.Action != "Allow" { + if matchPatterns.OwnerOnly { + policyErr = fmt.Errorf("ownerOnly works with the Allow action %v", req.NamespacedName) + return policyErr + } + } + } + return policyErr +} + +func validateFileSchema(policy *securityv1.KubeArmorPolicy, req ctrl.Request) error { + var policyErr error + for _, matchPaths := range policy.Spec.File.MatchPaths { + if policy.Spec.Action != "Allow" { + if matchPaths.OwnerOnly { + policyErr = fmt.Errorf("ownerOnly works with the Allow action %v", req.NamespacedName) + return policyErr + } + } + for _, fromSource := range matchPaths.FromSource { + if fromSource.Path != "" { + if fromSource.Recursive || !fromSource.Recursive { + policyErr = fmt.Errorf("Found invalid schema Recursive for Path %v", req.NamespacedName) + return policyErr + } + } + } + } + for _, matchDirectories := range policy.Spec.File.MatchDirectories { + if policy.Spec.Action != "Allow" { + if matchDirectories.OwnerOnly { + policyErr = fmt.Errorf("ownerOnly works with the Allow action %v", req.NamespacedName) + return policyErr + } + } + } + for _, matchPatterns := range policy.Spec.File.MatchPatterns { + if policy.Spec.Action != "Allow" { + if matchPatterns.OwnerOnly { + policyErr = fmt.Errorf("ownerOnly works with the Allow action %v", req.NamespacedName) + return policyErr + } + } + } + return policyErr +} + +func validateNetworkSchema(policy *securityv1.KubeArmorPolicy, req ctrl.Request) error { + var policyErr error + for _, matchProtocols := range policy.Spec.Network.MatchProtocols { + for _, fromSource := range matchProtocols.FromSource { + if fromSource.Path != "" { + if fromSource.Recursive || !fromSource.Recursive { + policyErr = fmt.Errorf("recursive is only effective with directories, not paths %v", req.NamespacedName) + return policyErr + } + } + } + } + return policyErr +} + +func validateCapabilitiesSchema(policy *securityv1.KubeArmorPolicy, req ctrl.Request) error { + var policyErr error + for _, matchCapabilities := range policy.Spec.Capabilities.MatchCapabilities { + for _, fromSource := range matchCapabilities.FromSource { + if fromSource.Path != "" { + if fromSource.Recursive || !fromSource.Recursive { + policyErr = fmt.Errorf("recursive is only effective with directories, not paths %v", req.NamespacedName) + return policyErr + } + } + } + } + return policyErr +}