Custom Policies
Overview
You can write custom policies in Rego.
Once you finish writing custom policies, you can pass the directory where those policies are stored with --policy
option.
trivy conf --policy /path/to/custom_policies --namespaces user /path/to/config_dir
As for --namespaces
option, the detail is described as below.
File formats
If a file name matches the following file patterns, Trivy will parse the file and pass it as input to your Rego policy.
File format | File pattern |
---|---|
JSON | *.json |
YAML | *.yaml and *.yml |
Dockerfile | Dockerfile , Dockerfile.* , and *.Dockerfile |
Containerfile | Containerfile , Containerfile.* , and *.Containerfile |
Terraform | *.tf and *.tf.json |
Configuration languages
In the above general file formats, Trivy automatically identifies the following types of configuration files:
- CloudFormation (JSON/YAML)
- Kubernetes (JSON/YAML)
- Helm (YAML)
- Terraform Plan (JSON)
This is useful for filtering inputs, as described below.
Rego format
A single package must contain only one policy.
Example
package user.kubernetes.ID001
import lib.result
__rego_metadata__ := {
"id": "ID001",
"title": "Deployment not allowed",
"severity": "LOW",
"description": "Deployments are not allowed because of some reasons.",
}
__rego_input__ := {
"selector": [
{"type": "kubernetes"},
],
}
deny[res] {
input.kind == "Deployment"
msg := sprintf("Found deployment '%s' but deployments are not allowed", [input.metadata.name])
res := result.new(msg, input)
}
In this example, ID001 "Deployment not allowed" is defined under user.kubernetes.ID001
.
If you add a new custom policy, it must be defined under a new package like user.kubernetes.ID002
.
Policy structure
package
(required)-
- MUST follow the Rego's specification
- MUST be unique per policy
- SHOULD include policy id for uniqueness
- MAY include the group name such as
kubernetes
for clarity- Group name has no effect on policy evaluation
import data.lib.result
(optional)-
- MAY be defined if you would like to embellish your result(s) with line numbers and code highlighting
__rego_metadata__
(optional)-
- SHOULD be defined for clarity since these values will be displayed in the scan results
__rego_input__
(optional)-
- MAY be defined when you want to specify input format
deny
(required)-
- SHOULD be
deny
or start withdeny_
- Although
warn
,warn_*
,violation
,violation_
also work for compatibility,deny
is recommended as severity can be defined in__rego_metadata__
.
- Although
- SHOULD return ONE OF:
- The result of a call to
result.new(msg, cause)
. Themsg
is astring
describing the issue occurrence, and thecause
is the property/object where the issue occurred. Providing this allows Trivy to ascertain line numbers and highlight code in the output. - A
string
denoting the detected issue- Although
object
withmsg
field is accepted, other fields are dropped andstring
is recommended ifresult.new()
is not utilised. - e.g.
{"msg": "deny message", "details": "something"}
- Although
- The result of a call to
- SHOULD be
Package
A package name must be unique per policy.
Example
package user.kubernetes.ID001
By default, only builtin.*
packages will be evaluated.
If you define custom packages, you have to specify the package prefix via --namespaces
option.
trivy conf --policy /path/to/custom_policies --namespaces user /path/to/config_dir
In this case, user.*
will be evaluated.
Any package prefixes such as main
and user
are allowed.
Metadata
Metadata helps enrich Trivy's scan results with useful information.
Example
__rego_metadata__ := {
"id": "ID001",
"title": "Deployment not allowed",
"severity": "LOW",
"description": "Deployments are not allowed because of some reasons.",
"recommended_actions": "Remove Deployment",
"url": "https://cloud.google.com/blog/products/containers-kubernetes/kubernetes-best-practices-resource-requests-and-limits",
}
All fields under __rego_metadata__
are optional.
Field name | Allowed values | Default value | In table | In JSON |
---|---|---|---|---|
id | Any characters | N/A | ||
title | Any characters | N/A | ||
severity | LOW , MEDIUM , HIGH , CRITICAL |
UNKNOWN | ||
description | Any characters | |||
recommended_actions | Any characters | |||
url | Any characters |
Some fields are displayed in scan results.
k.yaml (kubernetes)
───────────────────
Tests: 32 (SUCCESSES: 31, FAILURES: 1, EXCEPTIONS: 0)
Failures: 1 (UNKNOWN: 0, LOW: 1, MEDIUM: 0, HIGH: 0, CRITICAL: 0)
LOW: Found deployment 'my-deployment' but deployments are not allowed
════════════════════════════════════════════════════════════════════════
Deployments are not allowed because of some reasons.
────────────────────────────────────────────────────────────────────────
k.yaml:1-2
────────────────────────────────────────────────────────────────────────
1 ┌ apiVersion: v1
2 └ kind: Deployment
────────────────────────────────────────────────────────────────────────
Input
You can specify input format via __rego_input__
.
All fields under __rego_input
are optional.
Example
__rego_input__ := {
"combine": false,
"selector": [
{"type": "kubernetes"},
],
}
combine
(boolean)- The details are here.
selector
(array)-
This option filters the input by file format or configuration language. In the above example, Trivy passes only Kubernetes files to this policy. Even if a Dockerfile exists in the specified directory, it will not be passed to the policy as input.
When configuration languages such as Kubernetes are not identified, file formats such as JSON will be used as
type
. When a configuration language is identified, it will overwritetype
.Example
pod.yaml
including Kubernetes Pod will be handled askubernetes
, notyaml
.type
is overwritten bykubernetes
fromyaml
.type
acceptskubernetes
,dockerfile
,cloudformation
,terraform
,terraformplan
,json
, oryaml
.