Skip to content

Instantly share code, notes, and snippets.

@sebradloff
Last active September 5, 2021 15:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sebradloff/9b6f1a273328280533b4feaac12dd829 to your computer and use it in GitHub Desktop.
Save sebradloff/9b6f1a273328280533b4feaac12dd829 to your computer and use it in GitHub Desktop.
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: ApiGatewayIngressPath01
metadata:
name: apigatewayingresspath01
spec:
match:
kinds:
- apiGroups:
- networking.k8s.io
kinds:
- Ingress
# @title Enforce API Gateway Ingress path per service
#
# There should be one Ingress per service receiving requests at "/api/${resource}".
# We do not want seperate services serving traffic on the same top level path.
# We should not see "/api/foozles" and "/api/foozles/rejoice" serving traffic
# to two seperate services.
#
# @enforcement deny
# @kinds networking.k8s.io/Ingress
package api_gateway_ingress_path_01
import data.lib.core
policyID := "P0005"
identical(obj, resource) {
obj.metadata.namespace == resource.metadata.namespace
obj.metadata.name == resource.metadata.name
}
paths_conflict(path1, path2) = result {
path1_sub_api := trim_prefix(path1, "/api/")
path2_sub_api := trim_prefix(path2, "/api/")
# split by '/' because the characted can not be interpreted by rego regex which uses re2
# https://github.com/google/re2/wiki/Syntax
path1_arr := split(path1_sub_api, "/")
path2_arr := split(path2_sub_api, "/")
path1_arr_rsplit := regex.split("[^\\w|-]+", path1_arr[0])
path2_arr_rsplit := regex.split("[^\\w|-]+", path2_arr[0])
result := path1_arr_rsplit[0] == path2_arr_rsplit[0]
}
violation[msg] {
ns := data.inventory.cluster.v1.Namespace[_].metadata.name
other_ing := data.inventory.namespace[ns]["networking.k8s.io/v1"].Ingress[_]
curr_ing := core.resource
not identical(other_ing, curr_ing)
curr_ing_path := curr_ing.spec.rules[_].http.paths[_].path
other_ing_path := other_ing.spec.rules[_].http.paths[_].path
paths_conflict(curr_ing_path, other_ing_path)
msg := core.format_with_id(sprintf("%s/%s: http rule with path %s conflicts with %s/%s http rule path %s", [curr_ing.metadata.namespace, curr_ing.metadata.name, curr_ing_path, other_ing.metadata.namespace, other_ing.metadata.name, other_ing_path]), policyID)
}
package api_gateway_ingress_path_01
test_paths_conflict_same_paths_returns_true {
test_paths := ["/api/foozles", "/api/foozles"]
results := paths_conflict(test_paths[0], test_paths[1])
results == true
}
test_paths_conflict_same_root_path_returns_true {
test_paths := ["/api/foozles", "/api/foozles/bar"]
results := paths_conflict(test_paths[0], test_paths[1])
results == true
}
test_paths_conflict_diff_root_path_returns_false {
test_paths := ["/api/foozles-bar", "/api/foozles/bar"]
results := paths_conflict(test_paths[0], test_paths[1])
results == false
}
test_input_as_conflicting_ingresses_throws_violation {
cluster_inv := {"v1": {"Namespace": [create_namespace("default"), create_namespace("foo")]}}
ns_inv := {"foo": {"networking.k8s.io/v1": {"Ingress": [create_ingress("foozles", "foo", "/api/foozles(/|$)(.*)"), create_ingress("foo", "foo", "/api/foo(/|$)(.*)")]}}}
input := {"review": {"object": create_ingress("foozles-bar", "foo", "/api/foozles/bar(/|$)(.*)")}}
results := violation with input as input with data.inventory.cluster as cluster_inv with data.inventory.namespace as ns_inv
count(results) == 1
}
test_input_non_conflicting_ingress_no_violations {
cluster_inv := {"v1": {"Namespace": [create_namespace("default"), create_namespace("foo"), create_namespace("bar")]}}
ns_inv := {"foo": {"networking.k8s.io/v1": {"Ingress": [create_ingress("foozles", "foo", "/api/foozles(/|$)(.*)"), create_ingress("foo", "foo", "/api/foo(/|$)(.*)")]}}}
input := {"review": {"object": create_ingress("bar", "bar", "/api/bar(/|$)(.*)")}}
results := violation with input as input with data.inventory.cluster as cluster_inv with data.inventory.namespace as ns_inv
count(results) == 0
}
test_input_ingress_already_on_cluster_no_violation {
cluster_inv := {"v1": {"Namespace": [create_namespace("default"), create_namespace("foo")]}}
ns_inv := {"foo": {"networking.k8s.io/v1": {"Ingress": [create_ingress("foozles", "foo", "/api/foozles(/|$)(.*)"), create_ingress("foo", "foo", "/api/foo(/|$)(.*)")]}}}
input := {"review": {"object": create_ingress("foozles", "foo", "/api/foozles(/|$)(.*)")}}
results := violation with input as input with data.inventory.cluster as cluster_inv with data.inventory.namespace as ns_inv
count(results) == 0
}
create_ingress(name, namespace, path) = ing {
ing := {
"apiVersion": "networking.k8s.io/v1",
"kind": "Ingress",
"metadata": {
"annotations": {"nginx.ingress.kubernetes.io/rewrite-target": "/$2"},
"name": name,
"namespace": namespace,
},
"spec": {"rules": [{"http": {"paths": [{
"pathType": "Prefix",
"path": path,
"backend": {"service": {
"name": "foo",
"port": {"number": 80},
}},
}]}}]},
}
}
create_namespace(name) = ns {
ns := {
"apiVersion": "v1",
"kind": "Namespace",
"metadata": {"name": name},
"spec": {"finalizers": ["kubernetes"]},
}
}
apiVersion: templates.gatekeeper.sh/v1beta1
kind: ConstraintTemplate
metadata:
creationTimestamp: null
name: apigatewayingresspath01
spec:
crd:
spec:
names:
kind: ApiGatewayIngressPath01
targets:
- libs:
- |-
package lib.core
default is_gatekeeper = false
is_gatekeeper {
has_field(input, "review")
has_field(input.review, "object")
}
resource = input.review.object {
is_gatekeeper
}
resource = input {
not is_gatekeeper
}
format(msg) = {"msg": msg}
format_with_id(msg, id) = msg_fmt {
msg_fmt := {
"msg": sprintf("%s: %s", [id, msg]),
"details": {"policyID": id},
}
}
apiVersion = resource.apiVersion
name = resource.metadata.name
kind = resource.kind
labels = resource.metadata.labels
annotations = resource.metadata.annotations
gv := split(apiVersion, "/")
group = gv[0] {
contains(apiVersion, "/")
}
group = "core" {
not contains(apiVersion, "/")
}
version := gv[minus(count(gv), 1)]
has_field(obj, field) {
not object.get(obj, field, "N_DEFINED") == "N_DEFINED"
}
missing_field(obj, field) {
obj[field] == ""
}
missing_field(obj, field) {
not has_field(obj, field)
}
rego: |-
package api_gateway_ingress_path_01
import data.lib.core
policyID := "P0005"
identical(obj, resource) {
obj.metadata.namespace == resource.metadata.namespace
obj.metadata.name == resource.metadata.name
}
paths_conflict(path1, path2) = result {
path1_sub_api := trim_prefix(path1, "/api/")
path2_sub_api := trim_prefix(path2, "/api/")
# split by '/' because the characted can not be interpreted by rego regex which uses re2
# https://github.com/google/re2/wiki/Syntax
path1_arr := split(path1_sub_api, "/")
path2_arr := split(path2_sub_api, "/")
path1_arr_rsplit := regex.split("[^\\w|-]+", path1_arr[0])
path2_arr_rsplit := regex.split("[^\\w|-]+", path2_arr[0])
result := path1_arr_rsplit[0] == path2_arr_rsplit[0]
}
violation[msg] {
ns := data.inventory.cluster.v1.Namespace[_].metadata.name
other_ing := data.inventory.namespace[ns]["networking.k8s.io/v1"].Ingress[_]
curr_ing := core.resource
not identical(other_ing, curr_ing)
curr_ing_path := curr_ing.spec.rules[_].http.paths[_].path
other_ing_path := other_ing.spec.rules[_].http.paths[_].path
paths_conflict(curr_ing_path, other_ing_path)
msg := core.format_with_id(sprintf("%s/%s: http rule with path %s conflicts with %s/%s http rule path %s", [curr_ing.metadata.namespace, curr_ing.metadata.name, curr_ing_path, other_ing.metadata.namespace, other_ing.metadata.name, other_ing_path]), policyID)
}
target: admission.k8s.gatekeeper.sh
status: {}
apiVersion: config.gatekeeper.sh/v1alpha1
kind: Config
metadata:
name: config
namespace: gatekeeper-system
spec:
sync:
syncOnly:
- group: "networking.k8s.io"
version: "v1"
kind: "Ingress"
- group: ""
version: "v1"
kind: "Namespace"
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: ContainerDenyWithoutResourceRequests01
metadata:
name: containerdenywithoutresourcerequests01
spec:
match:
kinds:
- apiGroups:
- ""
kinds:
- Pod
# @title Containers must have resource requests
#
# All containers must have resource requests so the scheduler can better binpack each node.
# Reading on managing container resources: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/
#
# @enforcement deny
# @kinds core/Pod
package container_deny_without_resource_requests_01
import data.lib.core
import data.lib.pods
policyID := "P0003"
violation[msg] {
container := pods.containers[_]
not container_requests_provided(container)
msg := core.format_with_id(sprintf("%s/%s/%s: Container resource requests cpu and memory must be specified", [core.kind, core.name, container.name]), policyID)
}
violation[msg] {
container := pods.containers[_]
not container_requests_zero_memory(container)
msg := core.format_with_id(sprintf("%s/%s/%s: Container resource memory requests can not have zero value (%s)", [core.kind, core.name, container.name, container.resources.requests.memory]), policyID)
}
violation[msg] {
container := pods.containers[_]
not container_requests_zero_cpu(container)
msg := core.format_with_id(sprintf("%s/%s/%s: Container resource cpu requests can not have zero value (%s)", [core.kind, core.name, container.name, container.resources.requests.cpu]), policyID)
}
container_requests_provided(container) {
container.resources.requests.cpu
container.resources.requests.memory
}
container_requests_zero_memory(container) {
not regex.match("^0.*", container.resources.requests.memory)
}
container_requests_zero_cpu(container) {
not regex.match("^0.*", container.resources.requests.cpu)
}
package container_deny_without_resource_requests_01
test_input_as_pod_both_containers_missing_resource_requests {
c1_resources := {}
c2_resources := {}
input := {"review": {"object": pod_input(c1_resources, c2_resources)}}
results := violation with input as input
count(results) == 2
}
test_input_as_pod_one_container_missing_resources {
c1_resources := {"requests": {"cpu": "10m", "memory": "100Mi"}}
c2_resources := {}
input := {"review": {"object": pod_input(c1_resources, c2_resources)}}
results := violation with input as input
count(results) == 1
}
test_input_as_pod_one_container_zero_cpu {
c1_resources := {"requests": {"cpu": "10m", "memory": "100Mi"}}
c2_resources := {"requests": {"cpu": "0m", "memory": "100Mi"}}
input := {"review": {"object": pod_input(c1_resources, c2_resources)}}
results := violation with input as input
count(results) == 1
}
test_input_as_pod_one_container_zero_memory {
c1_resources := {"requests": {"cpu": "10m", "memory": "100Mi"}}
c2_resources := {"requests": {"cpu": "10m", "memory": "0Mi"}}
input := {"review": {"object": pod_input(c1_resources, c2_resources)}}
results := violation with input as input
count(results) == 1
}
test_input_as_pod_one_container_zero_cpu_and_zero_memory {
c1_resources := {"requests": {"cpu": "10m", "memory": "100Mi"}}
c2_resources := {"requests": {"cpu": "0m", "memory": "0Mi"}}
input := {"review": {"object": pod_input(c1_resources, c2_resources)}}
results := violation with input as input
count(results) == 2
}
test_input_as_pod_all_resource_requests_set_properly {
c1_resources := {"requests": {"cpu": "10m", "memory": "100Mi"}}
c2_resources := {"requests": {"cpu": "10m", "memory": "100Mi"}}
input := {"review": {"object": pod_input(c1_resources, c2_resources)}}
results := violation with input as input
count(results) == 0
}
pod_input(c1_resources, c2_resources) = output {
output = {
"kind": "Pod",
"metadata": {"name": "test"},
"spec": {"containers": [
{
"name": "test-1",
"image": "busybox",
"resources": c1_resources,
},
{
"name": "test-2",
"image": "busybox",
"resources": c2_resources,
},
]},
}
}
apiVersion: templates.gatekeeper.sh/v1beta1
kind: ConstraintTemplate
metadata:
creationTimestamp: null
name: containerdenywithoutresourcerequests01
spec:
crd:
spec:
names:
kind: ContainerDenyWithoutResourceRequests01
targets:
- libs:
- |-
package lib.core
default is_gatekeeper = false
is_gatekeeper {
has_field(input, "review")
has_field(input.review, "object")
}
resource = input.review.object {
is_gatekeeper
}
resource = input {
not is_gatekeeper
}
format(msg) = {"msg": msg}
format_with_id(msg, id) = msg_fmt {
msg_fmt := {
"msg": sprintf("%s: %s", [id, msg]),
"details": {"policyID": id},
}
}
apiVersion = resource.apiVersion
name = resource.metadata.name
kind = resource.kind
labels = resource.metadata.labels
annotations = resource.metadata.annotations
gv := split(apiVersion, "/")
group = gv[0] {
contains(apiVersion, "/")
}
group = "core" {
not contains(apiVersion, "/")
}
version := gv[minus(count(gv), 1)]
has_field(obj, field) {
not object.get(obj, field, "N_DEFINED") == "N_DEFINED"
}
missing_field(obj, field) {
obj[field] == ""
}
missing_field(obj, field) {
not has_field(obj, field)
}
- |-
package lib.pods
import data.lib.core
default pod = false
pod = core.resource.spec.template {
pod_templates := ["daemonset", "deployment", "job", "replicaset", "replicationcontroller", "statefulset"]
lower(core.kind) == pod_templates[_]
}
pod = core.resource {
lower(core.kind) == "pod"
}
pod = core.resource.spec.jobTemplate.spec.template {
lower(core.kind) == "cronjob"
}
containers[container] {
keys = {"containers", "initContainers"}
all_containers = [c | keys[k]; c = pod.spec[k][_]]
container = all_containers[_]
}
volumes[volume] {
volume = pod.spec.volumes[_]
}
rego: |-
package container_deny_without_resource_requests_01
import data.lib.core
import data.lib.pods
policyID := "P0003"
violation[msg] {
container := pods.containers[_]
not container_requests_provided(container)
msg := core.format_with_id(sprintf("%s/%s/%s: Container resource requests cpu and memory must be specified", [core.kind, core.name, container.name]), policyID)
}
violation[msg] {
container := pods.containers[_]
not container_requests_zero_memory(container)
msg := core.format_with_id(sprintf("%s/%s/%s: Container resource memory requests can not have zero value (%s)", [core.kind, core.name, container.name, container.resources.requests.memory]), policyID)
}
violation[msg] {
container := pods.containers[_]
not container_requests_zero_cpu(container)
msg := core.format_with_id(sprintf("%s/%s/%s: Container resource cpu requests can not have zero value (%s)", [core.kind, core.name, container.name, container.resources.requests.cpu]), policyID)
}
container_requests_provided(container) {
container.resources.requests.cpu
container.resources.requests.memory
}
container_requests_zero_memory(container) {
not regex.match("^0.*", container.resources.requests.memory)
}
container_requests_zero_cpu(container) {
not regex.match("^0.*", container.resources.requests.cpu)
}
target: admission.k8s.gatekeeper.sh
status: {}
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: ContainerDenyWithoutResourceRequests02
metadata:
name: containerdenywithoutresourcerequests02
spec:
match:
kinds:
- apiGroups:
- apps
- batch
- ""
kinds:
- DaemonSet
- Deployment
- StatefulSet
- CronJob
- Job
- Pod
# @title Containers must have resource requests
#
# All containers must have resource requests so the scheduler can better binpack each node.
# Reading on managing container resources: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/
#
# @enforcement deny
# @kinds apps/DaemonSet apps/Deployment apps/StatefulSet batch/CronJob batch/Job core/Pod
package container_deny_without_resource_requests_02
import data.lib.core
import data.lib.pods
policyID := "P0004"
violation[msg] {
container := pods.containers[_]
not container_requests_provided(container)
msg := core.format_with_id(sprintf("%s/%s/%s: Container resource requests cpu and memory must be specified", [core.kind, core.name, container.name]), policyID)
}
violation[msg] {
container := pods.containers[_]
not container_requests_zero_memory(container)
msg := core.format_with_id(sprintf("%s/%s/%s: Container resource memory requests can not have zero value (%s)", [core.kind, core.name, container.name, container.resources.requests.memory]), policyID)
}
violation[msg] {
container := pods.containers[_]
not container_requests_zero_cpu(container)
msg := core.format_with_id(sprintf("%s/%s/%s: Container resource cpu requests can not have zero value (%s)", [core.kind, core.name, container.name, container.resources.requests.cpu]), policyID)
}
container_requests_provided(container) {
container.resources.requests.cpu
container.resources.requests.memory
}
container_requests_zero_memory(container) {
not regex.match("^0.*", container.resources.requests.memory)
}
container_requests_zero_cpu(container) {
not regex.match("^0.*", container.resources.requests.cpu)
}
package container_deny_without_resource_requests_02
test_input_as_pod_both_containers_missing_resource_requests {
c1_resources := {}
c2_resources := {}
input := {"review": {"object": pod_input(c1_resources, c2_resources)}}
results := violation with input as input
count(results) == 2
}
test_input_as_pod_one_container_missing_resources {
c1_resources := {"requests": {"cpu": "10m", "memory": "100Mi"}}
c2_resources := {}
input := {"review": {"object": pod_input(c1_resources, c2_resources)}}
results := violation with input as input
count(results) == 1
}
test_input_as_pod_one_container_zero_cpu {
c1_resources := {"requests": {"cpu": "10m", "memory": "100Mi"}}
c2_resources := {"requests": {"cpu": "0m", "memory": "100Mi"}}
input := {"review": {"object": pod_input(c1_resources, c2_resources)}}
results := violation with input as input
count(results) == 1
}
test_input_as_pod_one_container_zero_memory {
c1_resources := {"requests": {"cpu": "10m", "memory": "100Mi"}}
c2_resources := {"requests": {"cpu": "10m", "memory": "0Mi"}}
input := {"review": {"object": pod_input(c1_resources, c2_resources)}}
results := violation with input as input
count(results) == 1
}
test_input_as_pod_one_container_zero_cpu_and_zero_memory {
c1_resources := {"requests": {"cpu": "10m", "memory": "100Mi"}}
c2_resources := {"requests": {"cpu": "0m", "memory": "0Mi"}}
input := {"review": {"object": pod_input(c1_resources, c2_resources)}}
results := violation with input as input
count(results) == 2
}
test_input_as_pod_all_resource_requests_set_properly {
c1_resources := {"requests": {"cpu": "10m", "memory": "100Mi"}}
c2_resources := {"requests": {"cpu": "10m", "memory": "100Mi"}}
input := {"review": {"object": pod_input(c1_resources, c2_resources)}}
results := violation with input as input
count(results) == 0
}
pod_input(c1_resources, c2_resources) = output {
output = {
"kind": "Pod",
"metadata": {"name": "test"},
"spec": {"containers": [
{
"name": "test-1",
"image": "busybox",
"resources": c1_resources,
},
{
"name": "test-2",
"image": "busybox",
"resources": c2_resources,
},
]},
}
}
apiVersion: templates.gatekeeper.sh/v1beta1
kind: ConstraintTemplate
metadata:
creationTimestamp: null
name: containerdenywithoutresourcerequests02
spec:
crd:
spec:
names:
kind: ContainerDenyWithoutResourceRequests02
targets:
- libs:
- |-
package lib.core
default is_gatekeeper = false
is_gatekeeper {
has_field(input, "review")
has_field(input.review, "object")
}
resource = input.review.object {
is_gatekeeper
}
resource = input {
not is_gatekeeper
}
format(msg) = {"msg": msg}
format_with_id(msg, id) = msg_fmt {
msg_fmt := {
"msg": sprintf("%s: %s", [id, msg]),
"details": {"policyID": id},
}
}
apiVersion = resource.apiVersion
name = resource.metadata.name
kind = resource.kind
labels = resource.metadata.labels
annotations = resource.metadata.annotations
gv := split(apiVersion, "/")
group = gv[0] {
contains(apiVersion, "/")
}
group = "core" {
not contains(apiVersion, "/")
}
version := gv[minus(count(gv), 1)]
has_field(obj, field) {
not object.get(obj, field, "N_DEFINED") == "N_DEFINED"
}
missing_field(obj, field) {
obj[field] == ""
}
missing_field(obj, field) {
not has_field(obj, field)
}
- |-
package lib.pods
import data.lib.core
default pod = false
pod = core.resource.spec.template {
pod_templates := ["daemonset", "deployment", "job", "replicaset", "replicationcontroller", "statefulset"]
lower(core.kind) == pod_templates[_]
}
pod = core.resource {
lower(core.kind) == "pod"
}
pod = core.resource.spec.jobTemplate.spec.template {
lower(core.kind) == "cronjob"
}
containers[container] {
keys = {"containers", "initContainers"}
all_containers = [c | keys[k]; c = pod.spec[k][_]]
container = all_containers[_]
}
volumes[volume] {
volume = pod.spec.volumes[_]
}
rego: |-
package container_deny_without_resource_requests_02
import data.lib.core
import data.lib.pods
policyID := "P0004"
violation[msg] {
container := pods.containers[_]
not container_requests_provided(container)
msg := core.format_with_id(sprintf("%s/%s/%s: Container resource requests cpu and memory must be specified", [core.kind, core.name, container.name]), policyID)
}
violation[msg] {
container := pods.containers[_]
not container_requests_zero_memory(container)
msg := core.format_with_id(sprintf("%s/%s/%s: Container resource memory requests can not have zero value (%s)", [core.kind, core.name, container.name, container.resources.requests.memory]), policyID)
}
violation[msg] {
container := pods.containers[_]
not container_requests_zero_cpu(container)
msg := core.format_with_id(sprintf("%s/%s/%s: Container resource cpu requests can not have zero value (%s)", [core.kind, core.name, container.name, container.resources.requests.cpu]), policyID)
}
container_requests_provided(container) {
container.resources.requests.cpu
container.resources.requests.memory
}
container_requests_zero_memory(container) {
not regex.match("^0.*", container.resources.requests.memory)
}
container_requests_zero_cpu(container) {
not regex.match("^0.*", container.resources.requests.cpu)
}
target: admission.k8s.gatekeeper.sh
status: {}
apiVersion: apps/v1
kind: Deployment
metadata:
name: bar
spec:
replicas: 1
selector:
matchLabels:
app: bar
template:
metadata:
labels:
app: bar
spec:
containers:
- name: bar
image: hashicorp/http-echo:0.2.3
args:
- "-text=bar"
ports:
- containerPort: 5678
resources:
requests:
cpu: 1m
memory: 10Mi
---
apiVersion: v1
kind: Service
metadata:
name: bar
spec:
ports:
- port: 80
targetPort: 5678
protocol: TCP
name: http
selector:
app: bar
apiVersion: apps/v1
kind: Deployment
metadata:
name: foo
spec:
replicas: 1
selector:
matchLabels:
app: foo
template:
metadata:
labels:
app: foo
spec:
containers:
- name: foo
image: hashicorp/http-echo:0.2.3
args:
- "-text=foo"
ports:
- containerPort: 5678
resources:
requests:
cpu: 1m
memory: 10Mi
---
apiVersion: v1
kind: Service
metadata:
name: foo
spec:
ports:
- port: 80
targetPort: 5678
protocol: TCP
name: http
selector:
app: foo
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /$2
name: foo
spec:
rules:
- http:
paths:
- pathType: Prefix
path: "/api/foozles(/|$)(.*)"
backend:
service:
name: foo
port:
number: 80
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /$2
name: bar
spec:
rules:
- http:
paths:
- pathType: Prefix
path: "/api/foozles/bar(/|$)(.*)" # will conflict with /api/foozles
backend:
service:
name: bar
port:
number: 80
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /$2
name: foozles-bar
spec:
rules:
- http:
paths:
- pathType: Prefix
path: "/api/foozles-bar(/|$)(.*)" # should not conflict
backend:
service:
name: foo
port:
number: 80
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: NamespaceTeamLabel01
metadata:
name: namespaceteamlabel01
spec:
match:
kinds:
- apiGroups:
- ""
kinds:
- Namespace
# @title Namespaces must have a team label
#
# Required team label on all namespaces to determine ownership
#
# @enforcement deny
# @kinds core/Namespace
package namespace_team_label_01
violation[{"msg": msg, "details": {}}] {
resource := input.review.object
not resource.metadata.labels.team
msg := sprintf("%s: Namespace does not have a required 'team' label", [resource.metadata.name])
}
package namespace_team_label_01
test_namespace_without_team_label_is_violation {
input := {"review": {"object": {
"kind": "Namespace",
"metadata": {
"labels": {"not": "important"},
"name": "test",
},
}}}
results := violation with input as input
count(results) > 0
}
test_namespace_without_labels_is_violation {
input := {"review": {"object": {
"kind": "Namespace",
"metadata": {"name": "test"},
}}}
results := violation with input as input
count(results) > 0
}
test_namespace_with_team_label_is_not_violation {
input := {"review": {"object": {
"kind": "Namespace",
"metadata": {
"labels": {"team": "core"},
"name": "test",
},
}}}
results := violation with input as input
count(results) == 0
}
apiVersion: templates.gatekeeper.sh/v1beta1
kind: ConstraintTemplate
metadata:
creationTimestamp: null
name: namespaceteamlabel01
spec:
crd:
spec:
names:
kind: NamespaceTeamLabel01
targets:
- rego: |-
package namespace_team_label_01
violation[{"msg": msg, "details": {}}] {
resource := input.review.object
not resource.metadata.labels.team
msg := sprintf("%s: Namespace does not have a required 'team' label", [resource.metadata.name])
}
target: admission.k8s.gatekeeper.sh
status: {}
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: NamespaceTeamLabel02
metadata:
name: namespaceteamlabel02
spec:
match:
kinds:
- apiGroups:
- ""
kinds:
- Namespace
# @title Namespaces must have a team label
#
# Required team label on all namespaces to determine ownership
#
# @enforcement deny
# @kinds core/Namespace
package namespace_team_label_02
import data.lib.core
policyID := "P0002"
default has_team_label = false
has_team_label {
core.has_field(core.labels, "team")
}
violation[msg] {
not has_team_label
msg := core.format_with_id(sprintf("%s: Namespace does not have a required 'team' label", [core.name]), policyID)
}
package namespace_team_label_02
test_namespace_without_team_label_is_violation {
input := {"review": {"object": {
"kind": "Namespace",
"metadata": {
"labels": {"not": "important"},
"name": "test",
},
}}}
results := violation with input as input
count(results) > 0
}
test_namespace_without_labels_is_violation {
input := {"review": {"object": {
"kind": "Namespace",
"metadata": {"name": "test"},
}}}
results := violation with input as input
count(results) > 0
}
test_namespace_with_team_label_is_not_violation {
input := {"review": {"object": {
"kind": "Namespace",
"metadata": {
"labels": {"team": "core"},
"name": "test",
},
}}}
results := violation with input as input
count(results) == 0
}
apiVersion: templates.gatekeeper.sh/v1beta1
kind: ConstraintTemplate
metadata:
creationTimestamp: null
name: namespaceteamlabel02
spec:
crd:
spec:
names:
kind: NamespaceTeamLabel02
targets:
- libs:
- |-
package lib.core
default is_gatekeeper = false
is_gatekeeper {
has_field(input, "review")
has_field(input.review, "object")
}
resource = input.review.object {
is_gatekeeper
}
resource = input {
not is_gatekeeper
}
format(msg) = {"msg": msg}
format_with_id(msg, id) = msg_fmt {
msg_fmt := {
"msg": sprintf("%s: %s", [id, msg]),
"details": {"policyID": id},
}
}
apiVersion = resource.apiVersion
name = resource.metadata.name
kind = resource.kind
labels = resource.metadata.labels
annotations = resource.metadata.annotations
gv := split(apiVersion, "/")
group = gv[0] {
contains(apiVersion, "/")
}
group = "core" {
not contains(apiVersion, "/")
}
version := gv[minus(count(gv), 1)]
has_field(obj, field) {
not object.get(obj, field, "N_DEFINED") == "N_DEFINED"
}
missing_field(obj, field) {
obj[field] == ""
}
missing_field(obj, field) {
not has_field(obj, field)
}
rego: |-
package namespace_team_label_02
import data.lib.core
policyID := "P0002"
default has_team_label = false
has_team_label {
core.has_field(core.labels, "team")
}
violation[msg] {
not has_team_label
msg := core.format_with_id(sprintf("%s: Namespace does not have a required 'team' label", [core.name]), policyID)
}
target: admission.k8s.gatekeeper.sh
status: {}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment