commit 2af3420aec987c56e2e57e96d97677250cfff8e6 Author: Konstantin Demin Date: Thu Jul 11 13:37:35 2024 +0300 initial commit diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..ca4f779 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,198 @@ +FROM docker.io/rockdrilla/angie-conv:deps-v1 as deps + +## --- + +FROM deps as certs +SHELL [ "/bin/sh", "-ec" ] + +COPY /scripts/* /usr/local/sbin/ +COPY /extra-scripts/* /usr/local/sbin/ + +## consult https://github.com/certifi/python-certifi/ +ENV CERTIFI_COMMIT=bd8153872e9c6fc98f4023df9c2deaffea2fa463 + +RUN pkg='curl' ; \ + apt-install.sh ca-certificates ${pkg} ; \ + ## process certifi + ls -l /etc/ssl/certs/ca-certificates.crt ; \ + certifi-extras.sh ; \ + ls -l /etc/ssl/certs/ca-certificates.crt + +## --- + +FROM deps as pycache +SHELL [ "/bin/sh", "-ec" ] + +COPY /scripts/* /usr/local/sbin/ +COPY /extra-scripts/* /usr/local/sbin/ + +ENV PYTHONDONTWRITEBYTECODE='' + +## Python cache preseed +RUN libpython="${PYTHON_SITE_PACKAGES%/*}" ; \ + find "${libpython}/" -mindepth 1 -maxdepth 1 -printf '%P\0' \ + | sed -zEn \ + -e '/^(asyncio|collections|concurrent|encodings|html|importlib|json|logging|multiprocessing|re|urllib|xml)$/p' \ + | sort -zV \ + | env -C "${libpython}" xargs -0r \ + python3 -m compileall -q -j 2 ; \ + find "${PYTHON_SITE_PACKAGES}/" -mindepth 1 -maxdepth 1 -printf '%P\0' \ + | sed -zE \ + -e '/\.(dist-info|pth|txt)$/d' \ + -e '/^(pip|pkg_resources|setuptools|wheel)$/d' \ + | sort -zV \ + | env -C "${PYTHON_SITE_PACKAGES}" xargs -0r \ + python3 -m compileall -q -j 2 + +## Python cache warmup +RUN python3 -m site > /dev/null ; \ + echo > /tmp/f.j2 ; \ + jinja.py /tmp/f.j2 ; \ + pip-env.sh pip list -v >/dev/null ; \ + find "${PYTHON_SITE_PACKAGES}/pip/" -name __pycache__ -exec rm -rf {} + + +## Python cache adjustments +RUN d="@$(date '+%s')" ; \ + libpython="${PYTHON_SITE_PACKAGES%/*}" ; \ + find "${libpython}/" -name '*.pyc' -exec touch -m -d "$d" {} + ; \ + find "${libpython}/" -name __pycache__ -exec touch -m -d "$d" {} + + +## --- + +FROM deps +SHELL [ "/bin/sh", "-ec" ] + +## NB: NGX_DEBUG is set via build script + +COPY /Dockerfile /usr/local/share/ + +COPY --from=certs /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt +COPY --from=certs /usr/local/share/ca-certificates/ /usr/local/share/ca-certificates/ + +## RFC: Python cache +## TODO: reduce load by selecting only __pycache__ directories in either way +# COPY --from=pycache /usr/local/lib/ /usr/local/lib/ + +ENV ANGIE_MODULES_DIR=/usr/lib/angie/modules + +COPY /scripts/* /usr/local/bin/ + +RUN _UID=11111 _GID=11111 ; \ + echo "angie:x:${_UID}:${_GID}:Angie:/etc/angie:/bin/false" >> /etc/passwd ; \ + echo "angie:x:${_GID}:" >> /etc/group ; \ + echo 'angie:!:::::::' >> /etc/shadow + +RUN apt-install.sh angie ; \ + apt-clean.sh ; \ + ## verify Angie layout + [ -d "${ANGIE_MODULES_DIR}" ] ; \ + n='/usr/sbin/angie' ; \ + [ -x "$n-debug" ] ; \ + [ -x "$n-nodebug" ] ; \ + ## adjust Angie binaries + rm -fv "$n" ; \ + if [ "${NGX_DEBUG}" = 0 ] ; then \ + rm -fv "$n-debug" ; \ + mv -fv "$n-nodebug" "$n" ; \ + ln -fsv "${n##*/}" "$n-nodebug" ; \ + ln -fsv /bin/false "$n-debug" ; \ + else \ + rm -fv "$n-nodebug" ; \ + mv -fv "$n-debug" "$n" ; \ + ln -fsv "${n##*/}" "$n-debug" ; \ + ln -fsv /bin/false "$n-nodebug" ; \ + fi + +## preserve snippets from Angie config directory +## ref: https://git.angie.software/web-server/angie/src/tag/Angie-1.6.0/conf +RUN d=/etc/angie ; t=$(mktemp -d) ; \ + tar -C "$d" -cf - \ + fastcgi_params \ + fastcgi.conf \ + mime.types \ + prometheus_all.conf \ + scgi_params \ + uwsgi_params \ + | tar -C "$t" -xf - ; \ + rm -rf "$d" ; \ + install -d "$d" "$d/snip.dist" ; \ + tar -C "$t" -cf - . | tar -C "$d/snip.dist" -xf - ; \ + rm -rf "$t" ; \ + chown -hR 0:0 "$d" ; \ + chmod go-w "$d" ; \ + find "$d/" -type f -exec chmod 0644 {} + + +## produce own layout for Angie >:) +## /angie/ is persistence store +RUN install -d -o angie -g angie -m 03777 /angie /run/angie ; \ + ## adjust paths across filesystem + rm -rfv /var/cache/angie/ /var/lib/angie/ /var/log/angie/ ; \ + ln -sv /run/angie/cache /var/cache/angie ; \ + ln -sv /run/angie/lib /var/lib/angie ; \ + ln -sv /run/angie/log /var/log/angie ; \ + ## adjust paths in config directory + cd /etc/angie/ ; \ + ln -sv /run/angie run ; \ + ln -sv /run/angie/lock lock.d ; \ + ln -sv ${ANGIE_MODULES_DIR} modules.dist ; \ + ## hyper-modular paths: + data='conf mod modules njs site snip static' ; \ + vardata='cache lib log' ; \ + for n in ${data} ; do \ + for d in "$n" "$n.dist" ; do \ + [ -e "$d" ] || install -d "$d" ; \ + done ; \ + done ; \ + for n in ${data} ${vardata} ; do \ + ln -sv "/run/angie/$n" "$n.d" ; \ + done + +VOLUME [ "/run/angie" ] + +COPY /angie/ /etc/angie/ +RUN find /etc/angie/ -name .gitkeep -delete ; \ + find /etc/angie/ ! -type l -exec chmod go-w {} + + +## misc tools +RUN apt-install.sh \ + brotli \ + zstd \ + ; \ + apt-clean.sh + +## relatively lightweight modules +RUN apt-install-angie-mod.sh \ + auth-jwt \ + auth-spnego \ + brotli \ + cache-purge \ + echo \ + geoip2 \ + headers-more \ + subs \ + testcookie \ + upload \ + zip \ + zstd \ + ; \ + apt-clean.sh + +## image-entry.sh is placed into /usr/local/bin/ to allow custom entrypoint/chaining: +## - there's no need to change ENTRYPOINT/CMD +## - custom entrypoint should be placed in /usr/local/sbin/ +## - custom entrypoint should "exec" /usr/local/bin/image-entry.sh +COPY /image-entry.sh /usr/local/bin/ +COPY /image-entry.d/ /image-entry.d/ + +## must be bind-mounted only for local customization/overrides! +# RUN install -d /image-entry + +## misc defaults +ENV DUMB_INIT_SETSID=0 \ + MALLOC_ARENA_MAX=4 \ + GOMAXPROCS=4 + +STOPSIGNAL SIGQUIT + +ENTRYPOINT [ "image-entry.sh" ] +CMD [ "angie", "-g", "daemon off;" ] diff --git a/Dockerfile.base b/Dockerfile.base new file mode 100644 index 0000000..d7d9e3f --- /dev/null +++ b/Dockerfile.base @@ -0,0 +1,227 @@ +# FROM docker.io/debian:bookworm-slim as base-upstream +ARG PYTHONTAG=3.11.9-slim-bookworm +FROM docker.io/python:${PYTHONTAG} as base-upstream + +FROM base-upstream as base +SHELL [ "/bin/sh", "-ec" ] + +COPY /Dockerfile.base /usr/local/share/ + +COPY /scripts/* /usr/local/sbin/ +COPY /extra-scripts/* /usr/local/sbin/ + +## PATH: remove /sbin and /bin (/usr is merged) +ENV PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin \ + TMPDIR=/tmp \ + LANG=C.UTF-8 \ + LC_ALL=C.UTF-8 \ + TERM=linux \ + TZ=Etc/UTC \ + MALLOC_ARENA_MAX=2 \ + PYTHONUNBUFFERED=1 \ + PYTHONDONTWRITEBYTECODE=1 + +## local development +# ENV PIP_INDEX="http://127.0.0.1:8081/repository/proxy_pypi/pypi/" \ +# PIP_INDEX_URL="http://127.0.0.1:8081/repository/proxy_pypi/simple/" \ +# PIP_TRUSTED_HOST="localhost" + +COPY /apt/prefs.backports /etc/apt/preferences.d/backports +COPY /apt/sources.debian /etc/apt/sources.list.d/debian.sources + +## prevent services from auto-starting, part 1 +RUN s='/usr/sbin/policy-rc.d' ; b='/usr/bin/policy-rc.d' ; \ + rm -f "$s" "$b" ; \ + echo '#!/bin/sh' > "$b" ; \ + echo 'exit 101' >> "$b" ; \ + chmod 0755 "$b" ; \ + ln -s "$b" "$s" + +RUN divert_true() { divert-rm.sh "$1" ; ln -sv /bin/true "$1" ; } ; \ + ## prevent services from auto-starting, part 2 + divert_true /sbin/start-stop-daemon ; \ + ## always report that we're in chroot + divert_true /usr/bin/ischroot ; \ + ## hide systemd helpers + divert_true /usr/bin/deb-systemd-helper ; \ + divert_true /usr/bin/deb-systemd-invoke + +RUN apt-env.sh apt-get update ; \ + apt-env.sh apt-get upgrade -y ; \ + apt-clean.sh + +## perl-base: hardlink->symlink +RUN d=/usr/bin ; \ + find $d/ -wholename "$d/perl5*" -exec ln -fsv perl {} ';' ; \ + ls -li $d/perl* + +## remove unwanted binaries +RUN set -f ; \ + for i in \ + addgroup \ + addpart \ + adduser \ + apt-ftparchive \ + agetty \ + badblocks \ + blkdiscard \ + blkid \ + blkzone \ + blockdev \ + bsd-write \ + chage \ + chcpu \ + chfn \ + chgpasswd \ + chmem \ + chpasswd \ + chsh \ + cpgr \ + cppw \ + ctrlaltdel \ + debugfs \ + delgroup \ + delpart \ + deluser \ + dmesg \ + dumpe2fs \ + e2freefrag \ + e2fsck \ + e2image \ + e2label \ + e2mmpstatus \ + e2scrub \ + 'e2scrub*' \ + e2undo \ + e4crypt \ + e4defrag \ + expiry \ + faillock \ + fdformat \ + fincore \ + findfs \ + fsck \ + 'fsck.*' \ + fsfreeze \ + fstrim \ + getty \ + gpasswd \ + groupadd \ + groupdel \ + groupmems \ + groupmod \ + grpck \ + grpconv \ + grpunconv \ + hwclock \ + isosize \ + last \ + lastb \ + ldattach \ + losetup \ + lsblk \ + lsirq \ + lslogins \ + mcookie \ + mesg \ + mke2fs \ + mkfs \ + 'mkfs.*' \ + mkhomedir_helper \ + mklost+found \ + mkswap \ + mount \ + newgrp \ + newusers \ + pam-auth-update \ + pam_getenv \ + pam_namespace_helper \ + pam_timestamp_check \ + partx \ + passwd \ + pivot_root \ + pwck \ + pwconv \ + pwhistory_helper \ + pwunconv \ + raw \ + readprofile \ + resize2fs \ + resizepart \ + rtcwake \ + sg \ + shadowconfig \ + su \ + sulogin \ + swaplabel \ + swapoff \ + swapon \ + switch_root \ + tune2fs \ + umount \ + unix_chkpwd \ + unix_update \ + update-passwd \ + useradd \ + userdel \ + usermod \ + utmpdump \ + vigr \ + vipw \ + wall \ + wdctl \ + wipefs \ + write \ + 'write.*' \ + zramctl \ + ; do \ + for d in /usr/sbin /usr/bin /sbin /bin ; do \ + find "$d/" ! -type d -wholename "$d/$i" \ + | while read -r p ; do \ + [ -n "$p" ] || continue ; \ + [ -e "$p" ] || continue ; \ + dpkg -S "$p" >/dev/null 2>&1 || continue ; \ + divert-rm.sh "$p" ; \ + done ; \ + done ; \ + for d in /usr/sbin /usr/bin /sbin /bin ; do \ + find "$d/" ! -type d -wholename "$d/$i" \ + | while read -r p ; do \ + [ -n "$p" ] || continue ; \ + [ -e "$p" ] || continue ; \ + rm -fv "$p" ; \ + done ; \ + done ; \ + done + +RUN apt-remove.sh \ + ca-certificates \ + e2fsprogs \ + ; \ + apt-clean.sh + +## "docker.io/python"-specific cleanup +RUN rm -f /root/.wget-hsts + +RUN pip-env.sh pip list --format freeze \ + | grep -F '==' | awk -F= '{print $1}' \ + | xargs -r pip-env.sh pip install -U ; \ + python-rm-cache.sh "${PYTHON_SITE_PACKAGES}" + +RUN libpython="${PYTHON_SITE_PACKAGES%/*}" ; \ + rm -rfv \ + /usr/local/bin/idle* \ + "${libpython}/ensurepip/_bundled" \ + "${libpython}/idlelib" \ + "${libpython}/tkinter" \ + "${libpython}/turtle.py" \ + "${libpython}/turtledemo" \ + ; \ + python-rm-cache.sh /usr/local + +RUN find /usr/local/sbin/ ! -type d -ls -delete ; \ + find /run/ -mindepth 1 -ls -delete || : ; \ + install -d -m 01777 /run/lock + +ENTRYPOINT [ ] +CMD [ "bash" ] diff --git a/Dockerfile.deps b/Dockerfile.deps new file mode 100644 index 0000000..b001dee --- /dev/null +++ b/Dockerfile.deps @@ -0,0 +1,96 @@ +FROM docker.io/rockdrilla/angie-conv:base-v1 as base + +## --- + +FROM base as setup +SHELL [ "/bin/sh", "-ec" ] + +COPY /scripts/* /usr/local/sbin/ +COPY /extra-scripts/* /usr/local/sbin/ + +ADD https://angie.software/keys/angie-signing.gpg /tmp/angie.gpg.bin +COPY /apt/sources.angie /etc/apt/sources.list.d/angie.txt + +RUN pkg='gnupg' ; \ + apt-install.sh ${pkg} ; \ + ## process Angie GPG keyring / APT sources + gpg-export.sh /tmp/angie.gpg.bin /etc/apt/keyrings/angie.gpg.asc ; \ + rm -f /tmp/angie.gpg.bin ; \ + env -C /etc/apt/sources.list.d mv angie.txt angie.sources ; \ + ## verify sources! + apt-env.sh apt-get update ; \ + apt-remove.sh ${pkg} ; \ + apt-clean.sh + +ENV INSTALL_WHEELS='jinja2 netaddr psutil pyyaml wcmatch' +ENV DEV_PACKAGES='libyaml-dev' +# markupsafe, psutil +ENV CIBUILDWHEEL=1 +# pyyaml +ENV PYYAML_FORCE_CYTHON=1 + +RUN w=$(mktemp -d) ; \ + { apt-mark showauto ; apt-mark showmanual ; } | sort -uV > "$w/t0" ; \ + printf '%s\n' ${DEV_PACKAGES} | sort -uV > "$w/t1" ; \ + apt-install.sh ${DEV_PACKAGES} ; \ + { apt-mark showauto ; apt-mark showmanual ; } | sort -uV > "$w/t2" ; \ + set +e ; \ + grep -Fxv -f "$w/t0" "$w/t2" > "$w/t3" ; \ + grep -Fxv -f "$w/t1" "$w/t3" > "$w/t4" ; \ + grep -Ev -e '-(dev|doc)$' "$w/t4" > "${PYTHON_SITE_PACKAGES}/apt-deps.txt" ; \ + set -e ; \ + rm -rf "$w/" ; unset w ; \ + apt-install.sh build-essential ; \ + pip-env.sh pip install 'cython' ; \ + pip-env.sh pip install --no-binary :all: ${INSTALL_WHEELS} ; \ + pip-env.sh pip uninstall -y 'cython' ; \ + python-rm-cache.sh "${PYTHON_SITE_PACKAGES}" ; \ + rm -rf \ + "${PYTHON_SITE_PACKAGES}/psutil/tests" \ + ; \ + find "${PYTHON_SITE_PACKAGES}/" -type f -name '*.so*' -exec ls -l {} + ; \ + echo ; \ + find "${PYTHON_SITE_PACKAGES}/" -type f -name '*.so*' -printf '%p\0' \ + | sed -zE '/rust/d' \ + | xargs -0r strip --strip-debug ; \ + echo ; \ + find "${PYTHON_SITE_PACKAGES}/" -type f -name '*.so*' -exec ls -l {} + ; \ + apt-remove.sh build-essential ; \ + apt-clean.sh + +## --- + +FROM base as deps +SHELL [ "/bin/sh", "-ec" ] + +COPY /Dockerfile.deps /usr/local/share/ + +COPY --from=setup /etc/apt/keyrings/angie.gpg.asc /etc/apt/keyrings/ +COPY --from=setup /etc/apt/sources.list.d/angie.sources /etc/apt/sources.list.d/ + +## Python: site-packages +COPY --from=setup /usr/local/bin/ /usr/local/bin/ +COPY --from=setup /${PYTHON_SITE_PACKAGES}/ /${PYTHON_SITE_PACKAGES}/ + +COPY /scripts/* /usr/local/sbin/ + +## install missing dependencies for Python site-packages +RUN f="${PYTHON_SITE_PACKAGES}/apt-deps.txt" ; \ + [ -s "$f" ] || exit 0 ; \ + xargs -a "$f" apt-install.sh ; \ + apt-clean.sh + +## common deps +RUN apt-install.sh \ + dumb-init \ + gettext-base \ + jq \ + netbase \ + netcat-openbsd \ + openssl \ + procps \ + psmisc \ + ; \ + apt-clean.sh + +RUN find /usr/local/sbin/ ! -type d -ls -delete diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..67db858 --- /dev/null +++ b/LICENSE @@ -0,0 +1,175 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. diff --git a/angie/angie.conf b/angie/angie.conf new file mode 100644 index 0000000..ba02fb9 --- /dev/null +++ b/angie/angie.conf @@ -0,0 +1,22 @@ +pid run/angie.pid; +lock_file lock.d/angie.lock; + +## almost useless +include mod.d/core-*.conf; + +# mod-http.conf.in +# mod-mail.conf.in +# mod-stream.conf.in +include run/mod-*.conf; + +events { + include conf.d/core_ev-*.conf; + include snip.d/core_ev-*.conf; +} +include conf.d/core-*.conf; +include snip.d/core-*.conf; + +# ctx-http.conf.in +# ctx-mail.conf.in +# ctx-stream.conf.in +include run/ctx-*.conf; \ No newline at end of file diff --git a/angie/conf.dist/core-error-log.conf b/angie/conf.dist/core-error-log.conf new file mode 100644 index 0000000..566c8e7 --- /dev/null +++ b/angie/conf.dist/core-error-log.conf @@ -0,0 +1 @@ +error_log log.d/error.log warn; \ No newline at end of file diff --git a/angie/conf.dist/core-pcre-jit.conf b/angie/conf.dist/core-pcre-jit.conf new file mode 100644 index 0000000..a78c335 --- /dev/null +++ b/angie/conf.dist/core-pcre-jit.conf @@ -0,0 +1 @@ +pcre_jit on; \ No newline at end of file diff --git a/angie/conf.dist/core-preserve-env.conf.j2 b/angie/conf.dist/core-preserve-env.conf.j2 new file mode 100644 index 0000000..2f31b0f --- /dev/null +++ b/angie/conf.dist/core-preserve-env.conf.j2 @@ -0,0 +1,3 @@ +{% for v in os.getenv('NGX_CORE_ENV', '').split(sep=' ') -%} +env {{ v }}; +{% endfor -%} diff --git a/angie/conf.dist/core-user.conf.in b/angie/conf.dist/core-user.conf.in new file mode 100644 index 0000000..33929d2 --- /dev/null +++ b/angie/conf.dist/core-user.conf.in @@ -0,0 +1 @@ +user ${NGX_USER} ${NGX_GROUP}; \ No newline at end of file diff --git a/angie/conf.dist/core-worker.conf.in b/angie/conf.dist/core-worker.conf.in new file mode 100644 index 0000000..a2f6a1d --- /dev/null +++ b/angie/conf.dist/core-worker.conf.in @@ -0,0 +1,3 @@ +worker_processes ${NGX_WORKER_PROCESSES}; +worker_priority ${NGX_WORKER_PRIORITY}; +worker_rlimit_nofile ${NGX_WORKER_RLIMIT_NOFILE}; \ No newline at end of file diff --git a/angie/conf.dist/core_ev-worker.conf.in b/angie/conf.dist/core_ev-worker.conf.in new file mode 100644 index 0000000..87bf0c4 --- /dev/null +++ b/angie/conf.dist/core_ev-worker.conf.in @@ -0,0 +1 @@ +worker_connections ${NGX_WORKER_CONNECTIONS}; \ No newline at end of file diff --git a/angie/conf.dist/http-access-log.conf b/angie/conf.dist/http-access-log.conf new file mode 100644 index 0000000..1465352 --- /dev/null +++ b/angie/conf.dist/http-access-log.conf @@ -0,0 +1,12 @@ +log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + +log_format extended '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" rt="$request_time" ' + '"$http_user_agent" "$http_x_forwarded_for" ' + 'h="$host" sn="$server_name" ru="$request_uri" u="$uri" ' + 'ucs="$upstream_cache_status" ua="$upstream_addr" us="$upstream_status" ' + 'uct="$upstream_connect_time" urt="$upstream_response_time"'; + +access_log log.d/access.log main; diff --git a/angie/conf.dist/http-max-ranges.conf.in b/angie/conf.dist/http-max-ranges.conf.in new file mode 100644 index 0000000..91658f0 --- /dev/null +++ b/angie/conf.dist/http-max-ranges.conf.in @@ -0,0 +1 @@ +max_ranges ${NGX_HTTP_MAX_RANGES}; \ No newline at end of file diff --git a/angie/conf.dist/http-mime-types.conf b/angie/conf.dist/http-mime-types.conf new file mode 100644 index 0000000..6757e54 --- /dev/null +++ b/angie/conf.dist/http-mime-types.conf @@ -0,0 +1,8 @@ +include snip.d/mime.types; + +types { + font/ttf ttf; + application/font-sfnt otf; +} + +default_type application/octet-stream; diff --git a/angie/ctx-http.conf b/angie/ctx-http.conf new file mode 100644 index 0000000..cc64391 --- /dev/null +++ b/angie/ctx-http.conf @@ -0,0 +1,5 @@ +http { + include conf.d/http-*.conf; + include snip.d/http-*.conf; + include site.d/http-*.conf; +} \ No newline at end of file diff --git a/angie/ctx-mail.conf b/angie/ctx-mail.conf new file mode 100644 index 0000000..0780fca --- /dev/null +++ b/angie/ctx-mail.conf @@ -0,0 +1,5 @@ +mail { + include conf.d/mail-*.conf; + include snip.d/mail-*.conf; + include site.d/mail-*.conf; +} \ No newline at end of file diff --git a/angie/ctx-stream.conf b/angie/ctx-stream.conf new file mode 100644 index 0000000..1bf0cf5 --- /dev/null +++ b/angie/ctx-stream.conf @@ -0,0 +1,5 @@ +stream { + include conf.d/stream-*.conf; + include snip.d/stream-*.conf; + include site.d/stream-*.conf; +} \ No newline at end of file diff --git a/angie/mod-http.conf b/angie/mod-http.conf new file mode 100644 index 0000000..4164f42 --- /dev/null +++ b/angie/mod-http.conf @@ -0,0 +1 @@ +include mod.d/http-*.conf; \ No newline at end of file diff --git a/angie/mod-mail.conf b/angie/mod-mail.conf new file mode 100644 index 0000000..752474e --- /dev/null +++ b/angie/mod-mail.conf @@ -0,0 +1 @@ +include mod.d/mail-*.conf; \ No newline at end of file diff --git a/angie/mod-stream.conf b/angie/mod-stream.conf new file mode 100644 index 0000000..bafd341 --- /dev/null +++ b/angie/mod-stream.conf @@ -0,0 +1 @@ +include mod.d/stream-*.conf; \ No newline at end of file diff --git a/angie/mod.dist/.brotli.preseed b/angie/mod.dist/.brotli.preseed new file mode 100644 index 0000000..e69de29 diff --git a/angie/mod.dist/.otel.preseed b/angie/mod.dist/.otel.preseed new file mode 100644 index 0000000..e69de29 diff --git a/angie/mod.dist/.postgres.preseed b/angie/mod.dist/.postgres.preseed new file mode 100644 index 0000000..e69de29 diff --git a/angie/mod.dist/.rtmp.preseed b/angie/mod.dist/.rtmp.preseed new file mode 100644 index 0000000..e69de29 diff --git a/angie/mod.dist/.vts.preseed b/angie/mod.dist/.vts.preseed new file mode 100644 index 0000000..e69de29 diff --git a/angie/mod.dist/.zstd.preseed b/angie/mod.dist/.zstd.preseed new file mode 100644 index 0000000..e69de29 diff --git a/angie/mod.dist/http-brotli-filter.conf b/angie/mod.dist/http-brotli-filter.conf new file mode 100644 index 0000000..2acbe61 --- /dev/null +++ b/angie/mod.dist/http-brotli-filter.conf @@ -0,0 +1 @@ +load_module modules.d/ngx_http_brotli_filter_module.so; \ No newline at end of file diff --git a/angie/mod.dist/http-brotli-static.conf b/angie/mod.dist/http-brotli-static.conf new file mode 100644 index 0000000..5d02905 --- /dev/null +++ b/angie/mod.dist/http-brotli-static.conf @@ -0,0 +1 @@ +load_module modules.d/ngx_http_brotli_static_module.so; \ No newline at end of file diff --git a/angie/mod.dist/http-brotli.conf b/angie/mod.dist/http-brotli.conf new file mode 100644 index 0000000..c365782 --- /dev/null +++ b/angie/mod.dist/http-brotli.conf @@ -0,0 +1,2 @@ +load_module modules.d/ngx_http_brotli_filter_module.so; +load_module modules.d/ngx_http_brotli_static_module.so; \ No newline at end of file diff --git a/angie/mod.dist/http-otel.conf b/angie/mod.dist/http-otel.conf new file mode 100644 index 0000000..1cdafc5 --- /dev/null +++ b/angie/mod.dist/http-otel.conf @@ -0,0 +1 @@ +load_module modules.d/ngx_otel_module.so; \ No newline at end of file diff --git a/angie/mod.dist/http-postgres.conf b/angie/mod.dist/http-postgres.conf new file mode 100644 index 0000000..5048d82 --- /dev/null +++ b/angie/mod.dist/http-postgres.conf @@ -0,0 +1 @@ +load_module modules.d/ngx_postgres_module.so; \ No newline at end of file diff --git a/angie/mod.dist/http-rtmp.conf b/angie/mod.dist/http-rtmp.conf new file mode 100644 index 0000000..a4bc030 --- /dev/null +++ b/angie/mod.dist/http-rtmp.conf @@ -0,0 +1 @@ +load_module modules.d/ngx_rtmp_module.so; \ No newline at end of file diff --git a/angie/mod.dist/http-sts.conf b/angie/mod.dist/http-sts.conf new file mode 100644 index 0000000..01e70c2 --- /dev/null +++ b/angie/mod.dist/http-sts.conf @@ -0,0 +1 @@ +load_module modules.d/ngx_http_stream_server_traffic_status_module.so; \ No newline at end of file diff --git a/angie/mod.dist/http-vts.conf b/angie/mod.dist/http-vts.conf new file mode 100644 index 0000000..0dfb96f --- /dev/null +++ b/angie/mod.dist/http-vts.conf @@ -0,0 +1 @@ +load_module modules.d/ngx_http_vhost_traffic_status_module.so; \ No newline at end of file diff --git a/angie/mod.dist/http-zstd-filter.conf b/angie/mod.dist/http-zstd-filter.conf new file mode 100644 index 0000000..cb586bc --- /dev/null +++ b/angie/mod.dist/http-zstd-filter.conf @@ -0,0 +1 @@ +load_module modules.d/ngx_http_zstd_filter_module.so; \ No newline at end of file diff --git a/angie/mod.dist/http-zstd-static.conf b/angie/mod.dist/http-zstd-static.conf new file mode 100644 index 0000000..e1f6190 --- /dev/null +++ b/angie/mod.dist/http-zstd-static.conf @@ -0,0 +1 @@ +load_module modules.d/ngx_http_zstd_static_module.so; \ No newline at end of file diff --git a/angie/mod.dist/http-zstd.conf b/angie/mod.dist/http-zstd.conf new file mode 100644 index 0000000..591f74b --- /dev/null +++ b/angie/mod.dist/http-zstd.conf @@ -0,0 +1,2 @@ +load_module modules.d/ngx_http_zstd_filter_module.so; +load_module modules.d/ngx_http_zstd_static_module.so; \ No newline at end of file diff --git a/angie/mod.dist/stream-sts.conf b/angie/mod.dist/stream-sts.conf new file mode 100644 index 0000000..f0807d9 --- /dev/null +++ b/angie/mod.dist/stream-sts.conf @@ -0,0 +1 @@ +load_module modules.d/ngx_stream_server_traffic_status_module.so; \ No newline at end of file diff --git a/angie/snip.dist/core-quic-bpf.conf b/angie/snip.dist/core-quic-bpf.conf new file mode 100644 index 0000000..57e3c82 --- /dev/null +++ b/angie/snip.dist/core-quic-bpf.conf @@ -0,0 +1 @@ +quic_bpf on; \ No newline at end of file diff --git a/angie/snip.dist/core_ev-accept-mutex-delay.conf b/angie/snip.dist/core_ev-accept-mutex-delay.conf new file mode 100644 index 0000000..8cf9b15 --- /dev/null +++ b/angie/snip.dist/core_ev-accept-mutex-delay.conf @@ -0,0 +1 @@ +accept_mutex_delay 200ms; \ No newline at end of file diff --git a/angie/snip.dist/core_ev-accept-mutex.conf b/angie/snip.dist/core_ev-accept-mutex.conf new file mode 100644 index 0000000..9b9e7eb --- /dev/null +++ b/angie/snip.dist/core_ev-accept-mutex.conf @@ -0,0 +1 @@ +accept_mutex on; \ No newline at end of file diff --git a/angie/snip.dist/core_ev-multi-accept.conf b/angie/snip.dist/core_ev-multi-accept.conf new file mode 100644 index 0000000..19df249 --- /dev/null +++ b/angie/snip.dist/core_ev-multi-accept.conf @@ -0,0 +1 @@ +multi_accept on; \ No newline at end of file diff --git a/angie/static.dist/robots.txt b/angie/static.dist/robots.txt new file mode 100644 index 0000000..1f53798 --- /dev/null +++ b/angie/static.dist/robots.txt @@ -0,0 +1,2 @@ +User-agent: * +Disallow: / diff --git a/apt/prefs.backports b/apt/prefs.backports new file mode 100644 index 0000000..8658903 --- /dev/null +++ b/apt/prefs.backports @@ -0,0 +1,4 @@ +## example: +# Package: src:curl +# Pin: release n=bookworm-backports +# Pin-Priority: 600 diff --git a/apt/sources.angie b/apt/sources.angie new file mode 100644 index 0000000..cd6c48c --- /dev/null +++ b/apt/sources.angie @@ -0,0 +1,5 @@ +Types: deb +URIs: http://download.angie.software/angie/debian/12 +Suites: bookworm +Components: main +Signed-By: /etc/apt/keyrings/angie.gpg.asc diff --git a/apt/sources.debian b/apt/sources.debian new file mode 100644 index 0000000..75936f9 --- /dev/null +++ b/apt/sources.debian @@ -0,0 +1,15 @@ +Types: deb +URIs: http://deb.debian.org/debian +## local development +# URIs: http://127.0.0.1:8081/repository/proxy_apt_debian +Suites: bookworm bookworm-updates bookworm-proposed-updates bookworm-backports +Components: main +Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg + +Types: deb +## local development +# URIs: http://127.0.0.1:8081/repository/proxy_apt_debian-security +URIs: http://deb.debian.org/debian-security +Suites: bookworm-security +Components: main +Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg diff --git a/build-scripts/image-base.sh b/build-scripts/image-base.sh new file mode 100755 index 0000000..605bca4 --- /dev/null +++ b/build-scripts/image-base.sh @@ -0,0 +1,51 @@ +#!/bin/sh +set -ef +cd "$(dirname "$0")/.." + +set -a +BUILDAH_FORMAT="${BUILDAH_FORMAT:-docker}" +BUILDAH_ISOLATION="${BUILDAH_ISOLATION:-chroot}" +BUILDAH_NETWORK="${BUILDAH_NETWORK:-host}" +set +a + +PYTHONTAG="${PYTHONTAG:-3.11.9-slim-bookworm}" + +grab_site_packages() { + podman run \ + --pull=always --rm \ + --entrypoint='[]' \ + --user=nobody:nogroup \ + -e LANG=C.UTF-8 \ + -e LC_ALL=C.UTF-8 \ + -e MALLOC_ARENA_MAX=2 \ + -e PYTHONUNBUFFERED=1 \ + -e PYTHONDONTWRITEBYTECODE=1 \ + "$1" \ + python3 -c 'import site;print(site.getsitepackages()[0])' +} + +PYTHON_SITE_PACKAGES=$(grab_site_packages "docker.io/python:${PYTHONTAG}") +[ -n "${PYTHON_SITE_PACKAGES:?}" ] + +img="docker.io/rockdrilla/angie-conv:base-v1" + +buildah bud --network="${BUILDAH_NETWORK}" \ + -f ./Dockerfile.base \ + -t "${img}" \ + --pull=missing --no-cache --squash \ + --build-arg "PYTHONTAG=${PYTHONTAG}" \ + --env "PYTHON_SITE_PACKAGES=${PYTHON_SITE_PACKAGES}" \ + --unsetenv GPG_KEY \ + --unsetenv PYTHON_PIP_VERSION \ + --unsetenv PYTHON_SETUPTOOLS_VERSION \ + --unsetenv PYTHON_GET_PIP_SHA256 \ + --unsetenv PYTHON_GET_PIP_URL \ + + +c=$(buildah from --pull=never "${img}") || true +if [ -z "$c" ] ; then + buildah rmi -f "${img}" + exit 1 +fi +buildah config --created-by /usr/local/share/Dockerfile.base "$c" +buildah commit --rm --squash "$c" "${img}" diff --git a/build-scripts/image-deps.sh b/build-scripts/image-deps.sh new file mode 100755 index 0000000..be965d8 --- /dev/null +++ b/build-scripts/image-deps.sh @@ -0,0 +1,16 @@ +#!/bin/sh +set -ef +cd "$(dirname "$0")/.." + +set -a +BUILDAH_FORMAT="${BUILDAH_FORMAT:-docker}" +BUILDAH_ISOLATION="${BUILDAH_ISOLATION:-chroot}" +BUILDAH_NETWORK="${BUILDAH_NETWORK:-host}" +set +a + +img="docker.io/rockdrilla/angie-conv:deps-v1" + +exec buildah bud --network="${BUILDAH_NETWORK}" \ + -f ./Dockerfile.deps \ + -t "${img}" \ + --pull=missing --no-cache diff --git a/build-scripts/image.sh b/build-scripts/image.sh new file mode 100755 index 0000000..a5a665f --- /dev/null +++ b/build-scripts/image.sh @@ -0,0 +1,35 @@ +#!/bin/sh +set -ef +cd "$(dirname "$0")/.." + +set -a +BUILDAH_FORMAT="${BUILDAH_FORMAT:-docker}" +BUILDAH_ISOLATION="${BUILDAH_ISOLATION:-chroot}" +BUILDAH_NETWORK="${BUILDAH_NETWORK:-host}" +set +a + +ANGIE_VERSION="${ANGIE_VERSION:-1.6.0}" + +## likely the same as in https://pkg.go.dev/strconv#ParseBool +gobool_to_int() { + ## local value=$1 + ## local default=$2 + case "${1:-_}" in + 1 | [Tt] | [Tt][Rr][Uu][Ee] ) echo 1 ;; + 0 | [Ff] | [Ff][Aa][Ll][Ss][Ee] ) echo 0 ;; + * ) echo "${2:-error}" ;; + esac +} + +NGX_DEBUG=$(gobool_to_int "${1:-0}" 0) +case "${NGX_DEBUG}" in +0 ) img="docker.io/rockdrilla/angie-conv:${ANGIE_VERSION}-v1" ;; +1 ) img="docker.io/rockdrilla/angie-conv:debug-${ANGIE_VERSION}-v1" ;; +esac + +exec buildah bud --network="${BUILDAH_NETWORK}" \ + -f ./Dockerfile \ + -t "${img}" \ + --env "ANGIE_VERSION=${ANGIE_VERSION}" \ + --env "NGX_DEBUG=${NGX_DEBUG}" \ + --pull=missing --no-cache diff --git a/extra-scripts/certifi-extras.sh b/extra-scripts/certifi-extras.sh new file mode 100755 index 0000000..02ea1a5 --- /dev/null +++ b/extra-scripts/certifi-extras.sh @@ -0,0 +1,65 @@ +#!/bin/sh +set -ef + +certifi_uri="https://raw.githubusercontent.com/certifi/python-certifi/${CERTIFI_COMMIT:?}/certifi/cacert.pem" +dst_dir=/usr/local/share/ca-certificates + +w=$(mktemp -d) ; : "${w:?}" +curl -sSL "${certifi_uri}" > "$w/cacert.pem" + +def_bundle=/etc/ssl/certs/ca-certificates.crt + +bundle_offsets() { + grep -Fhne '-----END CERTIFICATE-----' "$1" | cut -d : -f 1 \ + | { + s=1 ; while read -r e ; do + [ -n "$e" ] || continue + echo "$s,$e" + s=$((e+1)) + done + } +} + +set +e +bundle_offsets "${def_bundle}" > "$w/offsets.0" +bundle_offsets "$w/cacert.pem" > "$w/offsets.1" +set -e + +bundle_fingerprints() { + while read -r a ; do + [ -n "$a" ] || continue + sed -ne "${a}p" "$1" | openssl x509 -noout -fingerprint + done < "$2" +} + +set +e +bundle_fingerprints "${def_bundle}" "$w/offsets.0" > "$w/fingerprints.0" +bundle_fingerprints "$w/cacert.pem" "$w/offsets.1" > "$w/fingerprints.1" +set -e + +set +e +grep -Fxv -f "$w/fingerprints.0" "$w/fingerprints.1" > "$w/fingerprints.diff" +set -e + +if [ -s "$w/fingerprints.diff" ] ; then + set +e + grep -Fxn -f "$w/fingerprints.diff" "$w/fingerprints.1" | cut -d : -f 1 > "$w/records.diff" + set -e + + terse_fingerprint() { + cut -d = -f 2- | tr '[:upper:]' '[:lower:]' | tr -cd '[:alnum:]' + } + + mkdir "$w/extras" + + while read -r n ; do + [ -n "$n" ] || continue + fp=$(sed -ne "${n}p" "$w/fingerprints.1" | terse_fingerprint) + off=$(sed -ne "${n}p" "$w/offsets.1") + sed -ne "${off}p" "$w/cacert.pem" | openssl x509 > "${dst_dir}/certifi-${fp}.crt" + done < "$w/records.diff" +fi + +rm -rf "$w" + +update-ca-certificates --fresh diff --git a/extra-scripts/gpg-batch.sh b/extra-scripts/gpg-batch.sh new file mode 100755 index 0000000..244ea54 --- /dev/null +++ b/extra-scripts/gpg-batch.sh @@ -0,0 +1,45 @@ +#!/bin/sh +set -ef + +: "${GPG_KEYSERVER:=hkps://keyserver.ubuntu.com}" + +[ $# != 0 ] || exit 1 + +case "$1" in +1 | start ) + [ -n "${GNUPGHOME}" ] || exit 1 + [ -d "${GNUPGHOME}" ] || exit 1 + + cd "${GNUPGHOME}" + cat > gpg.conf <<-EOF + quiet + batch + trust-model always + no-auto-check-trustdb + ignore-time-conflict + keyid-format 0xlong + keyserver ${GPG_KEYSERVER} + EOF + cat > dirmngr.conf <<-EOF + quiet + batch + keyserver ${GPG_KEYSERVER} + EOF + gpg --update-trustdb >/dev/null 2>&1 + gpg --list-keys >/dev/null 2>&1 + dirmngr >/dev/null 2>&1 +;; +0 | stop ) + [ -n "${GNUPGHOME}" ] || exit 0 + [ -d "${GNUPGHOME}" ] || exit 1 + + cd "${GNUPGHOME}" + gpgconf --kill all + cd / + rm -rf "${GNUPGHOME}" +;; +* ) + exit 1 +;; +esac +exit 0 diff --git a/extra-scripts/gpg-export.sh b/extra-scripts/gpg-export.sh new file mode 100755 index 0000000..dda0678 --- /dev/null +++ b/extra-scripts/gpg-export.sh @@ -0,0 +1,28 @@ +#!/bin/sh +set -ef + +: "${1:?}" "${2:?}" + +w=$(mktemp -d) ; : "${w:?}" + +gpg_on() { gpg-batch.sh start ; } +gpg_off() { + cd / + gpg-batch.sh stop + unset GNUPGHOME + rm -rf "$w" + exit "${1:-0}" +} + +( + export GNUPGHOME="$w/.gnupg" + mkdir -m 0700 "${GNUPGHOME}" + gpg_on + + gpg --import "$1" + gpg --armor --export > "$w/export" + cat < "$w/export" > "$2" + gpg --show-keys "$2" + + gpg_off +) || gpg_off 1 diff --git a/image-entry.d/00-common.envsh b/image-entry.d/00-common.envsh new file mode 100644 index 0000000..5843bcf --- /dev/null +++ b/image-entry.d/00-common.envsh @@ -0,0 +1,242 @@ +#!/bin/sh + +have_envvar() { + [ -n "$1" ] || return 1 + grep -Ezq "^$1=" /proc/self/environ || return +} + +## unexporting variable in (POSIX) sh is PITA =/ +unexport() { + unset ___k ___v + for ___k ; do + [ -n "${___k}" ] || continue + have_envvar "${___k}" || continue + + ___v=$(eval printf '%s' "\"\${${___k}}\"") + eval "unset ${___k}" + eval "${___k}=$(env printf '%s' \"\${___v}\")" + unset ___v + done + unset ___k +} + +## likely the same as in https://pkg.go.dev/strconv#ParseBool +gobool_to_int() { + ## local value=$1 + ## local default=$2 + case "${1:-_}" in + 1 | [Tt] | [Tt][Rr][Uu][Ee] ) echo 1 ;; + 0 | [Ff] | [Ff][Aa][Ll][Ss][Ee] ) echo 0 ;; + * ) echo "${2:-error}" ;; + esac +} + +[ -n "${__IEP_SRC:-}" ] || __IEP_SRC="$0" + +IEP_TRACE=$(gobool_to_int "${IEP_TRACE:-0}" 0) +export IEP_TRACE +if [ "${IEP_TRACE}" = 1 ] ; then + log_always() { echo "# $(date +'%Y-%m-%d %H:%M:%S.%03N %z'): ${__IEP_SRC}${*:+: $*}" >&2 ; } +else + log_always() { echo "# ${__IEP_SRC}${*:+: $*}" >&2 ; } +fi + +IEP_VERBOSE=$(gobool_to_int "${IEP_VERBOSE:-${IEP_TRACE}}" "${IEP_TRACE}") +export IEP_VERBOSE +if [ "${IEP_VERBOSE}" = 1 ] ; then + log() { log_always "$@" ; } + log_file() { sed -E '/^./s,^, ,' < "$1" >&2 ; } +else + log() { : ;} + log_file() { :; } +fi + +if [ "${IEP_VERBOSE}" = 0 ] ; then + ln_s() { ln -s "$@" || return; } +else + ln_s() { ln -sv "$@" || return; } +fi + +have_cmd() { command -v "$1" >/dev/null 2>&1 || return ; } + +strip_suffix() { printf '%s' "${1%"$2"}" | tr -s '/' ; } + +untemplate_path() { + case "$1" in + ## inplace + /run/angie/* | /etc/angie/run/* ) + strip_suffix "$1" "$2" + ;; + /etc/angie/conf.d/* | /etc/angie/mod.d/* | /etc/angie/modules.d/* | /etc/angie/njs.d/* | /etc/angie/site.d/* | /etc/angie/snip.d/* ) + strip_suffix "$1" "$2" + ;; + /etc/angie/static.d/* ) + strip_suffix "$1" "$2" + ;; + ## set appropriate location + /etc/angie/* ) + strip_suffix "/run/angie${1#/etc/angie}" "$2" + ;; + /tmp/* ) + log_always "untemplate_path() shouldn't work with /tmp/: $1" + strip_suffix "$1" "$2" + ;; + ## last resort - STRONGLY AVOID + /* ) + log_always "untemplate_path() does uncommon/last-resort mapping for: $1" + strip_suffix "/run/angie/tmp$1" "$2" + ;; + ## misbehavior! + * ) + log_always "untemplate_path() doesn't work with relative paths: $1" + return 1 + ;; + esac +} + +install_userdir() { + if [ "${IEP_ROOT}" = 1 ] ; then + install -d -o "${NGX_USER}" -g "${NGX_GROUP}" "$@" + else + install -d "$@" + fi +} + +untemplate_file_envsubst() { + [ -n "$1" ] || return + [ -f "$1" ] || { log_always "file not found: $1" ; return 1 ; } + + [ -n "${NGX_ENVSUBST_SUFFIX:-}" ] || { log "NGX_ENVSUBST_SUFFIX is empty" ; return 1 ; } + + __dest="$2" + [ -n "${__dest}" ] || __dest=$(untemplate_path "$1" "${NGX_ENVSUBST_SUFFIX}") || return + + [ -d "${__dest%/*}" ] || install_userdir "${__dest%/*}" || return + + log "Running envsubst: $1 -> ${__dest}" + envsubst.sh < "$1" > "${__dest}" || return +} + +## notes: +## - (OPTIONAL) place own wrapper script as "/usr/local/sbin/jinja.py" +## in order to perform different template processing +untemplate_file_jinja() { + [ -n "$1" ] || return + [ -f "$1" ] || { log_always "file not found: $1" ; return 1 ; } + + [ -n "${NGX_JINJA_SUFFIX:-}" ] || { log "NGX_JINJA_SUFFIX is empty" ; return 1 ; } + + __dest="$2" + [ -n "${__dest}" ] || __dest=$(untemplate_path "$1" "${NGX_JINJA_SUFFIX}") || return + + [ -d "${__dest%/*}" ] || install_userdir "${__dest%/*}" || return + + log "Running jinja.py: $1 -> ${__dest}" + jinja.py "$1" "${__dest}" || return +} + +untemplate_dir_envsubst() { + [ -n "${NGX_ENVSUBST_SUFFIX:-}" ] || { log "NGX_ENVSUBST_SUFFIX is empty" ; return 1 ; } + + __template_list=$(mktemp) || return + + find "$@" -follow -type f -name "*${NGX_ENVSUBST_SUFFIX}" \ + | sort -uV > "${__template_list}" + + __have_args="${ENVSUBST_ARGS:+1}" + if [ -z "${__have_args}" ] ; then + ## optimize envsubst.sh invocation by caching argument list + ## ref: envsubst.sh + ENVSUBST_ARGS=$(mktemp) || return + envsubst-args.sh > "${ENVSUBST_ARGS}" + export ENVSUBST_ARGS + fi + + while read -r _orig_file ; do + [ -n "${_orig_file}" ] || continue + untemplate_file_envsubst "${_orig_file}" + done < "${__template_list}" + + if [ -z "${__have_args}" ] ; then + rm -f "${ENVSUBST_ARGS}" ; unset ENVSUBST_ARGS + fi + unset __have_args + + rm -f "${__template_list}" ; unset __template_list +} + +untemplate_dir_jinja() { + [ -n "${NGX_JINJA_SUFFIX:-}" ] || { log "NGX_JINJA_SUFFIX is empty" ; return 1 ; } + + __template_list=$(mktemp) || return + + find "$@" -follow -type f -name "*${NGX_JINJA_SUFFIX}" \ + | sort -uV > "${__template_list}" + + while read -r _orig_file ; do + [ -n "${_orig_file}" ] || continue + untemplate_file_jinja "${_orig_file}" + done < "${__template_list}" + + rm -f "${__template_list}" ; unset __template_list +} + +remap_path() { + [ -n "$1" ] || return + + case "$1" in + ## conf + /etc/angie/conf.dist/* ) echo "${2:-/etc/angie/conf.d}${1#/etc/angie/conf.dist}" ;; + /etc/angie/conf/* ) echo "${2:-/etc/angie/conf.d}${1#/etc/angie/conf}" ;; + /angie/conf/* ) echo "${2:-/etc/angie/conf.d}${1#/angie/conf}" ;; + ## mod + /etc/angie/mod.dist/* ) echo "${2:-/etc/angie/mod.d}${1#/etc/angie/mod.dist}" ;; + /etc/angie/mod/* ) echo "${2:-/etc/angie/mod.d}${1#/etc/angie/mod}" ;; + /angie/mod/* ) echo "${2:-/etc/angie/mod.d}${1#/angie/mod}" ;; + ## modules + /etc/angie/modules.dist/* ) echo "${2:-/etc/angie/modules.d}${1#/etc/angie/modules.dist}" ;; + /etc/angie/modules/* ) echo "${2:-/etc/angie/modules.d}${1#/etc/angie/modules}" ;; + /angie/modules/* ) echo "${2:-/etc/angie/modules.d}${1#/angie/modules}" ;; + ## njs + /etc/angie/njs.dist/* ) echo "${2:-/etc/angie/njs.d}${1#/etc/angie/njs.dist}" ;; + /etc/angie/njs/* ) echo "${2:-/etc/angie/njs.d}${1#/etc/angie/njs}" ;; + /angie/njs/* ) echo "${2:-/etc/angie/njs.d}${1#/angie/njs}" ;; + ## site + /etc/angie/site.dist/* ) echo "${2:-/etc/angie/site.d}${1#/etc/angie/site.dist}" ;; + /etc/angie/site/* ) echo "${2:-/etc/angie/site.d}${1#/etc/angie/site}" ;; + /angie/site/* ) echo "${2:-/etc/angie/site.d}${1#/angie/site}" ;; + ## snip + /etc/angie/snip.dist/* ) echo "${2:-/etc/angie/snip.d}${1#/etc/angie/snip.dist}" ;; + /etc/angie/snip/* ) echo "${2:-/etc/angie/snip.d}${1#/etc/angie/snip}" ;; + /angie/snip/* ) echo "${2:-/etc/angie/snip.d}${1#/angie/snip}" ;; + + ## static + /etc/angie/static.dist/* ) echo "${2:-/etc/angie/static.d}${1#/etc/angie/static.dist}" ;; + /etc/angie/static/* ) echo "${2:-/etc/angie/static.d}${1#/etc/angie/static}" ;; + /angie/static/* ) echo "${2:-/etc/angie/static.d}${1#/angie/static}" ;; + + ## log + /etc/angie/log.dist/* ) echo "${2:-/etc/angie/log.d}${1#/etc/angie/log.dist}" ;; + /angie/log/* ) echo "${2:-/etc/angie/log.d}${1#/angie/log}" ;; + + ## misbehavior! + * ) + log_always "remap_path() doesn't know how to handle this path: $1" + return 1 + ;; + esac +} + +merged_root='/run/angie/merged' +combine_remap_path() { + [ -n "$1" ] || return + + case "$1" in + "${merged_root}"/* ) echo "${2:-/run/angie}${1#"${merged_root}"}" ;; + ## misbehavior! + * ) + log_always "combine_remap_path() doesn't know how to handle this path: $1" + return 1 + ;; + esac +} diff --git a/image-entry.d/01-defaults.envsh b/image-entry.d/01-defaults.envsh new file mode 100755 index 0000000..c57fb23 --- /dev/null +++ b/image-entry.d/01-defaults.envsh @@ -0,0 +1,39 @@ +#!/bin/sh + +## NB: NGX_DEBUG is set via image build script + +set -a + +NGX_HTTP=$(gobool_to_int "${NGX_HTTP:-1}" 1) +NGX_MAIL=$(gobool_to_int "${NGX_MAIL:-0}" 0) +NGX_STREAM=$(gobool_to_int "${NGX_STREAM:-0}" 0) + +NGX_STRICT_LOAD=$(gobool_to_int "${NGX_STRICT_LOAD:-1}" 1) + +NGX_CORE_MODULES="${NGX_CORE_MODULES:-}" +NGX_CORE_EVENTS_SNIPPETS="${NGX_CORE_EVENTS_SNIPPETS:-}" +NGX_CORE_SNIPPETS="${NGX_CORE_SNIPPETS:-}" + +NGX_CORE_ENV="${NGX_CORE_ENV:-}" + +NGX_PROCESS_STATIC=$(gobool_to_int "${NGX_PROCESS_STATIC:-0}" 0) + +NGX_ENVSUBST_SUFFIX="${NGX_ENVSUBST_SUFFIX:-.in}" +case "${NGX_ENVSUBST_SUFFIX}" in +.* ) ;; +* ) NGX_ENVSUBST_SUFFIX=".${NGX_ENVSUBST_SUFFIX}" ;; +esac +NGX_JINJA_SUFFIX="${NGX_JINJA_SUFFIX:-.j2}" +case "${NGX_JINJA_SUFFIX}" in +.* ) ;; +* ) NGX_JINJA_SUFFIX=".${NGX_JINJA_SUFFIX}" ;; +esac + +set +a + +if [ "${NGX_HTTP}${NGX_MAIL}${NGX_STREAM}" = '000' ] ; then + log_always '======================================' + log_always 'WARNING!' + log_always 'Angie is almost completely TURNED OFF' + log_always '======================================' +fi diff --git a/image-entry.d/02-detect-nonroot.envsh b/image-entry.d/02-detect-nonroot.envsh new file mode 100755 index 0000000..1fa40f9 --- /dev/null +++ b/image-entry.d/02-detect-nonroot.envsh @@ -0,0 +1,6 @@ +#!/bin/sh + +unset IEP_ROOT +IEP_ROOT=1 +[ "$(stat -c %u /proc/1)" = 0 ] || IEP_ROOT=0 +export IEP_ROOT diff --git a/image-entry.d/03-detect-local-override.envsh b/image-entry.d/03-detect-local-override.envsh new file mode 100755 index 0000000..95df4eb --- /dev/null +++ b/image-entry.d/03-detect-local-override.envsh @@ -0,0 +1,17 @@ +#!/bin/sh + +unset IEP_LOCAL_OVERRIDE +IEP_LOCAL_OVERRIDE=0 + +unset _fsspec _fstarget _fstype _fsopts _fsreq _fspass +while read -r _fsspec _fstarget _fstype _fsopts _fsreq _fspass ; do + case "${_fstarget}" in + /angie | /angie/* ) + IEP_LOCAL_OVERRIDE=1 + break + ;; + esac +done < /proc/mounts +unset _fsspec _fstarget _fstype _fsopts _fsreq _fspass + +export IEP_LOCAL_OVERRIDE diff --git a/image-entry.d/10-core-user.envsh b/image-entry.d/10-core-user.envsh new file mode 100755 index 0000000..38b7e2f --- /dev/null +++ b/image-entry.d/10-core-user.envsh @@ -0,0 +1,70 @@ +#!/bin/sh + +if [ "${IEP_ROOT}" = 0 ] ; then + log "Running as non-root: user/group configuration may be excessive" +fi + +unset _NGX_USER _NGX_GROUP +## here should be SANE defaults (!) +_NGX_USER=angie +_NGX_GROUP=angie + +[ -n "${NGX_USER:-}" ] || NGX_USER=${_NGX_USER} +case "${NGX_USER}" in +"${_NGX_USER}" ) ;; +## numeric id - remap to name +[1-9]* ) + _user_name=$(getent passwd "${NGX_USER}" | cut -d: -f1) + if [ -n "${_user_name}" ] ; then + NGX_USER=${_user_name} + else + log_always "NGX_USER: ID is not known in /etc/passwd: ${NGX_USER}" + log_always "setting NGX_USER=${_NGX_USER}" + NGX_USER=${_NGX_USER} + fi + unset _user_name +;; +* ) + _user_name=$(getent passwd "${NGX_USER}" | cut -d: -f1) + if [ -n "${_user_name}" ] ; then + NGX_USER=${_user_name} + else + log_always "NGX_USER: name is not known in /etc/passwd: ${NGX_USER}" + log_always "setting NGX_USER=${_NGX_USER}" + NGX_USER=${_NGX_USER} + fi + unset _user_name +;; +esac + +[ -n "${NGX_GROUP:-}" ] || NGX_GROUP=${_NGX_GROUP} +case "${NGX_GROUP}" in +"${_NGX_GROUP}" ) ;; +## numeric id - remap to name +[1-9]* ) + _group_name=$(getent group "${NGX_GROUP}" | cut -d: -f1) + if [ -n "${_group_name}" ] ; then + NGX_GROUP=${_group_name} + else + log_always "NGX_GROUP: ID is not known in /etc/group: ${NGX_GROUP}" + log_always "setting NGX_GROUP=${_NGX_GROUP}" + NGX_GROUP=${_NGX_GROUP} + fi + unset _group_name +;; +* ) + _group_name=$(getent group "${NGX_GROUP}" | cut -d: -f1) + if [ -n "${_group_name}" ] ; then + NGX_GROUP=${_group_name} + else + log_always "NGX_GROUP: name is not known in /etc/group: ${NGX_GROUP}" + log_always "setting NGX_GROUP=${_NGX_GROUP}" + NGX_GROUP=${_NGX_GROUP} + fi + unset _group_name +;; +esac + +export NGX_USER NGX_GROUP + +unset _NGX_USER _NGX_GROUP diff --git a/image-entry.d/11-core-env.envsh b/image-entry.d/11-core-env.envsh new file mode 100755 index 0000000..3fa283f --- /dev/null +++ b/image-entry.d/11-core-env.envsh @@ -0,0 +1,34 @@ +#!/bin/sh + +if [ -z "${NGX_CORE_ENV:-}" ] ; then + NGX_CORE_ENV='TZ MALLOC_ARENA_MAX' +else + unset __set_f + __set_f= + case "${-}" in + *f* ) __set_f=1 ;; + esac + [ -n "${__set_f}" ] || set -f + + unset __env __have_tz __have_malloc + for __env in ${NGX_CORE_ENV} ; do + case "${__env}" in + TZ | TZ=* ) + __have_tz=1 + ;; + MALLOC_ARENA_MAX | MALLOC_ARENA_MAX=* ) + __have_malloc=1 + ;; + esac + done + unset __env + + [ -n "${__have_malloc}" ] || NGX_CORE_ENV="MALLOC_ARENA_MAX ${NGX_CORE_ENV}" + [ -n "${__have_tz}" ] || NGX_CORE_ENV="TZ ${NGX_CORE_ENV}" + unset __have_tz __have_malloc + + [ -n "${__set_f}" ] || set +f + unset __set_f +fi + +export NGX_CORE_ENV diff --git a/image-entry.d/12-core-worker-defaults.envsh b/image-entry.d/12-core-worker-defaults.envsh new file mode 100755 index 0000000..915f2ff --- /dev/null +++ b/image-entry.d/12-core-worker-defaults.envsh @@ -0,0 +1,122 @@ +#!/bin/sh + +unset _NGX_WORKER_PROCESSES _NGX_WORKER_PRIORITY _NGX_WORKER_RLIMIT_NOFILE _NGX_WORKER_CONNECTIONS +## here should be SANE defaults (!) +_NGX_WORKER_PROCESSES=2 +_NGX_WORKER_PRIORITY=0 +_NGX_WORKER_RLIMIT_NOFILE=16384 +_NGX_WORKER_CONNECTIONS=4096 + +[ -n "${NGX_WORKER_PROCESSES:-}" ] || NGX_WORKER_PROCESSES=${_NGX_WORKER_PROCESSES} +case "${NGX_WORKER_PROCESSES}" in +"${_NGX_WORKER_PROCESSES}" ) ;; +[1-9] | [1-9][0-9] ) ;; +0 | [Aa][Uu][Tt][Oo] ) + log_always "NGX_WORKER_PROCESSES=${NGX_WORKER_PROCESSES} isn't supported yet" + log_always "setting NGX_WORKER_PROCESSES=${_NGX_WORKER_PROCESSES}" + NGX_WORKER_PROCESSES=${_NGX_WORKER_PROCESSES} +;; +* ) + log_always "NGX_WORKER_PROCESSES: unrecognized value: ${NGX_WORKER_PROCESSES}" + log_always "setting NGX_WORKER_PROCESSES=${_NGX_WORKER_PROCESSES}" + NGX_WORKER_PROCESSES=${_NGX_WORKER_PROCESSES} +;; +esac + +[ -n "${NGX_WORKER_PRIORITY:-}" ] || NGX_WORKER_PRIORITY=${_NGX_WORKER_PRIORITY} +case "${NGX_WORKER_PRIORITY}" in +"${_NGX_WORKER_PRIORITY}" ) ;; +-[1-9] | -1[0-9] | -20 ) ;; + [0-9] | 1[0-9] | 20 ) ;; +* ) + log_always "NGX_WORKER_PRIORITY: unrecognized value: ${NGX_WORKER_PRIORITY}" + log_always "setting NGX_WORKER_PRIORITY=${_NGX_WORKER_PRIORITY}" + NGX_WORKER_PRIORITY=${_NGX_WORKER_PRIORITY} +;; +esac + +[ -n "${NGX_WORKER_RLIMIT_NOFILE:-}" ] || NGX_WORKER_RLIMIT_NOFILE=${_NGX_WORKER_RLIMIT_NOFILE} +case "${NGX_WORKER_RLIMIT_NOFILE}" in +"${_NGX_WORKER_RLIMIT_NOFILE}" ) ;; +[1-9] | [1-9][0-9] ) + log_always "NGX_WORKER_RLIMIT_NOFILE: too low: ${NGX_WORKER_RLIMIT_NOFILE}" + log_always "setting NGX_WORKER_RLIMIT_NOFILE=${_NGX_WORKER_RLIMIT_NOFILE}" + NGX_WORKER_RLIMIT_NOFILE=${_NGX_WORKER_RLIMIT_NOFILE} +;; +## allow values within [100;9999999] +[1-9][0-9][0-9] ) ;; +[1-9][0-9][0-9][0-9] ) ;; +[1-9][0-9][0-9][0-9][0-9] ) ;; +[1-9][0-9][0-9][0-9][0-9][0-9] ) ;; +[1-9][0-9][0-9][0-9][0-9][0-9][0-9] ) ;; +* ) + log_always "NGX_WORKER_RLIMIT_NOFILE: unrecognized value: ${NGX_WORKER_RLIMIT_NOFILE}" + log_always "setting NGX_WORKER_RLIMIT_NOFILE=${_NGX_WORKER_RLIMIT_NOFILE}" + NGX_WORKER_RLIMIT_NOFILE=${_NGX_WORKER_RLIMIT_NOFILE} +;; +esac + +[ -n "${NGX_WORKER_CONNECTIONS:-}" ] || NGX_WORKER_CONNECTIONS=${_NGX_WORKER_CONNECTIONS} +case "${NGX_WORKER_CONNECTIONS}" in +"${_NGX_WORKER_CONNECTIONS}" ) ;; +[1-9] | [1-9][0-9] ) + log_always "NGX_WORKER_CONNECTIONS: too low: ${NGX_WORKER_CONNECTIONS}" + log_always "setting NGX_WORKER_CONNECTIONS=${_NGX_WORKER_CONNECTIONS}" + NGX_WORKER_CONNECTIONS=${_NGX_WORKER_CONNECTIONS} +;; +## allow values within [100;9999999] +[1-9][0-9][0-9] ) ;; +[1-9][0-9][0-9][0-9] ) ;; +[1-9][0-9][0-9][0-9][0-9] ) ;; +[1-9][0-9][0-9][0-9][0-9][0-9] ) ;; +[1-9][0-9][0-9][0-9][0-9][0-9][0-9] ) ;; +* ) + log_always "NGX_WORKER_CONNECTIONS: unrecognized value: ${NGX_WORKER_CONNECTIONS}" + log_always "setting NGX_WORKER_CONNECTIONS=${_NGX_WORKER_CONNECTIONS}" + NGX_WORKER_CONNECTIONS=${_NGX_WORKER_CONNECTIONS} +;; +esac + +nofile_soft=$(ulimit -Sn) +nofile_hard=$(ulimit -Hn) + +if [ "${nofile_hard}" = unlimited ] ; then + ## minor hack (if applicable) :) + nofile_hard=${NGX_WORKER_RLIMIT_NOFILE} +fi + +nofile_ok=0 +while : ; do + [ ${nofile_hard} -ge ${NGX_WORKER_RLIMIT_NOFILE} ] || break + [ ${nofile_soft} -ge ${NGX_WORKER_RLIMIT_NOFILE} ] || break + + nofile_ok=1 +break ; done + +if [ ${nofile_ok} = 0 ] ; then + log_always "adjusting 'nofile' limits" + + log_always "Limits before:" + sed -En '1p;/open files/p' < /proc/$$/limits >&2 + + if [ ${nofile_hard} -lt ${NGX_WORKER_RLIMIT_NOFILE} ] ; then + ulimit -Hn "${NGX_WORKER_RLIMIT_NOFILE}" + nofile_hard=$(ulimit -Hn) + fi + if [ ${nofile_hard} -lt ${NGX_WORKER_RLIMIT_NOFILE} ] ; then + log_always "lowering NGX_WORKER_RLIMIT_NOFILE to ${nofile_hard}" + NGX_WORKER_RLIMIT_NOFILE=${nofile_hard} + fi + + if [ ${nofile_soft} -lt ${NGX_WORKER_RLIMIT_NOFILE} ] ; then + ulimit -Sn "${NGX_WORKER_RLIMIT_NOFILE}" + fi + + log_always "Limits after:" + sed -En '1p;/open files/p' < /proc/$$/limits >&2 +fi +unset nofile_soft nofile_hard nofile_ok + +export NGX_WORKER_PROCESSES NGX_WORKER_PRIORITY NGX_WORKER_RLIMIT_NOFILE NGX_WORKER_CONNECTIONS + +unset _NGX_WORKER_PROCESSES _NGX_WORKER_PRIORITY _NGX_WORKER_RLIMIT_NOFILE _NGX_WORKER_CONNECTIONS diff --git a/image-entry.d/20-http.envsh b/image-entry.d/20-http.envsh new file mode 100755 index 0000000..218f0cd --- /dev/null +++ b/image-entry.d/20-http.envsh @@ -0,0 +1,10 @@ +#!/bin/sh + +if [ "${NGX_HTTP}" = 0 ] ; then + unset NGX_HTTP_MODULES NGX_HTTP_SNIPPETS +else + set -a + NGX_HTTP_MODULES="${NGX_HTTP_MODULES:-}" + NGX_HTTP_SNIPPETS="${NGX_HTTP_SNIPPETS:-}" + set +a +fi diff --git a/image-entry.d/21-http-max-ranges.envsh b/image-entry.d/21-http-max-ranges.envsh new file mode 100755 index 0000000..9154c29 --- /dev/null +++ b/image-entry.d/21-http-max-ranges.envsh @@ -0,0 +1,27 @@ +#!/bin/sh + +if [ "${NGX_HTTP}" = 0 ] ; then + unset NGX_HTTP_MAX_RANGES +else + unset _NGX_HTTP_MAX_RANGES + ## here should be SANE defaults (!) + _NGX_HTTP_MAX_RANGES=16 + + [ -n "${NGX_HTTP_MAX_RANGES:-}" ] || NGX_HTTP_MAX_RANGES=${_NGX_HTTP_MAX_RANGES} + case "${NGX_HTTP_MAX_RANGES}" in + "${_NGX_HTTP_MAX_RANGES}" ) ;; + [1-9] | [1-9][0-9] | [1-9][0-9][0-9] ) ;; + 0 ) + log "HTTP: Range/If-Range/Accept-Ranges support is disabled" + ;; + * ) + log_always "NGX_HTTP_MAX_RANGES: unrecognized value: ${NGX_HTTP_MAX_RANGES}" + log_always "setting NGX_HTTP_MAX_RANGES=${_NGX_HTTP_MAX_RANGES}" + NGX_HTTP_MAX_RANGES=${_NGX_HTTP_MAX_RANGES} + ;; + esac + + export NGX_HTTP_MAX_RANGES + + unset _NGX_HTTP_MAX_RANGES +fi diff --git a/image-entry.d/30-mail.envsh b/image-entry.d/30-mail.envsh new file mode 100755 index 0000000..a4f2782 --- /dev/null +++ b/image-entry.d/30-mail.envsh @@ -0,0 +1,10 @@ +#!/bin/sh + +if [ "${NGX_MAIL}" = 0 ] ; then + unset NGX_MAIL_MODULES NGX_MAIL_SNIPPETS +else + set -a + NGX_MAIL_MODULES="${NGX_MAIL_MODULES:-}" + NGX_MAIL_SNIPPETS="${NGX_MAIL_SNIPPETS:-}" + set +a +fi diff --git a/image-entry.d/40-stream.envsh b/image-entry.d/40-stream.envsh new file mode 100755 index 0000000..f800bd3 --- /dev/null +++ b/image-entry.d/40-stream.envsh @@ -0,0 +1,10 @@ +#!/bin/sh + +if [ "${NGX_STREAM}" = 0 ] ; then + unset NGX_STREAM_MODULES NGX_STREAM_SNIPPETS +else + set -a + NGX_STREAM_MODULES="${NGX_STREAM_MODULES:-}" + NGX_STREAM_SNIPPETS="${NGX_STREAM_SNIPPETS:-}" + set +a +fi diff --git a/image-entry.d/50-flush-run-volume.sh b/image-entry.d/50-flush-run-volume.sh new file mode 100755 index 0000000..be6f192 --- /dev/null +++ b/image-entry.d/50-flush-run-volume.sh @@ -0,0 +1,11 @@ +#!/bin/sh +set -f + +. /image-entry.d/00-common.envsh + +find /run/angie/ -mindepth 1 -exec rm -rf {} + + +[ -d /run/angie/tmp ] || install -d -m 03777 /run/angie/tmp +[ -d /run/angie/lock ] || install_userdir /run/angie/lock + +exit 0 diff --git a/image-entry.d/51-topmost-configs.sh b/image-entry.d/51-topmost-configs.sh new file mode 100755 index 0000000..f929ffa --- /dev/null +++ b/image-entry.d/51-topmost-configs.sh @@ -0,0 +1,19 @@ +#!/bin/sh +set -f + +. /image-entry.d/00-common.envsh + +s=/etc/angie +d=/run/angie + +comps='' +[ "${NGX_HTTP}" = 0 ] || comps="${comps} http" +[ "${NGX_MAIL}" = 0 ] || comps="${comps} mail" +[ "${NGX_STREAM}" = 0 ] || comps="${comps} stream" + +for n in ${comps} ; do + ln_s "$s/ctx-$n.conf" "$d/" + ln_s "$s/mod-$n.conf" "$d/" +done + +exit 0 diff --git a/image-entry.d/52-merge-tree.sh b/image-entry.d/52-merge-tree.sh new file mode 100755 index 0000000..e6f88f2 --- /dev/null +++ b/image-entry.d/52-merge-tree.sh @@ -0,0 +1,28 @@ +#!/bin/sh +set -ef + +. /image-entry.d/00-common.envsh + +[ -d "${merged_root}" ] || install -d "${merged_root}" + +dirs='conf mod modules njs site snip' +[ "${NGX_PROCESS_STATIC}" = 0 ] || dirs="${dirs} static" + +for n in ${dirs} ; do + merged_dir="${merged_root}/$n" + while read -r old_path ; do + [ -n "${old_path}" ] || continue + + new_path=$(remap_path "${old_path}" "${merged_dir}") + [ -n "${new_path}" ] + + new_dir="${new_path%/*}" + [ -d "${new_dir}" ] || mkdir -p "${new_dir}" + + ln_s "${old_path}" "${new_path}" + done <<-EOF + $(overlay-dir-list.sh "/etc/angie/$n.dist" "/etc/angie/$n" "/angie/$n") + EOF +done + +exit 0 diff --git a/image-entry.d/53-expand-templates.sh b/image-entry.d/53-expand-templates.sh new file mode 100755 index 0000000..bb29c93 --- /dev/null +++ b/image-entry.d/53-expand-templates.sh @@ -0,0 +1,9 @@ +#!/bin/sh +set -ef + +. /image-entry.d/00-common.envsh + +untemplate_dir_envsubst "${merged_root}" +untemplate_dir_jinja "${merged_root}" + +exit 0 diff --git a/image-entry.d/54-combine-tree.sh b/image-entry.d/54-combine-tree.sh new file mode 100755 index 0000000..25d8724 --- /dev/null +++ b/image-entry.d/54-combine-tree.sh @@ -0,0 +1,185 @@ +#!/bin/sh +set -f + +. /image-entry.d/00-common.envsh + +[ "${NGX_STRICT_LOAD}" = 0 ] || set -e + +load_error() { + [ "${load_error_seen:-}" = 1 ] || log_always 'tree combine has failed' + load_error_seen=1 + if [ "${NGX_STRICT_LOAD}" = 1 ] ; then + t=10 + log_always "injecting delay for $t seconds" + sleep $t + exit 1 + fi +} + +smart_ln() { + if [ -h "$1" ] ; then + ln_s "$(readlink -e "$1")" "$2" + else + cp "$1" "$2" + fi +} + +dirs='conf modules njs site' +[ "${NGX_PROCESS_STATIC}" = 0 ] || dirs="${dirs} static" +while read -r old_path ; do + [ -n "${old_path}" ] || continue + + new_path=$(combine_remap_path "${old_path}") + [ -n "${new_path}" ] + + new_dir="${new_path%/*}" + [ -d "${new_dir}" ] || mkdir -p "${new_dir}" + + smart_ln "${old_path}" "${new_path}" +done <<-EOF +$( + for n in ${dirs} ; do + [ -d "${merged_root}/$n" ] || continue + find "${merged_root}/$n/" ! -type d + done | sort -V +) +EOF + +if [ "${NGX_PROCESS_STATIC}" = 0 ] ; then + for d in /angie/static /etc/angie/static /etc/angie/static.dist ; do + [ -d "$d" ] || continue + ln_s "$d" /run/angie/static + break + done +fi + +dirs='cache lib log' +for n in ${dirs} ; do + s="/angie/$n" + d="/run/angie/$n" + + if [ -d "$s" ] ; then + ln_s "$s" "$d" + else + [ -d "$d" ] || install_userdir "$d" + fi +done + +## provide same symlinks as upstream (both Angie and nginx) docker images do +d=/run/angie/log +[ -e "$d/access.log" ] || ln_s /dev/stdout "$d/access.log" +[ -e "$d/error.log" ] || ln_s /dev/stderr "$d/error.log" + +## Angie modules are loaded in [strict] order! +combine_modules() { + [ -n "$1" ] || return 1 + n="$1" ; shift + + i=0 + for m ; do + [ -n "$m" ] || continue + + old_name="mod/$n-$m.conf" + + old_path="${merged_root}/${old_name}" + if ! [ -f "${old_path}" ] ; then + log_always "file ${old_name} is not found" + load_error + log "file ${old_name} is skipped" + continue + fi + + new_path=$(printf "/run/angie/mod/$n-%02d-%s.conf" "$i" "$m") + new_dir="${new_path%/*}" + [ -d "${new_dir}" ] || mkdir -p "${new_dir}" + + smart_ln "${old_path}" "${new_path}" + + i=$((i+1)) + done +} + +combine_modules core ${NGX_CORE_MODULES:-} +combine_modules http ${NGX_HTTP_MODULES:-} +combine_modules mail ${NGX_MAIL_MODULES:-} +combine_modules stream ${NGX_STREAM_MODULES:-} + +combine_snippets() { + [ -n "$1" ] || return 1 + n="$1" ; shift + + for s ; do + [ -n "$s" ] || continue + + old_name="snip/$n-$s.conf" + + old_path="${merged_root}/${old_name}" + if ! [ -f "${old_path}" ] ; then + log_always "file ${old_name} is not found" + load_error + log "file ${old_name} is skipped" + continue + fi + + new_path="/run/angie/${old_name}" + new_dir="${new_path%/*}" + [ -d "${new_dir}" ] || mkdir -p "${new_dir}" + + smart_ln "${old_path}" "${new_path}" + done +} + +combine_snippets core ${NGX_CORE_SNIPPETS:-} +combine_snippets core_ev ${NGX_CORE_EVENTS_SNIPPETS:-} +combine_snippets http ${NGX_HTTP_SNIPPETS:-} +combine_snippets mail ${NGX_MAIL_SNIPPETS:-} +combine_snippets stream ${NGX_STREAM_SNIPPETS:-} + +combine_module_snippets() { + [ -n "$1" ] || return 1 + n="$1" ; shift + + for s ; do + [ -n "$s" ] || continue + + old_name="snip/$n-$s.conf" + + old_path="${merged_root}/${old_name}" + if ! [ -f "${old_path}" ] ; then + log "file ${old_name} is not found, skipping" + continue + fi + + new_path="/run/angie/${old_name}" + new_dir="${new_path%/*}" + [ -d "${new_dir}" ] || mkdir -p "${new_dir}" + + smart_ln "${old_path}" "${new_path}" + done +} + +combine_module_snippets core ${NGX_CORE_MODULES:-} +combine_module_snippets http ${NGX_HTTP_MODULES:-} +combine_module_snippets mail ${NGX_MAIL_MODULES:-} +combine_module_snippets stream ${NGX_STREAM_MODULES:-} + +## merge remaining snippets +while read -r old_path ; do + [ -n "${old_path}" ] || continue + + new_path=$(combine_remap_path "${old_path}") + [ -n "${new_path}" ] || exit 1 + + new_dir="${new_path%/*}" + [ -d "${new_dir}" ] || mkdir -p "${new_dir}" + + smart_ln "${old_path}" "${new_path}" +done <<-EOF +$( + find "${merged_root}/snip/" ! -type d \ + | grep -Ev -e "^${merged_root}/snip/(core|core_ev|http|mail|stream)-[^/]*\.conf\$" \ + | sort -V +) +EOF + +exit 0 diff --git a/image-entry.d/55-remove-merged-tree.sh b/image-entry.d/55-remove-merged-tree.sh new file mode 100755 index 0000000..3385b1c --- /dev/null +++ b/image-entry.d/55-remove-merged-tree.sh @@ -0,0 +1,11 @@ +#!/bin/sh +set -f + +. /image-entry.d/00-common.envsh + +if [ "${IEP_TRACE}" = 1 ] ; then + log_always "NOT removing merged tree: ${merged_root}/" +else + log "removing merged tree: ${merged_root}/" + rm -rf "${merged_root}" +fi diff --git a/image-entry.d/56-adjust-core-user.sh b/image-entry.d/56-adjust-core-user.sh new file mode 100755 index 0000000..2e49e59 --- /dev/null +++ b/image-entry.d/56-adjust-core-user.sh @@ -0,0 +1,16 @@ +#!/bin/sh +set -f + +. /image-entry.d/00-common.envsh + +conf=/etc/angie/conf.d/core-user.conf + +if [ "${IEP_ROOT}" = 1 ] ; then + log "Running as root, no need to adjust configuration" + exit 0 +fi + +log_always "Running as non-root, adjusting configuration" +rm -fv "${conf}" + +exit 0 diff --git a/image-entry.d/90-angie-config-test.sh b/image-entry.d/90-angie-config-test.sh new file mode 100755 index 0000000..07117b6 --- /dev/null +++ b/image-entry.d/90-angie-config-test.sh @@ -0,0 +1,25 @@ +#!/bin/sh +set -f + +. /image-entry.d/00-common.envsh + +## merely debug test +log_always 'test Angie configuration:' +log_always '=========================' +angie -t +r=$? +log_always '=========================' + +## cleanup after test +rm -f /run/angie/angie.pid + +if [ $r = 0 ] ; then + log_always 'ready to run Angie' +else + log_always 'configuration test has failed, see above' + t=15 + log_always "injecting delay for $t seconds" + sleep $t +fi + +exit 0 diff --git a/image-entry.sh b/image-entry.sh new file mode 100755 index 0000000..966b044 --- /dev/null +++ b/image-entry.sh @@ -0,0 +1,95 @@ +#!/bin/sh +set -f + +iep_preserve_env() { + ## preserve LD_PRELOAD + unset __IEP_LD_PRELOAD + __IEP_LD_PRELOAD="${LD_PRELOAD:-}" + unset LD_PRELOAD + + ## glibc: preserve MALLOC_ARENA_MAX + unset __IEP_MALLOC_ARENA_MAX + __IEP_MALLOC_ARENA_MAX=${MALLOC_ARENA_MAX:-2} + export MALLOC_ARENA_MAX=2 +} + +iep_restore_env() { + unset IEP_VERBOSE IEP_TRACE IEP_ROOT IEP_LOCAL_OVERRIDE + + ## restore LD_PRELOAD + if [ -n "${__IEP_LD_PRELOAD}" ] ; then + export LD_PRELOAD="${__IEP_LD_PRELOAD}" + fi + unset __IEP_LD_PRELOAD + + ## glibc: restore MALLOC_ARENA_MAX + if [ "${MALLOC_ARENA_MAX}" = 2 ] ; then + export MALLOC_ARENA_MAX="${__IEP_MALLOC_ARENA_MAX}" + fi + unset __IEP_MALLOC_ARENA_MAX +} + +iep_preserve_env + +## early setup TMPDIR (affects "mktemp") +export TMPDIR=/run/angie/tmp +[ -d "${TMPDIR}" ] || install -d -m 03777 "${TMPDIR}" + +## RFC: no need to run entire entrypoint for custom command +# case "$1" in +# angie | */angie ) ;; +# * ) +# unset IEP_INIT DUMB_INIT_ARGS +# iep_restore_env +# exec "$@" +# ;; +# esac + +unset __IEP_SRC ; __IEP_SRC="${0##*/}" +. /image-entry.d/00-common.envsh + +unexport IEP_INIT DUMB_INIT_ARGS + +## run parts (if any) +while read -r f ; do + [ -n "$f" ] || continue + [ -f "$f" ] || continue + + case "$f" in + *.envsh ) + if ! [ -x "$f" ] ; then + log "NOT sourcing $f - not executable" + continue + fi + log_always "sourcing $f" + __IEP_SRC="$f" + . "$f" + __IEP_SRC="${0##*/}" + ;; + * ) + if ! [ -x "$f" ] ; then + log "NOT running $f - not executable" + continue + fi + log_always "running $f" + "$f" + ;; + esac +done < "${__t}" + cat < "${__t}" > "$1" +} + +debconf_trim_i18n /var/cache/debconf/templates.dat +while read -r tmpl ; do + [ -n "${tmpl}" ] || continue + [ -s "${tmpl}" ] || continue + debconf_trim_i18n "${tmpl}" +done <&2 <<-EOF + + $0: + NGX_DEBUG is not set - defaulting to NGX_DEBUG=0 + + EOF + NGX_DEBUG=0 +fi + +# ANGIE_MODULES_DIR=/usr/lib/angie/modules +d=/etc/angie/mod.dist + +## produce package list +p= +for i ; do + [ -n "$i" ] || continue + p="$p${p:+ }angie-module-$i" +done + +[ -n "$p" ] || exit 0 + +apt-install.sh $p + +[ -d "$d" ] || install -d "$d" + +list_ngx_modules() { + set +e + dpkg-query -L "$1" \ + | grep -F -e "${ANGIE_MODULES_DIR}/" \ + | grep -E -e '/[^/]+_module(-debug)?\.so$' \ + | sort -V \ + | xargs -r ls -U1d 2>/dev/null + set -e +} + +gen_mod_config() { + if [ -s "$2" ] ; then + printf '%s: configuration already exists: %s\n' "$1" "$2" >&2 + return + fi + + [ -n "$3" ] || return + + for __m in $3 ; do + echo "load_module ${__m};" >> "$2" + done +} + +for i ; do + [ -n "$i" ] || continue + + p="angie-module-$i" + + ## adjust modules: + ## - remove debug module if not in debug image + ## - move debug module to usual location otherwise + while read -r fmod ; do + case "${fmod}" in + *-debug.so ) ;; + * ) continue ;; + esac + + if [ "${NGX_DEBUG}" = 0 ] ; then + rm -fv "${fmod}" + else + fmod_nodebug="${fmod%-debug.so}.so" + rm -fv "${fmod_nodebug}" + mv -fv "${fmod}" "${fmod_nodebug}" + fi + done <<-EOF + $(list_ngx_modules "$p") + EOF + + if [ -e "$d/.$i.preseed" ] ; then + printf '%s: skipping generation of attachable module configs\n' "$p" >&2 + continue + fi + + ## produce attachable module configs + http_modules= + mail_modules= + stream_modules= + while read -r fmod ; do + fmod_short="modules.dist/${fmod#"${ANGIE_MODULES_DIR}/"}" + fname=${fmod##*/} + case "${fname}" in + ngx_http_* ) + http_modules="${http_modules}${http_modules:+ }${fmod_short}" + ;; + ngx_mail_* ) + mail_modules="${mail_modules}${mail_modules:+ }${fmod_short}" + ;; + ngx_stream_* ) + stream_modules="${stream_modules}${stream_modules:+ }${fmod_short}" + ;; + ## damn NDK + ndk_http_* ) + http_modules="${http_modules}${http_modules:+ }${fmod_short}" + ;; + * ) + env printf '%s: unable to determine module type for file (skipping): %q\n' "$p" "${fmod}" >&2 + continue + ;; + esac + done <<-EOF + $(list_ngx_modules "$p") + EOF + + [ -z "${http_modules}" ] || gen_mod_config "$p" "$d/http-$i.conf" "${http_modules}" + [ -z "${mail_modules}" ] || gen_mod_config "$p" "$d/mail-$i.conf" "${mail_modules}" + [ -z "${stream_modules}" ] || gen_mod_config "$p" "$d/stream-$i.conf" "${stream_modules}" +done diff --git a/scripts/apt-install.sh b/scripts/apt-install.sh new file mode 100755 index 0000000..7859e80 --- /dev/null +++ b/scripts/apt-install.sh @@ -0,0 +1,44 @@ +#!/bin/sh +set -ef + +find_fresh_ts() { + { + find "$@" -exec stat -c '%Y' '{}' '+' 2>/dev/null || : + # duck and cover! + echo 1 + } | sort -rn | head -n 1 +} + +_apt_update() { + # update package lists; may fail sometimes, + # e.g. soon-to-release channels like Debian "bullseye" @ 22.04.2021 + + # (wannabe) smart package list update + ts_sources=$(find_fresh_ts /etc/apt/ -follow -regextype egrep -regex '.+\.(list|sources)$' -type f) + ts_lists=$(find_fresh_ts /var/lib/apt/lists/ -maxdepth 1 -regextype egrep -regex '.+_Packages(\.(bz2|gz|lz[4o]|xz|zstd?))?$' -type f) + if [ ${ts_sources} -gt ${ts_lists} ] ; then + apt-env.sh apt-get update + fi +} + +_dpkg_avail_hack() { + VERSION_CODENAME=$(. /etc/os-release ; printf '%s' "${VERSION_CODENAME}") || : + f="${DPKG_ADMINDIR:-/var/lib/dpkg}/available" + # if ${VERSION_CODENAME} is empty then we're on Debian sid or so :) + case "${VERSION_CODENAME}" in + stretch | buster | bionic | focal ) + # ref: https://unix.stackexchange.com/a/271387/49297 + if [ -s "$f" ] ; then + return + fi + /usr/lib/dpkg/methods/apt/update "${DPKG_ADMINDIR:-/var/lib/dpkg}" apt apt + ;; + * ) + touch "$f" + ;; + esac +} + +_apt_update +_dpkg_avail_hack +exec apt-env.sh apt-get install -y --no-install-recommends --no-install-suggests "$@" diff --git a/scripts/apt-remove.sh b/scripts/apt-remove.sh new file mode 100755 index 0000000..dc032b6 --- /dev/null +++ b/scripts/apt-remove.sh @@ -0,0 +1,5 @@ +#!/bin/sh +set -ef + +apt-env.sh apt-get purge -y --allow-remove-essential "$@" +exec apt-env.sh apt-get autopurge -y diff --git a/scripts/divert-rm.sh b/scripts/divert-rm.sh new file mode 100755 index 0000000..3793b22 --- /dev/null +++ b/scripts/divert-rm.sh @@ -0,0 +1,7 @@ +#!/bin/sh +set -ef +: "${1:?}" +d=$(printf '%s' "/run/$1" | tr -s '/') +mkdir -p "$(dirname "$d")" +dpkg-divert --divert "$d" --rename "$1" 2>/dev/null +rm -f "$d" diff --git a/scripts/envsubst-args.sh b/scripts/envsubst-args.sh new file mode 100755 index 0000000..d318c87 --- /dev/null +++ b/scripts/envsubst-args.sh @@ -0,0 +1,21 @@ +#!/bin/sh +set -f + +sed -znE '/^([^=]+)=.*$/s,,\1,p' /proc/self/environ \ +| sed -zE \ + -e '/^_$/d;/^ENVSUBST_/d;' \ + -e '/^__IEP/d;/^IEP_(INIT|VERBOSE|TRACE)$/d' \ +| { + if [ -n "${ENVSUBST_EXCLUDE_REGEX:-}" ] ; then + grep -zEv -e "${ENVSUBST_EXCLUDE_REGEX}" + else + if [ -n "${ENVSUBST_INCLUDE_REGEX:-}" ] ; then + grep -zE -e "${ENVSUBST_INCLUDE_REGEX}" + else + cat + fi + fi +} \ +| sort -zV \ +| xargs -0 -r printf '${%s} ' \ +| sed -zE 's/ $//' diff --git a/scripts/envsubst.sh b/scripts/envsubst.sh new file mode 100755 index 0000000..c446305 --- /dev/null +++ b/scripts/envsubst.sh @@ -0,0 +1,12 @@ +#!/bin/sh +set -f + +while [ -n "${ENVSUBST_ARGS}" ] ; do + [ -f "${ENVSUBST_ARGS}" ] || break + [ -s "${ENVSUBST_ARGS}" ] || break + + exec envsubst "$(cat "${ENVSUBST_ARGS}" 3: + raise ValueError('too many arguments (needed: 2)') + +if not sys.argv[1]: + raise ValueError('specify input file') +input_file = sys.argv[1] +if not os.path.exists(input_file): + raise ValueError('input file does not exist') + +if len(sys.argv) == 3: + if not sys.argv[2]: + raise ValueError('specify output file') + output_file = sys.argv[2] +else: + output_file, ext = os.path.splitext(input_file) + if ext != NGX_JINJA_SUFFIX: + raise ValueError(f'input file name extension mismatch (not a "{NGX_JINJA_SUFFIX}")') + +if input_file == output_file: + raise ValueError('unable to process template inplace') + +kwargs = {} +for m in NGX_JINJA_MODULES: + kwargs[m] = importlib.import_module(m) +kwargs['env'] = os.environ +kwargs['cfg'] = {} + + +def merge_dict_from_file(filename): + if not filename: + return False + if not isinstance(filename, str): + return False + if filename == '': + return False + if not os.path.exists(filename): + return False + if not os.path.isfile(filename): + print(f'{ME}: not a file, skipping: {filename}', file=sys.stderr) + return False + if filename.endswith('.yml') or filename.endswith('.yaml'): + with open(filename, mode='r', encoding='utf-8') as fx: + x = yaml.safe_load(fx) + kwargs['cfg'] = kwargs['cfg'] | x + return True + if filename.endswith('.json'): + with open(filename, mode='r', encoding='utf-8') as fx: + x = json.load(fx) + kwargs['cfg'] = kwargs['cfg'] | x + return True + print(f'{ME}: non-recognized name extension: {filename}', file=sys.stderr) + return False + + +for base_name in NGX_JINJA_CONFIG_NAMES: + for file_name in [ base_name + '.' + ext for ext in [ 'yml', 'yaml', 'json' ] ]: + if merge_dict_from_file(file_name): + break + continue +merge_dict_from_file(NGX_JINJA_CONFIG) + +with open(input_file, mode='r', encoding='utf-8') as fin: + template = jinja2.Template(fin.read()) +rendered = template.render(**kwargs) + +with open(output_file, mode='w', encoding='utf-8') as fout: + fout.write(rendered) diff --git a/scripts/overlay-dir-list.sh b/scripts/overlay-dir-list.sh new file mode 100755 index 0000000..c8bc4cb --- /dev/null +++ b/scripts/overlay-dir-list.sh @@ -0,0 +1,278 @@ +#!/bin/sh +set -f + +## ephemeral limit, keep in sync with verify_depth() +max_depth=99 +default_depth=20 + +me="${0##*/}" +usage() { + cat >&2 <<-EOF + # usage: ${me} [options] [ ..] + # options: + # -d , --depth , --depth= + # limit search depth to (default: ${default_depth}, max: ${max_depth}) + # -z, --zero + # separate entries with NUL instead of LF + # -s, --split-path + # separate directory and file name parts with "|" instead of "/" + EOF + exit "${1:-0}" +} +[ $# != 0 ] || usage + +msg() { echo ${1:+"# ${me}: $*"} >&2 ; } +msgf() { + _____fmt="$1" ; shift + env printf "# ${me}: ${_____fmt}\\n" ${@+"$@"} >&2 + unset _____fmt +} + +verify_depth() { + case "$1" in + [1-9] | [1-9][0-9] ) return 0 ;; + esac + msgf 'error: wrong depth specifier: %q' "$1" + usage 1 +} + +o_depth= +f_zero_eol= +f_split_path= + +## process options +want_value= +n_opt=0 +for i ; do + if [ -n "${want_value}" ] ; then + case "${want_value}" in + depth ) + o_depth="$i" + verify_depth "${o_depth}" + ;; + esac + + want_value= + n_opt=$((n_opt+1)) + continue + fi + + case "$i" in + -d | --depth | --depth=* ) + if [ -n "${o_depth}" ] ; then + msg 'error: "depth" option already set' + usage 1 + fi + case "$i" in + *=* ) + o_depth="${i#*=}" + verify_depth "${o_depth}" + ;; + * ) want_value=depth ;; + esac + ;; + -z | --zero ) + if [ -n "${f_zero_eol}" ] ; then + msg 'error: "zero" flag already set' + usage 1 + fi + f_zero_eol=1 + ;; + -s | --split-path ) + if [ -n "${f_split_path}" ] ; then + msg 'error: "split-path" flag already set' + usage 1 + fi + f_split_path=1 + ;; + -* ) + msgf 'unknown option: %q' "$i" + usage 1 + ;; + * ) break ;; + esac + + n_opt=$((n_opt+1)) +done + +[ ${n_opt} = 0 ] || shift ${n_opt} + +[ $# != 0 ] || usage 1 + +: "${o_depth:=${default_depth}}" + +path_sep='/' +[ -z "${f_split_path}" ] || path_sep='|' + +entry_sep='\n' +[ -z "${f_zero_eol}" ] || entry_sep='\0' + +entry_fmt="%s${path_sep}%s${entry_sep}" + +## work directory +w=$(mktemp -d) ; : "${w:?}" + +_cleanup() { + cd / + rm -rf -- "$w" +} + +adjust_with_skiplist() { + [ -s "$1" ] || return 0 + [ -s "$2" ] || return 0 + + __skiptype="$3" + while read -r __skipname ; do + [ -n "${__skipname}" ] || continue + + mawk \ + -v "n=${__skipname}" \ + -v "t=${__skiptype}" \ + ' + BEGIN { + RS = ORS = "\0"; + FS = OFS = "|"; + } + { + if ($3 != n) { print; next; } + if (t == "") { next; } + if ($1 ~ t) { next; } + print; + } + ' < "$2" > "$2.t" + mv -f "$2.t" "$2" + done < "$1" + unset __skipname __skiptype +} + +: > "$w/all" + +## process arguments +for i ; do + ## arguments with '|' are skipped - try naming paths simplier + case "$i" in + *\|* ) + msgf 'argument with "|" is IGNORED: %q' "$i" + continue + ;; + esac + + ## non-existent directories are silently skipped + [ -d "$i" ] || continue + + ## generate directory listing + ## paths with '\n' and '|' are silently skipped - try naming paths simplier + find "$i/" -follow -mindepth 1 -maxdepth 1 ! -name '*|*' -printf '%y|%h|%P\0' \ + | sed -zE '/\n/d' > "$w/current" + + ## filter out uncommon entry types + ## allowed types: + ## d - directory + ## f - file + ## less common types (but also allowed): + ## b - block device + ## c - character device + ## p - pipe + ## s - socket + sed -i -zE '/^[^bcdfps]/d' "$w/current" + + ## empty directory - continue with next argument + [ -s "$w/current" ] || continue + + ## generate skiplist + grep -zE '\.-$' < "$w/current" \ + | cut -z -d '|' -f 3 \ + | sed -zE 's/\.-$//' \ + | tr '\0' '\n' > "$w/skip" + ## adjust current list: remove entries from skiplist + sed -i -zE '/\.-$/d' "$w/current" + + ## adjust accumulated list: remove "skip" entries + adjust_with_skiplist "$w/skip" "$w/all" + rm -f "$w/skip" + + ## adjust accumulated list: override all entries with "non-dir" entries from current list + ## NB: entry type from current list overrides entry from previously accumulated list + sed -zEn '/^[^d]/p' < "$w/current" \ + | cut -z -d '|' -f 3 \ + | tr '\0' '\n' > "$w/nondir" + adjust_with_skiplist "$w/nondir" "$w/all" + rm -f "$w/nondir" + + ## adjust accumulated list: override "non-dir" entries with "dir" entries from current list + ## NB: entry type from current list overrides entry from previously accumulated list + sed -zEn '/^d/p' < "$w/current" \ + | cut -z -d '|' -f 3 \ + | tr '\0' '\n' > "$w/dir" + adjust_with_skiplist "$w/dir" "$w/all" '[^d]' + rm -f "$w/dir" + + ## merge lists + sort -zV -t '|' -k 3 < "$w/current" >> "$w/all" + rm -f "$w/current" +done + +# nothing to do? +if ! [ -s "$w/all" ] ; then + _cleanup + exit 0 +fi + +cut -z -d '|' -f '3' < "$w/all" \ +| sort -zuV \ +| tr '\0' '\n' > "$w/names" + +: > "$w/dirnames" + +sub_depth=$(( o_depth - 1 )) + +while read -r name ; do + mawk \ + -v "n=${name}" \ + ' + BEGIN { + RS = "\0"; ORS = "\n"; + FS = OFS = "|"; + } + + $3 == n { print; } + ' < "$w/all" > "$w/list" + + while IFS='|' read -r dtype dir _name ; do + [ -n "${dtype}" ] || continue + + # ${_name} is unused + case "${dtype}" in + d ) + [ "${sub_depth}" != 0 ] || continue + + if grep -Fxq -e "${name}" "$w/dirnames" ; then + continue + fi + + printf '%s\n' "${name}" >> "$w/dirnames" + + mawk \ + -v "n=${name}" \ + ' + BEGIN { + ORS = "\0"; + FS = "|"; OFS = "/"; + } + + $3 == n { print $2,$3; } + ' < "$w/list" > "$w/dir.args" + + xargs -0 -a "$w/dir.args" "$0" ${f_zero_eol:+-z} ${f_split_path:+-s} -d "${sub_depth}" + rm -f "$w/dir.args" + ;; + * ) + printf "${entry_fmt}" "${dir}" "${name}" + ;; + esac + done < "$w/list" + +done < "$w/names" + +_cleanup +exit 0 diff --git a/scripts/pip-env.sh b/scripts/pip-env.sh new file mode 100755 index 0000000..8a182f8 --- /dev/null +++ b/scripts/pip-env.sh @@ -0,0 +1,8 @@ +#!/bin/sh +set -a +PIP_DISABLE_PIP_VERSION_CHECK=1 +PIP_NO_CACHE_DIR=1 +PIP_ROOT_USER_ACTION=ignore +PIP_NO_COMPILE=1 +set +a +exec "$@" diff --git a/scripts/python-rm-cache.sh b/scripts/python-rm-cache.sh new file mode 100755 index 0000000..039b846 --- /dev/null +++ b/scripts/python-rm-cache.sh @@ -0,0 +1,7 @@ +#!/bin/sh +set -f +for i ; do + find "$i/" -name __pycache__ -exec rm -rf {} + + find "$i/" ! -type d -name '*.py[co]' -exec rm -f {} + +done +exit 0 diff --git a/scripts/static-compress.sh b/scripts/static-compress.sh new file mode 100755 index 0000000..5b8c829 --- /dev/null +++ b/scripts/static-compress.sh @@ -0,0 +1,65 @@ +#!/bin/sh +set -f + +COMPRESS_MIN_RATIO=90 + +if command -V gzip >/dev/null ; then has_gzip=1 ; fi +if command -V brotli >/dev/null ; then has_brotli=1 ; fi +if command -V zstd >/dev/null ; then has_zstd=1 ; fi + +do_gzip() { [ -s "$1.gz" ] || gzip -1kf "$1" ; comp_fixup "$1" "$1.gz" ; } +do_brotli() { [ -s "$1.br" ] || brotli -1kf "$1" ; comp_fixup "$1" "$1.br" ; } +do_zstd() { [ -s "$1.zst" ] || zstd -q1kf "$1" ; comp_fixup "$1" "$1.zst" ; } + +comp_fixup() { + size1=$(env stat -c '%s' "$1") || return + + [ -f "$2" ] || return + [ -s "$2" ] || { rm -f "$2" ; return ; } + size2=$(env stat -c '%s' "$2") || return + + pow1=${#size1} ; pow2=${#size2} + + ## if size2 is _longer_ than size1 - compression did something wrong (file is bigger) + if [ ${pow2} -gt ${pow1} ] ; then + rm -f "$2" ; return + fi + + ## if size1 is _longer_ size2 more than 2 digits - compression was done very successful + ## doubtful but okay (c) Oleg Tinkov + if [ $(( pow1 - pow2 )) -gt 2 ] ; then + return + fi + + ## math hack! + if [ ${pow1} -gt 7 ] ; then + skew=$(( pow1 - 4 )) + pow1=$(( pow1 - skew )) + pow2=$(( pow2 - skew )) + size1=$(printf '%s' "${size1}" | cut -c 1-${pow1}) + size2=$(printf '%s' "${size2}" | cut -c 1-${pow2}) + fi + + ratio=$(( (100 * size2) / size1 )) + if [ ${ratio} -ge ${COMPRESS_MIN_RATIO} ] ; then + rm -f "$2" + else + ## seems to be excessive + : touch -r "$1" -m "$2" + fi +} + +for i ; do + [ -n "$i" ] || continue + + case "$i" in + *.br | *.gz | *.zst ) continue ;; + esac + + [ -f "$i" ] || continue + [ -s "$i" ] || continue + + [ -z "${has_gzip}" ] || do_gzip "$i" + [ -z "${has_brotli}" ] || do_brotli "$i" + [ -z "${has_zstd}" ] || do_zstd "$i" +done