A Taste of Policies
In a previous blog post, we introduced the basic concepts of the Enterprise Contract. This time, we explore it further to showcase the usage of policies.
Throughtout this post, we will use a container image from one of the author’s side projects. This image was built in a GitHub Workflow and was signed with the “keyless” sigstore workflow.
IMAGE=quay.io/lucarval/festoji:latest
For posterity, here is the version of the ec CLI used:
$ ec version
Version v0.1.1857-b8f0da8
Source ID b8f0da8848c5230a46dc62e5aa3eab77b0085d75
Change date 2023-08-08 15:05:57 +0000 UTC (6 days ago)
ECC v0.0.0-20230725143429-4731fc7d3b41
OPA v0.55.0
Conftest v0.44.1
Cosign N/A
Sigstore v1.7.1
Rekor v1.2.2-0.20230530122220-67cc9e58bd23
Tekton Pipeline v0.47.0
Kubernetes Client v0.27.4
The most basic verification we can do on this image is to verify its signature and signed SLSA Provenance were created by the expected identity:
$ ec validate image --policy '' --image $IMAGE \
--certificate-identity-regexp='https:\/\/github\.com\/(slsa-framework\/slsa-github-generator|lcarva\/festoji)\/' \
--certificate-oidc-issuer='https://token.actions.githubusercontent.com' \
--output yaml
A regular expression is needed for the certificate identity because the signature and the signed SLSA Provenance were created by different identities.
Adding a Policy
The verification above is useful, but we want more. We want to apply policy rules to the signature and to the SLSA Provenance. We also want to more easily specify the values for the certificate flags. Let’s create a policy!
---
identity:
subjectRegExp: >-
https:\/\/github\.com\/(slsa-framework\/slsa-github-generator|lcarva\/festoji)\/
issuer: https://token.actions.githubusercontent.com
sources:
- policy:
- github.com/enterprise-contract/ec-policies//policy/lib
- github.com/enterprise-contract/ec-policies//policy/release
ruleData:
allowed_gh_workflow_repos:
- lcarva/festoji
allowed_gh_workflow_refs:
- refs/heads/master
allowed_gh_workflow_names:
- Package
allowed_gh_workflow_triggers:
- push
configuration:
include:
- github_certificate
This policy moves the certificate flags to the policy itself. It also specifies certain policy rules
to be executed. Here we are including some of the existing Enterprise Contract policy rules,
github_certificate.
These policy rules rely on certain data to be provided, e.g. the expected GitHub Workflow
repository. With this policy saved as policy.yaml
, we can simplify how the CLI is invoked:
$ ec validate image --policy 'policy.yaml' --image $IMAGE --info --output yaml
The --info
flag is used to display additional information about the policy rules, such as their
descriptions.
We can tweak the values of the ruleData
in the policy to see what a failure would look like:
violations:
- metadata:
code: github_certificate.gh_workflow_name
description: Check if the value of the GitHub Workflow Name extension in the
image signature certificate matches one of the allowed values. Use the rule
data key "allowed_gh_workflow_names" to specify the list of allowed values.
An empty allow list, which is the default value, causes this check to succeeded.
title: GitHub Workflow Name
msg: 'Name "Package" not in allowed list: ["spam"]'
- metadata:
code: github_certificate.gh_workflow_ref
description: Check if the value of the GitHub Workflow Ref extension in the
image signature certificate matches one of the allowed values. Use the rule
data key "allowed_gh_workflow_refs" to specify the list of allowed values.
An empty allow list, which is the default value, causes this check to succeeded.
title: GitHub Workflow Repository
msg: 'Ref "refs/heads/master" not in allowed list: ["refs/heads/spam"]'
- metadata:
code: github_certificate.gh_workflow_repository
description: Check if the value of the GitHub Workflow Repository extension
in the image signature certificate matches one of the allowed values. Use
the rule data key "allowed_gh_workflow_repos" to specify the list of allowed
values. An empty allow list, which is the default value, causes this check
to succeeded.
title: GitHub Workflow Repository
msg: 'Repository "lcarva/festoji" not in allowed list: ["spam/spam"]'
- metadata:
code: github_certificate.gh_workflow_trigger
description: Check if the value of the GitHub Workflow Trigger extension in
the image signature certificate matches one of the allowed values. Use the
rule data key "allowed_gh_workflow_triggers" to specify the list of allowed
values. An empty allow list, which is the default value, causes this check
to succeeded.
title: GitHub Workflow Trigger
msg: 'Trigger "push" not in allowed list: ["spam"]'
As you can see, the Enterprise Contract provides a comprehensive list of all the identified violations.
Bring Your Own Policy Rules
In addition to the policy rules provide by the Enterprise Contract, it is also possible to use your own policy rules. The festoji-policies git repository illustrates this. It contains a very simple layout:
$ tree
.
├── LICENSE
└── policies
└── github_slsa_provenance.rego
2 directories, 2 files
The important bits are in the rego file:
# METADATA
# title: GitHub SLSA Provenance
# description: >-
# Verify SLSA Provenance created in GitHub meets requirements.
package festoji.policies.github_slsa_provenance
import future.keywords.contains
import future.keywords.if
import future.keywords.in
# METADATA
# title: Materials
# description: Verify SLSA Provenance materials are correct.
# custom:
# short_name: materials
# failure_msg: Unexpected materials
deny contains result if {
some att in input.attestations
match := [material |
some material in att.statement.predicate.materials
material.uri == "git+https://github.com/lcarva/festoji@refs/heads/master"
]
count(match) == 0
result := "Unexpected materials"
}
This defines a single policy rule that verifies the materials section of the SLSA Provenance contain the expected git repository. The Enterprise Contract relies on rego annotations to provide additional information about each of the policy rules. See the docs for more information.
Let’s add this rule to our previous policy:
---
identity:
subjectRegExp: >-
https:\/\/github\.com\/(slsa-framework\/slsa-github-generator|lcarva\/festoji)\/
issuer: https://token.actions.githubusercontent.com
sources:
- policy:
- github.com/enterprise-contract/ec-policies//policy/lib
- github.com/enterprise-contract/ec-policies//policy/release
ruleData:
allowed_gh_workflow_repos:
- lcarva/festoji
allowed_gh_workflow_refs:
- refs/heads/master
allowed_gh_workflow_names:
- Package
allowed_gh_workflow_triggers:
- push
# A new policy source group uses the custom policy rules.
- policy:
- github.com/lcarva/festoji-policies//policies
configuration:
include:
- github_certificate
- github_slsa_provenance # Also specify which custom policy rule to include.
We can run the ec CLI again with this new policy. The default output should be the same. Let’s use
the flag --show-successes
to ensure the custom policy rule was included:
$ ec validate image --policy policy.yaml --image $IMAGE --info --output yaml --show-successes
[…]
- metadata:
code: festoji.policies.github_slsa_provenance.materials
description: Verify SLSA Provenance materials are correct.
title: Materials
msg: Pass
- metadata:
code: github_certificate.gh_workflow_extensions
description: Check if the image signature certificate contains the expected
GitHub extensions. These are the extensions that represent the GitHub workflow
trigger, sha, name, repository, and ref.
title: GitHub Workflow Certificate Extensions
msg: Pass
- metadata:
code: github_certificate.gh_workflow_name
description: Check if the value of the GitHub Workflow Name extension in the
image signature certificate matches one of the allowed values. Use the rule
data key "allowed_gh_workflow_names" to specify the list of allowed values.
An empty allow list, which is the default value, causes this check to succeeded.
title: GitHub Workflow Name
msg: Pass
[…]
Success!
Takeaway
If there is one thing I would like you to takeaway from this blog post is that defining your policy for verifying images in a single file is very powerful. This is effectively the “contract” in Enterprise Contract. You can add it to a git repository to have a formal review process while keeping an audit trail as well as use it as the source of truth for different teams in your organization. (Is PolicyOps a thing?!)