Skip to content

Commit b591146

Browse files
authored
Merge pull request #66 from Peefy/add-more-container-validation-modules
feat: add more container validation modules
2 parents 43e4ced + 1fda8db commit b591146

File tree

25 files changed

+283
-2
lines changed

25 files changed

+283
-2
lines changed

k8s_manifests_containers/kcl.mod

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
[package]
22
name = "k8s_manifests_containers"
3-
version = "0.1.0"
3+
version = "0.1.1"
44
description = "`k8s_manifests_containers` can be used to get all containers resources in a Pod resource."

k8s_manifests_containers/main.k

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ is_exempt = lambda image: str, exemptImages: [str] = [] -> bool {
99
}
1010

1111
# Get Containers from the input resource item.
12-
get_containers = lambda item, exemptImages = [] -> [] {
12+
get_containers = lambda item: {str:}, exemptImages: [str] = [] -> [] {
1313
containers = []
1414
if item.kind == "Pod":
1515
containers = (item.spec.containers or []) + (item.spec.initContainers or []) + (item.spec.ephemeralContainers or [])

required-drop-all/README.md

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
## Introduction
2+
3+
`require-pod-requests-limits` is a KCL validation module
4+
5+
## Resource
6+
7+
The Code source and documents are [here](https://github.com/kcl-lang/artifacthub/tree/main/require-pod-requests-limits)

required-drop-all/kcl.mod

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
[package]
2+
name = "require-pod-requests-limits"
3+
version = "0.1.0"
4+
description = "`require-pod-requests-limits` is a KCL validation module"
5+

required-drop-all/kcl.mod.lock

Whitespace-only changes.

required-drop-all/main.k

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
"""Containers must drop `ALL` capabilities."""
2+
# Judge a image in a container config is exempt
3+
is_exempt = lambda image: str, exemptImages: [str] = [] -> bool {
4+
result = False
5+
if exemptImages:
6+
result = any exempt_image in exemptImages {
7+
(image.startswith(exempt_image.removesuffix("*")) if exempt_image.endswith("*") else exempt_image == image)
8+
}
9+
result
10+
}
11+
12+
# Get Containers from the input resource item.
13+
get_containers = lambda item: {str:}, exemptImages: [str] = [] -> [{str:}] {
14+
containers = []
15+
if item.kind == "Pod":
16+
containers = (item.spec.containers or []) + (item.spec.initContainers or []) + (item.spec.ephemeralContainers or [])
17+
elif item.kind == "Deployment":
18+
containers = (item.spec.template.spec.containers or []) + (item.spec.template.spec.initContainers or []) + (item.spec.template.spec.ephemeralContainers or [])
19+
containers = [c for c in containers if not is_exempt(c.image, exemptImages)]
20+
}
21+
22+
validate_container = lambda container: {str:} -> bool {
23+
drop: [str] = container?.securityContext?.capabilities?.drop or []
24+
any d in drop {
25+
d.upper() == "ALL"
26+
}
27+
}
28+
29+
# Define the validation function
30+
validate = lambda item: {str:} {
31+
containers = get_containers(item)
32+
if containers:
33+
container_list_disallow = [c.name for c in containers if not validate_container(c)]
34+
assert len(container_list_disallow) == 0, "CPU and memory resource requests and limits are required. for containers {}".format(container_list_disallow)
35+
item
36+
}
37+
# Validate All resource
38+
items = [validate(i) for i in option("items") or []]

required-drop-cap-net-all/README.md

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
## Introduction
2+
3+
`required-drop-cap-net-all` is a KCL validation module
4+
5+
## Resource
6+
7+
The Code source and documents are [here](https://github.com/kcl-lang/artifacthub/tree/main/required-drop-cap-net-all)

required-drop-cap-net-all/kcl.mod

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
[package]
2+
name = "required-drop-cap-net-all"
3+
version = "0.1.0"
4+
description = "`required-drop-cap-net-all` is a KCL validation module"
5+

required-drop-cap-net-all/kcl.mod.lock

Whitespace-only changes.

required-drop-cap-net-all/main.k

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
"""Containers must drop `ALL` capabilities."""
2+
# Judge a image in a container config is exempt
3+
is_exempt = lambda image: str, exemptImages: [str] = [] -> bool {
4+
result = False
5+
if exemptImages:
6+
result = any exempt_image in exemptImages {
7+
(image.startswith(exempt_image.removesuffix("*")) if exempt_image.endswith("*") else exempt_image == image)
8+
}
9+
result
10+
}
11+
12+
# Get Containers from the input resource item.
13+
get_containers = lambda item: {str:}, exemptImages: [str] = [] -> [{str:}] {
14+
containers = []
15+
if item.kind == "Pod":
16+
containers = (item.spec.containers or []) + (item.spec.initContainers or []) + (item.spec.ephemeralContainers or [])
17+
elif item.kind == "Deployment":
18+
containers = (item.spec.template.spec.containers or []) + (item.spec.template.spec.initContainers or []) + (item.spec.template.spec.ephemeralContainers or [])
19+
containers = [c for c in containers if not is_exempt(c.image, exemptImages)]
20+
}
21+
22+
validate_container = lambda container: {str:} -> bool {
23+
drop: [str] = container?.securityContext?.capabilities?.drop or []
24+
any d in drop {
25+
d.upper() == "CAP_NET_RAW"
26+
}
27+
}
28+
29+
# Define the validation function
30+
validate = lambda item: {str:} {
31+
containers = get_containers(item)
32+
if containers:
33+
container_list_disallow = [c.name for c in containers if not validate_container(c)]
34+
assert len(container_list_disallow) == 0, "CPU and memory resource requests and limits are required. for containers {}".format(container_list_disallow)
35+
item
36+
}
37+
# Validate All resource
38+
items = [validate(i) for i in option("items") or []]
+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
## Introduction
2+
3+
`require-pod-requests-limits` is a KCL validation module
4+
5+
## Resource
6+
7+
The Code source and documents are [here](https://github.com/kcl-lang/artifacthub/tree/main/require-pod-requests-limits)

required-pod-requests-limits/kcl.mod

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
[package]
2+
name = "require-pod-requests-limits"
3+
version = "0.1.0"
4+
description = "`require-pod-requests-limits` is a KCL validation module"
5+

required-pod-requests-limits/kcl.mod.lock

Whitespace-only changes.

required-pod-requests-limits/main.k

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# Judge a image in a container config is exempt
2+
is_exempt = lambda image: str, exemptImages: [str] = [] -> bool {
3+
result = False
4+
if exemptImages:
5+
result = any exempt_image in exemptImages {
6+
(image.startswith(exempt_image.removesuffix("*")) if exempt_image.endswith("*") else exempt_image == image)
7+
}
8+
result
9+
}
10+
11+
# Get Containers from the input resource item.
12+
get_containers = lambda item: {str:}, exemptImages: [str] = [] -> [{str:}] {
13+
containers = []
14+
if item.kind == "Pod":
15+
containers = (item.spec.containers or []) + (item.spec.initContainers or []) + (item.spec.ephemeralContainers or [])
16+
elif item.kind == "Deployment":
17+
containers = (item.spec.template.spec.containers or []) + (item.spec.template.spec.initContainers or []) + (item.spec.template.spec.ephemeralContainers or [])
18+
containers = [c for c in containers if not is_exempt(c.image, exemptImages)]
19+
}
20+
21+
validate_pod_resources = lambda container: {str:} -> bool {
22+
container?.requests?.memory and container?.requests?.cpu and container?.limits?.memory
23+
}
24+
25+
# Define the validation function
26+
validate = lambda item: {str:} {
27+
containers = get_containers(item)
28+
if containers:
29+
container_list_disallow = [c.name for c in containers if not validate_pod_resources(c)]
30+
assert len(container_list_disallow) == 0, "CPU and memory resource requests and limits are required. for containers {}".format(container_list_disallow)
31+
item
32+
}
33+
# Validate All resource
34+
items = [validate(i) for i in option("items") or []]

required-root-fs/README.md

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
## Introduction
2+
3+
`required-root-fs` is a KCL validation module
4+
5+
## Resource
6+
7+
The Code source and documents are [here](https://github.com/kcl-lang/artifacthub/tree/main/required-root-fs)

required-root-fs/kcl.mod

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
[package]
2+
name = "required-root-fs"
3+
version = "0.1.0"
4+
description = "`required-root-fs` is a KCL validation module"
5+

required-root-fs/kcl.mod.lock

Whitespace-only changes.

required-root-fs/main.k

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
validate_root_fs = lambda container: {str:} -> bool {
2+
container?.securityContext?.readOnlyRootFilesystem is True
3+
}
4+
5+
# Define the validation function
6+
validate = lambda item {
7+
containers: [{str:}] = []
8+
if item.kind == "Pod":
9+
containers = (item.spec.containers or []) + (item.spec.initContainers or []) + (item.spec.ephemeralContainers or [])
10+
elif item.kind == "Deployment":
11+
containers = (item.spec.template.spec.containers or []) + (item.spec.template.spec.initContainers or []) + (item.spec.template.spec.ephemeralContainers or [])
12+
if containers:
13+
container_list_disallow = [c.name for c in containers if not validate_root_fs(c)]
14+
assert len(container_list_disallow) == 0, "Root filesystem must be read-only for containers {}".format(container_list_disallow)
15+
item
16+
}
17+
# Validate All resource
18+
items = [validate(i) for i in option("items") or []]

restrict-image-registries/README.md

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
## Introduction
2+
3+
`restrict-image-registries` is a KCL validation module
4+
5+
## Resource
6+
7+
The Code source and documents are [here](https://github.com/kcl-lang/artifacthub/tree/main/restrict-image-registries)

restrict-image-registries/kcl.mod

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
[package]
2+
name = "restrict-image-registries"
3+
version = "0.1.0"
4+
description = "`restrict-image-registries` is a KCL validation module"
5+

restrict-image-registries/kcl.mod.lock

Whitespace-only changes.

restrict-image-registries/main.k

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import yaml
2+
3+
registries: [str] = option("params")?.registries or []
4+
5+
validate_image_registry = lambda image: str, registries: [str] -> bool {
6+
any registry in registries {
7+
image.startswith(registry)
8+
}
9+
}
10+
11+
# Define the validation function
12+
validate = lambda item, registries: [str] {
13+
containers: [{str:}] = []
14+
if item.kind == "Pod":
15+
containers = (item.spec.containers or []) + (item.spec.initContainers or []) + (item.spec.ephemeralContainers or [])
16+
elif item.kind == "Deployment":
17+
containers = (item.spec.template.spec.containers or []) + (item.spec.template.spec.initContainers or []) + (item.spec.template.spec.ephemeralContainers or [])
18+
if containers:
19+
image_list_disallow = [c.image for c in containers if not validate_image_registry(c.image, registries)]
20+
assert len(image_list_disallow) == 0, "container images {} is not allowed, expected {}".format(image_list_disallow, registries)
21+
item
22+
}
23+
# Validate All resource
24+
items = [validate(i, registries) for i in option("items") or []]
25+
26+
if option("__test__"):
27+
validate(yaml.decode("""\
28+
apiVersion: v1
29+
kind: Pod
30+
metadata:
31+
name: goodpod02-registry
32+
namespace: ir-pods-namespace
33+
spec:
34+
initContainers:
35+
- name: nginx-init
36+
image: bar.io/nginx
37+
- name: busybox-init
38+
image: eu.foo.io/busybox
39+
containers:
40+
- name: k8s-nginx
41+
image: bar.io/nginx
42+
- name: busybox
43+
image: eu.foo.io1/busybox
44+
"""), ["bar.io/", "eu.foo.io/"])
+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
## Introduction
2+
3+
`restrict-service-external-ips` is a KCL validation module
4+
5+
## Resource
6+
7+
The Code source and documents are [here](https://github.com/kcl-lang/artifacthub/tree/main/restrict-service-external-ips)

restrict-service-external-ips/kcl.mod

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
[package]
2+
name = "restrict-service-external-ips"
3+
version = "0.1.0"
4+
description = "`restrict-service-external-ips` is a KCL validation module"
5+

restrict-service-external-ips/main.k

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
"""Service externalIPs can be used for a MITM attack (CVE-2020-8554).
2+
Restrict externalIPs or limit to a known set of addresses.
3+
See: https://github.com/kyverno/kyverno/issues/1367. This policy validates
4+
that the `externalIPs` field is not set on a Service.
5+
"""
6+
import yaml
7+
8+
externalIPs: [str] = option("params")?.externalIPs or []
9+
10+
# Define the validation function
11+
validate = lambda item, externalIPs: [str] {
12+
if item.kind == "Service" and externalIPs:
13+
input = item?.spec?.externalIPs or []
14+
assert all ip in input {
15+
ip in externalIPs
16+
} if input, "externalIPs ${item?.spec?.externalIPs} are not allowed, expected ${externalIPs}"
17+
item
18+
}
19+
# Validate All resource
20+
items = [validate(i, externalIPs) for i in option("items") or []]
21+
22+
if option("__test__"):
23+
validate(yaml.decode("""\
24+
apiVersion: v1
25+
kind: Service
26+
metadata:
27+
name: badservice01-eip
28+
spec:
29+
selector:
30+
app: MyApp
31+
ports:
32+
- protocol: TCP
33+
port: 80
34+
targetPort: 9376
35+
externalIPs:
36+
- 127.0.0.1 # Error suite: 127.0.0.2
37+
"""), ["127.0.0.1"])

0 commit comments

Comments
 (0)