Product

Getting started with Rego policies in Chainguard Enforce

Adam Dawson, Product Manager
December 20, 2022
copied

Chainguard Enforce now has support for the Rego Policy Language. Rego is a declarative policy language that is used to evaluate structured input data such as Kubernetes manifests and JSON documents. This new feature of Enforce enables users to apply policies that can evaluate Kubernetes admission requests and object metadata to make comprehensive decisions about the workloads that are admitted to their clusters. Rego support also enables users to enhance existing cloud-native policies by adding additional software supply chain security checks all in one place: Chainguard Enforce.

This blog will help you start defining useful Rego-based policies and distributing them to your clusters in Enforce. For more information about writing Rego policies, check out the Rego Policy Language Docs.

A typical use case for Enforce is ensuring that container images are trusted before being admitted to a cluster. However, in many cases, organizations want to ensure not just the provenance of the container images themselves, but also that the runtime container instances meet a baseline threshold security posture. 

For this tutorial, let’s imagine that an organization has the following two security policies:

  • Running Pods must meet some baseline Pod Security Standards such as:
    • Host namespaces must not be allowed for workloads, meaning the following fields must all be false:
      • spec.hostNetwork
      • spec.hostPID
      • spec.hostIPC
      • Privileged Pods must be disallowed
  • The Kubernetes manifest metadata must contain the following labels:
    • env: production
    • approved-by: compliance-team

The Enforce Policy Catalog contains several pre-written Rego-based policies that you can customize and apply to easily enforce security best practices on your clusters. The catalog also contains a blank Rego policy template that you can use to build your own policy, like this one. 

-- CODE language-bash -- # Copyright 2022 Chainguard, Inc. # SPDX-License-Identifier: Apache-2.0 apiVersion: policy.sigstore.dev/v1beta1 kind: ClusterImagePolicy metadata: name: myfirst-rego-policy spec: images: [glob: '**'] authorities: [static: {action: pass}] mode: warn policy: includeSpec: true type: rego data: | package sigstore default isCompliant = false isCompliant { # Rego logic goes here; must evaluate to true for policy to pass }

We’ll use this template to construct our organization’s policies.

First, let’s look at the ClusterImagePolicy spec: which is defined in the sigstore API

-- CODE language-bash -- spec: images: [glob: '**'] authorities: [static: {action: pass}] mode: warn

  • The images field defines the filter for which requests we want to evaluate with this policy. We are evaluating everything.
  • The authorities field is used in evaluating image signatures. Since we aren’t using signatures in this policy, we will set it to pass. This will be a common setting in Rego-based policies unless you are also evaluating signatures simultaneously.
  • Finally, the policy is being implemented in warn mode, which can generate an alert via CloudEvents to notify administrators of violations without blocking deployments. You can also use enforce mode to block deployments that violate the policy.

Now let’s build our policy:

-- CODE language-bash -- policy: includeSpec: true includeObjectMeta: true type: rego data: | package sigstore default isCompliant = false isCompliant { # Rego logic goes here; must evaluate to true for policy to pass }

Since we want to use Rego to evaluate structured data that isn’t supplied in the registry image itself, we need to provide some input data for the policy to use:

The API defines the following options:

  • includeSpec: allows you to access the fields in the spec: portion of the Kubernetes manifest, including the container configuration, image names, replicas, resources, and more.
  • includeObjectMeta: allows you to access the fields in the metadata: portion of the manifest, including the object’s name and labels
  • includeTypeMeta: allows access to the top level fields in the manifest, ie, the kind and apiVersion
  • fetchconfigFile: fetches the OCI config file from the registry, which contains metadata about the image in the registry

Since we want to evaluate the metadata labels and Pod Security spec, we need to includeSpec: and includeObjectMeta:

The policy type: should be “rego” (By default, Enforce supports “cue” policy language here), and the policy itself is defined in the data: field.

Rego policies in Enforce must include package sigstore, and must evaluate the isCompliant boolean to “true” to successfully pass the policy. You can see that isCompliant is set to false by default, and the logic in the braces must flip the boolean to true for the policy to pass. This same structure must be present in all Rego-based policies in Enforce.

-- CODE language-bash -- data: | package sigstore default isCompliant = false isCompliant { # Rego logic goes here; must evaluate to true for policy to pass }

Additional details about this structure:

  • Multiple conditions may exist inside the braces; these will be ANDed together, meaning that they must all pass for isCompliant to be true
  • Multiple evaluations (different sets of braces) may exist in the policy, these will be ORed together, meaning that if any of them is true, the boolean will be true

To access the data we’ve imported above, start your statements with “input.” and append the input document hierarchy down to the level of the value you want to evaluate. Let’s start by checking that the required labels exist in the ObjectMeta data:

-- CODE language-bash -- policy: includeSpec: true includeObjectMeta: true type: rego data: | package sigstore default isCompliant = false isCompliant { input.metadata.labels.env == "production" input.metadata.labels.approved-by == "compliance-team" }

This policy will evaluate to true only if both labels exist in the metadata portion of the manifest.

Now let’s check to make sure our Pod Security Specs are properly set:

-- CODE language-bash -- policy: includeSpec: true includeObjectMeta: true type: rego data: | package sigstore default isCompliant = false isCompliant { input.spec.hostNetwork == "false" input.spec.hostPID == "false" input.spec.hostIPC == "false" }

This policy will pass if all the restricted values are set to false.

In some cases, you may want to evaluate an item that can be repeated in the manifest, such as an image source that is included in multiple container specs in the same manifest, such as this snippet with a disallowed nginx image from dockerhub:

-- CODE language-bash -- spec: containers: - name: "your-container-name" image: nginx:latest - name: "another-container-name" image: nginx

In this case, you’ll need to iterate over the image array and check all the relevant fields for the restricted value. You can use [_] to iterate through the array. The Rego docs show that you can use not in conjunction with the contains() built-in function to evaluate all items in the array.

-- CODE language-bash -- policy: includeSpec: true type: rego data: | package sigstore default isCompliant = false isCompliant { result:= input.spec.containers.image[_] not contains(result,"docker.io") }

This policy will not admit pods that come from docker.io.

Finally, let's construct a policy that disallows privilege escalation in Pods per the Kubernetes PodSecurity Baseline standard: 

-- CODE language-bash -- policy: includeSpec: true type: rego data: | package sigstore default isCompliant = false isCompliant { filteredContainers = [c | c := input.spec.containers[_]; c.securityContext.allowPrivilegeEscalation == true ] filteredInitContainers = [c | c := input.spec.initContainers[_]; c.securityContext.allowPrivilegeEscalation == true ] filteredEphemeralContainers = [c | c := input.spec.ephemeralContainers[_]; c.securityContext.allowPrivilegeEscalation == true ] (count(filteredContainers) + count(filteredInitContainers) + count(filteredEphemeralContainers)) == 0 }

This policy shows a method of declaring a variable and using it to count up all the instances of privilege escalation across Pod types, and evaluate that the final count is 0 to pass.

These are some very simple examples to help you get started writing Rego policies in Chainguard Enforce. The Rego Language Reference describes many more built-in functions, operations, and data types you can use to evaluate your policies, just remember that you must use the sigstore API standard isCompliant evaluation in your Enforce policies. 

Sign up for a free trial of Chainguard Enforce to start securing your software supply chain today!

Related articles

Ready to lock down your supply chain?

Talk to our customer obsessed, community-driven team.