Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs: add docs tutorial on custom policies with rego #6104

Merged
merged 5 commits into from
Feb 14, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 104 additions & 0 deletions docs/tutorials/misconfiguration/custom-checks.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
# Custom Checks with Rego

Trivy can scan configuration files for common security issues (a.k.a IaC misconfiguration scanning). In addition to a comprehensive built in database of checks, you can add your own custom checks. Checks are written in [Rego](https://www.openpolicyagent.org/docs/latest/policy-language/) language and the full documentation for checks and customizing them is available [here](https://aquasecurity.github.io/trivy/latest/docs/scanner/misconfiguration/custom/).

This tutorial will walk you through writing a custom policy in Rego that checks for an issue in a Dockerfile.

When you are writing a check, it's important to understand the input to the check. This will be the IaC file that you are scanning; for example, a Kubernetes YAML resource definition, or an AWS JSON CloudFormation, or in our case a Dockerfile.

Since Rego is primarily tailored to query JSON objects, all incoming configuration files needs to be first converted to structured object, which is available to the Rego code as the input variable. This is nothing that users have to do manually in Trivy. Instead, Rego makes it possible to pass in custom Schemas that detail how files are converted.

[Here you can find the schemas](https://github.com/aquasecurity/defsec/tree/master/pkg/rego/schemas) that define how different configuration files are converted to JSON by Trivy.
This tutorial will make use of the [dockerfile.json schema](https://github.com/aquasecurity/defsec/tree/master/pkg/rego/schemas). Alternatively, users can use the [Schema Explorer.](https://aquasecurity.github.io/trivy-schemas/)
Either schema listed in the directory is needed to verify our configuration file.

## Create a Rego file and Specify Trivy metadata

First, create a new `.rego` file e.g. a `docker-check.rego` file:
```
touch docker-check.rego
```

Next, we need to specify metadata about the check. This is information that helps Trivy load and process the check.

```
# METADATA
# title: Verify Image
# description: Verify Image is allowed to be used and in the right format
# schemas:
# - input: schema["dockerfile"]
# custom:
# id: ID001
# severity: MEDIUM
# input:
# selector:
# - type: dockerfile
```

Important: The `METADATA` has to be defined on top of the file.

More information on the different fields in the metadata can be found in the [Trivy documentation.](https://aquasecurity.github.io/trivy/latest/docs/scanner/misconfiguration/custom/)

## Package and imports

```
package custom.dockerfile.ID001

import future.keywords.in
```

Every rego policy has a package name. In our case, we will call it `custom.dockerfile.ID001` to avoid confusion between custom checks and built-in checks. The group name `dockerfile` has no effect on the package name. Note that each package has to contain only one policy. However, we can pass multiple policies into our Trivy scan.
The first keyword of the package, in this case `custom`, will be reused in the `trivy` command as the `--namespace`.

## Allowed data

The check that we are setting up compares the container images used in the Dockerfile with a list of white-listed container images. Thus, we need to add the images that are allowed to be used in the Dockerfile to our check. In our case, we will store them in an array of arrays:
```
allowed_images := {
["node:21-alpine3.19", "as", "build-deps"],
["nginx:1.2"]
}
```

## Select the images that are used in the Dockerfile

Next, we need to iterate over the different commands in our Dockerfile and identify the commands that use base container images:
```
deny[msg] {
input.Stages[m].Commands[l].Cmd == "from"
val := input.Stages[m].Commands[l].Value
not val in allowed_images
msg := sprintf("The container image '%s' used in the Dockerfile is not allowed", val)
}
```

Let's look at the policy line by line:

1. The rule should always be `deny` in the Trivy Rego checks
2. `input.Stages[m].Commands[l].Cmd` `input` allows us to access the different commands in the Dockerfile. We need to access the commands that use "FROM". Every command will be converted to lowercase.
3. `val := input.Stages[m].Commands[l].Value` accesses the value of the `FROM` command and stores it in `val`
4. `not val in allowed_images` checks whether val is not part of our allowed images list; this part of the policy relies on the import statement
5. In case our policy fails, the `msg` will be printed with the image name used in `val`

Note that Rego

* uses `AND` automatically to combine conditions in this policy
* automatically iterates through the array of commands in the Dockefile and allowed images

## Run the policy in a Trivy misconfiguration scan
```bash
trivy fs --scanners misconf --policy ./docker-check.rego --namespaces custom ./Dockerfile
```

Please replace:

* `./docker-check.rego` with the file path to your policy
* `custom` should be replaced with your package name if different
* `./Dockerfile` is the path to the Dockerfile that should be scanned

**Note**: If you define custom packages, you have to specify the package prefix via `--namespaces` option. In our case, we called the custom package `custom`.

## Resources

* [Rego provides a long list of courses](https://academy.styra.com/collections) that can be useful in writing more complex policies
* [The Rego documentation provides detailed information on the different types, iterations etc.](https://www.openpolicyagent.org/docs/latest/)
1 change: 1 addition & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ nav:
- GitOps: tutorials/kubernetes/gitops.md
- Misconfiguration:
- Terraform scanning: tutorials/misconfiguration/terraform.md
- Custom Policies with Rego: tutorials/misconfiguration/custom-checks.md
knqyf263 marked this conversation as resolved.
Show resolved Hide resolved
- Signing:
- Vulnerability Scan Record Attestation: tutorials/signing/vuln-attestation.md
- Shell:
Expand Down