Home
Unchained
Open Source Blog

Unlocking efficiency and security on GitLab: On-demand images with 0-CVE packages powered by Wolfi

Batuhan Apaydin and Furkan Türkal

Welcome back to our blog post series about wolfi-act! In this installment, we're excited to show you how to harness the capabilities of wolfi-act within the GitLab CI/CD ecosystem. You may recall from our recent blog post that wolfi-act initially made waves in the GitHub Actions arena. Speaking of which, if you missed our previous post, we encourage you to give it a read for a deeper dive into the wolfi-act project. However, the good news is that it's not limited to GitHub Actions anymore!

In the ever-evolving world of software development, staying ahead of the curve requires the right tools and practices. Whether you're a developer, a DevOps engineer, or a tech enthusiast, you'll want to meet GitLab – a CI/CD platform that's transforming the way teams build, deploy, and manage software.

Now, let's get to the heart of the matter—integrating wolfi-act into your GitLab CI/CD pipeline. This powerful combination opens up a world of possibilities for automating and enhancing your development workflow. We'll guide you through the process, step by step, so you can leverage wolfi-act on GitLab and supercharge your CI/CD pipeline.

If you're coming from a background of using GitHub-hosted Runners and are new to GitLab CI/CD, understanding GitLab Executors is key to understanding how wolfi-act could be useful for your pipelines.

In the GitLab CI/CD ecosystem, executors are pivotal components that determine how your CI/CD jobs are executed. While they serve a similar purpose to GitHub-hosted Runners, GitLab Executors come with their own set of features and benefits.

At their core, GitLab Executors are responsible for running the jobs defined in your CI/CD pipelines. They dictate the environment in which these jobs are executed and influence key aspects of the execution process.

GitLab provides several types of Executors, each with its own unique characteristics, allowing you to tailor your CI/CD setup to match your project's specific requirements.

Today, we’ll be focusing on the Docker and Kubernetes executor types where the wolfi-act integration makes things easier for you. If you want to learn more about the other supported types of executors in GitLab, here is the full list.


  • Docker Executors: Run CI/CD jobs in isolated Docker containers for flexibility and reproducibility.


  • Kubernetes Executors: Execute CI/CD jobs within Kubernetes pods, offering scalability and resource efficiency but requiring Kubernetes expertise.

Both executor types need container images to be able to execute jobs. This is where wolfi-act becomes handy.

The challenge remains consistent with what we discussed in our previous blog post. Ensuring that all the necessary tools are readily available within container images for running pipelines poses a significant hurdle. This issue arises because every time a new tool is required for a pipeline, it must be incorporated into the specified container image for the task. Consequently, there's a repetitive process of rebuilding the image to accommodate evolving needs, demanding both time and effort.

Moreover, this practice often leads to the creation of excessively large container images, exceeding 1 GB in size. These images tend to contain an abundance of tools that surpass the actual requirements of a given pipeline. As a result, this surplus of tools contributes to increased network bandwidth consumption and prolonged build times.

Furthermore, it's highly likely that these images will go unattended, as they're typically created for a specific, short-term purpose to fulfill current objectives. Additionally, they tend to accumulate in your Container Registry, essentially becoming digital clutter (needs to be garbage collected after a while). From a security standpoint, unmaintained container images can pose risks by potentially exposing your system to new vulnerabilities.

In the GitHub Actions demo within the previous blog post, we have used GitHub’s reusable workflow feature to avoid duplication in workflows. GitHub Actions offers reusable workflows as a means to create a centralized and easily maintainable set of job definitions. These workflows allow developers to define jobs in a single place and reuse them across repositories.

On the other hand, GitLab CI/CD provides hidden jobs, which enable job definition sharing without exposing them in the main CI/CD configuration file, promoting cleaner and more modular configurations. We have to explain what the hidden jobs are before because we defined the `wolfi-act` as a hidden job within the following example. You can learn more about tips and tricks for avoiding duplication in your CI/CD workflow on GitLab, here.

Talk is cheap, show me the code!

> Note: You can find all the source code in the GitLab repository, here.

The following code snippet is the equivalent of the example in the previous blog post with few differences:


stages:
- build-publish-oci-image-with-apko

.wolfi-act:
image: docker:20.10.12
services:
- name: docker:20.10.12-dind
entrypoint: ["env", "-u", "DOCKER_HOST"]
command: ["dockerd-entrypoint.sh", "--tls=false", "--storage-driver=overlay2"]
alias: docker
variables:
DOCKER_HOST: tcp://docker:2375/
DOCKER_TLS_CERTDIR: ""
DOCKER_DRIVER: overlay2
before_script:
- |
#!/usr/bin/env bash
apk add curl jq
curl -sSLO https://packages.wolfi.dev/os/wolfi-signing.rsa.pub
cp wolfi-signing.rsa.pub /etc/apk/keys/
echo "https://packages.wolfi.dev/os" >> /etc/apk/repositories
apk update
apk add apko
echo "[+] apko version: $(apko version)"

cat >./wolfi-act.apko.config.yaml <> ./wolfi-act.apko.config.yaml
done
fi

cat ./wolfi-act.apko.config.yaml

docker run --rm \
-v ${PWD}:/work \
-w /work \
ghcr.io/wolfi-dev/apko:latest \
build \
--arch=x86_64 \
--sbom=false \
wolfi-act.apko.config.yaml \
wolfi-act:latest \
wolfi-act.tar 2>/dev/null

docker load < wolfi-act.tar

script:
- env | sed '/^Signed-off-by/d' | sed '/^COMMAND=/,/COMMAND=/d' | sed '/^DOCKER_ENV_COMMAND=/,/DOCKER_ENV_COMMAND=/d' | tee wolfi-act.github.env
- CMD=$COMMAND
- echo -e "Executing:\n$CMD"
- CMD="set -e; $COMMAND"
- |
docker run -i --rm --platform linux/amd64 \
-v ${PWD}:/work \
-w /work \
--env-file wolfi-act.github.env \
-e CMD="$CMD" \
wolfi-act:latest-amd64 \
bash -exc "$CMD"

build-publish-oci-image-with-apko:
stage: build-publish-oci-image-with-apko
extends: .wolfi-act
variables:
APKO_ARCHS: x86_64,aarch64
APKO_KEYS: https://packages.wolfi.dev/os/wolfi-signing.rsa.pub
APKO_REPOS: https://packages.wolfi.dev/os
APKO_DEFAULT_CONF: https://raw.githubusercontent.com/chainguard-images/images/main/images/wolfi-base/configs/latest.apko.yaml
PACKAGES: curl,apko,cosign,crane
COMMAND: |
set -x

# Make sure repo has an apko.yaml file, otherwise use default
if [[ ! -f apko.yaml ]]; then
echo "Warning: no apko.yaml in repo, downloading from $APKO_DEFAULT_CONF"
curl -sL -o apko.yaml $APKO_DEFAULT_CONF
fi

# Login to OCI registry
apko login $CI_REGISTRY -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD

# Publish image with apko and capture the index digest
apko publish --arch $APKO_ARCHS \
-k $APKO_KEYS -r $APKO_REPOS \
apko.yaml $CI_REGISTRY_IMAGE > .digest

# Sign with cosign
cosign sign --yes $(cat .digest)

# Lastly, tag the image with crane
crane copy $(cat .digest) $CI_REGISTRY_IMAGE:latest

In the example above we are using Docker executors which means that the jobs will be executing within the isolated container. In order to build wolfi-act behind the scenes, we need a Docker Daemon up and running, this is the reason why we specified docker:20.10.12-dindimage in the `services` section with a bunch of variables to be able to spin it up successfully. The docker CLI itself will be installed via the image we specified in the `image` section docker:20.10.12.

As you can see here we defined `wolfi-act` as a hidden job and we used it in the `build-publish-oci-image-with-apko`job through the '.extends` keyword. We also expect two environment variables, one for packages that need to be installed and the other for the script as we did in the previous blog post for the GitHub Actions demo.

As you can see here we defined `wolfi-act` as a hidden job and we used it in the `build-publish-oci-image-with-apko` job through the '.extends` keyword. We also expect two environment variables, one for packages that need to be installed and the other for the script as we did in the previous blog post for the GitHub Actions demo.

The result:

> https://gitlab.com/developer-guy/wolfi-act-em-all/-/jobs/5178848138

Whee! That’s all you need to do to install necessary tools for your scripts, easy peasy!

Thanks for reading, hope you enjoyed it, see you next time!

Share

Ready to Lock Down Your Supply Chain?

Talk to our customer obsessed, community-driven team.

Get Started