Hardening Docker with OPA and Harbor
Tags: docker, opa, openpolicyagent, containersecurity
Published: 2024-02-03 15:53:19

---
title: Hardening Docker with OPA and Harbor
published: true
description: Technical implementation steps for my article on Securing Docker with OPA and Harbor
tags: docker, opa, openpolicyagent, containersecurity
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v2890vbrny2b7frvrgy4.png
# published_at: 2024-02-03 12:45 +0000
---

In my article [Docker Chronicles - Securing Docker instances with OPA and Harbor](https://www.linkedin.com/pulse/docker-chronicles-securing-instances-opa-harbor-rendler-661if) 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.

```bash
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.

```bash
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.

![First bundle](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bja2r0hpwcm48rcg0wb2.png)

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.

```bash
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:

```bash
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:

```bash
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:

```json
"authorization-plugins": ["openpolicyagent/opa-docker-authz-v2:0.9"]
```

Run the following command to restart the Docker daemon:

```bash
sudo systemctl restart docker
```

To validate that everything is working run the following command:

```bash
docker image pull nginx:latest
```

And you should now receive an error similar to this:

```bash
$ 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.