diff --git a/.github/workflows/audit.yml b/.github/workflows/audit.yml index 01de43b..9a13ac6 100644 --- a/.github/workflows/audit.yml +++ b/.github/workflows/audit.yml @@ -15,6 +15,7 @@ jobs: fail-fast: false matrix: os: [ ubuntu-latest, ubuntu-24.04-arm ] + distro: [ alpine, debian ] runs-on: ${{ matrix.os }} env: TAG: latest @@ -24,6 +25,11 @@ jobs: uses: aquasecurity/setup-trivy@v0.3.1 with: cache: true + - name: Set environment variables + run: | + if [ "${{ matrix.distro }}" = "debian" ]; then + echo "IS_DEBIAN=1" >> $GITHUB_ENV + fi - name: Audit Docker image for amd64 if: ${{ matrix.os == 'ubuntu-latest' }} run: ./script/release-workflow/audit.sh diff --git a/.github/workflows/release_image.yml b/.github/workflows/release_image.yml index cd8aeef..095f2c4 100644 --- a/.github/workflows/release_image.yml +++ b/.github/workflows/release_image.yml @@ -40,6 +40,10 @@ on: jobs: release: runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + distro: [ alpine, debian ] steps: - uses: actions/checkout@v6 @@ -57,6 +61,12 @@ jobs: username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} + - name: Set environment variables + run: | + if [ "${{ matrix.distro }}" = "debian" ]; then + echo "IS_DEBIAN=1" >> $GITHUB_ENV + fi + - name: Release image run: script/release-workflow/run.sh env: diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index bf8ec81..df47edb 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -12,12 +12,22 @@ on: jobs: test: runs-on: "ubuntu-latest" + strategy: + fail-fast: false + matrix: + distro: [ alpine, debian ] + steps: - uses: actions/checkout@v6 - uses: ruby/setup-ruby@v1 - run: "bundle install" - run: "bundle exec rake" + - name: Set environment variables + run: | + if [ "${{ matrix.distro }}" = "debian" ]; then + echo "IS_DEBIAN=1" >> $GITHUB_ENV + fi - name: Setup Docker builder run: ./script/release-workflow/docker-prepare.sh diff --git a/Dockerfile.debian b/Dockerfile.debian new file mode 100644 index 0000000..8f57af7 --- /dev/null +++ b/Dockerfile.debian @@ -0,0 +1,81 @@ +FROM ruby:3.4.10 AS base + +FROM base AS base-amd64 +ENV SUPERCRONIC_SHA1SUM=712d2ece75da6f6e530192a151488578153e4e96 +FROM base AS base-arm64 +ENV SUPERCRONIC_SHA1SUM=93323899ddca3f1198f1796a4bf4418ed1e7982e +FROM base AS base-arm +ENV SUPERCRONIC_SHA1SUM=dcc8535b46bd9752fbbe177fddd7f6f0451da9a4 + +# Supercronic - use base-$TARGETARCH to select correct base image SUPERCRONIC_SHA1SUM +ARG TARGETARCH +FROM base-$TARGETARCH AS pb-dev + +# Install Supercronic +ARG TARGETARCH +ENV SUPERCRONIC_URL=https://github.com/aptible/supercronic/releases/download/v0.2.47/supercronic-linux-${TARGETARCH} \ + SUPERCRONIC=supercronic-linux-${TARGETARCH} +RUN wget "$SUPERCRONIC_URL" \ + && echo "${SUPERCRONIC_SHA1SUM} ${SUPERCRONIC}" | sha1sum -c - \ + && chmod +x "$SUPERCRONIC" \ + && mv "$SUPERCRONIC" "/usr/local/bin/${SUPERCRONIC}" \ + && ln -s "/usr/local/bin/${SUPERCRONIC}" /usr/local/bin/supercronic + +# Installation path +ENV HOME=/pact_broker + +# Setup ruby user & install application dependencies +RUN set -ex && \ + useradd -d $HOME -m -s /bin/false -r -g root ruby && \ + chmod g+w $HOME + +# Install Gems +WORKDIR $HOME +COPY pact_broker/Gemfile pact_broker/Gemfile.lock $HOME/ +RUN cat Gemfile.lock | grep -A1 "BUNDLED WITH" | tail -n1 | awk '{print $1}' > BUNDLER_VERSION +RUN set -ex && \ + apt-get update && \ + DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ + dnsutils \ + g++ \ + gcc \ + git \ + libc6-dev \ + libpq-dev \ + libsqlite3-dev \ + libyaml-dev \ + make && \ + gem install bundler -v $(cat BUNDLER_VERSION) && \ + bundle config set deployment 'true' && \ + bundle config set no-cache 'true' && \ + bundle config set without 'development test' && \ + bundle install && \ + rm -rf vendor/bundle/ruby/*/cache .bundle/cache && \ + find $HOME/vendor/bundle /usr/local/lib/ruby/gems \ + \( -name Gemfile.lock -o -name package-lock.json \) -exec rm -rf {} + && \ + find $HOME/vendor/bundle /usr/local/lib/ruby/gems \ + \( -name *.pem -o -name *.key -o -name *.java -o -name *.jar \) | \ + grep -e sample -e test -e spec | xargs rm -rf {} + && \ + apt-get clean && \ + rm -rf /var/lib/apt/lists/* + +# Install source +COPY pact_broker $HOME/ +RUN mv $HOME/clean.sh /usr/local/bin/clean + +RUN ln -s /pact_broker/script/db-migrate.sh /usr/local/bin/db-migrate +RUN ln -s /pact_broker/script/db-version.sh /usr/local/bin/db-version + +# Hide pattern matching warnings +ENV RUBYOPT="-W:no-experimental" + +# Start Puma +ENV RACK_ENV=production +ENV PACT_BROKER_DATABASE_CLEAN_ENABLED=false +ENV PACT_BROKER_DATABASE_CLEAN_CRON_SCHEDULE="15 2 * * *" +ENV PACT_BROKER_DATABASE_CLEAN_DELETION_LIMIT=500 +ENV PACT_BROKER_DATABASE_CLEAN_OVERWRITTEN_DATA_MAX_AGE=7 +ENV PACT_BROKER_DATABASE_CLEAN_DRY_RUN=false +USER ruby +ENTRYPOINT ["sh", "./entrypoint.sh"] +CMD ["config.ru"] diff --git a/docker-compose-debian.yml b/docker-compose-debian.yml new file mode 100644 index 0000000..23e0d5e --- /dev/null +++ b/docker-compose-debian.yml @@ -0,0 +1,51 @@ +version: "3" + +services: + postgres: + image: postgres:18 + healthcheck: + test: psql postgres --command "select 1" -U postgres + # ports: + # - "5432:5432" + environment: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: password + POSTGRES_DB: postgres + volumes: + - ${PWD}/docker/postgres-entrypoint.sh:/docker-entrypoint-initdb.d/init-db.sh + - ${PWD}/docker/pg-dump.sql:/tmp/pg-dump.sql + logging: + driver: none + + pact-broker-debian: + build: + dockerfile: ./Dockerfile.debian + context: . + ports: + - "9292:9292" + depends_on: + - postgres + environment: + PACT_BROKER_CONF: /tmp/pact_broker.yml + PACT_BROKER_PORT_ENVIRONMENT_VARIABLE_NAME: PORT + PORT: "9292" + PACT_BROKER_DATABASE_URL_ENVIRONMENT_VARIABLE_NAME: DATABASE_URL + DATABASE_URL: "postgres://postgres:password@postgres/postgres" + PACT_BROKER_DATABASE_CLEAN_ENABLED: "false" + PACT_BROKER_DATABASE_CLEAN_CRON_SCHEDULE: "* * * * *" + PACT_BROKER_DATABASE_CLEAN_DRY_RUN: "false" + PACT_BROKER_DATABASE_CLEAN_DELETION_LIMIT: "500" + PACT_BROKER_SQL_LOG_WARN_DURATION: "60" + # Keep all prod versions, AND the latest version for every pacticipant/tag, and all versions less than 30 days old + PACT_BROKER_DATABASE_CLEAN_KEEP_VERSION_SELECTORS: '[{"latest": true, "tag": true}, {"max_age": 30}]' + PACT_BROKER_DATABASE_CONNECT_MAX_RETRIES: "10" + # PACT_BROKER_DATABASE_USERNAME: postgres + # PACT_BROKER_DATABASE_PASSWORD: password + # PACT_BROKER_DATABASE_HOST: postgres + # PACT_BROKER_DATABASE_NAME: postgres + # PACT_BROKER_PORT: "9292" + # PACT_BROKER_LOG_LEVEL: DEBUG + # PACT_BROKER_SQL_LOG_LEVEL: NONE + # PACT_BROKER_DATABASE_CONNECT_MAX_RETRIES: "5" + volumes: + - ./docker/config/pact_broker.yml:/tmp/pact_broker.yml diff --git a/script/.trivyignore b/script/.trivyignore index f8b3ccc..e051b24 100644 --- a/script/.trivyignore +++ b/script/.trivyignore @@ -1,3 +1,5 @@ + +# alpine CVE-2026-39822 CVE-2026-42505 GHSA-6jxj-px6v-747w @@ -13,4 +15,127 @@ GHSA-p67v-3w7g-wjg7 GHSA-phwj-rprq-35pp GHSA-wfpw-mmfh-qq69 GHSA-wjv4-x9w8-wm3h -GHSA-g9g8-vgvw-g3vf \ No newline at end of file +GHSA-g9g8-vgvw-g3vf +# Debian +CVE-2026-58016 +CVE-2026-56372 +CVE-2019-16224 +CVE-2026-44172 +CVE-2026-42216 +CVE-2026-13221 +CVE-2026-20884 +CVE-2026-6653 +CVE-2026-43185 +CVE-2025-68318 +CVE-2026-60002 +CVE-2026-13221 +CVE-2026-42496 +CVE-2026-8376 +CVE-2026-49261 +CVE-2019-16225 +CVE-2019-16227 +CVE-2026-42217 +CVE-2026-24450 +CVE-2026-24660 +# Debian high vulns +CVE-2013-7445 +CVE-2019-16226 +CVE-2019-16228 +CVE-2019-19449 +CVE-2019-19814 +CVE-2021-3847 +CVE-2021-3864 +CVE-2024-21803 +CVE-2024-58015 +CVE-2025-12495 +CVE-2025-12839 +CVE-2025-12840 +CVE-2025-22104 +CVE-2025-38137 +CVE-2025-38187 +CVE-2025-38204 +CVE-2025-38206 +CVE-2025-38421 +CVE-2025-38636 +CVE-2025-39859 +CVE-2025-39862 +CVE-2025-39958 +CVE-2025-59375 +CVE-2025-64181 +CVE-2025-68431 +CVE-2025-69720 +CVE-2026-11940 +CVE-2026-12064 +CVE-2026-12912 +CVE-2026-20889 +CVE-2026-21413 +CVE-2026-23102 +CVE-2026-23208 +CVE-2026-23327 +CVE-2026-24882 +CVE-2026-25210 +CVE-2026-27622 +CVE-2026-31493 +CVE-2026-31536 +CVE-2026-31568 +CVE-2026-32740 +CVE-2026-32741 +CVE-2026-32882 +CVE-2026-33164 +CVE-2026-34379 +CVE-2026-34543 +CVE-2026-34544 +CVE-2026-34545 +CVE-2026-34588 +CVE-2026-40244 +CVE-2026-40250 +CVE-2026-41071 +CVE-2026-41142 +CVE-2026-41992 +CVE-2026-42497 +CVE-2026-43198 +CVE-2026-43263 +CVE-2026-44168 +CVE-2026-44171 +CVE-2026-45186 +CVE-2026-46130 +CVE-2026-46181 +CVE-2026-46279 +CVE-2026-48163 +CVE-2026-48165 +CVE-2026-48962 +CVE-2026-52991 +CVE-2026-53000 +CVE-2026-53010 +CVE-2026-53091 +CVE-2026-53277 +CVE-2026-53615 +CVE-2026-54369 +CVE-2026-56131 +CVE-2026-56374 +CVE-2026-56407 +CVE-2026-56408 +CVE-2026-57432 +CVE-2026-58010 +CVE-2026-58011 +CVE-2026-58012 +CVE-2026-58013 +CVE-2026-58014 +CVE-2026-58015 +CVE-2026-58471 +CVE-2026-58472 +CVE-2026-59999 +CVE-2026-60000 +CVE-2026-61857 +CVE-2026-61861 +CVE-2026-61863 +CVE-2026-61866 +CVE-2026-61870 +CVE-2026-7210 +CVE-2026-8286 +CVE-2026-8927 +CVE-2026-8932 +CVE-2026-9079 +CVE-2026-9080 +CVE-2026-9538 +CVE-2026-9545 \ No newline at end of file diff --git a/script/release-workflow/docker-build.sh b/script/release-workflow/docker-build.sh index a3daefe..961977e 100755 --- a/script/release-workflow/docker-build.sh +++ b/script/release-workflow/docker-build.sh @@ -5,11 +5,15 @@ set -euo >/dev/null ## This will allow for local use for testing or scanning with trivy (multi-manifest builds cannot be imported) ## we will build a multi-manifest build during ./docker-push.sh ARCHES=${ARCHES:-'amd64'} + +DEBIAN=${IS_DEBIAN:+"-debian"} + DOCKER_IMAGE_ORG_AND_NAME="${DOCKER_REPOSITORY:-pactfoundation}/pact-broker" for arch in $ARCHES; do docker buildx build \ --platform linux/$arch \ --output type=docker \ - --tag ${DOCKER_IMAGE_ORG_AND_NAME}:latest-${arch} \ + --tag ${DOCKER_IMAGE_ORG_AND_NAME}:latest-${arch}${DEBIAN} \ + -f Dockerfile${IS_DEBIAN:+.debian} \ . done \ No newline at end of file diff --git a/script/release-workflow/image-scan.sh b/script/release-workflow/image-scan.sh index e4c8398..06422b8 100755 --- a/script/release-workflow/image-scan.sh +++ b/script/release-workflow/image-scan.sh @@ -5,4 +5,5 @@ set -euo >/dev/null script_dir=$(cd "$(dirname $0)" && pwd) ARCH=${ARCH:-'amd64'} -trivy image ${DOCKER_IMAGE_ORG_AND_NAME}:latest-${ARCH} --exit-code 1 --ignorefile ${script_dir}/../.trivyignore +IS_DEBIAN=${IS_DEBIAN:+"-debian"} +trivy image ${DOCKER_IMAGE_ORG_AND_NAME}:latest-${ARCH}${IS_DEBIAN} --severity HIGH,CRITICAL --exit-code 1 --ignorefile ${script_dir}/../.trivyignore diff --git a/script/release-workflow/set-env-vars.sh b/script/release-workflow/set-env-vars.sh index 7b4a29d..e899a4a 100755 --- a/script/release-workflow/set-env-vars.sh +++ b/script/release-workflow/set-env-vars.sh @@ -27,9 +27,11 @@ if [ -z "$TAG" ]; then export VERSION=$(bundle exec bump show-next $INCREMENT) fi + DEBIAN=${IS_DEBIAN:+"-debian"} + export PACT_BROKER_VERSION=$(grep "pact_broker (" pact_broker/Gemfile.lock | awk -F '[()]' '{print $2}') - export TAG="$VERSION-pactbroker${PACT_BROKER_VERSION}" - export MAJOR_TAG="$(echo $VERSION | cut -d'.' -f1)" + export TAG="$VERSION-pactbroker${PACT_BROKER_VERSION}${DEBIAN}" + export MAJOR_TAG="$(echo $VERSION | cut -d'.' -f1)${DEBIAN}" echo "INCREMENT=$INCREMENT" echo "VERSION=$VERSION" diff --git a/script/test.sh b/script/test.sh index 8211073..8c3e84d 100755 --- a/script/test.sh +++ b/script/test.sh @@ -3,11 +3,12 @@ set -e : "${TAG:?TAG must be provided}" +DEBIAN=${IS_DEBIAN:+"-debian"} docker_compose_files=$(find . -name "docker-compose-test*.yml") for file in $docker_compose_files; do - cat $file | sed -e "s/pactfoundation\/pact-broker:latest.*/pactfoundation\/pact-broker:${TAG}\"/g" > dc-tmp + cat $file | sed -e "s/pactfoundation\/pact-broker:latest.*/pactfoundation\/pact-broker:${TAG}${DEBIAN}\"/g" > dc-tmp mv dc-tmp $file done