Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 66 additions & 0 deletions .github/workflows/docker-publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# ------------------------------------------------------------------
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@davewichers (how) do you publish docker images? https://hub.docker.com/r/owasp/benchmark this one is some months old. This workflow could help automating this, but if you prefer manual publish, we do not need it.

# INACTIVE BY DEFAULT -- manual trigger only (workflow_dispatch).
#
# This workflow builds and publishes a multi-architecture Docker image
# (linux/amd64 + linux/arm64) to Docker Hub.
#
# TO ACTIVATE:
# 1. Add two repository secrets (Settings > Secrets and variables > Actions):
# DOCKERHUB_USERNAME - your Docker Hub username
# DOCKERHUB_TOKEN - a Docker Hub access token (not your password)
# 2. Optionally add automatic triggers by uncommenting the lines below:
# push:
# branches: [master]
# paths: ['VMs/Dockerfile']
# release:
# types: [published]
#
# Until you do both steps, this workflow does nothing on its own.
# ------------------------------------------------------------------

name: Docker Publish

on:
workflow_dispatch:
# Uncomment the triggers below when ready to automate:
# push:
# branches: [master]
# paths: ['VMs/Dockerfile']
# release:
# types: [published]

env:
IMAGE_NAME: owasp/benchmark
PLATFORMS: linux/amd64,linux/arm64

jobs:
build-and-push:
runs-on: ubuntu-latest

permissions:
contents: read

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Set up QEMU (multi-arch emulation)
uses: docker/setup-qemu-action@v3

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

- name: Log in to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}

- name: Build and push multi-arch image
uses: docker/build-push-action@v6
with:
context: VMs
file: VMs/Dockerfile
platforms: ${{ env.PLATFORMS }}
push: true
tags: ${{ env.IMAGE_NAME }}:latest
111 changes: 111 additions & 0 deletions PR_multi-arch-docker.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
# Multi-Architecture Docker Image — Build and Publish Guide

This document accompanies PR #445, which switches the BenchmarkJava Docker image
to a multi-architecture (`linux/amd64` + `linux/arm64`) build via `docker buildx`.

## Summary

The published `owasp/benchmark:latest` Docker image was built on an ARM64 host,
making it `linux/arm64` only. On amd64 machines (the vast majority of CI runners
and developer workstations), Docker falls back to QEMU emulation, causing
startup times over 60 seconds — long enough to break downstream CI (e.g., ZAP
scans reported in #223).

The build tooling now uses `docker buildx build` with
`--platform linux/amd64,linux/arm64`. Docker Hub receives a single manifest
list that serves the native image for each architecture automatically.

## What changed

| File | Change |
|------|--------|
| `VMs/buildDockerImage.sh` | Rewritten to use `docker buildx build` with `--platform linux/amd64,linux/arm64`, `--file VMs/Dockerfile`, and context `VMs`. Builds and pushes a multi-arch manifest list to Docker Hub in one step. |
| `VMs/Dockerfile` | Collapsed multiple `RUN` layers into single chained commands; added `rm -rf /var/lib/apt/lists/*`; added `EXPOSE 8443` and `CMD ["./runBenchmark.sh"]` so the container starts the benchmark by default. Base image remains `ubuntu:latest` per maintainer preference. |
| `VMs/runDockerImage.sh` | Updated image tag from `benchmark` to `owasp/benchmark` to match the new build script's output. |
| `.github/workflows/docker-publish.yml` | New CI workflow (manual trigger only, inactive by default) for automated multi-arch builds. Activation steps below. |

## Manual build

Prerequisites: Docker with buildx support (Docker Desktop 19.03+ or Docker
Engine with the buildx plugin), and a Docker Hub login with push access to
the `owasp/benchmark` namespace:

```bash
docker login -u <your-dockerhub-username>
```

Then, from the **repository root**:

```bash
./VMs/buildDockerImage.sh
```

This builds for both amd64 and arm64 and pushes `owasp/benchmark:latest` to
Docker Hub. `--push` is required because multi-arch manifest lists cannot be
loaded into the local Docker daemon; build and push happen together.

## Running the published image

After publishing, run the image with:

```bash
./VMs/runDockerImage.sh
```

This pulls `owasp/benchmark:latest` from Docker Hub and starts the benchmark
inside the container.

## Activating the GitHub Actions workflow

The workflow file is at `.github/workflows/docker-publish.yml`, but it only
runs when manually dispatched until two activation steps are completed.

### Step 1: Add Docker Hub secrets

Go to **Settings > Secrets and variables > Actions** in the GitHub repository
and add:

| Secret name | Value |
|-------------|-------|
| `DOCKERHUB_USERNAME` | Docker Hub username |
| `DOCKERHUB_TOKEN` | Docker Hub access token (create at https://hub.docker.com/settings/security) |

### Step 2: Enable automatic triggers (optional)

Open `.github/workflows/docker-publish.yml` and uncomment the trigger lines:

```yaml
on:
workflow_dispatch:
# Uncomment when ready:
push:
branches: [master]
paths: ['VMs/Dockerfile']
release:
types: [published]
```

This rebuilds and pushes the image whenever the Dockerfile is changed on
`master` or a new GitHub release is published.

### Step 3: Test with manual trigger first

Before enabling automatic triggers, verify the workflow manually:

1. Go to **Actions > Docker Publish** in the repository.
2. Click **Run workflow**.
3. Confirm both architectures appear with:

```bash
docker manifest inspect owasp/benchmark:latest
```

The output should list entries for both `amd64` and `arm64`.

## What was not changed

| Item | Reason |
|------|--------|
| JDK version (17) | Tracked separately in #227. |
| `bench:bench` user/password | Test image only, not a production deployment. |
| `timeout 60 ./runBenchmark.sh; exit 0` warm-up | Intentional — caches runtime dependencies into the image. |
51 changes: 26 additions & 25 deletions VMs/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,43 +2,44 @@
FROM ubuntu:latest
LABEL org.opencontainers.image.authors="Dave Wichers dave.wichers@owasp.org"

RUN apt-get update
RUN DEBIAN_FRONTEND="noninteractive" apt-get -y install tzdata
RUN apt-get install -q -y \
openjdk-17-jre-headless \
openjdk-17-jdk \
git \
maven \
wget \
iputils-ping \
&& apt-get clean
RUN apt-get update \
&& DEBIAN_FRONTEND="noninteractive" apt-get -y install tzdata \
&& apt-get install -q -y \
openjdk-17-jre-headless \
openjdk-17-jdk \
git \
maven \
wget \
iputils-ping \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*

RUN mkdir /owasp
WORKDIR /owasp

# Download, build, install Benchmark Utilities required by crawler and scorecard generation
RUN git clone https://github.com/OWASP-Benchmark/BenchmarkUtils.git
WORKDIR /owasp/BenchmarkUtils
RUN mvn install
RUN git clone https://github.com/OWASP-Benchmark/BenchmarkUtils.git \
&& cd BenchmarkUtils \
&& mvn install

# Download, build BenchmarkJava
WORKDIR /owasp
RUN git clone https://github.com/OWASP-Benchmark/BenchmarkJava

# Workaround for security fix for CVE-2022-24765
RUN git config --global --add safe.directory /owasp/BenchmarkJava
RUN git clone https://github.com/OWASP-Benchmark/BenchmarkJava \
&& git config --global --add safe.directory /owasp/BenchmarkJava \
&& cd BenchmarkJava \
&& mvn clean package cargo:install

WORKDIR /owasp/BenchmarkJava
RUN mvn clean package cargo:install

RUN useradd -d /home/bench -m -s /bin/bash bench
RUN echo bench:bench | chpasswd
RUN useradd -d /home/bench -m -s /bin/bash bench \
&& echo bench:bench | chpasswd

RUN chown -R bench /owasp/
ENV PATH=/owasp/BenchmarkJava:$PATH

# start up Benchmark once, for 60 seconds, then kill it, so the additional dependencies required to run it are downloaded/cached in the image as well.
# exit 0 is required to return a 'success' code, otherwise the timeout returns a failure code, causing the Docker build to fail.
# Start up Benchmark once for 60 seconds then kill it, so additional runtime
# dependencies are downloaded and cached in the image.
# exit 0 prevents the timeout return code from failing the Docker build.
WORKDIR /owasp/BenchmarkJava
RUN timeout 60 ./runBenchmark.sh; exit 0

EXPOSE 8443
CMD ["./runBenchmark.sh"]

40 changes: 28 additions & 12 deletions VMs/buildDockerImage.sh
Original file line number Diff line number Diff line change
@@ -1,16 +1,32 @@
# Pull in latest version of ubuntu. This builds an image using the OS native to this platform.
docker pull ubuntu:latest
# Remove any ubuntu:<none> image if it was left behind by a new version of ubuntu:latest being pulled
i=$(docker images | grep "ubuntu" | grep "<none" | awk '{print $3}')
if [ "$i" ]
then
docker rmi $i
#!/usr/bin/env bash
set -euo pipefail

IMAGE="owasp/benchmark"
TAG="latest"
PLATFORMS="linux/amd64,linux/arm64"
BUILDER_NAME="benchmark-multiarch"

# Create (or re-use) a buildx builder that supports multi-platform builds.
if ! docker buildx inspect "$BUILDER_NAME" >/dev/null 2>&1; then
echo "Creating buildx builder: $BUILDER_NAME"
docker buildx create --name "$BUILDER_NAME" --use
else
docker buildx use "$BUILDER_NAME"
fi

# Since Docker doesn't auto delete anything, just like for the Ubuntu update, delete any existing benchmark:latest image before building a new one
docker image rm benchmark:latest
docker build -t benchmark .
# Build and push a multi-architecture image in one step.
# --push is required because multi-arch manifest lists cannot be loaded into
# the local daemon. The image is pushed directly to Docker Hub, so this
# script requires `docker login` first.
# Run this script from the repository root; paths below mirror the CI
# workflow (.github/workflows/docker-publish.yml).
echo "Building ${IMAGE}:${TAG} for ${PLATFORMS} ..."
docker buildx build \
--platform "$PLATFORMS" \
--tag "${IMAGE}:${TAG}" \
--file VMs/Dockerfile \
--push \
VMs

# Once verified/tested, to publish an update to the OWASP Benchmark Docker image, run the following:
# docker push owasp/benchmark:latest
echo "Done. Published ${IMAGE}:${TAG} for ${PLATFORMS}."

2 changes: 1 addition & 1 deletion VMs/runDockerImage.sh
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
docker run -t -i -p 8443:8443 --rm benchmark /bin/bash -c "git pull && ./runRemoteAccessibleBenchmark.sh"
docker run -t -i -p 8443:8443 --rm owasp/benchmark /bin/bash -c "git pull && ./runRemoteAccessibleBenchmark.sh"

Loading