diff --git a/rules/exposure-to-internet-via-gateway-api/raw.rego b/rules/exposure-to-internet-via-gateway-api/raw.rego index 0fac5da2d..da59f3d18 100644 --- a/rules/exposure-to-internet-via-gateway-api/raw.rego +++ b/rules/exposure-to-internet-via-gateway-api/raw.rego @@ -17,10 +17,11 @@ deny[msga] { not is_exposed_service(svc) wl := input[_] - wl.metadata.namespace == svc.metadata.namespace + is_same_namespace(wl.metadata, svc.metadata) spec_template_spec_patterns := {"Deployment", "ReplicaSet", "DaemonSet", "StatefulSet", "Pod", "Job", "CronJob"} spec_template_spec_patterns[wl.kind] - wl_connected_to_service(wl, svc) + pod := get_pod_spec(wl)["spec"] + wl_connected_to_service(pod, svc) result := svc_connected_to_httproute(svc, httproute) @@ -56,18 +57,56 @@ is_exposed_service(svc) { svc.spec.type == "LoadBalancer" } -wl_connected_to_service(wl, svc) { +wl_connected_to_service(wl, svc) { + + count({x | svc.spec.selector[x] == wl.metadata.labels[x]}) == count(svc.spec.selector) } -wl_connected_to_service(wl, svc) { - wl.spec.selector.matchLabels == svc.spec.selector + +is_same_namespace(metadata1, metadata2) { + metadata1.namespace == metadata2.namespace } -wl_connected_to_service(wl, svc) { - count({x | svc.spec.selector[x] == wl.spec.template.metadata.labels[x]}) == count(svc.spec.selector) +is_same_namespace(metadata1, metadata2) { + not metadata1.namespace + not metadata2.namespace } +is_same_namespace(metadata1, metadata2) { + not metadata2.namespace + metadata1.namespace == "default" +} + +is_same_namespace(metadata1, metadata2) { + not metadata1.namespace + metadata2.namespace == "default" +} + + + +# get_volume - get resource spec paths for {"Deployment","ReplicaSet","DaemonSet","StatefulSet","Job"} +get_pod_spec(resources) := result { + resources_kinds := {"Deployment","ReplicaSet","DaemonSet","StatefulSet","Job"} + resources_kinds[resources.kind] + result = {"spec": resources.spec.template, "start_of_path": "spec.template."} +} + +# get_volume - get resource spec paths for "Pod" +get_pod_spec(resources) := result { + resources.kind == "Pod" + result = {"spec": resources, "start_of_path": ""} +} + +# get_volume - get resource spec paths for "CronJob" +get_pod_spec(resources) := result { + resources.kind == "CronJob" + result = {"spec": resources.spec.jobTemplate.spec.template.spec, "start_of_path": "spec.jobTemplate.spec.template.spec."} +} + + + + svc_connected_to_httproute(svc, httproute) = result { rule := httproute.spec.rules[i] ref := rule.backendRefs[j] diff --git a/rules/exposure-to-internet-via-gateway-api/test/failed_with_httproute_multiservice/expected.json b/rules/exposure-to-internet-via-gateway-api/test/failed_with_httproute_multiservice/expected.json new file mode 100644 index 000000000..1c52ffa5d --- /dev/null +++ b/rules/exposure-to-internet-via-gateway-api/test/failed_with_httproute_multiservice/expected.json @@ -0,0 +1,161 @@ +[ + { + "alertMessage": "workload 'httpbin' is exposed through httproute 'httpbin'", + "failedPaths": [], + "fixPaths": [], + "ruleStatus": "", + "packagename": "armo_builtins", + "alertScore": 7, + "alertObject": { + "k8sApiObjects": [ + { + "apiVersion": "apps/v1", + "kind": "Deployment", + "metadata": { + "name": "httpbin" + } + } + ] + }, + "relatedObjects": [ + { + "object": { + "apiVersion": "gateway.networking.k8s.io/v1", + "kind": "HTTPRoute", + "metadata": { + "creationTimestamp": "2024-02-04T19:06:03Z", + "generation": 1, + "labels": { + "example": "httpbin-route" + }, + "name": "httpbin", + "namespace": "httpbin", + "resourceVersion": "914", + "uid": "fd820080-801d-4fa7-934a-e23abe8bf746" + }, + "spec": { + "hostnames": [ + "www.example.com" + ], + "parentRefs": [ + { + "group": "gateway.networking.k8s.io", + "kind": "Gateway", + "name": "http", + "namespace": "gloo-system" + } + ], + "rules": [ + { + "backendRefs": [ + { + "group": "", + "kind": "Service", + "name": "httpbin", + "port": 8000, + "weight": 1 + } + ], + "matches": [ + { + "path": { + "type": "PathPrefix", + "value": "/" + } + } + ] + } + ] + }, + "status": { + "parents": [ + { + "conditions": [ + { + "lastTransitionTime": "2024-02-04T19:06:03Z", + "message": "", + "observedGeneration": 1, + "reason": "Accepted", + "status": "True", + "type": "Accepted" + }, + { + "lastTransitionTime": "2024-02-04T19:06:03Z", + "message": "", + "observedGeneration": 1, + "reason": "ResolvedRefs", + "status": "True", + "type": "ResolvedRefs" + } + ], + "controllerName": "solo.io/gloo-gateway", + "parentRef": { + "group": "gateway.networking.k8s.io", + "kind": "Gateway", + "name": "http", + "namespace": "gloo-system" + } + } + ] + } + }, + "failedPaths": [ + "spec.rules[0].backendRefs[0].name" + ], + "reviewPaths": [ + "spec.rules[0].backendRefs[0].name" + ] + }, + { + "object": { + "apiVersion": "v1", + "kind": "Service", + "metadata": { + "creationTimestamp": "2024-02-04T19:05:12Z", + "labels": { + "app": "httpbin", + "service": "httpbin" + }, + "name": "httpbin", + "namespace": "httpbin", + "resourceVersion": "811", + "uid": "c391feb7-54e5-41b2-869b-33166869f1b7" + }, + "spec": { + "clusterIP": "10.96.162.234", + "clusterIPs": [ + "10.96.162.234" + ], + "internalTrafficPolicy": "Cluster", + "ipFamilies": [ + "IPv4" + ], + "ipFamilyPolicy": "SingleStack", + "ports": [ + { + "name": "http", + "port": 8000, + "protocol": "TCP", + "targetPort": 8080 + }, + { + "name": "tcp", + "port": 9000, + "protocol": "TCP", + "targetPort": 9000 + } + ], + "selector": { + "app": "httpbin" + }, + "sessionAffinity": "None", + "type": "ClusterIP" + }, + "status": { + "loadBalancer": {} + } + } + } + ] + } +] \ No newline at end of file diff --git a/rules/exposure-to-internet-via-gateway-api/test/failed_with_httproute_multiservice/input/deployment.yaml b/rules/exposure-to-internet-via-gateway-api/test/failed_with_httproute_multiservice/input/deployment.yaml new file mode 100644 index 000000000..2b40cae26 --- /dev/null +++ b/rules/exposure-to-internet-via-gateway-api/test/failed_with_httproute_multiservice/input/deployment.yaml @@ -0,0 +1,93 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + deployment.kubernetes.io/revision: "1" + creationTimestamp: "2024-02-04T19:05:12Z" + generation: 1 + name: httpbin + namespace: httpbin + resourceVersion: "870" + uid: 7462bb4c-b5a2-413e-80ee-c1baaf34aade +spec: + progressDeadlineSeconds: 600 + replicas: 1 + revisionHistoryLimit: 10 + selector: + matchLabels: + app: httpbin + version: v1 + strategy: + rollingUpdate: + maxSurge: 25% + maxUnavailable: 25% + type: RollingUpdate + template: + metadata: + labels: + app: httpbin + version: v1 + spec: + containers: + - args: + - -port + - "8080" + - -max-duration + - 600s + command: + - go-httpbin + image: docker.io/mccutchen/go-httpbin:v2.6.0 + imagePullPolicy: IfNotPresent + name: httpbin + ports: + - containerPort: 8080 + protocol: TCP + resources: {} + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + - command: + - tail + - -f + - /dev/null + image: curlimages/curl:7.83.1 + imagePullPolicy: IfNotPresent + name: curl + resources: + limits: + cpu: 200m + requests: + cpu: 100m + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + - image: gcr.io/solo-public/docs/hey:0.1.4 + imagePullPolicy: IfNotPresent + name: hey + resources: {} + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + dnsPolicy: ClusterFirst + restartPolicy: Always + schedulerName: default-scheduler + securityContext: {} + serviceAccount: httpbin + serviceAccountName: httpbin + terminationGracePeriodSeconds: 30 +status: + availableReplicas: 1 + conditions: + - lastTransitionTime: "2024-02-04T19:05:32Z" + lastUpdateTime: "2024-02-04T19:05:32Z" + message: Deployment has minimum availability. + reason: MinimumReplicasAvailable + status: "True" + type: Available + - lastTransitionTime: "2024-02-04T19:05:12Z" + lastUpdateTime: "2024-02-04T19:05:32Z" + message: ReplicaSet "httpbin-f46cc8b9b" has successfully progressed. + reason: NewReplicaSetAvailable + status: "True" + type: Progressing + observedGeneration: 1 + readyReplicas: 1 + replicas: 1 + updatedReplicas: 1 diff --git a/rules/exposure-to-internet-via-gateway-api/test/failed_with_httproute_multiservice/input/httproute.yaml b/rules/exposure-to-internet-via-gateway-api/test/failed_with_httproute_multiservice/input/httproute.yaml new file mode 100644 index 000000000..44b941b78 --- /dev/null +++ b/rules/exposure-to-internet-via-gateway-api/test/failed_with_httproute_multiservice/input/httproute.yaml @@ -0,0 +1,51 @@ +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + creationTimestamp: "2024-02-04T19:06:03Z" + generation: 1 + labels: + example: httpbin-route + name: httpbin + namespace: httpbin + resourceVersion: "914" + uid: fd820080-801d-4fa7-934a-e23abe8bf746 +spec: + hostnames: + - www.example.com + parentRefs: + - group: gateway.networking.k8s.io + kind: Gateway + name: http + namespace: gloo-system + rules: + - backendRefs: + - group: "" + kind: Service + name: httpbin + port: 8000 + weight: 1 + matches: + - path: + type: PathPrefix + value: / +status: + parents: + - conditions: + - lastTransitionTime: "2024-02-04T19:06:03Z" + message: "" + observedGeneration: 1 + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: "2024-02-04T19:06:03Z" + message: "" + observedGeneration: 1 + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: solo.io/gloo-gateway + parentRef: + group: gateway.networking.k8s.io + kind: Gateway + name: http + namespace: gloo-system diff --git a/rules/exposure-to-internet-via-gateway-api/test/failed_with_httproute_multiservice/input/service.yaml b/rules/exposure-to-internet-via-gateway-api/test/failed_with_httproute_multiservice/input/service.yaml new file mode 100644 index 000000000..40e721d26 --- /dev/null +++ b/rules/exposure-to-internet-via-gateway-api/test/failed_with_httproute_multiservice/input/service.yaml @@ -0,0 +1,34 @@ +apiVersion: v1 +kind: Service +metadata: + creationTimestamp: "2024-02-04T19:05:12Z" + labels: + app: httpbin + service: httpbin + name: httpbin + namespace: httpbin + resourceVersion: "811" + uid: c391feb7-54e5-41b2-869b-33166869f1b7 +spec: + clusterIP: 10.96.162.234 + clusterIPs: + - 10.96.162.234 + internalTrafficPolicy: Cluster + ipFamilies: + - IPv4 + ipFamilyPolicy: SingleStack + ports: + - name: http + port: 8000 + protocol: TCP + targetPort: 8080 + - name: tcp + port: 9000 + protocol: TCP + targetPort: 9000 + selector: + app: httpbin + sessionAffinity: None + type: ClusterIP +status: + loadBalancer: {} diff --git a/rules/exposure-to-internet-via-gateway-api/test/failed_with_httproute_multiservice/input/service2.yaml b/rules/exposure-to-internet-via-gateway-api/test/failed_with_httproute_multiservice/input/service2.yaml new file mode 100644 index 000000000..c1fe8622b --- /dev/null +++ b/rules/exposure-to-internet-via-gateway-api/test/failed_with_httproute_multiservice/input/service2.yaml @@ -0,0 +1,34 @@ +apiVersion: v1 +kind: Service +metadata: + creationTimestamp: "2024-02-04T19:05:12Z" + labels: + app: httpbin + service: httpbin + name: httpbin + namespace: httpbin2 + resourceVersion: "811" + uid: c391feb7-54e5-41b2-869b-33166869f1b7 +spec: + clusterIP: 10.96.162.234 + clusterIPs: + - 10.96.162.234 + internalTrafficPolicy: Cluster + ipFamilies: + - IPv4 + ipFamilyPolicy: SingleStack + ports: + - name: http + port: 8000 + protocol: TCP + targetPort: 8080 + - name: tcp + port: 9000 + protocol: TCP + targetPort: 9000 + selector: + app: httpbin + sessionAffinity: None + type: ClusterIP +status: + loadBalancer: {} diff --git a/rules/exposure-to-internet-via-istio-ingress/raw.rego b/rules/exposure-to-internet-via-istio-ingress/raw.rego index 922968569..fbc80ebac 100644 --- a/rules/exposure-to-internet-via-istio-ingress/raw.rego +++ b/rules/exposure-to-internet-via-istio-ingress/raw.rego @@ -45,7 +45,8 @@ deny[msga] { is_same_namespace(connected_service, wl) spec_template_spec_patterns := {"Deployment", "ReplicaSet", "DaemonSet", "StatefulSet", "Pod", "Job", "CronJob"} spec_template_spec_patterns[wl.kind] - wl_connected_to_service(wl, connected_service) + pod := get_pod_spec(wl)["spec"] + wl_connected_to_service(pod, connected_service) # print("Found the workload that the service is connected to", wl) @@ -141,6 +142,7 @@ is_exposed_service(svc) { svc.spec.type == "LoadBalancer" } + wl_connected_to_service(wl, svc) { count({x | svc.spec.selector[x] == wl.metadata.labels[x]}) == count(svc.spec.selector) } @@ -176,3 +178,22 @@ get_fqsn(ns, dest_host) = fqsn { } + +# get_volume - get resource spec paths for {"Deployment","ReplicaSet","DaemonSet","StatefulSet","Job"} +get_pod_spec(resources) := result { + resources_kinds := {"Deployment","ReplicaSet","DaemonSet","StatefulSet","Job"} + resources_kinds[resources.kind] + result = {"spec": resources.spec.template, "start_of_path": "spec.template."} +} + +# get_volume - get resource spec paths for "Pod" +get_pod_spec(resources) := result { + resources.kind == "Pod" + result = {"spec": resources, "start_of_path": ""} +} + +# get_volume - get resource spec paths for "CronJob" +get_pod_spec(resources) := result { + resources.kind == "CronJob" + result = {"spec": resources.spec.jobTemplate.spec.template.spec, "start_of_path": "spec.jobTemplate.spec.template.spec."} +} diff --git a/rules/exposure-to-internet-via-istio-ingress/test/failed_with_beta/expected.json b/rules/exposure-to-internet-via-istio-ingress/test/failed_with_beta/expected.json index df1b5fa11..c10105d16 100644 --- a/rules/exposure-to-internet-via-istio-ingress/test/failed_with_beta/expected.json +++ b/rules/exposure-to-internet-via-istio-ingress/test/failed_with_beta/expected.json @@ -171,10 +171,7 @@ } ], "selector": { - "app": "int-0721", - "context": "default", - "name": "int-0721", - "role": "app" + "app": "nginx" }, "sessionAffinity": "None", "type": "ClusterIP" diff --git a/rules/exposure-to-internet-via-istio-ingress/test/failed_with_beta/input/service.yaml b/rules/exposure-to-internet-via-istio-ingress/test/failed_with_beta/input/service.yaml index 8b478e61c..ed3bc0f7a 100644 --- a/rules/exposure-to-internet-via-istio-ingress/test/failed_with_beta/input/service.yaml +++ b/rules/exposure-to-internet-via-istio-ingress/test/failed_with_beta/input/service.yaml @@ -34,10 +34,7 @@ spec: protocol: TCP targetPort: 8080 selector: - app: int-0721 - context: default - name: int-0721 - role: app + app: nginx sessionAffinity: None type: ClusterIP status: diff --git a/rules/exposure-to-internet-via-istio-ingress/test/failed_with_beta_multiservice/expected.json b/rules/exposure-to-internet-via-istio-ingress/test/failed_with_beta_multiservice/expected.json new file mode 100644 index 000000000..c10105d16 --- /dev/null +++ b/rules/exposure-to-internet-via-istio-ingress/test/failed_with_beta_multiservice/expected.json @@ -0,0 +1,190 @@ +[ + { + "alertMessage": "workload 'nginx' is exposed through virtualservice 'int-0721'", + "failedPaths": [], + "reviewPaths": null, + "deletePaths": null, + "fixPaths": [], + "ruleStatus": "", + "packagename": "armo_builtins", + "alertScore": 7, + "alertObject": { + "k8sApiObjects": [ + { + "apiVersion": "apps/v1", + "kind": "Deployment", + "metadata": { + "labels": { + "app": "int-0721", + "context": "default", + "name": "int-0721", + "role": "app" + }, + "name": "nginx" + } + } + ] + }, + "relatedObjects": [ + { + "object": { + "apiVersion": "networking.istio.io/v1beta1", + "kind": "VirtualService", + "metadata": { + "creationTimestamp": "2024-04-15T08:56:16Z", + "generation": 2, + "labels": { + "app": "int-0721", + "context": "default", + "name": "int-0721", + "owner": "int-0721", + "owner-namespace": "kt-itinternal", + "role": "app" + }, + "name": "int-0721", + "namespace": "kt-itinternal" + }, + "spec": { + "gateways": [ + "kt-connections/prod-lan-gateway", + "kt-connections/sdfsdfs-gateway", + "kt-connections/api-public", + "kt-connections/aaaaa-public", + "kt-connections/mydddd-public" + ], + "hosts": [ + "api.stg.prod.lan" + ], + "http": [ + { + "corsPolicy": { + "allowHeaders": [ + "authorization", + "Origin", + "Content-Type", + "Accept" + ], + "allowMethods": [ + "POST", + "GET", + "OPTIONS", + "PUT", + "PATCH", + "DELETE" + ], + "allowOrigins": [ + { + "regex": ".*" + } + ] + }, + "match": [ + { + "authority": { + "exact": "api.stg.prod.lan" + }, + "uri": { + "prefix": "/int/0698/" + } + } + ], + "name": "api-http-stgprodlan", + "rewrite": { + "uri": "/" + }, + "route": [ + { + "destination": { + "host": "int-0721.kt-itinternal.svc.cluster.local", + "port": { + "number": 8080 + } + }, + "headers": { + "request": { + "set": { + "X-Forwarded-Prefix": "/int/0698", + "X-Forwarded-Proto": "https" + } + } + } + } + ] + } + ] + } + }, + "failedPaths": [ + "spec.http[0].routes[0].destination.host" + ], + "reviewPaths": [ + "spec.http[0].routes[0].destination.host" + ], + "deletePaths": null, + "fixPaths": null + }, + { + "object": { + "apiVersion": "v1", + "kind": "Service", + "metadata": { + "creationTimestamp": "2024-04-15T09:00:11Z", + "labels": { + "app": "int-0721", + "context": "default", + "name": "int-0721", + "owner": "int-0721", + "owner-namespace": "kt-itinternal", + "role": "app" + }, + "name": "int-0721", + "namespace": "kt-itinternal", + "ownerReferences": [ + { + "apiVersion": "msss/v1alpha1", + "blockOwnerDeletion": true, + "controller": true, + "kind": "Microservice", + "name": "int-0721", + "uid": "14a69d5b-249c-487d-9500-645bda6a4c56" + } + ], + "resourceVersion": "3779885629", + "uid": "0428cb70-5d8f-4345-8ef2-5b0a249e0793" + }, + "spec": { + "clusterIP": "10.81.208.49", + "clusterIPs": [ + "10.81.208.49" + ], + "internalTrafficPolicy": "Cluster", + "ipFamilies": [ + "IPv4" + ], + "ipFamilyPolicy": "SingleStack", + "ports": [ + { + "name": "http", + "port": 8080, + "protocol": "TCP", + "targetPort": 8080 + } + ], + "selector": { + "app": "nginx" + }, + "sessionAffinity": "None", + "type": "ClusterIP" + }, + "status": { + "loadBalancer": {} + } + }, + "failedPaths": null, + "reviewPaths": null, + "deletePaths": null, + "fixPaths": null + } + ] + } +] \ No newline at end of file diff --git a/rules/exposure-to-internet-via-istio-ingress/test/failed_with_beta_multiservice/gateway.yaml b/rules/exposure-to-internet-via-istio-ingress/test/failed_with_beta_multiservice/gateway.yaml new file mode 100644 index 000000000..6371f688d --- /dev/null +++ b/rules/exposure-to-internet-via-istio-ingress/test/failed_with_beta_multiservice/gateway.yaml @@ -0,0 +1,24 @@ +apiVersion: networking.istio.io/v1beta1 +kind: Gateway +metadata: + annotations: + meta.helm.sh/release-name: legacy-compatibility + meta.helm.sh/release-namespace: kt-itinternal + creationTimestamp: "2023-03-16T10:18:13Z" + generation: 1 + labels: + app.kubernetes.io/managed-by: Helm + name: prod-lan-gateway + namespace: kt-itinternal + resourceVersion: "713118464" + uid: 46cb8274-3569-4b4a-952a-52037a352715 +spec: + selector: + istio: ingressgateway + servers: + - hosts: + - legacy.stg.prod.lan + port: + name: http + number: 80 + protocol: HTTP \ No newline at end of file diff --git a/rules/exposure-to-internet-via-istio-ingress/test/failed_with_beta_multiservice/input/deployment.yaml b/rules/exposure-to-internet-via-istio-ingress/test/failed_with_beta_multiservice/input/deployment.yaml new file mode 100644 index 000000000..8586c6131 --- /dev/null +++ b/rules/exposure-to-internet-via-istio-ingress/test/failed_with_beta_multiservice/input/deployment.yaml @@ -0,0 +1,58 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app: int-0721 + context: default + name: int-0721 + role: app + name: nginx + namespace: kt-itinternal +spec: + progressDeadlineSeconds: 600 + replicas: 1 + revisionHistoryLimit: 10 + selector: + matchLabels: + app: nginx + strategy: + rollingUpdate: + maxSurge: 25% + maxUnavailable: 25% + type: RollingUpdate + template: + metadata: + labels: + app: nginx + spec: + containers: + - image: nginx + imagePullPolicy: Always + name: nginx + resources: {} + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + dnsPolicy: ClusterFirst + restartPolicy: Always + schedulerName: default-scheduler + securityContext: {} + terminationGracePeriodSeconds: 30 +status: + availableReplicas: 1 + conditions: + - lastTransitionTime: "2024-11-19T19:33:45Z" + lastUpdateTime: "2024-11-19T19:33:45Z" + message: Deployment has minimum availability. + reason: MinimumReplicasAvailable + status: "True" + type: Available + - lastTransitionTime: "2024-11-19T19:33:37Z" + lastUpdateTime: "2024-11-19T19:33:45Z" + message: ReplicaSet "nginx-7854ff8877" has successfully progressed. + reason: NewReplicaSetAvailable + status: "True" + type: Progressing + observedGeneration: 1 + readyReplicas: 1 + replicas: 1 + updatedReplicas: 1 diff --git a/rules/exposure-to-internet-via-istio-ingress/test/failed_with_beta_multiservice/input/gateway.yaml b/rules/exposure-to-internet-via-istio-ingress/test/failed_with_beta_multiservice/input/gateway.yaml new file mode 100644 index 000000000..61743f7c9 --- /dev/null +++ b/rules/exposure-to-internet-via-istio-ingress/test/failed_with_beta_multiservice/input/gateway.yaml @@ -0,0 +1,15 @@ +apiVersion: networking.istio.io/v1beta1 +kind: Gateway +metadata: + name: prod-lan-gateway + namespace: kt-connections +spec: + selector: + istio: ingressgateway + servers: + - hosts: + - legacy.stg.prod.lan + port: + name: http + number: 80 + protocol: HTTP \ No newline at end of file diff --git a/rules/exposure-to-internet-via-istio-ingress/test/failed_with_beta_multiservice/input/istio-gw.yaml b/rules/exposure-to-internet-via-istio-ingress/test/failed_with_beta_multiservice/input/istio-gw.yaml new file mode 100644 index 000000000..2c430ffd1 --- /dev/null +++ b/rules/exposure-to-internet-via-istio-ingress/test/failed_with_beta_multiservice/input/istio-gw.yaml @@ -0,0 +1,32 @@ +apiVersion: v1 +kind: Service +metadata: + labels: + app: istio-ingressgateway + istio: ingressgateway + istio.io/rev: default + operator.istio.io/component: IngressGateways + operator.istio.io/managed: Reconcile + operator.istio.io/version: 1.18.7 + release: istio + name: istio-ingressgateway + namespace: istio-system +spec: + externalTrafficPolicy: Local + internalTrafficPolicy: Cluster + ipFamilies: + - IPv4 + ipFamilyPolicy: SingleStack + ports: + - name: status-port + nodePort: 30432 + port: 15033 + protocol: TCP + targetPort: 15033 + selector: + app: istio-ingressgateway + istio: ingressgateway + sessionAffinity: None + type: NodePort +status: + loadBalancer: {} \ No newline at end of file diff --git a/rules/exposure-to-internet-via-istio-ingress/test/failed_with_beta_multiservice/input/service.yaml b/rules/exposure-to-internet-via-istio-ingress/test/failed_with_beta_multiservice/input/service.yaml new file mode 100644 index 000000000..ed3bc0f7a --- /dev/null +++ b/rules/exposure-to-internet-via-istio-ingress/test/failed_with_beta_multiservice/input/service.yaml @@ -0,0 +1,41 @@ +apiVersion: v1 +kind: Service +metadata: + creationTimestamp: "2024-04-15T09:00:11Z" + labels: + app: int-0721 + context: default + name: int-0721 + owner: int-0721 + owner-namespace: kt-itinternal + role: app + name: int-0721 + namespace: kt-itinternal + ownerReferences: + - apiVersion: msss/v1alpha1 + blockOwnerDeletion: true + controller: true + kind: Microservice + name: int-0721 + uid: 14a69d5b-249c-487d-9500-645bda6a4c56 + resourceVersion: "3779885629" + uid: 0428cb70-5d8f-4345-8ef2-5b0a249e0793 +spec: + clusterIP: 10.81.208.49 + clusterIPs: + - 10.81.208.49 + internalTrafficPolicy: Cluster + ipFamilies: + - IPv4 + ipFamilyPolicy: SingleStack + ports: + - name: http + port: 8080 + protocol: TCP + targetPort: 8080 + selector: + app: nginx + sessionAffinity: None + type: ClusterIP +status: + loadBalancer: {} \ No newline at end of file diff --git a/rules/exposure-to-internet-via-istio-ingress/test/failed_with_beta_multiservice/input/vs.yaml b/rules/exposure-to-internet-via-istio-ingress/test/failed_with_beta_multiservice/input/vs.yaml new file mode 100644 index 000000000..37f65359b --- /dev/null +++ b/rules/exposure-to-internet-via-istio-ingress/test/failed_with_beta_multiservice/input/vs.yaml @@ -0,0 +1,57 @@ +apiVersion: networking.istio.io/v1beta1 +kind: VirtualService +metadata: + creationTimestamp: "2024-04-15T08:56:16Z" + generation: 2 + labels: + app: int-0721 + context: default + name: int-0721 + owner: int-0721 + owner-namespace: kt-itinternal + role: app + name: int-0721 + namespace: kt-itinternal +spec: + gateways: + - kt-connections/prod-lan-gateway + - kt-connections/sdfsdfs-gateway + - kt-connections/api-public + - kt-connections/aaaaa-public + - kt-connections/mydddd-public + hosts: + - api.stg.prod.lan + http: + - corsPolicy: + allowHeaders: + - authorization + - Origin + - Content-Type + - Accept + allowMethods: + - POST + - GET + - OPTIONS + - PUT + - PATCH + - DELETE + allowOrigins: + - regex: .* + match: + - authority: + exact: api.stg.prod.lan + uri: + prefix: /int/0698/ + name: api-http-stgprodlan + rewrite: + uri: / + route: + - destination: + host: int-0721.kt-itinternal.svc.cluster.local + port: + number: 8080 + headers: + request: + set: + X-Forwarded-Prefix: /int/0698 + X-Forwarded-Proto: https \ No newline at end of file diff --git a/rules/exposure-to-internet-via-istio-ingress/test/failed_with_beta_multiservice/service.yaml b/rules/exposure-to-internet-via-istio-ingress/test/failed_with_beta_multiservice/service.yaml new file mode 100644 index 000000000..07b2c3e1e --- /dev/null +++ b/rules/exposure-to-internet-via-istio-ingress/test/failed_with_beta_multiservice/service.yaml @@ -0,0 +1,48 @@ +apiVersion: v1 +kind: Service +metadata: + annotations: + ctx.vrs.meeseeks.sre.monaco-telecom.mc/default: "3664777130" + ms.vrs.meeseeks.sre.monaco-telecom.mc/int-0721: "3779862107" + pe.vrs.meeseeks.sre.monaco-telecom.mc/int-0721: "2387894954" + creationTimestamp: "2024-04-15T09:00:11Z" + labels: + app: int-0721 + context: default + name: int-0721 + owner: int-0721 + owner-namespace: kt-itinternal + role: app + name: int-0721 + namespace: kt-itinternal + ownerReferences: + - apiVersion: msss/v1alpha1 + blockOwnerDeletion: true + controller: true + kind: Microservice + name: int-0721 + uid: 14a69d5b-249c-487d-9500-645bda6a4c56 + resourceVersion: "3779885629" + uid: 0428cb70-5d8f-4345-8ef2-5b0a249e0793 +spec: + clusterIP: 10.81.208.49 + clusterIPs: + - 10.81.208.49 + internalTrafficPolicy: Cluster + ipFamilies: + - IPv4 + ipFamilyPolicy: SingleStack + ports: + - name: http + port: 8080 + protocol: TCP + targetPort: 8080 + selector: + app: int-0721 + context: default + name: int-0721 + role: app + sessionAffinity: None + type: ClusterIP +status: + loadBalancer: {} \ No newline at end of file diff --git a/rules/exposure-to-internet-via-istio-ingress/test/failed_with_beta_multiservice/service2.yaml b/rules/exposure-to-internet-via-istio-ingress/test/failed_with_beta_multiservice/service2.yaml new file mode 100644 index 000000000..474f7278a --- /dev/null +++ b/rules/exposure-to-internet-via-istio-ingress/test/failed_with_beta_multiservice/service2.yaml @@ -0,0 +1,48 @@ +apiVersion: v1 +kind: Service +metadata: + annotations: + ctx.vrs.meeseeks.sre.monaco-telecom.mc/default: "3664777130" + ms.vrs.meeseeks.sre.monaco-telecom.mc/int-0721: "3779862107" + pe.vrs.meeseeks.sre.monaco-telecom.mc/int-0721: "2387894954" + creationTimestamp: "2024-04-15T09:00:11Z" + labels: + app: int-0721 + context: default + name: int-0721 + owner: int-0721 + owner-namespace: kt-itinternal + role: app + name: int-0721 + namespace: kt-itinternal-2 + ownerReferences: + - apiVersion: msss/v1alpha1 + blockOwnerDeletion: true + controller: true + kind: Microservice + name: int-0721 + uid: 14a69d5b-249c-487d-9500-645bda6a4c56 + resourceVersion: "3779885629" + uid: 0428cb70-5d8f-4345-8ef2-5b0a249e0793 +spec: + clusterIP: 10.81.208.49 + clusterIPs: + - 10.81.208.49 + internalTrafficPolicy: Cluster + ipFamilies: + - IPv4 + ipFamilyPolicy: SingleStack + ports: + - name: http + port: 8080 + protocol: TCP + targetPort: 8080 + selector: + app: int-0721 + context: default + name: int-0721 + role: app + sessionAffinity: None + type: ClusterIP +status: + loadBalancer: {} \ No newline at end of file diff --git a/rules/exposure-to-internet-via-istio-ingress/test/failed_with_beta_multiservice/vs.yaml b/rules/exposure-to-internet-via-istio-ingress/test/failed_with_beta_multiservice/vs.yaml new file mode 100644 index 000000000..13e3c06a5 --- /dev/null +++ b/rules/exposure-to-internet-via-istio-ingress/test/failed_with_beta_multiservice/vs.yaml @@ -0,0 +1,57 @@ +apiVersion: networking.istio.io/v1beta1 +kind: VirtualService +metadata: + creationTimestamp: "2024-04-15T08:56:16Z" + generation: 2 + labels: + app: int-0721 + context: default + name: int-0721 + owner: int-0721 + owner-namespace: kt-itinternal + role: app + name: int-0721 + namespace: kt-itinternal +spec: + gateways: + - kt-connections/wildcard-stg-gateway + - kt-connections/sdfsdfs-gateway + - kt-connections/api-public + - kt-connections/aaaaa-public + - kt-connections/mydddd-public + hosts: + - api.stg.prod.lan + http: + - corsPolicy: + allowHeaders: + - authorization + - Origin + - Content-Type + - Accept + allowMethods: + - POST + - GET + - OPTIONS + - PUT + - PATCH + - DELETE + allowOrigins: + - regex: .* + match: + - authority: + exact: api.stg.prod.lan + uri: + prefix: /int/0698/ + name: api-http-stgprodlan + rewrite: + uri: / + route: + - destination: + host: int-0721.kt-itinternal.svc.cluster.local + port: + number: 8080 + headers: + request: + set: + X-Forwarded-Prefix: /int/0698 + X-Forwarded-Proto: https \ No newline at end of file diff --git a/rules/exposure-to-internet/raw.rego b/rules/exposure-to-internet/raw.rego index d4e849926..1efd84a4b 100644 --- a/rules/exposure-to-internet/raw.rego +++ b/rules/exposure-to-internet/raw.rego @@ -9,7 +9,9 @@ deny[msga] { wl := input[_] spec_template_spec_patterns := {"Deployment", "ReplicaSet", "DaemonSet", "StatefulSet", "Pod", "Job", "CronJob"} spec_template_spec_patterns[wl.kind] - wl_connected_to_service(wl, service) + is_same_namespace(wl.metadata, service.metadata) + pod := get_pod_spec(wl)["spec"] + wl_connected_to_service(pod, service) failPath := ["spec.type"] msga := { "alertMessage": sprintf("workload '%v' is exposed through service '%v'", [wl.metadata.name, service.metadata.name]), @@ -46,6 +48,7 @@ deny[msga] { wl := input[_] spec_template_spec_patterns := {"Deployment", "ReplicaSet", "DaemonSet", "StatefulSet", "Pod", "Job", "CronJob"} spec_template_spec_patterns[wl.kind] + is_same_namespace(wl.metadata, svc.metadata) wl_connected_to_service(wl, svc) result := svc_connected_to_ingress(svc, ingress) @@ -82,6 +85,7 @@ is_exposed_service(svc) { svc.spec.type == "LoadBalancer" } + wl_connected_to_service(wl, svc) { count({x | svc.spec.selector[x] == wl.metadata.labels[x]}) == count(svc.spec.selector) } @@ -103,3 +107,43 @@ svc_connected_to_ingress(svc, ingress) = result { } + +is_same_namespace(metadata1, metadata2) { + metadata1.namespace == metadata2.namespace +} + +is_same_namespace(metadata1, metadata2) { + not metadata1.namespace + not metadata2.namespace +} + +is_same_namespace(metadata1, metadata2) { + not metadata2.namespace + metadata1.namespace == "default" +} + +is_same_namespace(metadata1, metadata2) { + not metadata1.namespace + metadata2.namespace == "default" +} + + + +# get_volume - get resource spec paths for {"Deployment","ReplicaSet","DaemonSet","StatefulSet","Job"} +get_pod_spec(resources) := result { + resources_kinds := {"Deployment","ReplicaSet","DaemonSet","StatefulSet","Job"} + resources_kinds[resources.kind] + result = {"spec": resources.spec.template, "start_of_path": "spec.template."} +} + +# get_volume - get resource spec paths for "Pod" +get_pod_spec(resources) := result { + resources.kind == "Pod" + result = {"spec": resources, "start_of_path": ""} +} + +# get_volume - get resource spec paths for "CronJob" +get_pod_spec(resources) := result { + resources.kind == "CronJob" + result = {"spec": resources.spec.jobTemplate.spec.template.spec, "start_of_path": "spec.jobTemplate.spec.template.spec."} +} diff --git a/rules/exposure-to-internet/test/failed_with_service_nodeport/expected.json b/rules/exposure-to-internet/test/failed_with_service_nodeport/expected.json index f7b9f7d97..e08a934f7 100644 --- a/rules/exposure-to-internet/test/failed_with_service_nodeport/expected.json +++ b/rules/exposure-to-internet/test/failed_with_service_nodeport/expected.json @@ -23,7 +23,8 @@ "apiVersion": "v1", "kind": "Service", "metadata": { - "name": "my-service" + "name": "my-service", + "namespace": "ns1" }, "spec": { "ports": [ diff --git a/rules/exposure-to-internet/test/failed_with_service_nodeport/input/deployment.yaml b/rules/exposure-to-internet/test/failed_with_service_nodeport/input/deployment.yaml index 49a2f5106..cc2367fae 100644 --- a/rules/exposure-to-internet/test/failed_with_service_nodeport/input/deployment.yaml +++ b/rules/exposure-to-internet/test/failed_with_service_nodeport/input/deployment.yaml @@ -2,7 +2,7 @@ apiVersion: apps/v1 kind: Deployment metadata: name: my-app - namespace: default + namespace: ns1 spec: selector: matchLabels: diff --git a/rules/exposure-to-internet/test/failed_with_service_nodeport/input/service.yaml b/rules/exposure-to-internet/test/failed_with_service_nodeport/input/service.yaml index 94654bcb9..7a8cd132d 100644 --- a/rules/exposure-to-internet/test/failed_with_service_nodeport/input/service.yaml +++ b/rules/exposure-to-internet/test/failed_with_service_nodeport/input/service.yaml @@ -2,6 +2,7 @@ apiVersion: v1 kind: Service metadata: name: my-service + namespace: ns1 spec: selector: app: argo-server diff --git a/rules/exposure-to-internet/test/failed_with_service_nodeport/input/service2.yaml b/rules/exposure-to-internet/test/failed_with_service_nodeport/input/service2.yaml new file mode 100644 index 000000000..73dfe2737 --- /dev/null +++ b/rules/exposure-to-internet/test/failed_with_service_nodeport/input/service2.yaml @@ -0,0 +1,13 @@ +apiVersion: v1 +kind: Service +metadata: + name: my-service2 + namespace: ns2 +spec: + selector: + app: argo-server + type: NodePort + ports: + - port: 80 + targetPort: 80 + nodePort: 30008 diff --git a/rules/outdated-k8s-version/raw.rego b/rules/outdated-k8s-version/raw.rego index 810931a3b..2f7d109f5 100644 --- a/rules/outdated-k8s-version/raw.rego +++ b/rules/outdated-k8s-version/raw.rego @@ -18,7 +18,7 @@ deny[msga] { has_outdated_version(version) { # the `supported_k8s_versions` is validated in the validations script against "https://api.github.com/repos/kubernetes/kubernetes/releases" - supported_k8s_versions := ["v1.31", "v1.30", "v1.29"] + supported_k8s_versions := ["v1.32", "v1.31", "v1.30"] every v in supported_k8s_versions{ not startswith(version, v) } diff --git a/rules/unauthenticated-service/raw.rego b/rules/unauthenticated-service/raw.rego index 4ba95ab14..d6b9e007e 100644 --- a/rules/unauthenticated-service/raw.rego +++ b/rules/unauthenticated-service/raw.rego @@ -10,6 +10,7 @@ deny contains msga if { wl := input[_] spec_template_spec_patterns := {"Deployment", "ReplicaSet", "DaemonSet", "StatefulSet", "Pod", "Job", "CronJob"} spec_template_spec_patterns[wl.kind] + is_same_namespace(wl, service) wl_connected_to_service(wl, service) service_scan_result := input[_] @@ -34,6 +35,8 @@ has_unauthenticated_service(service_name, namespace, service_scan_result) if { service_scan_result.spec.ports[_].authenticated == false } + + wl_connected_to_service(wl, svc) if { count({x | svc.spec.selector[x] == wl.metadata.labels[x]}) == count(svc.spec.selector) } @@ -41,3 +44,23 @@ wl_connected_to_service(wl, svc) if { wl_connected_to_service(wl, svc) if { wl.spec.selector.matchLabels == svc.spec.selector } + + +is_same_namespace(metadata1, metadata2) { + metadata1.namespace == metadata2.namespace +} + +is_same_namespace(metadata1, metadata2) { + not metadata1.namespace + not metadata2.namespace +} + +is_same_namespace(metadata1, metadata2) { + not metadata2.namespace + metadata1.namespace == "default" +} + +is_same_namespace(metadata1, metadata2) { + not metadata1.namespace + metadata2.namespace == "default" +} \ No newline at end of file