From 19c4fa52c256acbbdd619e7da8016474700d4170 Mon Sep 17 00:00:00 2001 From: iamdoubz <> Date: Thu, 29 Oct 2026 23:01:32 -0500 Subject: [PATCH] Rework freenginx.sh: options, logging, and error handling - Switch to bash with strict mode; stop on the first failure and show the tail of the log - Log all output to a timestamped file; -v streams it, -q hides steps - Add -r/-j/-l/-c options and --no- toggles; versions overridable via environment - Fix kTLS kernel check, USE_ZSTD toggle, curl ignoring HTTP errors, and &>/dev/null backgrounding commands under dash - Document options in README --- README.md | 8 + freenginx.sh | 423 ++++++++++++++++++++++++++++----------------------- 2 files changed, 243 insertions(+), 188 deletions(-) diff --git a/README.md b/README.md index d062b1a..d3659c2 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,14 @@ There are quite a few dependencies to install. The main ones being: # How to download Please use the provided script `freenginx.sh`. Make sure to make it executable first `sudo chmod +x freenginx.sh`. Then run it with `./freenginx.sh`. +By default only the build steps are printed and all output goes to a log file in the build root. Options: +- `-v` stream all build output (still logged), `-q` print only errors and the final summary +- `-r DIR` build root, `-j N` parallel jobs, `-l FILE` log file, `-c` run `make clean` first +- `--no-brotli`, `--no-zstd`, `--no-geoip2`, `--no-headers`, `--no-ktls`, `--no-debug` +- Versions can be set from the environment: `V_NGINX=1.31.6 V_QSSL=3.6.5 ./freenginx.sh` + +If a step fails, the script stops and prints the end of the log. + # Make a backup of your existing binary Type `which nginx` and make a copy i.e. `sudo cp /usr/sbin/nginx /usr/sbin/nginx_apt`. **NOTE**: if you have *not* installed nginx, you do not have to do this step. **NOTE**: this script for freenginx is built as a drop in replacement for nginx. diff --git a/freenginx.sh b/freenginx.sh index 5c68182..969ea8d 100644 --- a/freenginx.sh +++ b/freenginx.sh @@ -1,204 +1,251 @@ -#!/bin/sh -#################################### -### START change these variables ### -#################################### -V_NGINX="1.31.5" -V_ZLIB="1.3.2" -V_PCRE="10.49" -V_QSSL="3.6.5" -V_MAXM="1.14.1" -V_HEAD="0.41" -USE_ZSTD=true -BUILDROOT="/home/dwdoubet/Gits/free" -#################################### -#### END change these variables #### -#################################### -USE_KTLS="" -BUILD_INFO="w/GeoIP2,Brotli,Zstd,H3,Headers-More,Quantum,debug" +#!/usr/bin/env bash +# Build freenginx from source with bundled zlib/PCRE2/OpenSSL and third-party modules. +# Installing is left to you: cd into the freenginx source dir and run `sudo make install`. +# +# Every setting below can be overridden from the environment, e.g. +# V_NGINX=1.31.6 BUILDROOT=/tmp/free ./freenginx.sh -v +set -Eeuo pipefail -kernelcheck() -{ - MAJOR_VERSION=$(uname -r | awk -F '.' '{print $1}') - MINOR_VERSION=$(uname -r | awk -F '.' '{print $2}') - if [ $MAJOR_VERSION -ge 5 ] && [ $MINOR_VERSION -gt 9 ] || [ $MAJOR_VERSION -ge 6 ] ; then - return true +### Versions +: "${V_NGINX:=1.31.5}" +: "${V_ZLIB:=1.3.2}" +: "${V_PCRE:=10.49}" +: "${V_QSSL:=3.6.5}" +: "${V_MAXM:=1.14.1}" +: "${V_HEAD:=0.41}" + +### Build settings +: "${BUILDROOT:=$HOME/Gits/free}" +: "${JOBS:=$(nproc)}" +: "${LOG:=}" # default: $BUILDROOT/build--.log +: "${VERBOSITY:=1}" # 0 = quiet, 1 = steps only, 2 = everything +: "${CLEAN:=0}" + +### Optional features (1 = on, 0 = off) +: "${USE_BROTLI:=1}" +: "${USE_ZSTD:=1}" +: "${USE_GEOIP2:=1}" +: "${USE_HEADERS:=1}" +: "${USE_KTLS:=1}" +: "${USE_DEBUG:=1}" + +usage() { + cat </build-$V_NGINX-.log) + -q quiet: print only errors and the final summary + -v verbose: stream all build output to the terminal (still logged) + -c run 'make clean' in the freenginx tree before configuring + --no-brotli | --no-zstd | --no-geoip2 | --no-headers | --no-ktls | --no-debug + disable an optional feature + -h show this help + +Versions come from the top of the script or the environment: + V_NGINX V_ZLIB V_PCRE V_QSSL V_MAXM V_HEAD +EOF +} + +while [ $# -gt 0 ]; do + case $1 in + -r) BUILDROOT=${2:?-r needs a directory}; shift ;; + -j) JOBS=${2:?-j needs a number}; shift ;; + -l) LOG=${2:?-l needs a file}; shift ;; + -q) VERBOSITY=0 ;; + -v) VERBOSITY=2 ;; + -c) CLEAN=1 ;; + --no-brotli) USE_BROTLI=0 ;; + --no-zstd) USE_ZSTD=0 ;; + --no-geoip2) USE_GEOIP2=0 ;; + --no-headers) USE_HEADERS=0 ;; + --no-ktls) USE_KTLS=0 ;; + --no-debug) USE_DEBUG=0 ;; + -h|--help) usage; exit 0 ;; + *) echo "Unknown option: $1" >&2; usage >&2; exit 1 ;; + esac + shift +done + +### Logging: everything goes to $LOG; the terminal gets steps (1) or everything (2). +log() { printf '%s\n' "$*" >>"$LOG"; } +step() { + if [ "$VERBOSITY" -ge 1 ]; then printf '==> %s\n' "$*"; fi + printf '\n==> [%s] %s\n' "$(date +%T)" "$*" >>"$LOG" +} +run() { + log "+ $*" + if [ "$VERBOSITY" -ge 2 ]; then "$@" 2>&1 | tee -a "$LOG"; else "$@" >>"$LOG" 2>&1; fi +} +on_error() { + local rc=$? + echo "!! Build failed (exit $rc, line $1). Full log: $LOG" >&2 + if [ "$VERBOSITY" -lt 2 ]; then tail -n 30 "$LOG" >&2; fi + exit "$rc" +} + +### Helpers +fetch() { # url dir: download and extract into $BUILDROOT unless dir already exists + local url=$1 dir=$2 file=${1##*/} + if [ -d "$dir" ]; then step "$dir (cached)"; return 0; fi + step "Downloading $dir" + run curl -fsSL -o "$file" "$url" + case $file in + *.zip) run unzip -q "$file" ;; + *) run tar -xzf "$file" ;; + esac + rm -f "$file" +} + +sync_git() { # url dir: clone, or fast-forward an existing clone + if [ -d "$2/.git" ]; then + step "Updating $2" + run git -C "$2" pull --ff-only else - return false + step "Cloning $2" + run git clone "$1" "$2" fi + run git -C "$2" submodule update --init --recursive --jobs "$JOBS" } -check_dir() -{ - path=$1 - what=$2 - if [ ! -d "$path" ]; then - if [ $what = "create" ]; then - echo " " - echo "Creating directory at $path" - mkdir -p "$path" - elif [ $what = "zlib" ]; then - echo " " - echo "zlib Downloading..." - curl -s -L -O "https://www.zlib.net/zlib-$V_ZLIB.tar.gz" - echo "zlib Extracting..." - tar -xzf "zlib-$V_ZLIB.tar.gz" - rm "zlib-$V_ZLIB.tar.gz" - elif [ $what = "pcre" ]; then - echo " " - echo "pcre2 Downloading..." - curl -s -L -O "https://github.com/PCRE2Project/pcre2/releases/download/pcre2-$V_PCRE/pcre2-$V_PCRE.tar.gz" - echo "pcre2 Extracting..." - tar -xzf "pcre2-$V_PCRE.tar.gz" - rm "pcre2-$V_PCRE.tar.gz" - elif [ $what = "openssl" ]; then - echo " " - echo "openssl Downloading..." - curl -s -L -O "https://github.com/openssl/openssl/releases/download/openssl-$V_QSSL/openssl-$V_QSSL.tar.gz" - echo "openssl Extracting..." - tar -xzf "openssl-$V_QSSL.tar.gz" - rm "openssl-$V_QSSL.tar.gz" - elif [ $what = "headers" ]; then - echo " " - echo "Headers More Downloading..." - curl -s -L -O "https://github.com/openresty/headers-more-nginx-module/archive/refs/tags/v$V_HEAD.zip" - echo "Headers More Extracting..." - unzip -qq "v$V_HEAD.zip" - rm "v$V_HEAD.zip" - elif [ $what = "brotli" ]; then - echo " " - echo "Brotli Downloading..." - git clone --quiet --recurse-submodules -j8 https://github.com/google/ngx_brotli ngx-brotli - elif [ $what = "zstd" ]; then - echo " " - echo "Zstd Downloading..." - git clone --quiet https://github.com/HanadaLee/ngx_http_zstd_module.git ngx-zstd - elif [ $what = "geoip2" ]; then - echo " " - echo "GeoIP2 Downloading..." - git clone --quiet https://github.com/leev/ngx_http_geoip2_module nginx-geoip2 - elif [ $what = "maxmind" ]; then - echo " " - echo "MaxMind downloading..." - curl -s -L -O "https://github.com/maxmind/libmaxminddb/releases/download/$V_MAXM/libmaxminddb-$V_MAXM.tar.gz" - echo "MaxMind Extracting..." - tar -xzf "libmaxminddb-$V_MAXM.tar.gz" - rm "libmaxminddb-$V_MAXM.tar.gz" - cd "libmaxminddb-$V_MAXM" - ./configure &>/dev/null - make &>/dev/null - sudo make install &>/dev/null - # sudo sh -c "echo /usr/local/lib >> /etc/ld.so.conf.d/local.conf" - sudo ldconfig &>/dev/null - elif [ $what = "ktls" ]; then - if [ kernelcheck ] ; then - echo " " - echo "Enabling KTLS..." - sudo modprobe tls - USE_KTLS=" enable-ktls" - BUILD_INFO="$BUILD_INFO,KTLS" - fi - elif [ $what = "freenginx" ]; then - echo " " - echo "freenginx Downloading..." - curl -s -L -O "https://github.com/freenginx/nginx/archive/refs/tags/release-$V_NGINX.tar.gz" - tar -xzf "release-$V_NGINX.tar.gz" - rm "release-$V_NGINX.tar.gz" - else - echo " " - echo "Path: $path What: $what" - fi - fi +ktls_supported() { # kernel >= 5.10 + local maj min + IFS=. read -r maj min _ <<<"$(uname -r)" + [ "$maj" -gt 5 ] || { [ "$maj" -eq 5 ] && [ "$min" -ge 10 ]; } } -### Main script start -# Create build directory -CURRENTDIR="$BUILDROOT" -check_dir $CURRENTDIR create -cd "$CURRENTDIR" +build_brotli() { + local src=ngx-brotli/deps/brotli + local flags="-Ofast -m64 -march=native -mtune=native -flto -funroll-loops -ffunction-sections -fdata-sections -Wl,--gc-sections" + step "Building brotli" + run cmake -S "$src" -B "$src/out" -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=OFF \ + -DCMAKE_C_FLAGS="$flags" -DCMAKE_CXX_FLAGS="$flags" -DCMAKE_INSTALL_PREFIX=./installed + run cmake --build "$src/out" --config Release --target brotlienc -j "$JOBS" +} -# zlib -CURRENTDIR="$BUILDROOT/zlib-$V_ZLIB" -check_dir $CURRENTDIR zlib +install_maxmind() { # system-wide, only the first time a given version is fetched + local dir=libmaxminddb-$V_MAXM + if [ -d "$dir" ]; then step "$dir (cached, assumed installed)"; return 0; fi + fetch "https://github.com/maxmind/libmaxminddb/releases/download/$V_MAXM/$dir.tar.gz" "$dir" + step "Installing $dir system-wide (sudo)" + cd "$dir" + run ./configure + run make -j "$JOBS" + run sudo make install + run sudo ldconfig + cd "$BUILDROOT" +} -# pcre -CURRENTDIR="$BUILDROOT/pcre2-$V_PCRE" -check_dir $CURRENTDIR pcre +### Setup +case $BUILDROOT in /*) ;; *) BUILDROOT=$PWD/$BUILDROOT ;; esac +mkdir -p "$BUILDROOT" +: "${LOG:=$BUILDROOT/build-$V_NGINX-$(date +%Y%m%d-%H%M%S).log}" +case $LOG in /*) ;; *) LOG=$PWD/$LOG ;; esac +mkdir -p "$(dirname "$LOG")" +: >"$LOG" +trap 'on_error $LINENO' ERR +cd "$BUILDROOT" -# openssl -CURRENTDIR="$BUILDROOT/openssl-$V_QSSL" -check_dir $CURRENTDIR openssl +missing=() +for t in curl tar unzip git cmake make cc; do command -v "$t" >/dev/null || missing+=("$t"); done +if [ ${#missing[@]} -gt 0 ]; then echo "Missing required tools: ${missing[*]}" >&2; exit 1; fi -# headers -CURRENTDIR="$BUILDROOT/headers-more-nginx-module-$V_HEAD" -check_dir $CURRENTDIR headers +step "freenginx $V_NGINX in $BUILDROOT (log: $LOG)" +set | grep -E '^(V_[A-Z]+|USE_[A-Z0-9]+|JOBS|BUILDROOT|VERBOSITY|CLEAN)=' >>"$LOG" -# brotli -CURRENTDIR="$BUILDROOT/ngx-brotli" -check_dir $CURRENTDIR brotli -cd "$CURRENTDIR" -git pull --quiet -CURRENTDIR="$BUILDROOT/ngx-brotli/deps/brotli/out" -check_dir $CURRENTDIR create -cd "$CURRENTDIR" -cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=OFF -DCMAKE_C_FLAGS="-Ofast -m64 -march=native -mtune=native -flto -funroll-loops -ffunction-sections -fdata-sections -Wl,--gc-sections" \ - -DCMAKE_CXX_FLAGS="-Ofast -m64 -march=native -mtune=native -flto -funroll-loops -ffunction-sections -fdata-sections -Wl,--gc-sections" -DCMAKE_INSTALL_PREFIX=./installed .. &>/dev/null -cmake --build . --config Release --target brotlienc &>/dev/null +### Sources +fetch "https://www.zlib.net/zlib-$V_ZLIB.tar.gz" "zlib-$V_ZLIB" +fetch "https://github.com/PCRE2Project/pcre2/releases/download/pcre2-$V_PCRE/pcre2-$V_PCRE.tar.gz" "pcre2-$V_PCRE" +fetch "https://github.com/openssl/openssl/releases/download/openssl-$V_QSSL/openssl-$V_QSSL.tar.gz" "openssl-$V_QSSL" +fetch "https://github.com/freenginx/nginx/archive/refs/tags/release-$V_NGINX.tar.gz" "nginx-release-$V_NGINX" -# zstd -if [ $USE_ZSTD ] ; then - CURRENTDIR="$BUILDROOT" - cd "$CURRENTDIR" - CURRENTDIR="$BUILDROOT/ngx-zstd" - check_dir $CURRENTDIR zstd - cd "$CURRENTDIR" - git pull --quiet +### Optional modules (BUILD_INFO order matches the old hardcoded string) +info=() mods=() +if [ "$USE_GEOIP2" = 1 ]; then + install_maxmind + sync_git https://github.com/leev/ngx_http_geoip2_module nginx-geoip2 + info+=(GeoIP2); mods+=(--add-module=../nginx-geoip2) +fi +if [ "$USE_BROTLI" = 1 ]; then + sync_git https://github.com/google/ngx_brotli ngx-brotli + build_brotli + info+=(Brotli); mods+=(--add-module=../ngx-brotli) +fi +if [ "$USE_ZSTD" = 1 ]; then + sync_git https://github.com/HanadaLee/ngx_http_zstd_module.git ngx-zstd + info+=(Zstd); mods+=(--add-module=../ngx-zstd) +fi +info+=(H3) +if [ "$USE_HEADERS" = 1 ]; then + fetch "https://github.com/openresty/headers-more-nginx-module/archive/refs/tags/v$V_HEAD.zip" "headers-more-nginx-module-$V_HEAD" + info+=(Headers-More); mods+=(--add-module=../headers-more-nginx-module-$V_HEAD) +fi +info+=(Quantum) +if [ "$USE_DEBUG" = 1 ]; then info+=(debug); mods+=(--with-debug); fi +ktls_opt="" +if [ "$USE_KTLS" = 1 ]; then + if ktls_supported; then + step "Enabling kTLS (sudo)" + run sudo modprobe tls + ktls_opt=" enable-ktls" + info+=(KTLS) + else + step "Kernel $(uname -r) is older than 5.10, skipping kTLS" + fi +fi +build_info="w/$(IFS=,; echo "${info[*]}")" + +### Configure and build +SRC=$BUILDROOT/nginx-release-$V_NGINX +cd "$SRC" +if [ "$CLEAN" = 1 ] && [ -f Makefile ]; then + step "Cleaning previous build" + run make clean fi -# geoip2 -CURRENTDIR="$BUILDROOT" -cd "$CURRENTDIR" -CURRENTDIR="$BUILDROOT/nginx-geoip2" -check_dir $CURRENTDIR geoip2 -cd "$CURRENTDIR" -git pull --quiet - -# maxmind -CURRENTDIR="$BUILDROOT" -cd "$CURRENTDIR" -CURRENTDIR="$BUILDROOT/libmaxminddb-$V_MAXM" -check_dir $CURRENTDIR maxmind - -# ktls -CURRENTDIR="$BUILDROOT" -cd "$CURRENTDIR" -CURRENTDIR="$BUILDROOT/ktls" -check_dir $CURRENTDIR ktls - -# freenginx -CURRENTDIR="$BUILDROOT" -cd "$CURRENTDIR" -CURRENTDIR="$BUILDROOT/nginx-release-$V_NGINX" -check_dir $CURRENTDIR freenginx -cd "$CURRENTDIR" -echo " " -echo " " -echo "Configuring freenginx..." -#make clean &>/dev/null -./auto/configure --build="$BUILD_INFO" --prefix=/etc/nginx --sbin-path=/usr/sbin/nginx --modules-path=/usr/lib/nginx/modules --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log \ - --http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx.pid --lock-path=/var/run/nginx.lock --http-client-body-temp-path=/var/cache/nginx/client_temp --http-proxy-temp-path=/var/cache/nginx/proxy_temp \ - --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp --http-scgi-temp-path=/var/cache/nginx/scgi_temp \ - --user=nginx --group=nginx --with-compat --with-file-aio --with-threads --with-http_addition_module --with-http_auth_request_module --with-http_dav_module \ - --with-http_gunzip_module --with-http_gzip_static_module --with-http_random_index_module --with-http_realip_module \ - --with-http_secure_link_module --with-http_slice_module --with-http_ssl_module --with-http_stub_status_module --with-http_sub_module \ - --with-http_v2_module --with-http_v3_module --with-stream --with-select_module --with-poll_module --with-stream_realip_module --with-stream_ssl_module --with-stream_ssl_preread_module \ - --with-zlib=../zlib-$V_ZLIB --with-pcre=../pcre2-$V_PCRE --with-openssl=../openssl-$V_QSSL --with-openssl-opt="no-asm no-tests enable-tls1_3$USE_KTLS" \ - --add-module='../ngx-brotli' --add-module='../nginx-geoip2' --add-module="../headers-more-nginx-module-$V_HEAD" --add-module="../ngx-zstd" \ +step "Configuring freenginx ($build_info)" +run ./auto/configure \ + --build="$build_info" \ + --prefix=/etc/nginx \ + --sbin-path=/usr/sbin/nginx \ + --modules-path=/usr/lib/nginx/modules \ + --conf-path=/etc/nginx/nginx.conf \ + --error-log-path=/var/log/nginx/error.log \ + --http-log-path=/var/log/nginx/access.log \ + --pid-path=/var/run/nginx.pid \ + --lock-path=/var/run/nginx.lock \ + --http-client-body-temp-path=/var/cache/nginx/client_temp \ + --http-proxy-temp-path=/var/cache/nginx/proxy_temp \ + --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp \ + --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp \ + --http-scgi-temp-path=/var/cache/nginx/scgi_temp \ + --user=nginx --group=nginx \ + --with-compat --with-file-aio --with-threads \ + --with-http_addition_module --with-http_auth_request_module --with-http_dav_module \ + --with-http_gunzip_module --with-http_gzip_static_module --with-http_random_index_module \ + --with-http_realip_module --with-http_secure_link_module --with-http_slice_module \ + --with-http_ssl_module --with-http_stub_status_module --with-http_sub_module \ + --with-http_v2_module --with-http_v3_module \ + --with-stream --with-stream_realip_module --with-stream_ssl_module --with-stream_ssl_preread_module \ + --with-select_module --with-poll_module \ + --with-zlib="../zlib-$V_ZLIB" \ + --with-pcre="../pcre2-$V_PCRE" \ + --with-openssl="../openssl-$V_QSSL" \ + --with-openssl-opt="no-asm no-tests enable-tls1_3$ktls_opt" \ + "${mods[@]}" \ --with-cc-opt="-g -O2 -ffile-prefix-map=/data/builder/debuild/nginx-$V_NGINX/debian/debuild-base/nginx-$V_NGINX=. -flto=auto -ffat-lto-objects -fstack-protector-strong -Wformat -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -fPIC -I../openssl-$V_QSSL/build/include" \ - --with-ld-opt="-Wl,-Bsymbolic-functions -flto=auto -ffat-lto-objects -Wl,-z,relro -Wl,-z,now -Wl,--as-needed -pie -L../openssl-$V_QSSL/build/lib" \ - --with-debug > config.log -echo " " -make -j8 > make.log -echo " " -$BUILDROOT/nginx-release-$V_NGINX/objs/nginx -V -echo " " -ls -lh $BUILDROOT/nginx-release-$V_NGINX/objs/nginx \ No newline at end of file + --with-ld-opt="-Wl,-Bsymbolic-functions -flto=auto -ffat-lto-objects -Wl,-z,relro -Wl,-z,now -Wl,--as-needed -pie -L../openssl-$V_QSSL/build/lib" + +step "Compiling with $JOBS jobs (this takes a while)" +run make -j "$JOBS" + +### Summary (always shown, even with -q) +{ + echo + "$SRC/objs/nginx" -V 2>&1 + ls -lh "$SRC/objs/nginx" + echo + echo "Log: $LOG" + echo "Install: cd $SRC && sudo make install" +} | tee -a "$LOG" -- 2.34.1