Hardening Docker with OPA and Harbor
Denis Rendler
Posted on February 3, 2024
In my article Docker Chronicles - Securing Docker instances with OPA and Harbor I am discussing the benefits that Open-Policy-Agent (OPA) and Harbor bring to securing Docker instances.
This post aims to guide you through the technical steps that I implemented for leveraging OPA and Harbor to not only secure your Docker containers but also to streamline the management of the policies. With OPA's powerful policy-as-code capabilities and Harbor's efficient image management system, you'll discover how to build a Docker ecosystem that's robust, secure, and compliant with industry best practices.
Let's begin.
The Setup
The code repository we will be using can be found here: https://github.com/httpsec-eu/opa-article
First we need to create the Gitlab repository for our policies. I named my repository OPA policies
. If you want to use the bash commands you should change the environment vars to your own values.
export GL=gitlab.example.com
export AUTH_TOKEN=gl-123456789ABCDEFG
curl --request POST --header "PRIVATE-TOKEN: ${AUTH_TOKEN}" \
--header "Content-Type: application/json" --data '{
"name": "opa-policies", "description": "OPA Policies", "path": "opa-policies",
"namespace_id": "1", "initialize_with_readme": "true"}' \
--url "https://${GL}/api/v4/projects/"
Now that we created the Gitlab project to host our policies, you can go ahead an copy the /docker
folder from my repository. Along with the folder you can copy the .gitlab-ci.yml
which provides you with the pipeline to create the OPA bundle and push it to Harbor.
The pipeline uses several variables described below:
Variable name | Purpose | Default value |
---|---|---|
BUNDLE | the name of the bundle which will be used as a Harbor artefact name | bundle |
REPO | the folder name to build | ./docker |
TAG | the tag to set for the bundle when pushed to the repository | 0.1 |
IMG_REPO | the repository URL domain | harbor.httpsec.eu |
REPO_USER | the robot account to be used to login to Harbor | no value |
REPO_PASS | the robot account password | no value |
Next we need to create the Harbor repository. I named mine docker-opa
. If you want, I provided a quick curl
call to create and setup the project - please change the env vars to your own.
export REPO=harbor.example.com
export AUTH=[BASIC_AUTH_TOKEN_FOR_REOBOT_ACCOUNT_HERE]
curl -X 'POST' \
"https://${REPO}/api/v2.0/projects" \
-H 'accept: application/json' \
-H 'Content-Type: application/json' \
-H "authorization: Basic ${AUTH}" \
-d '{
"project_name": "docker-opa",
"public": false,
"metadata": {
"enable_content_trust": "true",
"enable_content_trust_cosign": "true"
}
}'
Once the repository is created we need to create a Harbor robot account which we will use to login to Harbor from our Gitlab instance. You can set the variables REPO_USER
and REPO_PASS
as environment variables in the Gitlab project so that you don't have to enter them manually each time you run the pipeline.
Build the first bundle
Now that we created the two repositories we need to build our first OPA bundle. Go to your Gitlab project and select Build > Pipelines
and click on Run pipeline
. Fill in the variables or use the default values and select Run
. Once the pipeline finishes you should see a new entry in the Harbor project similar to figure below.
NOTE: in the screenshot the name of the bundle is docker_auth
because I set the Gitlab pipeline variable BUNDLE
to the same name.
Configure Docker
The next steps are to install the Docker plugin and configure it to pull the policies from our Harbor instance.
Before we install the plugin we need to set the configuration file. The /etc/docker
directory will be mounted as /opa
in the container running the plugin, so let’s create a sub-directory for our configuration file there.
sudo mkdir -p /etc/docker/opa-config
Now copy or move the config.yaml
file from my repository to the /etc/docker/opa-confg/
folder.
To install the plugin run the following command:
docker plugin install openpolicyagent/opa-docker-authz-v2:0.9 opa-args="-config-file /opa/opa-config/config.yaml"
To validate that our plugin has been installed correctly, run the following command:
docker plugin ls
If the everything works you should see an output similar to this:
ID NAME DESCRIPTION ENABLED
d6cee85ae9aa openpolicyagent/opa-docker-authz-v2:0.9 A policy-enabled authorization plugin for Do… true
The last step is to configure Docker to use plugin. For this we need to edit the file, or create it if it doesn't exist, /etc/docker/daemon.json
and add the following line:
"authorization-plugins": ["openpolicyagent/opa-docker-authz-v2:0.9"]
Run the following command to restart the Docker daemon:
sudo systemctl restart docker
To validate that everything is working run the following command:
docker image pull nginx:latest
And you should now receive an error similar to this:
$ docker image pull nginx:latest
Error response from daemon: authorization denied by plugin openpolicyagent/opa-docker-authz-v2:0.9: request rejected by administrative policy
Now your Docker instance is hardened.
Final thoughts
As we wrap up, hardening Docker environments by integrating Open Policy Agent (OPA) and Harbor represents a good starting step in securing containerised applications. Through this tutorial, we've seen how OPA's policy-as-code approach, combined with Harbor's robust image management capabilities, can create a more secure and manageable Docker ecosystem.
By implementing these tools, OPS and security teams can enforce consistent security policies and practices, reducing the risk of vulnerabilities and enhancing the overall security posture of their applications.
Posted on February 3, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.