From 4fa3a6126c15155257657273a409f8eb2ff8a593 Mon Sep 17 00:00:00 2001 From: Konstantin Pavlov Date: Wed, 11 Feb 2026 12:19:27 -0800 Subject: [PATCH 01/19] Moved templates to a separate directory --- .../library/Dockerfile-alpine-otel.template | 0 .../library/Dockerfile-alpine-perl.template | 0 .../library/Dockerfile-alpine-slim.template | 0 .../library/Dockerfile-alpine.template | 0 .../library/Dockerfile-debian-otel.template | 0 .../library/Dockerfile-debian-perl.template | 0 .../library/Dockerfile-debian.template | 0 7 files changed, 0 insertions(+), 0 deletions(-) rename Dockerfile-alpine-otel.template => templates/library/Dockerfile-alpine-otel.template (100%) rename Dockerfile-alpine-perl.template => templates/library/Dockerfile-alpine-perl.template (100%) rename Dockerfile-alpine-slim.template => templates/library/Dockerfile-alpine-slim.template (100%) rename Dockerfile-alpine.template => templates/library/Dockerfile-alpine.template (100%) rename Dockerfile-debian-otel.template => templates/library/Dockerfile-debian-otel.template (100%) rename Dockerfile-debian-perl.template => templates/library/Dockerfile-debian-perl.template (100%) rename Dockerfile-debian.template => templates/library/Dockerfile-debian.template (100%) diff --git a/Dockerfile-alpine-otel.template b/templates/library/Dockerfile-alpine-otel.template similarity index 100% rename from Dockerfile-alpine-otel.template rename to templates/library/Dockerfile-alpine-otel.template diff --git a/Dockerfile-alpine-perl.template b/templates/library/Dockerfile-alpine-perl.template similarity index 100% rename from Dockerfile-alpine-perl.template rename to templates/library/Dockerfile-alpine-perl.template diff --git a/Dockerfile-alpine-slim.template b/templates/library/Dockerfile-alpine-slim.template similarity index 100% rename from Dockerfile-alpine-slim.template rename to templates/library/Dockerfile-alpine-slim.template diff --git a/Dockerfile-alpine.template b/templates/library/Dockerfile-alpine.template similarity index 100% rename from Dockerfile-alpine.template rename to templates/library/Dockerfile-alpine.template diff --git a/Dockerfile-debian-otel.template b/templates/library/Dockerfile-debian-otel.template similarity index 100% rename from Dockerfile-debian-otel.template rename to templates/library/Dockerfile-debian-otel.template diff --git a/Dockerfile-debian-perl.template b/templates/library/Dockerfile-debian-perl.template similarity index 100% rename from Dockerfile-debian-perl.template rename to templates/library/Dockerfile-debian-perl.template diff --git a/Dockerfile-debian.template b/templates/library/Dockerfile-debian.template similarity index 100% rename from Dockerfile-debian.template rename to templates/library/Dockerfile-debian.template From 33c9690bc4e6548621664a48d512a7f3c237bb20 Mon Sep 17 00:00:00 2001 From: Konstantin Pavlov Date: Wed, 11 Feb 2026 12:28:15 -0800 Subject: [PATCH 02/19] Moved entrypoint scripts to a separate directory --- entrypoint/{ => library}/10-listen-on-ipv6-by-default.sh | 0 entrypoint/{ => library}/15-local-resolvers.envsh | 0 entrypoint/{ => library}/20-envsubst-on-templates.sh | 0 entrypoint/{ => library}/30-tune-worker-processes.sh | 0 entrypoint/{ => library}/docker-entrypoint.sh | 0 5 files changed, 0 insertions(+), 0 deletions(-) rename entrypoint/{ => library}/10-listen-on-ipv6-by-default.sh (100%) rename entrypoint/{ => library}/15-local-resolvers.envsh (100%) rename entrypoint/{ => library}/20-envsubst-on-templates.sh (100%) rename entrypoint/{ => library}/30-tune-worker-processes.sh (100%) rename entrypoint/{ => library}/docker-entrypoint.sh (100%) diff --git a/entrypoint/10-listen-on-ipv6-by-default.sh b/entrypoint/library/10-listen-on-ipv6-by-default.sh similarity index 100% rename from entrypoint/10-listen-on-ipv6-by-default.sh rename to entrypoint/library/10-listen-on-ipv6-by-default.sh diff --git a/entrypoint/15-local-resolvers.envsh b/entrypoint/library/15-local-resolvers.envsh similarity index 100% rename from entrypoint/15-local-resolvers.envsh rename to entrypoint/library/15-local-resolvers.envsh diff --git a/entrypoint/20-envsubst-on-templates.sh b/entrypoint/library/20-envsubst-on-templates.sh similarity index 100% rename from entrypoint/20-envsubst-on-templates.sh rename to entrypoint/library/20-envsubst-on-templates.sh diff --git a/entrypoint/30-tune-worker-processes.sh b/entrypoint/library/30-tune-worker-processes.sh similarity index 100% rename from entrypoint/30-tune-worker-processes.sh rename to entrypoint/library/30-tune-worker-processes.sh diff --git a/entrypoint/docker-entrypoint.sh b/entrypoint/library/docker-entrypoint.sh similarity index 100% rename from entrypoint/docker-entrypoint.sh rename to entrypoint/library/docker-entrypoint.sh From 6277c452bc678abe28175b9010df58cdb57f0b54 Mon Sep 17 00:00:00 2001 From: Konstantin Pavlov Date: Wed, 11 Feb 2026 12:20:57 -0800 Subject: [PATCH 03/19] Imported unprivileged images templates This corresponds to docker-nginx-unpriveleged commit f8a83af7a01de7b40b4ea95027572bdeee3d37de --- .../Dockerfile-alpine-otel.template | 74 +++++++++ .../Dockerfile-alpine-perl.template | 69 ++++++++ .../Dockerfile-alpine-slim.template | 119 ++++++++++++++ .../unprivileged/Dockerfile-alpine.template | 77 +++++++++ .../Dockerfile-debian-otel.template | 97 +++++++++++ .../Dockerfile-debian-perl.template | 95 +++++++++++ .../unprivileged/Dockerfile-debian.template | 153 ++++++++++++++++++ 7 files changed, 684 insertions(+) create mode 100644 templates/unprivileged/Dockerfile-alpine-otel.template create mode 100644 templates/unprivileged/Dockerfile-alpine-perl.template create mode 100644 templates/unprivileged/Dockerfile-alpine-slim.template create mode 100644 templates/unprivileged/Dockerfile-alpine.template create mode 100644 templates/unprivileged/Dockerfile-debian-otel.template create mode 100644 templates/unprivileged/Dockerfile-debian-perl.template create mode 100644 templates/unprivileged/Dockerfile-debian.template diff --git a/templates/unprivileged/Dockerfile-alpine-otel.template b/templates/unprivileged/Dockerfile-alpine-otel.template new file mode 100644 index 000000000..14212a097 --- /dev/null +++ b/templates/unprivileged/Dockerfile-alpine-otel.template @@ -0,0 +1,74 @@ +ARG IMAGE=nginxinc/nginx-unprivileged:%%NGINX_VERSION%%-alpine +FROM $IMAGE + +ENV OTEL_VERSION=%%OTEL_VERSION%% + +ARG UID=101 +ARG GID=101 + +USER root + +RUN set -x \ + && apkArch="$(cat /etc/apk/arch)" \ + && nginxPackages="%%PACKAGES%% + " \ +# install prerequisites for public key and pkg-oss checks + && apk add --no-cache --virtual .checksum-deps \ + openssl \ + && case "$apkArch" in \ + x86_64|aarch64) \ +# arches officially built by upstream + apk add -X "%%PACKAGEREPO%%v$(egrep -o '^[0-9]+\.[0-9]+' /etc/alpine-release)/main" --no-cache $nginxPackages \ + ;; \ + *) \ +# we're on an architecture upstream doesn't officially build for +# let's build binaries from the published packaging sources + set -x \ + && tempDir="$(mktemp -d)" \ + && chown nobody:nobody $tempDir \ + && apk add --no-cache --virtual .build-deps \ + gcc \ + libc-dev \ + make \ + openssl-dev \ + pcre2-dev \ + zlib-dev \ + linux-headers \ + cmake \ + bash \ + alpine-sdk \ + findutils \ + curl \ + xz \ + protobuf-dev \ + grpc-dev \ + && su nobody -s /bin/sh -c " \ + export HOME=${tempDir} \ + && cd ${tempDir} \ + && curl -f -L -O https://github.com/nginx/pkg-oss/archive/%%REVISION%%.tar.gz \ + && PKGOSSCHECKSUM=\"%%PKGOSSCHECKSUM%% *%%REVISION%%.tar.gz\" \ + && if [ \"\$(openssl sha512 -r %%REVISION%%.tar.gz)\" = \"\$PKGOSSCHECKSUM\" ]; then \ + echo \"pkg-oss tarball checksum verification succeeded!\"; \ + else \ + echo \"pkg-oss tarball checksum verification failed!\"; \ + exit 1; \ + fi \ + && tar xzvf %%REVISION%%.tar.gz \ + && cd pkg-oss-%%REVISION%% \ + && cd alpine \ + && make %%BUILDTARGET%% \ + && apk index --allow-untrusted -o ${tempDir}/packages/alpine/${apkArch}/APKINDEX.tar.gz ${tempDir}/packages/alpine/${apkArch}/*.apk \ + && abuild-sign -k ${tempDir}/.abuild/abuild-key.rsa ${tempDir}/packages/alpine/${apkArch}/APKINDEX.tar.gz \ + " \ + && cp ${tempDir}/.abuild/abuild-key.rsa.pub /etc/apk/keys/ \ + && apk del --no-network .build-deps \ + && apk add -X ${tempDir}/packages/alpine/ --no-cache $nginxPackages \ + ;; \ + esac \ +# remove checksum deps + && apk del --no-network .checksum-deps \ +# if we have leftovers from building, let's purge them (including extra, unnecessary build deps) + && if [ -n "$tempDir" ]; then rm -rf "$tempDir"; fi \ + && if [ -f "/etc/apk/keys/abuild-key.rsa.pub" ]; then rm -f /etc/apk/keys/abuild-key.rsa.pub; fi + +USER $UID diff --git a/templates/unprivileged/Dockerfile-alpine-perl.template b/templates/unprivileged/Dockerfile-alpine-perl.template new file mode 100644 index 000000000..ac2f023cf --- /dev/null +++ b/templates/unprivileged/Dockerfile-alpine-perl.template @@ -0,0 +1,69 @@ +ARG IMAGE=nginxinc/nginx-unprivileged:%%NGINX_VERSION%%-alpine +FROM $IMAGE + +ARG UID=101 +ARG GID=101 + +USER root + +RUN set -x \ + && apkArch="$(cat /etc/apk/arch)" \ + && nginxPackages="%%PACKAGES%% + " \ +# install prerequisites for public key and pkg-oss checks + && apk add --no-cache --virtual .checksum-deps \ + openssl \ + && case "$apkArch" in \ + x86_64|aarch64) \ +# arches officially built by upstream + apk add -X "%%PACKAGEREPO%%v$(egrep -o '^[0-9]+\.[0-9]+' /etc/alpine-release)/main" --no-cache $nginxPackages \ + ;; \ + *) \ +# we're on an architecture upstream doesn't officially build for +# let's build binaries from the published packaging sources + set -x \ + && tempDir="$(mktemp -d)" \ + && chown nobody:nobody $tempDir \ + && apk add --no-cache --virtual .build-deps \ + gcc \ + libc-dev \ + make \ + openssl-dev \ + pcre2-dev \ + zlib-dev \ + linux-headers \ + perl-dev \ + bash \ + alpine-sdk \ + findutils \ + curl \ + && su nobody -s /bin/sh -c " \ + export HOME=${tempDir} \ + && cd ${tempDir} \ + && curl -f -L -O https://github.com/nginx/pkg-oss/archive/%%REVISION%%.tar.gz \ + && PKGOSSCHECKSUM=\"%%PKGOSSCHECKSUM%% *%%REVISION%%.tar.gz\" \ + && if [ \"\$(openssl sha512 -r %%REVISION%%.tar.gz)\" = \"\$PKGOSSCHECKSUM\" ]; then \ + echo \"pkg-oss tarball checksum verification succeeded!\"; \ + else \ + echo \"pkg-oss tarball checksum verification failed!\"; \ + exit 1; \ + fi \ + && tar xzvf %%REVISION%%.tar.gz \ + && cd pkg-oss-%%REVISION%% \ + && cd alpine \ + && make %%BUILDTARGET%% \ + && apk index --allow-untrusted -o ${tempDir}/packages/alpine/${apkArch}/APKINDEX.tar.gz ${tempDir}/packages/alpine/${apkArch}/*.apk \ + && abuild-sign -k ${tempDir}/.abuild/abuild-key.rsa ${tempDir}/packages/alpine/${apkArch}/APKINDEX.tar.gz \ + " \ + && cp ${tempDir}/.abuild/abuild-key.rsa.pub /etc/apk/keys/ \ + && apk del --no-network .build-deps \ + && apk add -X ${tempDir}/packages/alpine/ --no-cache $nginxPackages \ + ;; \ + esac \ +# remove checksum deps + && apk del --no-network .checksum-deps \ +# if we have leftovers from building, let's purge them (including extra, unnecessary build deps) + && if [ -n "$tempDir" ]; then rm -rf "$tempDir"; fi \ + && if [ -f "/etc/apk/keys/abuild-key.rsa.pub" ]; then rm -f /etc/apk/keys/abuild-key.rsa.pub; fi + +USER $UID diff --git a/templates/unprivileged/Dockerfile-alpine-slim.template b/templates/unprivileged/Dockerfile-alpine-slim.template new file mode 100644 index 000000000..0a550e9d3 --- /dev/null +++ b/templates/unprivileged/Dockerfile-alpine-slim.template @@ -0,0 +1,119 @@ +ARG IMAGE=alpine:%%ALPINE_VERSION%% +FROM $IMAGE + +LABEL maintainer="NGINX Docker Maintainers " + +ENV NGINX_VERSION=%%NGINX_VERSION%% +ENV PKG_RELEASE=%%PKG_RELEASE%% +ENV DYNPKG_RELEASE=%%DYNPKG_RELEASE%% + +ARG UID=101 +ARG GID=101 + +RUN set -x \ +# create nginx user/group first, to be consistent throughout docker variants + && addgroup -g $GID -S nginx || true \ + && adduser -S -D -H -u $UID -h /var/cache/nginx -s /sbin/nologin -G nginx -g nginx nginx || true \ + && apkArch="$(cat /etc/apk/arch)" \ + && nginxPackages="%%PACKAGES%% + " \ +# install prerequisites for public key and pkg-oss checks + && apk add --no-cache --virtual .checksum-deps \ + openssl \ + && case "$apkArch" in \ + x86_64|aarch64) \ +# arches officially built by upstream + set -x \ + && KEY_SHA512="e09fa32f0a0eab2b879ccbbc4d0e4fb9751486eedda75e35fac65802cc9faa266425edf83e261137a2f4d16281ce2c1a5f4502930fe75154723da014214f0655" \ + && wget -O /tmp/nginx_signing.rsa.pub https://nginx.org/keys/nginx_signing.rsa.pub \ + && if echo "$KEY_SHA512 */tmp/nginx_signing.rsa.pub" | sha512sum -c -; then \ + echo "key verification succeeded!"; \ + mv /tmp/nginx_signing.rsa.pub /etc/apk/keys/; \ + else \ + echo "key verification failed!"; \ + exit 1; \ + fi \ + && apk add -X "%%PACKAGEREPO%%v$(egrep -o '^[0-9]+\.[0-9]+' /etc/alpine-release)/main" --no-cache $nginxPackages \ + ;; \ + *) \ +# we're on an architecture upstream doesn't officially build for +# let's build binaries from the published packaging sources + set -x \ + && tempDir="$(mktemp -d)" \ + && chown nobody:nobody $tempDir \ + && apk add --no-cache --virtual .build-deps \ + gcc \ + libc-dev \ + make \ + openssl-dev \ + pcre2-dev \ + zlib-dev \ + linux-headers \ + bash \ + alpine-sdk \ + findutils \ + curl \ + && su nobody -s /bin/sh -c " \ + export HOME=${tempDir} \ + && cd ${tempDir} \ + && curl -f -L -O https://github.com/nginx/pkg-oss/archive/%%REVISION%%.tar.gz \ + && PKGOSSCHECKSUM=\"%%PKGOSSCHECKSUM%% *%%REVISION%%.tar.gz\" \ + && if [ \"\$(openssl sha512 -r %%REVISION%%.tar.gz)\" = \"\$PKGOSSCHECKSUM\" ]; then \ + echo \"pkg-oss tarball checksum verification succeeded!\"; \ + else \ + echo \"pkg-oss tarball checksum verification failed!\"; \ + exit 1; \ + fi \ + && tar xzvf %%REVISION%%.tar.gz \ + && cd pkg-oss-%%REVISION%% \ + && cd alpine \ + && make %%BUILDTARGET%% \ + && apk index --allow-untrusted -o ${tempDir}/packages/alpine/${apkArch}/APKINDEX.tar.gz ${tempDir}/packages/alpine/${apkArch}/*.apk \ + && abuild-sign -k ${tempDir}/.abuild/abuild-key.rsa ${tempDir}/packages/alpine/${apkArch}/APKINDEX.tar.gz \ + " \ + && cp ${tempDir}/.abuild/abuild-key.rsa.pub /etc/apk/keys/ \ + && apk del --no-network .build-deps \ + && apk add -X ${tempDir}/packages/alpine/ --no-cache $nginxPackages \ + ;; \ + esac \ +# remove checksum deps + && apk del --no-network .checksum-deps \ +# if we have leftovers from building, let's purge them (including extra, unnecessary build deps) + && if [ -n "$tempDir" ]; then rm -rf "$tempDir"; fi \ + && if [ -f "/etc/apk/keys/abuild-key.rsa.pub" ]; then rm -f /etc/apk/keys/abuild-key.rsa.pub; fi \ +# Add `envsubst` for templating environment variables + && apk add --no-cache gettext-envsubst \ +# Bring in tzdata so users could set the timezones through the environment +# variables + && apk add --no-cache tzdata \ +# forward request and error logs to docker log collector + && ln -sf /dev/stdout /var/log/nginx/access.log \ + && ln -sf /dev/stderr /var/log/nginx/error.log \ +# create a docker-entrypoint.d directory + && mkdir /docker-entrypoint.d + +# implement changes required to run NGINX as an unprivileged user +RUN sed -i 's,listen 80;,listen 8080;,' /etc/nginx/conf.d/default.conf \ + && sed -i '/user nginx;/d' /etc/nginx/nginx.conf \ + && sed -i 's,\(/var\)\{0\,1\}/run/nginx.pid,/tmp/nginx.pid,' /etc/nginx/nginx.conf \ + && sed -i "/^http {/a \ proxy_temp_path /tmp/proxy_temp;\n client_body_temp_path /tmp/client_temp;\n fastcgi_temp_path /tmp/fastcgi_temp;\n uwsgi_temp_path /tmp/uwsgi_temp;\n scgi_temp_path /tmp/scgi_temp;\n" /etc/nginx/nginx.conf \ +# nginx user must own the cache and etc directory to write cache and tweak the nginx config + && chown -R $UID:0 /var/cache/nginx \ + && chmod -R g+w /var/cache/nginx \ + && chown -R $UID:0 /etc/nginx \ + && chmod -R g+w /etc/nginx + +COPY docker-entrypoint.sh / +COPY 10-listen-on-ipv6-by-default.sh /docker-entrypoint.d +COPY 15-local-resolvers.envsh /docker-entrypoint.d +COPY 20-envsubst-on-templates.sh /docker-entrypoint.d +COPY 30-tune-worker-processes.sh /docker-entrypoint.d +ENTRYPOINT ["/docker-entrypoint.sh"] + +EXPOSE 8080 + +STOPSIGNAL SIGQUIT + +USER $UID + +CMD ["nginx", "-g", "daemon off;"] diff --git a/templates/unprivileged/Dockerfile-alpine.template b/templates/unprivileged/Dockerfile-alpine.template new file mode 100644 index 000000000..b81400922 --- /dev/null +++ b/templates/unprivileged/Dockerfile-alpine.template @@ -0,0 +1,77 @@ +ARG IMAGE=nginxinc/nginx-unprivileged:%%NGINX_VERSION%%-alpine-slim +FROM $IMAGE + +ENV NJS_VERSION=%%NJS_VERSION%% +ENV NJS_RELEASE=%%NJS_RELEASE%% + +ARG UID=101 +ARG GID=101 + +USER root + +RUN set -x \ + && apkArch="$(cat /etc/apk/arch)" \ + && nginxPackages="%%PACKAGES%% + " \ +# install prerequisites for public key and pkg-oss checks + && apk add --no-cache --virtual .checksum-deps \ + openssl \ + && case "$apkArch" in \ + x86_64|aarch64) \ +# arches officially built by upstream + apk add -X "%%PACKAGEREPO%%v$(egrep -o '^[0-9]+\.[0-9]+' /etc/alpine-release)/main" --no-cache $nginxPackages \ + ;; \ + *) \ +# we're on an architecture upstream doesn't officially build for +# let's build binaries from the published packaging sources + set -x \ + && tempDir="$(mktemp -d)" \ + && chown nobody:nobody $tempDir \ + && apk add --no-cache --virtual .build-deps \ + gcc \ + libc-dev \ + make \ + openssl-dev \ + pcre2-dev \ + zlib-dev \ + linux-headers \ + libxslt-dev \ + gd-dev \ + geoip-dev \ + libedit-dev \ + bash \ + alpine-sdk \ + findutils \ + curl \ + && su nobody -s /bin/sh -c " \ + export HOME=${tempDir} \ + && cd ${tempDir} \ + && curl -f -L -O https://github.com/nginx/pkg-oss/archive/%%REVISION%%.tar.gz \ + && PKGOSSCHECKSUM=\"%%PKGOSSCHECKSUM%% *%%REVISION%%.tar.gz\" \ + && if [ \"\$(openssl sha512 -r %%REVISION%%.tar.gz)\" = \"\$PKGOSSCHECKSUM\" ]; then \ + echo \"pkg-oss tarball checksum verification succeeded!\"; \ + else \ + echo \"pkg-oss tarball checksum verification failed!\"; \ + exit 1; \ + fi \ + && tar xzvf %%REVISION%%.tar.gz \ + && cd pkg-oss-%%REVISION%% \ + && cd alpine \ + && make %%BUILDTARGET%% \ + && apk index --allow-untrusted -o ${tempDir}/packages/alpine/${apkArch}/APKINDEX.tar.gz ${tempDir}/packages/alpine/${apkArch}/*.apk \ + && abuild-sign -k ${tempDir}/.abuild/abuild-key.rsa ${tempDir}/packages/alpine/${apkArch}/APKINDEX.tar.gz \ + " \ + && cp ${tempDir}/.abuild/abuild-key.rsa.pub /etc/apk/keys/ \ + && apk del --no-network .build-deps \ + && apk add -X ${tempDir}/packages/alpine/ --no-cache $nginxPackages \ + ;; \ + esac \ +# remove checksum deps + && apk del --no-network .checksum-deps \ +# if we have leftovers from building, let's purge them (including extra, unnecessary build deps) + && if [ -n "$tempDir" ]; then rm -rf "$tempDir"; fi \ + && if [ -f "/etc/apk/keys/abuild-key.rsa.pub" ]; then rm -f /etc/apk/keys/abuild-key.rsa.pub; fi \ +# Bring in curl and ca-certificates to make registering on DNS SD easier + && apk add --no-cache curl ca-certificates + +USER $UID diff --git a/templates/unprivileged/Dockerfile-debian-otel.template b/templates/unprivileged/Dockerfile-debian-otel.template new file mode 100644 index 000000000..93b8ea16e --- /dev/null +++ b/templates/unprivileged/Dockerfile-debian-otel.template @@ -0,0 +1,97 @@ +ARG IMAGE=nginxinc/nginx-unprivileged:%%NGINX_VERSION%% +FROM $IMAGE + +ENV OTEL_VERSION=%%OTEL_VERSION%% + +ARG UID=101 +ARG GID=101 + +USER root + +RUN set -x; \ + NGINX_GPGKEY_PATH=/etc/apt/keyrings/nginx-archive-keyring.gpg; \ + dpkgArch="$(dpkg --print-architecture)" \ + && nginxPackages="%%PACKAGES%% + " \ + && case "$dpkgArch" in \ + amd64|arm64) \ +# arches officially built by upstream + echo "deb [signed-by=$NGINX_GPGKEY_PATH] %%PACKAGEREPO%% %%DEBIAN_VERSION%% nginx" >> /etc/apt/sources.list.d/nginx.list \ + && apt-get update \ + ;; \ + *) \ +# we're on an architecture upstream doesn't officially build for +# let's build binaries from the published packaging sources +# new directory for storing sources and .deb files + tempDir="$(mktemp -d)" \ + && chmod 777 "$tempDir" \ +# (777 to ensure APT's "_apt" user can access it too) + \ +# save list of currently-installed packages so build dependencies can be cleanly removed later + && savedAptMark="$(apt-mark showmanual)" \ + \ +# build .deb files from upstream's packaging sources + && apt-get update \ + && apt-get install --no-install-recommends --no-install-suggests -y \ + curl \ + devscripts \ + equivs \ + git \ + libxml2-utils \ + lsb-release \ + xsltproc \ + && ( \ + cd "$tempDir" \ + && REVISION="%%REVISION%%" \ + && REVISION=${REVISION%~*} \ + && curl -f -L -O https://github.com/nginx/pkg-oss/archive/${REVISION}.tar.gz \ + && PKGOSSCHECKSUM="%%PKGOSSCHECKSUM%% *${REVISION}.tar.gz" \ + && if [ "$(openssl sha512 -r ${REVISION}.tar.gz)" = "$PKGOSSCHECKSUM" ]; then \ + echo "pkg-oss tarball checksum verification succeeded!"; \ + else \ + echo "pkg-oss tarball checksum verification failed!"; \ + exit 1; \ + fi \ + && tar xzvf ${REVISION}.tar.gz \ + && cd pkg-oss-${REVISION} \ + && cd debian \ + && for target in %%BUILDTARGET%%; do \ + make rules-$target; \ + mk-build-deps --install --tool="apt-get -o Debug::pkgProblemResolver=yes --no-install-recommends --yes" \ + debuild-$target/nginx-$NGINX_VERSION/debian/control; \ + done \ + && make %%BUILDTARGET%% \ + ) \ +# we don't remove APT lists here because they get re-downloaded and removed later + \ +# reset apt-mark's "manual" list so that "purge --auto-remove" will remove all build dependencies +# (which is done after we install the built packages so we don't have to redownload any overlapping dependencies) + && apt-mark showmanual | xargs apt-mark auto > /dev/null \ + && { [ -z "$savedAptMark" ] || apt-mark manual $savedAptMark; } \ + \ +# create a temporary local APT repo to install from (so that dependency resolution can be handled by APT, as it should be) + && ls -lAFh "$tempDir" \ + && ( cd "$tempDir" && dpkg-scanpackages . > Packages ) \ + && grep '^Package: ' "$tempDir/Packages" \ + && echo "deb [ trusted=yes ] file://$tempDir ./" > /etc/apt/sources.list.d/temp.list \ +# work around the following APT issue by using "Acquire::GzipIndexes=false" (overriding "/etc/apt/apt.conf.d/docker-gzip-indexes") +# Could not open file /var/lib/apt/lists/partial/_tmp_tmp.ODWljpQfkE_._Packages - open (13: Permission denied) +# ... +# E: Failed to fetch store:/var/lib/apt/lists/partial/_tmp_tmp.ODWljpQfkE_._Packages Could not open file /var/lib/apt/lists/partial/_tmp_tmp.ODWljpQfkE_._Packages - open (13: Permission denied) + && apt-get -o Acquire::GzipIndexes=false update \ + ;; \ + esac \ + \ + && apt-get install --no-install-recommends --no-install-suggests -y \ + $nginxPackages \ + gettext-base \ + curl \ + && apt-get remove --purge --auto-remove -y && rm -rf /var/lib/apt/lists/* /etc/apt/sources.list.d/nginx.list \ + \ +# if we have leftovers from building, let's purge them (including extra, unnecessary build deps) + && if [ -n "$tempDir" ]; then \ + apt-get purge -y --auto-remove \ + && rm -rf "$tempDir" /etc/apt/sources.list.d/temp.list; \ + fi + +USER $UID diff --git a/templates/unprivileged/Dockerfile-debian-perl.template b/templates/unprivileged/Dockerfile-debian-perl.template new file mode 100644 index 000000000..fa99560f8 --- /dev/null +++ b/templates/unprivileged/Dockerfile-debian-perl.template @@ -0,0 +1,95 @@ +ARG IMAGE=nginxinc/nginx-unprivileged:%%NGINX_VERSION%% +FROM $IMAGE + +ARG UID=101 +ARG GID=101 + +USER root + +RUN set -x; \ + NGINX_GPGKEY_PATH=/etc/apt/keyrings/nginx-archive-keyring.gpg; \ + dpkgArch="$(dpkg --print-architecture)" \ + && nginxPackages="%%PACKAGES%% + " \ + && case "$dpkgArch" in \ + amd64|arm64) \ +# arches officially built by upstream + echo "deb [signed-by=$NGINX_GPGKEY_PATH] %%PACKAGEREPO%% %%DEBIAN_VERSION%% nginx" >> /etc/apt/sources.list.d/nginx.list \ + && apt-get update \ + ;; \ + *) \ +# we're on an architecture upstream doesn't officially build for +# let's build binaries from the published packaging sources +# new directory for storing sources and .deb files + tempDir="$(mktemp -d)" \ + && chmod 777 "$tempDir" \ +# (777 to ensure APT's "_apt" user can access it too) + \ +# save list of currently-installed packages so build dependencies can be cleanly removed later + && savedAptMark="$(apt-mark showmanual)" \ + \ +# build .deb files from upstream's packaging sources + && apt-get update \ + && apt-get install --no-install-recommends --no-install-suggests -y \ + curl \ + devscripts \ + equivs \ + git \ + libxml2-utils \ + lsb-release \ + xsltproc \ + && ( \ + cd "$tempDir" \ + && REVISION="%%REVISION%%" \ + && REVISION=${REVISION%~*} \ + && curl -f -L -O https://github.com/nginx/pkg-oss/archive/${REVISION}.tar.gz \ + && PKGOSSCHECKSUM="%%PKGOSSCHECKSUM%% *${REVISION}.tar.gz" \ + && if [ "$(openssl sha512 -r ${REVISION}.tar.gz)" = "$PKGOSSCHECKSUM" ]; then \ + echo "pkg-oss tarball checksum verification succeeded!"; \ + else \ + echo "pkg-oss tarball checksum verification failed!"; \ + exit 1; \ + fi \ + && tar xzvf ${REVISION}.tar.gz \ + && cd pkg-oss-${REVISION} \ + && cd debian \ + && for target in %%BUILDTARGET%%; do \ + make rules-$target; \ + mk-build-deps --install --tool="apt-get -o Debug::pkgProblemResolver=yes --no-install-recommends --yes" \ + debuild-$target/nginx-$NGINX_VERSION/debian/control; \ + done \ + && make %%BUILDTARGET%% \ + ) \ +# we don't remove APT lists here because they get re-downloaded and removed later + \ +# reset apt-mark's "manual" list so that "purge --auto-remove" will remove all build dependencies +# (which is done after we install the built packages so we don't have to redownload any overlapping dependencies) + && apt-mark showmanual | xargs apt-mark auto > /dev/null \ + && { [ -z "$savedAptMark" ] || apt-mark manual $savedAptMark; } \ + \ +# create a temporary local APT repo to install from (so that dependency resolution can be handled by APT, as it should be) + && ls -lAFh "$tempDir" \ + && ( cd "$tempDir" && dpkg-scanpackages . > Packages ) \ + && grep '^Package: ' "$tempDir/Packages" \ + && echo "deb [ trusted=yes ] file://$tempDir ./" > /etc/apt/sources.list.d/temp.list \ +# work around the following APT issue by using "Acquire::GzipIndexes=false" (overriding "/etc/apt/apt.conf.d/docker-gzip-indexes") +# Could not open file /var/lib/apt/lists/partial/_tmp_tmp.ODWljpQfkE_._Packages - open (13: Permission denied) +# ... +# E: Failed to fetch store:/var/lib/apt/lists/partial/_tmp_tmp.ODWljpQfkE_._Packages Could not open file /var/lib/apt/lists/partial/_tmp_tmp.ODWljpQfkE_._Packages - open (13: Permission denied) + && apt-get -o Acquire::GzipIndexes=false update \ + ;; \ + esac \ + \ + && apt-get install --no-install-recommends --no-install-suggests -y \ + $nginxPackages \ + gettext-base \ + curl \ + && apt-get remove --purge --auto-remove -y && rm -rf /var/lib/apt/lists/* /etc/apt/sources.list.d/nginx.list \ + \ +# if we have leftovers from building, let's purge them (including extra, unnecessary build deps) + && if [ -n "$tempDir" ]; then \ + apt-get purge -y --auto-remove \ + && rm -rf "$tempDir" /etc/apt/sources.list.d/temp.list; \ + fi + +USER $UID diff --git a/templates/unprivileged/Dockerfile-debian.template b/templates/unprivileged/Dockerfile-debian.template new file mode 100644 index 000000000..ed3076da9 --- /dev/null +++ b/templates/unprivileged/Dockerfile-debian.template @@ -0,0 +1,153 @@ +ARG IMAGE=debian:%%DEBIAN_VERSION%%-slim +FROM $IMAGE + +LABEL maintainer="NGINX Docker Maintainers " + +ENV NGINX_VERSION=%%NGINX_VERSION%% +ENV NJS_VERSION=%%NJS_VERSION%% +ENV NJS_RELEASE=%%NJS_RELEASE%% +ENV PKG_RELEASE=%%PKG_RELEASE%% +ENV DYNPKG_RELEASE=%%DYNPKG_RELEASE%% + +ARG UID=101 +ARG GID=101 + +RUN set -x \ +# create nginx user/group first, to be consistent throughout docker variants + && groupadd --system --gid $GID nginx || true \ + && useradd --system --gid nginx --no-create-home --home /nonexistent --comment "nginx user" --shell /bin/false --uid $UID nginx || true \ + && apt-get update \ + && apt-get install --no-install-recommends --no-install-suggests -y gnupg1 ca-certificates \ + && \ + NGINX_GPGKEYS="573BFD6B3D8FBC641079A6ABABF5BD827BD9BF62 8540A6F18833A80E9C1653A42FD21310B49F6B46 9E9BE90EACBCDE69FE9B204CBCDCD8A38D88A2B3"; \ + NGINX_GPGKEY_PATH=/etc/apt/keyrings/nginx-archive-keyring.gpg; \ + export GNUPGHOME="$(mktemp -d)"; \ + found=''; \ + for NGINX_GPGKEY in $NGINX_GPGKEYS; do \ + for server in \ + hkp://keyserver.ubuntu.com:80 \ + pgp.mit.edu \ + ; do \ + echo "Fetching GPG key $NGINX_GPGKEY from $server"; \ + gpg1 --batch --keyserver "$server" --keyserver-options timeout=10 --recv-keys "$NGINX_GPGKEY" && found=yes && break; \ + done; \ + test -z "$found" && echo >&2 "error: failed to fetch GPG key $NGINX_GPGKEY" && exit 1; \ + done; \ + gpg1 --batch --export $NGINX_GPGKEYS > "$NGINX_GPGKEY_PATH" ; \ + rm -rf "$GNUPGHOME"; \ + apt-get remove --purge --auto-remove -y gnupg1 && rm -rf /var/lib/apt/lists/* \ + && dpkgArch="$(dpkg --print-architecture)" \ + && nginxPackages="%%PACKAGES%% + " \ + && case "$dpkgArch" in \ + amd64|arm64) \ +# arches officially built by upstream + echo "deb [signed-by=$NGINX_GPGKEY_PATH] %%PACKAGEREPO%% %%DEBIAN_VERSION%% nginx" >> /etc/apt/sources.list.d/nginx.list \ + && apt-get update \ + ;; \ + *) \ +# we're on an architecture upstream doesn't officially build for +# let's build binaries from the published packaging sources +# new directory for storing sources and .deb files + tempDir="$(mktemp -d)" \ + && chmod 777 "$tempDir" \ +# (777 to ensure APT's "_apt" user can access it too) + \ +# save list of currently-installed packages so build dependencies can be cleanly removed later + && savedAptMark="$(apt-mark showmanual)" \ + \ +# build .deb files from upstream's packaging sources + && apt-get update \ + && apt-get install --no-install-recommends --no-install-suggests -y \ + curl \ + devscripts \ + equivs \ + git \ + libxml2-utils \ + lsb-release \ + xsltproc \ + && ( \ + cd "$tempDir" \ + && REVISION="%%REVISION%%" \ + && REVISION=${REVISION%~*} \ + && curl -f -L -O https://github.com/nginx/pkg-oss/archive/${REVISION}.tar.gz \ + && PKGOSSCHECKSUM="%%PKGOSSCHECKSUM%% *${REVISION}.tar.gz" \ + && if [ "$(openssl sha512 -r ${REVISION}.tar.gz)" = "$PKGOSSCHECKSUM" ]; then \ + echo "pkg-oss tarball checksum verification succeeded!"; \ + else \ + echo "pkg-oss tarball checksum verification failed!"; \ + exit 1; \ + fi \ + && tar xzvf ${REVISION}.tar.gz \ + && cd pkg-oss-${REVISION} \ + && cd debian \ + && for target in %%BUILDTARGET%%; do \ + make rules-$target; \ + mk-build-deps --install --tool="apt-get -o Debug::pkgProblemResolver=yes --no-install-recommends --yes" \ + debuild-$target/nginx-$NGINX_VERSION/debian/control; \ + done \ + && make %%BUILDTARGET%% \ + ) \ +# we don't remove APT lists here because they get re-downloaded and removed later + \ +# reset apt-mark's "manual" list so that "purge --auto-remove" will remove all build dependencies +# (which is done after we install the built packages so we don't have to redownload any overlapping dependencies) + && apt-mark showmanual | xargs apt-mark auto > /dev/null \ + && { [ -z "$savedAptMark" ] || apt-mark manual $savedAptMark; } \ + \ +# create a temporary local APT repo to install from (so that dependency resolution can be handled by APT, as it should be) + && ls -lAFh "$tempDir" \ + && ( cd "$tempDir" && dpkg-scanpackages . > Packages ) \ + && grep '^Package: ' "$tempDir/Packages" \ + && echo "deb [ trusted=yes ] file://$tempDir ./" > /etc/apt/sources.list.d/temp.list \ +# work around the following APT issue by using "Acquire::GzipIndexes=false" (overriding "/etc/apt/apt.conf.d/docker-gzip-indexes") +# Could not open file /var/lib/apt/lists/partial/_tmp_tmp.ODWljpQfkE_._Packages - open (13: Permission denied) +# ... +# E: Failed to fetch store:/var/lib/apt/lists/partial/_tmp_tmp.ODWljpQfkE_._Packages Could not open file /var/lib/apt/lists/partial/_tmp_tmp.ODWljpQfkE_._Packages - open (13: Permission denied) + && apt-get -o Acquire::GzipIndexes=false update \ + ;; \ + esac \ + \ + && apt-get install --no-install-recommends --no-install-suggests -y \ + $nginxPackages \ + gettext-base \ + curl \ + && apt-get remove --purge --auto-remove -y && rm -rf /var/lib/apt/lists/* /etc/apt/sources.list.d/nginx.list \ + \ +# if we have leftovers from building, let's purge them (including extra, unnecessary build deps) + && if [ -n "$tempDir" ]; then \ + apt-get purge -y --auto-remove \ + && rm -rf "$tempDir" /etc/apt/sources.list.d/temp.list; \ + fi \ +# forward request and error logs to docker log collector + && ln -sf /dev/stdout /var/log/nginx/access.log \ + && ln -sf /dev/stderr /var/log/nginx/error.log \ +# create a docker-entrypoint.d directory + && mkdir /docker-entrypoint.d + +# implement changes required to run NGINX as an unprivileged user +RUN sed -i 's,listen 80;,listen 8080;,' /etc/nginx/conf.d/default.conf \ + && sed -i '/user nginx;/d' /etc/nginx/nginx.conf \ + && sed -i 's,\(/var\)\{0\,1\}/run/nginx.pid,/tmp/nginx.pid,' /etc/nginx/nginx.conf \ + && sed -i "/^http {/a \ proxy_temp_path /tmp/proxy_temp;\n client_body_temp_path /tmp/client_temp;\n fastcgi_temp_path /tmp/fastcgi_temp;\n uwsgi_temp_path /tmp/uwsgi_temp;\n scgi_temp_path /tmp/scgi_temp;\n" /etc/nginx/nginx.conf \ + && sed -i 's,PIDFILE=${PIDFILE:-/run/nginx.pid},PIDFILE=${PIDFILE:-/tmp/nginx.pid},' /etc/init.d/nginx \ +# nginx user must own the cache and etc directory to write cache and tweak the nginx config + && chown -R $UID:0 /var/cache/nginx \ + && chmod -R g+w /var/cache/nginx \ + && chown -R $UID:0 /etc/nginx \ + && chmod -R g+w /etc/nginx + +COPY docker-entrypoint.sh / +COPY 10-listen-on-ipv6-by-default.sh /docker-entrypoint.d +COPY 15-local-resolvers.envsh /docker-entrypoint.d +COPY 20-envsubst-on-templates.sh /docker-entrypoint.d +COPY 30-tune-worker-processes.sh /docker-entrypoint.d +ENTRYPOINT ["/docker-entrypoint.sh"] + +EXPOSE 8080 + +STOPSIGNAL SIGQUIT + +USER $UID + +CMD ["nginx", "-g", "daemon off;"] From cc22f7b0385db601934e8fce81de3f7aa8e50629 Mon Sep 17 00:00:00 2001 From: Konstantin Pavlov Date: Wed, 11 Feb 2026 12:29:48 -0800 Subject: [PATCH 04/19] Imported unprivileged images entrypoints This corresponds to docker-nginx-unpriveleged commit f8a83af7a01de7b40b4ea95027572bdeee3d37de --- .../10-listen-on-ipv6-by-default.sh | 67 +++++++ .../unprivileged/15-local-resolvers.envsh | 15 ++ .../unprivileged/20-envsubst-on-templates.sh | 78 ++++++++ .../unprivileged/30-tune-worker-processes.sh | 188 ++++++++++++++++++ entrypoint/unprivileged/docker-entrypoint.sh | 47 +++++ 5 files changed, 395 insertions(+) create mode 100755 entrypoint/unprivileged/10-listen-on-ipv6-by-default.sh create mode 100755 entrypoint/unprivileged/15-local-resolvers.envsh create mode 100755 entrypoint/unprivileged/20-envsubst-on-templates.sh create mode 100755 entrypoint/unprivileged/30-tune-worker-processes.sh create mode 100755 entrypoint/unprivileged/docker-entrypoint.sh diff --git a/entrypoint/unprivileged/10-listen-on-ipv6-by-default.sh b/entrypoint/unprivileged/10-listen-on-ipv6-by-default.sh new file mode 100755 index 000000000..176607941 --- /dev/null +++ b/entrypoint/unprivileged/10-listen-on-ipv6-by-default.sh @@ -0,0 +1,67 @@ +#!/bin/sh +# vim:sw=4:ts=4:et + +set -e + +entrypoint_log() { + if [ -z "${NGINX_ENTRYPOINT_QUIET_LOGS:-}" ]; then + echo "$@" + fi +} + +ME=$(basename "$0") +DEFAULT_CONF_FILE="etc/nginx/conf.d/default.conf" + +# check if we have ipv6 available +if [ ! -f "/proc/net/if_inet6" ]; then + entrypoint_log "$ME: info: ipv6 not available" + exit 0 +fi + +if [ ! -f "/$DEFAULT_CONF_FILE" ]; then + entrypoint_log "$ME: info: /$DEFAULT_CONF_FILE is not a file or does not exist" + exit 0 +fi + +# check if the file can be modified, e.g. not on a r/o filesystem +touch /$DEFAULT_CONF_FILE 2>/dev/null || { entrypoint_log "$ME: info: can not modify /$DEFAULT_CONF_FILE (read-only file system?)"; exit 0; } + +# check if the file is already modified, e.g. on a container restart +grep -q "listen \[::\]:8080;" /$DEFAULT_CONF_FILE && { entrypoint_log "$ME: info: IPv6 listen already enabled"; exit 0; } + +if [ -f "/etc/os-release" ]; then + . /etc/os-release +else + entrypoint_log "$ME: info: can not guess the operating system" + exit 0 +fi + +entrypoint_log "$ME: info: Getting the checksum of /$DEFAULT_CONF_FILE" + +case "$ID" in + "debian") + CHECKSUM=$(dpkg-query --show --showformat='${Conffiles}\n' nginx | grep $DEFAULT_CONF_FILE | cut -d' ' -f 3) + echo "$CHECKSUM /$DEFAULT_CONF_FILE" | md5sum -c - >/dev/null 2>&1 || { + entrypoint_log "$ME: info: /$DEFAULT_CONF_FILE differs from the packaged version" + exit 0 + } + ;; + "alpine") + CHECKSUM=$(apk manifest nginx 2>/dev/null| grep $DEFAULT_CONF_FILE | cut -d' ' -f 1 | cut -d ':' -f 2) + echo "$CHECKSUM /$DEFAULT_CONF_FILE" | sha1sum -c - >/dev/null 2>&1 || { + entrypoint_log "$ME: info: /$DEFAULT_CONF_FILE differs from the packaged version" + exit 0 + } + ;; + *) + entrypoint_log "$ME: info: Unsupported distribution" + exit 0 + ;; +esac + +# enable ipv6 on default.conf listen sockets +sed -i -E 's,listen 8080;,listen 8080;\n listen [::]:8080;,' /$DEFAULT_CONF_FILE + +entrypoint_log "$ME: info: Enabled listen on IPv6 in /$DEFAULT_CONF_FILE" + +exit 0 diff --git a/entrypoint/unprivileged/15-local-resolvers.envsh b/entrypoint/unprivileged/15-local-resolvers.envsh new file mode 100755 index 000000000..e830ddacd --- /dev/null +++ b/entrypoint/unprivileged/15-local-resolvers.envsh @@ -0,0 +1,15 @@ +#!/bin/sh +# vim:sw=2:ts=2:sts=2:et + +set -eu + +LC_ALL=C +PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin + +[ "${NGINX_ENTRYPOINT_LOCAL_RESOLVERS:-}" ] || return 0 + +NGINX_LOCAL_RESOLVERS=$(awk 'BEGIN{ORS=" "} $1=="nameserver" {if ($2 ~ ":") {print "["$2"]"} else {print $2}}' /etc/resolv.conf) + +NGINX_LOCAL_RESOLVERS="${NGINX_LOCAL_RESOLVERS% }" + +export NGINX_LOCAL_RESOLVERS diff --git a/entrypoint/unprivileged/20-envsubst-on-templates.sh b/entrypoint/unprivileged/20-envsubst-on-templates.sh new file mode 100755 index 000000000..6938405be --- /dev/null +++ b/entrypoint/unprivileged/20-envsubst-on-templates.sh @@ -0,0 +1,78 @@ +#!/bin/sh + +set -e + +ME=$(basename "$0") + +entrypoint_log() { + if [ -z "${NGINX_ENTRYPOINT_QUIET_LOGS:-}" ]; then + echo "$@" + fi +} + +add_stream_block() { + local conffile="/etc/nginx/nginx.conf" + + if grep -q -E "\s*stream\s*\{" "$conffile"; then + entrypoint_log "$ME: $conffile contains a stream block; include $stream_output_dir/*.conf to enable stream templates" + else + # check if the file can be modified, e.g. not on a r/o filesystem + touch "$conffile" 2>/dev/null || { entrypoint_log "$ME: info: can not modify $conffile (read-only file system?)"; exit 0; } + entrypoint_log "$ME: Appending stream block to $conffile to include $stream_output_dir/*.conf" + cat << END >> "$conffile" +# added by "$ME" on "$(date)" +stream { + include $stream_output_dir/*.conf; +} +END + fi +} + +auto_envsubst() { + local template_dir="${NGINX_ENVSUBST_TEMPLATE_DIR:-/etc/nginx/templates}" + local suffix="${NGINX_ENVSUBST_TEMPLATE_SUFFIX:-.template}" + local output_dir="${NGINX_ENVSUBST_OUTPUT_DIR:-/etc/nginx/conf.d}" + local stream_suffix="${NGINX_ENVSUBST_STREAM_TEMPLATE_SUFFIX:-.stream-template}" + local stream_output_dir="${NGINX_ENVSUBST_STREAM_OUTPUT_DIR:-/etc/nginx/stream-conf.d}" + local filter="${NGINX_ENVSUBST_FILTER:-}" + + local template defined_envs relative_path output_path subdir + defined_envs=$(printf '${%s} ' $(awk "END { for (name in ENVIRON) { print ( name ~ /${filter}/ ) ? name : \"\" } }" < /dev/null )) + [ -d "$template_dir" ] || return 0 + if [ ! -w "$output_dir" ]; then + entrypoint_log "$ME: ERROR: $template_dir exists, but $output_dir is not writable" + return 0 + fi + find "$template_dir" -follow -type f -name "*$suffix" -print | while read -r template; do + relative_path="${template#"$template_dir/"}" + output_path="$output_dir/${relative_path%"$suffix"}" + subdir=$(dirname "$relative_path") + # create a subdirectory where the template file exists + mkdir -p "$output_dir/$subdir" + entrypoint_log "$ME: Running envsubst on $template to $output_path" + envsubst "$defined_envs" < "$template" > "$output_path" + done + + # Print the first file with the stream suffix, this will be false if there are none + if test -n "$(find "$template_dir" -name "*$stream_suffix" -print -quit)"; then + mkdir -p "$stream_output_dir" + if [ ! -w "$stream_output_dir" ]; then + entrypoint_log "$ME: ERROR: $template_dir exists, but $stream_output_dir is not writable" + return 0 + fi + add_stream_block + find "$template_dir" -follow -type f -name "*$stream_suffix" -print | while read -r template; do + relative_path="${template#"$template_dir/"}" + output_path="$stream_output_dir/${relative_path%"$stream_suffix"}" + subdir=$(dirname "$relative_path") + # create a subdirectory where the template file exists + mkdir -p "$stream_output_dir/$subdir" + entrypoint_log "$ME: Running envsubst on $template to $output_path" + envsubst "$defined_envs" < "$template" > "$output_path" + done + fi +} + +auto_envsubst + +exit 0 diff --git a/entrypoint/unprivileged/30-tune-worker-processes.sh b/entrypoint/unprivileged/30-tune-worker-processes.sh new file mode 100755 index 000000000..defb994f3 --- /dev/null +++ b/entrypoint/unprivileged/30-tune-worker-processes.sh @@ -0,0 +1,188 @@ +#!/bin/sh +# vim:sw=2:ts=2:sts=2:et + +set -eu + +LC_ALL=C +ME=$(basename "$0") +PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin + +[ "${NGINX_ENTRYPOINT_WORKER_PROCESSES_AUTOTUNE:-}" ] || exit 0 + +touch /etc/nginx/nginx.conf 2>/dev/null || { echo >&2 "$ME: error: can not modify /etc/nginx/nginx.conf (read-only file system?)"; exit 0; } + +ceildiv() { + num=$1 + div=$2 + echo $(( (num + div - 1) / div )) +} + +get_cpuset() { + cpusetroot=$1 + cpusetfile=$2 + ncpu=0 + [ -f "$cpusetroot/$cpusetfile" ] || return 1 + for token in $( tr ',' ' ' < "$cpusetroot/$cpusetfile" ); do + case "$token" in + *-*) + count=$( seq $(echo "$token" | tr '-' ' ') | wc -l ) + ncpu=$(( ncpu+count )) + ;; + *) + ncpu=$(( ncpu+1 )) + ;; + esac + done + echo "$ncpu" +} + +get_quota() { + cpuroot=$1 + ncpu=0 + [ -f "$cpuroot/cpu.cfs_quota_us" ] || return 1 + [ -f "$cpuroot/cpu.cfs_period_us" ] || return 1 + cfs_quota=$( cat "$cpuroot/cpu.cfs_quota_us" ) + cfs_period=$( cat "$cpuroot/cpu.cfs_period_us" ) + [ "$cfs_quota" = "-1" ] && return 1 + [ "$cfs_period" = "0" ] && return 1 + ncpu=$( ceildiv "$cfs_quota" "$cfs_period" ) + [ "$ncpu" -gt 0 ] || return 1 + echo "$ncpu" +} + +get_quota_v2() { + cpuroot=$1 + ncpu=0 + [ -f "$cpuroot/cpu.max" ] || return 1 + cfs_quota=$( cut -d' ' -f 1 < "$cpuroot/cpu.max" ) + cfs_period=$( cut -d' ' -f 2 < "$cpuroot/cpu.max" ) + [ "$cfs_quota" = "max" ] && return 1 + [ "$cfs_period" = "0" ] && return 1 + ncpu=$( ceildiv "$cfs_quota" "$cfs_period" ) + [ "$ncpu" -gt 0 ] || return 1 + echo "$ncpu" +} + +get_cgroup_v1_path() { + needle=$1 + found= + foundroot= + mountpoint= + + [ -r "/proc/self/mountinfo" ] || return 1 + [ -r "/proc/self/cgroup" ] || return 1 + + while IFS= read -r line; do + case "$needle" in + "cpuset") + case "$line" in + *cpuset*) + found=$( echo "$line" | cut -d ' ' -f 4,5 ) + break + ;; + esac + ;; + "cpu") + case "$line" in + *cpuset*) + ;; + *cpu,cpuacct*|*cpuacct,cpu|*cpuacct*|*cpu*) + found=$( echo "$line" | cut -d ' ' -f 4,5 ) + break + ;; + esac + esac + done << __EOF__ +$( grep -F -- '- cgroup ' /proc/self/mountinfo ) +__EOF__ + + while IFS= read -r line; do + controller=$( echo "$line" | cut -d: -f 2 ) + case "$needle" in + "cpuset") + case "$controller" in + cpuset) + mountpoint=$( echo "$line" | cut -d: -f 3 ) + break + ;; + esac + ;; + "cpu") + case "$controller" in + cpu,cpuacct|cpuacct,cpu|cpuacct|cpu) + mountpoint=$( echo "$line" | cut -d: -f 3 ) + break + ;; + esac + ;; + esac +done << __EOF__ +$( grep -F -- 'cpu' /proc/self/cgroup ) +__EOF__ + + case "${found%% *}" in + "/") + foundroot="${found##* }$mountpoint" + ;; + "$mountpoint") + foundroot="${found##* }" + ;; + esac + echo "$foundroot" +} + +get_cgroup_v2_path() { + found= + foundroot= + mountpoint= + + [ -r "/proc/self/mountinfo" ] || return 1 + [ -r "/proc/self/cgroup" ] || return 1 + + while IFS= read -r line; do + found=$( echo "$line" | cut -d ' ' -f 4,5 ) + done << __EOF__ +$( grep -F -- '- cgroup2 ' /proc/self/mountinfo ) +__EOF__ + + while IFS= read -r line; do + mountpoint=$( echo "$line" | cut -d: -f 3 ) +done << __EOF__ +$( grep -F -- '0::' /proc/self/cgroup ) +__EOF__ + + case "${found%% *}" in + "") + return 1 + ;; + "/") + foundroot="${found##* }$mountpoint" + ;; + "$mountpoint" | /../*) + foundroot="${found##* }" + ;; + esac + echo "$foundroot" +} + +ncpu_online=$( getconf _NPROCESSORS_ONLN ) +ncpu_cpuset= +ncpu_quota= +ncpu_cpuset_v2= +ncpu_quota_v2= + +cpuset=$( get_cgroup_v1_path "cpuset" ) && ncpu_cpuset=$( get_cpuset "$cpuset" "cpuset.effective_cpus" ) || ncpu_cpuset=$ncpu_online +cpu=$( get_cgroup_v1_path "cpu" ) && ncpu_quota=$( get_quota "$cpu" ) || ncpu_quota=$ncpu_online +cgroup_v2=$( get_cgroup_v2_path ) && ncpu_cpuset_v2=$( get_cpuset "$cgroup_v2" "cpuset.cpus.effective" ) || ncpu_cpuset_v2=$ncpu_online +cgroup_v2=$( get_cgroup_v2_path ) && ncpu_quota_v2=$( get_quota_v2 "$cgroup_v2" ) || ncpu_quota_v2=$ncpu_online + +ncpu=$( printf "%s\n%s\n%s\n%s\n%s\n" \ + "$ncpu_online" \ + "$ncpu_cpuset" \ + "$ncpu_quota" \ + "$ncpu_cpuset_v2" \ + "$ncpu_quota_v2" \ + | sort -n \ + | head -n 1 ) + +sed -i.bak -r 's/^(worker_processes)(.*)$/# Commented out by '"$ME"' on '"$(date)"'\n#\1\2\n\1 '"$ncpu"';/' /etc/nginx/nginx.conf diff --git a/entrypoint/unprivileged/docker-entrypoint.sh b/entrypoint/unprivileged/docker-entrypoint.sh new file mode 100755 index 000000000..8ea04f217 --- /dev/null +++ b/entrypoint/unprivileged/docker-entrypoint.sh @@ -0,0 +1,47 @@ +#!/bin/sh +# vim:sw=4:ts=4:et + +set -e + +entrypoint_log() { + if [ -z "${NGINX_ENTRYPOINT_QUIET_LOGS:-}" ]; then + echo "$@" + fi +} + +if [ "$1" = "nginx" ] || [ "$1" = "nginx-debug" ]; then + if /usr/bin/find "/docker-entrypoint.d/" -mindepth 1 -maxdepth 1 -type f -print -quit 2>/dev/null | read v; then + entrypoint_log "$0: /docker-entrypoint.d/ is not empty, will attempt to perform configuration" + + entrypoint_log "$0: Looking for shell scripts in /docker-entrypoint.d/" + find "/docker-entrypoint.d/" -follow -type f -print | sort -V | while read -r f; do + case "$f" in + *.envsh) + if [ -x "$f" ]; then + entrypoint_log "$0: Sourcing $f"; + . "$f" + else + # warn on shell scripts without exec bit + entrypoint_log "$0: Ignoring $f, not executable"; + fi + ;; + *.sh) + if [ -x "$f" ]; then + entrypoint_log "$0: Launching $f"; + "$f" + else + # warn on shell scripts without exec bit + entrypoint_log "$0: Ignoring $f, not executable"; + fi + ;; + *) entrypoint_log "$0: Ignoring $f";; + esac + done + + entrypoint_log "$0: Configuration complete; ready for start up" + else + entrypoint_log "$0: No files found in /docker-entrypoint.d/, skipping configuration" + fi +fi + +exec "$@" From 4c2182a53e32dc9db7af59f61220e607811e77b3 Mon Sep 17 00:00:00 2001 From: Konstantin Pavlov Date: Tue, 10 Feb 2026 18:51:58 -0800 Subject: [PATCH 05/19] update.sh: create both library and unprivileged Dockerfiles --- update.sh | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/update.sh b/update.sh index 08fc1dfda..6a994ba6a 100755 --- a/update.sh +++ b/update.sh @@ -213,17 +213,18 @@ generated_warning() { __EOF__ } +for target in library unprivileged; do for branch in "${branches[@]}"; do for variant in \ alpine{,-perl,-otel,-slim} \ debian{,-perl,-otel}; do - echo "$branch: $variant dockerfiles" - dir="$branch/$variant" + echo "$target $branch: $variant dockerfiles" + dir="$target/$branch/$variant" variant="$(basename "$variant")" - [ -d "$dir" ] || continue + mkdir -p "$dir" - template="Dockerfile-${variant}.template" + template="templates/${target}/Dockerfile-${variant}.template" { generated_warning cat "$template" @@ -267,9 +268,10 @@ for branch in "${branches[@]}"; do for variant in \ alpine-slim \ debian; do \ - echo "$branch: $variant entrypoint scripts" - dir="$branch/$variant" - cp -a entrypoint/*.sh "$dir/" - cp -a entrypoint/*.envsh "$dir/" + echo "$target $branch: $variant entrypoint scripts" + dir="$target/$branch/$variant" + cp -a entrypoint/$target/*.sh "$dir/" + cp -a entrypoint/$target/*.envsh "$dir/" done done +done From 8262b673edfaeac84974c43110ffb9cfabebfa1a Mon Sep 17 00:00:00 2001 From: Konstantin Pavlov Date: Wed, 11 Feb 2026 12:33:16 -0800 Subject: [PATCH 06/19] Regenerated images sources in new directories --- .../mainline}/alpine-otel/Dockerfile | 0 .../mainline}/alpine-perl/Dockerfile | 0 .../10-listen-on-ipv6-by-default.sh | 0 .../alpine-slim/15-local-resolvers.envsh | 0 .../alpine-slim/20-envsubst-on-templates.sh | 0 .../alpine-slim/30-tune-worker-processes.sh | 0 .../mainline}/alpine-slim/Dockerfile | 0 .../alpine-slim/docker-entrypoint.sh | 0 .../mainline}/alpine/Dockerfile | 0 .../mainline}/debian-otel/Dockerfile | 0 .../mainline}/debian-perl/Dockerfile | 0 .../debian/10-listen-on-ipv6-by-default.sh | 0 .../mainline}/debian/15-local-resolvers.envsh | 0 .../debian/20-envsubst-on-templates.sh | 0 .../debian/30-tune-worker-processes.sh | 0 .../mainline}/debian/Dockerfile | 0 .../mainline}/debian/docker-entrypoint.sh | 0 .../stable}/alpine-otel/Dockerfile | 0 .../stable}/alpine-perl/Dockerfile | 0 .../10-listen-on-ipv6-by-default.sh | 0 .../alpine-slim/15-local-resolvers.envsh | 0 .../alpine-slim/20-envsubst-on-templates.sh | 0 .../alpine-slim/30-tune-worker-processes.sh | 0 .../stable}/alpine-slim/Dockerfile | 0 .../stable}/alpine-slim/docker-entrypoint.sh | 0 {stable => library/stable}/alpine/Dockerfile | 0 .../stable}/debian-otel/Dockerfile | 0 .../stable}/debian-perl/Dockerfile | 0 .../debian/10-listen-on-ipv6-by-default.sh | 0 .../stable}/debian/15-local-resolvers.envsh | 0 .../debian/20-envsubst-on-templates.sh | 0 .../debian/30-tune-worker-processes.sh | 0 {stable => library/stable}/debian/Dockerfile | 0 .../stable}/debian/docker-entrypoint.sh | 0 unprivileged/mainline/alpine-otel/Dockerfile | 86 ++++++++ unprivileged/mainline/alpine-perl/Dockerfile | 81 ++++++++ .../10-listen-on-ipv6-by-default.sh | 67 +++++++ .../alpine-slim/15-local-resolvers.envsh | 15 ++ .../alpine-slim/20-envsubst-on-templates.sh | 78 ++++++++ .../alpine-slim/30-tune-worker-processes.sh | 188 ++++++++++++++++++ unprivileged/mainline/alpine-slim/Dockerfile | 125 ++++++++++++ .../mainline/alpine-slim/docker-entrypoint.sh | 47 +++++ unprivileged/mainline/alpine/Dockerfile | 88 ++++++++ unprivileged/mainline/debian-otel/Dockerfile | 109 ++++++++++ unprivileged/mainline/debian-perl/Dockerfile | 107 ++++++++++ .../debian/10-listen-on-ipv6-by-default.sh | 67 +++++++ .../mainline/debian/15-local-resolvers.envsh | 15 ++ .../debian/20-envsubst-on-templates.sh | 78 ++++++++ .../debian/30-tune-worker-processes.sh | 188 ++++++++++++++++++ unprivileged/mainline/debian/Dockerfile | 164 +++++++++++++++ .../mainline/debian/docker-entrypoint.sh | 47 +++++ unprivileged/stable/alpine-otel/Dockerfile | 86 ++++++++ unprivileged/stable/alpine-perl/Dockerfile | 81 ++++++++ .../10-listen-on-ipv6-by-default.sh | 67 +++++++ .../alpine-slim/15-local-resolvers.envsh | 15 ++ .../alpine-slim/20-envsubst-on-templates.sh | 78 ++++++++ .../alpine-slim/30-tune-worker-processes.sh | 188 ++++++++++++++++++ unprivileged/stable/alpine-slim/Dockerfile | 125 ++++++++++++ .../stable/alpine-slim/docker-entrypoint.sh | 47 +++++ unprivileged/stable/alpine/Dockerfile | 88 ++++++++ unprivileged/stable/debian-otel/Dockerfile | 109 ++++++++++ unprivileged/stable/debian-perl/Dockerfile | 107 ++++++++++ .../debian/10-listen-on-ipv6-by-default.sh | 67 +++++++ .../stable/debian/15-local-resolvers.envsh | 15 ++ .../stable/debian/20-envsubst-on-templates.sh | 78 ++++++++ .../stable/debian/30-tune-worker-processes.sh | 188 ++++++++++++++++++ unprivileged/stable/debian/Dockerfile | 164 +++++++++++++++ .../stable/debian/docker-entrypoint.sh | 47 +++++ 68 files changed, 3100 insertions(+) rename {mainline => library/mainline}/alpine-otel/Dockerfile (100%) rename {mainline => library/mainline}/alpine-perl/Dockerfile (100%) rename {mainline => library/mainline}/alpine-slim/10-listen-on-ipv6-by-default.sh (100%) rename {mainline => library/mainline}/alpine-slim/15-local-resolvers.envsh (100%) rename {mainline => library/mainline}/alpine-slim/20-envsubst-on-templates.sh (100%) rename {mainline => library/mainline}/alpine-slim/30-tune-worker-processes.sh (100%) rename {mainline => library/mainline}/alpine-slim/Dockerfile (100%) rename {mainline => library/mainline}/alpine-slim/docker-entrypoint.sh (100%) rename {mainline => library/mainline}/alpine/Dockerfile (100%) rename {mainline => library/mainline}/debian-otel/Dockerfile (100%) rename {mainline => library/mainline}/debian-perl/Dockerfile (100%) rename {mainline => library/mainline}/debian/10-listen-on-ipv6-by-default.sh (100%) rename {mainline => library/mainline}/debian/15-local-resolvers.envsh (100%) rename {mainline => library/mainline}/debian/20-envsubst-on-templates.sh (100%) rename {mainline => library/mainline}/debian/30-tune-worker-processes.sh (100%) rename {mainline => library/mainline}/debian/Dockerfile (100%) rename {mainline => library/mainline}/debian/docker-entrypoint.sh (100%) rename {stable => library/stable}/alpine-otel/Dockerfile (100%) rename {stable => library/stable}/alpine-perl/Dockerfile (100%) rename {stable => library/stable}/alpine-slim/10-listen-on-ipv6-by-default.sh (100%) rename {stable => library/stable}/alpine-slim/15-local-resolvers.envsh (100%) rename {stable => library/stable}/alpine-slim/20-envsubst-on-templates.sh (100%) rename {stable => library/stable}/alpine-slim/30-tune-worker-processes.sh (100%) rename {stable => library/stable}/alpine-slim/Dockerfile (100%) rename {stable => library/stable}/alpine-slim/docker-entrypoint.sh (100%) rename {stable => library/stable}/alpine/Dockerfile (100%) rename {stable => library/stable}/debian-otel/Dockerfile (100%) rename {stable => library/stable}/debian-perl/Dockerfile (100%) rename {stable => library/stable}/debian/10-listen-on-ipv6-by-default.sh (100%) rename {stable => library/stable}/debian/15-local-resolvers.envsh (100%) rename {stable => library/stable}/debian/20-envsubst-on-templates.sh (100%) rename {stable => library/stable}/debian/30-tune-worker-processes.sh (100%) rename {stable => library/stable}/debian/Dockerfile (100%) rename {stable => library/stable}/debian/docker-entrypoint.sh (100%) create mode 100644 unprivileged/mainline/alpine-otel/Dockerfile create mode 100644 unprivileged/mainline/alpine-perl/Dockerfile create mode 100755 unprivileged/mainline/alpine-slim/10-listen-on-ipv6-by-default.sh create mode 100755 unprivileged/mainline/alpine-slim/15-local-resolvers.envsh create mode 100755 unprivileged/mainline/alpine-slim/20-envsubst-on-templates.sh create mode 100755 unprivileged/mainline/alpine-slim/30-tune-worker-processes.sh create mode 100644 unprivileged/mainline/alpine-slim/Dockerfile create mode 100755 unprivileged/mainline/alpine-slim/docker-entrypoint.sh create mode 100644 unprivileged/mainline/alpine/Dockerfile create mode 100644 unprivileged/mainline/debian-otel/Dockerfile create mode 100644 unprivileged/mainline/debian-perl/Dockerfile create mode 100755 unprivileged/mainline/debian/10-listen-on-ipv6-by-default.sh create mode 100755 unprivileged/mainline/debian/15-local-resolvers.envsh create mode 100755 unprivileged/mainline/debian/20-envsubst-on-templates.sh create mode 100755 unprivileged/mainline/debian/30-tune-worker-processes.sh create mode 100644 unprivileged/mainline/debian/Dockerfile create mode 100755 unprivileged/mainline/debian/docker-entrypoint.sh create mode 100644 unprivileged/stable/alpine-otel/Dockerfile create mode 100644 unprivileged/stable/alpine-perl/Dockerfile create mode 100755 unprivileged/stable/alpine-slim/10-listen-on-ipv6-by-default.sh create mode 100755 unprivileged/stable/alpine-slim/15-local-resolvers.envsh create mode 100755 unprivileged/stable/alpine-slim/20-envsubst-on-templates.sh create mode 100755 unprivileged/stable/alpine-slim/30-tune-worker-processes.sh create mode 100644 unprivileged/stable/alpine-slim/Dockerfile create mode 100755 unprivileged/stable/alpine-slim/docker-entrypoint.sh create mode 100644 unprivileged/stable/alpine/Dockerfile create mode 100644 unprivileged/stable/debian-otel/Dockerfile create mode 100644 unprivileged/stable/debian-perl/Dockerfile create mode 100755 unprivileged/stable/debian/10-listen-on-ipv6-by-default.sh create mode 100755 unprivileged/stable/debian/15-local-resolvers.envsh create mode 100755 unprivileged/stable/debian/20-envsubst-on-templates.sh create mode 100755 unprivileged/stable/debian/30-tune-worker-processes.sh create mode 100644 unprivileged/stable/debian/Dockerfile create mode 100755 unprivileged/stable/debian/docker-entrypoint.sh diff --git a/mainline/alpine-otel/Dockerfile b/library/mainline/alpine-otel/Dockerfile similarity index 100% rename from mainline/alpine-otel/Dockerfile rename to library/mainline/alpine-otel/Dockerfile diff --git a/mainline/alpine-perl/Dockerfile b/library/mainline/alpine-perl/Dockerfile similarity index 100% rename from mainline/alpine-perl/Dockerfile rename to library/mainline/alpine-perl/Dockerfile diff --git a/mainline/alpine-slim/10-listen-on-ipv6-by-default.sh b/library/mainline/alpine-slim/10-listen-on-ipv6-by-default.sh similarity index 100% rename from mainline/alpine-slim/10-listen-on-ipv6-by-default.sh rename to library/mainline/alpine-slim/10-listen-on-ipv6-by-default.sh diff --git a/mainline/alpine-slim/15-local-resolvers.envsh b/library/mainline/alpine-slim/15-local-resolvers.envsh similarity index 100% rename from mainline/alpine-slim/15-local-resolvers.envsh rename to library/mainline/alpine-slim/15-local-resolvers.envsh diff --git a/mainline/alpine-slim/20-envsubst-on-templates.sh b/library/mainline/alpine-slim/20-envsubst-on-templates.sh similarity index 100% rename from mainline/alpine-slim/20-envsubst-on-templates.sh rename to library/mainline/alpine-slim/20-envsubst-on-templates.sh diff --git a/mainline/alpine-slim/30-tune-worker-processes.sh b/library/mainline/alpine-slim/30-tune-worker-processes.sh similarity index 100% rename from mainline/alpine-slim/30-tune-worker-processes.sh rename to library/mainline/alpine-slim/30-tune-worker-processes.sh diff --git a/mainline/alpine-slim/Dockerfile b/library/mainline/alpine-slim/Dockerfile similarity index 100% rename from mainline/alpine-slim/Dockerfile rename to library/mainline/alpine-slim/Dockerfile diff --git a/mainline/alpine-slim/docker-entrypoint.sh b/library/mainline/alpine-slim/docker-entrypoint.sh similarity index 100% rename from mainline/alpine-slim/docker-entrypoint.sh rename to library/mainline/alpine-slim/docker-entrypoint.sh diff --git a/mainline/alpine/Dockerfile b/library/mainline/alpine/Dockerfile similarity index 100% rename from mainline/alpine/Dockerfile rename to library/mainline/alpine/Dockerfile diff --git a/mainline/debian-otel/Dockerfile b/library/mainline/debian-otel/Dockerfile similarity index 100% rename from mainline/debian-otel/Dockerfile rename to library/mainline/debian-otel/Dockerfile diff --git a/mainline/debian-perl/Dockerfile b/library/mainline/debian-perl/Dockerfile similarity index 100% rename from mainline/debian-perl/Dockerfile rename to library/mainline/debian-perl/Dockerfile diff --git a/mainline/debian/10-listen-on-ipv6-by-default.sh b/library/mainline/debian/10-listen-on-ipv6-by-default.sh similarity index 100% rename from mainline/debian/10-listen-on-ipv6-by-default.sh rename to library/mainline/debian/10-listen-on-ipv6-by-default.sh diff --git a/mainline/debian/15-local-resolvers.envsh b/library/mainline/debian/15-local-resolvers.envsh similarity index 100% rename from mainline/debian/15-local-resolvers.envsh rename to library/mainline/debian/15-local-resolvers.envsh diff --git a/mainline/debian/20-envsubst-on-templates.sh b/library/mainline/debian/20-envsubst-on-templates.sh similarity index 100% rename from mainline/debian/20-envsubst-on-templates.sh rename to library/mainline/debian/20-envsubst-on-templates.sh diff --git a/mainline/debian/30-tune-worker-processes.sh b/library/mainline/debian/30-tune-worker-processes.sh similarity index 100% rename from mainline/debian/30-tune-worker-processes.sh rename to library/mainline/debian/30-tune-worker-processes.sh diff --git a/mainline/debian/Dockerfile b/library/mainline/debian/Dockerfile similarity index 100% rename from mainline/debian/Dockerfile rename to library/mainline/debian/Dockerfile diff --git a/mainline/debian/docker-entrypoint.sh b/library/mainline/debian/docker-entrypoint.sh similarity index 100% rename from mainline/debian/docker-entrypoint.sh rename to library/mainline/debian/docker-entrypoint.sh diff --git a/stable/alpine-otel/Dockerfile b/library/stable/alpine-otel/Dockerfile similarity index 100% rename from stable/alpine-otel/Dockerfile rename to library/stable/alpine-otel/Dockerfile diff --git a/stable/alpine-perl/Dockerfile b/library/stable/alpine-perl/Dockerfile similarity index 100% rename from stable/alpine-perl/Dockerfile rename to library/stable/alpine-perl/Dockerfile diff --git a/stable/alpine-slim/10-listen-on-ipv6-by-default.sh b/library/stable/alpine-slim/10-listen-on-ipv6-by-default.sh similarity index 100% rename from stable/alpine-slim/10-listen-on-ipv6-by-default.sh rename to library/stable/alpine-slim/10-listen-on-ipv6-by-default.sh diff --git a/stable/alpine-slim/15-local-resolvers.envsh b/library/stable/alpine-slim/15-local-resolvers.envsh similarity index 100% rename from stable/alpine-slim/15-local-resolvers.envsh rename to library/stable/alpine-slim/15-local-resolvers.envsh diff --git a/stable/alpine-slim/20-envsubst-on-templates.sh b/library/stable/alpine-slim/20-envsubst-on-templates.sh similarity index 100% rename from stable/alpine-slim/20-envsubst-on-templates.sh rename to library/stable/alpine-slim/20-envsubst-on-templates.sh diff --git a/stable/alpine-slim/30-tune-worker-processes.sh b/library/stable/alpine-slim/30-tune-worker-processes.sh similarity index 100% rename from stable/alpine-slim/30-tune-worker-processes.sh rename to library/stable/alpine-slim/30-tune-worker-processes.sh diff --git a/stable/alpine-slim/Dockerfile b/library/stable/alpine-slim/Dockerfile similarity index 100% rename from stable/alpine-slim/Dockerfile rename to library/stable/alpine-slim/Dockerfile diff --git a/stable/alpine-slim/docker-entrypoint.sh b/library/stable/alpine-slim/docker-entrypoint.sh similarity index 100% rename from stable/alpine-slim/docker-entrypoint.sh rename to library/stable/alpine-slim/docker-entrypoint.sh diff --git a/stable/alpine/Dockerfile b/library/stable/alpine/Dockerfile similarity index 100% rename from stable/alpine/Dockerfile rename to library/stable/alpine/Dockerfile diff --git a/stable/debian-otel/Dockerfile b/library/stable/debian-otel/Dockerfile similarity index 100% rename from stable/debian-otel/Dockerfile rename to library/stable/debian-otel/Dockerfile diff --git a/stable/debian-perl/Dockerfile b/library/stable/debian-perl/Dockerfile similarity index 100% rename from stable/debian-perl/Dockerfile rename to library/stable/debian-perl/Dockerfile diff --git a/stable/debian/10-listen-on-ipv6-by-default.sh b/library/stable/debian/10-listen-on-ipv6-by-default.sh similarity index 100% rename from stable/debian/10-listen-on-ipv6-by-default.sh rename to library/stable/debian/10-listen-on-ipv6-by-default.sh diff --git a/stable/debian/15-local-resolvers.envsh b/library/stable/debian/15-local-resolvers.envsh similarity index 100% rename from stable/debian/15-local-resolvers.envsh rename to library/stable/debian/15-local-resolvers.envsh diff --git a/stable/debian/20-envsubst-on-templates.sh b/library/stable/debian/20-envsubst-on-templates.sh similarity index 100% rename from stable/debian/20-envsubst-on-templates.sh rename to library/stable/debian/20-envsubst-on-templates.sh diff --git a/stable/debian/30-tune-worker-processes.sh b/library/stable/debian/30-tune-worker-processes.sh similarity index 100% rename from stable/debian/30-tune-worker-processes.sh rename to library/stable/debian/30-tune-worker-processes.sh diff --git a/stable/debian/Dockerfile b/library/stable/debian/Dockerfile similarity index 100% rename from stable/debian/Dockerfile rename to library/stable/debian/Dockerfile diff --git a/stable/debian/docker-entrypoint.sh b/library/stable/debian/docker-entrypoint.sh similarity index 100% rename from stable/debian/docker-entrypoint.sh rename to library/stable/debian/docker-entrypoint.sh diff --git a/unprivileged/mainline/alpine-otel/Dockerfile b/unprivileged/mainline/alpine-otel/Dockerfile new file mode 100644 index 000000000..ef59609d0 --- /dev/null +++ b/unprivileged/mainline/alpine-otel/Dockerfile @@ -0,0 +1,86 @@ +# +# NOTE: THIS DOCKERFILE IS GENERATED VIA "update.sh" +# +# PLEASE DO NOT EDIT IT DIRECTLY. +# +ARG IMAGE=nginxinc/nginx-unprivileged:1.29.5-alpine +FROM $IMAGE + +ENV OTEL_VERSION=0.1.2 + +ARG UID=101 +ARG GID=101 + +USER root + +RUN set -x \ + && apkArch="$(cat /etc/apk/arch)" \ + && nginxPackages=" \ + nginx=${NGINX_VERSION}-r${PKG_RELEASE} \ + nginx-module-xslt=${NGINX_VERSION}-r${DYNPKG_RELEASE} \ + nginx-module-geoip=${NGINX_VERSION}-r${DYNPKG_RELEASE} \ + nginx-module-image-filter=${NGINX_VERSION}-r${DYNPKG_RELEASE} \ + nginx-module-njs=${NGINX_VERSION}.${NJS_VERSION}-r${NJS_RELEASE} \ + nginx-module-acme=${NGINX_VERSION}.${ACME_VERSION}-r${PKG_RELEASE} \ + nginx-module-otel=${NGINX_VERSION}.${OTEL_VERSION}-r${PKG_RELEASE} \ + " \ +# install prerequisites for public key and pkg-oss checks + && apk add --no-cache --virtual .checksum-deps \ + openssl \ + && case "$apkArch" in \ + x86_64|aarch64) \ +# arches officially built by upstream + apk add -X "https://nginx.org/packages/mainline/alpine/v$(egrep -o '^[0-9]+\.[0-9]+' /etc/alpine-release)/main" --no-cache $nginxPackages \ + ;; \ + *) \ +# we're on an architecture upstream doesn't officially build for +# let's build binaries from the published packaging sources + set -x \ + && tempDir="$(mktemp -d)" \ + && chown nobody:nobody $tempDir \ + && apk add --no-cache --virtual .build-deps \ + gcc \ + libc-dev \ + make \ + openssl-dev \ + pcre2-dev \ + zlib-dev \ + linux-headers \ + cmake \ + bash \ + alpine-sdk \ + findutils \ + curl \ + xz \ + protobuf-dev \ + grpc-dev \ + && su nobody -s /bin/sh -c " \ + export HOME=${tempDir} \ + && cd ${tempDir} \ + && curl -f -L -O https://github.com/nginx/pkg-oss/archive/${NGINX_VERSION}-${PKG_RELEASE}.tar.gz \ + && PKGOSSCHECKSUM=\"b8584eaa97130ba7743dfbb2a10f665d64cb54b864e2038d0fd298d24682fc05eb4472738430b15862dabc6f374917f1b9889117051a852d36d0a6c8bc898921 *${NGINX_VERSION}-${PKG_RELEASE}.tar.gz\" \ + && if [ \"\$(openssl sha512 -r ${NGINX_VERSION}-${PKG_RELEASE}.tar.gz)\" = \"\$PKGOSSCHECKSUM\" ]; then \ + echo \"pkg-oss tarball checksum verification succeeded!\"; \ + else \ + echo \"pkg-oss tarball checksum verification failed!\"; \ + exit 1; \ + fi \ + && tar xzvf ${NGINX_VERSION}-${PKG_RELEASE}.tar.gz \ + && cd pkg-oss-${NGINX_VERSION}-${PKG_RELEASE} \ + && cd alpine \ + && make module-otel \ + && apk index --allow-untrusted -o ${tempDir}/packages/alpine/${apkArch}/APKINDEX.tar.gz ${tempDir}/packages/alpine/${apkArch}/*.apk \ + && abuild-sign -k ${tempDir}/.abuild/abuild-key.rsa ${tempDir}/packages/alpine/${apkArch}/APKINDEX.tar.gz \ + " \ + && cp ${tempDir}/.abuild/abuild-key.rsa.pub /etc/apk/keys/ \ + && apk del --no-network .build-deps \ + && apk add -X ${tempDir}/packages/alpine/ --no-cache $nginxPackages \ + ;; \ + esac \ +# remove checksum deps + && apk del --no-network .checksum-deps \ +# if we have leftovers from building, let's purge them (including extra, unnecessary build deps) + && if [ -n "$tempDir" ]; then rm -rf "$tempDir"; fi \ + && if [ -f "/etc/apk/keys/abuild-key.rsa.pub" ]; then rm -f /etc/apk/keys/abuild-key.rsa.pub; fi + +USER $UID diff --git a/unprivileged/mainline/alpine-perl/Dockerfile b/unprivileged/mainline/alpine-perl/Dockerfile new file mode 100644 index 000000000..4045e6973 --- /dev/null +++ b/unprivileged/mainline/alpine-perl/Dockerfile @@ -0,0 +1,81 @@ +# +# NOTE: THIS DOCKERFILE IS GENERATED VIA "update.sh" +# +# PLEASE DO NOT EDIT IT DIRECTLY. +# +ARG IMAGE=nginxinc/nginx-unprivileged:1.29.5-alpine +FROM $IMAGE + +ARG UID=101 +ARG GID=101 + +USER root + +RUN set -x \ + && apkArch="$(cat /etc/apk/arch)" \ + && nginxPackages=" \ + nginx=${NGINX_VERSION}-r${PKG_RELEASE} \ + nginx-module-xslt=${NGINX_VERSION}-r${DYNPKG_RELEASE} \ + nginx-module-geoip=${NGINX_VERSION}-r${DYNPKG_RELEASE} \ + nginx-module-image-filter=${NGINX_VERSION}-r${DYNPKG_RELEASE} \ + nginx-module-perl=${NGINX_VERSION}-r${DYNPKG_RELEASE} \ + nginx-module-njs=${NGINX_VERSION}.${NJS_VERSION}-r${NJS_RELEASE} \ + nginx-module-acme=${NGINX_VERSION}.${ACME_VERSION}-r${PKG_RELEASE} \ + " \ +# install prerequisites for public key and pkg-oss checks + && apk add --no-cache --virtual .checksum-deps \ + openssl \ + && case "$apkArch" in \ + x86_64|aarch64) \ +# arches officially built by upstream + apk add -X "https://nginx.org/packages/mainline/alpine/v$(egrep -o '^[0-9]+\.[0-9]+' /etc/alpine-release)/main" --no-cache $nginxPackages \ + ;; \ + *) \ +# we're on an architecture upstream doesn't officially build for +# let's build binaries from the published packaging sources + set -x \ + && tempDir="$(mktemp -d)" \ + && chown nobody:nobody $tempDir \ + && apk add --no-cache --virtual .build-deps \ + gcc \ + libc-dev \ + make \ + openssl-dev \ + pcre2-dev \ + zlib-dev \ + linux-headers \ + perl-dev \ + bash \ + alpine-sdk \ + findutils \ + curl \ + && su nobody -s /bin/sh -c " \ + export HOME=${tempDir} \ + && cd ${tempDir} \ + && curl -f -L -O https://github.com/nginx/pkg-oss/archive/${NGINX_VERSION}-${PKG_RELEASE}.tar.gz \ + && PKGOSSCHECKSUM=\"b8584eaa97130ba7743dfbb2a10f665d64cb54b864e2038d0fd298d24682fc05eb4472738430b15862dabc6f374917f1b9889117051a852d36d0a6c8bc898921 *${NGINX_VERSION}-${PKG_RELEASE}.tar.gz\" \ + && if [ \"\$(openssl sha512 -r ${NGINX_VERSION}-${PKG_RELEASE}.tar.gz)\" = \"\$PKGOSSCHECKSUM\" ]; then \ + echo \"pkg-oss tarball checksum verification succeeded!\"; \ + else \ + echo \"pkg-oss tarball checksum verification failed!\"; \ + exit 1; \ + fi \ + && tar xzvf ${NGINX_VERSION}-${PKG_RELEASE}.tar.gz \ + && cd pkg-oss-${NGINX_VERSION}-${PKG_RELEASE} \ + && cd alpine \ + && make module-perl \ + && apk index --allow-untrusted -o ${tempDir}/packages/alpine/${apkArch}/APKINDEX.tar.gz ${tempDir}/packages/alpine/${apkArch}/*.apk \ + && abuild-sign -k ${tempDir}/.abuild/abuild-key.rsa ${tempDir}/packages/alpine/${apkArch}/APKINDEX.tar.gz \ + " \ + && cp ${tempDir}/.abuild/abuild-key.rsa.pub /etc/apk/keys/ \ + && apk del --no-network .build-deps \ + && apk add -X ${tempDir}/packages/alpine/ --no-cache $nginxPackages \ + ;; \ + esac \ +# remove checksum deps + && apk del --no-network .checksum-deps \ +# if we have leftovers from building, let's purge them (including extra, unnecessary build deps) + && if [ -n "$tempDir" ]; then rm -rf "$tempDir"; fi \ + && if [ -f "/etc/apk/keys/abuild-key.rsa.pub" ]; then rm -f /etc/apk/keys/abuild-key.rsa.pub; fi + +USER $UID diff --git a/unprivileged/mainline/alpine-slim/10-listen-on-ipv6-by-default.sh b/unprivileged/mainline/alpine-slim/10-listen-on-ipv6-by-default.sh new file mode 100755 index 000000000..176607941 --- /dev/null +++ b/unprivileged/mainline/alpine-slim/10-listen-on-ipv6-by-default.sh @@ -0,0 +1,67 @@ +#!/bin/sh +# vim:sw=4:ts=4:et + +set -e + +entrypoint_log() { + if [ -z "${NGINX_ENTRYPOINT_QUIET_LOGS:-}" ]; then + echo "$@" + fi +} + +ME=$(basename "$0") +DEFAULT_CONF_FILE="etc/nginx/conf.d/default.conf" + +# check if we have ipv6 available +if [ ! -f "/proc/net/if_inet6" ]; then + entrypoint_log "$ME: info: ipv6 not available" + exit 0 +fi + +if [ ! -f "/$DEFAULT_CONF_FILE" ]; then + entrypoint_log "$ME: info: /$DEFAULT_CONF_FILE is not a file or does not exist" + exit 0 +fi + +# check if the file can be modified, e.g. not on a r/o filesystem +touch /$DEFAULT_CONF_FILE 2>/dev/null || { entrypoint_log "$ME: info: can not modify /$DEFAULT_CONF_FILE (read-only file system?)"; exit 0; } + +# check if the file is already modified, e.g. on a container restart +grep -q "listen \[::\]:8080;" /$DEFAULT_CONF_FILE && { entrypoint_log "$ME: info: IPv6 listen already enabled"; exit 0; } + +if [ -f "/etc/os-release" ]; then + . /etc/os-release +else + entrypoint_log "$ME: info: can not guess the operating system" + exit 0 +fi + +entrypoint_log "$ME: info: Getting the checksum of /$DEFAULT_CONF_FILE" + +case "$ID" in + "debian") + CHECKSUM=$(dpkg-query --show --showformat='${Conffiles}\n' nginx | grep $DEFAULT_CONF_FILE | cut -d' ' -f 3) + echo "$CHECKSUM /$DEFAULT_CONF_FILE" | md5sum -c - >/dev/null 2>&1 || { + entrypoint_log "$ME: info: /$DEFAULT_CONF_FILE differs from the packaged version" + exit 0 + } + ;; + "alpine") + CHECKSUM=$(apk manifest nginx 2>/dev/null| grep $DEFAULT_CONF_FILE | cut -d' ' -f 1 | cut -d ':' -f 2) + echo "$CHECKSUM /$DEFAULT_CONF_FILE" | sha1sum -c - >/dev/null 2>&1 || { + entrypoint_log "$ME: info: /$DEFAULT_CONF_FILE differs from the packaged version" + exit 0 + } + ;; + *) + entrypoint_log "$ME: info: Unsupported distribution" + exit 0 + ;; +esac + +# enable ipv6 on default.conf listen sockets +sed -i -E 's,listen 8080;,listen 8080;\n listen [::]:8080;,' /$DEFAULT_CONF_FILE + +entrypoint_log "$ME: info: Enabled listen on IPv6 in /$DEFAULT_CONF_FILE" + +exit 0 diff --git a/unprivileged/mainline/alpine-slim/15-local-resolvers.envsh b/unprivileged/mainline/alpine-slim/15-local-resolvers.envsh new file mode 100755 index 000000000..e830ddacd --- /dev/null +++ b/unprivileged/mainline/alpine-slim/15-local-resolvers.envsh @@ -0,0 +1,15 @@ +#!/bin/sh +# vim:sw=2:ts=2:sts=2:et + +set -eu + +LC_ALL=C +PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin + +[ "${NGINX_ENTRYPOINT_LOCAL_RESOLVERS:-}" ] || return 0 + +NGINX_LOCAL_RESOLVERS=$(awk 'BEGIN{ORS=" "} $1=="nameserver" {if ($2 ~ ":") {print "["$2"]"} else {print $2}}' /etc/resolv.conf) + +NGINX_LOCAL_RESOLVERS="${NGINX_LOCAL_RESOLVERS% }" + +export NGINX_LOCAL_RESOLVERS diff --git a/unprivileged/mainline/alpine-slim/20-envsubst-on-templates.sh b/unprivileged/mainline/alpine-slim/20-envsubst-on-templates.sh new file mode 100755 index 000000000..6938405be --- /dev/null +++ b/unprivileged/mainline/alpine-slim/20-envsubst-on-templates.sh @@ -0,0 +1,78 @@ +#!/bin/sh + +set -e + +ME=$(basename "$0") + +entrypoint_log() { + if [ -z "${NGINX_ENTRYPOINT_QUIET_LOGS:-}" ]; then + echo "$@" + fi +} + +add_stream_block() { + local conffile="/etc/nginx/nginx.conf" + + if grep -q -E "\s*stream\s*\{" "$conffile"; then + entrypoint_log "$ME: $conffile contains a stream block; include $stream_output_dir/*.conf to enable stream templates" + else + # check if the file can be modified, e.g. not on a r/o filesystem + touch "$conffile" 2>/dev/null || { entrypoint_log "$ME: info: can not modify $conffile (read-only file system?)"; exit 0; } + entrypoint_log "$ME: Appending stream block to $conffile to include $stream_output_dir/*.conf" + cat << END >> "$conffile" +# added by "$ME" on "$(date)" +stream { + include $stream_output_dir/*.conf; +} +END + fi +} + +auto_envsubst() { + local template_dir="${NGINX_ENVSUBST_TEMPLATE_DIR:-/etc/nginx/templates}" + local suffix="${NGINX_ENVSUBST_TEMPLATE_SUFFIX:-.template}" + local output_dir="${NGINX_ENVSUBST_OUTPUT_DIR:-/etc/nginx/conf.d}" + local stream_suffix="${NGINX_ENVSUBST_STREAM_TEMPLATE_SUFFIX:-.stream-template}" + local stream_output_dir="${NGINX_ENVSUBST_STREAM_OUTPUT_DIR:-/etc/nginx/stream-conf.d}" + local filter="${NGINX_ENVSUBST_FILTER:-}" + + local template defined_envs relative_path output_path subdir + defined_envs=$(printf '${%s} ' $(awk "END { for (name in ENVIRON) { print ( name ~ /${filter}/ ) ? name : \"\" } }" < /dev/null )) + [ -d "$template_dir" ] || return 0 + if [ ! -w "$output_dir" ]; then + entrypoint_log "$ME: ERROR: $template_dir exists, but $output_dir is not writable" + return 0 + fi + find "$template_dir" -follow -type f -name "*$suffix" -print | while read -r template; do + relative_path="${template#"$template_dir/"}" + output_path="$output_dir/${relative_path%"$suffix"}" + subdir=$(dirname "$relative_path") + # create a subdirectory where the template file exists + mkdir -p "$output_dir/$subdir" + entrypoint_log "$ME: Running envsubst on $template to $output_path" + envsubst "$defined_envs" < "$template" > "$output_path" + done + + # Print the first file with the stream suffix, this will be false if there are none + if test -n "$(find "$template_dir" -name "*$stream_suffix" -print -quit)"; then + mkdir -p "$stream_output_dir" + if [ ! -w "$stream_output_dir" ]; then + entrypoint_log "$ME: ERROR: $template_dir exists, but $stream_output_dir is not writable" + return 0 + fi + add_stream_block + find "$template_dir" -follow -type f -name "*$stream_suffix" -print | while read -r template; do + relative_path="${template#"$template_dir/"}" + output_path="$stream_output_dir/${relative_path%"$stream_suffix"}" + subdir=$(dirname "$relative_path") + # create a subdirectory where the template file exists + mkdir -p "$stream_output_dir/$subdir" + entrypoint_log "$ME: Running envsubst on $template to $output_path" + envsubst "$defined_envs" < "$template" > "$output_path" + done + fi +} + +auto_envsubst + +exit 0 diff --git a/unprivileged/mainline/alpine-slim/30-tune-worker-processes.sh b/unprivileged/mainline/alpine-slim/30-tune-worker-processes.sh new file mode 100755 index 000000000..defb994f3 --- /dev/null +++ b/unprivileged/mainline/alpine-slim/30-tune-worker-processes.sh @@ -0,0 +1,188 @@ +#!/bin/sh +# vim:sw=2:ts=2:sts=2:et + +set -eu + +LC_ALL=C +ME=$(basename "$0") +PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin + +[ "${NGINX_ENTRYPOINT_WORKER_PROCESSES_AUTOTUNE:-}" ] || exit 0 + +touch /etc/nginx/nginx.conf 2>/dev/null || { echo >&2 "$ME: error: can not modify /etc/nginx/nginx.conf (read-only file system?)"; exit 0; } + +ceildiv() { + num=$1 + div=$2 + echo $(( (num + div - 1) / div )) +} + +get_cpuset() { + cpusetroot=$1 + cpusetfile=$2 + ncpu=0 + [ -f "$cpusetroot/$cpusetfile" ] || return 1 + for token in $( tr ',' ' ' < "$cpusetroot/$cpusetfile" ); do + case "$token" in + *-*) + count=$( seq $(echo "$token" | tr '-' ' ') | wc -l ) + ncpu=$(( ncpu+count )) + ;; + *) + ncpu=$(( ncpu+1 )) + ;; + esac + done + echo "$ncpu" +} + +get_quota() { + cpuroot=$1 + ncpu=0 + [ -f "$cpuroot/cpu.cfs_quota_us" ] || return 1 + [ -f "$cpuroot/cpu.cfs_period_us" ] || return 1 + cfs_quota=$( cat "$cpuroot/cpu.cfs_quota_us" ) + cfs_period=$( cat "$cpuroot/cpu.cfs_period_us" ) + [ "$cfs_quota" = "-1" ] && return 1 + [ "$cfs_period" = "0" ] && return 1 + ncpu=$( ceildiv "$cfs_quota" "$cfs_period" ) + [ "$ncpu" -gt 0 ] || return 1 + echo "$ncpu" +} + +get_quota_v2() { + cpuroot=$1 + ncpu=0 + [ -f "$cpuroot/cpu.max" ] || return 1 + cfs_quota=$( cut -d' ' -f 1 < "$cpuroot/cpu.max" ) + cfs_period=$( cut -d' ' -f 2 < "$cpuroot/cpu.max" ) + [ "$cfs_quota" = "max" ] && return 1 + [ "$cfs_period" = "0" ] && return 1 + ncpu=$( ceildiv "$cfs_quota" "$cfs_period" ) + [ "$ncpu" -gt 0 ] || return 1 + echo "$ncpu" +} + +get_cgroup_v1_path() { + needle=$1 + found= + foundroot= + mountpoint= + + [ -r "/proc/self/mountinfo" ] || return 1 + [ -r "/proc/self/cgroup" ] || return 1 + + while IFS= read -r line; do + case "$needle" in + "cpuset") + case "$line" in + *cpuset*) + found=$( echo "$line" | cut -d ' ' -f 4,5 ) + break + ;; + esac + ;; + "cpu") + case "$line" in + *cpuset*) + ;; + *cpu,cpuacct*|*cpuacct,cpu|*cpuacct*|*cpu*) + found=$( echo "$line" | cut -d ' ' -f 4,5 ) + break + ;; + esac + esac + done << __EOF__ +$( grep -F -- '- cgroup ' /proc/self/mountinfo ) +__EOF__ + + while IFS= read -r line; do + controller=$( echo "$line" | cut -d: -f 2 ) + case "$needle" in + "cpuset") + case "$controller" in + cpuset) + mountpoint=$( echo "$line" | cut -d: -f 3 ) + break + ;; + esac + ;; + "cpu") + case "$controller" in + cpu,cpuacct|cpuacct,cpu|cpuacct|cpu) + mountpoint=$( echo "$line" | cut -d: -f 3 ) + break + ;; + esac + ;; + esac +done << __EOF__ +$( grep -F -- 'cpu' /proc/self/cgroup ) +__EOF__ + + case "${found%% *}" in + "/") + foundroot="${found##* }$mountpoint" + ;; + "$mountpoint") + foundroot="${found##* }" + ;; + esac + echo "$foundroot" +} + +get_cgroup_v2_path() { + found= + foundroot= + mountpoint= + + [ -r "/proc/self/mountinfo" ] || return 1 + [ -r "/proc/self/cgroup" ] || return 1 + + while IFS= read -r line; do + found=$( echo "$line" | cut -d ' ' -f 4,5 ) + done << __EOF__ +$( grep -F -- '- cgroup2 ' /proc/self/mountinfo ) +__EOF__ + + while IFS= read -r line; do + mountpoint=$( echo "$line" | cut -d: -f 3 ) +done << __EOF__ +$( grep -F -- '0::' /proc/self/cgroup ) +__EOF__ + + case "${found%% *}" in + "") + return 1 + ;; + "/") + foundroot="${found##* }$mountpoint" + ;; + "$mountpoint" | /../*) + foundroot="${found##* }" + ;; + esac + echo "$foundroot" +} + +ncpu_online=$( getconf _NPROCESSORS_ONLN ) +ncpu_cpuset= +ncpu_quota= +ncpu_cpuset_v2= +ncpu_quota_v2= + +cpuset=$( get_cgroup_v1_path "cpuset" ) && ncpu_cpuset=$( get_cpuset "$cpuset" "cpuset.effective_cpus" ) || ncpu_cpuset=$ncpu_online +cpu=$( get_cgroup_v1_path "cpu" ) && ncpu_quota=$( get_quota "$cpu" ) || ncpu_quota=$ncpu_online +cgroup_v2=$( get_cgroup_v2_path ) && ncpu_cpuset_v2=$( get_cpuset "$cgroup_v2" "cpuset.cpus.effective" ) || ncpu_cpuset_v2=$ncpu_online +cgroup_v2=$( get_cgroup_v2_path ) && ncpu_quota_v2=$( get_quota_v2 "$cgroup_v2" ) || ncpu_quota_v2=$ncpu_online + +ncpu=$( printf "%s\n%s\n%s\n%s\n%s\n" \ + "$ncpu_online" \ + "$ncpu_cpuset" \ + "$ncpu_quota" \ + "$ncpu_cpuset_v2" \ + "$ncpu_quota_v2" \ + | sort -n \ + | head -n 1 ) + +sed -i.bak -r 's/^(worker_processes)(.*)$/# Commented out by '"$ME"' on '"$(date)"'\n#\1\2\n\1 '"$ncpu"';/' /etc/nginx/nginx.conf diff --git a/unprivileged/mainline/alpine-slim/Dockerfile b/unprivileged/mainline/alpine-slim/Dockerfile new file mode 100644 index 000000000..d1b3c0dd3 --- /dev/null +++ b/unprivileged/mainline/alpine-slim/Dockerfile @@ -0,0 +1,125 @@ +# +# NOTE: THIS DOCKERFILE IS GENERATED VIA "update.sh" +# +# PLEASE DO NOT EDIT IT DIRECTLY. +# +ARG IMAGE=alpine:3.23 +FROM $IMAGE + +LABEL maintainer="NGINX Docker Maintainers " + +ENV NGINX_VERSION=1.29.5 +ENV PKG_RELEASE=1 +ENV DYNPKG_RELEASE=1 + +ARG UID=101 +ARG GID=101 + +RUN set -x \ +# create nginx user/group first, to be consistent throughout docker variants + && addgroup -g $GID -S nginx || true \ + && adduser -S -D -H -u $UID -h /var/cache/nginx -s /sbin/nologin -G nginx -g nginx nginx || true \ + && apkArch="$(cat /etc/apk/arch)" \ + && nginxPackages=" \ + nginx=${NGINX_VERSION}-r${PKG_RELEASE} \ + " \ +# install prerequisites for public key and pkg-oss checks + && apk add --no-cache --virtual .checksum-deps \ + openssl \ + && case "$apkArch" in \ + x86_64|aarch64) \ +# arches officially built by upstream + set -x \ + && KEY_SHA512="e09fa32f0a0eab2b879ccbbc4d0e4fb9751486eedda75e35fac65802cc9faa266425edf83e261137a2f4d16281ce2c1a5f4502930fe75154723da014214f0655" \ + && wget -O /tmp/nginx_signing.rsa.pub https://nginx.org/keys/nginx_signing.rsa.pub \ + && if echo "$KEY_SHA512 */tmp/nginx_signing.rsa.pub" | sha512sum -c -; then \ + echo "key verification succeeded!"; \ + mv /tmp/nginx_signing.rsa.pub /etc/apk/keys/; \ + else \ + echo "key verification failed!"; \ + exit 1; \ + fi \ + && apk add -X "https://nginx.org/packages/mainline/alpine/v$(egrep -o '^[0-9]+\.[0-9]+' /etc/alpine-release)/main" --no-cache $nginxPackages \ + ;; \ + *) \ +# we're on an architecture upstream doesn't officially build for +# let's build binaries from the published packaging sources + set -x \ + && tempDir="$(mktemp -d)" \ + && chown nobody:nobody $tempDir \ + && apk add --no-cache --virtual .build-deps \ + gcc \ + libc-dev \ + make \ + openssl-dev \ + pcre2-dev \ + zlib-dev \ + linux-headers \ + bash \ + alpine-sdk \ + findutils \ + curl \ + && su nobody -s /bin/sh -c " \ + export HOME=${tempDir} \ + && cd ${tempDir} \ + && curl -f -L -O https://github.com/nginx/pkg-oss/archive/${NGINX_VERSION}-${PKG_RELEASE}.tar.gz \ + && PKGOSSCHECKSUM=\"b8584eaa97130ba7743dfbb2a10f665d64cb54b864e2038d0fd298d24682fc05eb4472738430b15862dabc6f374917f1b9889117051a852d36d0a6c8bc898921 *${NGINX_VERSION}-${PKG_RELEASE}.tar.gz\" \ + && if [ \"\$(openssl sha512 -r ${NGINX_VERSION}-${PKG_RELEASE}.tar.gz)\" = \"\$PKGOSSCHECKSUM\" ]; then \ + echo \"pkg-oss tarball checksum verification succeeded!\"; \ + else \ + echo \"pkg-oss tarball checksum verification failed!\"; \ + exit 1; \ + fi \ + && tar xzvf ${NGINX_VERSION}-${PKG_RELEASE}.tar.gz \ + && cd pkg-oss-${NGINX_VERSION}-${PKG_RELEASE} \ + && cd alpine \ + && make base \ + && apk index --allow-untrusted -o ${tempDir}/packages/alpine/${apkArch}/APKINDEX.tar.gz ${tempDir}/packages/alpine/${apkArch}/*.apk \ + && abuild-sign -k ${tempDir}/.abuild/abuild-key.rsa ${tempDir}/packages/alpine/${apkArch}/APKINDEX.tar.gz \ + " \ + && cp ${tempDir}/.abuild/abuild-key.rsa.pub /etc/apk/keys/ \ + && apk del --no-network .build-deps \ + && apk add -X ${tempDir}/packages/alpine/ --no-cache $nginxPackages \ + ;; \ + esac \ +# remove checksum deps + && apk del --no-network .checksum-deps \ +# if we have leftovers from building, let's purge them (including extra, unnecessary build deps) + && if [ -n "$tempDir" ]; then rm -rf "$tempDir"; fi \ + && if [ -f "/etc/apk/keys/abuild-key.rsa.pub" ]; then rm -f /etc/apk/keys/abuild-key.rsa.pub; fi \ +# Add `envsubst` for templating environment variables + && apk add --no-cache gettext-envsubst \ +# Bring in tzdata so users could set the timezones through the environment +# variables + && apk add --no-cache tzdata \ +# forward request and error logs to docker log collector + && ln -sf /dev/stdout /var/log/nginx/access.log \ + && ln -sf /dev/stderr /var/log/nginx/error.log \ +# create a docker-entrypoint.d directory + && mkdir /docker-entrypoint.d + +# implement changes required to run NGINX as an unprivileged user +RUN sed -i 's,listen 80;,listen 8080;,' /etc/nginx/conf.d/default.conf \ + && sed -i '/user nginx;/d' /etc/nginx/nginx.conf \ + && sed -i 's,\(/var\)\{0\,1\}/run/nginx.pid,/tmp/nginx.pid,' /etc/nginx/nginx.conf \ + && sed -i "/^http {/a \ proxy_temp_path /tmp/proxy_temp;\n client_body_temp_path /tmp/client_temp;\n fastcgi_temp_path /tmp/fastcgi_temp;\n uwsgi_temp_path /tmp/uwsgi_temp;\n scgi_temp_path /tmp/scgi_temp;\n" /etc/nginx/nginx.conf \ +# nginx user must own the cache and etc directory to write cache and tweak the nginx config + && chown -R $UID:0 /var/cache/nginx \ + && chmod -R g+w /var/cache/nginx \ + && chown -R $UID:0 /etc/nginx \ + && chmod -R g+w /etc/nginx + +COPY docker-entrypoint.sh / +COPY 10-listen-on-ipv6-by-default.sh /docker-entrypoint.d +COPY 15-local-resolvers.envsh /docker-entrypoint.d +COPY 20-envsubst-on-templates.sh /docker-entrypoint.d +COPY 30-tune-worker-processes.sh /docker-entrypoint.d +ENTRYPOINT ["/docker-entrypoint.sh"] + +EXPOSE 8080 + +STOPSIGNAL SIGQUIT + +USER $UID + +CMD ["nginx", "-g", "daemon off;"] diff --git a/unprivileged/mainline/alpine-slim/docker-entrypoint.sh b/unprivileged/mainline/alpine-slim/docker-entrypoint.sh new file mode 100755 index 000000000..8ea04f217 --- /dev/null +++ b/unprivileged/mainline/alpine-slim/docker-entrypoint.sh @@ -0,0 +1,47 @@ +#!/bin/sh +# vim:sw=4:ts=4:et + +set -e + +entrypoint_log() { + if [ -z "${NGINX_ENTRYPOINT_QUIET_LOGS:-}" ]; then + echo "$@" + fi +} + +if [ "$1" = "nginx" ] || [ "$1" = "nginx-debug" ]; then + if /usr/bin/find "/docker-entrypoint.d/" -mindepth 1 -maxdepth 1 -type f -print -quit 2>/dev/null | read v; then + entrypoint_log "$0: /docker-entrypoint.d/ is not empty, will attempt to perform configuration" + + entrypoint_log "$0: Looking for shell scripts in /docker-entrypoint.d/" + find "/docker-entrypoint.d/" -follow -type f -print | sort -V | while read -r f; do + case "$f" in + *.envsh) + if [ -x "$f" ]; then + entrypoint_log "$0: Sourcing $f"; + . "$f" + else + # warn on shell scripts without exec bit + entrypoint_log "$0: Ignoring $f, not executable"; + fi + ;; + *.sh) + if [ -x "$f" ]; then + entrypoint_log "$0: Launching $f"; + "$f" + else + # warn on shell scripts without exec bit + entrypoint_log "$0: Ignoring $f, not executable"; + fi + ;; + *) entrypoint_log "$0: Ignoring $f";; + esac + done + + entrypoint_log "$0: Configuration complete; ready for start up" + else + entrypoint_log "$0: No files found in /docker-entrypoint.d/, skipping configuration" + fi +fi + +exec "$@" diff --git a/unprivileged/mainline/alpine/Dockerfile b/unprivileged/mainline/alpine/Dockerfile new file mode 100644 index 000000000..703344389 --- /dev/null +++ b/unprivileged/mainline/alpine/Dockerfile @@ -0,0 +1,88 @@ +# +# NOTE: THIS DOCKERFILE IS GENERATED VIA "update.sh" +# +# PLEASE DO NOT EDIT IT DIRECTLY. +# +ARG IMAGE=nginxinc/nginx-unprivileged:1.29.5-alpine-slim +FROM $IMAGE + +ENV NJS_VERSION=0.9.5 +ENV NJS_RELEASE=1 + +ARG UID=101 +ARG GID=101 + +USER root + +RUN set -x \ + && apkArch="$(cat /etc/apk/arch)" \ + && nginxPackages=" \ + nginx=${NGINX_VERSION}-r${PKG_RELEASE} \ + nginx-module-xslt=${NGINX_VERSION}-r${DYNPKG_RELEASE} \ + nginx-module-geoip=${NGINX_VERSION}-r${DYNPKG_RELEASE} \ + nginx-module-image-filter=${NGINX_VERSION}-r${DYNPKG_RELEASE} \ + nginx-module-njs=${NGINX_VERSION}.${NJS_VERSION}-r${NJS_RELEASE} \ + nginx-module-acme=${NGINX_VERSION}.${ACME_VERSION}-r${PKG_RELEASE} \ + " \ +# install prerequisites for public key and pkg-oss checks + && apk add --no-cache --virtual .checksum-deps \ + openssl \ + && case "$apkArch" in \ + x86_64|aarch64) \ +# arches officially built by upstream + apk add -X "https://nginx.org/packages/mainline/alpine/v$(egrep -o '^[0-9]+\.[0-9]+' /etc/alpine-release)/main" --no-cache $nginxPackages \ + ;; \ + *) \ +# we're on an architecture upstream doesn't officially build for +# let's build binaries from the published packaging sources + set -x \ + && tempDir="$(mktemp -d)" \ + && chown nobody:nobody $tempDir \ + && apk add --no-cache --virtual .build-deps \ + gcc \ + libc-dev \ + make \ + openssl-dev \ + pcre2-dev \ + zlib-dev \ + linux-headers \ + libxslt-dev \ + gd-dev \ + geoip-dev \ + libedit-dev \ + bash \ + alpine-sdk \ + findutils \ + curl \ + && su nobody -s /bin/sh -c " \ + export HOME=${tempDir} \ + && cd ${tempDir} \ + && curl -f -L -O https://github.com/nginx/pkg-oss/archive/${NGINX_VERSION}-${PKG_RELEASE}.tar.gz \ + && PKGOSSCHECKSUM=\"b8584eaa97130ba7743dfbb2a10f665d64cb54b864e2038d0fd298d24682fc05eb4472738430b15862dabc6f374917f1b9889117051a852d36d0a6c8bc898921 *${NGINX_VERSION}-${PKG_RELEASE}.tar.gz\" \ + && if [ \"\$(openssl sha512 -r ${NGINX_VERSION}-${PKG_RELEASE}.tar.gz)\" = \"\$PKGOSSCHECKSUM\" ]; then \ + echo \"pkg-oss tarball checksum verification succeeded!\"; \ + else \ + echo \"pkg-oss tarball checksum verification failed!\"; \ + exit 1; \ + fi \ + && tar xzvf ${NGINX_VERSION}-${PKG_RELEASE}.tar.gz \ + && cd pkg-oss-${NGINX_VERSION}-${PKG_RELEASE} \ + && cd alpine \ + && make module-geoip module-image-filter module-njs module-xslt module-acme \ + && apk index --allow-untrusted -o ${tempDir}/packages/alpine/${apkArch}/APKINDEX.tar.gz ${tempDir}/packages/alpine/${apkArch}/*.apk \ + && abuild-sign -k ${tempDir}/.abuild/abuild-key.rsa ${tempDir}/packages/alpine/${apkArch}/APKINDEX.tar.gz \ + " \ + && cp ${tempDir}/.abuild/abuild-key.rsa.pub /etc/apk/keys/ \ + && apk del --no-network .build-deps \ + && apk add -X ${tempDir}/packages/alpine/ --no-cache $nginxPackages \ + ;; \ + esac \ +# remove checksum deps + && apk del --no-network .checksum-deps \ +# if we have leftovers from building, let's purge them (including extra, unnecessary build deps) + && if [ -n "$tempDir" ]; then rm -rf "$tempDir"; fi \ + && if [ -f "/etc/apk/keys/abuild-key.rsa.pub" ]; then rm -f /etc/apk/keys/abuild-key.rsa.pub; fi \ +# Bring in curl and ca-certificates to make registering on DNS SD easier + && apk add --no-cache curl ca-certificates + +USER $UID diff --git a/unprivileged/mainline/debian-otel/Dockerfile b/unprivileged/mainline/debian-otel/Dockerfile new file mode 100644 index 000000000..8fca4b697 --- /dev/null +++ b/unprivileged/mainline/debian-otel/Dockerfile @@ -0,0 +1,109 @@ +# +# NOTE: THIS DOCKERFILE IS GENERATED VIA "update.sh" +# +# PLEASE DO NOT EDIT IT DIRECTLY. +# +ARG IMAGE=nginxinc/nginx-unprivileged:1.29.5 +FROM $IMAGE + +ENV OTEL_VERSION=0.1.2 + +ARG UID=101 +ARG GID=101 + +USER root + +RUN set -x; \ + NGINX_GPGKEY_PATH=/etc/apt/keyrings/nginx-archive-keyring.gpg; \ + dpkgArch="$(dpkg --print-architecture)" \ + && nginxPackages=" \ + nginx=${NGINX_VERSION}-${PKG_RELEASE} \ + nginx-module-xslt=${NGINX_VERSION}-${DYNPKG_RELEASE} \ + nginx-module-geoip=${NGINX_VERSION}-${DYNPKG_RELEASE} \ + nginx-module-image-filter=${NGINX_VERSION}-${DYNPKG_RELEASE} \ + nginx-module-njs=${NGINX_VERSION}+${NJS_VERSION}-${NJS_RELEASE} \ + nginx-module-acme=${NGINX_VERSION}+${ACME_VERSION}-${PKG_RELEASE} \ + nginx-module-otel=${NGINX_VERSION}+${OTEL_VERSION}-${PKG_RELEASE} \ + " \ + && case "$dpkgArch" in \ + amd64|arm64) \ +# arches officially built by upstream + echo "deb [signed-by=$NGINX_GPGKEY_PATH] https://nginx.org/packages/mainline/debian/ trixie nginx" >> /etc/apt/sources.list.d/nginx.list \ + && apt-get update \ + ;; \ + *) \ +# we're on an architecture upstream doesn't officially build for +# let's build binaries from the published packaging sources +# new directory for storing sources and .deb files + tempDir="$(mktemp -d)" \ + && chmod 777 "$tempDir" \ +# (777 to ensure APT's "_apt" user can access it too) + \ +# save list of currently-installed packages so build dependencies can be cleanly removed later + && savedAptMark="$(apt-mark showmanual)" \ + \ +# build .deb files from upstream's packaging sources + && apt-get update \ + && apt-get install --no-install-recommends --no-install-suggests -y \ + curl \ + devscripts \ + equivs \ + git \ + libxml2-utils \ + lsb-release \ + xsltproc \ + && ( \ + cd "$tempDir" \ + && REVISION="${NGINX_VERSION}-${PKG_RELEASE}" \ + && REVISION=${REVISION%~*} \ + && curl -f -L -O https://github.com/nginx/pkg-oss/archive/${REVISION}.tar.gz \ + && PKGOSSCHECKSUM="b8584eaa97130ba7743dfbb2a10f665d64cb54b864e2038d0fd298d24682fc05eb4472738430b15862dabc6f374917f1b9889117051a852d36d0a6c8bc898921 *${REVISION}.tar.gz" \ + && if [ "$(openssl sha512 -r ${REVISION}.tar.gz)" = "$PKGOSSCHECKSUM" ]; then \ + echo "pkg-oss tarball checksum verification succeeded!"; \ + else \ + echo "pkg-oss tarball checksum verification failed!"; \ + exit 1; \ + fi \ + && tar xzvf ${REVISION}.tar.gz \ + && cd pkg-oss-${REVISION} \ + && cd debian \ + && for target in module-otel; do \ + make rules-$target; \ + mk-build-deps --install --tool="apt-get -o Debug::pkgProblemResolver=yes --no-install-recommends --yes" \ + debuild-$target/nginx-$NGINX_VERSION/debian/control; \ + done \ + && make module-otel \ + ) \ +# we don't remove APT lists here because they get re-downloaded and removed later + \ +# reset apt-mark's "manual" list so that "purge --auto-remove" will remove all build dependencies +# (which is done after we install the built packages so we don't have to redownload any overlapping dependencies) + && apt-mark showmanual | xargs apt-mark auto > /dev/null \ + && { [ -z "$savedAptMark" ] || apt-mark manual $savedAptMark; } \ + \ +# create a temporary local APT repo to install from (so that dependency resolution can be handled by APT, as it should be) + && ls -lAFh "$tempDir" \ + && ( cd "$tempDir" && dpkg-scanpackages . > Packages ) \ + && grep '^Package: ' "$tempDir/Packages" \ + && echo "deb [ trusted=yes ] file://$tempDir ./" > /etc/apt/sources.list.d/temp.list \ +# work around the following APT issue by using "Acquire::GzipIndexes=false" (overriding "/etc/apt/apt.conf.d/docker-gzip-indexes") +# Could not open file /var/lib/apt/lists/partial/_tmp_tmp.ODWljpQfkE_._Packages - open (13: Permission denied) +# ... +# E: Failed to fetch store:/var/lib/apt/lists/partial/_tmp_tmp.ODWljpQfkE_._Packages Could not open file /var/lib/apt/lists/partial/_tmp_tmp.ODWljpQfkE_._Packages - open (13: Permission denied) + && apt-get -o Acquire::GzipIndexes=false update \ + ;; \ + esac \ + \ + && apt-get install --no-install-recommends --no-install-suggests -y \ + $nginxPackages \ + gettext-base \ + curl \ + && apt-get remove --purge --auto-remove -y && rm -rf /var/lib/apt/lists/* /etc/apt/sources.list.d/nginx.list \ + \ +# if we have leftovers from building, let's purge them (including extra, unnecessary build deps) + && if [ -n "$tempDir" ]; then \ + apt-get purge -y --auto-remove \ + && rm -rf "$tempDir" /etc/apt/sources.list.d/temp.list; \ + fi + +USER $UID diff --git a/unprivileged/mainline/debian-perl/Dockerfile b/unprivileged/mainline/debian-perl/Dockerfile new file mode 100644 index 000000000..b0819b75f --- /dev/null +++ b/unprivileged/mainline/debian-perl/Dockerfile @@ -0,0 +1,107 @@ +# +# NOTE: THIS DOCKERFILE IS GENERATED VIA "update.sh" +# +# PLEASE DO NOT EDIT IT DIRECTLY. +# +ARG IMAGE=nginxinc/nginx-unprivileged:1.29.5 +FROM $IMAGE + +ARG UID=101 +ARG GID=101 + +USER root + +RUN set -x; \ + NGINX_GPGKEY_PATH=/etc/apt/keyrings/nginx-archive-keyring.gpg; \ + dpkgArch="$(dpkg --print-architecture)" \ + && nginxPackages=" \ + nginx=${NGINX_VERSION}-${PKG_RELEASE} \ + nginx-module-xslt=${NGINX_VERSION}-${DYNPKG_RELEASE} \ + nginx-module-geoip=${NGINX_VERSION}-${DYNPKG_RELEASE} \ + nginx-module-image-filter=${NGINX_VERSION}-${DYNPKG_RELEASE} \ + nginx-module-perl=${NGINX_VERSION}-${DYNPKG_RELEASE} \ + nginx-module-njs=${NGINX_VERSION}+${NJS_VERSION}-${NJS_RELEASE} \ + nginx-module-acme=${NGINX_VERSION}+${ACME_VERSION}-${PKG_RELEASE} \ + " \ + && case "$dpkgArch" in \ + amd64|arm64) \ +# arches officially built by upstream + echo "deb [signed-by=$NGINX_GPGKEY_PATH] https://nginx.org/packages/mainline/debian/ trixie nginx" >> /etc/apt/sources.list.d/nginx.list \ + && apt-get update \ + ;; \ + *) \ +# we're on an architecture upstream doesn't officially build for +# let's build binaries from the published packaging sources +# new directory for storing sources and .deb files + tempDir="$(mktemp -d)" \ + && chmod 777 "$tempDir" \ +# (777 to ensure APT's "_apt" user can access it too) + \ +# save list of currently-installed packages so build dependencies can be cleanly removed later + && savedAptMark="$(apt-mark showmanual)" \ + \ +# build .deb files from upstream's packaging sources + && apt-get update \ + && apt-get install --no-install-recommends --no-install-suggests -y \ + curl \ + devscripts \ + equivs \ + git \ + libxml2-utils \ + lsb-release \ + xsltproc \ + && ( \ + cd "$tempDir" \ + && REVISION="${NGINX_VERSION}-${PKG_RELEASE}" \ + && REVISION=${REVISION%~*} \ + && curl -f -L -O https://github.com/nginx/pkg-oss/archive/${REVISION}.tar.gz \ + && PKGOSSCHECKSUM="b8584eaa97130ba7743dfbb2a10f665d64cb54b864e2038d0fd298d24682fc05eb4472738430b15862dabc6f374917f1b9889117051a852d36d0a6c8bc898921 *${REVISION}.tar.gz" \ + && if [ "$(openssl sha512 -r ${REVISION}.tar.gz)" = "$PKGOSSCHECKSUM" ]; then \ + echo "pkg-oss tarball checksum verification succeeded!"; \ + else \ + echo "pkg-oss tarball checksum verification failed!"; \ + exit 1; \ + fi \ + && tar xzvf ${REVISION}.tar.gz \ + && cd pkg-oss-${REVISION} \ + && cd debian \ + && for target in module-perl; do \ + make rules-$target; \ + mk-build-deps --install --tool="apt-get -o Debug::pkgProblemResolver=yes --no-install-recommends --yes" \ + debuild-$target/nginx-$NGINX_VERSION/debian/control; \ + done \ + && make module-perl \ + ) \ +# we don't remove APT lists here because they get re-downloaded and removed later + \ +# reset apt-mark's "manual" list so that "purge --auto-remove" will remove all build dependencies +# (which is done after we install the built packages so we don't have to redownload any overlapping dependencies) + && apt-mark showmanual | xargs apt-mark auto > /dev/null \ + && { [ -z "$savedAptMark" ] || apt-mark manual $savedAptMark; } \ + \ +# create a temporary local APT repo to install from (so that dependency resolution can be handled by APT, as it should be) + && ls -lAFh "$tempDir" \ + && ( cd "$tempDir" && dpkg-scanpackages . > Packages ) \ + && grep '^Package: ' "$tempDir/Packages" \ + && echo "deb [ trusted=yes ] file://$tempDir ./" > /etc/apt/sources.list.d/temp.list \ +# work around the following APT issue by using "Acquire::GzipIndexes=false" (overriding "/etc/apt/apt.conf.d/docker-gzip-indexes") +# Could not open file /var/lib/apt/lists/partial/_tmp_tmp.ODWljpQfkE_._Packages - open (13: Permission denied) +# ... +# E: Failed to fetch store:/var/lib/apt/lists/partial/_tmp_tmp.ODWljpQfkE_._Packages Could not open file /var/lib/apt/lists/partial/_tmp_tmp.ODWljpQfkE_._Packages - open (13: Permission denied) + && apt-get -o Acquire::GzipIndexes=false update \ + ;; \ + esac \ + \ + && apt-get install --no-install-recommends --no-install-suggests -y \ + $nginxPackages \ + gettext-base \ + curl \ + && apt-get remove --purge --auto-remove -y && rm -rf /var/lib/apt/lists/* /etc/apt/sources.list.d/nginx.list \ + \ +# if we have leftovers from building, let's purge them (including extra, unnecessary build deps) + && if [ -n "$tempDir" ]; then \ + apt-get purge -y --auto-remove \ + && rm -rf "$tempDir" /etc/apt/sources.list.d/temp.list; \ + fi + +USER $UID diff --git a/unprivileged/mainline/debian/10-listen-on-ipv6-by-default.sh b/unprivileged/mainline/debian/10-listen-on-ipv6-by-default.sh new file mode 100755 index 000000000..176607941 --- /dev/null +++ b/unprivileged/mainline/debian/10-listen-on-ipv6-by-default.sh @@ -0,0 +1,67 @@ +#!/bin/sh +# vim:sw=4:ts=4:et + +set -e + +entrypoint_log() { + if [ -z "${NGINX_ENTRYPOINT_QUIET_LOGS:-}" ]; then + echo "$@" + fi +} + +ME=$(basename "$0") +DEFAULT_CONF_FILE="etc/nginx/conf.d/default.conf" + +# check if we have ipv6 available +if [ ! -f "/proc/net/if_inet6" ]; then + entrypoint_log "$ME: info: ipv6 not available" + exit 0 +fi + +if [ ! -f "/$DEFAULT_CONF_FILE" ]; then + entrypoint_log "$ME: info: /$DEFAULT_CONF_FILE is not a file or does not exist" + exit 0 +fi + +# check if the file can be modified, e.g. not on a r/o filesystem +touch /$DEFAULT_CONF_FILE 2>/dev/null || { entrypoint_log "$ME: info: can not modify /$DEFAULT_CONF_FILE (read-only file system?)"; exit 0; } + +# check if the file is already modified, e.g. on a container restart +grep -q "listen \[::\]:8080;" /$DEFAULT_CONF_FILE && { entrypoint_log "$ME: info: IPv6 listen already enabled"; exit 0; } + +if [ -f "/etc/os-release" ]; then + . /etc/os-release +else + entrypoint_log "$ME: info: can not guess the operating system" + exit 0 +fi + +entrypoint_log "$ME: info: Getting the checksum of /$DEFAULT_CONF_FILE" + +case "$ID" in + "debian") + CHECKSUM=$(dpkg-query --show --showformat='${Conffiles}\n' nginx | grep $DEFAULT_CONF_FILE | cut -d' ' -f 3) + echo "$CHECKSUM /$DEFAULT_CONF_FILE" | md5sum -c - >/dev/null 2>&1 || { + entrypoint_log "$ME: info: /$DEFAULT_CONF_FILE differs from the packaged version" + exit 0 + } + ;; + "alpine") + CHECKSUM=$(apk manifest nginx 2>/dev/null| grep $DEFAULT_CONF_FILE | cut -d' ' -f 1 | cut -d ':' -f 2) + echo "$CHECKSUM /$DEFAULT_CONF_FILE" | sha1sum -c - >/dev/null 2>&1 || { + entrypoint_log "$ME: info: /$DEFAULT_CONF_FILE differs from the packaged version" + exit 0 + } + ;; + *) + entrypoint_log "$ME: info: Unsupported distribution" + exit 0 + ;; +esac + +# enable ipv6 on default.conf listen sockets +sed -i -E 's,listen 8080;,listen 8080;\n listen [::]:8080;,' /$DEFAULT_CONF_FILE + +entrypoint_log "$ME: info: Enabled listen on IPv6 in /$DEFAULT_CONF_FILE" + +exit 0 diff --git a/unprivileged/mainline/debian/15-local-resolvers.envsh b/unprivileged/mainline/debian/15-local-resolvers.envsh new file mode 100755 index 000000000..e830ddacd --- /dev/null +++ b/unprivileged/mainline/debian/15-local-resolvers.envsh @@ -0,0 +1,15 @@ +#!/bin/sh +# vim:sw=2:ts=2:sts=2:et + +set -eu + +LC_ALL=C +PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin + +[ "${NGINX_ENTRYPOINT_LOCAL_RESOLVERS:-}" ] || return 0 + +NGINX_LOCAL_RESOLVERS=$(awk 'BEGIN{ORS=" "} $1=="nameserver" {if ($2 ~ ":") {print "["$2"]"} else {print $2}}' /etc/resolv.conf) + +NGINX_LOCAL_RESOLVERS="${NGINX_LOCAL_RESOLVERS% }" + +export NGINX_LOCAL_RESOLVERS diff --git a/unprivileged/mainline/debian/20-envsubst-on-templates.sh b/unprivileged/mainline/debian/20-envsubst-on-templates.sh new file mode 100755 index 000000000..6938405be --- /dev/null +++ b/unprivileged/mainline/debian/20-envsubst-on-templates.sh @@ -0,0 +1,78 @@ +#!/bin/sh + +set -e + +ME=$(basename "$0") + +entrypoint_log() { + if [ -z "${NGINX_ENTRYPOINT_QUIET_LOGS:-}" ]; then + echo "$@" + fi +} + +add_stream_block() { + local conffile="/etc/nginx/nginx.conf" + + if grep -q -E "\s*stream\s*\{" "$conffile"; then + entrypoint_log "$ME: $conffile contains a stream block; include $stream_output_dir/*.conf to enable stream templates" + else + # check if the file can be modified, e.g. not on a r/o filesystem + touch "$conffile" 2>/dev/null || { entrypoint_log "$ME: info: can not modify $conffile (read-only file system?)"; exit 0; } + entrypoint_log "$ME: Appending stream block to $conffile to include $stream_output_dir/*.conf" + cat << END >> "$conffile" +# added by "$ME" on "$(date)" +stream { + include $stream_output_dir/*.conf; +} +END + fi +} + +auto_envsubst() { + local template_dir="${NGINX_ENVSUBST_TEMPLATE_DIR:-/etc/nginx/templates}" + local suffix="${NGINX_ENVSUBST_TEMPLATE_SUFFIX:-.template}" + local output_dir="${NGINX_ENVSUBST_OUTPUT_DIR:-/etc/nginx/conf.d}" + local stream_suffix="${NGINX_ENVSUBST_STREAM_TEMPLATE_SUFFIX:-.stream-template}" + local stream_output_dir="${NGINX_ENVSUBST_STREAM_OUTPUT_DIR:-/etc/nginx/stream-conf.d}" + local filter="${NGINX_ENVSUBST_FILTER:-}" + + local template defined_envs relative_path output_path subdir + defined_envs=$(printf '${%s} ' $(awk "END { for (name in ENVIRON) { print ( name ~ /${filter}/ ) ? name : \"\" } }" < /dev/null )) + [ -d "$template_dir" ] || return 0 + if [ ! -w "$output_dir" ]; then + entrypoint_log "$ME: ERROR: $template_dir exists, but $output_dir is not writable" + return 0 + fi + find "$template_dir" -follow -type f -name "*$suffix" -print | while read -r template; do + relative_path="${template#"$template_dir/"}" + output_path="$output_dir/${relative_path%"$suffix"}" + subdir=$(dirname "$relative_path") + # create a subdirectory where the template file exists + mkdir -p "$output_dir/$subdir" + entrypoint_log "$ME: Running envsubst on $template to $output_path" + envsubst "$defined_envs" < "$template" > "$output_path" + done + + # Print the first file with the stream suffix, this will be false if there are none + if test -n "$(find "$template_dir" -name "*$stream_suffix" -print -quit)"; then + mkdir -p "$stream_output_dir" + if [ ! -w "$stream_output_dir" ]; then + entrypoint_log "$ME: ERROR: $template_dir exists, but $stream_output_dir is not writable" + return 0 + fi + add_stream_block + find "$template_dir" -follow -type f -name "*$stream_suffix" -print | while read -r template; do + relative_path="${template#"$template_dir/"}" + output_path="$stream_output_dir/${relative_path%"$stream_suffix"}" + subdir=$(dirname "$relative_path") + # create a subdirectory where the template file exists + mkdir -p "$stream_output_dir/$subdir" + entrypoint_log "$ME: Running envsubst on $template to $output_path" + envsubst "$defined_envs" < "$template" > "$output_path" + done + fi +} + +auto_envsubst + +exit 0 diff --git a/unprivileged/mainline/debian/30-tune-worker-processes.sh b/unprivileged/mainline/debian/30-tune-worker-processes.sh new file mode 100755 index 000000000..defb994f3 --- /dev/null +++ b/unprivileged/mainline/debian/30-tune-worker-processes.sh @@ -0,0 +1,188 @@ +#!/bin/sh +# vim:sw=2:ts=2:sts=2:et + +set -eu + +LC_ALL=C +ME=$(basename "$0") +PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin + +[ "${NGINX_ENTRYPOINT_WORKER_PROCESSES_AUTOTUNE:-}" ] || exit 0 + +touch /etc/nginx/nginx.conf 2>/dev/null || { echo >&2 "$ME: error: can not modify /etc/nginx/nginx.conf (read-only file system?)"; exit 0; } + +ceildiv() { + num=$1 + div=$2 + echo $(( (num + div - 1) / div )) +} + +get_cpuset() { + cpusetroot=$1 + cpusetfile=$2 + ncpu=0 + [ -f "$cpusetroot/$cpusetfile" ] || return 1 + for token in $( tr ',' ' ' < "$cpusetroot/$cpusetfile" ); do + case "$token" in + *-*) + count=$( seq $(echo "$token" | tr '-' ' ') | wc -l ) + ncpu=$(( ncpu+count )) + ;; + *) + ncpu=$(( ncpu+1 )) + ;; + esac + done + echo "$ncpu" +} + +get_quota() { + cpuroot=$1 + ncpu=0 + [ -f "$cpuroot/cpu.cfs_quota_us" ] || return 1 + [ -f "$cpuroot/cpu.cfs_period_us" ] || return 1 + cfs_quota=$( cat "$cpuroot/cpu.cfs_quota_us" ) + cfs_period=$( cat "$cpuroot/cpu.cfs_period_us" ) + [ "$cfs_quota" = "-1" ] && return 1 + [ "$cfs_period" = "0" ] && return 1 + ncpu=$( ceildiv "$cfs_quota" "$cfs_period" ) + [ "$ncpu" -gt 0 ] || return 1 + echo "$ncpu" +} + +get_quota_v2() { + cpuroot=$1 + ncpu=0 + [ -f "$cpuroot/cpu.max" ] || return 1 + cfs_quota=$( cut -d' ' -f 1 < "$cpuroot/cpu.max" ) + cfs_period=$( cut -d' ' -f 2 < "$cpuroot/cpu.max" ) + [ "$cfs_quota" = "max" ] && return 1 + [ "$cfs_period" = "0" ] && return 1 + ncpu=$( ceildiv "$cfs_quota" "$cfs_period" ) + [ "$ncpu" -gt 0 ] || return 1 + echo "$ncpu" +} + +get_cgroup_v1_path() { + needle=$1 + found= + foundroot= + mountpoint= + + [ -r "/proc/self/mountinfo" ] || return 1 + [ -r "/proc/self/cgroup" ] || return 1 + + while IFS= read -r line; do + case "$needle" in + "cpuset") + case "$line" in + *cpuset*) + found=$( echo "$line" | cut -d ' ' -f 4,5 ) + break + ;; + esac + ;; + "cpu") + case "$line" in + *cpuset*) + ;; + *cpu,cpuacct*|*cpuacct,cpu|*cpuacct*|*cpu*) + found=$( echo "$line" | cut -d ' ' -f 4,5 ) + break + ;; + esac + esac + done << __EOF__ +$( grep -F -- '- cgroup ' /proc/self/mountinfo ) +__EOF__ + + while IFS= read -r line; do + controller=$( echo "$line" | cut -d: -f 2 ) + case "$needle" in + "cpuset") + case "$controller" in + cpuset) + mountpoint=$( echo "$line" | cut -d: -f 3 ) + break + ;; + esac + ;; + "cpu") + case "$controller" in + cpu,cpuacct|cpuacct,cpu|cpuacct|cpu) + mountpoint=$( echo "$line" | cut -d: -f 3 ) + break + ;; + esac + ;; + esac +done << __EOF__ +$( grep -F -- 'cpu' /proc/self/cgroup ) +__EOF__ + + case "${found%% *}" in + "/") + foundroot="${found##* }$mountpoint" + ;; + "$mountpoint") + foundroot="${found##* }" + ;; + esac + echo "$foundroot" +} + +get_cgroup_v2_path() { + found= + foundroot= + mountpoint= + + [ -r "/proc/self/mountinfo" ] || return 1 + [ -r "/proc/self/cgroup" ] || return 1 + + while IFS= read -r line; do + found=$( echo "$line" | cut -d ' ' -f 4,5 ) + done << __EOF__ +$( grep -F -- '- cgroup2 ' /proc/self/mountinfo ) +__EOF__ + + while IFS= read -r line; do + mountpoint=$( echo "$line" | cut -d: -f 3 ) +done << __EOF__ +$( grep -F -- '0::' /proc/self/cgroup ) +__EOF__ + + case "${found%% *}" in + "") + return 1 + ;; + "/") + foundroot="${found##* }$mountpoint" + ;; + "$mountpoint" | /../*) + foundroot="${found##* }" + ;; + esac + echo "$foundroot" +} + +ncpu_online=$( getconf _NPROCESSORS_ONLN ) +ncpu_cpuset= +ncpu_quota= +ncpu_cpuset_v2= +ncpu_quota_v2= + +cpuset=$( get_cgroup_v1_path "cpuset" ) && ncpu_cpuset=$( get_cpuset "$cpuset" "cpuset.effective_cpus" ) || ncpu_cpuset=$ncpu_online +cpu=$( get_cgroup_v1_path "cpu" ) && ncpu_quota=$( get_quota "$cpu" ) || ncpu_quota=$ncpu_online +cgroup_v2=$( get_cgroup_v2_path ) && ncpu_cpuset_v2=$( get_cpuset "$cgroup_v2" "cpuset.cpus.effective" ) || ncpu_cpuset_v2=$ncpu_online +cgroup_v2=$( get_cgroup_v2_path ) && ncpu_quota_v2=$( get_quota_v2 "$cgroup_v2" ) || ncpu_quota_v2=$ncpu_online + +ncpu=$( printf "%s\n%s\n%s\n%s\n%s\n" \ + "$ncpu_online" \ + "$ncpu_cpuset" \ + "$ncpu_quota" \ + "$ncpu_cpuset_v2" \ + "$ncpu_quota_v2" \ + | sort -n \ + | head -n 1 ) + +sed -i.bak -r 's/^(worker_processes)(.*)$/# Commented out by '"$ME"' on '"$(date)"'\n#\1\2\n\1 '"$ncpu"';/' /etc/nginx/nginx.conf diff --git a/unprivileged/mainline/debian/Dockerfile b/unprivileged/mainline/debian/Dockerfile new file mode 100644 index 000000000..7c185ff7e --- /dev/null +++ b/unprivileged/mainline/debian/Dockerfile @@ -0,0 +1,164 @@ +# +# NOTE: THIS DOCKERFILE IS GENERATED VIA "update.sh" +# +# PLEASE DO NOT EDIT IT DIRECTLY. +# +ARG IMAGE=debian:trixie-slim +FROM $IMAGE + +LABEL maintainer="NGINX Docker Maintainers " + +ENV NGINX_VERSION=1.29.5 +ENV NJS_VERSION=0.9.5 +ENV NJS_RELEASE=1~trixie +ENV PKG_RELEASE=1~trixie +ENV DYNPKG_RELEASE=1~trixie + +ARG UID=101 +ARG GID=101 + +RUN set -x \ +# create nginx user/group first, to be consistent throughout docker variants + && groupadd --system --gid $GID nginx || true \ + && useradd --system --gid nginx --no-create-home --home /nonexistent --comment "nginx user" --shell /bin/false --uid $UID nginx || true \ + && apt-get update \ + && apt-get install --no-install-recommends --no-install-suggests -y gnupg1 ca-certificates \ + && \ + NGINX_GPGKEYS="573BFD6B3D8FBC641079A6ABABF5BD827BD9BF62 8540A6F18833A80E9C1653A42FD21310B49F6B46 9E9BE90EACBCDE69FE9B204CBCDCD8A38D88A2B3"; \ + NGINX_GPGKEY_PATH=/etc/apt/keyrings/nginx-archive-keyring.gpg; \ + export GNUPGHOME="$(mktemp -d)"; \ + found=''; \ + for NGINX_GPGKEY in $NGINX_GPGKEYS; do \ + for server in \ + hkp://keyserver.ubuntu.com:80 \ + pgp.mit.edu \ + ; do \ + echo "Fetching GPG key $NGINX_GPGKEY from $server"; \ + gpg1 --batch --keyserver "$server" --keyserver-options timeout=10 --recv-keys "$NGINX_GPGKEY" && found=yes && break; \ + done; \ + test -z "$found" && echo >&2 "error: failed to fetch GPG key $NGINX_GPGKEY" && exit 1; \ + done; \ + gpg1 --batch --export $NGINX_GPGKEYS > "$NGINX_GPGKEY_PATH" ; \ + rm -rf "$GNUPGHOME"; \ + apt-get remove --purge --auto-remove -y gnupg1 && rm -rf /var/lib/apt/lists/* \ + && dpkgArch="$(dpkg --print-architecture)" \ + && nginxPackages=" \ + nginx=${NGINX_VERSION}-${PKG_RELEASE} \ + nginx-module-xslt=${NGINX_VERSION}-${DYNPKG_RELEASE} \ + nginx-module-geoip=${NGINX_VERSION}-${DYNPKG_RELEASE} \ + nginx-module-image-filter=${NGINX_VERSION}-${DYNPKG_RELEASE} \ + nginx-module-njs=${NGINX_VERSION}+${NJS_VERSION}-${NJS_RELEASE} \ + nginx-module-acme=${NGINX_VERSION}+${ACME_VERSION}-${PKG_RELEASE} \ + " \ + && case "$dpkgArch" in \ + amd64|arm64) \ +# arches officially built by upstream + echo "deb [signed-by=$NGINX_GPGKEY_PATH] https://nginx.org/packages/mainline/debian/ trixie nginx" >> /etc/apt/sources.list.d/nginx.list \ + && apt-get update \ + ;; \ + *) \ +# we're on an architecture upstream doesn't officially build for +# let's build binaries from the published packaging sources +# new directory for storing sources and .deb files + tempDir="$(mktemp -d)" \ + && chmod 777 "$tempDir" \ +# (777 to ensure APT's "_apt" user can access it too) + \ +# save list of currently-installed packages so build dependencies can be cleanly removed later + && savedAptMark="$(apt-mark showmanual)" \ + \ +# build .deb files from upstream's packaging sources + && apt-get update \ + && apt-get install --no-install-recommends --no-install-suggests -y \ + curl \ + devscripts \ + equivs \ + git \ + libxml2-utils \ + lsb-release \ + xsltproc \ + && ( \ + cd "$tempDir" \ + && REVISION="${NGINX_VERSION}-${PKG_RELEASE}" \ + && REVISION=${REVISION%~*} \ + && curl -f -L -O https://github.com/nginx/pkg-oss/archive/${REVISION}.tar.gz \ + && PKGOSSCHECKSUM="b8584eaa97130ba7743dfbb2a10f665d64cb54b864e2038d0fd298d24682fc05eb4472738430b15862dabc6f374917f1b9889117051a852d36d0a6c8bc898921 *${REVISION}.tar.gz" \ + && if [ "$(openssl sha512 -r ${REVISION}.tar.gz)" = "$PKGOSSCHECKSUM" ]; then \ + echo "pkg-oss tarball checksum verification succeeded!"; \ + else \ + echo "pkg-oss tarball checksum verification failed!"; \ + exit 1; \ + fi \ + && tar xzvf ${REVISION}.tar.gz \ + && cd pkg-oss-${REVISION} \ + && cd debian \ + && for target in base module-geoip module-image-filter module-njs module-xslt module-acme; do \ + make rules-$target; \ + mk-build-deps --install --tool="apt-get -o Debug::pkgProblemResolver=yes --no-install-recommends --yes" \ + debuild-$target/nginx-$NGINX_VERSION/debian/control; \ + done \ + && make base module-geoip module-image-filter module-njs module-xslt module-acme \ + ) \ +# we don't remove APT lists here because they get re-downloaded and removed later + \ +# reset apt-mark's "manual" list so that "purge --auto-remove" will remove all build dependencies +# (which is done after we install the built packages so we don't have to redownload any overlapping dependencies) + && apt-mark showmanual | xargs apt-mark auto > /dev/null \ + && { [ -z "$savedAptMark" ] || apt-mark manual $savedAptMark; } \ + \ +# create a temporary local APT repo to install from (so that dependency resolution can be handled by APT, as it should be) + && ls -lAFh "$tempDir" \ + && ( cd "$tempDir" && dpkg-scanpackages . > Packages ) \ + && grep '^Package: ' "$tempDir/Packages" \ + && echo "deb [ trusted=yes ] file://$tempDir ./" > /etc/apt/sources.list.d/temp.list \ +# work around the following APT issue by using "Acquire::GzipIndexes=false" (overriding "/etc/apt/apt.conf.d/docker-gzip-indexes") +# Could not open file /var/lib/apt/lists/partial/_tmp_tmp.ODWljpQfkE_._Packages - open (13: Permission denied) +# ... +# E: Failed to fetch store:/var/lib/apt/lists/partial/_tmp_tmp.ODWljpQfkE_._Packages Could not open file /var/lib/apt/lists/partial/_tmp_tmp.ODWljpQfkE_._Packages - open (13: Permission denied) + && apt-get -o Acquire::GzipIndexes=false update \ + ;; \ + esac \ + \ + && apt-get install --no-install-recommends --no-install-suggests -y \ + $nginxPackages \ + gettext-base \ + curl \ + && apt-get remove --purge --auto-remove -y && rm -rf /var/lib/apt/lists/* /etc/apt/sources.list.d/nginx.list \ + \ +# if we have leftovers from building, let's purge them (including extra, unnecessary build deps) + && if [ -n "$tempDir" ]; then \ + apt-get purge -y --auto-remove \ + && rm -rf "$tempDir" /etc/apt/sources.list.d/temp.list; \ + fi \ +# forward request and error logs to docker log collector + && ln -sf /dev/stdout /var/log/nginx/access.log \ + && ln -sf /dev/stderr /var/log/nginx/error.log \ +# create a docker-entrypoint.d directory + && mkdir /docker-entrypoint.d + +# implement changes required to run NGINX as an unprivileged user +RUN sed -i 's,listen 80;,listen 8080;,' /etc/nginx/conf.d/default.conf \ + && sed -i '/user nginx;/d' /etc/nginx/nginx.conf \ + && sed -i 's,\(/var\)\{0\,1\}/run/nginx.pid,/tmp/nginx.pid,' /etc/nginx/nginx.conf \ + && sed -i "/^http {/a \ proxy_temp_path /tmp/proxy_temp;\n client_body_temp_path /tmp/client_temp;\n fastcgi_temp_path /tmp/fastcgi_temp;\n uwsgi_temp_path /tmp/uwsgi_temp;\n scgi_temp_path /tmp/scgi_temp;\n" /etc/nginx/nginx.conf \ + && sed -i 's,PIDFILE=${PIDFILE:-/run/nginx.pid},PIDFILE=${PIDFILE:-/tmp/nginx.pid},' /etc/init.d/nginx \ +# nginx user must own the cache and etc directory to write cache and tweak the nginx config + && chown -R $UID:0 /var/cache/nginx \ + && chmod -R g+w /var/cache/nginx \ + && chown -R $UID:0 /etc/nginx \ + && chmod -R g+w /etc/nginx + +COPY docker-entrypoint.sh / +COPY 10-listen-on-ipv6-by-default.sh /docker-entrypoint.d +COPY 15-local-resolvers.envsh /docker-entrypoint.d +COPY 20-envsubst-on-templates.sh /docker-entrypoint.d +COPY 30-tune-worker-processes.sh /docker-entrypoint.d +ENTRYPOINT ["/docker-entrypoint.sh"] + +EXPOSE 8080 + +STOPSIGNAL SIGQUIT + +USER $UID + +CMD ["nginx", "-g", "daemon off;"] diff --git a/unprivileged/mainline/debian/docker-entrypoint.sh b/unprivileged/mainline/debian/docker-entrypoint.sh new file mode 100755 index 000000000..8ea04f217 --- /dev/null +++ b/unprivileged/mainline/debian/docker-entrypoint.sh @@ -0,0 +1,47 @@ +#!/bin/sh +# vim:sw=4:ts=4:et + +set -e + +entrypoint_log() { + if [ -z "${NGINX_ENTRYPOINT_QUIET_LOGS:-}" ]; then + echo "$@" + fi +} + +if [ "$1" = "nginx" ] || [ "$1" = "nginx-debug" ]; then + if /usr/bin/find "/docker-entrypoint.d/" -mindepth 1 -maxdepth 1 -type f -print -quit 2>/dev/null | read v; then + entrypoint_log "$0: /docker-entrypoint.d/ is not empty, will attempt to perform configuration" + + entrypoint_log "$0: Looking for shell scripts in /docker-entrypoint.d/" + find "/docker-entrypoint.d/" -follow -type f -print | sort -V | while read -r f; do + case "$f" in + *.envsh) + if [ -x "$f" ]; then + entrypoint_log "$0: Sourcing $f"; + . "$f" + else + # warn on shell scripts without exec bit + entrypoint_log "$0: Ignoring $f, not executable"; + fi + ;; + *.sh) + if [ -x "$f" ]; then + entrypoint_log "$0: Launching $f"; + "$f" + else + # warn on shell scripts without exec bit + entrypoint_log "$0: Ignoring $f, not executable"; + fi + ;; + *) entrypoint_log "$0: Ignoring $f";; + esac + done + + entrypoint_log "$0: Configuration complete; ready for start up" + else + entrypoint_log "$0: No files found in /docker-entrypoint.d/, skipping configuration" + fi +fi + +exec "$@" diff --git a/unprivileged/stable/alpine-otel/Dockerfile b/unprivileged/stable/alpine-otel/Dockerfile new file mode 100644 index 000000000..62383f63a --- /dev/null +++ b/unprivileged/stable/alpine-otel/Dockerfile @@ -0,0 +1,86 @@ +# +# NOTE: THIS DOCKERFILE IS GENERATED VIA "update.sh" +# +# PLEASE DO NOT EDIT IT DIRECTLY. +# +ARG IMAGE=nginxinc/nginx-unprivileged:1.28.2-alpine +FROM $IMAGE + +ENV OTEL_VERSION=0.1.2 + +ARG UID=101 +ARG GID=101 + +USER root + +RUN set -x \ + && apkArch="$(cat /etc/apk/arch)" \ + && nginxPackages=" \ + nginx=${NGINX_VERSION}-r${PKG_RELEASE} \ + nginx-module-xslt=${NGINX_VERSION}-r${DYNPKG_RELEASE} \ + nginx-module-geoip=${NGINX_VERSION}-r${DYNPKG_RELEASE} \ + nginx-module-image-filter=${NGINX_VERSION}-r${DYNPKG_RELEASE} \ + nginx-module-njs=${NGINX_VERSION}.${NJS_VERSION}-r${NJS_RELEASE} \ + nginx-module-acme=${NGINX_VERSION}.${ACME_VERSION}-r${PKG_RELEASE} \ + nginx-module-otel=${NGINX_VERSION}.${OTEL_VERSION}-r${PKG_RELEASE} \ + " \ +# install prerequisites for public key and pkg-oss checks + && apk add --no-cache --virtual .checksum-deps \ + openssl \ + && case "$apkArch" in \ + x86_64|aarch64) \ +# arches officially built by upstream + apk add -X "https://nginx.org/packages/alpine/v$(egrep -o '^[0-9]+\.[0-9]+' /etc/alpine-release)/main" --no-cache $nginxPackages \ + ;; \ + *) \ +# we're on an architecture upstream doesn't officially build for +# let's build binaries from the published packaging sources + set -x \ + && tempDir="$(mktemp -d)" \ + && chown nobody:nobody $tempDir \ + && apk add --no-cache --virtual .build-deps \ + gcc \ + libc-dev \ + make \ + openssl-dev \ + pcre2-dev \ + zlib-dev \ + linux-headers \ + cmake \ + bash \ + alpine-sdk \ + findutils \ + curl \ + xz \ + protobuf-dev \ + grpc-dev \ + && su nobody -s /bin/sh -c " \ + export HOME=${tempDir} \ + && cd ${tempDir} \ + && curl -f -L -O https://github.com/nginx/pkg-oss/archive/${NGINX_VERSION}-${PKG_RELEASE}.tar.gz \ + && PKGOSSCHECKSUM=\"ef4545c05b1632a056482e3dbb47bb5d7393238318db3491e8bb308218cdb5f32dbb2ac73509097ac2426fd73270bc97836843a8b1846a396fd94e60826f7e3f *${NGINX_VERSION}-${PKG_RELEASE}.tar.gz\" \ + && if [ \"\$(openssl sha512 -r ${NGINX_VERSION}-${PKG_RELEASE}.tar.gz)\" = \"\$PKGOSSCHECKSUM\" ]; then \ + echo \"pkg-oss tarball checksum verification succeeded!\"; \ + else \ + echo \"pkg-oss tarball checksum verification failed!\"; \ + exit 1; \ + fi \ + && tar xzvf ${NGINX_VERSION}-${PKG_RELEASE}.tar.gz \ + && cd pkg-oss-${NGINX_VERSION}-${PKG_RELEASE} \ + && cd alpine \ + && make module-otel \ + && apk index --allow-untrusted -o ${tempDir}/packages/alpine/${apkArch}/APKINDEX.tar.gz ${tempDir}/packages/alpine/${apkArch}/*.apk \ + && abuild-sign -k ${tempDir}/.abuild/abuild-key.rsa ${tempDir}/packages/alpine/${apkArch}/APKINDEX.tar.gz \ + " \ + && cp ${tempDir}/.abuild/abuild-key.rsa.pub /etc/apk/keys/ \ + && apk del --no-network .build-deps \ + && apk add -X ${tempDir}/packages/alpine/ --no-cache $nginxPackages \ + ;; \ + esac \ +# remove checksum deps + && apk del --no-network .checksum-deps \ +# if we have leftovers from building, let's purge them (including extra, unnecessary build deps) + && if [ -n "$tempDir" ]; then rm -rf "$tempDir"; fi \ + && if [ -f "/etc/apk/keys/abuild-key.rsa.pub" ]; then rm -f /etc/apk/keys/abuild-key.rsa.pub; fi + +USER $UID diff --git a/unprivileged/stable/alpine-perl/Dockerfile b/unprivileged/stable/alpine-perl/Dockerfile new file mode 100644 index 000000000..cb958431c --- /dev/null +++ b/unprivileged/stable/alpine-perl/Dockerfile @@ -0,0 +1,81 @@ +# +# NOTE: THIS DOCKERFILE IS GENERATED VIA "update.sh" +# +# PLEASE DO NOT EDIT IT DIRECTLY. +# +ARG IMAGE=nginxinc/nginx-unprivileged:1.28.2-alpine +FROM $IMAGE + +ARG UID=101 +ARG GID=101 + +USER root + +RUN set -x \ + && apkArch="$(cat /etc/apk/arch)" \ + && nginxPackages=" \ + nginx=${NGINX_VERSION}-r${PKG_RELEASE} \ + nginx-module-xslt=${NGINX_VERSION}-r${DYNPKG_RELEASE} \ + nginx-module-geoip=${NGINX_VERSION}-r${DYNPKG_RELEASE} \ + nginx-module-image-filter=${NGINX_VERSION}-r${DYNPKG_RELEASE} \ + nginx-module-perl=${NGINX_VERSION}-r${DYNPKG_RELEASE} \ + nginx-module-njs=${NGINX_VERSION}.${NJS_VERSION}-r${NJS_RELEASE} \ + nginx-module-acme=${NGINX_VERSION}.${ACME_VERSION}-r${PKG_RELEASE} \ + " \ +# install prerequisites for public key and pkg-oss checks + && apk add --no-cache --virtual .checksum-deps \ + openssl \ + && case "$apkArch" in \ + x86_64|aarch64) \ +# arches officially built by upstream + apk add -X "https://nginx.org/packages/alpine/v$(egrep -o '^[0-9]+\.[0-9]+' /etc/alpine-release)/main" --no-cache $nginxPackages \ + ;; \ + *) \ +# we're on an architecture upstream doesn't officially build for +# let's build binaries from the published packaging sources + set -x \ + && tempDir="$(mktemp -d)" \ + && chown nobody:nobody $tempDir \ + && apk add --no-cache --virtual .build-deps \ + gcc \ + libc-dev \ + make \ + openssl-dev \ + pcre2-dev \ + zlib-dev \ + linux-headers \ + perl-dev \ + bash \ + alpine-sdk \ + findutils \ + curl \ + && su nobody -s /bin/sh -c " \ + export HOME=${tempDir} \ + && cd ${tempDir} \ + && curl -f -L -O https://github.com/nginx/pkg-oss/archive/${NGINX_VERSION}-${PKG_RELEASE}.tar.gz \ + && PKGOSSCHECKSUM=\"ef4545c05b1632a056482e3dbb47bb5d7393238318db3491e8bb308218cdb5f32dbb2ac73509097ac2426fd73270bc97836843a8b1846a396fd94e60826f7e3f *${NGINX_VERSION}-${PKG_RELEASE}.tar.gz\" \ + && if [ \"\$(openssl sha512 -r ${NGINX_VERSION}-${PKG_RELEASE}.tar.gz)\" = \"\$PKGOSSCHECKSUM\" ]; then \ + echo \"pkg-oss tarball checksum verification succeeded!\"; \ + else \ + echo \"pkg-oss tarball checksum verification failed!\"; \ + exit 1; \ + fi \ + && tar xzvf ${NGINX_VERSION}-${PKG_RELEASE}.tar.gz \ + && cd pkg-oss-${NGINX_VERSION}-${PKG_RELEASE} \ + && cd alpine \ + && make module-perl \ + && apk index --allow-untrusted -o ${tempDir}/packages/alpine/${apkArch}/APKINDEX.tar.gz ${tempDir}/packages/alpine/${apkArch}/*.apk \ + && abuild-sign -k ${tempDir}/.abuild/abuild-key.rsa ${tempDir}/packages/alpine/${apkArch}/APKINDEX.tar.gz \ + " \ + && cp ${tempDir}/.abuild/abuild-key.rsa.pub /etc/apk/keys/ \ + && apk del --no-network .build-deps \ + && apk add -X ${tempDir}/packages/alpine/ --no-cache $nginxPackages \ + ;; \ + esac \ +# remove checksum deps + && apk del --no-network .checksum-deps \ +# if we have leftovers from building, let's purge them (including extra, unnecessary build deps) + && if [ -n "$tempDir" ]; then rm -rf "$tempDir"; fi \ + && if [ -f "/etc/apk/keys/abuild-key.rsa.pub" ]; then rm -f /etc/apk/keys/abuild-key.rsa.pub; fi + +USER $UID diff --git a/unprivileged/stable/alpine-slim/10-listen-on-ipv6-by-default.sh b/unprivileged/stable/alpine-slim/10-listen-on-ipv6-by-default.sh new file mode 100755 index 000000000..176607941 --- /dev/null +++ b/unprivileged/stable/alpine-slim/10-listen-on-ipv6-by-default.sh @@ -0,0 +1,67 @@ +#!/bin/sh +# vim:sw=4:ts=4:et + +set -e + +entrypoint_log() { + if [ -z "${NGINX_ENTRYPOINT_QUIET_LOGS:-}" ]; then + echo "$@" + fi +} + +ME=$(basename "$0") +DEFAULT_CONF_FILE="etc/nginx/conf.d/default.conf" + +# check if we have ipv6 available +if [ ! -f "/proc/net/if_inet6" ]; then + entrypoint_log "$ME: info: ipv6 not available" + exit 0 +fi + +if [ ! -f "/$DEFAULT_CONF_FILE" ]; then + entrypoint_log "$ME: info: /$DEFAULT_CONF_FILE is not a file or does not exist" + exit 0 +fi + +# check if the file can be modified, e.g. not on a r/o filesystem +touch /$DEFAULT_CONF_FILE 2>/dev/null || { entrypoint_log "$ME: info: can not modify /$DEFAULT_CONF_FILE (read-only file system?)"; exit 0; } + +# check if the file is already modified, e.g. on a container restart +grep -q "listen \[::\]:8080;" /$DEFAULT_CONF_FILE && { entrypoint_log "$ME: info: IPv6 listen already enabled"; exit 0; } + +if [ -f "/etc/os-release" ]; then + . /etc/os-release +else + entrypoint_log "$ME: info: can not guess the operating system" + exit 0 +fi + +entrypoint_log "$ME: info: Getting the checksum of /$DEFAULT_CONF_FILE" + +case "$ID" in + "debian") + CHECKSUM=$(dpkg-query --show --showformat='${Conffiles}\n' nginx | grep $DEFAULT_CONF_FILE | cut -d' ' -f 3) + echo "$CHECKSUM /$DEFAULT_CONF_FILE" | md5sum -c - >/dev/null 2>&1 || { + entrypoint_log "$ME: info: /$DEFAULT_CONF_FILE differs from the packaged version" + exit 0 + } + ;; + "alpine") + CHECKSUM=$(apk manifest nginx 2>/dev/null| grep $DEFAULT_CONF_FILE | cut -d' ' -f 1 | cut -d ':' -f 2) + echo "$CHECKSUM /$DEFAULT_CONF_FILE" | sha1sum -c - >/dev/null 2>&1 || { + entrypoint_log "$ME: info: /$DEFAULT_CONF_FILE differs from the packaged version" + exit 0 + } + ;; + *) + entrypoint_log "$ME: info: Unsupported distribution" + exit 0 + ;; +esac + +# enable ipv6 on default.conf listen sockets +sed -i -E 's,listen 8080;,listen 8080;\n listen [::]:8080;,' /$DEFAULT_CONF_FILE + +entrypoint_log "$ME: info: Enabled listen on IPv6 in /$DEFAULT_CONF_FILE" + +exit 0 diff --git a/unprivileged/stable/alpine-slim/15-local-resolvers.envsh b/unprivileged/stable/alpine-slim/15-local-resolvers.envsh new file mode 100755 index 000000000..e830ddacd --- /dev/null +++ b/unprivileged/stable/alpine-slim/15-local-resolvers.envsh @@ -0,0 +1,15 @@ +#!/bin/sh +# vim:sw=2:ts=2:sts=2:et + +set -eu + +LC_ALL=C +PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin + +[ "${NGINX_ENTRYPOINT_LOCAL_RESOLVERS:-}" ] || return 0 + +NGINX_LOCAL_RESOLVERS=$(awk 'BEGIN{ORS=" "} $1=="nameserver" {if ($2 ~ ":") {print "["$2"]"} else {print $2}}' /etc/resolv.conf) + +NGINX_LOCAL_RESOLVERS="${NGINX_LOCAL_RESOLVERS% }" + +export NGINX_LOCAL_RESOLVERS diff --git a/unprivileged/stable/alpine-slim/20-envsubst-on-templates.sh b/unprivileged/stable/alpine-slim/20-envsubst-on-templates.sh new file mode 100755 index 000000000..6938405be --- /dev/null +++ b/unprivileged/stable/alpine-slim/20-envsubst-on-templates.sh @@ -0,0 +1,78 @@ +#!/bin/sh + +set -e + +ME=$(basename "$0") + +entrypoint_log() { + if [ -z "${NGINX_ENTRYPOINT_QUIET_LOGS:-}" ]; then + echo "$@" + fi +} + +add_stream_block() { + local conffile="/etc/nginx/nginx.conf" + + if grep -q -E "\s*stream\s*\{" "$conffile"; then + entrypoint_log "$ME: $conffile contains a stream block; include $stream_output_dir/*.conf to enable stream templates" + else + # check if the file can be modified, e.g. not on a r/o filesystem + touch "$conffile" 2>/dev/null || { entrypoint_log "$ME: info: can not modify $conffile (read-only file system?)"; exit 0; } + entrypoint_log "$ME: Appending stream block to $conffile to include $stream_output_dir/*.conf" + cat << END >> "$conffile" +# added by "$ME" on "$(date)" +stream { + include $stream_output_dir/*.conf; +} +END + fi +} + +auto_envsubst() { + local template_dir="${NGINX_ENVSUBST_TEMPLATE_DIR:-/etc/nginx/templates}" + local suffix="${NGINX_ENVSUBST_TEMPLATE_SUFFIX:-.template}" + local output_dir="${NGINX_ENVSUBST_OUTPUT_DIR:-/etc/nginx/conf.d}" + local stream_suffix="${NGINX_ENVSUBST_STREAM_TEMPLATE_SUFFIX:-.stream-template}" + local stream_output_dir="${NGINX_ENVSUBST_STREAM_OUTPUT_DIR:-/etc/nginx/stream-conf.d}" + local filter="${NGINX_ENVSUBST_FILTER:-}" + + local template defined_envs relative_path output_path subdir + defined_envs=$(printf '${%s} ' $(awk "END { for (name in ENVIRON) { print ( name ~ /${filter}/ ) ? name : \"\" } }" < /dev/null )) + [ -d "$template_dir" ] || return 0 + if [ ! -w "$output_dir" ]; then + entrypoint_log "$ME: ERROR: $template_dir exists, but $output_dir is not writable" + return 0 + fi + find "$template_dir" -follow -type f -name "*$suffix" -print | while read -r template; do + relative_path="${template#"$template_dir/"}" + output_path="$output_dir/${relative_path%"$suffix"}" + subdir=$(dirname "$relative_path") + # create a subdirectory where the template file exists + mkdir -p "$output_dir/$subdir" + entrypoint_log "$ME: Running envsubst on $template to $output_path" + envsubst "$defined_envs" < "$template" > "$output_path" + done + + # Print the first file with the stream suffix, this will be false if there are none + if test -n "$(find "$template_dir" -name "*$stream_suffix" -print -quit)"; then + mkdir -p "$stream_output_dir" + if [ ! -w "$stream_output_dir" ]; then + entrypoint_log "$ME: ERROR: $template_dir exists, but $stream_output_dir is not writable" + return 0 + fi + add_stream_block + find "$template_dir" -follow -type f -name "*$stream_suffix" -print | while read -r template; do + relative_path="${template#"$template_dir/"}" + output_path="$stream_output_dir/${relative_path%"$stream_suffix"}" + subdir=$(dirname "$relative_path") + # create a subdirectory where the template file exists + mkdir -p "$stream_output_dir/$subdir" + entrypoint_log "$ME: Running envsubst on $template to $output_path" + envsubst "$defined_envs" < "$template" > "$output_path" + done + fi +} + +auto_envsubst + +exit 0 diff --git a/unprivileged/stable/alpine-slim/30-tune-worker-processes.sh b/unprivileged/stable/alpine-slim/30-tune-worker-processes.sh new file mode 100755 index 000000000..defb994f3 --- /dev/null +++ b/unprivileged/stable/alpine-slim/30-tune-worker-processes.sh @@ -0,0 +1,188 @@ +#!/bin/sh +# vim:sw=2:ts=2:sts=2:et + +set -eu + +LC_ALL=C +ME=$(basename "$0") +PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin + +[ "${NGINX_ENTRYPOINT_WORKER_PROCESSES_AUTOTUNE:-}" ] || exit 0 + +touch /etc/nginx/nginx.conf 2>/dev/null || { echo >&2 "$ME: error: can not modify /etc/nginx/nginx.conf (read-only file system?)"; exit 0; } + +ceildiv() { + num=$1 + div=$2 + echo $(( (num + div - 1) / div )) +} + +get_cpuset() { + cpusetroot=$1 + cpusetfile=$2 + ncpu=0 + [ -f "$cpusetroot/$cpusetfile" ] || return 1 + for token in $( tr ',' ' ' < "$cpusetroot/$cpusetfile" ); do + case "$token" in + *-*) + count=$( seq $(echo "$token" | tr '-' ' ') | wc -l ) + ncpu=$(( ncpu+count )) + ;; + *) + ncpu=$(( ncpu+1 )) + ;; + esac + done + echo "$ncpu" +} + +get_quota() { + cpuroot=$1 + ncpu=0 + [ -f "$cpuroot/cpu.cfs_quota_us" ] || return 1 + [ -f "$cpuroot/cpu.cfs_period_us" ] || return 1 + cfs_quota=$( cat "$cpuroot/cpu.cfs_quota_us" ) + cfs_period=$( cat "$cpuroot/cpu.cfs_period_us" ) + [ "$cfs_quota" = "-1" ] && return 1 + [ "$cfs_period" = "0" ] && return 1 + ncpu=$( ceildiv "$cfs_quota" "$cfs_period" ) + [ "$ncpu" -gt 0 ] || return 1 + echo "$ncpu" +} + +get_quota_v2() { + cpuroot=$1 + ncpu=0 + [ -f "$cpuroot/cpu.max" ] || return 1 + cfs_quota=$( cut -d' ' -f 1 < "$cpuroot/cpu.max" ) + cfs_period=$( cut -d' ' -f 2 < "$cpuroot/cpu.max" ) + [ "$cfs_quota" = "max" ] && return 1 + [ "$cfs_period" = "0" ] && return 1 + ncpu=$( ceildiv "$cfs_quota" "$cfs_period" ) + [ "$ncpu" -gt 0 ] || return 1 + echo "$ncpu" +} + +get_cgroup_v1_path() { + needle=$1 + found= + foundroot= + mountpoint= + + [ -r "/proc/self/mountinfo" ] || return 1 + [ -r "/proc/self/cgroup" ] || return 1 + + while IFS= read -r line; do + case "$needle" in + "cpuset") + case "$line" in + *cpuset*) + found=$( echo "$line" | cut -d ' ' -f 4,5 ) + break + ;; + esac + ;; + "cpu") + case "$line" in + *cpuset*) + ;; + *cpu,cpuacct*|*cpuacct,cpu|*cpuacct*|*cpu*) + found=$( echo "$line" | cut -d ' ' -f 4,5 ) + break + ;; + esac + esac + done << __EOF__ +$( grep -F -- '- cgroup ' /proc/self/mountinfo ) +__EOF__ + + while IFS= read -r line; do + controller=$( echo "$line" | cut -d: -f 2 ) + case "$needle" in + "cpuset") + case "$controller" in + cpuset) + mountpoint=$( echo "$line" | cut -d: -f 3 ) + break + ;; + esac + ;; + "cpu") + case "$controller" in + cpu,cpuacct|cpuacct,cpu|cpuacct|cpu) + mountpoint=$( echo "$line" | cut -d: -f 3 ) + break + ;; + esac + ;; + esac +done << __EOF__ +$( grep -F -- 'cpu' /proc/self/cgroup ) +__EOF__ + + case "${found%% *}" in + "/") + foundroot="${found##* }$mountpoint" + ;; + "$mountpoint") + foundroot="${found##* }" + ;; + esac + echo "$foundroot" +} + +get_cgroup_v2_path() { + found= + foundroot= + mountpoint= + + [ -r "/proc/self/mountinfo" ] || return 1 + [ -r "/proc/self/cgroup" ] || return 1 + + while IFS= read -r line; do + found=$( echo "$line" | cut -d ' ' -f 4,5 ) + done << __EOF__ +$( grep -F -- '- cgroup2 ' /proc/self/mountinfo ) +__EOF__ + + while IFS= read -r line; do + mountpoint=$( echo "$line" | cut -d: -f 3 ) +done << __EOF__ +$( grep -F -- '0::' /proc/self/cgroup ) +__EOF__ + + case "${found%% *}" in + "") + return 1 + ;; + "/") + foundroot="${found##* }$mountpoint" + ;; + "$mountpoint" | /../*) + foundroot="${found##* }" + ;; + esac + echo "$foundroot" +} + +ncpu_online=$( getconf _NPROCESSORS_ONLN ) +ncpu_cpuset= +ncpu_quota= +ncpu_cpuset_v2= +ncpu_quota_v2= + +cpuset=$( get_cgroup_v1_path "cpuset" ) && ncpu_cpuset=$( get_cpuset "$cpuset" "cpuset.effective_cpus" ) || ncpu_cpuset=$ncpu_online +cpu=$( get_cgroup_v1_path "cpu" ) && ncpu_quota=$( get_quota "$cpu" ) || ncpu_quota=$ncpu_online +cgroup_v2=$( get_cgroup_v2_path ) && ncpu_cpuset_v2=$( get_cpuset "$cgroup_v2" "cpuset.cpus.effective" ) || ncpu_cpuset_v2=$ncpu_online +cgroup_v2=$( get_cgroup_v2_path ) && ncpu_quota_v2=$( get_quota_v2 "$cgroup_v2" ) || ncpu_quota_v2=$ncpu_online + +ncpu=$( printf "%s\n%s\n%s\n%s\n%s\n" \ + "$ncpu_online" \ + "$ncpu_cpuset" \ + "$ncpu_quota" \ + "$ncpu_cpuset_v2" \ + "$ncpu_quota_v2" \ + | sort -n \ + | head -n 1 ) + +sed -i.bak -r 's/^(worker_processes)(.*)$/# Commented out by '"$ME"' on '"$(date)"'\n#\1\2\n\1 '"$ncpu"';/' /etc/nginx/nginx.conf diff --git a/unprivileged/stable/alpine-slim/Dockerfile b/unprivileged/stable/alpine-slim/Dockerfile new file mode 100644 index 000000000..8c5c95c27 --- /dev/null +++ b/unprivileged/stable/alpine-slim/Dockerfile @@ -0,0 +1,125 @@ +# +# NOTE: THIS DOCKERFILE IS GENERATED VIA "update.sh" +# +# PLEASE DO NOT EDIT IT DIRECTLY. +# +ARG IMAGE=alpine:3.23 +FROM $IMAGE + +LABEL maintainer="NGINX Docker Maintainers " + +ENV NGINX_VERSION=1.28.2 +ENV PKG_RELEASE=1 +ENV DYNPKG_RELEASE=1 + +ARG UID=101 +ARG GID=101 + +RUN set -x \ +# create nginx user/group first, to be consistent throughout docker variants + && addgroup -g $GID -S nginx || true \ + && adduser -S -D -H -u $UID -h /var/cache/nginx -s /sbin/nologin -G nginx -g nginx nginx || true \ + && apkArch="$(cat /etc/apk/arch)" \ + && nginxPackages=" \ + nginx=${NGINX_VERSION}-r${PKG_RELEASE} \ + " \ +# install prerequisites for public key and pkg-oss checks + && apk add --no-cache --virtual .checksum-deps \ + openssl \ + && case "$apkArch" in \ + x86_64|aarch64) \ +# arches officially built by upstream + set -x \ + && KEY_SHA512="e09fa32f0a0eab2b879ccbbc4d0e4fb9751486eedda75e35fac65802cc9faa266425edf83e261137a2f4d16281ce2c1a5f4502930fe75154723da014214f0655" \ + && wget -O /tmp/nginx_signing.rsa.pub https://nginx.org/keys/nginx_signing.rsa.pub \ + && if echo "$KEY_SHA512 */tmp/nginx_signing.rsa.pub" | sha512sum -c -; then \ + echo "key verification succeeded!"; \ + mv /tmp/nginx_signing.rsa.pub /etc/apk/keys/; \ + else \ + echo "key verification failed!"; \ + exit 1; \ + fi \ + && apk add -X "https://nginx.org/packages/alpine/v$(egrep -o '^[0-9]+\.[0-9]+' /etc/alpine-release)/main" --no-cache $nginxPackages \ + ;; \ + *) \ +# we're on an architecture upstream doesn't officially build for +# let's build binaries from the published packaging sources + set -x \ + && tempDir="$(mktemp -d)" \ + && chown nobody:nobody $tempDir \ + && apk add --no-cache --virtual .build-deps \ + gcc \ + libc-dev \ + make \ + openssl-dev \ + pcre2-dev \ + zlib-dev \ + linux-headers \ + bash \ + alpine-sdk \ + findutils \ + curl \ + && su nobody -s /bin/sh -c " \ + export HOME=${tempDir} \ + && cd ${tempDir} \ + && curl -f -L -O https://github.com/nginx/pkg-oss/archive/${NGINX_VERSION}-${PKG_RELEASE}.tar.gz \ + && PKGOSSCHECKSUM=\"ef4545c05b1632a056482e3dbb47bb5d7393238318db3491e8bb308218cdb5f32dbb2ac73509097ac2426fd73270bc97836843a8b1846a396fd94e60826f7e3f *${NGINX_VERSION}-${PKG_RELEASE}.tar.gz\" \ + && if [ \"\$(openssl sha512 -r ${NGINX_VERSION}-${PKG_RELEASE}.tar.gz)\" = \"\$PKGOSSCHECKSUM\" ]; then \ + echo \"pkg-oss tarball checksum verification succeeded!\"; \ + else \ + echo \"pkg-oss tarball checksum verification failed!\"; \ + exit 1; \ + fi \ + && tar xzvf ${NGINX_VERSION}-${PKG_RELEASE}.tar.gz \ + && cd pkg-oss-${NGINX_VERSION}-${PKG_RELEASE} \ + && cd alpine \ + && make base \ + && apk index --allow-untrusted -o ${tempDir}/packages/alpine/${apkArch}/APKINDEX.tar.gz ${tempDir}/packages/alpine/${apkArch}/*.apk \ + && abuild-sign -k ${tempDir}/.abuild/abuild-key.rsa ${tempDir}/packages/alpine/${apkArch}/APKINDEX.tar.gz \ + " \ + && cp ${tempDir}/.abuild/abuild-key.rsa.pub /etc/apk/keys/ \ + && apk del --no-network .build-deps \ + && apk add -X ${tempDir}/packages/alpine/ --no-cache $nginxPackages \ + ;; \ + esac \ +# remove checksum deps + && apk del --no-network .checksum-deps \ +# if we have leftovers from building, let's purge them (including extra, unnecessary build deps) + && if [ -n "$tempDir" ]; then rm -rf "$tempDir"; fi \ + && if [ -f "/etc/apk/keys/abuild-key.rsa.pub" ]; then rm -f /etc/apk/keys/abuild-key.rsa.pub; fi \ +# Add `envsubst` for templating environment variables + && apk add --no-cache gettext-envsubst \ +# Bring in tzdata so users could set the timezones through the environment +# variables + && apk add --no-cache tzdata \ +# forward request and error logs to docker log collector + && ln -sf /dev/stdout /var/log/nginx/access.log \ + && ln -sf /dev/stderr /var/log/nginx/error.log \ +# create a docker-entrypoint.d directory + && mkdir /docker-entrypoint.d + +# implement changes required to run NGINX as an unprivileged user +RUN sed -i 's,listen 80;,listen 8080;,' /etc/nginx/conf.d/default.conf \ + && sed -i '/user nginx;/d' /etc/nginx/nginx.conf \ + && sed -i 's,\(/var\)\{0\,1\}/run/nginx.pid,/tmp/nginx.pid,' /etc/nginx/nginx.conf \ + && sed -i "/^http {/a \ proxy_temp_path /tmp/proxy_temp;\n client_body_temp_path /tmp/client_temp;\n fastcgi_temp_path /tmp/fastcgi_temp;\n uwsgi_temp_path /tmp/uwsgi_temp;\n scgi_temp_path /tmp/scgi_temp;\n" /etc/nginx/nginx.conf \ +# nginx user must own the cache and etc directory to write cache and tweak the nginx config + && chown -R $UID:0 /var/cache/nginx \ + && chmod -R g+w /var/cache/nginx \ + && chown -R $UID:0 /etc/nginx \ + && chmod -R g+w /etc/nginx + +COPY docker-entrypoint.sh / +COPY 10-listen-on-ipv6-by-default.sh /docker-entrypoint.d +COPY 15-local-resolvers.envsh /docker-entrypoint.d +COPY 20-envsubst-on-templates.sh /docker-entrypoint.d +COPY 30-tune-worker-processes.sh /docker-entrypoint.d +ENTRYPOINT ["/docker-entrypoint.sh"] + +EXPOSE 8080 + +STOPSIGNAL SIGQUIT + +USER $UID + +CMD ["nginx", "-g", "daemon off;"] diff --git a/unprivileged/stable/alpine-slim/docker-entrypoint.sh b/unprivileged/stable/alpine-slim/docker-entrypoint.sh new file mode 100755 index 000000000..8ea04f217 --- /dev/null +++ b/unprivileged/stable/alpine-slim/docker-entrypoint.sh @@ -0,0 +1,47 @@ +#!/bin/sh +# vim:sw=4:ts=4:et + +set -e + +entrypoint_log() { + if [ -z "${NGINX_ENTRYPOINT_QUIET_LOGS:-}" ]; then + echo "$@" + fi +} + +if [ "$1" = "nginx" ] || [ "$1" = "nginx-debug" ]; then + if /usr/bin/find "/docker-entrypoint.d/" -mindepth 1 -maxdepth 1 -type f -print -quit 2>/dev/null | read v; then + entrypoint_log "$0: /docker-entrypoint.d/ is not empty, will attempt to perform configuration" + + entrypoint_log "$0: Looking for shell scripts in /docker-entrypoint.d/" + find "/docker-entrypoint.d/" -follow -type f -print | sort -V | while read -r f; do + case "$f" in + *.envsh) + if [ -x "$f" ]; then + entrypoint_log "$0: Sourcing $f"; + . "$f" + else + # warn on shell scripts without exec bit + entrypoint_log "$0: Ignoring $f, not executable"; + fi + ;; + *.sh) + if [ -x "$f" ]; then + entrypoint_log "$0: Launching $f"; + "$f" + else + # warn on shell scripts without exec bit + entrypoint_log "$0: Ignoring $f, not executable"; + fi + ;; + *) entrypoint_log "$0: Ignoring $f";; + esac + done + + entrypoint_log "$0: Configuration complete; ready for start up" + else + entrypoint_log "$0: No files found in /docker-entrypoint.d/, skipping configuration" + fi +fi + +exec "$@" diff --git a/unprivileged/stable/alpine/Dockerfile b/unprivileged/stable/alpine/Dockerfile new file mode 100644 index 000000000..53c7dc39f --- /dev/null +++ b/unprivileged/stable/alpine/Dockerfile @@ -0,0 +1,88 @@ +# +# NOTE: THIS DOCKERFILE IS GENERATED VIA "update.sh" +# +# PLEASE DO NOT EDIT IT DIRECTLY. +# +ARG IMAGE=nginxinc/nginx-unprivileged:1.28.2-alpine-slim +FROM $IMAGE + +ENV NJS_VERSION=0.9.5 +ENV NJS_RELEASE=1 + +ARG UID=101 +ARG GID=101 + +USER root + +RUN set -x \ + && apkArch="$(cat /etc/apk/arch)" \ + && nginxPackages=" \ + nginx=${NGINX_VERSION}-r${PKG_RELEASE} \ + nginx-module-xslt=${NGINX_VERSION}-r${DYNPKG_RELEASE} \ + nginx-module-geoip=${NGINX_VERSION}-r${DYNPKG_RELEASE} \ + nginx-module-image-filter=${NGINX_VERSION}-r${DYNPKG_RELEASE} \ + nginx-module-njs=${NGINX_VERSION}.${NJS_VERSION}-r${NJS_RELEASE} \ + nginx-module-acme=${NGINX_VERSION}.${ACME_VERSION}-r${PKG_RELEASE} \ + " \ +# install prerequisites for public key and pkg-oss checks + && apk add --no-cache --virtual .checksum-deps \ + openssl \ + && case "$apkArch" in \ + x86_64|aarch64) \ +# arches officially built by upstream + apk add -X "https://nginx.org/packages/alpine/v$(egrep -o '^[0-9]+\.[0-9]+' /etc/alpine-release)/main" --no-cache $nginxPackages \ + ;; \ + *) \ +# we're on an architecture upstream doesn't officially build for +# let's build binaries from the published packaging sources + set -x \ + && tempDir="$(mktemp -d)" \ + && chown nobody:nobody $tempDir \ + && apk add --no-cache --virtual .build-deps \ + gcc \ + libc-dev \ + make \ + openssl-dev \ + pcre2-dev \ + zlib-dev \ + linux-headers \ + libxslt-dev \ + gd-dev \ + geoip-dev \ + libedit-dev \ + bash \ + alpine-sdk \ + findutils \ + curl \ + && su nobody -s /bin/sh -c " \ + export HOME=${tempDir} \ + && cd ${tempDir} \ + && curl -f -L -O https://github.com/nginx/pkg-oss/archive/${NGINX_VERSION}-${PKG_RELEASE}.tar.gz \ + && PKGOSSCHECKSUM=\"ef4545c05b1632a056482e3dbb47bb5d7393238318db3491e8bb308218cdb5f32dbb2ac73509097ac2426fd73270bc97836843a8b1846a396fd94e60826f7e3f *${NGINX_VERSION}-${PKG_RELEASE}.tar.gz\" \ + && if [ \"\$(openssl sha512 -r ${NGINX_VERSION}-${PKG_RELEASE}.tar.gz)\" = \"\$PKGOSSCHECKSUM\" ]; then \ + echo \"pkg-oss tarball checksum verification succeeded!\"; \ + else \ + echo \"pkg-oss tarball checksum verification failed!\"; \ + exit 1; \ + fi \ + && tar xzvf ${NGINX_VERSION}-${PKG_RELEASE}.tar.gz \ + && cd pkg-oss-${NGINX_VERSION}-${PKG_RELEASE} \ + && cd alpine \ + && make module-geoip module-image-filter module-njs module-xslt module-acme \ + && apk index --allow-untrusted -o ${tempDir}/packages/alpine/${apkArch}/APKINDEX.tar.gz ${tempDir}/packages/alpine/${apkArch}/*.apk \ + && abuild-sign -k ${tempDir}/.abuild/abuild-key.rsa ${tempDir}/packages/alpine/${apkArch}/APKINDEX.tar.gz \ + " \ + && cp ${tempDir}/.abuild/abuild-key.rsa.pub /etc/apk/keys/ \ + && apk del --no-network .build-deps \ + && apk add -X ${tempDir}/packages/alpine/ --no-cache $nginxPackages \ + ;; \ + esac \ +# remove checksum deps + && apk del --no-network .checksum-deps \ +# if we have leftovers from building, let's purge them (including extra, unnecessary build deps) + && if [ -n "$tempDir" ]; then rm -rf "$tempDir"; fi \ + && if [ -f "/etc/apk/keys/abuild-key.rsa.pub" ]; then rm -f /etc/apk/keys/abuild-key.rsa.pub; fi \ +# Bring in curl and ca-certificates to make registering on DNS SD easier + && apk add --no-cache curl ca-certificates + +USER $UID diff --git a/unprivileged/stable/debian-otel/Dockerfile b/unprivileged/stable/debian-otel/Dockerfile new file mode 100644 index 000000000..c8cd23392 --- /dev/null +++ b/unprivileged/stable/debian-otel/Dockerfile @@ -0,0 +1,109 @@ +# +# NOTE: THIS DOCKERFILE IS GENERATED VIA "update.sh" +# +# PLEASE DO NOT EDIT IT DIRECTLY. +# +ARG IMAGE=nginxinc/nginx-unprivileged:1.28.2 +FROM $IMAGE + +ENV OTEL_VERSION=0.1.2 + +ARG UID=101 +ARG GID=101 + +USER root + +RUN set -x; \ + NGINX_GPGKEY_PATH=/etc/apt/keyrings/nginx-archive-keyring.gpg; \ + dpkgArch="$(dpkg --print-architecture)" \ + && nginxPackages=" \ + nginx=${NGINX_VERSION}-${PKG_RELEASE} \ + nginx-module-xslt=${NGINX_VERSION}-${DYNPKG_RELEASE} \ + nginx-module-geoip=${NGINX_VERSION}-${DYNPKG_RELEASE} \ + nginx-module-image-filter=${NGINX_VERSION}-${DYNPKG_RELEASE} \ + nginx-module-njs=${NGINX_VERSION}+${NJS_VERSION}-${NJS_RELEASE} \ + nginx-module-acme=${NGINX_VERSION}+${ACME_VERSION}-${PKG_RELEASE} \ + nginx-module-otel=${NGINX_VERSION}+${OTEL_VERSION}-${PKG_RELEASE} \ + " \ + && case "$dpkgArch" in \ + amd64|arm64) \ +# arches officially built by upstream + echo "deb [signed-by=$NGINX_GPGKEY_PATH] https://nginx.org/packages/debian/ trixie nginx" >> /etc/apt/sources.list.d/nginx.list \ + && apt-get update \ + ;; \ + *) \ +# we're on an architecture upstream doesn't officially build for +# let's build binaries from the published packaging sources +# new directory for storing sources and .deb files + tempDir="$(mktemp -d)" \ + && chmod 777 "$tempDir" \ +# (777 to ensure APT's "_apt" user can access it too) + \ +# save list of currently-installed packages so build dependencies can be cleanly removed later + && savedAptMark="$(apt-mark showmanual)" \ + \ +# build .deb files from upstream's packaging sources + && apt-get update \ + && apt-get install --no-install-recommends --no-install-suggests -y \ + curl \ + devscripts \ + equivs \ + git \ + libxml2-utils \ + lsb-release \ + xsltproc \ + && ( \ + cd "$tempDir" \ + && REVISION="${NGINX_VERSION}-${PKG_RELEASE}" \ + && REVISION=${REVISION%~*} \ + && curl -f -L -O https://github.com/nginx/pkg-oss/archive/${REVISION}.tar.gz \ + && PKGOSSCHECKSUM="ef4545c05b1632a056482e3dbb47bb5d7393238318db3491e8bb308218cdb5f32dbb2ac73509097ac2426fd73270bc97836843a8b1846a396fd94e60826f7e3f *${REVISION}.tar.gz" \ + && if [ "$(openssl sha512 -r ${REVISION}.tar.gz)" = "$PKGOSSCHECKSUM" ]; then \ + echo "pkg-oss tarball checksum verification succeeded!"; \ + else \ + echo "pkg-oss tarball checksum verification failed!"; \ + exit 1; \ + fi \ + && tar xzvf ${REVISION}.tar.gz \ + && cd pkg-oss-${REVISION} \ + && cd debian \ + && for target in module-otel; do \ + make rules-$target; \ + mk-build-deps --install --tool="apt-get -o Debug::pkgProblemResolver=yes --no-install-recommends --yes" \ + debuild-$target/nginx-$NGINX_VERSION/debian/control; \ + done \ + && make module-otel \ + ) \ +# we don't remove APT lists here because they get re-downloaded and removed later + \ +# reset apt-mark's "manual" list so that "purge --auto-remove" will remove all build dependencies +# (which is done after we install the built packages so we don't have to redownload any overlapping dependencies) + && apt-mark showmanual | xargs apt-mark auto > /dev/null \ + && { [ -z "$savedAptMark" ] || apt-mark manual $savedAptMark; } \ + \ +# create a temporary local APT repo to install from (so that dependency resolution can be handled by APT, as it should be) + && ls -lAFh "$tempDir" \ + && ( cd "$tempDir" && dpkg-scanpackages . > Packages ) \ + && grep '^Package: ' "$tempDir/Packages" \ + && echo "deb [ trusted=yes ] file://$tempDir ./" > /etc/apt/sources.list.d/temp.list \ +# work around the following APT issue by using "Acquire::GzipIndexes=false" (overriding "/etc/apt/apt.conf.d/docker-gzip-indexes") +# Could not open file /var/lib/apt/lists/partial/_tmp_tmp.ODWljpQfkE_._Packages - open (13: Permission denied) +# ... +# E: Failed to fetch store:/var/lib/apt/lists/partial/_tmp_tmp.ODWljpQfkE_._Packages Could not open file /var/lib/apt/lists/partial/_tmp_tmp.ODWljpQfkE_._Packages - open (13: Permission denied) + && apt-get -o Acquire::GzipIndexes=false update \ + ;; \ + esac \ + \ + && apt-get install --no-install-recommends --no-install-suggests -y \ + $nginxPackages \ + gettext-base \ + curl \ + && apt-get remove --purge --auto-remove -y && rm -rf /var/lib/apt/lists/* /etc/apt/sources.list.d/nginx.list \ + \ +# if we have leftovers from building, let's purge them (including extra, unnecessary build deps) + && if [ -n "$tempDir" ]; then \ + apt-get purge -y --auto-remove \ + && rm -rf "$tempDir" /etc/apt/sources.list.d/temp.list; \ + fi + +USER $UID diff --git a/unprivileged/stable/debian-perl/Dockerfile b/unprivileged/stable/debian-perl/Dockerfile new file mode 100644 index 000000000..8add4c7c4 --- /dev/null +++ b/unprivileged/stable/debian-perl/Dockerfile @@ -0,0 +1,107 @@ +# +# NOTE: THIS DOCKERFILE IS GENERATED VIA "update.sh" +# +# PLEASE DO NOT EDIT IT DIRECTLY. +# +ARG IMAGE=nginxinc/nginx-unprivileged:1.28.2 +FROM $IMAGE + +ARG UID=101 +ARG GID=101 + +USER root + +RUN set -x; \ + NGINX_GPGKEY_PATH=/etc/apt/keyrings/nginx-archive-keyring.gpg; \ + dpkgArch="$(dpkg --print-architecture)" \ + && nginxPackages=" \ + nginx=${NGINX_VERSION}-${PKG_RELEASE} \ + nginx-module-xslt=${NGINX_VERSION}-${DYNPKG_RELEASE} \ + nginx-module-geoip=${NGINX_VERSION}-${DYNPKG_RELEASE} \ + nginx-module-image-filter=${NGINX_VERSION}-${DYNPKG_RELEASE} \ + nginx-module-perl=${NGINX_VERSION}-${DYNPKG_RELEASE} \ + nginx-module-njs=${NGINX_VERSION}+${NJS_VERSION}-${NJS_RELEASE} \ + nginx-module-acme=${NGINX_VERSION}+${ACME_VERSION}-${PKG_RELEASE} \ + " \ + && case "$dpkgArch" in \ + amd64|arm64) \ +# arches officially built by upstream + echo "deb [signed-by=$NGINX_GPGKEY_PATH] https://nginx.org/packages/debian/ trixie nginx" >> /etc/apt/sources.list.d/nginx.list \ + && apt-get update \ + ;; \ + *) \ +# we're on an architecture upstream doesn't officially build for +# let's build binaries from the published packaging sources +# new directory for storing sources and .deb files + tempDir="$(mktemp -d)" \ + && chmod 777 "$tempDir" \ +# (777 to ensure APT's "_apt" user can access it too) + \ +# save list of currently-installed packages so build dependencies can be cleanly removed later + && savedAptMark="$(apt-mark showmanual)" \ + \ +# build .deb files from upstream's packaging sources + && apt-get update \ + && apt-get install --no-install-recommends --no-install-suggests -y \ + curl \ + devscripts \ + equivs \ + git \ + libxml2-utils \ + lsb-release \ + xsltproc \ + && ( \ + cd "$tempDir" \ + && REVISION="${NGINX_VERSION}-${PKG_RELEASE}" \ + && REVISION=${REVISION%~*} \ + && curl -f -L -O https://github.com/nginx/pkg-oss/archive/${REVISION}.tar.gz \ + && PKGOSSCHECKSUM="ef4545c05b1632a056482e3dbb47bb5d7393238318db3491e8bb308218cdb5f32dbb2ac73509097ac2426fd73270bc97836843a8b1846a396fd94e60826f7e3f *${REVISION}.tar.gz" \ + && if [ "$(openssl sha512 -r ${REVISION}.tar.gz)" = "$PKGOSSCHECKSUM" ]; then \ + echo "pkg-oss tarball checksum verification succeeded!"; \ + else \ + echo "pkg-oss tarball checksum verification failed!"; \ + exit 1; \ + fi \ + && tar xzvf ${REVISION}.tar.gz \ + && cd pkg-oss-${REVISION} \ + && cd debian \ + && for target in module-perl; do \ + make rules-$target; \ + mk-build-deps --install --tool="apt-get -o Debug::pkgProblemResolver=yes --no-install-recommends --yes" \ + debuild-$target/nginx-$NGINX_VERSION/debian/control; \ + done \ + && make module-perl \ + ) \ +# we don't remove APT lists here because they get re-downloaded and removed later + \ +# reset apt-mark's "manual" list so that "purge --auto-remove" will remove all build dependencies +# (which is done after we install the built packages so we don't have to redownload any overlapping dependencies) + && apt-mark showmanual | xargs apt-mark auto > /dev/null \ + && { [ -z "$savedAptMark" ] || apt-mark manual $savedAptMark; } \ + \ +# create a temporary local APT repo to install from (so that dependency resolution can be handled by APT, as it should be) + && ls -lAFh "$tempDir" \ + && ( cd "$tempDir" && dpkg-scanpackages . > Packages ) \ + && grep '^Package: ' "$tempDir/Packages" \ + && echo "deb [ trusted=yes ] file://$tempDir ./" > /etc/apt/sources.list.d/temp.list \ +# work around the following APT issue by using "Acquire::GzipIndexes=false" (overriding "/etc/apt/apt.conf.d/docker-gzip-indexes") +# Could not open file /var/lib/apt/lists/partial/_tmp_tmp.ODWljpQfkE_._Packages - open (13: Permission denied) +# ... +# E: Failed to fetch store:/var/lib/apt/lists/partial/_tmp_tmp.ODWljpQfkE_._Packages Could not open file /var/lib/apt/lists/partial/_tmp_tmp.ODWljpQfkE_._Packages - open (13: Permission denied) + && apt-get -o Acquire::GzipIndexes=false update \ + ;; \ + esac \ + \ + && apt-get install --no-install-recommends --no-install-suggests -y \ + $nginxPackages \ + gettext-base \ + curl \ + && apt-get remove --purge --auto-remove -y && rm -rf /var/lib/apt/lists/* /etc/apt/sources.list.d/nginx.list \ + \ +# if we have leftovers from building, let's purge them (including extra, unnecessary build deps) + && if [ -n "$tempDir" ]; then \ + apt-get purge -y --auto-remove \ + && rm -rf "$tempDir" /etc/apt/sources.list.d/temp.list; \ + fi + +USER $UID diff --git a/unprivileged/stable/debian/10-listen-on-ipv6-by-default.sh b/unprivileged/stable/debian/10-listen-on-ipv6-by-default.sh new file mode 100755 index 000000000..176607941 --- /dev/null +++ b/unprivileged/stable/debian/10-listen-on-ipv6-by-default.sh @@ -0,0 +1,67 @@ +#!/bin/sh +# vim:sw=4:ts=4:et + +set -e + +entrypoint_log() { + if [ -z "${NGINX_ENTRYPOINT_QUIET_LOGS:-}" ]; then + echo "$@" + fi +} + +ME=$(basename "$0") +DEFAULT_CONF_FILE="etc/nginx/conf.d/default.conf" + +# check if we have ipv6 available +if [ ! -f "/proc/net/if_inet6" ]; then + entrypoint_log "$ME: info: ipv6 not available" + exit 0 +fi + +if [ ! -f "/$DEFAULT_CONF_FILE" ]; then + entrypoint_log "$ME: info: /$DEFAULT_CONF_FILE is not a file or does not exist" + exit 0 +fi + +# check if the file can be modified, e.g. not on a r/o filesystem +touch /$DEFAULT_CONF_FILE 2>/dev/null || { entrypoint_log "$ME: info: can not modify /$DEFAULT_CONF_FILE (read-only file system?)"; exit 0; } + +# check if the file is already modified, e.g. on a container restart +grep -q "listen \[::\]:8080;" /$DEFAULT_CONF_FILE && { entrypoint_log "$ME: info: IPv6 listen already enabled"; exit 0; } + +if [ -f "/etc/os-release" ]; then + . /etc/os-release +else + entrypoint_log "$ME: info: can not guess the operating system" + exit 0 +fi + +entrypoint_log "$ME: info: Getting the checksum of /$DEFAULT_CONF_FILE" + +case "$ID" in + "debian") + CHECKSUM=$(dpkg-query --show --showformat='${Conffiles}\n' nginx | grep $DEFAULT_CONF_FILE | cut -d' ' -f 3) + echo "$CHECKSUM /$DEFAULT_CONF_FILE" | md5sum -c - >/dev/null 2>&1 || { + entrypoint_log "$ME: info: /$DEFAULT_CONF_FILE differs from the packaged version" + exit 0 + } + ;; + "alpine") + CHECKSUM=$(apk manifest nginx 2>/dev/null| grep $DEFAULT_CONF_FILE | cut -d' ' -f 1 | cut -d ':' -f 2) + echo "$CHECKSUM /$DEFAULT_CONF_FILE" | sha1sum -c - >/dev/null 2>&1 || { + entrypoint_log "$ME: info: /$DEFAULT_CONF_FILE differs from the packaged version" + exit 0 + } + ;; + *) + entrypoint_log "$ME: info: Unsupported distribution" + exit 0 + ;; +esac + +# enable ipv6 on default.conf listen sockets +sed -i -E 's,listen 8080;,listen 8080;\n listen [::]:8080;,' /$DEFAULT_CONF_FILE + +entrypoint_log "$ME: info: Enabled listen on IPv6 in /$DEFAULT_CONF_FILE" + +exit 0 diff --git a/unprivileged/stable/debian/15-local-resolvers.envsh b/unprivileged/stable/debian/15-local-resolvers.envsh new file mode 100755 index 000000000..e830ddacd --- /dev/null +++ b/unprivileged/stable/debian/15-local-resolvers.envsh @@ -0,0 +1,15 @@ +#!/bin/sh +# vim:sw=2:ts=2:sts=2:et + +set -eu + +LC_ALL=C +PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin + +[ "${NGINX_ENTRYPOINT_LOCAL_RESOLVERS:-}" ] || return 0 + +NGINX_LOCAL_RESOLVERS=$(awk 'BEGIN{ORS=" "} $1=="nameserver" {if ($2 ~ ":") {print "["$2"]"} else {print $2}}' /etc/resolv.conf) + +NGINX_LOCAL_RESOLVERS="${NGINX_LOCAL_RESOLVERS% }" + +export NGINX_LOCAL_RESOLVERS diff --git a/unprivileged/stable/debian/20-envsubst-on-templates.sh b/unprivileged/stable/debian/20-envsubst-on-templates.sh new file mode 100755 index 000000000..6938405be --- /dev/null +++ b/unprivileged/stable/debian/20-envsubst-on-templates.sh @@ -0,0 +1,78 @@ +#!/bin/sh + +set -e + +ME=$(basename "$0") + +entrypoint_log() { + if [ -z "${NGINX_ENTRYPOINT_QUIET_LOGS:-}" ]; then + echo "$@" + fi +} + +add_stream_block() { + local conffile="/etc/nginx/nginx.conf" + + if grep -q -E "\s*stream\s*\{" "$conffile"; then + entrypoint_log "$ME: $conffile contains a stream block; include $stream_output_dir/*.conf to enable stream templates" + else + # check if the file can be modified, e.g. not on a r/o filesystem + touch "$conffile" 2>/dev/null || { entrypoint_log "$ME: info: can not modify $conffile (read-only file system?)"; exit 0; } + entrypoint_log "$ME: Appending stream block to $conffile to include $stream_output_dir/*.conf" + cat << END >> "$conffile" +# added by "$ME" on "$(date)" +stream { + include $stream_output_dir/*.conf; +} +END + fi +} + +auto_envsubst() { + local template_dir="${NGINX_ENVSUBST_TEMPLATE_DIR:-/etc/nginx/templates}" + local suffix="${NGINX_ENVSUBST_TEMPLATE_SUFFIX:-.template}" + local output_dir="${NGINX_ENVSUBST_OUTPUT_DIR:-/etc/nginx/conf.d}" + local stream_suffix="${NGINX_ENVSUBST_STREAM_TEMPLATE_SUFFIX:-.stream-template}" + local stream_output_dir="${NGINX_ENVSUBST_STREAM_OUTPUT_DIR:-/etc/nginx/stream-conf.d}" + local filter="${NGINX_ENVSUBST_FILTER:-}" + + local template defined_envs relative_path output_path subdir + defined_envs=$(printf '${%s} ' $(awk "END { for (name in ENVIRON) { print ( name ~ /${filter}/ ) ? name : \"\" } }" < /dev/null )) + [ -d "$template_dir" ] || return 0 + if [ ! -w "$output_dir" ]; then + entrypoint_log "$ME: ERROR: $template_dir exists, but $output_dir is not writable" + return 0 + fi + find "$template_dir" -follow -type f -name "*$suffix" -print | while read -r template; do + relative_path="${template#"$template_dir/"}" + output_path="$output_dir/${relative_path%"$suffix"}" + subdir=$(dirname "$relative_path") + # create a subdirectory where the template file exists + mkdir -p "$output_dir/$subdir" + entrypoint_log "$ME: Running envsubst on $template to $output_path" + envsubst "$defined_envs" < "$template" > "$output_path" + done + + # Print the first file with the stream suffix, this will be false if there are none + if test -n "$(find "$template_dir" -name "*$stream_suffix" -print -quit)"; then + mkdir -p "$stream_output_dir" + if [ ! -w "$stream_output_dir" ]; then + entrypoint_log "$ME: ERROR: $template_dir exists, but $stream_output_dir is not writable" + return 0 + fi + add_stream_block + find "$template_dir" -follow -type f -name "*$stream_suffix" -print | while read -r template; do + relative_path="${template#"$template_dir/"}" + output_path="$stream_output_dir/${relative_path%"$stream_suffix"}" + subdir=$(dirname "$relative_path") + # create a subdirectory where the template file exists + mkdir -p "$stream_output_dir/$subdir" + entrypoint_log "$ME: Running envsubst on $template to $output_path" + envsubst "$defined_envs" < "$template" > "$output_path" + done + fi +} + +auto_envsubst + +exit 0 diff --git a/unprivileged/stable/debian/30-tune-worker-processes.sh b/unprivileged/stable/debian/30-tune-worker-processes.sh new file mode 100755 index 000000000..defb994f3 --- /dev/null +++ b/unprivileged/stable/debian/30-tune-worker-processes.sh @@ -0,0 +1,188 @@ +#!/bin/sh +# vim:sw=2:ts=2:sts=2:et + +set -eu + +LC_ALL=C +ME=$(basename "$0") +PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin + +[ "${NGINX_ENTRYPOINT_WORKER_PROCESSES_AUTOTUNE:-}" ] || exit 0 + +touch /etc/nginx/nginx.conf 2>/dev/null || { echo >&2 "$ME: error: can not modify /etc/nginx/nginx.conf (read-only file system?)"; exit 0; } + +ceildiv() { + num=$1 + div=$2 + echo $(( (num + div - 1) / div )) +} + +get_cpuset() { + cpusetroot=$1 + cpusetfile=$2 + ncpu=0 + [ -f "$cpusetroot/$cpusetfile" ] || return 1 + for token in $( tr ',' ' ' < "$cpusetroot/$cpusetfile" ); do + case "$token" in + *-*) + count=$( seq $(echo "$token" | tr '-' ' ') | wc -l ) + ncpu=$(( ncpu+count )) + ;; + *) + ncpu=$(( ncpu+1 )) + ;; + esac + done + echo "$ncpu" +} + +get_quota() { + cpuroot=$1 + ncpu=0 + [ -f "$cpuroot/cpu.cfs_quota_us" ] || return 1 + [ -f "$cpuroot/cpu.cfs_period_us" ] || return 1 + cfs_quota=$( cat "$cpuroot/cpu.cfs_quota_us" ) + cfs_period=$( cat "$cpuroot/cpu.cfs_period_us" ) + [ "$cfs_quota" = "-1" ] && return 1 + [ "$cfs_period" = "0" ] && return 1 + ncpu=$( ceildiv "$cfs_quota" "$cfs_period" ) + [ "$ncpu" -gt 0 ] || return 1 + echo "$ncpu" +} + +get_quota_v2() { + cpuroot=$1 + ncpu=0 + [ -f "$cpuroot/cpu.max" ] || return 1 + cfs_quota=$( cut -d' ' -f 1 < "$cpuroot/cpu.max" ) + cfs_period=$( cut -d' ' -f 2 < "$cpuroot/cpu.max" ) + [ "$cfs_quota" = "max" ] && return 1 + [ "$cfs_period" = "0" ] && return 1 + ncpu=$( ceildiv "$cfs_quota" "$cfs_period" ) + [ "$ncpu" -gt 0 ] || return 1 + echo "$ncpu" +} + +get_cgroup_v1_path() { + needle=$1 + found= + foundroot= + mountpoint= + + [ -r "/proc/self/mountinfo" ] || return 1 + [ -r "/proc/self/cgroup" ] || return 1 + + while IFS= read -r line; do + case "$needle" in + "cpuset") + case "$line" in + *cpuset*) + found=$( echo "$line" | cut -d ' ' -f 4,5 ) + break + ;; + esac + ;; + "cpu") + case "$line" in + *cpuset*) + ;; + *cpu,cpuacct*|*cpuacct,cpu|*cpuacct*|*cpu*) + found=$( echo "$line" | cut -d ' ' -f 4,5 ) + break + ;; + esac + esac + done << __EOF__ +$( grep -F -- '- cgroup ' /proc/self/mountinfo ) +__EOF__ + + while IFS= read -r line; do + controller=$( echo "$line" | cut -d: -f 2 ) + case "$needle" in + "cpuset") + case "$controller" in + cpuset) + mountpoint=$( echo "$line" | cut -d: -f 3 ) + break + ;; + esac + ;; + "cpu") + case "$controller" in + cpu,cpuacct|cpuacct,cpu|cpuacct|cpu) + mountpoint=$( echo "$line" | cut -d: -f 3 ) + break + ;; + esac + ;; + esac +done << __EOF__ +$( grep -F -- 'cpu' /proc/self/cgroup ) +__EOF__ + + case "${found%% *}" in + "/") + foundroot="${found##* }$mountpoint" + ;; + "$mountpoint") + foundroot="${found##* }" + ;; + esac + echo "$foundroot" +} + +get_cgroup_v2_path() { + found= + foundroot= + mountpoint= + + [ -r "/proc/self/mountinfo" ] || return 1 + [ -r "/proc/self/cgroup" ] || return 1 + + while IFS= read -r line; do + found=$( echo "$line" | cut -d ' ' -f 4,5 ) + done << __EOF__ +$( grep -F -- '- cgroup2 ' /proc/self/mountinfo ) +__EOF__ + + while IFS= read -r line; do + mountpoint=$( echo "$line" | cut -d: -f 3 ) +done << __EOF__ +$( grep -F -- '0::' /proc/self/cgroup ) +__EOF__ + + case "${found%% *}" in + "") + return 1 + ;; + "/") + foundroot="${found##* }$mountpoint" + ;; + "$mountpoint" | /../*) + foundroot="${found##* }" + ;; + esac + echo "$foundroot" +} + +ncpu_online=$( getconf _NPROCESSORS_ONLN ) +ncpu_cpuset= +ncpu_quota= +ncpu_cpuset_v2= +ncpu_quota_v2= + +cpuset=$( get_cgroup_v1_path "cpuset" ) && ncpu_cpuset=$( get_cpuset "$cpuset" "cpuset.effective_cpus" ) || ncpu_cpuset=$ncpu_online +cpu=$( get_cgroup_v1_path "cpu" ) && ncpu_quota=$( get_quota "$cpu" ) || ncpu_quota=$ncpu_online +cgroup_v2=$( get_cgroup_v2_path ) && ncpu_cpuset_v2=$( get_cpuset "$cgroup_v2" "cpuset.cpus.effective" ) || ncpu_cpuset_v2=$ncpu_online +cgroup_v2=$( get_cgroup_v2_path ) && ncpu_quota_v2=$( get_quota_v2 "$cgroup_v2" ) || ncpu_quota_v2=$ncpu_online + +ncpu=$( printf "%s\n%s\n%s\n%s\n%s\n" \ + "$ncpu_online" \ + "$ncpu_cpuset" \ + "$ncpu_quota" \ + "$ncpu_cpuset_v2" \ + "$ncpu_quota_v2" \ + | sort -n \ + | head -n 1 ) + +sed -i.bak -r 's/^(worker_processes)(.*)$/# Commented out by '"$ME"' on '"$(date)"'\n#\1\2\n\1 '"$ncpu"';/' /etc/nginx/nginx.conf diff --git a/unprivileged/stable/debian/Dockerfile b/unprivileged/stable/debian/Dockerfile new file mode 100644 index 000000000..b20748972 --- /dev/null +++ b/unprivileged/stable/debian/Dockerfile @@ -0,0 +1,164 @@ +# +# NOTE: THIS DOCKERFILE IS GENERATED VIA "update.sh" +# +# PLEASE DO NOT EDIT IT DIRECTLY. +# +ARG IMAGE=debian:trixie-slim +FROM $IMAGE + +LABEL maintainer="NGINX Docker Maintainers " + +ENV NGINX_VERSION=1.28.2 +ENV NJS_VERSION=0.9.5 +ENV NJS_RELEASE=1~trixie +ENV PKG_RELEASE=1~trixie +ENV DYNPKG_RELEASE=1~trixie + +ARG UID=101 +ARG GID=101 + +RUN set -x \ +# create nginx user/group first, to be consistent throughout docker variants + && groupadd --system --gid $GID nginx || true \ + && useradd --system --gid nginx --no-create-home --home /nonexistent --comment "nginx user" --shell /bin/false --uid $UID nginx || true \ + && apt-get update \ + && apt-get install --no-install-recommends --no-install-suggests -y gnupg1 ca-certificates \ + && \ + NGINX_GPGKEYS="573BFD6B3D8FBC641079A6ABABF5BD827BD9BF62 8540A6F18833A80E9C1653A42FD21310B49F6B46 9E9BE90EACBCDE69FE9B204CBCDCD8A38D88A2B3"; \ + NGINX_GPGKEY_PATH=/etc/apt/keyrings/nginx-archive-keyring.gpg; \ + export GNUPGHOME="$(mktemp -d)"; \ + found=''; \ + for NGINX_GPGKEY in $NGINX_GPGKEYS; do \ + for server in \ + hkp://keyserver.ubuntu.com:80 \ + pgp.mit.edu \ + ; do \ + echo "Fetching GPG key $NGINX_GPGKEY from $server"; \ + gpg1 --batch --keyserver "$server" --keyserver-options timeout=10 --recv-keys "$NGINX_GPGKEY" && found=yes && break; \ + done; \ + test -z "$found" && echo >&2 "error: failed to fetch GPG key $NGINX_GPGKEY" && exit 1; \ + done; \ + gpg1 --batch --export $NGINX_GPGKEYS > "$NGINX_GPGKEY_PATH" ; \ + rm -rf "$GNUPGHOME"; \ + apt-get remove --purge --auto-remove -y gnupg1 && rm -rf /var/lib/apt/lists/* \ + && dpkgArch="$(dpkg --print-architecture)" \ + && nginxPackages=" \ + nginx=${NGINX_VERSION}-${PKG_RELEASE} \ + nginx-module-xslt=${NGINX_VERSION}-${DYNPKG_RELEASE} \ + nginx-module-geoip=${NGINX_VERSION}-${DYNPKG_RELEASE} \ + nginx-module-image-filter=${NGINX_VERSION}-${DYNPKG_RELEASE} \ + nginx-module-njs=${NGINX_VERSION}+${NJS_VERSION}-${NJS_RELEASE} \ + nginx-module-acme=${NGINX_VERSION}+${ACME_VERSION}-${PKG_RELEASE} \ + " \ + && case "$dpkgArch" in \ + amd64|arm64) \ +# arches officially built by upstream + echo "deb [signed-by=$NGINX_GPGKEY_PATH] https://nginx.org/packages/debian/ trixie nginx" >> /etc/apt/sources.list.d/nginx.list \ + && apt-get update \ + ;; \ + *) \ +# we're on an architecture upstream doesn't officially build for +# let's build binaries from the published packaging sources +# new directory for storing sources and .deb files + tempDir="$(mktemp -d)" \ + && chmod 777 "$tempDir" \ +# (777 to ensure APT's "_apt" user can access it too) + \ +# save list of currently-installed packages so build dependencies can be cleanly removed later + && savedAptMark="$(apt-mark showmanual)" \ + \ +# build .deb files from upstream's packaging sources + && apt-get update \ + && apt-get install --no-install-recommends --no-install-suggests -y \ + curl \ + devscripts \ + equivs \ + git \ + libxml2-utils \ + lsb-release \ + xsltproc \ + && ( \ + cd "$tempDir" \ + && REVISION="${NGINX_VERSION}-${PKG_RELEASE}" \ + && REVISION=${REVISION%~*} \ + && curl -f -L -O https://github.com/nginx/pkg-oss/archive/${REVISION}.tar.gz \ + && PKGOSSCHECKSUM="ef4545c05b1632a056482e3dbb47bb5d7393238318db3491e8bb308218cdb5f32dbb2ac73509097ac2426fd73270bc97836843a8b1846a396fd94e60826f7e3f *${REVISION}.tar.gz" \ + && if [ "$(openssl sha512 -r ${REVISION}.tar.gz)" = "$PKGOSSCHECKSUM" ]; then \ + echo "pkg-oss tarball checksum verification succeeded!"; \ + else \ + echo "pkg-oss tarball checksum verification failed!"; \ + exit 1; \ + fi \ + && tar xzvf ${REVISION}.tar.gz \ + && cd pkg-oss-${REVISION} \ + && cd debian \ + && for target in base module-geoip module-image-filter module-njs module-xslt module-acme; do \ + make rules-$target; \ + mk-build-deps --install --tool="apt-get -o Debug::pkgProblemResolver=yes --no-install-recommends --yes" \ + debuild-$target/nginx-$NGINX_VERSION/debian/control; \ + done \ + && make base module-geoip module-image-filter module-njs module-xslt module-acme \ + ) \ +# we don't remove APT lists here because they get re-downloaded and removed later + \ +# reset apt-mark's "manual" list so that "purge --auto-remove" will remove all build dependencies +# (which is done after we install the built packages so we don't have to redownload any overlapping dependencies) + && apt-mark showmanual | xargs apt-mark auto > /dev/null \ + && { [ -z "$savedAptMark" ] || apt-mark manual $savedAptMark; } \ + \ +# create a temporary local APT repo to install from (so that dependency resolution can be handled by APT, as it should be) + && ls -lAFh "$tempDir" \ + && ( cd "$tempDir" && dpkg-scanpackages . > Packages ) \ + && grep '^Package: ' "$tempDir/Packages" \ + && echo "deb [ trusted=yes ] file://$tempDir ./" > /etc/apt/sources.list.d/temp.list \ +# work around the following APT issue by using "Acquire::GzipIndexes=false" (overriding "/etc/apt/apt.conf.d/docker-gzip-indexes") +# Could not open file /var/lib/apt/lists/partial/_tmp_tmp.ODWljpQfkE_._Packages - open (13: Permission denied) +# ... +# E: Failed to fetch store:/var/lib/apt/lists/partial/_tmp_tmp.ODWljpQfkE_._Packages Could not open file /var/lib/apt/lists/partial/_tmp_tmp.ODWljpQfkE_._Packages - open (13: Permission denied) + && apt-get -o Acquire::GzipIndexes=false update \ + ;; \ + esac \ + \ + && apt-get install --no-install-recommends --no-install-suggests -y \ + $nginxPackages \ + gettext-base \ + curl \ + && apt-get remove --purge --auto-remove -y && rm -rf /var/lib/apt/lists/* /etc/apt/sources.list.d/nginx.list \ + \ +# if we have leftovers from building, let's purge them (including extra, unnecessary build deps) + && if [ -n "$tempDir" ]; then \ + apt-get purge -y --auto-remove \ + && rm -rf "$tempDir" /etc/apt/sources.list.d/temp.list; \ + fi \ +# forward request and error logs to docker log collector + && ln -sf /dev/stdout /var/log/nginx/access.log \ + && ln -sf /dev/stderr /var/log/nginx/error.log \ +# create a docker-entrypoint.d directory + && mkdir /docker-entrypoint.d + +# implement changes required to run NGINX as an unprivileged user +RUN sed -i 's,listen 80;,listen 8080;,' /etc/nginx/conf.d/default.conf \ + && sed -i '/user nginx;/d' /etc/nginx/nginx.conf \ + && sed -i 's,\(/var\)\{0\,1\}/run/nginx.pid,/tmp/nginx.pid,' /etc/nginx/nginx.conf \ + && sed -i "/^http {/a \ proxy_temp_path /tmp/proxy_temp;\n client_body_temp_path /tmp/client_temp;\n fastcgi_temp_path /tmp/fastcgi_temp;\n uwsgi_temp_path /tmp/uwsgi_temp;\n scgi_temp_path /tmp/scgi_temp;\n" /etc/nginx/nginx.conf \ + && sed -i 's,PIDFILE=${PIDFILE:-/run/nginx.pid},PIDFILE=${PIDFILE:-/tmp/nginx.pid},' /etc/init.d/nginx \ +# nginx user must own the cache and etc directory to write cache and tweak the nginx config + && chown -R $UID:0 /var/cache/nginx \ + && chmod -R g+w /var/cache/nginx \ + && chown -R $UID:0 /etc/nginx \ + && chmod -R g+w /etc/nginx + +COPY docker-entrypoint.sh / +COPY 10-listen-on-ipv6-by-default.sh /docker-entrypoint.d +COPY 15-local-resolvers.envsh /docker-entrypoint.d +COPY 20-envsubst-on-templates.sh /docker-entrypoint.d +COPY 30-tune-worker-processes.sh /docker-entrypoint.d +ENTRYPOINT ["/docker-entrypoint.sh"] + +EXPOSE 8080 + +STOPSIGNAL SIGQUIT + +USER $UID + +CMD ["nginx", "-g", "daemon off;"] diff --git a/unprivileged/stable/debian/docker-entrypoint.sh b/unprivileged/stable/debian/docker-entrypoint.sh new file mode 100755 index 000000000..8ea04f217 --- /dev/null +++ b/unprivileged/stable/debian/docker-entrypoint.sh @@ -0,0 +1,47 @@ +#!/bin/sh +# vim:sw=4:ts=4:et + +set -e + +entrypoint_log() { + if [ -z "${NGINX_ENTRYPOINT_QUIET_LOGS:-}" ]; then + echo "$@" + fi +} + +if [ "$1" = "nginx" ] || [ "$1" = "nginx-debug" ]; then + if /usr/bin/find "/docker-entrypoint.d/" -mindepth 1 -maxdepth 1 -type f -print -quit 2>/dev/null | read v; then + entrypoint_log "$0: /docker-entrypoint.d/ is not empty, will attempt to perform configuration" + + entrypoint_log "$0: Looking for shell scripts in /docker-entrypoint.d/" + find "/docker-entrypoint.d/" -follow -type f -print | sort -V | while read -r f; do + case "$f" in + *.envsh) + if [ -x "$f" ]; then + entrypoint_log "$0: Sourcing $f"; + . "$f" + else + # warn on shell scripts without exec bit + entrypoint_log "$0: Ignoring $f, not executable"; + fi + ;; + *.sh) + if [ -x "$f" ]; then + entrypoint_log "$0: Launching $f"; + "$f" + else + # warn on shell scripts without exec bit + entrypoint_log "$0: Ignoring $f, not executable"; + fi + ;; + *) entrypoint_log "$0: Ignoring $f";; + esac + done + + entrypoint_log "$0: Configuration complete; ready for start up" + else + entrypoint_log "$0: No files found in /docker-entrypoint.d/, skipping configuration" + fi +fi + +exec "$@" From 16de1cb07066cae8fa53d9b2e8c345145be3bdc8 Mon Sep 17 00:00:00 2001 From: Konstantin Pavlov Date: Tue, 10 Feb 2026 18:51:35 -0800 Subject: [PATCH 07/19] generate-stackbrew-library: adapted for the Dockerfiles move --- generate-stackbrew-library.sh | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/generate-stackbrew-library.sh b/generate-stackbrew-library.sh index fe79a05c7..af874ef9f 100755 --- a/generate-stackbrew-library.sh +++ b/generate-stackbrew-library.sh @@ -1,6 +1,8 @@ #!/bin/bash set -eu +[ -z "${GENERATE_STACKBREW_LIBRARY_TARGET:-}" ] && target="library" || target="$GENERATE_STACKBREW_LIBRARY_TARGET" + declare -A aliases aliases=( [mainline]='1 1.29 latest' @@ -58,9 +60,9 @@ join() { for version in "${versions[@]}"; do debian_otel="debian-otel" alpine_otel="alpine-otel" - commit="$(dirCommit "$version/$base")" + commit="$(dirCommit "$target/$version/$base")" - fullVersion="$(git show "$commit":"$version/$base/Dockerfile" | awk '$1 == "ENV" && $2 == "NGINX_VERSION" { print $3; exit }')" + fullVersion="$(git show "$commit":"$target/$version/$base/Dockerfile" | awk '$1 == "ENV" && $2 == "NGINX_VERSION" { print $3; exit }')" versionAliases=( $fullVersion ) if [ "$version" != "$fullVersion" ]; then @@ -68,7 +70,7 @@ for version in "${versions[@]}"; do fi versionAliases+=( ${aliases[$version]:-} ) - debianVersion="$(git show "$commit":"$version/$base/Dockerfile" | awk -F"[-:]" '$1 == "FROM debian" { print $2; exit }')" + debianVersion="$(git show "$commit":"$target/$version/$base/Dockerfile" | awk -F"[-:]" '$1 == "FROM debian" { print $2; exit }')" debianAliases=( ${versionAliases[@]/%/-$debianVersion} ) debianAliases=( "${debianAliases[@]//latest-/}" ) @@ -77,11 +79,11 @@ for version in "${versions[@]}"; do Tags: $(join ', ' "${versionAliases[@]}"), $(join ', ' "${debianAliases[@]}") Architectures: ${debian_architectures[$version]} GitCommit: $commit - Directory: $version/$base + Directory: $target/$version/$base EOE for variant in debian-perl; do - commit="$(dirCommit "$version/$variant")" + commit="$(dirCommit "$target/$version/$variant")" variantAliases=( "${versionAliases[@]/%/-perl}" ) variantAliases+=( "${versionAliases[@]/%/-${variant/debian/$debianVersion}}" ) @@ -92,12 +94,12 @@ for version in "${versions[@]}"; do Tags: $(join ', ' "${variantAliases[@]}") Architectures: ${debian_architectures[$version]} GitCommit: $commit - Directory: $version/$variant + Directory: $target/$version/$variant EOE done for variant in $debian_otel; do - commit="$(dirCommit "$version/$variant")" + commit="$(dirCommit "$target/$version/$variant")" variantAliases=( "${versionAliases[@]/%/-otel}" ) variantAliases+=( "${versionAliases[@]/%/-${variant/debian/$debianVersion}}" ) @@ -108,16 +110,16 @@ for version in "${versions[@]}"; do Tags: $(join ', ' "${variantAliases[@]}") Architectures: amd64, arm64v8 GitCommit: $commit - Directory: $version/$variant + Directory: $target/$version/$variant EOE done - commit="$(dirCommit "$version/alpine-slim")" - alpineVersion="$(git show "$commit":"$version/alpine-slim/Dockerfile" | awk -F: '$1 == "FROM alpine" { print $2; exit }')" + commit="$(dirCommit "$target/$version/alpine-slim")" + alpineVersion="$(git show "$commit":"$target/$version/alpine-slim/Dockerfile" | awk -F: '$1 == "FROM alpine" { print $2; exit }')" for variant in alpine alpine-perl alpine-slim; do - commit="$(dirCommit "$version/$variant")" + commit="$(dirCommit "$target/$version/$variant")" variantAliases=( "${versionAliases[@]/%/-$variant}" ) variantAliases+=( "${versionAliases[@]/%/-${variant/alpine/alpine$alpineVersion}}" ) @@ -128,12 +130,12 @@ for version in "${versions[@]}"; do Tags: $(join ', ' "${variantAliases[@]}") Architectures: arm64v8, arm32v6, arm32v7, ppc64le, s390x, i386, amd64, riscv64 GitCommit: $commit - Directory: $version/$variant + Directory: $target/$version/$variant EOE done for variant in $alpine_otel; do - commit="$(dirCommit "$version/$variant")" + commit="$(dirCommit "$target/$version/$variant")" variantAliases=( "${versionAliases[@]/%/-$variant}" ) variantAliases+=( "${versionAliases[@]/%/-${variant/alpine/alpine$alpineVersion}}" ) @@ -144,7 +146,7 @@ for version in "${versions[@]}"; do Tags: $(join ', ' "${variantAliases[@]}") Architectures: amd64, arm64v8 GitCommit: $commit - Directory: $version/$variant + Directory: $target/$version/$variant EOE done From f01c1e76e7edec00ea72ff510be6a2685387ae85 Mon Sep 17 00:00:00 2001 From: Konstantin Pavlov Date: Tue, 10 Feb 2026 19:00:51 -0800 Subject: [PATCH 08/19] sync-awsecr: adapted for the Dockerfiles move --- sync-awsecr.sh | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/sync-awsecr.sh b/sync-awsecr.sh index 599a33a12..916913814 100755 --- a/sync-awsecr.sh +++ b/sync-awsecr.sh @@ -52,8 +52,8 @@ join() { } for version in "${versions[@]}"; do - commit="$(dirCommit "$version/$base")" - fullVersion="$(git show "$commit":"$version/$base/Dockerfile" | awk '$1 == "ENV" && $2 == "NGINX_VERSION" { print $3; exit }')" + commit="$(dirCommit "library/$version/$base")" + fullVersion="$(git show "$commit":"library/$version/$base/Dockerfile" | awk '$1 == "ENV" && $2 == "NGINX_VERSION" { print $3; exit }')" pulllist+=( "$image:$fullVersion" ) for variant in perl alpine alpine-perl alpine-slim; do pulllist+=( "$image:$fullVersion-$variant" ) @@ -61,9 +61,9 @@ for version in "${versions[@]}"; do done for version in "${versions[@]}"; do - commit="$(dirCommit "$version/$base")" + commit="$(dirCommit "library/$version/$base")" - fullVersion="$(git show "$commit":"$version/$base/Dockerfile" | awk '$1 == "ENV" && $2 == "NGINX_VERSION" { print $3; exit }')" + fullVersion="$(git show "$commit":"library/$version/$base/Dockerfile" | awk '$1 == "ENV" && $2 == "NGINX_VERSION" { print $3; exit }')" versionAliases=( $fullVersion ) if [ "$version" != "$fullVersion" ]; then @@ -71,7 +71,7 @@ for version in "${versions[@]}"; do fi versionAliases+=( ${aliases[$version]:-} ) - debianVersion="$(git show "$commit":"$version/$base/Dockerfile" | awk -F"[-:]" '$1 == "FROM debian" { print $2; exit }')" + debianVersion="$(git show "$commit":"library/$version/$base/Dockerfile" | awk -F"[-:]" '$1 == "FROM debian" { print $2; exit }')" debianAliases=( ${versionAliases[@]/%/-$debianVersion} ) debianAliases=( "${debianAliases[@]//latest-/}" ) @@ -91,11 +91,11 @@ for version in "${versions[@]}"; do done done - commit="$(dirCommit "$version/alpine-slim")" - alpineVersion="$(git show "$commit":"$version/alpine-slim/Dockerfile" | awk -F: '$1 == "FROM alpine" { print $2; exit }')" + commit="$(dirCommit "library/$version/alpine-slim")" + alpineVersion="$(git show "$commit":"library/$version/alpine-slim/Dockerfile" | awk -F: '$1 == "FROM alpine" { print $2; exit }')" for variant in alpine alpine-perl alpine-slim; do - commit="$(dirCommit "$version/$variant")" + commit="$(dirCommit "library/$version/$variant")" variantAliases=( "${versionAliases[@]/%/-$variant}" ) variantAliases+=( "${versionAliases[@]/%/-${variant/alpine/alpine$alpineVersion}}" ) From 593024c333ff65e13874abad598a84e1c4a73762 Mon Sep 17 00:00:00 2001 From: Konstantin Pavlov Date: Wed, 11 Feb 2026 13:33:44 -0800 Subject: [PATCH 09/19] Don't hardcode to path to bash --- generate-stackbrew-library.sh | 2 +- sync-awsecr.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/generate-stackbrew-library.sh b/generate-stackbrew-library.sh index af874ef9f..4ec5e3913 100755 --- a/generate-stackbrew-library.sh +++ b/generate-stackbrew-library.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu [ -z "${GENERATE_STACKBREW_LIBRARY_TARGET:-}" ] && target="library" || target="$GENERATE_STACKBREW_LIBRARY_TARGET" diff --git a/sync-awsecr.sh b/sync-awsecr.sh index 916913814..fcc0af252 100755 --- a/sync-awsecr.sh +++ b/sync-awsecr.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu image="nginx" From 916954d0a406141f0a6beb636c7355147f72b363 Mon Sep 17 00:00:00 2001 From: Konstantin Pavlov Date: Wed, 11 Feb 2026 13:34:07 -0800 Subject: [PATCH 10/19] generate-stackbrew-library: made the parsers work with both targets --- generate-stackbrew-library.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/generate-stackbrew-library.sh b/generate-stackbrew-library.sh index 4ec5e3913..3df147eb8 100755 --- a/generate-stackbrew-library.sh +++ b/generate-stackbrew-library.sh @@ -62,7 +62,7 @@ for version in "${versions[@]}"; do alpine_otel="alpine-otel" commit="$(dirCommit "$target/$version/$base")" - fullVersion="$(git show "$commit":"$target/$version/$base/Dockerfile" | awk '$1 == "ENV" && $2 == "NGINX_VERSION" { print $3; exit }')" + fullVersion="$(git show "$commit":"$target/$version/$base/Dockerfile" | awk -F' |=| +' '$1 == "ENV" && $2 == "NGINX_VERSION" { print $3; exit }')" versionAliases=( $fullVersion ) if [ "$version" != "$fullVersion" ]; then @@ -70,7 +70,7 @@ for version in "${versions[@]}"; do fi versionAliases+=( ${aliases[$version]:-} ) - debianVersion="$(git show "$commit":"$target/$version/$base/Dockerfile" | awk -F"[-:]" '$1 == "FROM debian" { print $2; exit }')" + debianVersion="$(git show "$commit":"$target/$version/$base/Dockerfile" | awk -F"[-:]" '$1 ~ /^FROM debian|^ARG IMAGE=debian/ { print $2; exit }')" debianAliases=( ${versionAliases[@]/%/-$debianVersion} ) debianAliases=( "${debianAliases[@]//latest-/}" ) @@ -116,7 +116,7 @@ for version in "${versions[@]}"; do commit="$(dirCommit "$target/$version/alpine-slim")" - alpineVersion="$(git show "$commit":"$target/$version/alpine-slim/Dockerfile" | awk -F: '$1 == "FROM alpine" { print $2; exit }')" + alpineVersion="$(git show "$commit":"$target/$version/alpine-slim/Dockerfile" | awk -F: '$1 ~ /^FROM alpine|^ARG IMAGE=alpine/ { print $2; exit }')" for variant in alpine alpine-perl alpine-slim; do commit="$(dirCommit "$target/$version/$variant")" From d9c77d63de1cdbce3f403f57c5a07fb027993c3e Mon Sep 17 00:00:00 2001 From: Konstantin Pavlov Date: Wed, 11 Feb 2026 18:46:54 -0800 Subject: [PATCH 11/19] CI: cancel previous jobs when a new one is scheduled --- .github/workflows/ci.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 37d7a6218..1964fe1c2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,6 +10,10 @@ defaults: run: shell: 'bash -Eeuo pipefail -x {0}' +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: generate-jobs: From a6577944cf25aca8e0b5da9586e9ae412c037489 Mon Sep 17 00:00:00 2001 From: Konstantin Pavlov Date: Wed, 11 Feb 2026 18:47:17 -0800 Subject: [PATCH 12/19] CI: bump bashbrew to contemporary --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1964fe1c2..870455978 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,7 +23,7 @@ jobs: strategy: ${{ steps.generate-jobs.outputs.strategy }} steps: - uses: actions/checkout@v3 - - uses: docker-library/bashbrew@v0.1.12 + - uses: docker-library/bashbrew@v0.1.13 - id: generate-jobs name: Generate Jobs run: | From 2f2a9d46785fb23d650cb4bf7981d428d6ea59b1 Mon Sep 17 00:00:00 2001 From: Konstantin Pavlov Date: Wed, 11 Feb 2026 18:47:40 -0800 Subject: [PATCH 13/19] CI: check unprivileged variants on CI runs --- .github/workflows/ci.yml | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 870455978..16bd924ac 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -29,8 +29,15 @@ jobs: run: | strategy="$(GITHUB_REPOSITORY=nginx "$BASHBREW_SCRIPTS/github-actions/generate.sh")" strategy="$(GITHUB_REPOSITORY=nginx "$BASHBREW_SCRIPTS/github-actions/munge-i386.sh" -c <<<"$strategy")" - echo "strategy=$strategy" >> "$GITHUB_OUTPUT" - jq . <<<"$strategy" # sanity check / debugging aid + strategyu="$(jq 'walk(if type == "string" then gsub("nginx:"; "nginxinc/nginx-unprivileged:") else . end)' <<<"$strategy")" + strategyu="$(jq 'walk(if type == "string" then gsub("^library/"; "unprivileged/") else . end)' <<<"$strategyu")" + strategyu="$(jq 'walk(if type == "string" then gsub("'\''library/"; "'\''unprivileged/") else . end)' <<<"$strategyu")" + strategyu="$(jq '.matrix.include[].name |= "unpriv " + .' <<<"$strategyu")" + + EOF="EOF-$RANDOM-$RANDOM-$RANDOM" + echo "strategy<<$EOF" >> "$GITHUB_OUTPUT" + jq --argjson unpriv "$strategyu" '.matrix.include += $unpriv.matrix.include' <<< "$strategy" | tee -a "$GITHUB_OUTPUT" + echo "$EOF" >> "$GITHUB_OUTPUT" test: needs: generate-jobs From 50182f751f80781cc81ab9cee989a1caaca0c31d Mon Sep 17 00:00:00 2001 From: Konstantin Pavlov Date: Wed, 11 Feb 2026 18:53:20 -0800 Subject: [PATCH 14/19] Unprivileged: added nginx-module-acme to the main image Follows a763c4634835305c25d86aae388890ace226af1b --- templates/unprivileged/Dockerfile-alpine.template | 3 +++ templates/unprivileged/Dockerfile-debian.template | 3 +++ 2 files changed, 6 insertions(+) diff --git a/templates/unprivileged/Dockerfile-alpine.template b/templates/unprivileged/Dockerfile-alpine.template index b81400922..b58a65513 100644 --- a/templates/unprivileged/Dockerfile-alpine.template +++ b/templates/unprivileged/Dockerfile-alpine.template @@ -3,6 +3,7 @@ FROM $IMAGE ENV NJS_VERSION=%%NJS_VERSION%% ENV NJS_RELEASE=%%NJS_RELEASE%% +ENV ACME_VERSION=%%ACME_VERSION%% ARG UID=101 ARG GID=101 @@ -43,6 +44,8 @@ RUN set -x \ alpine-sdk \ findutils \ curl \ + cargo \ + clang-libclang \ && su nobody -s /bin/sh -c " \ export HOME=${tempDir} \ && cd ${tempDir} \ diff --git a/templates/unprivileged/Dockerfile-debian.template b/templates/unprivileged/Dockerfile-debian.template index ed3076da9..5c5c77a30 100644 --- a/templates/unprivileged/Dockerfile-debian.template +++ b/templates/unprivileged/Dockerfile-debian.template @@ -6,6 +6,7 @@ LABEL maintainer="NGINX Docker Maintainers " ENV NGINX_VERSION=%%NGINX_VERSION%% ENV NJS_VERSION=%%NJS_VERSION%% ENV NJS_RELEASE=%%NJS_RELEASE%% +ENV ACME_VERSION=%%ACME_VERSION%% ENV PKG_RELEASE=%%PKG_RELEASE%% ENV DYNPKG_RELEASE=%%DYNPKG_RELEASE%% @@ -59,6 +60,7 @@ RUN set -x \ # build .deb files from upstream's packaging sources && apt-get update \ && apt-get install --no-install-recommends --no-install-suggests -y \ + cargo \ curl \ devscripts \ equivs \ @@ -68,6 +70,7 @@ RUN set -x \ xsltproc \ && ( \ cd "$tempDir" \ + && export CARGO_HOME="$tempDir/.cargo" \ && REVISION="%%REVISION%%" \ && REVISION=${REVISION%~*} \ && curl -f -L -O https://github.com/nginx/pkg-oss/archive/${REVISION}.tar.gz \ From 58279a3ea9b5aa19a32b2c9c8b87f4d1f1aa6332 Mon Sep 17 00:00:00 2001 From: Konstantin Pavlov Date: Wed, 11 Feb 2026 18:53:46 -0800 Subject: [PATCH 15/19] Regenerated Dockerfiles --- unprivileged/mainline/alpine/Dockerfile | 3 +++ unprivileged/mainline/debian/Dockerfile | 3 +++ unprivileged/stable/alpine/Dockerfile | 3 +++ unprivileged/stable/debian/Dockerfile | 3 +++ 4 files changed, 12 insertions(+) diff --git a/unprivileged/mainline/alpine/Dockerfile b/unprivileged/mainline/alpine/Dockerfile index 703344389..ba8a27b3f 100644 --- a/unprivileged/mainline/alpine/Dockerfile +++ b/unprivileged/mainline/alpine/Dockerfile @@ -8,6 +8,7 @@ FROM $IMAGE ENV NJS_VERSION=0.9.5 ENV NJS_RELEASE=1 +ENV ACME_VERSION=0.3.1 ARG UID=101 ARG GID=101 @@ -54,6 +55,8 @@ RUN set -x \ alpine-sdk \ findutils \ curl \ + cargo \ + clang-libclang \ && su nobody -s /bin/sh -c " \ export HOME=${tempDir} \ && cd ${tempDir} \ diff --git a/unprivileged/mainline/debian/Dockerfile b/unprivileged/mainline/debian/Dockerfile index 7c185ff7e..8c52072f4 100644 --- a/unprivileged/mainline/debian/Dockerfile +++ b/unprivileged/mainline/debian/Dockerfile @@ -11,6 +11,7 @@ LABEL maintainer="NGINX Docker Maintainers " ENV NGINX_VERSION=1.29.5 ENV NJS_VERSION=0.9.5 ENV NJS_RELEASE=1~trixie +ENV ACME_VERSION=0.3.1 ENV PKG_RELEASE=1~trixie ENV DYNPKG_RELEASE=1~trixie @@ -70,6 +71,7 @@ RUN set -x \ # build .deb files from upstream's packaging sources && apt-get update \ && apt-get install --no-install-recommends --no-install-suggests -y \ + cargo \ curl \ devscripts \ equivs \ @@ -79,6 +81,7 @@ RUN set -x \ xsltproc \ && ( \ cd "$tempDir" \ + && export CARGO_HOME="$tempDir/.cargo" \ && REVISION="${NGINX_VERSION}-${PKG_RELEASE}" \ && REVISION=${REVISION%~*} \ && curl -f -L -O https://github.com/nginx/pkg-oss/archive/${REVISION}.tar.gz \ diff --git a/unprivileged/stable/alpine/Dockerfile b/unprivileged/stable/alpine/Dockerfile index 53c7dc39f..603c7ac13 100644 --- a/unprivileged/stable/alpine/Dockerfile +++ b/unprivileged/stable/alpine/Dockerfile @@ -8,6 +8,7 @@ FROM $IMAGE ENV NJS_VERSION=0.9.5 ENV NJS_RELEASE=1 +ENV ACME_VERSION=0.3.1 ARG UID=101 ARG GID=101 @@ -54,6 +55,8 @@ RUN set -x \ alpine-sdk \ findutils \ curl \ + cargo \ + clang-libclang \ && su nobody -s /bin/sh -c " \ export HOME=${tempDir} \ && cd ${tempDir} \ diff --git a/unprivileged/stable/debian/Dockerfile b/unprivileged/stable/debian/Dockerfile index b20748972..f5263619b 100644 --- a/unprivileged/stable/debian/Dockerfile +++ b/unprivileged/stable/debian/Dockerfile @@ -11,6 +11,7 @@ LABEL maintainer="NGINX Docker Maintainers " ENV NGINX_VERSION=1.28.2 ENV NJS_VERSION=0.9.5 ENV NJS_RELEASE=1~trixie +ENV ACME_VERSION=0.3.1 ENV PKG_RELEASE=1~trixie ENV DYNPKG_RELEASE=1~trixie @@ -70,6 +71,7 @@ RUN set -x \ # build .deb files from upstream's packaging sources && apt-get update \ && apt-get install --no-install-recommends --no-install-suggests -y \ + cargo \ curl \ devscripts \ equivs \ @@ -79,6 +81,7 @@ RUN set -x \ xsltproc \ && ( \ cd "$tempDir" \ + && export CARGO_HOME="$tempDir/.cargo" \ && REVISION="${NGINX_VERSION}-${PKG_RELEASE}" \ && REVISION=${REVISION%~*} \ && curl -f -L -O https://github.com/nginx/pkg-oss/archive/${REVISION}.tar.gz \ From 68e0749d7a2a732889ff941b6eb6ae80d9b7a788 Mon Sep 17 00:00:00 2001 From: Konstantin Pavlov Date: Wed, 11 Feb 2026 19:09:05 -0800 Subject: [PATCH 16/19] WIP: enable tests for unprivileged images --- .test/config.sh | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.test/config.sh b/.test/config.sh index e371f4043..fc3a1196c 100755 --- a/.test/config.sh +++ b/.test/config.sh @@ -8,4 +8,13 @@ imageTests+=( workers modules ' + [nginxinc/nginx-unprivileged]=' + ipv6 + static + templates + templates-resolver + templates-resolver-ipv6 + workers + modules + ' ) From 0ba70eff0df116bc3c8d838668e72f36a7870b66 Mon Sep 17 00:00:00 2001 From: Konstantin Pavlov Date: Fri, 13 Feb 2026 14:08:49 -0800 Subject: [PATCH 17/19] Tests: drop ivp6 tests from unprivileged images for now It fails (as it should), to be fixed later. --- .test/config.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/.test/config.sh b/.test/config.sh index fc3a1196c..a0b020f6d 100755 --- a/.test/config.sh +++ b/.test/config.sh @@ -9,7 +9,6 @@ imageTests+=( modules ' [nginxinc/nginx-unprivileged]=' - ipv6 static templates templates-resolver From d83a70bc9df7015370833cf25f34fb01be6efe1a Mon Sep 17 00:00:00 2001 From: Konstantin Pavlov Date: Fri, 13 Feb 2026 14:10:21 -0800 Subject: [PATCH 18/19] Tests: static: use a default port based on the image type --- .test/tests/static/run.sh | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.test/tests/static/run.sh b/.test/tests/static/run.sh index f026bedb3..1b44549a0 100755 --- a/.test/tests/static/run.sh +++ b/.test/tests/static/run.sh @@ -8,6 +8,9 @@ dir="$(dirname "$(readlink -f "$BASH_SOURCE")")" image="$1" +port=80 +if echo "$image" | grep -q "unprivileged"; then port=8080; fi + clientImage='buildpack-deps:buster-curl' # ensure the clientImage is ready and available if ! docker image inspect "$clientImage" &> /dev/null; then @@ -37,7 +40,7 @@ _request() { docker run --rm \ --link "$cid":nginx \ "$clientImage" \ - curl -fsSL -X"$method" --connect-to '::nginx:' "$@" "$proto://example.com/$url" + curl -fsSL -X"$method" --connect-to "::nginx:$port" "$@" "$proto://example.com/$url" } . "$HOME/oi/test/retry.sh" '[ "$(_request GET / --output /dev/null || echo $?)" != 7 ]' From 7786a3302de85601bb5039adf865b101f4a50f4d Mon Sep 17 00:00:00 2001 From: Konstantin Pavlov Date: Wed, 18 Feb 2026 16:49:09 -0800 Subject: [PATCH 19/19] Tests: disable modules test for unprivileged images --- .test/config.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/.test/config.sh b/.test/config.sh index a0b020f6d..5874e1133 100755 --- a/.test/config.sh +++ b/.test/config.sh @@ -14,6 +14,5 @@ imageTests+=( templates-resolver templates-resolver-ipv6 workers - modules ' )