Analyze and secure Dockerfiles in CI/CD with OPA

2 minute read

This guide will describe how you can use OPA (Open Policy Agent) to analyze Dockerfiles in your CI/CD pipelines to enforce best practices and security. I will be using Jenkins in the examples; however, any CI/CD service out there that uses Docker for building will apply.

OPA uses a rule files written in the Rego language and is easily readable to build off of examples.

Here are some basic rules for best practices and security:

 1package main
 2
 3# Looking for latest docker image used
 4deny[msg] {
 5    input[i].Cmd == "from"
 6    val := split(input[i].Value[0], ":")
 7    contains(lower(val[1]), "latest")
 8    msg = sprintf("Line %d: do not use 'latest' tag for base images", [i])
 9}
10
11# Looking for base image name
12deny[msg] {
13    input[i].Cmd == "from"
14    val := split(input[i].Value[0], ":")[0]
15    val != "python"
16    msg = "Use only official python image"
17}
18
19# Looking for ADD command instead using COPY command
20deny[msg] {
21    input[i].Cmd == "add"
22    val := concat(" ", input[i].Value)
23    msg = sprintf("Use COPY instead of ADD: %s", [val])
24}
25
26# sudo usage
27deny[msg] {
28    input[i].Cmd == "run"
29    val := concat(" ", input[i].Value)
30    contains(lower(val), "sudo")
31    msg = sprintf("Avoid using 'sudo' command: %s", [val])
32}

Here I’m strictly requiring a specific base image, but you could use this rule to look for only base images:

1deny[msg] {
2    input[i].Cmd == "from"
3    val := split(input[i].Value[0], "/")
4    count(val) > 1
5    msg = sprintf("Line %d: use a trusted base image", [i])
6}

Adding this to your CI/CD pipeline is very easy using the Conftest docker image. Below is a snippet from a Jenkinsfile but could be applied to any CI/CD service that uses Docker images for its build steps. Conftest looks for any rego files in a directory and will apply those rules to the file you point it at.

1stage('Conftest') {
2    steps {
3        script {
4            docker.image("openpolicyagent/conftest").inside("--entrypoint=") {
5                sh "conftest test -p ./.jenkins/policy ./Dockerfile"
6            }
7        }
8    }
9}

For added security, I add the Devops group as owners of the rules and build files in the CODEOWNERS file. We’re notified if any changes are made in any project repo.