-
Notifications
You must be signed in to change notification settings - Fork 48
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #570 from kubescape/feature/support-httproute-in-e…
…xposed-to-internet-rule Support HTTPRoute in Gateway API
- Loading branch information
Showing
14 changed files
with
896 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
{ | ||
"name": "Exposure to internet via Gateway API", | ||
"attributes": { | ||
"controlTypeTags": [ | ||
"security" | ||
], | ||
"attackTracks": [ | ||
{ | ||
"attackTrack": "workload-external-track", | ||
"categories": [ | ||
"Initial Access" | ||
] | ||
}, | ||
{ | ||
"attackTrack": "service-destruction", | ||
"categories": [ | ||
"Initial Access" | ||
] | ||
} | ||
] | ||
}, | ||
"description": "This control detect workloads that are exposed on Internet through a Gateway API (HTTPRoute,TCPRoute, UDPRoute). It fails in case it find workloads connected with these resources.", | ||
"remediation": "The user can evaluate its exposed resources and apply relevant changes wherever needed.", | ||
"rulesNames": ["exposure-to-internet-via-gateway-api"], | ||
"test": "Checks if workloads are exposed through the use of Gateway API (HTTPRoute,TCPRoute, UDPRoute).", | ||
"controlID": "C-0266", | ||
"baseScore": 7.0, | ||
"scanningScope": { | ||
"matches": [ | ||
"cluster" | ||
] | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
package armo_builtins | ||
import future.keywords.in | ||
|
||
|
||
deny[msga] { | ||
httproute := input[_] | ||
httproute.kind in ["HTTPRoute", "TCPRoute", "UDPRoute"] | ||
|
||
svc := input[_] | ||
svc.kind == "Service" | ||
|
||
# Make sure that they belong to the same namespace | ||
svc.metadata.namespace == httproute.metadata.namespace | ||
|
||
# avoid duplicate alerts | ||
# if service is already exposed through NodePort or LoadBalancer workload will fail on that | ||
not is_exposed_service(svc) | ||
|
||
wl := input[_] | ||
wl.metadata.namespace == svc.metadata.namespace | ||
spec_template_spec_patterns := {"Deployment", "ReplicaSet", "DaemonSet", "StatefulSet", "Pod", "Job", "CronJob"} | ||
spec_template_spec_patterns[wl.kind] | ||
wl_connected_to_service(wl, svc) | ||
|
||
result := svc_connected_to_httproute(svc, httproute) | ||
|
||
msga := { | ||
"alertMessage": sprintf("workload '%v' is exposed through httproute '%v'", [wl.metadata.name, httproute.metadata.name]), | ||
"packagename": "armo_builtins", | ||
"failedPaths": [], | ||
"fixPaths": [], | ||
"alertScore": 7, | ||
"alertObject": { | ||
"k8sApiObjects": [wl] | ||
}, | ||
"relatedObjects": [ | ||
{ | ||
"object": httproute, | ||
"reviewPaths": result, | ||
"failedPaths": result, | ||
}, | ||
{ | ||
"object": svc, | ||
} | ||
] | ||
} | ||
} | ||
|
||
# ==================================================================================== | ||
|
||
is_exposed_service(svc) { | ||
svc.spec.type == "NodePort" | ||
} | ||
|
||
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) | ||
} | ||
|
||
wl_connected_to_service(wl, svc) { | ||
wl.spec.selector.matchLabels == svc.spec.selector | ||
} | ||
|
||
wl_connected_to_service(wl, svc) { | ||
count({x | svc.spec.selector[x] == wl.spec.template.metadata.labels[x]}) == count(svc.spec.selector) | ||
} | ||
|
||
svc_connected_to_httproute(svc, httproute) = result { | ||
rule := httproute.spec.rules[i] | ||
ref := rule.backendRefs[j] | ||
ref.kind == "Service" | ||
svc.metadata.name == ref.name | ||
result := [sprintf("spec.rules[%d].backendRefs[%d].name", [i,j])] | ||
} | ||
|
62 changes: 62 additions & 0 deletions
62
rules/exposure-to-internet-via-gateway-api/rule.metadata.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
{ | ||
"name": "exposure-to-internet-via-gateway-api", | ||
"attributes": { | ||
}, | ||
"ruleLanguage": "Rego", | ||
"match": [ | ||
{ | ||
"apiGroups": [ | ||
"" | ||
], | ||
"apiVersions": [ | ||
"v1" | ||
], | ||
"resources": [ | ||
"Pod", | ||
"Service" | ||
] | ||
}, | ||
{ | ||
"apiGroups": [ | ||
"apps" | ||
], | ||
"apiVersions": [ | ||
"v1" | ||
], | ||
"resources": [ | ||
"Deployment", | ||
"ReplicaSet", | ||
"DaemonSet", | ||
"StatefulSet" | ||
] | ||
}, | ||
{ | ||
"apiGroups": [ | ||
"batch" | ||
], | ||
"apiVersions": [ | ||
"*" | ||
], | ||
"resources": [ | ||
"Job", | ||
"CronJob" | ||
] | ||
}, | ||
{ | ||
"apiGroups": [ | ||
"gateway.networking.k8s.io" | ||
], | ||
"apiVersions": [ | ||
"v1", "v1alpha2" | ||
], | ||
"resources": [ | ||
"HTTPRoute", | ||
"TCPRoute", | ||
"UDPRoute" | ||
] | ||
} | ||
], | ||
"description": "fails in case the running workload has binded Service and Gateway that are exposing it on Internet.", | ||
"remediation": "", | ||
"ruleQuery": "armo_builtins" | ||
} |
185 changes: 185 additions & 0 deletions
185
rules/exposure-to-internet-via-gateway-api/test/failed_with_httproute-istio/expected.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,185 @@ | ||
[ | ||
{ | ||
"alertMessage": "workload 'httpbin' is exposed through httproute 'http'", | ||
"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": { | ||
"annotations": { | ||
"kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"gateway.networking.k8s.io/v1beta1\",\"kind\":\"HTTPRoute\",\"metadata\":{\"annotations\":{},\"name\":\"http\",\"namespace\":\"default\"},\"spec\":{\"hostnames\":[\"httpbin.example.com\"],\"parentRefs\":[{\"name\":\"gateway\",\"namespace\":\"istio-ingress\"}],\"rules\":[{\"backendRefs\":[{\"name\":\"httpbin\",\"port\":8000}],\"matches\":[{\"path\":{\"type\":\"PathPrefix\",\"value\":\"/get\"}}]}]}}\n" | ||
}, | ||
"creationTimestamp": "2024-04-14T07:41:31Z", | ||
"generation": 1, | ||
"name": "http", | ||
"namespace": "default", | ||
"resourceVersion": "2647", | ||
"uid": "b7c1d09f-0cf8-4fc6-ada8-ec415b463038" | ||
}, | ||
"spec": { | ||
"hostnames": [ | ||
"httpbin.example.com" | ||
], | ||
"parentRefs": [ | ||
{ | ||
"group": "gateway.networking.k8s.io", | ||
"kind": "Gateway", | ||
"name": "gateway", | ||
"namespace": "istio-ingress" | ||
} | ||
], | ||
"rules": [ | ||
{ | ||
"backendRefs": [ | ||
{ | ||
"group": "", | ||
"kind": "Service", | ||
"name": "httpbin", | ||
"port": 8000, | ||
"weight": 1 | ||
} | ||
], | ||
"matches": [ | ||
{ | ||
"path": { | ||
"type": "PathPrefix", | ||
"value": "/get" | ||
} | ||
} | ||
] | ||
} | ||
] | ||
}, | ||
"status": { | ||
"parents": [ | ||
{ | ||
"conditions": [ | ||
{ | ||
"lastTransitionTime": "2024-04-14T07:41:38Z", | ||
"message": "", | ||
"observedGeneration": 1, | ||
"reason": "Accepted", | ||
"status": "True", | ||
"type": "Accepted" | ||
}, | ||
{ | ||
"lastTransitionTime": "2024-04-14T07:41:38Z", | ||
"message": "", | ||
"observedGeneration": 1, | ||
"reason": "ResolvedRefs", | ||
"status": "True", | ||
"type": "ResolvedRefs" | ||
} | ||
], | ||
"controllerName": "solo.io/gloo-gateway", | ||
"parentRef": { | ||
"group": "gateway.networking.k8s.io", | ||
"kind": "Gateway", | ||
"name": "gateway", | ||
"namespace": "istio-ingress" | ||
} | ||
}, | ||
{ | ||
"conditions": [ | ||
{ | ||
"lastTransitionTime": "2024-04-14T07:41:38Z", | ||
"message": "Route was valid", | ||
"observedGeneration": 1, | ||
"reason": "Accepted", | ||
"status": "True", | ||
"type": "Accepted" | ||
}, | ||
{ | ||
"lastTransitionTime": "2024-04-14T07:41:38Z", | ||
"message": "All references resolved", | ||
"observedGeneration": 1, | ||
"reason": "ResolvedRefs", | ||
"status": "True", | ||
"type": "ResolvedRefs" | ||
} | ||
], | ||
"controllerName": "istio.io/gateway-controller", | ||
"parentRef": { | ||
"group": "gateway.networking.k8s.io", | ||
"kind": "Gateway", | ||
"name": "gateway", | ||
"namespace": "istio-ingress" | ||
} | ||
} | ||
] | ||
} | ||
}, | ||
"failedPaths": [ | ||
"spec.rules[0].backendRefs[0].name" | ||
], | ||
"reviewPaths": [ | ||
"spec.rules[0].backendRefs[0].name" | ||
] | ||
}, | ||
{ | ||
"object": { | ||
"apiVersion": "v1", | ||
"kind": "Service", | ||
"metadata": { | ||
"annotations": { | ||
"kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"v1\",\"kind\":\"Service\",\"metadata\":{\"annotations\":{},\"labels\":{\"app\":\"httpbin\",\"service\":\"httpbin\"},\"name\":\"httpbin\",\"namespace\":\"default\"},\"spec\":{\"ports\":[{\"name\":\"http\",\"port\":8000,\"targetPort\":8080}],\"selector\":{\"app\":\"httpbin\"}}}\n" | ||
}, | ||
"creationTimestamp": "2024-04-14T07:39:35Z", | ||
"labels": { | ||
"app": "httpbin", | ||
"service": "httpbin" | ||
}, | ||
"name": "httpbin", | ||
"namespace": "default", | ||
"resourceVersion": "2328", | ||
"uid": "5b675069-a387-4fa4-83b6-8fd25462f714" | ||
}, | ||
"spec": { | ||
"clusterIP": "10.96.126.137", | ||
"clusterIPs": [ | ||
"10.96.126.137" | ||
], | ||
"internalTrafficPolicy": "Cluster", | ||
"ipFamilies": [ | ||
"IPv4" | ||
], | ||
"ipFamilyPolicy": "SingleStack", | ||
"ports": [ | ||
{ | ||
"name": "http", | ||
"port": 8000, | ||
"protocol": "TCP", | ||
"targetPort": 8080 | ||
} | ||
], | ||
"selector": { | ||
"app": "httpbin" | ||
}, | ||
"sessionAffinity": "None", | ||
"type": "ClusterIP" | ||
}, | ||
"status": { | ||
"loadBalancer": {} | ||
} | ||
} | ||
} | ||
] | ||
} | ||
] |
Oops, something went wrong.