Skip to content

Conftest

Conftest helps you write tests against structured configuration data. Using Conftest you can write tests for your Kubernetes configuration. Conftest uses the Rego language from Open Policy Agent for writing the assertions.

Here's a simple policy that checks whether a given container runs as root:

package main

deny[res] {
  input.kind == "Deployment"
  not input.spec.template.spec.securityContext.runAsNonRoot

  msg := "Containers must not run as root"

  res := {
    "msg": msg,
    "title": "Runs as root user"
  }
}

To integrate Conftest scanner change the value of the configAuditReports.scanner property to Conftest:

TRIVY_OPERATOR_NAMESPACE=<TRIVY_OPERATOR_NAMESPACE>
kubectl patch cm starboard -n $TRIVY_OPERATOR_NAMESPACE \
  --type merge \
  -p "$(cat <<EOF
{
  "data": {
    "configAuditReports.scanner": "Conftest"
  }
}
EOF
)"

Warning

Starboard does not ship with any default policies that can be used with Conftest plugin, therefore you have to add them manually.

In the following example, we'll use OPA polices provided by the AppShield project.

Start by cloning the AppShield repository and changing the current directory to the cloned repository:

git clone https://github.com/aquasecurity/appshield
cd appshield

Most of the Kubernetes policies defined by the AppShield project refer to OPA libraries called kubernetes.rego and utils.rego. You must add such libraries to the starboard-conftest-config ConfigMap along with the actual policies.

As an example, let's create the starboard-conftest-config ConfigMap with file_system_not_read_only.rego and uses_image_tag_latest.rego policies. Those two are very common checks performed by many other tools:

TRIVY_OPERATOR_NAMESPACE=<TRIVY_OPERATOR_NAMESPACE>
kubectl create configmap starboard-conftest-config --namespace $TRIVY_OPERATOR_NAMESPACE \
  --from-literal=conftest.imageRef=openpolicyagent/conftest:v0.30.0 \
  --from-file=conftest.library.kubernetes.rego=kubernetes/lib/kubernetes.rego \
  --from-file=conftest.library.utils.rego=kubernetes/lib/utils.rego \
  --from-file=conftest.policy.file_system_not_read_only.rego=kubernetes/policies/general/file_system_not_read_only.rego \
  --from-file=conftest.policy.uses_image_tag_latest.rego=kubernetes/policies/general/uses_image_tag_latest.rego \
  --from-literal=conftest.policy.file_system_not_read_only.kinds=Workload \
  --from-literal=conftest.policy.uses_image_tag_latest.kinds=Workload

Tip

For the operator the Helm install command may look as follows.

TRIVY_OPERATOR_NAMESPACE=<TRIVY_OPERATOR_NAMESPACE>
helm install starboard-operator aqua/starboard-operator \
  --namespace $TRIVY_OPERATOR_NAMESPACE --create-namespace \
  --set="targetNamespaces=default" \
  --set="starboard.configAuditReportsPlugin=Conftest" \
  --set-file="conftest.library.kubernetes\.rego=kubernetes/lib/kubernetes.rego" \
  --set-file="conftest.library.utils\.rego=kubernetes/lib/utils.rego" \
  --set-file="conftest.policy.file_system_not_read_only.rego=kubernetes/policies/general/file_system_not_read_only.rego" \
  --set-file="conftest.policy.uses_image_tag_latest.rego=kubernetes/policies/general/uses_image_tag_latest.rego" \
  --set-string="conftest.policy.file_system_not_read_only.kinds=Workload" \
  --set-string="conftest.policy.uses_image_tag_latest.kinds=Workload"

To test this setup out with Starboard CLI you can create the nginx Deployment with the latest nginx image and check its configuration:

kubectl create deployment nginx --image nginx
starboard scan configauditreports deployment/nginx

Finally, inspect the ConfigAuditReport to confirm that the Deployment is not compliant with test policies:

$ starboard get configauditreports deployment/nginx -o yaml
apiVersion: aquasecurity.github.io/v1alpha1
kind: ConfigAuditReport
metadata:
  creationTimestamp: "2021-10-27T12:42:20Z"
  generation: 1
  labels:
    plugin-config-hash: 5d5f578dd6
    resource-spec-hash: 7d48b6dfcf
    starboard.resource.kind: ReplicaSet
    starboard.resource.name: nginx-6799fc88d8
    starboard.resource.namespace: default
  name: replicaset-nginx-6799fc88d8
  namespace: default
  ownerReferences:
  - apiVersion: apps/v1
    blockOwnerDeletion: false
    controller: true
    kind: ReplicaSet
    name: nginx-6799fc88d8
    uid: cdfd93d7-9419-4e2d-a120-107bed2f3d57
  resourceVersion: "88048"
  uid: 362d5b06-65a5-4925-bf96-19d23f088e0c
report:
  updateTimestamp: "2021-10-27T12:42:20Z"
  scanner:
    name: Conftest
    vendor: Open Policy Agent
    version: v0.25.0
  summary:
    dangerCount: 2
    passCount: 0
    warningCount: 0
  checks:
  - category: Security
    checkID: Root file system is not read-only
    message: Container 'nginx' of ReplicaSet 'nginx-6799fc88d8' should set 'securityContext.readOnlyRootFilesystem'
      to true
    severity: danger
    success: false
  - category: Security
    checkID: Image tag ':latest' used
    message: Container 'nginx' of ReplicaSet 'nginx-6799fc88d8' should specify an
      image tag
    severity: danger
    success: false

Tip

The steps for configuring Conftest with Starboard CLI and Starboard Operator are the same except the namespace in which the starboard-conftest-config ConfigMap is created.

Settings

CONFIGMAP KEY DEFAULT DESCRIPTION
conftest.imageRef docker.io/openpolicyagent/conftest:v0.30.0 Conftest image reference
conftest.resources.requests.cpu 50m The minimum amount of CPU required to run Conftest scanner pod.
conftest.resources.requests.memory 50M The minimum amount of memory required to run Conftest scanner pod.
conftest.resources.limits.cpu 300m The maximum amount of CPU allowed to run Conftest scanner pod.
conftest.resources.limits.memory 300M The maximum amount of memory allowed to run Conftest scanner pod.
conftest.library.<name>.rego N/A Rego library with helper functions
conftest.policy.<name>.rego N/A Rego policy with the specified name
conftest.policy.<name>.kinds N/A A comma-separated list of Kubernetes kinds applicable to the policy with a given name. You can use Workload or * as special kinds to represent any Kubernetes workload or any object.