If storing Infrastructure as a Code, such as Kubernetes manifests, linting your configuration files with Conftest is a great first step. If you've structured your IaaC repository based on your infrastructure layout, ie. Stage-Environment-Domain-Application, you could also use Conftest to help validate the service names or namespaces based on their path with the help of the --combine
flag.
conftest test --combine --all-namespaces --data policy/data/dev dev
This way we can define a policy which ensures our manifests are in the correct stage by requiring the namespace to be prefixed with the stage and placing the manifest in folder structure which has the stage as the root folder name.
deny_namespace_incorrect_path[msg] {
manifest = input[idx].contents
file = input[idx].path
stagePath := split(file, "/")[0]
namespace := manifest.metadata.namespace
not startswith(namespace, stagePath)
msg := sprintf("Namespace %s must start with stagePath: %s - FILE: %s", [ namespace, stagePath, file])
}
Given this Deployment:
---
kind: Deployment
apiVersion: apps/v1
metadata:
name: conftest-example-namespace-fails
namespace: app
labels:
app: conftest-example-namespace-fails
spec:
replicas: 1
selector:
matchLabels:
app: conftest-example-namespace-fails
template:
metadata:
labels:
app: conftest-example-namespace-fails
spec:
containers:
- name: nginx
image: nginx
imagePullPolicy: Always
restartPolicy: Always
Here is the output you would expect if the file were placed at dev/app/fails-namespace-conftest-example.yaml:
...
FAIL - Combined - k8s.namespace - Namespace app must start with stagePath: dev - FILE: dev/app/fails-namespace-conftest-example.yaml
...
Another benefit is that you can use show the relative filepath for the FAIL
'ed policies by appending - FILE: %s
and passing the file
variable to the msg := sprintf(..)
:
FAIL - Combined - k8s.kind - Kind StatefulSet is not one of the allowed kinds: ["Deployment", "Service"] - FILE: dev/app/fails-kind-another-conftest-example.yaml
FAIL - Combined - k8s.kind - Kind ThisKindIsNotAllowed is not one of the allowed kinds: ["Deployment", "Service"] - FILE: dev/app/fails-kind-conftest-example.yaml
FAIL - Combined - k8s.namespace - Namespace app must be prefixed with stage prefix: dev- - FILE: dev/app/fails-namespace-conftest-example.yaml
FAIL - Combined - k8s.namespace - Namespace app must start with stagePath: dev - FILE: dev/app/fails-namespace-conftest-example.yaml
5 tests, 1 passed, 0 warnings, 4 failures, 0 exceptions
OPA Policies and Conftest are great tools for quickly defining linting rules which integrate easily into any pipeline. Up to this point I haven't found any tool which allows me to define such a rule without requiring me to use a specific convention.