Rego Policies
The Sigstore Policy Controller supports the Rego Policy Language, which is a declarative policy language that is used to evaluate structured input data such as Kubernetes manifests and JSON documents. This feature 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.
If you would like to write a Rego policy from scratch, or learn more about how to use this format, you can follow this guide.
Rego Policy Template
# Copyright 2022 Chainguard, Inc.
# SPDX-License-Identifier: Apache-2.0
apiVersion: policy.sigstore.dev/v1beta1
kind: ClusterImagePolicy
metadata:
name: my-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
}
In this policy, you should change the name
to be meaningful to you. The spec
fields are defined at ClusterImagePolicySpec. By default, this policy will apply to all images, as noted with the glob: '**'
parameter. If we keep this as is, this means that we are evaluating everything running in our cluster.
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.
The policy is being implemented in warn
mode, which can generate an alert through CloudEvents
to notify administrators of violations without blocking deployments. You can alternatively use mode: enforce
to block deployments that violate the policy.
The Rego policy itself is definied within the policy
section. The first requirement is to include the input data that is to be evaluated. By default, the image in the registry is available. To include additional metadata, one of more of the following should be set:
includeSpec:
allows you to access the fields in thespec
portion of the Kubernetes manifest, including the container configuration, image names, replicas, resources, and more.includeObjectMeta:
allows you to access the fields in themetadata:
portion of the manifest, including the object’s name and labels.includeTypeMeta:
allows access to the top level fields in the manifest, such as thekind
andapiVersion
.fetchConfigFile:
fetches the OCI config file from the registry, which contains metadata about the image in the registry.
Rego policies must specify type: rego
and the data
field must contain package sigstore
.
For the policy to pass, the isCompliant
field must evaluate to true
within the curly braces. The isCompliant
Boolean is set to false
by default, and the logic in the braces must flip the boolean to true
for the policy to pass.
If you define multiple conditions within the isCompliant
braces, these can be combined using the AND
keyword to the Boolean logic, meaning that each condition must pass for isCompliant
to resolve to true
. You can also define multiple evaluations (meaning, multiple sets of isCompliant
braces) in the same policy. You would combine these in your policy with the OR
keyword, meaning that if any of the stated conditions evaluate to true
, then the isCompliant
Boolean will also be true
.
This same structure must be present in all Rego-based policies.
Rego Policy to Check Metadata Labels
You can set a Rego policy to ensure that it is compliant with certain labels within your metadata.
For example, within the production environment (with the “production” label) you can ensure that the compliance team is the approver.
isCompliant {
input.metadata.labels.env == "production"
input.metadata.labels.approved-by == "compliance-team"
}
Here, the policy is requiring and checking that the labels exist in the ObjectMeta
data. This policy will evaluate to true only if both labels exist in the metadata portion of the manifest.
Rego Policy to Check Kubernetes Pod Security
As a cluster-level resource, a Kubernetes Pod Security Policy allows a cluster administrator to control security-sensitive aspects of a Pod’s specification. This defines a set of conditions that a Pod must meet so that it can be allowed into the cluster. You can think of it as a built-in admission controller which enforces security policies on Pods across a cluster.
This policy checks to make sure our Pod security specifications are properly set.
isCompliant {
input.spec.hostNetwork == "false"
input.spec.hostPID == "false"
input.spec.hostIPC == "false"
}
Here, hostNetwork
refers to the host’s networking namespace, hostPID
refers to the host process ID namespace, and hostPIC
refers to the IPC (interprocess communication) namespace.
This policy will pass if all the restricted values are set to false
.
Rego Policy that Disallows Specified Images
In some cases, you may want to evaluate an item elsewhere in the manifest, such as an image source that is included within the container specs of the same manifest. For example, a manifest may have a snippet with a disallowed NGINX image from Docker Hub:
spec:
containers:
- name: "your-container-name"
image: nginx:latest
- name: "another-container-name"
image: nginx
In this case, within the policy
section of your Rego policy, you’ll need to iterate over the image array and check all the relevant fields for the restricted value. You can use the [_]
syntax to iterate through the array. You can use the not
keyword in conjunction with the contains()
built-in function to evaluate all the items within the array.
isCompliant {
result:= input.spec.containers.image[_]
not contains(result,"docker.io")
}
This policy will not admit Pods that come from docker.io.
Rego Policy that Disallows Privilege Escalation in Pods
This example Rego policy will disallow privilege escalation in Pods following the Kubernetes Pod Security Baseline Standard. The Baseline Standard is a minimally restrictive policy which prevents known privilege escalations and allows the default and minimally specified Pod configuration.
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
}
Setting the allowPrivilegeEscalation
Boolean controls whether a process can gain more privileges than its parent process. This value will evaluate to true
when the container is run as privileged. You can review more information about how to configure a security context for a Pod or Container on the Kubernetes docs.
This Rego 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
in order for the policy to pass.
Rego Policy that Checks Maximum Age of Images
This example Rego policy checks the maximum age (in days) allowed for an image running in your cluster. Policy Controller measures this through the created
field of a container image’s configuration. This ensures that your images are regularly updated and maintained.
Note that some build tools may fail this check due to using a fixed time (like the Unix epoch) for creation in their reproducible builds. However, many of these tools support specifying SOURCE_DATE_EPOCH
, which aligns creation time with the date of the source commit.
policy:
fetchConfigFile: true
type: "rego"
data: |
package sigstore
nanosecs_per_second = 1000 * 1000 * 1000
nanosecs_per_day = 24 * 60 * 60 * nanosecs_per_second
# Change this to the maximum number of days you would like to allow
maximum_age = 30 * nanosecs_per_day
default isCompliant = false
isCompliant {
created := time.parse_rfc3339_ns(input.config[_].created)
time.now_ns() < created + maximum_age
}
Here, the policy defines a variable for the maximum_age
, in this case set to 30
, which you can change to the number of days old you would permit an image to be.
Within the isCompliant
braces, the Rego policy leverages time
to evaluate whether the current time is less than the maximum allowed age. To review the different methods of implementing time
within Rego, review the Time reference documentation.
Rego Policies that Define Custom Error and Warning Messages
Rego policies have the added benefit of allowing you to define custom error and warning messages.
This example attestations
block requires clusters to have a vulnerability report in order to be deemed compliant. Notice, though, that it also defines an errorMsg
string.
attestations:
- name: must-have-vuln-report
predicateType: vuln
policy:
type: rego
data: |
package sigstore
isCompliant[response] {
result = (input.predicateType == "chainguard.dev/attestation/vuln/v1")
errorMsg = "Not found expected predicate type 'chainguard.dev/attestation/vuln/v1'"
warnMsg = ""
response := {
"result" : result,
"error" : errorMsg,
"warning" : warnMsg
}
}
Here, the custom error message reads Not found expected predicate type 'chainguard.dev/attestation/vuln/v1'
. Rather than returning the default error, Policy Controller will return this string as a custom error message.
Notice, too, that the previous example defines a warnMsg
variable. Policy Controller will only return a warning message to the caller if the policy in question is in warn
mode, so in that case it was left as an empty string.
The following attestations
block is similar to the previous one, but this time it defines the warnMsg
variable to be used as a custom warning message.
attestations:
- name: must-have-vuln-report
predicateType: vuln
policy:
type: rego
data: |
package sigstore
isCompliant[response] {
result = (input.predicateType == "cosign.sigstore.dev/attestation/vuln/v1")
errorMsg = ""
warnMsg = "WARNING: Found an attestation with predicate type 'cosign.sigstore.dev/attestation/vuln/v1'"
response := {
"result" : result,
"error" : errorMsg,
"warning" : warnMsg
}
}
Defining custom error and warning messages with Rego can help with troubleshooting, as they can explain specific policy issues that otherwise may not be clearly understandable.
Learn More
To understand more about the Rego policy format, you can review the Rego Policy Reference which includes details on assignment and equality, arrays, objects, sets, and rules.
Last updated: 2024-05-10 15:56
Quick Nav
- Rego Policy Template
- Rego Policy to Check Metadata Labels
- Rego Policy to Check Kubernetes Pod Security
- Rego Policy that Disallows Specified Images
- Rego Policy that Disallows Privilege Escalation in Pods
- Rego Policy that Checks Maximum Age of Images
- Rego Policies that Define Custom Error and Warning Messages
- Learn More