Skip to content
Snippets Groups Projects
Commit 79d6e2f0 authored by Alain Atemnkeng's avatar Alain Atemnkeng :first_place:
Browse files

Update 4 files

- /Chart.yaml
- /templates/constraint_image_admission_controller.yaml
- /rego/image_admission_control/policy.rego
- /rego/image_admission_control/policy_test.rego
parent 26e1cbbd
Branches
Tags
1 merge request!72[PLATFORM-2185] Configure Image Provenance admission control with Gatekeeper
Pipeline #851129 passed with stages
in 16 seconds
apiVersion: v2
name: constraint-templates
version: 1.5.5
version: 1.5.9
appVersion: 1.0.0
package k8simageprovenance
# List of exactly allowed repositories
allowed_repos := {
"docker.io",
"quay.io",
"public.ecr.aws",
"gcr.io",
"ghcr.io",
"harbor.io",
"harbor.it.vt.edu",
"gitlab.com",
"code.vt.edu",
"registry-1.docker.io",
"602401143452.dkr.ecr.us-east-1.amazonaws.com",
"code.vt.edu:5005",
"docker.elastic.co",
"harbor.platform.it.vt.edu",
"registry.gitlab.com",
"registry.k8s.io"
}
# Function to extract hostname from image
get_hostname(image) = hostname {
trace(sprintf("Checking hostname for image: %v", [image]))
contains(image, "/")
parts := split(image, "/")
# If the first part contains a '.', assume it's a hostname
contains(parts[0], ".")
hostname := parts[0]
trace(sprintf("Extracted hostname: %v", [hostname]))
} else = "docker.io" { # Default to Docker Hub if no hostname is identifiable
not contains(image, "/")
trace("Defaulting to docker.io (no hostname found)")
} else = "docker.io" { # Default if no '.' in the first part of the name
contains(image, "/")
parts := split(image, "/")
not contains(parts[0], ".")
trace("Defaulting to docker.io (no '.' in first part of hostname)")
}
# Check if image is from an allowed repository
is_from_allowed_repo(image) {
trace(sprintf("Checking if image %v is from an allowed repo", [image]))
hostname := get_hostname(image)
allowed := [repo | repo := allowed_repos[_]; hostname == repo]
result := count(allowed) > 0
trace(sprintf("Image %v allowed: %v", [image, result]))
result
}
# Main violation logic for all container types
violation[{"msg": msg}] {
trace("Evaluating violation")
input.review.object.kind == "Pod"
# Check initContainers and ephemeralContainers alongside containers
container_types := ["containers", "initContainers", "ephemeralContainers"]
container_type := container_types[_]
container := input.review.object.spec[container_type][_]
trace(sprintf("Checking %v image: %v", [container_type, container.image]))
not is_from_allowed_repo(container.image)
msg := sprintf("Image %v in %v is not from an allowed repository. Please contact the platform team", [container.image, container_type])
trace(sprintf("Violation found: %v", [msg]))
}
package k8simageprovenance
# Helper function to create mock input for a Pod with different types of container images
create_input(image, container_type) = {
"review": {
"object": {
"kind": "Pod",
"spec": {
container_type: [
{
"image": image
}
]
}
}
}
}
# Test with an allowed image repository
test_with_allowed_image_repository {
container_types := ["containers", "initContainers", "ephemeralContainers"]
container_type := container_types[_]
test_input := create_input("docker.io/nginx", container_type)
results := data.k8simageprovenance.violation with input as test_input
count(results) == 0
}
# Test with a disallowed image repository
test_with_disallowed_image_repository {
container_types := ["containers", "initContainers", "ephemeralContainers"]
container_type := container_types[_]
test_input := create_input("unlistedrepo.com/image", container_type)
results := data.k8simageprovenance.violation with input as test_input
count(results) == 1
}
# Test with no repository specified (defaults to Docker Hub)
test_with_default_docker_hub_image {
container_types := ["containers", "initContainers", "ephemeralContainers"]
container_type := container_types[_]
test_input := create_input("nginx", container_type)
results := data.k8simageprovenance.violation with input as test_input
count(results) == 0
}
# Test with multiple containers, mixed repositories
test_with_mixed_repositories {
test_input := {
"review": {
"object": {
"kind": "Pod",
"spec": {
"containers": [
{"image": "docker.io/nginx"},
{"image": "unlistedrepo.com/image"}
],
"initContainers": [
{"image": "docker.io/initnginx"}
],
"ephemeralContainers": [
{"image": "quay.io/tempnginx"}
]
}
}
}
}
results := data.k8simageprovenance.violation with input as test_input
count(results) == 1
}
# Test with image from Docker Hub's default repository
test_with_docker_hub_default_repository {
container_types := ["containers", "initContainers", "ephemeralContainers"]
container_type := container_types[_]
test_input := create_input("alpine", container_type)
results := data.k8simageprovenance.violation with input as test_input
count(results) == 0
}
# Test with image from non-standard repository path
test_with_non_standard_repository_path {
container_types := ["containers", "initContainers", "ephemeralContainers"]
container_type := container_types[_]
test_input := create_input("docker.io/somepath/nginx", container_type)
results := data.k8simageprovenance.violation with input as test_input
count(results) == 0
}
# Separate tests for each container type with allowed images
test_with_allowed_image_containers {
test_input := create_input("docker.io/nginx", "containers")
results := data.k8simageprovenance.violation with input as test_input
count(results) == 0
}
test_with_allowed_image_init_containers {
test_input := create_input("docker.io/nginx", "initContainers")
results := data.k8simageprovenance.violation with input as test_input
count(results) == 0
}
test_with_allowed_image_ephemeral_containers {
test_input := create_input("docker.io/nginx", "ephemeralContainers")
results := data.k8simageprovenance.violation with input as test_input
count(results) == 0
}
......@@ -10,32 +10,8 @@ spec:
validation:
openAPIV3Schema:
type: object
properties:
properties: {}
targets:
- target: admission.k8s.gatekeeper.sh
rego: |
package k8simageprovenance
violation[{"msg": msg}] {
input.review.object.kind == "Pod"
container := input.review.object.spec.containers[_]
not startswith(container.image, "docker.io/")
not startswith(container.image, "quay.io/")
not startswith(container.image, "public.ecr.aws/")
not startswith(container.image, "gcr.io/")
not startswith(container.image, "ghcr.io/")
not startswith(container.image, "harbor.")
not startswith(container.image, "gitlab.")
msg := sprintf("Image %v is not from an allowed repository. Please contact the platform team", [container.image])
}
---
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sImageProvenance
metadata:
name: image-provenance-constraint
spec:
enforcementAction: deny
match:
kinds:
- apiGroups: [""]
kinds: ["Pod"]
{{.Files.Get "rego/image_provenance/policy.rego" | indent 8 }}
\ No newline at end of file
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment