diff --git a/.env b/.env index 3148b18..71e3f2e 100644 --- a/.env +++ b/.env @@ -1,4 +1,2 @@ -BASE_VERSION=3.24.1 -BASE_HASH=bec4ccd3817e7c824eb0388971a0b83fab111d586285511ba0266b77e8dc65a9 -OPENSSL_VERSION=4.0.2 +OPENSSL_VERSION=4.0.3 APP_VERSION=1.31.4 diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a623db0..087dd2e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -18,7 +18,7 @@ env: jobs: build-and-publish: if: github.actor == 'ammnt' - runs-on: ubuntu-24.04 + runs-on: ubuntu-26.04 permissions: contents: read packages: write @@ -40,8 +40,9 @@ jobs: - name: Load .env variables☁️ id: load_env run: | + set -euo pipefail while IFS= read -r line; do - if [[ ! "$line" =~ ^# ]] && [[ -n "$line" ]]; then + if [[ ! "$line" =~ ^# ]] && [[ -n "$line" ]] && [[ "$line" == *=* ]]; then key="${line%%=*}" value="${line#*=}" echo "$key=$value" >> $GITHUB_OUTPUT @@ -82,8 +83,6 @@ jobs: cache-from: type=gha cache-to: type=gha,mode=max build-args: | - BASE_VERSION=${{ steps.load_env.outputs.BASE_VERSION }} - BASE_HASH=${{ steps.load_env.outputs.BASE_HASH }} OPENSSL_VERSION=${{ steps.load_env.outputs.OPENSSL_VERSION }} APP_VERSION=${{ steps.load_env.outputs.APP_VERSION }} VCS_REF=${{ steps.load_env.outputs.VCS_REF }} @@ -91,6 +90,7 @@ jobs: - name: Slim and push image with Mint🔧 run: | + set -euo pipefail curl -sSfL "https://github.com/mintoolkit/mint/releases/latest/download/dist_linux.tar.gz" | tar -zxf - ./dist_linux/mint --quiet build \ --target ghcr.io/ammnt/freenginx:${{ steps.load_env.outputs.APP_VERSION }} \ @@ -110,6 +110,7 @@ jobs: - name: Get image digest🔢 id: digest run: | + set -euo pipefail DIGEST=$(docker inspect -f='{{index .RepoDigests 0}}' ghcr.io/ammnt/freenginx:${{ steps.load_env.outputs.APP_VERSION }} | cut -d'@' -f2) echo "digest=$DIGEST" >> $GITHUB_OUTPUT @@ -117,6 +118,7 @@ jobs: env: CI: true run: | + set -euo pipefail export DIVE_VERSION=$(curl -sSfL "https://api.github.com/repos/wagoodman/dive/releases/latest" | jq -r '.tag_name // empty' | sed -E 's/^v?([0-9][^"]+).*/\1/') curl -sSfL "https://github.com/wagoodman/dive/releases/download/v${DIVE_VERSION}/dive_${DIVE_VERSION}_linux_amd64.tar.gz" | tar -zxf - ./dive --config ./dive-ci.yml -j dive.report.json ghcr.io/ammnt/freenginx:${{ steps.load_env.outputs.APP_VERSION }} @@ -151,12 +153,13 @@ jobs: COSIGN_KEY: ${{ secrets.COSIGN_KEY }} COSIGN_PASSWORD: ${{ secrets.COSIGN_PASSWORD }} run: | + set -euo pipefail cosign sign -y --key env://COSIGN_KEY ghcr.io/ammnt/freenginx@${{ steps.digest.outputs.digest }} cosign sign -y --key env://COSIGN_KEY ammnt/freenginx@${{ steps.digest.outputs.digest }} security-scans: needs: build-and-publish - runs-on: ubuntu-24.04 + runs-on: ubuntu-26.04 permissions: contents: read security-events: write @@ -182,10 +185,11 @@ jobs: - name: Checkov scan🔍 if: matrix.scanner == 'checkov' run: | + set -euo pipefail curl -sSfLO "https://github.com/bridgecrewio/checkov/releases/latest/download/checkov_linux_X86_64.zip" unzip -q ./checkov_linux_X86_64.zip ./dist/checkov \ - --quiet \ + --quiet -s \ --output sarif \ --output-file-path results \ --framework dockerfile \ @@ -199,8 +203,6 @@ jobs: with: image: ghcr.io/ammnt/freenginx:${{ needs.build-and-publish.outputs.app_version }} format: sarif - exit-code: "1" - exit-level: "warn" ignore: "CIS-DI-0005,CIS-DI-0010" output: "dockle.sarif" @@ -218,8 +220,7 @@ jobs: image: ghcr.io/ammnt/freenginx:${{ needs.build-and-publish.outputs.app_version }} sarif-file: scout.sarif write-comment: true - summary: false - exit-code: true + summary: true github-token: ${{ secrets.GH_TOKEN }} - name: Trivy scan🛡️ @@ -230,9 +231,6 @@ jobs: scan-type: image format: sarif output: trivy.sarif - severity: "MEDIUM,HIGH,CRITICAL" - scanners: "vuln,secret" - exit-code: "1" github-pat: ${{ secrets.GH_TOKEN }} trivy-config: trivy.yaml @@ -242,7 +240,6 @@ jobs: with: image: ghcr.io/ammnt/freenginx:${{ needs.build-and-publish.outputs.app_version }} fail-build: false - severity-cutoff: critical output-format: sarif output-file: grype.sarif @@ -254,7 +251,7 @@ jobs: with: image: ghcr.io/ammnt/freenginx:${{ needs.build-and-publish.outputs.app_version }} sarif: true - args: --file=Dockerfile.template --sarif-file-output=snyk.sarif + args: --file=Dockerfile.template --severity-threshold=critical --sarif-file-output=snyk.sarif - name: Generate SBOM with Syft📋 if: matrix.scanner == 'syft' @@ -286,7 +283,7 @@ jobs: release: needs: [build-and-publish, security-scans] - runs-on: ubuntu-24.04 + runs-on: ubuntu-26.04 permissions: contents: write steps: diff --git a/Dockerfile.template b/Dockerfile.template index 1149918..eb1856a 100644 --- a/Dockerfile.template +++ b/Dockerfile.template @@ -1,6 +1,6 @@ # Build arguments for version pinning and reproducibility -ARG BASE_VERSION=3.24.1 -ARG BASE_HASH=bec4ccd3817e7c824eb0388971a0b83fab111d586285511ba0266b77e8dc65a9 +ARG BASE_VERSION=3.24.2 +ARG BASE_HASH=31b6477333eb8257db9e5d7c3a7264fd0467928756f0bbcc27d35bea5d28cdbd FROM alpine:${BASE_VERSION}@sha256:${BASE_HASH} AS builder ARG OPENSSL_VERSION ARG APP_VERSION @@ -17,8 +17,8 @@ ENV CC=clang \ WORKDIR /tmp # System setup and dependency installation RUN addgroup --system --gid "10001" freenginx && adduser --disabled-password --shell /bin/false --ingroup freenginx --uid "10001" --no-create-home freenginx \ - # Update system and install build dependencies - && apk -U -q upgrade && apk add -q --no-cache \ + # Install build dependencies + && apk add -q --no-cache \ clang22=22.1.3-r2 \ lld22=22.1.3-r0 \ llvm=22-r0 \ @@ -26,34 +26,46 @@ RUN addgroup --system --gid "10001" freenginx && adduser --disabled-password --s git=2.54.0-r0 \ mimalloc2-dev=2.2.7-r0 \ build-base=0.5-r4 \ - ca-certificates=20260611-r0 \ linux-headers=7.0.0-r1 \ upx=5.2.0-r0 \ - perl=5.42.2-r0 \ + perl=5.42.2-r1 \ cmake=4.2.3-r0 \ zstd-dev=1.5.7-r2 \ zstd-static=1.5.7-r2 \ - brotli-dev=1.2.0-r1 \ + brotli-dev=1.2.0-r1 \ brotli-static=1.2.0-r1 \ - curl=8.22.0-r0 \ - # Update CA certificates for SSL verification - && update-ca-certificates \ + autoconf=2.73-r0 \ + automake=1.18.1-r1 \ + libtool=2.6.0-r1 \ + python3-dev=3.14.8-r0 \ && git config --global advice.detachedHead false \ - && PCRE_VERSION=$(curl -sSfL "https://api.github.com/repos/PCRE2Project/pcre2/releases/latest" | grep '"tag_name":' | sed -E 's/.*"pcre2\-([^"]+)".*/\1/') \ - && ZLIB_VERSION=$(curl -sSfL "https://api.github.com/repos/zlib-ng/zlib-ng/releases/latest" | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/') \ - && ZSTD_VERSION=$(curl -sSfL "https://api.github.com/repos/tokers/zstd-nginx-module/releases/latest" | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/') \ + && PCRE_VERSION=$(git ls-remote --tags --refs https://github.com/PCRE2Project/pcre2 'pcre2-*' | grep -v -- '-RC' | sed 's|.*/||' | tail -1) \ + && ZLIB_VERSION=$(git ls-remote --tags --refs https://github.com/zlib-ng/zlib-ng | sed 's|.*/||' | sed 's/^v//' | sort -V | tail -1) \ + && ZSTD_VERSION=$(git ls-remote --tags --refs https://github.com/tokers/zstd-nginx-module | sed 's|.*/||' | sort -V | tail -1) \ + && NJS_VERSION=$(git ls-remote --tags --refs https://github.com/nginx/njs '[0-9]*' | sed 's|.*/||' | sort -V | tail -1) \ + && QJS_VERSION=$(git ls-remote --tags --refs https://github.com/quickjs-ng/quickjs 'v*' | sed 's|.*/||' | sort -V | tail -1) \ + && LIBXML2_VERSION=$(git ls-remote --tags --refs https://github.com/GNOME/libxml2 'v*' | sed 's|.*/||' | sort -V | tail -1) \ + && LIBXSLT_VERSION=$(git ls-remote --tags --refs https://github.com/GNOME/libxslt 'v*' | sed 's|.*/||' | sort -V | tail -1) \ # Clone FreeNGINX webserver and remove documentation && git clone -q --depth 1 --recursive -j8 --single-branch -b "release-${APP_VERSION}" https://github.com/freenginx/nginx freenginx && rm -rf /tmp/freenginx/docs/html/* \ # Clone OpenSSL with HTTP/3 and QUIC support && git clone -q --depth 1 --recursive -j8 --single-branch -b "openssl-${OPENSSL_VERSION}" https://github.com/openssl/openssl \ # Clone PCRE2 for regex with JIT support - && git clone -q --depth 1 --recursive -j8 --single-branch -b "pcre2-${PCRE_VERSION}" https://github.com/PCRE2Project/pcre2 \ + && git clone -q --depth 1 --recursive -j8 --single-branch -b "${PCRE_VERSION}" https://github.com/PCRE2Project/pcre2 \ # Clone zlib-ng for modern compression performance && git clone -q --depth 1 --recursive -j8 --single-branch -b "${ZLIB_VERSION}" https://github.com/zlib-ng/zlib-ng \ # Clone Brotli compression module && git clone -q --depth 1 --recurse-submodules -j8 https://github.com/google/ngx_brotli \ # Clone ZSTD compression module && git clone -q --depth 1 --recursive -j8 --single-branch -b "${ZSTD_VERSION}" https://github.com/tokers/zstd-nginx-module \ + # Clone NJS module + && git clone -q --depth 1 --recursive -j8 --single-branch -b "${NJS_VERSION}" https://github.com/nginx/njs \ + # Clone QuickJS-NG engine + && git clone -q --depth 1 --recursive -j8 --single-branch -b "${QJS_VERSION}" https://github.com/quickjs-ng/quickjs.git \ + # Clone libxml2 for NJS + && git clone -q --depth 1 --recursive -j8 --single-branch -b "${LIBXML2_VERSION}" https://github.com/GNOME/libxml2 \ + # Clone libxslt for NJS + && git clone -q --depth 1 --recursive -j8 --single-branch -b "${LIBXSLT_VERSION}" https://github.com/GNOME/libxslt \ # Remove Server header from HTTP responses (HTTP/1.1, HTTP/2, HTTP/3) && sed -ie 's@r->headers_out.server == NULL@0@g' \ /tmp/freenginx/src/http/ngx_http_header_filter_module.c \ @@ -62,6 +74,32 @@ RUN addgroup --system --gid "10001" freenginx && adduser --disabled-password --s # Remove default footer from error pages && sed -ie 's@