From 541c5659475c8eb45fef593a846047eb4e9cea55 Mon Sep 17 00:00:00 2001 From: Josh Knapp Date: Thu, 12 Nov 2026 05:58:57 -0800 Subject: [PATCH] Remove mod_http2/mod_md from Apache images, add build gate (CVE-2026-119618) mod_http2 (httpd 2.4.55-2.4.70) assign_headers() heap corruption -> RCE; AlmaLinux has no fixed httpd yet. Nothing in WHP uses HTTP/2 inside a container (HAProxy edge terminates h2/h3 and speaks HTTP/1.1 to backends), so uninstall mod_http2 (and mod_md if ever present) from cac, cac-fpm and shared-httpd. scripts/httpd-h2-md-gate.sh fails the build if http2_module, proxy_http2_module or md_module is loadable or `httpd -t` fails, writes /etc/whp-httpd-version and the images get LABEL org.whp.httpd-h2-md=removed. Co-Authored-By: Claude Sonnet 5.5 Claude-Session: https://claude.ai/code/session_0185ukXjXyosSkunDsdWz3mb --- .gitea/workflows/build-push.yaml | 1 + Dockerfile | 6 ++++ Dockerfile.fpm | 5 +++ Dockerfile.shared-httpd | 7 ++++ scripts/httpd-h2-md-gate.sh | 55 ++++++++++++++++++++++++++++++++ 5 files changed, 74 insertions(+) create mode 100755 scripts/httpd-h2-md-gate.sh diff --git a/.gitea/workflows/build-push.yaml b/.gitea/workflows/build-push.yaml index 8f76e58..61cc3ac 100644 --- a/.gitea/workflows/build-push.yaml +++ b/.gitea/workflows/build-push.yaml @@ -56,6 +56,7 @@ jobs: scripts/lsphp-src-overlay.sh \ scripts/lsphp-abi-gate.sh \ scripts/tune-mpm.sh \ + scripts/httpd-h2-md-gate.sh \ scripts/tests/lsphp-info-probe.test.sh \ scripts/lsphp-repo-version.sh \ scripts/tests/lsphp-repo-version.test.sh \ diff --git a/Dockerfile b/Dockerfile index fef95a5..990687d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -89,6 +89,12 @@ COPY ./configs/whp-security.conf /etc/httpd/conf.d/ # Set up cron job in a single layer RUN echo "15 */12 * * * root /scripts/log-rotate.sh" >> /etc/crontab +# CVE-2026-119618 defense in depth: no HTTP/2 / mod_md in the image, build fails +# if they come back or the config no longer parses. scripts/httpd-h2-md-gate.sh. +# Placed after every other package step so nothing later can re-add them. +RUN /scripts/httpd-h2-md-gate.sh remove && /scripts/httpd-h2-md-gate.sh check +LABEL org.whp.httpd-h2-md="removed" + ## Healthy = Apache answered with anything but a 5xx. `curl -f` also failed on ## 4xx, so a new site with an empty docroot (403) or an app with no route for / ## (404) showed unhealthy forever, and WHP's monitor restarted it as "PHP diff --git a/Dockerfile.fpm b/Dockerfile.fpm index 2a8ec58..2b6a90b 100644 --- a/Dockerfile.fpm +++ b/Dockerfile.fpm @@ -83,6 +83,11 @@ RUN sed -i "s/rhel\$releasever-amd64/rhel\$releasever-$(uname -m | sed s/x86_64/ # Set up cron job for log rotation RUN echo "15 */12 * * * root /scripts/log-rotate.sh" >> /etc/crontab +# CVE-2026-119618: this image carries httpd-core only (no mod_http2/mod_md). The +# gate keeps it that way and records the version in /etc/whp-httpd-version. +RUN /scripts/httpd-h2-md-gate.sh remove && /scripts/httpd-h2-md-gate.sh check +LABEL org.whp.httpd-h2-md="removed" + EXPOSE 9000 HEALTHCHECK --interval=30s --timeout=5s --start-period=60s --retries=3 \ diff --git a/Dockerfile.shared-httpd b/Dockerfile.shared-httpd index 9a7e223..6ef6e99 100644 --- a/Dockerfile.shared-httpd +++ b/Dockerfile.shared-httpd @@ -31,6 +31,7 @@ COPY ./scripts/log-rotate.sh /scripts/log-rotate.sh COPY ./scripts/entrypoint-shared-httpd.sh /scripts/entrypoint-shared-httpd.sh COPY ./scripts/stop-services.sh /scripts/stop-services.sh COPY ./scripts/tune-mpm.sh /scripts/tune-mpm.sh +COPY ./scripts/httpd-h2-md-gate.sh /scripts/httpd-h2-md-gate.sh RUN chmod +x /scripts/* # Generate self-signed SSL cert (same as main CAC image) @@ -49,6 +50,12 @@ RUN mkdir -p /etc/httpd/conf.d/vhosts # Set up cron job for log rotation RUN echo "15 */12 * * * root /scripts/log-rotate.sh" >> /etc/crontab +# CVE-2026-119618 defense in depth: no HTTP/2 / mod_md in the image, build fails +# if they come back or the config no longer parses. scripts/httpd-h2-md-gate.sh. +# Placed after every other package step so nothing later can re-add them. +RUN /scripts/httpd-h2-md-gate.sh remove && /scripts/httpd-h2-md-gate.sh check +LABEL org.whp.httpd-h2-md="removed" + EXPOSE 80 443 HEALTHCHECK --interval=30s --timeout=5s --start-period=60s --retries=3 \ diff --git a/scripts/httpd-h2-md-gate.sh b/scripts/httpd-h2-md-gate.sh new file mode 100755 index 0000000..698f934 --- /dev/null +++ b/scripts/httpd-h2-md-gate.sh @@ -0,0 +1,55 @@ +#!/bin/bash +# CVE-2026-119618 (mod_http2 assign_headers heap corruption, httpd 2.4.55-2.4.70) +# defense in depth. Nothing in WHP needs HTTP/2 or mod_md inside a container: the +# HAProxy edge terminates h2/h3 and speaks HTTP/1.1 to backends, and certificates +# come from the edge, not mod_md. So the modules are removed from every +# Alma-based Apache image and this gate fails the build if they ever come back. +# +# httpd-h2-md-gate.sh remove uninstall mod_http2 / mod_md RPMs (if present) +# httpd-h2-md-gate.sh check fail unless the modules cannot load, `httpd -t` +# passes, and record the httpd version +# +# Both are idempotent and safe on images with no Apache at all (a no-op). +set -euo pipefail + +BAD_MODULES='http2_module|proxy_http2_module|md_module' +MOD_PKGS='mod_http2 mod_md' + +cmd_remove() { + local p present=() + for p in $MOD_PKGS; do + rpm -q "$p" >/dev/null 2>&1 && present+=("$p") + done + if [ "${#present[@]}" -gt 0 ]; then + dnf remove -y "${present[@]}" + fi + # Belt and braces if a package was removed but a module file was left behind. + rm -f /etc/httpd/conf.modules.d/10-h2.conf /etc/httpd/conf.modules.d/10-proxy_h2.conf \ + /etc/httpd/conf.modules.d/*-md.conf \ + /usr/lib64/httpd/modules/mod_http2.so /usr/lib64/httpd/modules/mod_proxy_http2.so \ + /usr/lib64/httpd/modules/mod_md.so +} + +cmd_check() { + local p mods v fail=0 + command -v httpd >/dev/null 2>&1 || { echo "gate: no httpd binary" >&2; exit 1; } + for p in $MOD_PKGS; do + if rpm -q "$p" >/dev/null 2>&1; then echo "gate FAIL: RPM $p is installed" >&2; fail=1; fi + done + # Anchored on the module name column ("systemd_module" must NOT match md_module). + mods=$(httpd -M 2>/dev/null) || { echo "gate FAIL: httpd -M failed" >&2; exit 1; } + if printf '%s\n' "$mods" | grep -E "^ (${BAD_MODULES}) "; then + echo "gate FAIL: HTTP/2 or mod_md module still loadable" >&2; fail=1 + fi + [ "$fail" -eq 0 ] || exit 1 + httpd -t + v=$(rpm -q --qf '%{VERSION}-%{RELEASE}' httpd-core) + printf '%s\n' "$v" > /etc/whp-httpd-version + echo "gate OK: httpd-core ${v}; no http2/proxy_http2/md modules; httpd -t passes" +} + +case "${1:-}" in + remove) cmd_remove ;; + check) cmd_check ;; + *) echo "usage: $0 remove|check" >&2; exit 2 ;; +esac