diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml new file mode 100644 index 0000000000..2035c8c27b --- /dev/null +++ b/.github/workflows/docker-publish.yml @@ -0,0 +1,66 @@ +# ------------------------------------------------------------------ +# 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 diff --git a/PR_multi-arch-docker.md b/PR_multi-arch-docker.md new file mode 100644 index 0000000000..0141a2475e --- /dev/null +++ b/PR_multi-arch-docker.md @@ -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 +``` + +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. | diff --git a/VMs/Dockerfile b/VMs/Dockerfile index aaa8d235a4..e5d8a46e1d 100644 --- a/VMs/Dockerfile +++ b/VMs/Dockerfile @@ -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"] + diff --git a/VMs/buildDockerImage.sh b/VMs/buildDockerImage.sh index b0dd310374..ecd11cd166 100755 --- a/VMs/buildDockerImage.sh +++ b/VMs/buildDockerImage.sh @@ -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: image if it was left behind by a new version of ubuntu:latest being pulled -i=$(docker images | grep "ubuntu" | grep "/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}." diff --git a/VMs/runDockerImage.sh b/VMs/runDockerImage.sh index 5ece3d452b..d92af774e4 100755 --- a/VMs/runDockerImage.sh +++ b/VMs/runDockerImage.sh @@ -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"