Skip to content

Commit

Permalink
Add auto patching of some images
Browse files Browse the repository at this point in the history
  • Loading branch information
jjaniec committed Nov 9, 2023
1 parent b0ae29f commit 3c10c4e
Show file tree
Hide file tree
Showing 8 changed files with 204 additions and 1 deletion.
45 changes: 45 additions & 0 deletions .github/workflows/patch-images.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
name: patch-images

on:
push:
branches:
- 'master'
- 'feat/patch-images'
schedule:
- cron: '0 1 * * *'

jobs:
docker:
runs-on: ubuntu-latest
steps:

- name: Set up QEMU
uses: docker/setup-qemu-action@v1

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1

- name: Login to DockerHub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKERHUB_USER }}
password: ${{ secrets.DOCKERHUB_TOKEN }}

- uses: actions/checkout@v2

- name: Build and push latest
run: make patch-images
env:
DOCKERHUB_USER: ${{ secrets.DOCKERHUB_USER }}
DOCKERHUB_PASS: ${{ secrets.DOCKERHUB_PASS }}
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
ENABLE_PUSH: "true"

- name: Report Status
if: always()
uses: ravsamhq/notify-slack-action@v1
with:
status: ${{ job.status }}
notify_when: 'failure'
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
1 change: 1 addition & 0 deletions .tool-versions
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
python global system
trivy 0.47.0
7 changes: 7 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,22 +1,29 @@
REPOSITORY_ROOT = jjaniec

all: argocd-helm-secrets-sops airflow-exporter img-docker-alias jenkins-inbound-agent-img-awscli
patch-images: argocd-helm-secrets-sops-patch asdf-patch airflow-exporter-patch

argocd-helm-secrets-sops:
cd src/argocd-helm-secrets-sops && ./build-latest.sh "${REPOSITORY_ROOT}/argocd-helm-secrets-sops"
argocd-helm-secrets-sops-tests:
./tests/argocd-helm-secrets-sops/check_path.sh "jjaniec/argocd-helm-secrets-sops:latest"
argocd-helm-secrets-sops-patch:
./patch-images.sh src/argocd-helm-secrets-sops jjaniec/argocd-helm-secrets-sops argocd-helm-secrets-sops-tests

asdf:
cd src/asdf && ./build-latest.sh "${REPOSITORY_ROOT}/asdf"
asdf-tests:
./tests/asdf/check_path.sh "jjaniec/asdf:latest"
asdf-patch:
./patch-images.sh src/asdf jjaniec/asdf asdf-tests

airflow-exporter:
cd src/airflow-exporter && ./build-latest.sh "${REPOSITORY_ROOT}/airflow-exporter"
airflow-exporter-tests:
./tests/airflow-exporter/check_path.sh "jjaniec/airflow-exporter:latest"
./tests/airflow-exporter/metrics_exists.sh "jjaniec/airflow-exporter:latest"
airflow-exporter-patch:
./patch-images.sh src/airflow-exporter jjaniec/airflow-exporter airflow-exporter-tests

img-docker-alias:
cd src/img-docker-alias && ./build-latest.sh "${REPOSITORY_ROOT}/img-docker-alias"
Expand Down
59 changes: 58 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,58 @@
# oci-container-images-factory
# Project Copacetic: Directly patch container image vulnerabilities

![GitHub](https://img.shields.io/github/license/project-copacetic/copacetic)
[![codecov](https://codecov.io/gh/project-copacetic/copacetic/branch/main/graph/badge.svg?token=PBC8EPNHRL)](https://codecov.io/gh/project-copacetic/copacetic)
[![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/project-copacetic/copacetic/badge)](https://api.securityscorecards.dev/projects/github.com/project-copacetic/copacetic)
[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fproject-copacetic%2Fcopacetic.svg?type=shield&issueType=license)](https://app.fossa.com/projects/git%2Bgithub.com%2Fproject-copacetic%2Fcopacetic?ref=badge_shield&issueType=license)

`copa` is a CLI tool written in [Go](https://golang.org) and based on [buildkit](https://github.com/moby/buildkit) that can be used to directly patch container images given the vulnerability scanning results from popular tools like [Trivy](https://github.com/aquasecurity/trivy).

## Demo

![intro](demo/copa-demo.gif)

## Why?

We needed the ability to patch containers quickly without going upstream for a full rebuild. As the window between [vulnerability disclosure and active exploitation continues to narrow](https://www.bleepingcomputer.com/news/security/hackers-scan-for-vulnerabilities-within-15-minutes-of-disclosure/), there is a growing operational need to patch critical security vulnerabilities in container images so they can be quickly redeployed into production. The need is especially acute when those vulnerabilities are:

- inherited from base images several levels deep and waiting on updated releases to percolate through the supply chain is not an option
- found in 3rd party app images you don't maintain with update cadences that don't meet your security SLAs.

![direct image patching](./docs/imgs/direct-image-patching.png)

In addition to filling the operational gap not met by left-shift security practices and tools, the ability of `copa` to patch a container without requiring a rebuild of the container image provides other benefits:

- Allows users other than the image publishers to also patch container images, such as DevSecOps engineers.
- Reduces the storage and transmission costs of redistributing patched images by only creating an additional patch layer, instead of rebuilding the entire image which usually results in different layer hashes that break layer caching.
- Reduces the turnaround time for patching a container image by not having to wait for base image updates and being a faster operation than a full image rebuild.
- Reduces the complexity of patching the image from running a rebuild pipeline to running a single tool on the image.

## How?

The `copa` tool is an extensible engine that:

1. Parses the needed update packages from the container image’s vulnerability report produced by a scanner like Trivy. New adapters can be written to accommodate more report formats.
2. Obtains and processes the needed update packages using the appropriate package manager tools such as apt, apk, etc. New adapters can be written to support more package managers.
3. Applies the resulting update binaries to the container image using buildkit.

![report-driven vulnerability patching](./docs/imgs/vulnerability-patch.png)

This approach is motivated by the core principles of making direct container patching broadly applicable and accessible:

- **Copa supports patching _existing_ container images**.
- Devs don't need to build their images using specific tools or modify them in some way just to support container patching.
- **Copa works with the existing vulnerability scanning and mitigation ecosystems**.
- Image publishers don't need to create new workflows for container patching since Copa supports patching container images using the security update packages already being published today.
- Consumers do not need to migrate to a new and potentially more limited support ecosystem for custom distros or change their container vulnerability scanning pipelines to include remediation, since Copa can be integrated seamlessly as an extra step to patch containers based on those scanning reports.
- **Copa reduces the technical expertise needed and waiting on dependencies needed to patch an image**.
- For OS package vulnerabilities, no specialized knowledge about a specific image is needed to be patch it as Copa relies on the vulnerability remediation knowledge already embedded in the reports produced by popular container scanning tools today.

For more details refer to the [full documentation](https://project-copacetic.github.io/copacetic/).

## Contributing
There are several ways to get involved:
* Join the [mailing list](https://groups.google.com/g/project-copa) to get notifications for releases, security announcements, etc.
* Join the [biweekly community meetings](https://docs.google.com/document/d/1QdskbeCtgKcdWYHI6EXkLFxyzTCyVT6e8MgB3CaAhWI/edit#heading=h.294j02tlxam) to discuss development, issues, use cases, etc.
* Join the `#copa` channel on the [OCI Slack](https://communityinviter.com/apps/opencontainers/join-the-oci-community)

The project welcomes contributions and suggestions that abide by the [CNCF Code of Conduct](./CODE_OF_CONDUCT.md)
85 changes: 85 additions & 0 deletions patch-images.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
#!/bin/bash

set -o nounset
set -o pipefail
set -o errexit
# set -o xtrace
# set -o verbose

OS=$(uname -s | tr '[:upper:]' '[:lower:]')
ARCH=$(uname -m | tr '[:upper:]' '[:lower:]')
ARCH="amd64"

COPACETIC_VERSION="0.5.1"
COPACETIC_DOWNLOAD_URL="https://github.com/project-copacetic/copacetic/releases/download/v${COPACETIC_VERSION}/copa_${COPACETIC_VERSION}_${OS}_${ARCH}.tar.gz"

DIRECTORY="${1}"
IMAGE_REPOSITORY="${2}"
MAKEFILE_TEST_IMAGE_INSTRUCTION="${3}"

ROOT_DIR_LOCATION_="${ROOT_DIR_LOCATION:-$(pwd)}"
UTILS_DIR_LOCATION="${ROOT_DIR_LOCATION_}/utils"

if ! command -v trivy;
then
echo "Trivy not installed, downloading"
wget https://github.com/aquasecurity/trivy/releases/download/v0.47.0/trivy_0.47.0_Linux-64bit.deb
sudo dpkg -i trivy_0.47.0_Linux-64bit.deb
trivy version
fi;

if ! command -v copa;
then
echo "Copacetic not installed, downloading"
wget "${COPACETIC_DOWNLOAD_URL}"
tar -xvf "./copa_0.5.1_${OS}_${ARCH}.tar.gz"
./copa -v
fi;

if [ ! -f "${DIRECTORY}/patch-taglist.txt" ];
then
echo "${DIRECTORY}/patch-taglist.txt not found, skipping"
exit 0
fi;

while read tag;
do
echo "Patching ${IMAGE_REPOSITORY}:${tag}"
set +o errexit
trivy image \
--vuln-type os \
-f json \
--scanners vuln \
--ignore-unfixed \
--output result.json \
"${IMAGE_REPOSITORY}:${tag}"
./copa patch \
-i "docker.io/${IMAGE_REPOSITORY}:${tag}" \
--debug \
-f table \
-r result.json

# Only tag to test image, do not update latest tag on remote repo as we want to keep latest versions instaed
set -o errexit
docker tag "${IMAGE_REPOSITORY}:${tag}-patched" "${IMAGE_REPOSITORY}:latest"
make "${MAKEFILE_TEST_IMAGE_INSTRUCTION}"

# If tests passed, generate a table format output for readme, and push patched image
trivy image \
--vuln-type os \
-f table \
--scanners vuln \
--ignore-unfixed \
--output trivy-result.table \
"${IMAGE_REPOSITORY}:${tag}-patched"
cat trivy-result.table

set +o nounset
if [ "${ENABLE_PUSH}" = "true" ];
then
# Update ${tag} floating tag to latest patched image with same versions
docker tag "${IMAGE_REPOSITORY}:${tag}-patched" "${IMAGE_REPOSITORY}:${tag}"
docker push "${IMAGE_REPOSITORY}:${tag}"
fi;
set -o nounset
done < "${DIRECTORY}/patch-taglist.txt"
4 changes: 4 additions & 0 deletions src/airflow-exporter/patch-taglist.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
latest
2.6.3
2.5.3
2.4.3
2 changes: 2 additions & 0 deletions src/argocd-helm-secrets-sops/patch-taglist.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
latest
v2.6.13-v4.4.2-v3.7.3
2 changes: 2 additions & 0 deletions src/asdf/patch-taglist.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
latest
v0.12.0

0 comments on commit 3c10c4e

Please sign in to comment.