diff --git a/.gitea/workflows/build-publish.yml b/.gitea/workflows/build-publish.yml index 4454458..2928f7f 100644 --- a/.gitea/workflows/build-publish.yml +++ b/.gitea/workflows/build-publish.yml @@ -1,16 +1,19 @@ # ============================================================================= -# build-and-publish +# build-and-publish (multi-distro matrix) # -# Compiles a custom nginx (with ModSecurity, naxsi, lua, brotli, geoip2, etc.), -# packages the result as a Debian .deb named `twiy`, and uploads it to a -# Sonatype Nexus apt-hosted repository so users can install via `apt`. +# Builds twiy as a Debian .deb for each target distro in parallel: +# - trixie (Debian 13) -> uploaded to NEXUS_REPO_TRIXIE +# - raccoon (Ubuntu 26.04 LTS) -> uploaded to NEXUS_REPO_RACCOON # -# Triggers: -# * Every push to master. -# * Manual run from the Actions UI (workflow_dispatch). +# Each matrix job spins up a Docker container of the target distro on the +# Gitea runner host, builds nginx + modules INSIDE the container so apt deps +# and ldd resolution match what end users have, then uploads the resulting +# .deb to that distro's Nexus apt-hosted repository. # -# Required repository secrets (see the "Publish to Nexus" step for details): -# NEXUS_USER, NEXUS_PASS, NEXUS_URL, NEXUS_REPO +# Required repository secrets: +# NEXUS_USER, NEXUS_PASS, NEXUS_URL (shared) +# NEXUS_REPO_TRIXIE (Debian 13 target) +# NEXUS_REPO_RACCOON (Ubuntu 26.04 target) # ============================================================================= name: build-and-publish @@ -21,160 +24,140 @@ on: jobs: build: - # Pinned to ubuntu-22.04 because the build script targets the toolchain - # versions that ship with that release. Bumping this needs validation - # against the modules pinned in /version. + # Runner is just a docker host; build OS is determined by matrix.image. runs-on: ubuntu-22.04 + strategy: + # If trixie fails, still finish raccoon (and vice versa) — surface both. + fail-fast: false + matrix: + target: [trixie, raccoon] + include: + - target: trixie + image: debian:13 + nexus_repo_secret: NEXUS_REPO_TRIXIE + - target: raccoon + image: ubuntu:26.04 + nexus_repo_secret: NEXUS_REPO_RACCOON + steps: - name: Checkout source uses: actions/checkout@v4 - - name: Install build dependencies - run: | - set -euo pipefail - # Minimal toolchain to: build nginx (build-essential), package the - # output (dpkg-dev, fakeroot), and fetch sources (git, curl, wget). - # gnupg is kept in case a future step needs to verify upstream sigs. - sudo apt-get update -y - sudo apt-get install -y --no-install-recommends \ - git curl wget ca-certificates dpkg-dev fakeroot \ - build-essential gnupg - - - name: Compile nginx and modules - run: | - set -euo pipefail - # Touch /.dockerenv so build/run.sh's container-detection branch is - # taken: it skips `systemctl start nginx` (the runner has no systemd). - # The .deb's own postinst handles service start on the user's host. - sudo touch /.dockerenv - sudo bash build/run.sh new # download sources for nginx + modules - sudo bash build/run.sh build # configure, compile, install - sudo bash build/run.sh postfix # drop default configs into /nginx - - # ───────────────────────────────────────────────────────────────────────── - # Assemble the .deb by hand (we don't use debhelper because the build - # script already places everything at its final paths under the runner's - # root; we just need to mirror those paths into PKG_DIR and add control - # metadata). - # ───────────────────────────────────────────────────────────────────────── - - name: Assemble .deb package + - name: Build nginx and assemble .deb inside ${{ matrix.image }} id: pkg + env: + TARGET: ${{ matrix.target }} + IMAGE: ${{ matrix.image }} run: | set -euo pipefail - PKG_NAME="twiy" - NGINX_VER="$(nginx -v 2>&1 | awk -F'/' '{print $2}')" - # Append the CI run number as the Debian revision so each rebuild - # produces a strictly-greater version (e.g. 1.26.0-3 > 1.26.0-2 > - # 1.26.0). Without this, `apt upgrade twiy` would be a no-op when - # upstream nginx hasn't moved, so packaging fixes wouldn't reach - # users who already have the package installed. - VERSION="${NGINX_VER}-${GITHUB_RUN_NUMBER:-1}" - ARCH="amd64" - PKG_DIR="/opt/${PKG_NAME}_${VERSION}_${ARCH}" - DEB_DIR="${PKG_DIR}/DEBIAN" + mkdir -p dist + # The whole compile + .deb assembly happens inside the target distro + # container. Output is dropped into ./dist/ (mounted from the runner) + # so the publish step on the host can grab it. + sudo docker run --rm \ + -v "$PWD:/repo" \ + -w /repo \ + -e TARGET="$TARGET" \ + "$IMAGE" \ + bash -euxc ' + # build script handles its own apt-get install (per-distro list) + touch /.dockerenv + bash build/${TARGET}.sh new + bash build/${TARGET}.sh build + bash build/${TARGET}.sh postfix - # The `*_temp` dirs under /usr/local/nginx are nginx's compiled-in - # defaults for client_body / proxy / fastcgi / uwsgi / scgi temp - # storage (no --http-*-temp-path was passed to ./configure). They - # must exist before `nginx -t` runs, so we ship them empty in the - # .deb and the postinst chowns them to the nginx user. - sudo mkdir -p "${PKG_DIR}/usr/sbin" "${PKG_DIR}/nginx" \ - "${PKG_DIR}/etc/systemd/system" "${PKG_DIR}/var/log/nginx" \ - "${PKG_DIR}/usr/lib" "${PKG_DIR}/usr/local/lib" \ - "${PKG_DIR}/hostdata/default/public_html" \ - "${PKG_DIR}/usr/nginx_lua" \ - "${PKG_DIR}/usr/local/nginx/client_body_temp" \ - "${PKG_DIR}/usr/local/nginx/proxy_temp" \ - "${PKG_DIR}/usr/local/nginx/fastcgi_temp" \ - "${PKG_DIR}/usr/local/nginx/uwsgi_temp" \ - "${PKG_DIR}/usr/local/nginx/scgi_temp" + PKG_NAME="twiy" + NGINX_VER="$(nginx -v 2>&1 | awk -F/ "{print \$2}")" + # Append CI run number AND target so each rebuild is a strictly- + # greater Debian revision. Without this, `apt upgrade twiy` would + # be a no-op when upstream nginx hasnt moved, so packaging fixes + # wouldnt reach users who already have the package installed. + # The ~target suffix keeps trixie/raccoon versions distinct in + # case any introspection ever compares them. + VERSION="${NGINX_VER}-${GITHUB_RUN_NUMBER:-1}~${TARGET}" + ARCH="amd64" + PKG_DIR="/opt/${PKG_NAME}_${VERSION}_${ARCH}" + DEB_DIR="${PKG_DIR}/DEBIAN" - # Pull every artifact the build produced into the package tree. - # `|| true` on the recursive copies tolerates a missing source dir - # (e.g. when rebuilding without re-running postfix locally). - sudo cp /usr/sbin/nginx "${PKG_DIR}/usr/sbin/" - sudo cp -R /nginx/* "${PKG_DIR}/nginx/" || true - sudo cp /etc/systemd/system/nginx.service "${PKG_DIR}/etc/systemd/system/" - sudo cp -R /hostdata/default "${PKG_DIR}/hostdata/" || true - sudo cp -R /usr/nginx_lua "${PKG_DIR}/usr/" || true + # The *_temp dirs under /usr/local/nginx are nginxs compiled-in + # defaults for client_body / proxy / fastcgi / uwsgi / scgi temp + # storage (no --http-*-temp-path was passed to ./configure). They + # must exist before `nginx -t` runs, so we ship them empty in the + # .deb and the postinst chowns them to the nginx user. + mkdir -p "${PKG_DIR}/usr/sbin" "${PKG_DIR}/nginx" \ + "${PKG_DIR}/etc/systemd/system" "${PKG_DIR}/var/log/nginx" \ + "${PKG_DIR}/usr/lib" "${PKG_DIR}/usr/local/lib" \ + "${PKG_DIR}/hostdata/default/public_html" \ + "${PKG_DIR}/usr/nginx_lua" \ + "${PKG_DIR}/usr/local/nginx/client_body_temp" \ + "${PKG_DIR}/usr/local/nginx/proxy_temp" \ + "${PKG_DIR}/usr/local/nginx/fastcgi_temp" \ + "${PKG_DIR}/usr/local/nginx/uwsgi_temp" \ + "${PKG_DIR}/usr/local/nginx/scgi_temp" - # Bundle every shared library nginx links against. This makes the - # package self-contained: users don't need our exact build-host - # versions of libssl, libluajit, libmodsecurity, etc. The grep - # filters out the vDSO and the dynamic linker (which never appear - # as `=> /...`). - for lib in $(ldd /usr/sbin/nginx | grep '=> /' | awk '{print $3}'); do - sudo cp "$lib" "${PKG_DIR}/usr/lib/" || true - done + cp /usr/sbin/nginx "${PKG_DIR}/usr/sbin/" + cp -R /nginx/* "${PKG_DIR}/nginx/" || true + cp /etc/systemd/system/nginx.service "${PKG_DIR}/etc/systemd/system/" + cp -R /hostdata/default "${PKG_DIR}/hostdata/" || true + cp -R /usr/nginx_lua "${PKG_DIR}/usr/" || true - # ---- DEBIAN/control -------------------------------------------------- - # Minimum metadata dpkg requires. The .deb bundles every shared library - # nginx links against (see the ldd loop above), so the only Depends we - # declare is libjemalloc2 — the systemd unit LD_PRELOADs it for the - # nginx workers; without it, the unit would fail to start. - sudo mkdir -p "${DEB_DIR}" - sudo tee "${DEB_DIR}/control" >/dev/null < - Description: Nginx L7 DDoS Protection (The-World-Is-Yours), built by RAWeb CI. - EOF + # Bundle every shared library nginx links against. ldd resolves + # against THIS containers libraries (not the runner host) so the + # .deb gets the correct per-distro libs. + for lib in $(ldd /usr/sbin/nginx | grep "=> /" | awk "{print \$3}"); do + cp "$lib" "${PKG_DIR}/usr/lib/" || true + done - # ---- DEBIAN/postinst ------------------------------------------------- - # Runs after dpkg unpacks the files. Designed to be safe to re-run: - # `apt install --reinstall twiy` and `apt upgrade twiy` both invoke - # this script and must not fail. - # - # Every step that may legitimately fail on a re-run (user already - # exists, service already enabled, host has no systemd, etc.) ends - # in `|| true`, and we `exit 0` explicitly so a flaky systemctl - # never aborts a dpkg transaction. - sudo tee "${DEB_DIR}/postinst" >/dev/null <<'EOF' - #!/bin/bash - # Idempotent: safe on first install, upgrade, and reinstall. + # ---- DEBIAN/control -------------------------------------------- + mkdir -p "${DEB_DIR}" + cat > "${DEB_DIR}/control" < + Description: Nginx L7 DDoS Protection (The-World-Is-Yours), built by RAWeb CI for ${TARGET}. + EOF - # System user nginx workers run as. -r = system account (no aging, - # UID below SYS_UID_MAX), no shell, home set to nginx's prefix. - useradd -r -d /usr/local/nginx -s /bin/false nginx 2>/dev/null || true + # ---- DEBIAN/postinst ------------------------------------------- + cat > "${DEB_DIR}/postinst" <<"EOFPOSTINST" + #!/bin/bash + # Idempotent: safe on first install, upgrade, and reinstall. + useradd -r -d /usr/local/nginx -s /bin/false nginx 2>/dev/null || true + install -d -o nginx -g nginx -m 0755 \ + /usr/local/nginx \ + /usr/local/nginx/client_body_temp \ + /usr/local/nginx/proxy_temp \ + /usr/local/nginx/fastcgi_temp \ + /usr/local/nginx/uwsgi_temp \ + /usr/local/nginx/scgi_temp \ + /var/log/nginx + chown -R nginx:nginx /var/log/nginx /nginx /usr/local/nginx 2>/dev/null || true + systemctl daemon-reload 2>/dev/null || true + systemctl enable nginx.service 2>/dev/null || true + systemctl restart nginx.service 2>/dev/null || true + exit 0 + EOFPOSTINST + chmod 755 "${DEB_DIR}/postinst" - # nginx was compiled without --http-*-temp-path, so it defaults to - # / (/usr/local/nginx/client_body_temp etc.). The dirs - # already ship in the .deb, but `install -d` is the cleanest way to - # set owner/group/mode in one shot and is a no-op when the dir - # already exists with the right attributes. - install -d -o nginx -g nginx -m 0755 \ - /usr/local/nginx \ - /usr/local/nginx/client_body_temp \ - /usr/local/nginx/proxy_temp \ - /usr/local/nginx/fastcgi_temp \ - /usr/local/nginx/uwsgi_temp \ - /usr/local/nginx/scgi_temp \ - /var/log/nginx + dpkg-deb --build "${PKG_DIR}" + cp "${PKG_DIR}.deb" /repo/dist/ - # Recursive chown picks up any user-supplied configs already under - # /nginx (vhosts, certs) so reloads don't trip on permissions. - chown -R nginx:nginx /var/log/nginx /nginx /usr/local/nginx 2>/dev/null || true + # Hand ownership back to the runner UID so the host job can read. + chown $(stat -c "%u:%g" /repo) /repo/dist/$(basename "${PKG_DIR}.deb") - # Refresh systemd's view of unit files we just dropped, then bring - # the service up. `restart` (rather than `start`) handles the case - # where a previous broken install left the unit failed. - systemctl daemon-reload 2>/dev/null || true - systemctl enable nginx.service 2>/dev/null || true - systemctl restart nginx.service 2>/dev/null || true - exit 0 - EOF - sudo chmod 755 "${DEB_DIR}/postinst" - - # Build the .deb and hand ownership back to the runner user so the - # next step can read it without sudo. - sudo dpkg-deb --build "${PKG_DIR}" - DEB_FILE="${PKG_DIR}.deb" - sudo chown "$(id -u):$(id -g)" "${DEB_FILE}" + # Stash version for the publish step. + echo "${PKG_NAME}_${VERSION}_${ARCH}.deb" > /repo/dist/${TARGET}.name + echo "${VERSION}" > /repo/dist/${TARGET}.version + echo "${PKG_NAME}" > /repo/dist/${TARGET}.pkg + ' + # Surface the artifact paths for the next step. + DEB_FILE="$PWD/dist/$(cat dist/${TARGET}.name)" + PKG_NAME="$(cat dist/${TARGET}.pkg)" + VERSION="$(cat dist/${TARGET}.version)" { echo "deb_file=${DEB_FILE}" echo "version=${VERSION}" @@ -185,73 +168,44 @@ jobs: sha256sum "${DEB_FILE}" # ───────────────────────────────────────────────────────────────────────── - # Publish the built .deb to a Sonatype Nexus apt-hosted repository. - # - # Threat model for this step (the workflow file is public): - # * Credentials come exclusively from repository secrets, never source. - # * Credentials must never appear in argv (visible via /proc//cmdline - # to any local user) or in the runner's persistent filesystem. - # * If the job is cancelled or killed, secrets must still be wiped. - # - # To run this in your own fork, set four repository secrets: - # NEXUS_USER — Nexus account with write access to the apt repo - # NEXUS_PASS — its password (or token) - # NEXUS_URL — base URL, e.g. https://apt.example.com - # NEXUS_REPO — the apt-hosted repository name in Nexus + # Publish to Nexus (runs on the runner host, not in the build container). + # Same security posture as the previous workflow: + # * tmpfs scratch dir for credentials + # * trap covers EXIT INT TERM HUP + # * netrc auth (no -u user:pass on cmdline → no /proc leak) + # * NEXUS_HOST derived from NEXUS_URL so forks don't have to edit YAML + # The matrix-driven secret indirection picks the right per-distro repo. # ───────────────────────────────────────────────────────────────────────── - - name: Publish to Nexus + - name: Publish to Nexus (${{ matrix.target }}) env: NEXUS_USER: ${{ secrets.NEXUS_USER }} NEXUS_PASS: ${{ secrets.NEXUS_PASS }} NEXUS_URL: ${{ secrets.NEXUS_URL }} - NEXUS_REPO: ${{ secrets.NEXUS_REPO }} + NEXUS_REPO: ${{ secrets[matrix.nexus_repo_secret] }} DEB_FILE: ${{ steps.pkg.outputs.deb_file }} PKG_NAME: ${{ steps.pkg.outputs.pkg_name }} + TARGET: ${{ matrix.target }} run: | set -euo pipefail - umask 077 # any file we create is rw for us only + umask 077 - # ---- Secret-handling scratch dir ------------------------------------ - # /dev/shm is tmpfs (RAM-backed). Even if the runner's disk is later - # imaged or recovered, secrets written here never touch persistent - # storage. Fall back to /tmp on minimal images that lack /dev/shm. SECDIR="$(mktemp -d -p /dev/shm twiy-XXXXXXXX 2>/dev/null \ || mktemp -d -t twiy-XXXXXXXX)" chmod 700 "$SECDIR" - - # Trap covers normal exit, errors (set -e), and the common cancellation - # signals Gitea / GitHub send when a job is cancelled or times out. - # `shred -uz` overwrites then unlinks; on tmpfs the overwrite is mostly - # symbolic, but it's free defence-in-depth in case /dev/shm wasn't - # available and we fell back to a disk-backed /tmp. cleanup() { find "$SECDIR" -type f -exec shred -uz {} + 2>/dev/null || true rm -rf "$SECDIR" } trap cleanup EXIT INT TERM HUP - # ---- Build the netrc ------------------------------------------------- - # Why netrc and not `curl -u user:pass`: - # - `-u` puts the password in argv; any local user can read it from - # /proc//cmdline while the curl is in flight. - # - netrc is a 0600 file curl reads itself; the password never - # appears on a command line. - # Why `printf` (a bash builtin): builtins don't fork an external - # process, so the password is never an argv to any executable. - # The host string in netrc must match the URL host exactly, so we - # derive it from $NEXUS_URL rather than hardcoding it — this lets - # forks reuse the workflow without editing it. NEXUS_HOST="$(printf '%s' "$NEXUS_URL" | awk -F/ '{print $3}')" printf 'machine %s login %s password %s\n' \ "$NEXUS_HOST" "$NEXUS_USER" "$NEXUS_PASS" > "$SECDIR/netrc" - # Drop the in-memory copies now that the file is the source of truth. unset NEXUS_USER NEXUS_PASS - # ---- Replace any prior version of this package ----------------------- - # Nexus's apt-hosted format keeps every uploaded .deb forever unless we - # explicitly delete the old component. Without this, the repo grows - # unboundedly and `apt` may pick a stale version. Best-effort: a - # missing prior component is not an error. + # Replace the prior version of this same package in this same repo, + # if any. Best-effort: missing prior is not an error. (apt-hosted + # repos in Nexus retain every upload otherwise.) OLD_ID="$(curl -fsS --netrc-file "$SECDIR/netrc" \ "$NEXUS_URL/service/rest/v1/components?repository=$NEXUS_REPO" \ | PKG_NAME="$PKG_NAME" python3 -c ' @@ -265,25 +219,11 @@ jobs: "$NEXUS_URL/service/rest/v1/components/$OLD_ID" -o /dev/null fi - # ---- Upload the new .deb -------------------------------------------- - # Body goes to a file inside SECDIR so the trap shreds it too — Nexus - # error responses sometimes echo request metadata we'd rather not - # leave on disk. HTTP="$(curl -sS --netrc-file "$SECDIR/netrc" \ -o "$SECDIR/upload.body" -w '%{http_code}' \ -X POST -F "apt.asset=@$DEB_FILE" \ "$NEXUS_URL/service/rest/v1/components?repository=$NEXUS_REPO")" case "$HTTP" in - 201|204) echo "Uploaded $(basename "$DEB_FILE") to $NEXUS_URL/repository/$NEXUS_REPO/" ;; - *) echo "Upload failed (HTTP $HTTP)"; head -c 400 "$SECDIR/upload.body"; exit 1 ;; + 201|204) echo "[$TARGET] uploaded $(basename "$DEB_FILE") to $NEXUS_URL/repository/$NEXUS_REPO/" ;; + *) echo "[$TARGET] upload failed (HTTP $HTTP)"; head -c 400 "$SECDIR/upload.body"; exit 1 ;; esac - - # ---- Why we don't sign each .deb ourselves --------------------------- - # apt's trust chain on the client is: - # Release.gpg → Packages (verified by SHA256 in Release) - # → the .deb (verified by SHA256 in Packages) - # Signing the Release file is enough; per-.deb signatures are not - # consulted by apt during install. Nexus signs Release on every - # upload using a key bound at repo-creation time, and that private - # key never leaves the Nexus host — so we deliberately keep all - # signing material off the CI runner. diff --git a/README.md b/README.md index 8e8b008..d749316 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ ![Simple](https://c.tenor.com/uYqsM9uIyuYAAAAC/simple-easy.gif) -- [x] Debian 13 (trixie) supported +- [x] Debian 13 (trixie) and Ubuntu 26.04 LTS (raccoon) supported - [x] nginx 1.30.0 - [x] HTTP/3 (QUIC) via AWS-LC - [x] ModSecurity v3 (libmodsecurity) @@ -11,38 +11,55 @@ - [x] Cookie-based challenge - [x] [Versions List](https://git.julio.al/theraw/The-World-Is-Yours/src/branch/master/version) -## Easy install +## Easy install + +### Debian 13 (trixie) ```bash - sudo install -d /etc/apt/keyrings - sudo curl -fsSL https://apt.julio.al/repository/public/keys/raweb.asc \ - -o /etc/apt/keyrings/raweb.asc +sudo install -d /etc/apt/keyrings +sudo curl -fsSL https://apt.julio.al/repository/public/keys/raweb.asc -o /etc/apt/keyrings/raweb.asc +echo "deb [signed-by=/etc/apt/keyrings/raweb.asc] https://apt.julio.al/repository/raweb-trixie trixie main" | sudo tee /etc/apt/sources.list.d/raweb.list +sudo apt update && sudo apt install twiy +``` - echo "deb [signed-by=/etc/apt/keyrings/raweb.asc] https://apt.julio.al/repository/raweb trixie main" \ - | sudo tee /etc/apt/sources.list.d/raweb.list - - sudo apt update && sudo apt install twiy +### Ubuntu 26.04 LTS (raccoon) +```bash +sudo install -d /etc/apt/keyrings +sudo curl -fsSL https://apt.julio.al/repository/public/keys/raweb.asc -o /etc/apt/keyrings/raweb.asc +echo "deb [signed-by=/etc/apt/keyrings/raweb.asc] https://apt.julio.al/repository/raweb-raccoon raccoon main" | sudo tee /etc/apt/sources.list.d/raweb.list +sudo apt update && sudo apt install twiy ``` ## Compile from source -```bash -apt-get -y install git && cd /root/ && git clone https://github.com/theraw/The-World-Is-Yours.git && cd The-World-Is-Yours/ -bash build/run.sh new -bash build/run.sh build -bash build/run.sh postfix +Pick the script that matches your OS — they're separate so apt package +divergences (e.g. the t64 ABI transition on Ubuntu 24.04+) stay isolated. + +```bash +apt-get -y install git && cd /root/ && git clone https://git.julio.al/theraw/The-World-Is-Yours.git && cd The-World-Is-Yours/ + +# Debian 13 +bash build/trixie.sh new +bash build/trixie.sh build +bash build/trixie.sh postfix + +# Ubuntu 26.04 LTS +bash build/raccoon.sh new +bash build/raccoon.sh build +bash build/raccoon.sh postfix ``` -If you want to try with a custom nginx version then, open `version` file and change versions then run -```bash -bash build/run.sh new -bash build/run.sh build -``` +To try a different upstream version, edit `version` and re-run `new` then `build`. + ## CLI Info ``` -bash build/run.sh new => Download all modules + nginx that are missing from /opt/. (If you make version changes to 'version' file then simply rerun this to download again) -bash build/run.sh build => This is going to simply compile nginx nothing else. (You can run this as many times as you need, its not going to replace configs) -bash build/run.sh postfix => This will redownload /nginx/nginx.conf everytime you run it. (Suggested to run only once when you install nginx via my repo for first time) +bash build/.sh new => Download all modules + nginx that are missing from /opt/. + (Re-run after changing the `version` file to fetch new versions.) +bash build/.sh build => Compile nginx. Re-runnable; will not touch your configs. +bash build/.sh postfix => Drop the default /nginx/nginx.conf, vhost, and systemd unit + into place. Run once on first install; re-running overwrites + /nginx/nginx.conf. ``` +where `` is `trixie` or `raccoon`. ## Nginx info. @@ -69,16 +86,11 @@ nginx -s reload ## Performance - ### vs. vanilla nginx (same version, default config) | Area | Twiy | Vanilla nginx | Why | |---|---|---|---| | TLS handshake throughput | **+5–15%** | baseline | AWS-LC's tuned AES/ChaCha asm vs OpenSSL | -| Static file throughput | **2–5×** | baseline | `open_file_cache` (off by default in vanilla) | -| TLS resumed handshakes | **~10× CPU saving** | baseline | 200 MB shared session cache vs none | -| Per-handshake latency (cold) | **−50–200 ms p95** | baseline | OCSP stapling on by default | -| Compressed-text bandwidth | **−60 to −80%** | unchanged | brotli + gzip enabled in `http {}` | | WAF, Lua, HTTP/3 | included | not included | needs custom build | # Support options. diff --git a/build/raccoon.sh b/build/raccoon.sh new file mode 100644 index 0000000..d8eb379 --- /dev/null +++ b/build/raccoon.sh @@ -0,0 +1,395 @@ +. ./version +set -e +function reqs() { + apt-get update -y; apt-get upgrade -y; apt-get dist-upgrade -y; apt-get autoremove -y + DEBIAN_FRONTEND=noninteractive TZ=Etc/UTC apt-get -y install tzdata dialog + # apt-get purge nftables firewalld ufw -y; apt-get autoremove -y + apt-get -y install wget zip unzip build-essential libssl-dev curl nano git + # apt-get -y install iptables ipset + apt-get install libtool pkg-config make cmake automake autoconf golang-go ninja-build -y + apt-get install libyajl-dev ssdeep zlib1g-dev libxslt1-dev libgd-dev libgeoip-dev liblmdb-dev libfuzzy-dev libmaxminddb-dev liblua5.1-dev libcurl4-openssl-dev libxml2-dev mercurial libpcre2-dev libc-ares-dev libre2-dev libzstd-dev libjemalloc2 -y + mkdir -p $LUA_SCRIPTS +} +function clean_install() { + mkdir -p /opt/mod + + # Nginx + if [ ! -d /opt/nginx-${NGINX} ]; then + cd /opt/ && wget https://nginx.org/download/nginx-${NGINX}.tar.gz + tar xf nginx-${NGINX}.tar.gz && rm -Rf nginx-${NGINX}.tar.gz + fi + + # START OF SYSTEM REQUIRED LIBS + # ============================================================================================================ + # AWS-LC — TLS+QUIC backend. Replaces quictls/openssl. Built standalone + # (cmake+ninja) and installed to /usr/local/aws-lc/. nginx 1.29.2+ links + # against it via -I/-L; we no longer pass --with-openssl=PATH because we + # don't want nginx's configure to rebuild OpenSSL itself. + if [ ! -d /opt/mod/aws-lc-${SYSTEM_AWSLC} ]; then + cd /opt/mod && wget https://github.com/aws/aws-lc/archive/refs/tags/v${SYSTEM_AWSLC}.tar.gz + cd /opt/mod && tar xf v${SYSTEM_AWSLC}.tar.gz; rm -Rf v${SYSTEM_AWSLC}.tar.gz + fi + if [ ! -f /usr/local/aws-lc/lib/libssl.so ]; then + cd /opt/mod/aws-lc-${SYSTEM_AWSLC} && \ + cmake -GNinja -B build \ + -DCMAKE_INSTALL_PREFIX=/usr/local/aws-lc \ + -DBUILD_SHARED_LIBS=1 \ + -DCMAKE_BUILD_TYPE=Release && \ + cmake --build build -j`nproc` && \ + cmake --install build && \ + ldconfig + fi + + # ZLIB + if [ ! -d /opt/mod/zlib ]; then + cd /opt/mod && wget http://zlib.net/current/zlib.tar.gz + cd /opt/mod && tar xf zlib.tar.gz; rm -Rf zlib.tar.gz; mv zlib-* zlib + fi + + # SYSTEM_LUAJIT + if [ ! -d /opt/mod/luajit2-${SYSTEM_LUAJIT} ]; then + cd /opt/mod && wget https://github.com/openresty/luajit2/archive/refs/tags/v${SYSTEM_LUAJIT}.tar.gz + cd /opt/mod && tar xf v${SYSTEM_LUAJIT}.tar.gz && rm -Rf v${SYSTEM_LUAJIT}.tar.gz + if [ ! -d /usr/local/LuaJIT/include/luajit-2.1 ]; then + cd /opt/mod/luajit2-${SYSTEM_LUAJIT}/ && make clean && make install PREFIX=/usr/local/LuaJIT && ldconfig +# apt-get -y install liblua5.1-0-dev; apt-get -y install luarocks; luarocks install lua-resty-core + fi + fi + + # SYSTEM_MODSECURITY (v3 — libmodsecurity, what ModSecurity-nginx connector needs) + if [ ! -d /opt/mod/modsecurity-v${SYSTEM_MODSECURITY} ]; then + cd /opt/mod && wget https://github.com/SpiderLabs/ModSecurity/releases/download/v${SYSTEM_MODSECURITY}/modsecurity-v${SYSTEM_MODSECURITY}.tar.gz + cd /opt/mod && tar xf modsecurity-v${SYSTEM_MODSECURITY}.tar.gz; rm -Rf modsecurity-v${SYSTEM_MODSECURITY}.tar.gz + fi + if [ ! -f /usr/local/modsecurity/lib/libmodsecurity.so ]; then + cd /opt/mod/modsecurity-v${SYSTEM_MODSECURITY} && ./build.sh && ./configure --without-pcre --with-pcre2 && make -j`nproc` && make install + fi + + # SYSTEM_PCRE + # Use the official release tarball (bundles the sljit submodule needed for + # JIT). The /archive/refs/tags/ tarball from GitHub is a raw source snapshot + # that omits submodules and breaks `--with-pcre-jit`. + if [ ! -d /opt/mod/pcre2-${SYSTEM_PCRE} ]; then + cd /opt/mod && wget https://github.com/PCRE2Project/pcre2/releases/download/pcre2-${SYSTEM_PCRE}/pcre2-${SYSTEM_PCRE}.tar.gz + cd /opt/mod && tar xf pcre2-${SYSTEM_PCRE}.tar.gz; rm -Rf pcre2-${SYSTEM_PCRE}.tar.gz + fi + + # LibInjection + if [ ! -d /opt/mod/libinjection ]; then + cd /opt/mod && git clone https://github.com/libinjection/libinjection.git + cd /opt/mod/libinjection && ./autogen.sh && ./configure && make -j`nproc` && make install + fi + # END OF SYSTEM REQUIRED LIBS + # ============================================================================================================ + + + # START OF NGINX MODULES + # ============================================================================================================ + # NGX_MOD_LUA + if [ ! -d /opt/mod/lua-nginx-module-${NGX_MOD_LUA} ]; then + cd /opt/mod/; wget https://github.com/openresty/lua-nginx-module/archive/refs/tags/v${NGX_MOD_LUA}.tar.gz + cd /opt/mod/; tar xf v${NGX_MOD_LUA}.tar.gz; rm -Rf v${NGX_MOD_LUA}.tar.gz + sed -i 's/cookies/cookie/g' /opt/mod/lua-nginx-module-${NGX_MOD_LUA}/src/ngx_http_lua_headers_in.c + # AWS-LC compatibility: lua-nginx-module already has guards around APIs + # missing from BoringSSL (SSL_get1_supported_ciphers, SSL_export_keying_ + # material_early, etc.). AWS-LC has the same API limitations but defines + # OPENSSL_IS_AWSLC instead of OPENSSL_IS_BORINGSSL, so the guards never + # fire. Broaden every form (#if, #ifdef, #ifndef, #elif) to recognise + # both macros. Order matters: the bare `defined()` substitution runs + # first so the later #ifdef/#ifndef substitutions don't double-rewrite. + sed -i \ + -e 's@defined(OPENSSL_IS_BORINGSSL)@(defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC))@g' \ + -e 's@#ifdef OPENSSL_IS_BORINGSSL@#if (defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC))@g' \ + -e 's@#ifndef OPENSSL_IS_BORINGSSL@#if !(defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC))@g' \ + /opt/mod/lua-nginx-module-${NGX_MOD_LUA}/src/*.c + fi + + # NGX_LUA_CORE — must stay in lockstep with NGX_MOD_LUA. lua-resty-core + # does a strict-equality check on ngx.config.ngx_lua_version at startup, + # so an upstream bump on master silently breaks the build. Pinning via + # the tagged tarball (dir name embeds the version) means changing + # LUA_SCRIPTS_RESTYCORE in `version` invalidates the cache automatically. + if [ ! -d /opt/mod/lua-resty-core-${LUA_SCRIPTS_RESTYCORE} ]; then + cd /opt/mod/; wget https://github.com/openresty/lua-resty-core/archive/refs/tags/v${LUA_SCRIPTS_RESTYCORE}.tar.gz + cd /opt/mod/; tar xf v${LUA_SCRIPTS_RESTYCORE}.tar.gz; rm -Rf v${LUA_SCRIPTS_RESTYCORE}.tar.gz + cd /opt/mod/lua-resty-core-${LUA_SCRIPTS_RESTYCORE} && make install PREFIX=${LUA_SCRIPTS} + fi + + # NGX_LUA_LRUCACHE — same pattern, pinned to LUA_SCRIPTS_LRUCACHE. + if [ ! -d /opt/mod/lua-resty-lrucache-${LUA_SCRIPTS_LRUCACHE} ]; then + cd /opt/mod/; wget https://github.com/openresty/lua-resty-lrucache/archive/refs/tags/v${LUA_SCRIPTS_LRUCACHE}.tar.gz + cd /opt/mod/; tar xf v${LUA_SCRIPTS_LRUCACHE}.tar.gz; rm -Rf v${LUA_SCRIPTS_LRUCACHE}.tar.gz + cd /opt/mod/lua-resty-lrucache-${LUA_SCRIPTS_LRUCACHE} && make install PREFIX=${LUA_SCRIPTS} + fi + + # NGX_MOD_LUA_MYSQL + if [ ! -d /opt/mod/lua-resty-mysql-${NGX_MOD_LUA_MYSQL} ]; then + cd /opt/mod/; wget https://github.com/openresty/lua-resty-mysql/archive/refs/tags/v${NGX_MOD_LUA_MYSQL}.tar.gz + cd /opt/mod/; tar xf v${NGX_MOD_LUA_MYSQL}.tar.gz; rm -Rf v${NGX_MOD_LUA_MYSQL}.tar.gz + cd /opt/mod/lua-resty-mysql-${NGX_MOD_LUA_MYSQL} && make install PREFIX=${LUA_SCRIPTS} + fi + + # NGX_MOD_LUA_SRCACHE + if [ ! -d /opt/mod/srcache-nginx-module-${NGX_MOD_LUA_SRCACHE} ]; then + cd /opt/mod/; wget https://github.com/openresty/srcache-nginx-module/archive/refs/tags/v${NGX_MOD_LUA_SRCACHE}.tar.gz + cd /opt/mod/; tar xf v${NGX_MOD_LUA_SRCACHE}.tar.gz; rm -Rf v${NGX_MOD_LUA_SRCACHE}.tar.gz + fi + + # NGX_MOD_LUA_REDIS2 + if [ ! -d /opt/mod/redis2-nginx-module ]; then + cd /opt/mod/; git clone --recursive https://github.com/openresty/redis2-nginx-module.git + fi + + # NGX_MOD_LUA_LOCK 0.09 + if [ ! -d /opt/mod/lua-resty-lock-${NGX_MOD_LUA_LOCK} ]; then + cd /opt/mod/; wget https://github.com/openresty/lua-resty-lock/archive/refs/tags/v${NGX_MOD_LUA_LOCK}.tar.gz + cd /opt/mod/; tar xf v${NGX_MOD_LUA_LOCK}.tar.gz; rm -Rf v${NGX_MOD_LUA_LOCK}.tar.gz + cd /opt/mod/lua-resty-lock-${NGX_MOD_LUA_LOCK} && make install PREFIX=${LUA_SCRIPTS} + fi + + # NGX_MOD_LUA_CACHE + if [ ! -d /opt/mod/lua-resty-cache ]; then + cd /opt/mod/; git clone --branch feature-srcache --recursive https://github.com/lloydzhou/lua-resty-cache + cd /opt/mod/lua-resty-cache && make install PREFIX=${LUA_SCRIPTS} + fi + + # NGX_MOD_DEVELKIT + if [ ! -d /opt/mod/ngx_devel_kit-${NGX_MOD_DEVELKIT} ]; then + cd /opt/mod/; wget https://github.com/vision5/ngx_devel_kit/archive/refs/tags/v${NGX_MOD_DEVELKIT}.tar.gz + cd /opt/mod/; tar xf v${NGX_MOD_DEVELKIT}.tar.gz; rm -Rf v${NGX_MOD_DEVELKIT}.tar.gz + fi + + # NGX_MOD_GEOIP2 + if [ ! -d /opt/mod/ngx_http_geoip2_module-${NGX_MOD_GEOIP2} ]; then + cd /opt/mod/; wget https://github.com/leev/ngx_http_geoip2_module/archive/refs/tags/${NGX_MOD_GEOIP2}.tar.gz + cd /opt/mod/; tar xf ${NGX_MOD_GEOIP2}.tar.gz; rm -Rf ${NGX_MOD_GEOIP2}.tar.gz + fi + + # NGX_MOD_MODSECURITY + if [ ! -d /opt/mod/ModSecurity-nginx-${NGX_MOD_MODSECURITY} ]; then + cd /opt/mod/; wget https://github.com/SpiderLabs/ModSecurity-nginx/archive/refs/tags/v${NGX_MOD_MODSECURITY}.tar.gz + cd /opt/mod/; tar xf v${NGX_MOD_MODSECURITY}.tar.gz; rm -Rf v${NGX_MOD_MODSECURITY}.tar.gz + fi + + # NGX_MOD_HTTPFLV + if [ ! -d /opt/mod/nginx-http-flv-module-${NGX_MOD_HTTPFLV} ]; then + cd /opt/mod/; wget https://github.com/winshining/nginx-http-flv-module/archive/refs/tags/v${NGX_MOD_HTTPFLV}.tar.gz + cd /opt/mod/; tar xf v${NGX_MOD_HTTPFLV}.tar.gz; rm -Rf v${NGX_MOD_HTTPFLV}.tar.gz + fi + + # NGX_MOD_HEADERS_MORE + if [ ! -d /opt/mod/headers-more-nginx-module-${NGX_MOD_HEADERS_MORE} ]; then + cd /opt/mod/; wget https://github.com/openresty/headers-more-nginx-module/archive/refs/tags/v${NGX_MOD_HEADERS_MORE}.tar.gz + cd /opt/mod/; tar xf v${NGX_MOD_HEADERS_MORE}.tar.gz; rm -Rf v${NGX_MOD_HEADERS_MORE}.tar.gz + fi + + # NGX_MOD_SETMISC + if [ ! -d /opt/mod/set-misc-nginx-module-${NGX_MOD_SETMISC} ]; then + cd /opt/mod/; wget https://github.com/openresty/set-misc-nginx-module/archive/refs/tags/v${NGX_MOD_SETMISC}.tar.gz + cd /opt/mod/; tar xf v${NGX_MOD_SETMISC}.tar.gz; rm -Rf v${NGX_MOD_SETMISC}.tar.gz + fi + + # Testcookie + if [ ! -d /opt/mod/testcookie ]; then + cd /opt/mod/; git clone https://github.com/kyprizel/testcookie-nginx-module.git testcookie + fi + + # Brotli + if [ ! -d /opt/mod/ngx_brotli ]; then + cd /opt/mod/; git clone https://github.com/google/ngx_brotli.git ngx_brotli; cd /opt/mod/ngx_brotli && git submodule update --init + fi + + # Naxsi + if [ ! -d /opt/mod/naxsi ]; then + cd /opt/mod/; git clone --recurse-submodules https://github.com/wargio/naxsi.git naxsi + fi + + # NGX_MOD_ZSTD — Zstandard compression module from tokers. Pinned via + # NGX_MOD_ZSTD; tarball pattern (dir name embeds version → cache invalidates + # automatically when the pin moves). + if [ ! -d /opt/mod/zstd-nginx-module-${NGX_MOD_ZSTD} ]; then + cd /opt/mod/; wget https://github.com/tokers/zstd-nginx-module/archive/refs/tags/${NGX_MOD_ZSTD}.tar.gz + cd /opt/mod/; tar xf ${NGX_MOD_ZSTD}.tar.gz; rm -Rf ${NGX_MOD_ZSTD}.tar.gz + fi + + # END OF NGINX MODULES + # ============================================================================================================ +} + +test_nginx() { + cd /opt/nginx-${NGINX} && LUAJIT_LIB="/usr/local/LuaJIT/lib" LUAJIT_INC="/usr/local/LuaJIT/include/luajit-2.1/" CFLAGS=-fPIC CXXFLAGS=-fPIC ./configure --with-compat \ + --user=nginx \ + --group=nginx \ + --sbin-path=/usr/sbin/nginx \ + --conf-path=/nginx/nginx.conf \ + --modules-path=/nginx/modules \ + --pid-path=/var/run/nginx.pid \ + --lock-path=/var/run/nginx.lock \ + --error-log-path=/var/log/nginx/error.log \ + --http-log-path=/var/log/nginx/access.log \ + --with-pcre \ + --with-pcre-jit \ + --with-pcre=/opt/mod/pcre2-${SYSTEM_PCRE} \ + --with-zlib=/opt/mod/zlib \ + --with-threads \ + --with-file-aio \ + --with-http_ssl_module \ + --with-http_v2_module \ + --with-http_v3_module \ + --with-http_realip_module \ + --with-http_addition_module \ + --with-http_xslt_module \ + --with-http_image_filter_module \ + --with-http_geoip_module \ + --with-http_sub_module \ + --with-http_dav_module \ + --with-http_flv_module \ + --with-http_mp4_module \ + --with-http_gunzip_module \ + --with-http_gzip_static_module \ + --with-http_auth_request_module \ + --with-http_random_index_module \ + --with-http_secure_link_module \ + --with-http_slice_module \ + --with-http_stub_status_module \ + --with-mail \ + --with-mail_ssl_module \ + --with-stream \ + --with-stream_ssl_module \ + --with-stream_realip_module \ + --with-stream_geoip_module \ + --add-module=/opt/mod/ngx_devel_kit-${NGX_MOD_DEVELKIT} \ + --add-module=/opt/mod/set-misc-nginx-module-${NGX_MOD_SETMISC} \ + --add-module=/opt/mod/ngx_http_geoip2_module-${NGX_MOD_GEOIP2} \ + --add-module=/opt/mod/headers-more-nginx-module-${NGX_MOD_HEADERS_MORE} \ + --add-module=/opt/mod/lua-nginx-module-${NGX_MOD_LUA} \ + --add-module=/opt/mod/ModSecurity-nginx-${NGX_MOD_MODSECURITY} \ + --add-module=/opt/mod/naxsi/naxsi_src \ + --add-module=/opt/mod/nginx-http-flv-module-${NGX_MOD_HTTPFLV} \ + --add-module=/opt/mod/srcache-nginx-module-${NGX_MOD_LUA_SRCACHE} \ + --add-module=/opt/mod/redis2-nginx-module \ + --add-module=/opt/mod/ngx_brotli \ + --add-module=/opt/mod/zstd-nginx-module-${NGX_MOD_ZSTD} \ + --add-module=/opt/mod/testcookie \ + --with-cc-opt="-g -O2 -fstack-protector-strong -Wformat -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -fPIC -I/usr/local/aws-lc/include" \ + --with-ld-opt="-Wl,-rpath,/usr/local/LuaJIT/lib -Wl,-rpath,/usr/local/lib -Wl,-z,relro -Wl,-z,now -Wl,--as-needed -pie -L/opt/mod/pcre2-${SYSTEM_PCRE}/.libs -lpcre2-8 -L/usr/local/aws-lc/lib -lssl -lcrypto -Wl,-rpath,/usr/local/aws-lc/lib" + make clean +} +function build() { + cd /opt/nginx-${NGINX} && LUAJIT_LIB="/usr/local/LuaJIT/lib" LUAJIT_INC="/usr/local/LuaJIT/include/luajit-2.1/" CFLAGS=-fPIC CXXFLAGS=-fPIC ./configure --with-compat \ + --user=nginx \ + --group=nginx \ + --sbin-path=/usr/sbin/nginx \ + --conf-path=/nginx/nginx.conf \ + --modules-path=/nginx/modules \ + --pid-path=/var/run/nginx.pid \ + --lock-path=/var/run/nginx.lock \ + --error-log-path=/var/log/nginx/error.log \ + --http-log-path=/var/log/nginx/access.log \ + --with-pcre \ + --with-pcre-jit \ + --with-pcre=/opt/mod/pcre2-${SYSTEM_PCRE} \ + --with-zlib=/opt/mod/zlib \ + --with-threads \ + --with-file-aio \ + --with-http_ssl_module \ + --with-http_v2_module \ + --with-http_v3_module \ + --with-http_realip_module \ + --with-http_addition_module \ + --with-http_xslt_module \ + --with-http_image_filter_module \ + --with-http_geoip_module \ + --with-http_sub_module \ + --with-http_dav_module \ + --with-http_flv_module \ + --with-http_mp4_module \ + --with-http_gunzip_module \ + --with-http_gzip_static_module \ + --with-http_auth_request_module \ + --with-http_random_index_module \ + --with-http_secure_link_module \ + --with-http_slice_module \ + --with-http_stub_status_module \ + --with-mail \ + --with-mail_ssl_module \ + --with-stream \ + --with-stream_ssl_module \ + --with-stream_realip_module \ + --with-stream_geoip_module \ + --add-module=/opt/mod/ngx_devel_kit-${NGX_MOD_DEVELKIT} \ + --add-module=/opt/mod/set-misc-nginx-module-${NGX_MOD_SETMISC} \ + --add-module=/opt/mod/ngx_http_geoip2_module-${NGX_MOD_GEOIP2} \ + --add-module=/opt/mod/headers-more-nginx-module-${NGX_MOD_HEADERS_MORE} \ + --add-module=/opt/mod/ModSecurity-nginx-${NGX_MOD_MODSECURITY} \ + --add-module=/opt/mod/lua-nginx-module-${NGX_MOD_LUA} \ + --add-module=/opt/mod/naxsi/naxsi_src \ + --add-module=/opt/mod/nginx-http-flv-module-${NGX_MOD_HTTPFLV} \ + --add-module=/opt/mod/srcache-nginx-module-${NGX_MOD_LUA_SRCACHE} \ + --add-module=/opt/mod/redis2-nginx-module \ + --add-module=/opt/mod/ngx_brotli \ + --add-module=/opt/mod/zstd-nginx-module-${NGX_MOD_ZSTD} \ + --add-module=/opt/mod/testcookie \ + --with-cc-opt="-g -O2 -fstack-protector-strong -Wformat -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -fPIC -I/usr/local/aws-lc/include" \ + --with-ld-opt="-Wl,-rpath,/usr/local/LuaJIT/lib -Wl,-rpath,/usr/local/lib -Wl,-z,relro -Wl,-z,now -Wl,--as-needed -pie -L/opt/mod/pcre2-${SYSTEM_PCRE}/.libs -lpcre2-8 -L/usr/local/aws-lc/lib -lssl -lcrypto -Wl,-rpath,/usr/local/aws-lc/lib" + # NOTE: kept as separate statements (not `make && make install && make clean`) + # so `set -e` actually fires on a make failure. The && chain hides left-side + # failures from set -e, which previously let half-built nginx ship. + cd /opt/nginx-${NGINX} && make -j`nproc` + cd /opt/nginx-${NGINX} && make install + cd /opt/nginx-${NGINX} && make clean + unset NGINX +} +function post_build() { + useradd nginx; unset NGINX; rm -rf /nginx/*.default; + mkdir -p /nginx/live + mkdir -p /nginx/conf.d + mkdir -p /nginx/config + mkdir -p /var/log/nginx + mkdir -p /nginx/modsec; curl -s https://raw.githubusercontent.com/nbs-system/naxsi/master/naxsi_config/naxsi_core.rules > /nginx/modsec/naxi.core + curl -s https://raw.githubusercontent.com/theraw/The-World-Is-Yours/master/static/modsec/l7.conf > /nginx/modsec/l7.conf + curl -s https://raw.githubusercontent.com/SpiderLabs/ModSecurity/v3/master/modsecurity.conf-recommended > /nginx/modsec/modsecurity.conf + curl -s https://raw.githubusercontent.com/theraw/The-World-Is-Yours/master/static/modsec/tester.conf > /nginx/modsec/tester.conf + curl -s https://raw.githubusercontent.com/theraw/The-World-Is-Yours/master/static/modsec/unicode.mapping > /nginx/modsec/unicode.mapping + curl -s https://raw.githubusercontent.com/theraw/The-World-Is-Yours/master/static/nginx/nginx.conf > /nginx/nginx.conf + curl -s https://raw.githubusercontent.com/theraw/The-World-Is-Yours/master/static/nginx/live/default > /nginx/live/default + mkdir -p /hostdata/default/public_html/ && curl -s https://raw.githubusercontent.com/theraw/The-World-Is-Yours/master/static/index.html > /hostdata/default/public_html/index.html + mkdir -p /hostdata/default/public_html/cdn/modsec && curl -s https://raw.githubusercontent.com/theraw/The-World-Is-Yours/master/static/modsec/aes.min.js > /hostdata/default/public_html/cdn/modsec/aes.min.js + if [ -f "/run/.containerenv" ] || [ -f "/.dockerenv" ] || [ -f "/home/runner/.dockerenv" ]; then + echo "Skipping systemctl commands on GitHub runner" + mkdir -p /etc/systemd/system/ + curl -s https://raw.githubusercontent.com/theraw/The-World-Is-Yours/master/static/Raccoon/nginx.service > /etc/systemd/system/nginx.service + else + curl -s https://raw.githubusercontent.com/theraw/The-World-Is-Yours/master/static/Raccoon/nginx.service > /etc/systemd/system/nginx.service + systemctl daemon-reload + systemctl start nginx.service + systemctl enable nginx.service + fi +} + +# Handling command-line arguments +case "$1" in + new) + reqs + clean_install + ;; + test) + test_nginx + ;; + build) + build + ;; + postfix) + post_build + ;; + *) + echo "Invalid option: $1" + echo "Usage: $0 {new|test|build|postfix}" + echo "" + echo " new: will download all modules & nginx (if you change a version from file, simply rerun this to download that)" + echo " test: Test nginx configuration" + echo " build: Build nginx, or Rebuild (mods/configs will not be redownloaded this will only build)" + echo " postfix: After first installation, run this to download nginx configs (it will replace nginx.conf if there already is one)" + exit 1 + ;; +esac diff --git a/build/run.sh b/build/trixie.sh similarity index 99% rename from build/run.sh rename to build/trixie.sh index 27e637f..3415a90 100644 --- a/build/run.sh +++ b/build/trixie.sh @@ -358,9 +358,9 @@ function post_build() { if [ -f "/run/.containerenv" ] || [ -f "/.dockerenv" ] || [ -f "/home/runner/.dockerenv" ]; then echo "Skipping systemctl commands on GitHub runner" mkdir -p /etc/systemd/system/ - curl -s https://raw.githubusercontent.com/theraw/The-World-Is-Yours/master/static/Jammy/nginx.service > /etc/systemd/system/nginx.service + curl -s https://raw.githubusercontent.com/theraw/The-World-Is-Yours/master/static/Trixie/nginx.service > /etc/systemd/system/nginx.service else - curl -s https://raw.githubusercontent.com/theraw/The-World-Is-Yours/master/static/Jammy/nginx.service > /etc/systemd/system/nginx.service + curl -s https://raw.githubusercontent.com/theraw/The-World-Is-Yours/master/static/Trixie/nginx.service > /etc/systemd/system/nginx.service systemctl daemon-reload systemctl start nginx.service systemctl enable nginx.service diff --git a/static/Focal/nginx.service b/static/Focal/nginx.service deleted file mode 100644 index 4e90807..0000000 --- a/static/Focal/nginx.service +++ /dev/null @@ -1,16 +0,0 @@ -[Nginx] -Description=A high performance web server and a reverse proxy server -After=syslog.target network-online.target remote-fs.target nss-lookup.target -Wants=network-online.target - -[Service] -Type=forking -PIDFile=/var/run/nginx.pid -ExecStartPre=/usr/sbin/nginx -t -ExecStart=/usr/sbin/nginx -ExecReload=/usr/sbin/nginx -s reload -ExecStop=/bin/kill -s QUIT $MAINPID -PrivateTmp=true - -[Install] -WantedBy=multi-user.target diff --git a/static/Jammy/nginx.service b/static/Jammy/nginx.service deleted file mode 100644 index d9b1794..0000000 --- a/static/Jammy/nginx.service +++ /dev/null @@ -1,21 +0,0 @@ -[Unit] -Description=A high performance web server and a reverse proxy server -After=syslog.target network-online.target remote-fs.target nss-lookup.target -Wants=network-online.target - -[Service] -Type=forking -PIDFile=/run/nginx.pid -# jemalloc replaces glibc malloc — better fragmentation/perf under nginx's -# alloc/free churn at scale. Package depends on libjemalloc2 so the .so is -# guaranteed present. Removing this line falls back to glibc malloc cleanly. -Environment=LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libjemalloc.so.2 -ExecStartPre=/usr/bin/install -d -o nginx -g nginx -m 0755 /usr/local/nginx /usr/local/nginx/client_body_temp /usr/local/nginx/proxy_temp /usr/local/nginx/fastcgi_temp /usr/local/nginx/uwsgi_temp /usr/local/nginx/scgi_temp /var/log/nginx -ExecStartPre=/usr/sbin/nginx -t -ExecStart=/usr/sbin/nginx -ExecReload=/usr/sbin/nginx -s reload -ExecStop=/bin/kill -s QUIT $MAINPID -PrivateTmp=true - -[Install] -WantedBy=multi-user.target diff --git a/static/Raccoon/nginx.service b/static/Raccoon/nginx.service new file mode 100644 index 0000000..7708182 --- /dev/null +++ b/static/Raccoon/nginx.service @@ -0,0 +1,57 @@ +[Unit] +Description=A high performance web server and a reverse proxy server (twiy) +After=syslog.target network-online.target remote-fs.target nss-lookup.target +Wants=network-online.target + +[Service] +Type=forking +PIDFile=/run/nginx.pid + +# jemalloc replaces glibc malloc — better fragmentation/perf under nginx's +# alloc/free churn at scale. Package depends on libjemalloc2 so the .so is +# guaranteed present. Removing this line falls back to glibc malloc cleanly. +Environment=LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libjemalloc.so.2 + +# Self-heal: nginx was compiled without --http-*-temp-path so it expects +# these dirs under /usr/local/nginx; install -d is idempotent and fixes any +# missing/wrong ownership on every restart. +ExecStartPre=/usr/bin/install -d -o nginx -g nginx -m 0755 /usr/local/nginx /usr/local/nginx/client_body_temp /usr/local/nginx/proxy_temp /usr/local/nginx/fastcgi_temp /usr/local/nginx/uwsgi_temp /usr/local/nginx/scgi_temp /var/log/nginx +ExecStartPre=/usr/sbin/nginx -t +ExecStart=/usr/sbin/nginx +ExecReload=/usr/sbin/nginx -s reload +ExecStop=/bin/kill -s QUIT $MAINPID + +# ---- systemd hardening (systemd 257+ on Debian 13 / Ubuntu 26.04) ---- +# Each line shrinks the worker's blast radius without affecting throughput. +NoNewPrivileges=true +ProtectSystem=strict +ProtectHome=true +ProtectKernelTunables=true +ProtectKernelModules=true +ProtectKernelLogs=true +ProtectControlGroups=true +ProtectClock=true +ProtectHostname=true +PrivateDevices=true +PrivateTmp=true +RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6 AF_NETLINK +RestrictNamespaces=true +RestrictRealtime=true +RestrictSUIDSGID=true +LockPersonality=true +SystemCallArchitectures=native +SystemCallFilter=@system-service +SystemCallFilter=~@privileged @resources @mount @debug @cpu-emulation @obsolete @raw-io @reboot @swap + +# Paths nginx legitimately writes to. ProtectSystem=strict makes everything +# else read-only; these carve out the exceptions. +ReadWritePaths=/var/log/nginx /usr/local/nginx /run /nginx /hostdata + +# NOTE on MemoryDenyWriteExecute: LuaJIT does runtime JIT compilation and +# therefore needs writable+executable pages — enabling MDWE breaks Lua. Left +# off intentionally. + +LimitNOFILE=65535 + +[Install] +WantedBy=multi-user.target diff --git a/static/Trixie/nginx.service b/static/Trixie/nginx.service new file mode 100644 index 0000000..7708182 --- /dev/null +++ b/static/Trixie/nginx.service @@ -0,0 +1,57 @@ +[Unit] +Description=A high performance web server and a reverse proxy server (twiy) +After=syslog.target network-online.target remote-fs.target nss-lookup.target +Wants=network-online.target + +[Service] +Type=forking +PIDFile=/run/nginx.pid + +# jemalloc replaces glibc malloc — better fragmentation/perf under nginx's +# alloc/free churn at scale. Package depends on libjemalloc2 so the .so is +# guaranteed present. Removing this line falls back to glibc malloc cleanly. +Environment=LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libjemalloc.so.2 + +# Self-heal: nginx was compiled without --http-*-temp-path so it expects +# these dirs under /usr/local/nginx; install -d is idempotent and fixes any +# missing/wrong ownership on every restart. +ExecStartPre=/usr/bin/install -d -o nginx -g nginx -m 0755 /usr/local/nginx /usr/local/nginx/client_body_temp /usr/local/nginx/proxy_temp /usr/local/nginx/fastcgi_temp /usr/local/nginx/uwsgi_temp /usr/local/nginx/scgi_temp /var/log/nginx +ExecStartPre=/usr/sbin/nginx -t +ExecStart=/usr/sbin/nginx +ExecReload=/usr/sbin/nginx -s reload +ExecStop=/bin/kill -s QUIT $MAINPID + +# ---- systemd hardening (systemd 257+ on Debian 13 / Ubuntu 26.04) ---- +# Each line shrinks the worker's blast radius without affecting throughput. +NoNewPrivileges=true +ProtectSystem=strict +ProtectHome=true +ProtectKernelTunables=true +ProtectKernelModules=true +ProtectKernelLogs=true +ProtectControlGroups=true +ProtectClock=true +ProtectHostname=true +PrivateDevices=true +PrivateTmp=true +RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6 AF_NETLINK +RestrictNamespaces=true +RestrictRealtime=true +RestrictSUIDSGID=true +LockPersonality=true +SystemCallArchitectures=native +SystemCallFilter=@system-service +SystemCallFilter=~@privileged @resources @mount @debug @cpu-emulation @obsolete @raw-io @reboot @swap + +# Paths nginx legitimately writes to. ProtectSystem=strict makes everything +# else read-only; these carve out the exceptions. +ReadWritePaths=/var/log/nginx /usr/local/nginx /run /nginx /hostdata + +# NOTE on MemoryDenyWriteExecute: LuaJIT does runtime JIT compilation and +# therefore needs writable+executable pages — enabling MDWE breaks Lua. Left +# off intentionally. + +LimitNOFILE=65535 + +[Install] +WantedBy=multi-user.target diff --git a/version b/version index c26735d..6dd66a2 100644 --- a/version +++ b/version @@ -43,7 +43,7 @@ export SYSTEM_PCRE="10.47" export SYSTEM_AWSLC="1.72.0" # https://github.com/SpiderLabs/ModSecurity/releases 3.0.12 -export SYSTEM_MODSECURITY="3.0.12" +export SYSTEM_MODSECURITY="3.0.14" # https://github.com/openresty/lua-resty-mysql/tags export NGX_MOD_LUA_MYSQL="0.29"