Integrate Docker Image Signing in CI Pipeline
Published: 2023-05-23 21:02:22

**Why Bother with Docker Image Signing?**

a. Supply chain security: Ensures that the image is vetted before deployment to a production environment.

b. To pass vulnerability tests and checks: Vulnerability assessment tools used by QA engineers and other testers analyze the integrity of images to ensure it is unmodified and original. It verifies that the image is from the expected source.

c. Customer Trust: Customers and consumers of the image can have confidence in its originality and integrity.

d. Image Integrity: If anyone without access to the source tries to modify it, the signature verification would fail. Hence only you can sign your image with your chosen password or key. 
 

As an engineer, you want to maintain the integrity of the applications you are deploying to the production environment, even when there are attacks by shady internet characters. For Docker, there is a manual way to do this by doing `docker sign trust image:tag` before pushing the image to your repository and subsequently, deployment. Automating this process on your continuous integration is what this article focuses on. Our tools of choice are Github and CircleCI and cosign.

**Prerequisite**
1. Integrate CircleCI with GitHub as seen [here] (https://circleci.com/docs/getting-started/)

2. Container [repository](https://docs.docker.com/docker-hub/quickstart/)

3. A Dockerfile to build your [image](https://nodejs.org/en/docs/guides/nodejs-docker-webapp).

**Let's Dive Right In**

- _Create CircleCI Folder_
Create a CircleCI job with Docker and Nodejs preinstalled into the workflow. To do this, create a **.circleci**  folder at the root of your project. In that folder, create a **config.yml** file with the given configurations:


```
version: 2.1
jobs:
  deploy-sign-images-to-production:
    docker:
      - image: wecs/circle-ci-gcloud-node:0.1.0
    working_directory: ~/signed-images
      - run:
          name: Build and Deploy Signed Images
          command: |
            chmod u+x ./deployment.sh
            if [ "${CIRCLE_BRANCH}" == "main" ]; then
              ./deploy.sh
            fi
```



- _Create Bash Script_
Create a bash file called **deploy.sh** still in the root of your project. This will help install cosign to sign the docker image. 




```
# !/bin/sh
set -e
set -o pipefail

# login to docker hub
echo “$DOCKERHUB_PASSWORD” | docker login -u “$DOCKERHUB_USERNAME” — password-stdin
docker build -t dockerhuburl/imagename:$CIRCLE_SHA1 .

# download cosign
curl -LO https://github.com/sigstore/cosign/releases/latest/download/cosign-linux-amd64

# give cosign executable access
chmod +x cosign-linux-amd64

# move cosign to your CI machine bin folder
mv cosign-linux-amd64 /usr/local/bin/cosign

# check cosign version and that it installed successfully
cosign version

# generate key pair using set $COSIGN_PASSWORD
cosign generate-key-pair

# push to docker hub
docker -- push dockerhuburl/imagename:$CIRCLE_SHA1

# sign image
cosign sign --key cosign.key dockerhuburl/imagename:tag:$CIRCLE_SHA1 -y

# verify image
cosign verify --key cosign.pub dockerhuburl/imagename:tag:$CIRCLE_SHA1:$CIRCLE_SHA1
```



- _Explaining Environmental Variables Created_
First, we set need to set CircleCI environment variables. Read this article https://circleci.com/docs/env-vars/ for more insight. Set **$DOCKERHUB_PASSWORD** and **$DOCKERHUB_USERNAME**  to your docker hub login details. You do not need to set **$CIRCLE_SHA1** as it is a unique variable generated by CircleCI you can take advantage of in your pipeline. Then finally **create an environment variable called $COSIGN_PASSWORD** and pass any value you want to it. For this tutorial, I use **1234** as the password. This would be the password to help generate the cosign.key and cosign.pub files.




- _Final Review_
Very importantly, we need to note that we need the image to first exist in the docker repository so we can be able to sign it. Hence, why we push the image first before signing and verifying it, don't forget to have a Dockerfile in your environment. Create a pull request and merge it to your main branch on GitHub. Switch to your project dashboard on CircleCI to monitor your build. You should have your signed image pushed to docker hub. Hurray!!!



The sequel article would touch on how to create a policy that allows only signed images to be deployed in your Kubernetes cluster.