As we want to implement vulnerability scanning within our CI pipelines, let’s modify the mdo-posts repository we created in Chapter 11.
Let’s clone the repository first using the following command and cd into the workflows directory:
$ git clone [email protected]:<your_github_user>/mdo-posts.git $ cd mdo-posts/.github/workflows/
Anchore Grype offers an installation script within its GitHub repository that you can download and run, and it should set it up for you. We’ll modify the build.yaml file to include the following step before the Login to Docker Hub step so that we can install Grype within our CI workflow:
– name: Install Grype
id: install-grype
run: curl -sSfL https://raw.githubusercontent.com/anchore/grype/main/install.sh | sh -s
— -b /usr/local/bin
Next, we need to use Grype to scan our images for vulnerabilities.
Scanning images
To run container vulnerability scanning, we can use the following command:
$ grype <container-image>
This will report a list of vulnerabilities with severities—Negligible, Low , Medium, High, Critical, or Unknown—within the image. We can also set a threshold within Grype to fail when any vulnerabilities are equal to or worse than it. For example, if we don’t want to allow any Critical vulnerabilities in the container, we can use the following command:
$ grype -f critical <container-image>
To do so, we will add the following step within the build.yaml file after the Build the Docker image step:
- name: Scan Image for Vulnerabilities
id: vul-scan
run: grype -f critical ${{ secrets.DOCKER_USER }}/mdo-posts:$(git rev-parse –short “$GITHUB_SHA”)
As we’ve made all the changes, let’s push the modified CI pipeline using the following commands:
$ cp ~/modern-devops/ch13/grype/build.yaml .
$ git add –all
$ git commit -m “Added grype”
$ git push
As soon as we push the image, we will see the following in the GitHub Actions tab:
Figure 13.4 – Vulnerability scan failure
As we can see, Grype has reported several vulnerabilities with one being Critical. It has also failed the CI pipeline. That is automated vulnerability scanning in action. This will discover vulnerabilities and only allow builds to end up in your container registry if they meet minimum security standards.
We need to fix the issue here, so let’s look at a more recent image and see whether it can fix the problem. Therefore, instead of using python:3.7-alpine, we will use python:alpine3.18. Let’s do that and push our code to GitHub using the following commands:
$ cd ~/mdo-posts && cp ~/modern-devops/ch13/grype/Dockerfile .
$ git add –all
$ git commit -m “Updated base image”
$ git push
Let’s revisit GitHub Actions and see what we get in the build output:
Figure 13.5 – Vulnerability scan success
The vulnerability scan did not stop our CI build this time, as no Critical vulnerabilities were found.
Tip
Continually update the base image with time, as newer ones contain fewer vulnerabilities and fix older ones.
Now that we’ve secured the image for vulnerabilities, our CI pipeline is complete. You can replicate this process for other microservices as needed. Let’s proceed to discuss CD pipelines.
If you remember, in the last chapter, following the GitOps model, we stored the manifests of all resources on Git. However, due to security concerns with Kubernetes Secrets, we used SealedSecrets to manage them securely.
However, this may not be the ideal solution for all teams due to the following inherent issues:
- SealedSecrets are reliant on the controller that encrypts them. If we lose this controller, we also lose the ability to recreate the secret, essentially losing the Secret forever.
- Access to the Secret is limited to logging in to the cluster and using kubectl, which doesn’t provide non-admins with the ability to manage secrets. While this approach might suit some teams, it may not suit others.
Therefore, we will explore managing secrets using a Secrets management tool to establish a standardized method for centrally managing secrets with more granular control over access. Let’s delve into this topic in the next section.