From 48f13f97a3805d901688ba433e3d8e915bde1980 Mon Sep 17 00:00:00 2001 From: Konstantin Demin Date: Thu, 5 Jun 2025 11:01:19 +0300 Subject: [PATCH] initial commit --- .gitignore | 2 + Dockerfile | 198 +++++++++ Dockerfile.base | 365 +++++++++++++++++ Dockerfile.deps | 196 +++++++++ LICENSE | 175 ++++++++ TODO | 6 + angie/angie.conf | 15 + angie/autoconf/core-error-log.conf.j2 | 4 + angie/autoconf/core-lock-file.conf | 2 + angie/autoconf/core-pcre-jit.conf | 1 + angie/autoconf/core-user.conf.in | 3 + angie/autoconf/core-worker-env.conf.j2 | 31 ++ angie/autoconf/core-worker-env.j2inc | 4 + angie/autoconf/core-worker-env.txt.j2 | 12 + angie/autoconf/core-worker.conf.j2 | 10 + angie/autoconf/core_ev-worker.conf.j2 | 7 + angie/autoconf/http-access-log.conf | 2 + .../autoconf/http-access-log/default.conf.j2 | 4 + .../http-access-log/format/extended.conf | 7 + .../autoconf/http-access-log/format/main.conf | 4 + angie/autoconf/http-alt-svc.conf | 1 + angie/autoconf/http-buffers.conf | 4 + angie/autoconf/http-client-body-temp.conf | 1 + angie/autoconf/http-max-ranges.conf.j2 | 3 + angie/autoconf/http-mime-types.conf | 8 + .../http-request-headers-basic.conf.j2 | 26 ++ .../http-request-headers-forwarded.conf | 27 ++ angie/autoconf/http-resolver.conf.j2 | 2 + angie/autoconf/http-response-headers.conf | 1 + angie/autoconf/http-v2.conf.j2 | 3 + angie/autoconf/http-v3.conf.j2 | 3 + angie/autoconf/http-webroot.conf | 1 + angie/autoconf/mail-resolver.conf.j2 | 2 + angie/autoconf/stream-resolver.conf.j2 | 2 + angie/conf/acme/path.conf | 1 + angie/conf/brotli/buffers.conf | 5 + angie/conf/brotli/types.conf.j2 | 9 + angie/conf/core-quic-bpf.conf | 1 + angie/conf/core_ev-accept-mutex-delay.conf | 1 + angie/conf/core_ev-accept-mutex.conf | 1 + angie/conf/core_ev-multi-accept.conf | 1 + angie/conf/fastcgi/buffers.conf | 4 + angie/conf/fastcgi/cache-bypass.conf.j2 | 14 + angie/conf/fastcgi/headers.conf | 2 + angie/conf/fastcgi/param.conf | 7 + angie/conf/fastcgi/temp.conf | 1 + angie/conf/grpc/buffers.conf | 1 + angie/conf/grpc/headers.conf | 2 + angie/conf/grpc/ssl.conf.j2 | 7 + angie/conf/gzip/buffers.conf | 4 + angie/conf/gzip/proxied.conf | 1 + angie/conf/gzip/types.conf.j2 | 9 + angie/conf/gzip/vary.conf | 1 + angie/conf/http-acme.conf | 1 + angie/conf/http-brotli-static.conf | 1 + angie/conf/http-brotli.conf | 2 + angie/conf/http-fastcgi.conf | 1 + angie/conf/http-grpc.conf | 1 + angie/conf/http-gunzip.conf | 2 + angie/conf/http-gzip-static.conf | 1 + angie/conf/http-gzip.conf | 2 + angie/conf/http-modsecurity.conf | 4 + angie/conf/http-njs.conf | 1 + angie/conf/http-perl.conf | 1 + angie/conf/http-proxy.conf | 2 + angie/conf/http-quic-gso.conf.j2 | 5 + angie/conf/http-scgi.conf | 1 + angie/conf/http-ssl.conf.j2 | 25 ++ angie/conf/http-uwsgi.conf | 1 + angie/conf/http-v2.conf | 2 + angie/conf/http-v3.conf | 2 + angie/conf/http-zstd-static.conf | 1 + angie/conf/http-zstd.conf | 2 + angie/conf/http2/param.conf | 2 + angie/conf/http3/param.conf.j2 | 9 + angie/conf/mail-ssl.conf.j2 | 5 + angie/conf/njs/path.conf | 1 + angie/conf/njs/tls-ca-file.conf.in | 1 + angie/conf/perl/path.conf | 1 + angie/conf/proxy-http/buffers.conf | 4 + angie/conf/proxy-http/cache-bypass.conf.j2 | 14 + angie/conf/proxy-http/headers.conf | 2 + angie/conf/proxy-http/temp.conf | 1 + angie/conf/proxy-http/version.conf | 1 + angie/conf/proxy-stream/.gitkeep | 0 angie/conf/proxy/ssl.conf.j2 | 7 + angie/conf/scgi/buffers.conf | 4 + angie/conf/scgi/cache-bypass.conf.j2 | 14 + angie/conf/scgi/headers.conf | 2 + angie/conf/scgi/param.conf | 7 + angie/conf/scgi/temp.conf | 1 + angie/conf/ssl/cmd.conf.j2 | 3 + angie/conf/stream-njs.conf | 1 + angie/conf/stream-proxy.conf | 2 + angie/conf/stream-ssl.conf.j2 | 5 + angie/conf/uwsgi/buffers.conf | 4 + angie/conf/uwsgi/cache-bypass.conf.j2 | 14 + angie/conf/uwsgi/headers.conf | 2 + angie/conf/uwsgi/param.conf | 7 + angie/conf/uwsgi/ssl.conf.j2 | 7 + angie/conf/uwsgi/temp.conf | 1 + angie/conf/zstd/buffers.conf | 4 + angie/conf/zstd/types.conf.j2 | 9 + angie/ctx-core.conf | 2 + angie/ctx-core_ev.conf | 4 + angie/ctx-http.conf | 5 + angie/ctx-mail.conf | 5 + angie/ctx-stream.conf | 5 + angie/j2cfg.yml | 60 +++ angie/j2cfg/00-headers.yml.j2 | 53 +++ angie/mod-core.conf | 1 + angie/mod-http.conf | 1 + angie/mod-mail.conf | 1 + angie/mod-stream.conf | 1 + angie/mod/.brotli.preseed | 0 angie/mod/.njs-light.preseed | 0 angie/mod/.njs.preseed | 0 angie/mod/.otel.preseed | 0 angie/mod/.postgres.preseed | 0 angie/mod/.rtmp.preseed | 0 angie/mod/.vts.preseed | 0 angie/mod/.wamr.preseed | 0 angie/mod/.wasm.preseed | 0 angie/mod/.wasmtime.preseed | 0 angie/mod/.zstd.preseed | 0 angie/mod/core-wamr.conf | 1 + angie/mod/core-wasm.conf | 2 + angie/mod/core-wasmtime.conf | 1 + angie/mod/http-brotli-static.conf | 1 + angie/mod/http-brotli.conf | 1 + angie/mod/http-njs.conf | 1 + angie/mod/http-otel.conf | 1 + angie/mod/http-postgres.conf | 1 + angie/mod/http-rtmp.conf | 1 + angie/mod/http-sts.conf | 1 + angie/mod/http-vts.conf | 1 + angie/mod/http-wasm.conf | 1 + angie/mod/http-zstd-static.conf | 1 + angie/mod/http-zstd.conf | 1 + angie/mod/stream-njs.conf | 1 + angie/mod/stream-sts.conf | 1 + angie/modsecurity/rules.conf | 33 ++ angie/snip/cache.j2mod | 55 +++ angie/snip/deny-dotfiles | 3 + angie/snip/disable-compression.j2 | 8 + angie/snip/empty-favicon | 4 + angie/snip/fastcgi-location | 5 + angie/snip/fastcgi-request-headers.j2 | 6 + angie/snip/fastcgi-response-headers.j2 | 7 + angie/snip/grpc-request-headers.j2 | 6 + angie/snip/grpc-response-headers.j2 | 7 + angie/snip/http-alt-svc.j2 | 13 + angie/snip/http-response-headers.j2 | 6 + angie/snip/internal-area | 5 + angie/snip/log.j2mod | 12 + angie/snip/proxy-request-headers.j2 | 6 + angie/snip/proxy-response-headers.j2 | 7 + angie/snip/resolver.j2inc | 13 + angie/snip/scgi-request-headers.j2 | 6 + angie/snip/scgi-response-headers.j2 | 7 + angie/snip/ssl-intermediate.j2 | 2 + angie/snip/ssl-modern.j2 | 2 + angie/snip/ssl-old.j2 | 2 + angie/snip/ssl-profile.j2inc | 28 ++ angie/snip/uwsgi-request-headers.j2 | 6 + angie/snip/uwsgi-response-headers.j2 | 7 + angie/static/robots.txt | 2 + angie/tls/dh1024.pem | 5 + angie/tls/dh2048.pem | 8 + angie/tls/ffdhe2048.pem | 8 + angie/tls/ffdhe3072.pem | 11 + angie/tls/ffdhe4096.pem | 13 + apt/prefs.backports | 27 ++ apt/sources.angie | 5 + apt/sources.debian | 11 + build-scripts/image-base.sh | 50 +++ build-scripts/image-deps.sh | 19 + build-scripts/image.sh | 46 +++ doc/README.md | 3 + doc/examples/README.md | 9 + doc/examples/basic/Dockerfile | 4 + doc/examples/basic/README.md | 15 + doc/examples/basic/docker-compose.yml | 12 + doc/examples/basic/site/http-site.conf | 3 + doc/examples/basic/static/index.html | 5 + doc/examples/config-template/README.md | 113 +++++ .../config-template/conf/j2cfg/my-caches.yml | 16 + .../conf/site/http-my-cache.conf.j2 | 79 ++++ .../config-template/data/cache/.gitkeep | 0 .../config-template/data/lib/.gitkeep | 0 .../config-template/docker-compose.yml | 16 + doc/examples/j2cfg-override/Dockerfile | 13 + doc/examples/j2cfg-override/README.md | 32 ++ .../j2cfg-override/docker-compose.yml | 15 + .../j2cfg/override-compress-types.yml | 6 + .../j2cfg-override/site/http-site.conf | 3 + doc/examples/j2cfg-override/static/index.html | 5 + doc/examples/j2cfg-override/static/index.txt | 5 + doc/examples/njs/Dockerfile | 6 + doc/examples/njs/README.md | 47 +++ doc/examples/njs/docker-compose.yml | 13 + doc/examples/njs/site/http-env-js.conf | 11 + doc/examples/njs/site/ngx_env.js | 12 + doc/examples/perl/Dockerfile | 11 + doc/examples/perl/README.md | 70 ++++ doc/examples/perl/site/http-env-perl.conf | 11 + doc/examples/perl/site/ngx_env.pm | 22 + doc/examples/ssl/Dockerfile | 15 + doc/examples/ssl/README.md | 67 +++ doc/examples/ssl/demo-ca/0-CA-Root.crt | 19 + doc/examples/ssl/demo-ca/0-CA-Root.key | 27 ++ doc/examples/ssl/demo-ca/1-CA-Internal.crt | 19 + doc/examples/ssl/demo-ca/1-CA-Internal.key | 27 ++ doc/examples/ssl/demo-ca/2-example.org.crt | 21 + doc/examples/ssl/demo-ca/2-example.org.pem | 27 ++ .../ssl/demo-ca/3-www.example.org.crt | 20 + .../ssl/demo-ca/3-www.example.org.pem | 27 ++ doc/examples/ssl/demo-ca/Makefile | 12 + doc/examples/ssl/docker-compose.yml | 15 + doc/examples/ssl/site/http-site.conf | 33 ++ .../ssl/static/example.org/index.html | 5 + .../ssl/static/www.example.org/index.html | 5 + doc/examples/ssl/tls/ca/internal-ca.crt | 19 + doc/examples/ssl/tls/ca/root-ca.crt | 19 + doc/examples/ssl/tls/example.org.chain.crt | 40 ++ doc/examples/ssl/tls/example.org.pem | 27 ++ .../ssl/tls/www.example.org.chain.crt | 39 ++ doc/examples/ssl/tls/www.example.org.pem | 27 ++ doc/examples/static-template/Dockerfile | 4 + doc/examples/static-template/README.md | 26 ++ .../static-template/docker-compose.yml | 12 + .../static-template/site/http-site.conf | 3 + .../static-template/static/index.html.j2 | 6 + image-entry.d/00-common.envsh | 245 +++++++++++ image-entry.d/01-defaults.envsh | 34 ++ image-entry.d/02-nonroot.envsh | 6 + image-entry.d/03-local-ip-addresses.envsh | 30 ++ image-entry.d/04-resolver.envsh | 109 +++++ image-entry.d/05-ca-certificates.envsh | 12 + image-entry.d/10-core.envsh | 9 + image-entry.d/11-core-modules.envsh | 56 +++ image-entry.d/12-core-user.envsh | 76 ++++ image-entry.d/13-core-worker.envsh | 206 ++++++++++ image-entry.d/14-core-loglevel.envsh | 32 ++ image-entry.d/20-http.envsh | 18 + image-entry.d/21-http-modules.envsh | 104 +++++ image-entry.d/22-http-ssl.envsh | 9 + image-entry.d/23-http-max-ranges.envsh | 30 ++ image-entry.d/24-http-forward-headers.envsh | 112 +++++ image-entry.d/30-mail.envsh | 10 + image-entry.d/31-mail-modules.envsh | 41 ++ image-entry.d/32-mail-ssl.envsh | 9 + image-entry.d/40-stream.envsh | 10 + image-entry.d/41-stream-modules.envsh | 42 ++ image-entry.d/42-stream-ssl.envsh | 9 + image-entry.d/70-merge-dirs.sh | 40 ++ image-entry.d/71-topmost-configs.sh | 20 + image-entry.d/72-fullfil-tree.sh | 58 +++ image-entry.d/73-expand-templates.sh | 114 ++++++ image-entry.d/74-combine-tree.sh | 143 +++++++ image-entry.d/75-adjust-core-user.sh | 16 + image-entry.d/76-openssl-ca-certs.envsh | 135 ++++++ image-entry.d/90-angie-config-test.sh | 31 ++ image-entry.d/99-cleanup-env.envsh | 67 +++ image-entry.sh | 177 ++++++++ j2cfg/j2cfg-dump.py | 18 + j2cfg/j2cfg-multi.py | 24 ++ j2cfg/j2cfg-single.py | 28 ++ j2cfg/j2cfg/__init__.py | 266 ++++++++++++ j2cfg/j2cfg/functions.py | 387 ++++++++++++++++++ j2cfg/j2cfg/settings.py | 80 ++++ j2cfg/j2cfg/test.j2 | 173 ++++++++ requirements.txt | 4 + scripts-extra/certifi-extras.sh | 43 ++ scripts-extra/gpg-batch.sh | 45 ++ scripts-extra/gpg-export.sh | 28 ++ scripts/angie | 12 + scripts/angie-builtin-modules.sh | 26 ++ scripts/angie-reload.sh | 15 + scripts/apt-clean.sh | 56 +++ scripts/apt-env.sh | 8 + scripts/apt-install-angie-mod.sh | 267 ++++++++++++ scripts/apt-install.sh | 45 ++ scripts/apt-remove.sh | 5 + scripts/divert-rm.sh | 7 + scripts/dpkg-search.sh | 24 ++ scripts/envsubst-args.sh | 23 ++ scripts/envsubst.sh | 12 + scripts/j2cfg-dump | 2 + scripts/j2cfg-multi | 2 + scripts/j2cfg-single | 2 + scripts/openssl-cert-auto-pem.sh | 100 +++++ scripts/openssl-generate-dh-bundle.sh | 16 + scripts/openssl-ocsp.sh | 206 ++++++++++ scripts/pip-env.sh | 8 + scripts/python-rm-cache.sh | 9 + scripts/static-compress.sh | 54 +++ 297 files changed, 7136 insertions(+) create mode 100644 .gitignore create mode 100644 Dockerfile create mode 100644 Dockerfile.base create mode 100644 Dockerfile.deps create mode 100644 LICENSE create mode 100644 TODO create mode 100644 angie/angie.conf create mode 100644 angie/autoconf/core-error-log.conf.j2 create mode 100644 angie/autoconf/core-lock-file.conf create mode 100644 angie/autoconf/core-pcre-jit.conf create mode 100644 angie/autoconf/core-user.conf.in create mode 100644 angie/autoconf/core-worker-env.conf.j2 create mode 100644 angie/autoconf/core-worker-env.j2inc create mode 100644 angie/autoconf/core-worker-env.txt.j2 create mode 100644 angie/autoconf/core-worker.conf.j2 create mode 100644 angie/autoconf/core_ev-worker.conf.j2 create mode 100644 angie/autoconf/http-access-log.conf create mode 100644 angie/autoconf/http-access-log/default.conf.j2 create mode 100644 angie/autoconf/http-access-log/format/extended.conf create mode 100644 angie/autoconf/http-access-log/format/main.conf create mode 100644 angie/autoconf/http-alt-svc.conf create mode 100644 angie/autoconf/http-buffers.conf create mode 100644 angie/autoconf/http-client-body-temp.conf create mode 100644 angie/autoconf/http-max-ranges.conf.j2 create mode 100644 angie/autoconf/http-mime-types.conf create mode 100644 angie/autoconf/http-request-headers-basic.conf.j2 create mode 100644 angie/autoconf/http-request-headers-forwarded.conf create mode 100644 angie/autoconf/http-resolver.conf.j2 create mode 100644 angie/autoconf/http-response-headers.conf create mode 100644 angie/autoconf/http-v2.conf.j2 create mode 100644 angie/autoconf/http-v3.conf.j2 create mode 100644 angie/autoconf/http-webroot.conf create mode 100644 angie/autoconf/mail-resolver.conf.j2 create mode 100644 angie/autoconf/stream-resolver.conf.j2 create mode 100644 angie/conf/acme/path.conf create mode 100644 angie/conf/brotli/buffers.conf create mode 100644 angie/conf/brotli/types.conf.j2 create mode 100644 angie/conf/core-quic-bpf.conf create mode 100644 angie/conf/core_ev-accept-mutex-delay.conf create mode 100644 angie/conf/core_ev-accept-mutex.conf create mode 100644 angie/conf/core_ev-multi-accept.conf create mode 100644 angie/conf/fastcgi/buffers.conf create mode 100644 angie/conf/fastcgi/cache-bypass.conf.j2 create mode 100644 angie/conf/fastcgi/headers.conf create mode 100644 angie/conf/fastcgi/param.conf create mode 100644 angie/conf/fastcgi/temp.conf create mode 100644 angie/conf/grpc/buffers.conf create mode 100644 angie/conf/grpc/headers.conf create mode 100644 angie/conf/grpc/ssl.conf.j2 create mode 100644 angie/conf/gzip/buffers.conf create mode 100644 angie/conf/gzip/proxied.conf create mode 100644 angie/conf/gzip/types.conf.j2 create mode 100644 angie/conf/gzip/vary.conf create mode 100644 angie/conf/http-acme.conf create mode 100644 angie/conf/http-brotli-static.conf create mode 100644 angie/conf/http-brotli.conf create mode 100644 angie/conf/http-fastcgi.conf create mode 100644 angie/conf/http-grpc.conf create mode 100644 angie/conf/http-gunzip.conf create mode 100644 angie/conf/http-gzip-static.conf create mode 100644 angie/conf/http-gzip.conf create mode 100644 angie/conf/http-modsecurity.conf create mode 100644 angie/conf/http-njs.conf create mode 100644 angie/conf/http-perl.conf create mode 100644 angie/conf/http-proxy.conf create mode 100644 angie/conf/http-quic-gso.conf.j2 create mode 100644 angie/conf/http-scgi.conf create mode 100644 angie/conf/http-ssl.conf.j2 create mode 100644 angie/conf/http-uwsgi.conf create mode 100644 angie/conf/http-v2.conf create mode 100644 angie/conf/http-v3.conf create mode 100644 angie/conf/http-zstd-static.conf create mode 100644 angie/conf/http-zstd.conf create mode 100644 angie/conf/http2/param.conf create mode 100644 angie/conf/http3/param.conf.j2 create mode 100644 angie/conf/mail-ssl.conf.j2 create mode 100644 angie/conf/njs/path.conf create mode 100644 angie/conf/njs/tls-ca-file.conf.in create mode 100644 angie/conf/perl/path.conf create mode 100644 angie/conf/proxy-http/buffers.conf create mode 100644 angie/conf/proxy-http/cache-bypass.conf.j2 create mode 100644 angie/conf/proxy-http/headers.conf create mode 100644 angie/conf/proxy-http/temp.conf create mode 100644 angie/conf/proxy-http/version.conf create mode 100644 angie/conf/proxy-stream/.gitkeep create mode 100644 angie/conf/proxy/ssl.conf.j2 create mode 100644 angie/conf/scgi/buffers.conf create mode 100644 angie/conf/scgi/cache-bypass.conf.j2 create mode 100644 angie/conf/scgi/headers.conf create mode 100644 angie/conf/scgi/param.conf create mode 100644 angie/conf/scgi/temp.conf create mode 100644 angie/conf/ssl/cmd.conf.j2 create mode 100644 angie/conf/stream-njs.conf create mode 100644 angie/conf/stream-proxy.conf create mode 100644 angie/conf/stream-ssl.conf.j2 create mode 100644 angie/conf/uwsgi/buffers.conf create mode 100644 angie/conf/uwsgi/cache-bypass.conf.j2 create mode 100644 angie/conf/uwsgi/headers.conf create mode 100644 angie/conf/uwsgi/param.conf create mode 100644 angie/conf/uwsgi/ssl.conf.j2 create mode 100644 angie/conf/uwsgi/temp.conf create mode 100644 angie/conf/zstd/buffers.conf create mode 100644 angie/conf/zstd/types.conf.j2 create mode 100644 angie/ctx-core.conf create mode 100644 angie/ctx-core_ev.conf create mode 100644 angie/ctx-http.conf create mode 100644 angie/ctx-mail.conf create mode 100644 angie/ctx-stream.conf create mode 100644 angie/j2cfg.yml create mode 100644 angie/j2cfg/00-headers.yml.j2 create mode 100644 angie/mod-core.conf create mode 100644 angie/mod-http.conf create mode 100644 angie/mod-mail.conf create mode 100644 angie/mod-stream.conf create mode 100644 angie/mod/.brotli.preseed create mode 100644 angie/mod/.njs-light.preseed create mode 100644 angie/mod/.njs.preseed create mode 100644 angie/mod/.otel.preseed create mode 100644 angie/mod/.postgres.preseed create mode 100644 angie/mod/.rtmp.preseed create mode 100644 angie/mod/.vts.preseed create mode 100644 angie/mod/.wamr.preseed create mode 100644 angie/mod/.wasm.preseed create mode 100644 angie/mod/.wasmtime.preseed create mode 100644 angie/mod/.zstd.preseed create mode 100644 angie/mod/core-wamr.conf create mode 100644 angie/mod/core-wasm.conf create mode 100644 angie/mod/core-wasmtime.conf create mode 100644 angie/mod/http-brotli-static.conf create mode 100644 angie/mod/http-brotli.conf create mode 100644 angie/mod/http-njs.conf create mode 100644 angie/mod/http-otel.conf create mode 100644 angie/mod/http-postgres.conf create mode 100644 angie/mod/http-rtmp.conf create mode 100644 angie/mod/http-sts.conf create mode 100644 angie/mod/http-vts.conf create mode 100644 angie/mod/http-wasm.conf create mode 100644 angie/mod/http-zstd-static.conf create mode 100644 angie/mod/http-zstd.conf create mode 100644 angie/mod/stream-njs.conf create mode 100644 angie/mod/stream-sts.conf create mode 100644 angie/modsecurity/rules.conf create mode 100644 angie/snip/cache.j2mod create mode 100644 angie/snip/deny-dotfiles create mode 100644 angie/snip/disable-compression.j2 create mode 100644 angie/snip/empty-favicon create mode 100644 angie/snip/fastcgi-location create mode 100644 angie/snip/fastcgi-request-headers.j2 create mode 100644 angie/snip/fastcgi-response-headers.j2 create mode 100644 angie/snip/grpc-request-headers.j2 create mode 100644 angie/snip/grpc-response-headers.j2 create mode 100644 angie/snip/http-alt-svc.j2 create mode 100644 angie/snip/http-response-headers.j2 create mode 100644 angie/snip/internal-area create mode 100644 angie/snip/log.j2mod create mode 100644 angie/snip/proxy-request-headers.j2 create mode 100644 angie/snip/proxy-response-headers.j2 create mode 100644 angie/snip/resolver.j2inc create mode 100644 angie/snip/scgi-request-headers.j2 create mode 100644 angie/snip/scgi-response-headers.j2 create mode 100644 angie/snip/ssl-intermediate.j2 create mode 100644 angie/snip/ssl-modern.j2 create mode 100644 angie/snip/ssl-old.j2 create mode 100644 angie/snip/ssl-profile.j2inc create mode 100644 angie/snip/uwsgi-request-headers.j2 create mode 100644 angie/snip/uwsgi-response-headers.j2 create mode 100644 angie/static/robots.txt create mode 100644 angie/tls/dh1024.pem create mode 100644 angie/tls/dh2048.pem create mode 100644 angie/tls/ffdhe2048.pem create mode 100644 angie/tls/ffdhe3072.pem create mode 100644 angie/tls/ffdhe4096.pem create mode 100644 apt/prefs.backports create mode 100644 apt/sources.angie create mode 100644 apt/sources.debian create mode 100755 build-scripts/image-base.sh create mode 100755 build-scripts/image-deps.sh create mode 100755 build-scripts/image.sh create mode 100644 doc/README.md create mode 100644 doc/examples/README.md create mode 100644 doc/examples/basic/Dockerfile create mode 100644 doc/examples/basic/README.md create mode 100644 doc/examples/basic/docker-compose.yml create mode 100644 doc/examples/basic/site/http-site.conf create mode 100644 doc/examples/basic/static/index.html create mode 100644 doc/examples/config-template/README.md create mode 100644 doc/examples/config-template/conf/j2cfg/my-caches.yml create mode 100644 doc/examples/config-template/conf/site/http-my-cache.conf.j2 create mode 100644 doc/examples/config-template/data/cache/.gitkeep create mode 100644 doc/examples/config-template/data/lib/.gitkeep create mode 100644 doc/examples/config-template/docker-compose.yml create mode 100644 doc/examples/j2cfg-override/Dockerfile create mode 100644 doc/examples/j2cfg-override/README.md create mode 100644 doc/examples/j2cfg-override/docker-compose.yml create mode 100644 doc/examples/j2cfg-override/j2cfg/override-compress-types.yml create mode 100644 doc/examples/j2cfg-override/site/http-site.conf create mode 100644 doc/examples/j2cfg-override/static/index.html create mode 100644 doc/examples/j2cfg-override/static/index.txt create mode 100644 doc/examples/njs/Dockerfile create mode 100644 doc/examples/njs/README.md create mode 100644 doc/examples/njs/docker-compose.yml create mode 100644 doc/examples/njs/site/http-env-js.conf create mode 100644 doc/examples/njs/site/ngx_env.js create mode 100644 doc/examples/perl/Dockerfile create mode 100644 doc/examples/perl/README.md create mode 100644 doc/examples/perl/site/http-env-perl.conf create mode 100644 doc/examples/perl/site/ngx_env.pm create mode 100644 doc/examples/ssl/Dockerfile create mode 100644 doc/examples/ssl/README.md create mode 100644 doc/examples/ssl/demo-ca/0-CA-Root.crt create mode 100644 doc/examples/ssl/demo-ca/0-CA-Root.key create mode 100644 doc/examples/ssl/demo-ca/1-CA-Internal.crt create mode 100644 doc/examples/ssl/demo-ca/1-CA-Internal.key create mode 100644 doc/examples/ssl/demo-ca/2-example.org.crt create mode 100644 doc/examples/ssl/demo-ca/2-example.org.pem create mode 100644 doc/examples/ssl/demo-ca/3-www.example.org.crt create mode 100644 doc/examples/ssl/demo-ca/3-www.example.org.pem create mode 100644 doc/examples/ssl/demo-ca/Makefile create mode 100644 doc/examples/ssl/docker-compose.yml create mode 100644 doc/examples/ssl/site/http-site.conf create mode 100644 doc/examples/ssl/static/example.org/index.html create mode 100644 doc/examples/ssl/static/www.example.org/index.html create mode 100644 doc/examples/ssl/tls/ca/internal-ca.crt create mode 100644 doc/examples/ssl/tls/ca/root-ca.crt create mode 100644 doc/examples/ssl/tls/example.org.chain.crt create mode 100644 doc/examples/ssl/tls/example.org.pem create mode 100644 doc/examples/ssl/tls/www.example.org.chain.crt create mode 100644 doc/examples/ssl/tls/www.example.org.pem create mode 100644 doc/examples/static-template/Dockerfile create mode 100644 doc/examples/static-template/README.md create mode 100644 doc/examples/static-template/docker-compose.yml create mode 100644 doc/examples/static-template/site/http-site.conf create mode 100644 doc/examples/static-template/static/index.html.j2 create mode 100644 image-entry.d/00-common.envsh create mode 100755 image-entry.d/01-defaults.envsh create mode 100755 image-entry.d/02-nonroot.envsh create mode 100755 image-entry.d/03-local-ip-addresses.envsh create mode 100755 image-entry.d/04-resolver.envsh create mode 100755 image-entry.d/05-ca-certificates.envsh create mode 100755 image-entry.d/10-core.envsh create mode 100755 image-entry.d/11-core-modules.envsh create mode 100755 image-entry.d/12-core-user.envsh create mode 100755 image-entry.d/13-core-worker.envsh create mode 100755 image-entry.d/14-core-loglevel.envsh create mode 100755 image-entry.d/20-http.envsh create mode 100755 image-entry.d/21-http-modules.envsh create mode 100755 image-entry.d/22-http-ssl.envsh create mode 100755 image-entry.d/23-http-max-ranges.envsh create mode 100755 image-entry.d/24-http-forward-headers.envsh create mode 100755 image-entry.d/30-mail.envsh create mode 100755 image-entry.d/31-mail-modules.envsh create mode 100755 image-entry.d/32-mail-ssl.envsh create mode 100755 image-entry.d/40-stream.envsh create mode 100755 image-entry.d/41-stream-modules.envsh create mode 100755 image-entry.d/42-stream-ssl.envsh create mode 100755 image-entry.d/70-merge-dirs.sh create mode 100755 image-entry.d/71-topmost-configs.sh create mode 100755 image-entry.d/72-fullfil-tree.sh create mode 100755 image-entry.d/73-expand-templates.sh create mode 100755 image-entry.d/74-combine-tree.sh create mode 100755 image-entry.d/75-adjust-core-user.sh create mode 100755 image-entry.d/76-openssl-ca-certs.envsh create mode 100755 image-entry.d/90-angie-config-test.sh create mode 100755 image-entry.d/99-cleanup-env.envsh create mode 100755 image-entry.sh create mode 100755 j2cfg/j2cfg-dump.py create mode 100755 j2cfg/j2cfg-multi.py create mode 100755 j2cfg/j2cfg-single.py create mode 100644 j2cfg/j2cfg/__init__.py create mode 100644 j2cfg/j2cfg/functions.py create mode 100644 j2cfg/j2cfg/settings.py create mode 100644 j2cfg/j2cfg/test.j2 create mode 100644 requirements.txt create mode 100755 scripts-extra/certifi-extras.sh create mode 100755 scripts-extra/gpg-batch.sh create mode 100755 scripts-extra/gpg-export.sh create mode 100755 scripts/angie create mode 100755 scripts/angie-builtin-modules.sh create mode 100755 scripts/angie-reload.sh create mode 100755 scripts/apt-clean.sh create mode 100755 scripts/apt-env.sh create mode 100755 scripts/apt-install-angie-mod.sh create mode 100755 scripts/apt-install.sh create mode 100755 scripts/apt-remove.sh create mode 100755 scripts/divert-rm.sh create mode 100755 scripts/dpkg-search.sh create mode 100755 scripts/envsubst-args.sh create mode 100755 scripts/envsubst.sh create mode 100755 scripts/j2cfg-dump create mode 100755 scripts/j2cfg-multi create mode 100755 scripts/j2cfg-single create mode 100755 scripts/openssl-cert-auto-pem.sh create mode 100644 scripts/openssl-generate-dh-bundle.sh create mode 100755 scripts/openssl-ocsp.sh create mode 100755 scripts/pip-env.sh create mode 100755 scripts/python-rm-cache.sh create mode 100755 scripts/static-compress.sh diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5ee6d74 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/.mypy_cache/ +/.vscode/ diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..7c675bb --- /dev/null +++ b/Dockerfile @@ -0,0 +1,198 @@ +ARG IMAGE_VERSION +FROM docker.io/rockdrilla/angie-conv:${IMAGE_VERSION}-deps AS deps + +## --- + +FROM deps AS pycache +SHELL [ "/bin/sh", "-ec" ] + +COPY /scripts/* /usr/local/sbin/ +COPY /scripts-extra/* /usr/local/sbin/ + +COPY /j2cfg/ /usr/local/lib/j2cfg/ + +ENV PYTHONDONTWRITEBYTECODE='' + +## Python cache preseed + +RUN python3 -m compileall -q -j 2 /usr/local/lib/j2cfg/ + +RUN libpython="${PYTHON_SITE_PACKAGES%/*}" ; \ + find "${libpython}/" -mindepth 1 -maxdepth 1 -printf '%P\0' \ + | sed -zEn \ + -e '/^(collections|concurrent|encodings|importlib|json|logging|multiprocessing|re|urllib)$/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$/d' \ + | sort -zV \ + | env -C "${PYTHON_SITE_PACKAGES}" xargs -0r \ + python3 -m compileall -q -j 2 + +## Python cache warmup +RUN j2cfg-single /usr/local/lib/j2cfg/j2cfg/test.j2 /tmp/test ; \ + cat /tmp/test ; echo ; echo ; \ + rm -f /tmp/test + +WORKDIR /pycache +RUN find /usr/local/ -type f -name '*.py[co]' -printf '%P\0' \ + | sort -zV \ + | tar -C /usr/local --null -T - -cf - \ + | tar -xf - + +## Python cache adjustments +RUN d="@$(date '+%s')" ; \ + find /pycache/ -mindepth 1 -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 /j2cfg/ /usr/local/lib/j2cfg/ + +## RFC: Python cache +COPY --from=pycache /pycache/ /usr/local/ + +ENV ANGIE_MODULES_DIR=/usr/lib/angie/modules + +COPY /scripts/* /usr/local/bin/ +## fixup +RUN mv /usr/local/bin/angie /usr/local/sbin/ + +RUN _UID=333 _GID=333 ; \ + echo "angie:x:${_UID}:${_GID}:Angie:/etc/angie:/bin/false" >> /etc/passwd ; \ + echo "angie:x:${_GID}:" >> /etc/group ; \ + echo 'angie:!:::::::' >> /etc/shadow + +COPY /apt/sources.angie /etc/apt/sources.list.d/angie.sources + +RUN apt-install.sh \ + angie \ + angie-console-light \ + ; \ + apt-mark hold angie angie-console-light ; \ + apt-clean.sh ; \ + ## verify Angie layout + [ -d "${ANGIE_MODULES_DIR}" ] ; \ + n='/usr/sbin/angie' ; \ + [ -h "$n" ] ; \ + [ -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" ; \ + else \ + rm -fv "$n-nodebug" ; \ + mv -fv "$n-debug" "$n" ; \ + fi + +## copy directory structure +COPY /angie/ /etc/angie.dist/ +RUN ln -sv "${ANGIE_MODULES_DIR}" /etc/angie.dist/modules + +## preserve snippets from Angie config directory +## ref: https://git.angie.software/web-server/angie/src/tag/Angie-1.9.1/conf +RUN d=/etc/angie ; \ + tar -C "$d" -cf - \ + fastcgi_params \ + fastcgi.conf \ + mime.types \ + prometheus_all.conf \ + scgi_params \ + uwsgi_params \ + | tar -C "$d.dist/snip" -xf - + +## flush default directory +RUN rm -rf /etc/angie ; \ + mkdir /etc/angie + +## /angie/ is persistence store (if any) +## /run/ngx/ is runtime volume +RUN install -d -o angie -g angie -m 03777 /angie /run/ngx +VOLUME [ "/run/ngx" ] + +## adjust paths across filesystem +RUN for d in cache lib log ; do \ + rm -rfv "/var/$d/angie" ; \ + ln -sv "/run/ngx/$d" "/var/$d/angie" ; \ + done + +## special empty directory +RUN d='/var/lib/empty' ; \ + rm -rf "$d" ; \ + if [ -d "$d" ] ; then ls -ld "$d" ; exit 1 ; fi ; \ + install -d -m 0555 "$d" + +## prepare DH params for TLS +## NB: disabled in pipeline for now +## reason: too slow (and too much effort) +# RUN cd /etc/angie.dist/tls || exit 1 ; \ +# openssl-generate-dh-bundle.sh + +## future quirk for angie-module-modsecurity >:) +## a bit better config is here: +## /etc/angie.dist/modsecurity/rules.conf +RUN x='/etc/angie/modsecurity/rules.conf' ; \ + dpkg-divert --divert "$x.dist" --rename "$x" + +## preseed builtin modules list +RUN x='angie-builtin-modules.sh' ; \ + "$x" ; \ + rm -fv "$(which "$x")" + +## install relatively lightweight modules +RUN mkdir -p /etc/angie/mod ; \ + apt-install-angie-mod.sh \ + brotli \ + cache-purge \ + echo \ + geoip2 \ + headers-more \ + njs-light \ + subs \ + upload \ + zip \ + zstd \ + ; \ + apt-clean.sh ; \ + ## move fresh configs to appropriate location + find /etc/angie/mod/ -mindepth 1 -exec mv -nvt /etc/angie.dist/mod {} + ; \ + rm -rfv /etc/angie/mod + +## adjust permissions/ownership +RUN chown -hR 0:0 /etc/angie.dist /etc/angie ; \ + find /etc/angie.dist/ /etc/angie/ -name .gitkeep -type f -delete ; \ + find /etc/angie.dist/ /etc/angie/ -type d -exec chmod 0755 {} + ; \ + find /etc/angie.dist/ /etc/angie/ -type f -exec chmod 0644 {} + + +## 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 +## hovewer, this is discouraged (and this trick may be removed in future) +COPY /image-entry.sh /usr/local/bin/ +COPY /image-entry.d/ /image-entry.dist/ + +## further customization +RUN install -d /image-entry + +## must be bind-mounted only for local customization/overrides! +# RUN install -d /image-entry.local + +## misc defaults +ENV MALLOC_ARENA_MAX=4 + +STOPSIGNAL SIGQUIT + +ENTRYPOINT [ "image-entry.sh" ] +CMD [ "angie" ] diff --git a/Dockerfile.base b/Dockerfile.base new file mode 100644 index 0000000..800bd4d --- /dev/null +++ b/Dockerfile.base @@ -0,0 +1,365 @@ +# FROM docker.io/debian:bookworm-slim as base-upstream +ARG PYTHONTAG=3.12.11-slim-bookworm +FROM docker.io/python:${PYTHONTAG} AS base-upstream + +FROM base-upstream AS base-intermediate +SHELL [ "/bin/sh", "-ec" ] + +COPY /scripts/* /usr/local/sbin/ +COPY /scripts-extra/* /usr/local/sbin/ + +## PATH: remove /sbin and /bin (/usr is merged for Debian 12 and newer) +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 + +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-remove.sh \ + ca-certificates \ + e2fsprogs \ + ; \ + apt-env.sh apt-get upgrade -y ; \ + apt-install.sh \ + apt-utils \ + brotli \ + cron \ + curl \ + gettext-base \ + jdupes \ + jq \ + libcap2-bin \ + libjemalloc2 \ + logrotate \ + netbase \ + netcat-openbsd \ + openssl \ + procps \ + psmisc \ + tzdata \ + zstd \ + ; \ + apt-clean.sh ; \ + ## remove broken symlinks + find /etc/ -xdev -follow -type l -ls -delete + +## perl-base: hardlink->symlink +RUN set +e ; \ + d=/usr/bin ; \ + ls -li "$d/perl" ; \ + find "$d/" -xdev -samefile "$d/perl" 2>/dev/null \ + | grep -Fxv -e "$d/perl" \ + | while read -r p ; do \ + [ -n "$p" ] || continue ; \ + [ -e "$p" ] || continue ; \ + ls -li "$p" ; \ + rm -fv "$p" ; \ + ln -fsv perl "$p" ; \ + ls -li "$p" ; \ + echo ; \ + done + +## 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 \ + crontab \ + 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 \ + ## try dpkg-divert first + for d in /usr/sbin /usr/bin ; do \ + find "$d/" ! -type d -wholename "$d/$i" ; \ + done \ + | while read -r p ; do \ + [ -n "$p" ] || continue ; \ + [ -e "$p" ] || continue ; \ + dpkg-search.sh "$p" || continue ; \ + done \ + | sed -E '/^diversion by/d' \ + | sort -uV \ + | while read -r pkg path ; do \ + [ -n "${pkg}" ] || continue ; \ + [ -e "${path}" ] || continue ; \ + divert-rm.sh "${path}" ; \ + done ; \ + ## remove if still exists + for d in /usr/sbin /usr/bin ; do \ + find "$d/" ! -type d -wholename "$d/$i" ; \ + done \ + | while read -r p ; do \ + [ -n "$p" ] || continue ; \ + [ -e "$p" ] || continue ; \ + rm -fv "$p" ; \ + done ; \ + done ; \ + ## remove broken symlinks + find /bin/ /sbin/ -xdev -follow -type l -ls -delete + +## remove excessive privileges from binaries: setuid/setgid +RUN find / -xdev -type f -perm /7000 \ + | sort -V \ + | while read -r p ; do \ + [ -n "$p" ] || continue ; \ + ## clear setuid/setgid bit + m=$(env stat -c '0%a' "$p") ; \ + m=$(printf '0%o\n' $((m & 00777)) ) ; \ + ## try to lookup in dpkg database + n=$(set +e ; dpkg-search.sh "$p" | sed -E '/^diversion by/d' | cut -d ' ' -f2-) ; \ + if [ "$p" = "$n" ] ; then \ + o=$(env stat -c '%U' "$n") ; \ + g=$(env stat -c '%G' "$n") ; \ + dpkg-statoverride --force --update --add "$o" "$g" "$m" "$n" ; \ + else \ + env printf 'unable to find in dpkg database: %q\n' "$n" ; \ + chmod "$m" "$p" ; \ + fi ; \ + ls -l "$p" ; \ + done + +## remove excessive privileges from binaries: setcap +RUN find / -xdev -type f -executable -exec getcap {} + \ + | sort -V \ + | while read -r path caps ; do \ + [ -n "${path}" ] || continue ; \ + getcap -v "${path}" ; \ + setcap -r "${path}" "${caps}" 2>/dev/null || : ; \ + getcap -v "${path}" ; \ + done + +## "docker.io/python"-specific cleanup +RUN env -C /root rm -f .bash_history .python_history .wget-hsts + +## --- + +FROM base-intermediate AS certs +SHELL [ "/bin/sh", "-ec" ] + +## "2025.04.26" +ENV CERTIFI_COMMIT=275c9eb55733a464589c15fb4566fddd4598e5b2 + +# ARG CERTIFI_BASE_URI='https://raw.githubusercontent.com/certifi/python-certifi' + +ARG CERTIFI_BASE_URI='https://github.com/certifi/python-certifi/raw' +ARG CERTIFI_URI="${CERTIFI_BASE_URI}/${CERTIFI_COMMIT}/certifi/cacert.pem" + +# hadolint ignore=DL3020 +ADD "${CERTIFI_URI}" /tmp/certifi.crt + +RUN apt-install.sh ca-certificates ; \ + apt-clean.sh ; \ + ca_file='/etc/ssl/certs/ca-certificates.crt' ; \ + ls -l "${ca_file}" ; \ + ## process certifi + certifi-extras.sh /tmp/certifi.crt ; \ + openssl-cert-auto-pem.sh "${ca_file}" "${ca_file}.new" "${ca_file}.fp" ; \ + mv -f "${ca_file}.new" "${ca_file}" ; \ + chmod 0444 "${ca_file}" "${ca_file}.fp" ; \ + ls -l "${ca_file}" "${ca_file}.fp" + +## --- + +FROM base-intermediate AS apt-gpg +SHELL [ "/bin/sh", "-ec" ] + +COPY --from=certs /etc/ssl/certs/ca-certificates.* /etc/ssl/certs/ + +ADD https://angie.software/keys/angie-signing.gpg /tmp/angie.gpg.bin + +COPY /apt/sources.angie /etc/apt/angie.sources + +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 mv angie.sources sources.list.d/ ; \ + ## verify sources! + apt-env.sh apt-get update ; \ + apt-remove.sh ${pkg} ; \ + apt-clean.sh + +## --- + +FROM base-intermediate AS base +SHELL [ "/bin/sh", "-ec" ] + +COPY /Dockerfile.base /usr/local/share/ + +COPY --from=certs /etc/ssl/certs/ca-certificates.* /etc/ssl/certs/ +COPY --from=apt-gpg /etc/apt/keyrings/ /etc/apt/keyrings/ + +RUN python-rm-cache.sh /usr/local + +RUN pip-env.sh pip list --format freeze \ + | grep -F '==' | mawk -F= '{print $1}' \ + | xargs -r pip-env.sh pip install -U ; \ + python-rm-cache.sh /usr/local + +RUN libpython="${PYTHON_SITE_PACKAGES%/*}" ; \ + rm -rfv \ + /usr/local/bin/idle* \ + /usr/local/bin/pydoc* \ + "${libpython}/ensurepip/_bundled" \ + "${libpython}/idlelib" \ + "${libpython}/pydoc.py" \ + "${libpython}/pydoc_data" \ + "${libpython}/tkinter" \ + "${libpython}/turtle.py" \ + "${libpython}/turtledemo" \ + ; \ + find "${PYTHON_SITE_PACKAGES}/" -iname '*.exe' -ls -delete + +## adjust pip/certifi +RUN certifi_pem="${PYTHON_SITE_PACKAGES}/pip/_vendor/certifi/cacert.pem" ; \ + [ -d "${certifi_pem%/*}" ] || exit 0 ; \ + rm -f "${certifi_pem}" ; \ + ln -sv /etc/ssl/certs/ca-certificates.crt "${certifi_pem}" + +RUN apt-clean.sh + +RUN find /usr/local/sbin/ ! -type d -ls -delete ; \ + find /run/ -mindepth 1 -ls -delete || : ; \ + install -d -m 01777 /run/lock ; \ + jdupes -1LSpr /usr/ + +ENTRYPOINT [ ] +CMD [ "bash" ] diff --git a/Dockerfile.deps b/Dockerfile.deps new file mode 100644 index 0000000..2d1ffe6 --- /dev/null +++ b/Dockerfile.deps @@ -0,0 +1,196 @@ +ARG IMAGE_VERSION +FROM docker.io/rockdrilla/angie-conv:${IMAGE_VERSION}-base AS base + +## --- + +FROM base AS tools +SHELL [ "/bin/sh", "-ec" ] + +COPY /scripts/* /usr/local/sbin/ +COPY /scripts-extra/* /usr/local/sbin/ + +## current HEAD: "main: don't use secure_getenv", December 14, 2024 +ENV CATATONIT_COMMIT=56579adbb42c0c7ad94fc12d844b38fc5b37b3ce + +# ARG CATATONIT_BASE_URI='https://codeload.github.com/openSUSE/catatonit/tar.gz' +# ARG CATATONIT_URI="${CATATONIT_BASE_URI}/${CATATONIT_COMMIT}" + +ARG CATATONIT_BASE_URI='https://github.com/openSUSE/catatonit/archive' +ARG CATATONIT_URI="${CATATONIT_BASE_URI}/${CATATONIT_COMMIT}.tar.gz" + +## current HEAD: "initial commit ", May 27, 2025 +ENV OVERLAYDIRS_COMMIT=4ba42acfea72bbb378808bbf033396cd6a0e3d22 + +ARG OVERLAYDIRS_BASE_URI='https://git.krd.sh/krd/overlaydirs/archive' +ARG OVERLAYDIRS_URI="${OVERLAYDIRS_BASE_URI}/${OVERLAYDIRS_COMMIT}.tar.gz" + +# hadolint ignore=DL3020 +ADD "${CATATONIT_URI}" /tmp/catatonit.tar.gz +# hadolint ignore=DL3020 +ADD "${OVERLAYDIRS_URI}" /tmp/overlaydirs.tar.gz + +RUN pkg='build-essential debhelper musl-dev autoconf autoconf-archive libxxhash-dev' ; \ + apt-install.sh ${pkg} ; \ + DEB_HOST_GNU_TYPE=$(dpkg-architecture -q DEB_HOST_GNU_TYPE) ; \ + export HOSTCC="${DEB_HOST_GNU_TYPE}-gcc" ; \ + DEB_TARGET_GNU_TYPE=$(dpkg-architecture -q DEB_TARGET_GNU_TYPE) ; \ + DEB_TARGET_MUSL_TYPE=$(printf '%s' "${DEB_TARGET_GNU_TYPE}" | sed -E 's/-gnu$/-musl/') ; \ + CFLAGS_LTO="-flto=2 -fuse-linker-plugin -ffat-lto-objects -flto-partition=none" ; \ + CFLAGS_COMMON="-O2 -g -pipe -fPIE -fstack-protector-strong -fstack-clash-protection -fcf-protection" ; \ + CPPFLAGS="-Wall -Wextra -Werror=format-security -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2" ; \ + ## build catatonit + d=/tmp/catatonit ; \ + rm -rf "$d" ; \ + mkdir -p "$d" ; \ + ( \ + cd "$d" ; \ + tar --strip-components=1 -xf /tmp/catatonit.tar.gz ; \ + commit_abbrev=$(printf '%s' "${CATATONIT_COMMIT}" | cut -c1-8) ; \ + sed -i "s/+dev/+git.${commit_abbrev}/" configure.ac ; \ + export CC="${DEB_TARGET_MUSL_TYPE}-gcc" ; \ + export CFLAGS="${CFLAGS_LTO} ${CFLAGS_COMMON} ${CPPFLAGS}" ; \ + export LDFLAGS="-static-pie -Wl,-z,relro -Wl,-z,now" ; \ + autoreconf -fiv ; \ + ./configure ; \ + make -j1 ; \ + ls -l catatonit ; \ + # "${DEB_TARGET_GNU_TYPE}-strip" --strip-debug --strip-unneeded catatonit ; \ + strip --strip-debug --strip-unneeded catatonit ; \ + ls -l catatonit ; \ + cp catatonit /usr/local/bin/ ; \ + ) ; \ + rm -rf "$d" ; \ + ## build overlaydirs + d=/tmp/overlaydirs ; \ + rm -rf "$d" ; \ + mkdir -p "$d" ; \ + ( \ + cd "$d" ; \ + tar --strip-components=1 -xf /tmp/overlaydirs.tar.gz ; \ + export CROSS="${DEB_TARGET_GNU_TYPE}-" ; \ + export CFLAGS_COMMON CPPFLAGS ; \ + make -j1 clean build RELMODE=1 ; \ + ls -l overlaydirs ; \ + cp overlaydirs /usr/local/bin/ ; \ + ) ; \ + rm -rf "$d" ; \ + ## cleanup + apt-remove.sh ${pkg} ; \ + apt-clean.sh + +## --- + +## not actually used; only for reference + +FROM base AS python-ext-no-binary +SHELL [ "/bin/sh", "-ec" ] + +COPY /scripts/* /usr/local/sbin/ +COPY /scripts-extra/* /usr/local/sbin/ + +COPY /requirements.txt /tmp/ + +ENV DEV_PACKAGES='libyaml-dev' +# markupsafe, psutil +ENV CIBUILDWHEEL=1 +# pyyaml +ENV PYYAML_FORCE_CYTHON=1 + +RUN w=$(mktemp -d) ; : "${w:?}" ; \ + { 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~=3.0.12' ; \ + pip-env.sh pip install --no-binary :all: -r /tmp/requirements.txt ; \ + 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 --strip-unneeded ; \ + echo ; \ + find "${PYTHON_SITE_PACKAGES}/" -type f -name '*.so*' -exec ls -l {} + ; \ + apt-remove.sh build-essential ; \ + apt-clean.sh + +## avoid changing already present packages +RUN rm -rfv \ + /usr/local/bin/pip \ + /usr/local/bin/pip3* \ + ; \ + find "${PYTHON_SITE_PACKAGES}/" -mindepth 1 -maxdepth 1 -printf '%P\0' \ + | sed -zEn \ + -e '/^pip(|-.+\.dist-info)$/p' \ + | env -C "${PYTHON_SITE_PACKAGES}" xargs -0r rm -rf + +## --- + +FROM base AS python-ext +SHELL [ "/bin/sh", "-ec" ] + +COPY /scripts/* /usr/local/sbin/ +COPY /scripts-extra/* /usr/local/sbin/ + +COPY /requirements.txt /tmp/ + +RUN apt-install.sh binutils ; \ + pip-env.sh pip install -r /tmp/requirements.txt ; \ + 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 --strip-unneeded ; \ + echo ; \ + find "${PYTHON_SITE_PACKAGES}/" -type f -name '*.so*' -exec ls -l {} + ; \ + apt-remove.sh binutils ; \ + apt-clean.sh + +## avoid changing already present packages +RUN rm -rfv \ + /usr/local/bin/pip \ + /usr/local/bin/pip3* \ + ; \ + find "${PYTHON_SITE_PACKAGES}/" -mindepth 1 -maxdepth 1 -printf '%P\0' \ + | sed -zEn \ + -e '/^pip(|-.+\.dist-info)$/p' \ + | env -C "${PYTHON_SITE_PACKAGES}" xargs -0r rm -rf + +## --- + +FROM base AS deps +SHELL [ "/bin/sh", "-ec" ] + +COPY /Dockerfile.deps /usr/local/share/ + +COPY --from=tools /usr/local/bin/catatonit /usr/local/bin/ +COPY --from=tools /usr/local/bin/overlaydirs /usr/local/bin/ + +## Python: site-packages +COPY --from=python-ext /usr/local/bin/ /usr/local/bin/ +COPY --from=python-ext /${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 + +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/TODO b/TODO new file mode 100644 index 0000000..dbf4e3d --- /dev/null +++ b/TODO @@ -0,0 +1,6 @@ +- documentation +- examples +- convenient response headers: + - CORS / CORP / COOP / COEP + - Content-Security-Policy + - Permissions-Policy diff --git a/angie/angie.conf b/angie/angie.conf new file mode 100644 index 0000000..ec63cf6 --- /dev/null +++ b/angie/angie.conf @@ -0,0 +1,15 @@ +daemon off; +pid /run/ngx/angie.pid; + +include mod-core.conf; +# mod-http.conf +# mod-mail.conf +# mod-stream.conf +include /run/ngx/conf.ctx/mod-*.conf; + +include ctx-core_ev.conf; +include ctx-core.conf; +# ctx-http.conf +# ctx-mail.conf +# ctx-stream.conf +include /run/ngx/conf.ctx/ctx-*.conf; \ No newline at end of file diff --git a/angie/autoconf/core-error-log.conf.j2 b/angie/autoconf/core-error-log.conf.j2 new file mode 100644 index 0000000..cd06ec0 --- /dev/null +++ b/angie/autoconf/core-error-log.conf.j2 @@ -0,0 +1,4 @@ +{%- import 'snip/log.j2mod' as ngx_log -%} + +{# {{ ngx_log.error_log(dest='error.log', level=env.NGX_LOGLEVEL) }} #} +{{ ngx_log.error_log(level=env.NGX_LOGLEVEL) }} diff --git a/angie/autoconf/core-lock-file.conf b/angie/autoconf/core-lock-file.conf new file mode 100644 index 0000000..368a2c2 --- /dev/null +++ b/angie/autoconf/core-lock-file.conf @@ -0,0 +1,2 @@ +## not a real file but prefix for filenames +lock_file /run/ngx/lock/a; \ No newline at end of file diff --git a/angie/autoconf/core-pcre-jit.conf b/angie/autoconf/core-pcre-jit.conf new file mode 100644 index 0000000..a78c335 --- /dev/null +++ b/angie/autoconf/core-pcre-jit.conf @@ -0,0 +1 @@ +pcre_jit on; \ No newline at end of file diff --git a/angie/autoconf/core-user.conf.in b/angie/autoconf/core-user.conf.in new file mode 100644 index 0000000..1a7c83b --- /dev/null +++ b/angie/autoconf/core-user.conf.in @@ -0,0 +1,3 @@ +## if container is running in non-privileged mode, +## then this file is going to be removed by /image-entry/75-adjust-core-user.sh +user ${NGX_USER} ${NGX_GROUP}; \ No newline at end of file diff --git a/angie/autoconf/core-worker-env.conf.j2 b/angie/autoconf/core-worker-env.conf.j2 new file mode 100644 index 0000000..cd02dc4 --- /dev/null +++ b/angie/autoconf/core-worker-env.conf.j2 @@ -0,0 +1,31 @@ +{# TODO: investigate error with "include" #} +{# {%- include 'core-worker-env.j2inc' -%} #} +{%- set w_env = ( j2cfg.core_worker_env or [] ) | any_to_env_dict -%} +{#- NB: "TZ" is always provided by Angie itself -#} +{%- set w_vars_passthrough = w_env | dict_empty_keys | list_diff(env_vars_preserve + ['TZ']) -%} +{%- set vars_passthrough = (env_vars_passthrough + w_vars_passthrough) | uniq | list_intersect(env | dict_keys) -%} + +## NB: "TZ" is always provided by Angie itself + +## preserve +{%- for k in env_vars_preserve %} +env {{ k | ngx_esc }}; +{%- endfor %} + +## passthrough +{%- for k in vars_passthrough %} +env {{ k | ngx_esc }}; +{%- endfor %} + +{%- set w_vars_override = w_env | dict_non_empty_keys -%} +{% if w_vars_override %} +## WARNING! +## explicit environment variables are NOT implemented +## reason: envs are supported only for http_perl but not for http_js/stream_js +## solution: provide environment variables explicitly +## and then list them in "core_worker_env" key in config +## + {%- for k in w_vars_override %} +## env {{ "{}={}".format(k, w_env[k]) | ngx_esc }} + {%- endfor %} +{%- endif %} diff --git a/angie/autoconf/core-worker-env.j2inc b/angie/autoconf/core-worker-env.j2inc new file mode 100644 index 0000000..0465326 --- /dev/null +++ b/angie/autoconf/core-worker-env.j2inc @@ -0,0 +1,4 @@ +{%- set w_env = ( j2cfg.core_worker_env or [] ) | any_to_env_dict -%} +{#- NB: "TZ" is always provided by Angie itself -#} +{%- set w_vars_passthrough = w_env | dict_empty_keys | list_diff(env_vars_preserve + ['TZ']) -%} +{%- set vars_passthrough = (env_vars_passthrough + w_vars_passthrough) | uniq | list_intersect(env | dict_keys) -%} diff --git a/angie/autoconf/core-worker-env.txt.j2 b/angie/autoconf/core-worker-env.txt.j2 new file mode 100644 index 0000000..34f0832 --- /dev/null +++ b/angie/autoconf/core-worker-env.txt.j2 @@ -0,0 +1,12 @@ +{# TODO: investigate error with "include" #} +{# {%- include 'core-worker-env.j2inc' -%} #} +{%- set w_env = ( j2cfg.core_worker_env or [] ) | any_to_env_dict -%} +{#- NB: "TZ" is always provided by Angie itself -#} +{%- set w_vars_passthrough = w_env | dict_empty_keys | list_diff(env_vars_preserve + ['TZ']) -%} +{%- set vars_passthrough = (env_vars_passthrough + w_vars_passthrough) | uniq | list_intersect(env | dict_keys) -%} + +{#- NB: "TZ" is always provided by Angie itself -#} +{%- set all_vars = (env_vars_preserve + vars_passthrough + ['TZ']) | uniq -%} +{%- for k in all_vars %} +{{ k | ngx_esc }} +{%- endfor %} diff --git a/angie/autoconf/core-worker.conf.j2 b/angie/autoconf/core-worker.conf.j2 new file mode 100644 index 0000000..76554c0 --- /dev/null +++ b/angie/autoconf/core-worker.conf.j2 @@ -0,0 +1,10 @@ +worker_processes {{ env.NGX_WORKER_PROCESSES }}; +{%- if env.NGX_WORKER_CPU_AFFINITY %} +worker_cpu_affinity {{ env.NGX_WORKER_CPU_AFFINITY }}; +{%- endif %} +{%- if env.NGX_WORKER_PRIORITY %} +worker_priority {{ env.NGX_WORKER_PRIORITY }}; +{%- endif %} +{%- if env.NGX_WORKER_RLIMIT_NOFILE %} +worker_rlimit_nofile {{ env.NGX_WORKER_RLIMIT_NOFILE }}; +{%- endif %} \ No newline at end of file diff --git a/angie/autoconf/core_ev-worker.conf.j2 b/angie/autoconf/core_ev-worker.conf.j2 new file mode 100644 index 0000000..4a61641 --- /dev/null +++ b/angie/autoconf/core_ev-worker.conf.j2 @@ -0,0 +1,7 @@ +worker_connections {{ env.NGX_WORKER_CONNECTIONS }}; +{%- if env.NGX_WORKER_AIO_REQUESTS %} +worker_aio_requests {{ env.NGX_WORKER_AIO_REQUESTS }}; +{%- endif %} +{%- if env.NGX_WORKER_PRIORITY %} +worker_priority {{ env.NGX_WORKER_PRIORITY }}; +{%- endif %} \ No newline at end of file diff --git a/angie/autoconf/http-access-log.conf b/angie/autoconf/http-access-log.conf new file mode 100644 index 0000000..c27db01 --- /dev/null +++ b/angie/autoconf/http-access-log.conf @@ -0,0 +1,2 @@ +include autoconf/http-access-log/format/*.conf; +include autoconf/http-access-log/*.conf; \ No newline at end of file diff --git a/angie/autoconf/http-access-log/default.conf.j2 b/angie/autoconf/http-access-log/default.conf.j2 new file mode 100644 index 0000000..9bc89db --- /dev/null +++ b/angie/autoconf/http-access-log/default.conf.j2 @@ -0,0 +1,4 @@ +{%- import 'snip/log.j2mod' as ngx_log -%} + +{# {{ ngx_log.access_log(dest='access.log', format='main') }} #} +{{ ngx_log.access_log(format='main') }} diff --git a/angie/autoconf/http-access-log/format/extended.conf b/angie/autoconf/http-access-log/format/extended.conf new file mode 100644 index 0000000..e72d714 --- /dev/null +++ b/angie/autoconf/http-access-log/format/extended.conf @@ -0,0 +1,7 @@ +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"'; \ No newline at end of file diff --git a/angie/autoconf/http-access-log/format/main.conf b/angie/autoconf/http-access-log/format/main.conf new file mode 100644 index 0000000..618bc5e --- /dev/null +++ b/angie/autoconf/http-access-log/format/main.conf @@ -0,0 +1,4 @@ +log_format main + '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; \ No newline at end of file diff --git a/angie/autoconf/http-alt-svc.conf b/angie/autoconf/http-alt-svc.conf new file mode 100644 index 0000000..df069f3 --- /dev/null +++ b/angie/autoconf/http-alt-svc.conf @@ -0,0 +1 @@ +include snip/http-alt-svc; \ No newline at end of file diff --git a/angie/autoconf/http-buffers.conf b/angie/autoconf/http-buffers.conf new file mode 100644 index 0000000..2f8939d --- /dev/null +++ b/angie/autoconf/http-buffers.conf @@ -0,0 +1,4 @@ +subrequest_output_buffer_size 16k; +client_body_buffer_size 16k; +client_header_buffer_size 4k; +large_client_header_buffers 8 16k; diff --git a/angie/autoconf/http-client-body-temp.conf b/angie/autoconf/http-client-body-temp.conf new file mode 100644 index 0000000..d3d834c --- /dev/null +++ b/angie/autoconf/http-client-body-temp.conf @@ -0,0 +1 @@ +client_body_temp_path /run/ngx/cache/temp_client_body 2 2; \ No newline at end of file diff --git a/angie/autoconf/http-max-ranges.conf.j2 b/angie/autoconf/http-max-ranges.conf.j2 new file mode 100644 index 0000000..64b25de --- /dev/null +++ b/angie/autoconf/http-max-ranges.conf.j2 @@ -0,0 +1,3 @@ +{%- if env.NGX_HTTP_MAX_RANGES %} +max_ranges {{ env.NGX_HTTP_MAX_RANGES }}; +{%- endif %} \ No newline at end of file diff --git a/angie/autoconf/http-mime-types.conf b/angie/autoconf/http-mime-types.conf new file mode 100644 index 0000000..209d28d --- /dev/null +++ b/angie/autoconf/http-mime-types.conf @@ -0,0 +1,8 @@ +include snip/mime.types; + +types { + font/ttf ttf; + application/font-sfnt otf; +} + +default_type application/octet-stream; diff --git a/angie/autoconf/http-request-headers-basic.conf.j2 b/angie/autoconf/http-request-headers-basic.conf.j2 new file mode 100644 index 0000000..2c20f8d --- /dev/null +++ b/angie/autoconf/http-request-headers-basic.conf.j2 @@ -0,0 +1,26 @@ +map $http_upgrade + $req_connection +{ + default upgrade; + "" ""; +} + +map $http_user_agent + $req_user_agent +{ + default $http_user_agent; +{%- if env.NGX_HTTP_FAKE_UA %} + ## merely fake + "" {{ env.NGX_HTTP_FAKE_UA | ngx_esc }}; +{%- else %} + "" "Angie/$angie_version"; +{%- endif %} +} + +map $http_accept + $req_accept +{ + volatile; + default $http_accept; + "" "*/*"; +} \ No newline at end of file diff --git a/angie/autoconf/http-request-headers-forwarded.conf b/angie/autoconf/http-request-headers-forwarded.conf new file mode 100644 index 0000000..afb55c6 --- /dev/null +++ b/angie/autoconf/http-request-headers-forwarded.conf @@ -0,0 +1,27 @@ +## ref: +## - https://www.digitalocean.com/community/tools/nginx?domains.0.reverseProxy.reverseProxy=true +map $remote_addr + $proxy_forwarded_elem +{ + ## IPv4 addresses can be sent as-is + ~^[0-9.]+$ "for=$remote_addr"; + ## IPv6 addresses need to be bracketed and quoted + ~^[0-9A-Fa-f:.]+$ "for=\"[$remote_addr]\""; + ## Unix domain socket names cannot be represented in RFC 7239 syntax + default "for=unknown"; +} + +## ref: +## - https://www.digitalocean.com/community/tools/nginx?domains.0.reverseProxy.reverseProxy=true +## - https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Forwarded +map $http_forwarded + $proxy_add_forwarded +{ + volatile; + + ## if the incoming Forwarded header is syntactically valid, append to it + "~^(,[ \\t]*)*([!#$%&'*+.^_`|~0-9A-Za-z-]+=([!#$%&'*+.^_`|~0-9A-Za-z-]+|\"([\\t \\x21\\x23-\\x5B\\x5D-\\x7E\\x80-\\xFF]|\\\\[\\t \\x21-\\x7E\\x80-\\xFF])*\"))?(;([!#$%&'*+.^_`|~0-9A-Za-z-]+=([!#$%&'*+.^_`|~0-9A-Za-z-]+|\"([\\t \\x21\\x23-\\x5B\\x5D-\\x7E\\x80-\\xFF]|\\\\[\\t \\x21-\\x7E\\x80-\\xFF])*\"))?)*([ \\t]*,([ \\t]*([!#$%&'*+.^_`|~0-9A-Za-z-]+=([!#$%&'*+.^_`|~0-9A-Za-z-]+|\"([\\t \\x21\\x23-\\x5B\\x5D-\\x7E\\x80-\\xFF]|\\\\[\\t \\x21-\\x7E\\x80-\\xFF])*\"))?(;([!#$%&'*+.^_`|~0-9A-Za-z-]+=([!#$%&'*+.^_`|~0-9A-Za-z-]+|\"([\\t \\x21\\x23-\\x5B\\x5D-\\x7E\\x80-\\xFF]|\\\\[\\t \\x21-\\x7E\\x80-\\xFF])*\"))?)*)?)*$" "$http_forwarded, $proxy_forwarded_elem"; + + ## otherwise, replace it + default "$proxy_forwarded_elem"; +} \ No newline at end of file diff --git a/angie/autoconf/http-resolver.conf.j2 b/angie/autoconf/http-resolver.conf.j2 new file mode 100644 index 0000000..0ae14ed --- /dev/null +++ b/angie/autoconf/http-resolver.conf.j2 @@ -0,0 +1,2 @@ +{%- set resolver_status_zone = 'http_resolver' -%} +{% include 'snip/resolver.j2inc' %} diff --git a/angie/autoconf/http-response-headers.conf b/angie/autoconf/http-response-headers.conf new file mode 100644 index 0000000..85ade15 --- /dev/null +++ b/angie/autoconf/http-response-headers.conf @@ -0,0 +1 @@ +include snip/http-response-headers; \ No newline at end of file diff --git a/angie/autoconf/http-v2.conf.j2 b/angie/autoconf/http-v2.conf.j2 new file mode 100644 index 0000000..15e12a5 --- /dev/null +++ b/angie/autoconf/http-v2.conf.j2 @@ -0,0 +1,3 @@ +{%- if env.NGX_HTTP_V2 == '0' %} +http2 off; +{%- endif %} diff --git a/angie/autoconf/http-v3.conf.j2 b/angie/autoconf/http-v3.conf.j2 new file mode 100644 index 0000000..8173986 --- /dev/null +++ b/angie/autoconf/http-v3.conf.j2 @@ -0,0 +1,3 @@ +{%- if env.NGX_HTTP_V3 == '0' %} +http3 off; +{%- endif %} diff --git a/angie/autoconf/http-webroot.conf b/angie/autoconf/http-webroot.conf new file mode 100644 index 0000000..1e6939e --- /dev/null +++ b/angie/autoconf/http-webroot.conf @@ -0,0 +1 @@ +root static; \ No newline at end of file diff --git a/angie/autoconf/mail-resolver.conf.j2 b/angie/autoconf/mail-resolver.conf.j2 new file mode 100644 index 0000000..a3abee1 --- /dev/null +++ b/angie/autoconf/mail-resolver.conf.j2 @@ -0,0 +1,2 @@ +{%- set resolver_status_zone = 'mail_resolver' -%} +{% include 'snip/resolver.j2inc' %} diff --git a/angie/autoconf/stream-resolver.conf.j2 b/angie/autoconf/stream-resolver.conf.j2 new file mode 100644 index 0000000..b958ad8 --- /dev/null +++ b/angie/autoconf/stream-resolver.conf.j2 @@ -0,0 +1,2 @@ +{%- set resolver_status_zone = 'stream_resolver' -%} +{% include 'snip/resolver.j2inc' %} diff --git a/angie/conf/acme/path.conf b/angie/conf/acme/path.conf new file mode 100644 index 0000000..8c647d0 --- /dev/null +++ b/angie/conf/acme/path.conf @@ -0,0 +1 @@ +acme_client_path /run/ngx/lib/acme; \ No newline at end of file diff --git a/angie/conf/brotli/buffers.conf b/angie/conf/brotli/buffers.conf new file mode 100644 index 0000000..bcd9970 --- /dev/null +++ b/angie/conf/brotli/buffers.conf @@ -0,0 +1,5 @@ +brotli_comp_level 5; # default: 6 +brotli_window 128k; # default: 512k + +brotli_min_length 1024; +brotli_buffers 32 16k; \ No newline at end of file diff --git a/angie/conf/brotli/types.conf.j2 b/angie/conf/brotli/types.conf.j2 new file mode 100644 index 0000000..d719410 --- /dev/null +++ b/angie/conf/brotli/types.conf.j2 @@ -0,0 +1,9 @@ +{%- set mime_types = j2cfg.compress_types or [] -%} +{%- set mime_types = mime_types | any_to_str_list | uniq_str_list -%} +{%- if mime_types -%} +brotli_types +{%- for t in mime_types %} + {{ t }} +{%- endfor %} +; +{%- endif -%} \ No newline at end of file diff --git a/angie/conf/core-quic-bpf.conf b/angie/conf/core-quic-bpf.conf new file mode 100644 index 0000000..57e3c82 --- /dev/null +++ b/angie/conf/core-quic-bpf.conf @@ -0,0 +1 @@ +quic_bpf on; \ No newline at end of file diff --git a/angie/conf/core_ev-accept-mutex-delay.conf b/angie/conf/core_ev-accept-mutex-delay.conf new file mode 100644 index 0000000..8cf9b15 --- /dev/null +++ b/angie/conf/core_ev-accept-mutex-delay.conf @@ -0,0 +1 @@ +accept_mutex_delay 200ms; \ No newline at end of file diff --git a/angie/conf/core_ev-accept-mutex.conf b/angie/conf/core_ev-accept-mutex.conf new file mode 100644 index 0000000..9b9e7eb --- /dev/null +++ b/angie/conf/core_ev-accept-mutex.conf @@ -0,0 +1 @@ +accept_mutex on; \ No newline at end of file diff --git a/angie/conf/core_ev-multi-accept.conf b/angie/conf/core_ev-multi-accept.conf new file mode 100644 index 0000000..19df249 --- /dev/null +++ b/angie/conf/core_ev-multi-accept.conf @@ -0,0 +1 @@ +multi_accept on; \ No newline at end of file diff --git a/angie/conf/fastcgi/buffers.conf b/angie/conf/fastcgi/buffers.conf new file mode 100644 index 0000000..6198dff --- /dev/null +++ b/angie/conf/fastcgi/buffers.conf @@ -0,0 +1,4 @@ +fastcgi_buffers 16 16k; +fastcgi_buffer_size 16k; +fastcgi_busy_buffers_size 32k; +fastcgi_temp_file_write_size 32k; \ No newline at end of file diff --git a/angie/conf/fastcgi/cache-bypass.conf.j2 b/angie/conf/fastcgi/cache-bypass.conf.j2 new file mode 100644 index 0000000..dbf6b85 --- /dev/null +++ b/angie/conf/fastcgi/cache-bypass.conf.j2 @@ -0,0 +1,14 @@ +{%- set cache_bypass = j2cfg.cache_bypass or [] -%} +{%- if cache_bypass -%} +## disable (response) cache under following conditions +fastcgi_cache_bypass +{%- for v in cache_bypass %} + {{ v | ngx_esc }} +{%- endfor %} +; +fastcgi_no_cache +{%- for v in cache_bypass %} + {{ v | ngx_esc }} +{%- endfor %} +; +{%- endif -%} \ No newline at end of file diff --git a/angie/conf/fastcgi/headers.conf b/angie/conf/fastcgi/headers.conf new file mode 100644 index 0000000..ba891f2 --- /dev/null +++ b/angie/conf/fastcgi/headers.conf @@ -0,0 +1,2 @@ +include snip/fastcgi-request-headers; +include snip/fastcgi-response-headers; \ No newline at end of file diff --git a/angie/conf/fastcgi/param.conf b/angie/conf/fastcgi/param.conf new file mode 100644 index 0000000..21d0ce8 --- /dev/null +++ b/angie/conf/fastcgi/param.conf @@ -0,0 +1,7 @@ +include snip/fastcgi.conf; + +fastcgi_param PATH_INFO $path_info; + +fastcgi_param AUTH_USER $remote_user; +fastcgi_param REMOTE_USER $remote_user; +fastcgi_param HTTP_HOST $host; \ No newline at end of file diff --git a/angie/conf/fastcgi/temp.conf b/angie/conf/fastcgi/temp.conf new file mode 100644 index 0000000..ec77a3c --- /dev/null +++ b/angie/conf/fastcgi/temp.conf @@ -0,0 +1 @@ +fastcgi_temp_path /run/ngx/cache/temp_fastcgi 2 2; \ No newline at end of file diff --git a/angie/conf/grpc/buffers.conf b/angie/conf/grpc/buffers.conf new file mode 100644 index 0000000..03efeee --- /dev/null +++ b/angie/conf/grpc/buffers.conf @@ -0,0 +1 @@ +grpc_buffer_size 16k; \ No newline at end of file diff --git a/angie/conf/grpc/headers.conf b/angie/conf/grpc/headers.conf new file mode 100644 index 0000000..fd9595c --- /dev/null +++ b/angie/conf/grpc/headers.conf @@ -0,0 +1,2 @@ +include snip/grpc-request-headers; +include snip/grpc-response-headers; \ No newline at end of file diff --git a/angie/conf/grpc/ssl.conf.j2 b/angie/conf/grpc/ssl.conf.j2 new file mode 100644 index 0000000..79b5655 --- /dev/null +++ b/angie/conf/grpc/ssl.conf.j2 @@ -0,0 +1,7 @@ +{%- for k, v in j2cfg.tls.conf_cmd|dictsort %} +grpc_ssl_conf_command {{ k }} {{ v | ngx_esc }}; +{%- endfor %} + +grpc_ssl_trusted_certificate {{ env.NGX_SSL_CERT_FILE }}; +grpc_ssl_verify on; +grpc_ssl_server_name on; diff --git a/angie/conf/gzip/buffers.conf b/angie/conf/gzip/buffers.conf new file mode 100644 index 0000000..29544c5 --- /dev/null +++ b/angie/conf/gzip/buffers.conf @@ -0,0 +1,4 @@ +gzip_comp_level 2; # default: 1 + +gzip_min_length 1024; +gzip_buffers 32 16k; \ No newline at end of file diff --git a/angie/conf/gzip/proxied.conf b/angie/conf/gzip/proxied.conf new file mode 100644 index 0000000..989c9d7 --- /dev/null +++ b/angie/conf/gzip/proxied.conf @@ -0,0 +1 @@ +gzip_proxied any; \ No newline at end of file diff --git a/angie/conf/gzip/types.conf.j2 b/angie/conf/gzip/types.conf.j2 new file mode 100644 index 0000000..6dc5194 --- /dev/null +++ b/angie/conf/gzip/types.conf.j2 @@ -0,0 +1,9 @@ +{%- set mime_types = j2cfg.compress_types or [] -%} +{%- set mime_types = mime_types | any_to_str_list | uniq_str_list -%} +{%- if mime_types -%} +gzip_types +{%- for t in mime_types %} + {{ t }} +{%- endfor %} +; +{%- endif -%} \ No newline at end of file diff --git a/angie/conf/gzip/vary.conf b/angie/conf/gzip/vary.conf new file mode 100644 index 0000000..d157ade --- /dev/null +++ b/angie/conf/gzip/vary.conf @@ -0,0 +1 @@ +gzip_vary on; \ No newline at end of file diff --git a/angie/conf/http-acme.conf b/angie/conf/http-acme.conf new file mode 100644 index 0000000..c1ad96f --- /dev/null +++ b/angie/conf/http-acme.conf @@ -0,0 +1 @@ +include conf/acme/*.conf; \ No newline at end of file diff --git a/angie/conf/http-brotli-static.conf b/angie/conf/http-brotli-static.conf new file mode 100644 index 0000000..11a1aaf --- /dev/null +++ b/angie/conf/http-brotli-static.conf @@ -0,0 +1 @@ +brotli_static on; \ No newline at end of file diff --git a/angie/conf/http-brotli.conf b/angie/conf/http-brotli.conf new file mode 100644 index 0000000..7b14c95 --- /dev/null +++ b/angie/conf/http-brotli.conf @@ -0,0 +1,2 @@ +include conf/brotli/*.conf; +brotli on; \ No newline at end of file diff --git a/angie/conf/http-fastcgi.conf b/angie/conf/http-fastcgi.conf new file mode 100644 index 0000000..503b836 --- /dev/null +++ b/angie/conf/http-fastcgi.conf @@ -0,0 +1 @@ +include conf/fastcgi/*.conf; \ No newline at end of file diff --git a/angie/conf/http-grpc.conf b/angie/conf/http-grpc.conf new file mode 100644 index 0000000..2fc50e0 --- /dev/null +++ b/angie/conf/http-grpc.conf @@ -0,0 +1 @@ +include conf/grpc/*.conf; \ No newline at end of file diff --git a/angie/conf/http-gunzip.conf b/angie/conf/http-gunzip.conf new file mode 100644 index 0000000..ca799e2 --- /dev/null +++ b/angie/conf/http-gunzip.conf @@ -0,0 +1,2 @@ +gunzip_buffers 16 16k; +gunzip on; \ No newline at end of file diff --git a/angie/conf/http-gzip-static.conf b/angie/conf/http-gzip-static.conf new file mode 100644 index 0000000..aed8556 --- /dev/null +++ b/angie/conf/http-gzip-static.conf @@ -0,0 +1 @@ +gzip_static on; \ No newline at end of file diff --git a/angie/conf/http-gzip.conf b/angie/conf/http-gzip.conf new file mode 100644 index 0000000..78a0171 --- /dev/null +++ b/angie/conf/http-gzip.conf @@ -0,0 +1,2 @@ +include conf/gzip/*.conf; +gzip on; \ No newline at end of file diff --git a/angie/conf/http-modsecurity.conf b/angie/conf/http-modsecurity.conf new file mode 100644 index 0000000..977f54f --- /dev/null +++ b/angie/conf/http-modsecurity.conf @@ -0,0 +1,4 @@ +modsecurity_rules_file modsecurity/rules.conf; + +## NOT enabling ModSecurity by default! +# modsecurity on; \ No newline at end of file diff --git a/angie/conf/http-njs.conf b/angie/conf/http-njs.conf new file mode 100644 index 0000000..263aa8f --- /dev/null +++ b/angie/conf/http-njs.conf @@ -0,0 +1 @@ +include conf/njs/*.conf; \ No newline at end of file diff --git a/angie/conf/http-perl.conf b/angie/conf/http-perl.conf new file mode 100644 index 0000000..ea5c2c8 --- /dev/null +++ b/angie/conf/http-perl.conf @@ -0,0 +1 @@ +include conf/perl/*.conf; \ No newline at end of file diff --git a/angie/conf/http-proxy.conf b/angie/conf/http-proxy.conf new file mode 100644 index 0000000..4774c6b --- /dev/null +++ b/angie/conf/http-proxy.conf @@ -0,0 +1,2 @@ +include conf/proxy/*.conf; +include conf/proxy-http/*.conf; \ No newline at end of file diff --git a/angie/conf/http-quic-gso.conf.j2 b/angie/conf/http-quic-gso.conf.j2 new file mode 100644 index 0000000..5542f43 --- /dev/null +++ b/angie/conf/http-quic-gso.conf.j2 @@ -0,0 +1,5 @@ +quic_gso on; + +{%- if env.NGX_HTTP_PROXY == '1' %} +proxy_quic_gso on; +{%- endif %} \ No newline at end of file diff --git a/angie/conf/http-scgi.conf b/angie/conf/http-scgi.conf new file mode 100644 index 0000000..fe949c1 --- /dev/null +++ b/angie/conf/http-scgi.conf @@ -0,0 +1 @@ +include conf/scgi/*.conf; \ No newline at end of file diff --git a/angie/conf/http-ssl.conf.j2 b/angie/conf/http-ssl.conf.j2 new file mode 100644 index 0000000..9c24ee4 --- /dev/null +++ b/angie/conf/http-ssl.conf.j2 @@ -0,0 +1,25 @@ +include conf/ssl/*.conf; + +## lowering from 16k to 4k to improve time-to-first-byte +ssl_buffer_size 4k; + +{%- if env.NGX_HTTP_SSL_PROFILE %} +include snip/ssl-{{ env.NGX_HTTP_SSL_PROFILE }}; +{%- endif %} + +{%- if j2cfg.tls.stapling.enable %} +ssl_stapling on; + {%- if j2cfg.tls.stapling.verify %} +ssl_stapling_verify on; + {%- else %} +ssl_stapling_verify off; + {%- endif %} + {%- if j2cfg.tls.stapling.file %} +ssl_stapling_file {{ j2cfg.tls.stapling.file | ngx_esc }}; + {%- endif %} + {%- if j2cfg.tls.stapling.responder %} +ssl_stapling_responder {{ j2cfg.tls.stapling.responder | ngx_esc }}; + {%- endif %} +{%- else %} +ssl_stapling off; +{%- endif %} diff --git a/angie/conf/http-uwsgi.conf b/angie/conf/http-uwsgi.conf new file mode 100644 index 0000000..f23d742 --- /dev/null +++ b/angie/conf/http-uwsgi.conf @@ -0,0 +1 @@ +include conf/uwsgi/*.conf; \ No newline at end of file diff --git a/angie/conf/http-v2.conf b/angie/conf/http-v2.conf new file mode 100644 index 0000000..464f84b --- /dev/null +++ b/angie/conf/http-v2.conf @@ -0,0 +1,2 @@ +include conf/http2/*.conf; +http2 on; \ No newline at end of file diff --git a/angie/conf/http-v3.conf b/angie/conf/http-v3.conf new file mode 100644 index 0000000..9b55cae --- /dev/null +++ b/angie/conf/http-v3.conf @@ -0,0 +1,2 @@ +include conf/http3/*.conf; +http3 on; \ No newline at end of file diff --git a/angie/conf/http-zstd-static.conf b/angie/conf/http-zstd-static.conf new file mode 100644 index 0000000..1ef178e --- /dev/null +++ b/angie/conf/http-zstd-static.conf @@ -0,0 +1 @@ +zstd_static on; \ No newline at end of file diff --git a/angie/conf/http-zstd.conf b/angie/conf/http-zstd.conf new file mode 100644 index 0000000..e1740d6 --- /dev/null +++ b/angie/conf/http-zstd.conf @@ -0,0 +1,2 @@ +include conf/zstd/*.conf; +zstd on; \ No newline at end of file diff --git a/angie/conf/http2/param.conf b/angie/conf/http2/param.conf new file mode 100644 index 0000000..2645e78 --- /dev/null +++ b/angie/conf/http2/param.conf @@ -0,0 +1,2 @@ +http2_chunk_size 16k; +http2_body_preread_size 64k; \ No newline at end of file diff --git a/angie/conf/http3/param.conf.j2 b/angie/conf/http3/param.conf.j2 new file mode 100644 index 0000000..d063f9c --- /dev/null +++ b/angie/conf/http3/param.conf.j2 @@ -0,0 +1,9 @@ +http3_max_concurrent_streams 128; #default +http3_stream_buffer_size 64k; #default +quic_active_connection_id_limit 3; + +{%- if env.NGX_HTTP_PROXY == '1' %} +proxy_http3_max_concurrent_streams 128; #default +proxy_http3_stream_buffer_size 64k; #default +proxy_quic_active_connection_id_limit 3; +{%- endif %} \ No newline at end of file diff --git a/angie/conf/mail-ssl.conf.j2 b/angie/conf/mail-ssl.conf.j2 new file mode 100644 index 0000000..803cc1f --- /dev/null +++ b/angie/conf/mail-ssl.conf.j2 @@ -0,0 +1,5 @@ +include conf/ssl/*.conf; + +{%- if env.NGX_MAIL_SSL_PROFILE %} +include snip/ssl-{{ env.NGX_MAIL_SSL_PROFILE }}; +{%- endif %} diff --git a/angie/conf/njs/path.conf b/angie/conf/njs/path.conf new file mode 100644 index 0000000..7d320b8 --- /dev/null +++ b/angie/conf/njs/path.conf @@ -0,0 +1 @@ +js_path site; \ No newline at end of file diff --git a/angie/conf/njs/tls-ca-file.conf.in b/angie/conf/njs/tls-ca-file.conf.in new file mode 100644 index 0000000..8244737 --- /dev/null +++ b/angie/conf/njs/tls-ca-file.conf.in @@ -0,0 +1 @@ +js_fetch_trusted_certificate ${NGX_SSL_CERT_FILE}; \ No newline at end of file diff --git a/angie/conf/perl/path.conf b/angie/conf/perl/path.conf new file mode 100644 index 0000000..db9bf41 --- /dev/null +++ b/angie/conf/perl/path.conf @@ -0,0 +1 @@ +perl_modules site; \ No newline at end of file diff --git a/angie/conf/proxy-http/buffers.conf b/angie/conf/proxy-http/buffers.conf new file mode 100644 index 0000000..7e69216 --- /dev/null +++ b/angie/conf/proxy-http/buffers.conf @@ -0,0 +1,4 @@ +proxy_buffers 16 16k; +proxy_buffer_size 16k; +proxy_busy_buffers_size 32k; +proxy_temp_file_write_size 32k; \ No newline at end of file diff --git a/angie/conf/proxy-http/cache-bypass.conf.j2 b/angie/conf/proxy-http/cache-bypass.conf.j2 new file mode 100644 index 0000000..69d1110 --- /dev/null +++ b/angie/conf/proxy-http/cache-bypass.conf.j2 @@ -0,0 +1,14 @@ +{%- set cache_bypass = j2cfg.cache_bypass or [] -%} +{%- if cache_bypass -%} +## disable (response) cache under following conditions +proxy_cache_bypass +{%- for v in cache_bypass %} + {{ v | ngx_esc }} +{%- endfor %} +; +proxy_no_cache +{%- for v in cache_bypass %} + {{ v | ngx_esc }} +{%- endfor %} +; +{%- endif -%} \ No newline at end of file diff --git a/angie/conf/proxy-http/headers.conf b/angie/conf/proxy-http/headers.conf new file mode 100644 index 0000000..e8fc50b --- /dev/null +++ b/angie/conf/proxy-http/headers.conf @@ -0,0 +1,2 @@ +include snip/proxy-request-headers; +include snip/proxy-response-headers; \ No newline at end of file diff --git a/angie/conf/proxy-http/temp.conf b/angie/conf/proxy-http/temp.conf new file mode 100644 index 0000000..aab0c5f --- /dev/null +++ b/angie/conf/proxy-http/temp.conf @@ -0,0 +1 @@ +proxy_temp_path /run/ngx/cache/temp_proxy 2 2; \ No newline at end of file diff --git a/angie/conf/proxy-http/version.conf b/angie/conf/proxy-http/version.conf new file mode 100644 index 0000000..8d0948b --- /dev/null +++ b/angie/conf/proxy-http/version.conf @@ -0,0 +1 @@ +proxy_http_version 1.1; \ No newline at end of file diff --git a/angie/conf/proxy-stream/.gitkeep b/angie/conf/proxy-stream/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/angie/conf/proxy/ssl.conf.j2 b/angie/conf/proxy/ssl.conf.j2 new file mode 100644 index 0000000..011dec6 --- /dev/null +++ b/angie/conf/proxy/ssl.conf.j2 @@ -0,0 +1,7 @@ +{%- for k, v in j2cfg.tls.conf_cmd|dictsort %} +proxy_ssl_conf_command {{ k }} {{ v | ngx_esc }}; +{%- endfor %} + +proxy_ssl_trusted_certificate {{ env.NGX_SSL_CERT_FILE }}; +proxy_ssl_verify on; +proxy_ssl_server_name on; diff --git a/angie/conf/scgi/buffers.conf b/angie/conf/scgi/buffers.conf new file mode 100644 index 0000000..9871886 --- /dev/null +++ b/angie/conf/scgi/buffers.conf @@ -0,0 +1,4 @@ +scgi_buffers 16 16k; +scgi_buffer_size 16k; +scgi_busy_buffers_size 32k; +scgi_temp_file_write_size 32k; \ No newline at end of file diff --git a/angie/conf/scgi/cache-bypass.conf.j2 b/angie/conf/scgi/cache-bypass.conf.j2 new file mode 100644 index 0000000..8d7d52b --- /dev/null +++ b/angie/conf/scgi/cache-bypass.conf.j2 @@ -0,0 +1,14 @@ +{%- set cache_bypass = j2cfg.cache_bypass or [] -%} +{%- if cache_bypass -%} +## disable (response) cache under following conditions +scgi_cache_bypass +{%- for v in cache_bypass %} + {{ v | ngx_esc }} +{%- endfor %} +; +scgi_no_cache +{%- for v in cache_bypass %} + {{ v | ngx_esc }} +{%- endfor %} +; +{%- endif -%} \ No newline at end of file diff --git a/angie/conf/scgi/headers.conf b/angie/conf/scgi/headers.conf new file mode 100644 index 0000000..e642e0d --- /dev/null +++ b/angie/conf/scgi/headers.conf @@ -0,0 +1,2 @@ +include snip/scgi-request-headers; +include snip/scgi-response-headers; \ No newline at end of file diff --git a/angie/conf/scgi/param.conf b/angie/conf/scgi/param.conf new file mode 100644 index 0000000..b814a1d --- /dev/null +++ b/angie/conf/scgi/param.conf @@ -0,0 +1,7 @@ +include snip/scgi_params; + +scgi_param PATH_INFO $path_info; + +scgi_param AUTH_USER $remote_user; +scgi_param REMOTE_USER $remote_user; +scgi_param HTTP_HOST $host; \ No newline at end of file diff --git a/angie/conf/scgi/temp.conf b/angie/conf/scgi/temp.conf new file mode 100644 index 0000000..f6724ef --- /dev/null +++ b/angie/conf/scgi/temp.conf @@ -0,0 +1 @@ +scgi_temp_path /run/ngx/cache/temp_scgi 2 2; \ No newline at end of file diff --git a/angie/conf/ssl/cmd.conf.j2 b/angie/conf/ssl/cmd.conf.j2 new file mode 100644 index 0000000..8d54d61 --- /dev/null +++ b/angie/conf/ssl/cmd.conf.j2 @@ -0,0 +1,3 @@ +{%- for k, v in j2cfg.tls.conf_cmd|dictsort %} +ssl_conf_command {{ k }} {{ v | ngx_esc }}; +{%- endfor %} \ No newline at end of file diff --git a/angie/conf/stream-njs.conf b/angie/conf/stream-njs.conf new file mode 100644 index 0000000..263aa8f --- /dev/null +++ b/angie/conf/stream-njs.conf @@ -0,0 +1 @@ +include conf/njs/*.conf; \ No newline at end of file diff --git a/angie/conf/stream-proxy.conf b/angie/conf/stream-proxy.conf new file mode 100644 index 0000000..1cc5911 --- /dev/null +++ b/angie/conf/stream-proxy.conf @@ -0,0 +1,2 @@ +include conf/proxy/*.conf; +include conf/proxy-stream/*.conf; \ No newline at end of file diff --git a/angie/conf/stream-ssl.conf.j2 b/angie/conf/stream-ssl.conf.j2 new file mode 100644 index 0000000..5c3d156 --- /dev/null +++ b/angie/conf/stream-ssl.conf.j2 @@ -0,0 +1,5 @@ +include conf/ssl/*.conf; + +{%- if env.NGX_STREAM_SSL_PROFILE %} +include snip/ssl-{{ env.NGX_STREAM_SSL_PROFILE }}; +{%- endif %} diff --git a/angie/conf/uwsgi/buffers.conf b/angie/conf/uwsgi/buffers.conf new file mode 100644 index 0000000..2ac16d7 --- /dev/null +++ b/angie/conf/uwsgi/buffers.conf @@ -0,0 +1,4 @@ +uwsgi_buffers 16 16k; +uwsgi_buffer_size 16k; +uwsgi_busy_buffers_size 32k; +uwsgi_temp_file_write_size 32k; \ No newline at end of file diff --git a/angie/conf/uwsgi/cache-bypass.conf.j2 b/angie/conf/uwsgi/cache-bypass.conf.j2 new file mode 100644 index 0000000..09a35f7 --- /dev/null +++ b/angie/conf/uwsgi/cache-bypass.conf.j2 @@ -0,0 +1,14 @@ +{%- set cache_bypass = j2cfg.cache_bypass or [] -%} +{%- if cache_bypass -%} +## disable (response) cache under following conditions +uwsgi_cache_bypass +{%- for v in cache_bypass %} + {{ v | ngx_esc }} +{%- endfor %} +; +uwsgi_no_cache +{%- for v in cache_bypass %} + {{ v | ngx_esc }} +{%- endfor %} +; +{%- endif -%} \ No newline at end of file diff --git a/angie/conf/uwsgi/headers.conf b/angie/conf/uwsgi/headers.conf new file mode 100644 index 0000000..2a11d49 --- /dev/null +++ b/angie/conf/uwsgi/headers.conf @@ -0,0 +1,2 @@ +include snip/uwsgi-request-headers; +include snip/uwsgi-response-headers; \ No newline at end of file diff --git a/angie/conf/uwsgi/param.conf b/angie/conf/uwsgi/param.conf new file mode 100644 index 0000000..f1fb50e --- /dev/null +++ b/angie/conf/uwsgi/param.conf @@ -0,0 +1,7 @@ +include snip/uwsgi_params; + +uwsgi_param PATH_INFO $path_info; + +uwsgi_param AUTH_USER $remote_user; +uwsgi_param REMOTE_USER $remote_user; +uwsgi_param HTTP_HOST $host; \ No newline at end of file diff --git a/angie/conf/uwsgi/ssl.conf.j2 b/angie/conf/uwsgi/ssl.conf.j2 new file mode 100644 index 0000000..7dc8fe6 --- /dev/null +++ b/angie/conf/uwsgi/ssl.conf.j2 @@ -0,0 +1,7 @@ +{%- for k, v in j2cfg.tls.conf_cmd|dictsort %} +uwsgi_ssl_conf_command {{ k }} {{ v | ngx_esc }}; +{%- endfor %} + +uwsgi_ssl_trusted_certificate {{ env.NGX_SSL_CERT_FILE }}; +uwsgi_ssl_verify on; +uwsgi_ssl_server_name on; diff --git a/angie/conf/uwsgi/temp.conf b/angie/conf/uwsgi/temp.conf new file mode 100644 index 0000000..fc9beea --- /dev/null +++ b/angie/conf/uwsgi/temp.conf @@ -0,0 +1 @@ +uwsgi_temp_path /run/ngx/cache/temp_uwsgi 2 2; \ No newline at end of file diff --git a/angie/conf/zstd/buffers.conf b/angie/conf/zstd/buffers.conf new file mode 100644 index 0000000..c310cc5 --- /dev/null +++ b/angie/conf/zstd/buffers.conf @@ -0,0 +1,4 @@ +zstd_comp_level 2; # default: 1 + +zstd_min_length 1024; +zstd_buffers 32 16k; \ No newline at end of file diff --git a/angie/conf/zstd/types.conf.j2 b/angie/conf/zstd/types.conf.j2 new file mode 100644 index 0000000..924c58a --- /dev/null +++ b/angie/conf/zstd/types.conf.j2 @@ -0,0 +1,9 @@ +{%- set mime_types = j2cfg.compress_types or [] -%} +{%- set mime_types = mime_types | any_to_str_list | uniq_str_list -%} +{%- if mime_types -%} +zstd_types +{%- for t in mime_types %} + {{ t }} +{%- endfor %} +; +{%- endif -%} \ No newline at end of file diff --git a/angie/ctx-core.conf b/angie/ctx-core.conf new file mode 100644 index 0000000..84bd999 --- /dev/null +++ b/angie/ctx-core.conf @@ -0,0 +1,2 @@ +include autoconf/core-*.conf; +include /run/ngx/conf.load/core-*.conf; \ No newline at end of file diff --git a/angie/ctx-core_ev.conf b/angie/ctx-core_ev.conf new file mode 100644 index 0000000..57fc8d7 --- /dev/null +++ b/angie/ctx-core_ev.conf @@ -0,0 +1,4 @@ +events { + include autoconf/core_ev-*.conf; + include /run/ngx/conf.load/core_ev-*.conf; +} \ No newline at end of file diff --git a/angie/ctx-http.conf b/angie/ctx-http.conf new file mode 100644 index 0000000..d63b05e --- /dev/null +++ b/angie/ctx-http.conf @@ -0,0 +1,5 @@ +http { + include autoconf/http-*.conf; + include /run/ngx/conf.load/http-*.conf; + include site/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..c788142 --- /dev/null +++ b/angie/ctx-mail.conf @@ -0,0 +1,5 @@ +mail { + include autoconf/mail-*.conf; + include /run/ngx/conf.load/mail-*.conf; + include site/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..b4d6b55 --- /dev/null +++ b/angie/ctx-stream.conf @@ -0,0 +1,5 @@ +stream { + include autoconf/stream-*.conf; + include /run/ngx/conf.load/stream-*.conf; + include site/stream-*.conf; +} \ No newline at end of file diff --git a/angie/j2cfg.yml b/angie/j2cfg.yml new file mode 100644 index 0000000..f426ae6 --- /dev/null +++ b/angie/j2cfg.yml @@ -0,0 +1,60 @@ +cache_bypass: + - '$http_authorization' + - '$http_pragma' + - '$http_upgrade' + +compress_types: + - application/atom+xml + - application/javascript + - application/json + - application/vnd.api+json + - application/rss+xml + - application/x-javascript + - application/xhtml+xml + - application/xml + - image/svg+xml + - image/x-icon + - text/css + - text/javascript + - text/plain + - text/xml + +request_headers: + ## '$req_connection' is defined in /angie/autoconf/http-request-headers-basic.conf.j2 + Connection: '$req_connection' + Upgrade: '$http_upgrade' + ## do not pass Accept-Encoding to backend + Accept-Encoding: "" + ## almost all browsers doesn't support this + #Early-Data: '$ssl_early_data' + Early-Data: "" + +response_headers: {} + +tls: + ## https://docs.openssl.org/3.0/man3/SSL_CONF_cmd/#supported-configuration-file-commands + conf_cmd: + Options: PrioritizeChaCha + stapling: + enable: false + verify: true + profiles: + modern: + protocols: TLSv1.3 + #prefer_server_ciphers: off + session_tickets: off + session_timeout: 1d + intermediate: + protocols: TLSv1.2 TLSv1.3 + #prefer_server_ciphers: off + ciphers: ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305 + dhparam: tls/ffdhe2048.pem + session_tickets: off + session_timeout: 1d + old: + protocols: TLSv1 TLSv1.1 TLSv1.2 TLSv1.3 + prefer_server_ciphers: on + ciphers: ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA256:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA + dhparam: tls/dh1024.pem + session_tickets: off + session_timeout: 1d diff --git a/angie/j2cfg/00-headers.yml.j2 b/angie/j2cfg/00-headers.yml.j2 new file mode 100644 index 0000000..5a1f06a --- /dev/null +++ b/angie/j2cfg/00-headers.yml.j2 @@ -0,0 +1,53 @@ +{%- set transparent = false -%} +{%- if env.NGX_HTTP_TRANSPARENT_PROXY != None -%} + {%- set transparent = (env.NGX_HTTP_TRANSPARENT_PROXY | from_gobool) -%} +{%- endif -%} + +--- + +request_headers: +{%- if transparent %} + Host: '$proxy_host' + ## '$req_accept' is defined in /angie/autoconf.dist/http-request-headers-basic.conf.j2 + Accept: '$req_accept' + ## '$req_user_agent' is defined in /angie/autoconf.dist/http-request-headers-basic.conf.j2 + User-Agent: '$req_user_agent' +{%- else %} + Host: '$host' +{%- endif %} + +--- + +request_headers: +{%- if env.NGX_HTTP_FORWARDED == 'pass' %} + ## '$proxy_add_forwarded' is defined in /angie/autoconf.dist/http-request-headers-forwarded.conf + Forwarded: '$proxy_add_forwarded' +{%- elif env.NGX_HTTP_FORWARDED == 'remove' %} + Forwarded: '' +{%- endif %} + +--- + +request_headers: +{%- if env.NGX_HTTP_X_FORWARDED == 'pass' %} + X-Forwarded-Proto: '$scheme' + X-Forwarded-Host: '$host' + X-Forwarded-Server: '$host' + X-Forwarded-Port: '$server_port' + X-Forwarded-For: '$proxy_add_x_forwarded_for' +{%- elif env.NGX_HTTP_X_FORWARDED == 'remove' %} + X-Forwarded-Proto: '' + X-Forwarded-Host: '' + X-Forwarded-Server: '' + X-Forwarded-Port: '' + X-Forwarded-For: '' +{%- endif %} + +--- + +request_headers: +{%- if env.NGX_HTTP_X_REAL_IP == 'pass' %} + X-Real-IP: '$remote_addr' +{%- elif env.NGX_HTTP_X_REAL_IP == 'remove' %} + X-Real-IP: '' +{%- endif %} diff --git a/angie/mod-core.conf b/angie/mod-core.conf new file mode 100644 index 0000000..019b517 --- /dev/null +++ b/angie/mod-core.conf @@ -0,0 +1 @@ +include /run/ngx/conf.load/mod-core-*.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..93ffe50 --- /dev/null +++ b/angie/mod-http.conf @@ -0,0 +1 @@ +include /run/ngx/conf.load/mod-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..d6b6421 --- /dev/null +++ b/angie/mod-mail.conf @@ -0,0 +1 @@ +include /run/ngx/conf.load/mod-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..8c9e92e --- /dev/null +++ b/angie/mod-stream.conf @@ -0,0 +1 @@ +include /run/ngx/conf.load/mod-stream-*.conf; \ No newline at end of file diff --git a/angie/mod/.brotli.preseed b/angie/mod/.brotli.preseed new file mode 100644 index 0000000..e69de29 diff --git a/angie/mod/.njs-light.preseed b/angie/mod/.njs-light.preseed new file mode 100644 index 0000000..e69de29 diff --git a/angie/mod/.njs.preseed b/angie/mod/.njs.preseed new file mode 100644 index 0000000..e69de29 diff --git a/angie/mod/.otel.preseed b/angie/mod/.otel.preseed new file mode 100644 index 0000000..e69de29 diff --git a/angie/mod/.postgres.preseed b/angie/mod/.postgres.preseed new file mode 100644 index 0000000..e69de29 diff --git a/angie/mod/.rtmp.preseed b/angie/mod/.rtmp.preseed new file mode 100644 index 0000000..e69de29 diff --git a/angie/mod/.vts.preseed b/angie/mod/.vts.preseed new file mode 100644 index 0000000..e69de29 diff --git a/angie/mod/.wamr.preseed b/angie/mod/.wamr.preseed new file mode 100644 index 0000000..e69de29 diff --git a/angie/mod/.wasm.preseed b/angie/mod/.wasm.preseed new file mode 100644 index 0000000..e69de29 diff --git a/angie/mod/.wasmtime.preseed b/angie/mod/.wasmtime.preseed new file mode 100644 index 0000000..e69de29 diff --git a/angie/mod/.zstd.preseed b/angie/mod/.zstd.preseed new file mode 100644 index 0000000..e69de29 diff --git a/angie/mod/core-wamr.conf b/angie/mod/core-wamr.conf new file mode 100644 index 0000000..2d6cd28 --- /dev/null +++ b/angie/mod/core-wamr.conf @@ -0,0 +1 @@ +load_module modules/ngx_wamr_module.so; \ No newline at end of file diff --git a/angie/mod/core-wasm.conf b/angie/mod/core-wasm.conf new file mode 100644 index 0000000..51af949 --- /dev/null +++ b/angie/mod/core-wasm.conf @@ -0,0 +1,2 @@ +load_module modules/ngx_wasm_module.so; +load_module modules/ngx_wasm_core_module.so; \ No newline at end of file diff --git a/angie/mod/core-wasmtime.conf b/angie/mod/core-wasmtime.conf new file mode 100644 index 0000000..61aa901 --- /dev/null +++ b/angie/mod/core-wasmtime.conf @@ -0,0 +1 @@ +load_module modules/ngx_wasmtime_module.so; \ No newline at end of file diff --git a/angie/mod/http-brotli-static.conf b/angie/mod/http-brotli-static.conf new file mode 100644 index 0000000..7d25937 --- /dev/null +++ b/angie/mod/http-brotli-static.conf @@ -0,0 +1 @@ +load_module modules/ngx_http_brotli_static_module.so; \ No newline at end of file diff --git a/angie/mod/http-brotli.conf b/angie/mod/http-brotli.conf new file mode 100644 index 0000000..4d630a1 --- /dev/null +++ b/angie/mod/http-brotli.conf @@ -0,0 +1 @@ +load_module modules/ngx_http_brotli_filter_module.so; \ No newline at end of file diff --git a/angie/mod/http-njs.conf b/angie/mod/http-njs.conf new file mode 100644 index 0000000..38d5e8d --- /dev/null +++ b/angie/mod/http-njs.conf @@ -0,0 +1 @@ +load_module modules/ngx_http_js_module.so; \ No newline at end of file diff --git a/angie/mod/http-otel.conf b/angie/mod/http-otel.conf new file mode 100644 index 0000000..e9f7b76 --- /dev/null +++ b/angie/mod/http-otel.conf @@ -0,0 +1 @@ +load_module modules/ngx_otel_module.so; \ No newline at end of file diff --git a/angie/mod/http-postgres.conf b/angie/mod/http-postgres.conf new file mode 100644 index 0000000..e5b476c --- /dev/null +++ b/angie/mod/http-postgres.conf @@ -0,0 +1 @@ +load_module modules/ngx_postgres_module.so; \ No newline at end of file diff --git a/angie/mod/http-rtmp.conf b/angie/mod/http-rtmp.conf new file mode 100644 index 0000000..dbf7b7e --- /dev/null +++ b/angie/mod/http-rtmp.conf @@ -0,0 +1 @@ +load_module modules/ngx_rtmp_module.so; \ No newline at end of file diff --git a/angie/mod/http-sts.conf b/angie/mod/http-sts.conf new file mode 100644 index 0000000..a95ba1d --- /dev/null +++ b/angie/mod/http-sts.conf @@ -0,0 +1 @@ +load_module modules/ngx_http_stream_server_traffic_status_module.so; \ No newline at end of file diff --git a/angie/mod/http-vts.conf b/angie/mod/http-vts.conf new file mode 100644 index 0000000..4f0275d --- /dev/null +++ b/angie/mod/http-vts.conf @@ -0,0 +1 @@ +load_module modules/ngx_http_vhost_traffic_status_module.so; \ No newline at end of file diff --git a/angie/mod/http-wasm.conf b/angie/mod/http-wasm.conf new file mode 100644 index 0000000..74f8081 --- /dev/null +++ b/angie/mod/http-wasm.conf @@ -0,0 +1 @@ +load_module modules/ngx_http_wasm_host_module.so; \ No newline at end of file diff --git a/angie/mod/http-zstd-static.conf b/angie/mod/http-zstd-static.conf new file mode 100644 index 0000000..ff120f4 --- /dev/null +++ b/angie/mod/http-zstd-static.conf @@ -0,0 +1 @@ +load_module modules/ngx_http_zstd_static_module.so; \ No newline at end of file diff --git a/angie/mod/http-zstd.conf b/angie/mod/http-zstd.conf new file mode 100644 index 0000000..af6c5d7 --- /dev/null +++ b/angie/mod/http-zstd.conf @@ -0,0 +1 @@ +load_module modules/ngx_http_zstd_filter_module.so; \ No newline at end of file diff --git a/angie/mod/stream-njs.conf b/angie/mod/stream-njs.conf new file mode 100644 index 0000000..f9f6bc6 --- /dev/null +++ b/angie/mod/stream-njs.conf @@ -0,0 +1 @@ +load_module modules/ngx_stream_js_module.so; \ No newline at end of file diff --git a/angie/mod/stream-sts.conf b/angie/mod/stream-sts.conf new file mode 100644 index 0000000..bb65f07 --- /dev/null +++ b/angie/mod/stream-sts.conf @@ -0,0 +1 @@ +load_module modules/ngx_stream_server_traffic_status_module.so; \ No newline at end of file diff --git a/angie/modsecurity/rules.conf b/angie/modsecurity/rules.conf new file mode 100644 index 0000000..44f885b --- /dev/null +++ b/angie/modsecurity/rules.conf @@ -0,0 +1,33 @@ +Include modsecurity.conf + +# To enable the OWASP CRS, please perform the following steps: +# +# 1. Checkout Core Rule Set from GitHub and create config files as shown below: +# +# version='v4.14.0' +# uri="https://github.com/coreruleset/coreruleset/archive/refs/tags/${version}.tar.gz" +# dst_dir='/etc/angie/modsecurity/coreruleset' +# w=$(mktemp -d) ; : "${w:?}" +# cd "$w/" +# tarball="coreruleset.tar.gz" +# curl -Lo "${tarball}" "${uri}" +# mkdir coreruleset +# tar -C ./coreruleset --strip-components=1 -xf "${tarball}" +# rm -f "${tarball}" ; unset tarball +# for p in \ +# crs-setup.conf \ +# rules/REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf \ +# rules/RESPONSE-999-EXCLUSION-RULES-AFTER-CRS.conf \ +# ; do +# src="coreruleset/$p.example" +# dst="${dst_dir}/$p" +# [ -f "${src}" ] || continue +# [ -d "${dst%/*}" ] || mkdir -p "${dst%/*}" +# cp -nv "${src}" "${dst}" +# done +# rm -rf "${w:?}/" ; unset w +# +# 2. Uncomment both 'Include' directives below + +#Include coreruleset/crs-setup.conf +#Include coreruleset/rules/*.conf diff --git a/angie/snip/cache.j2mod b/angie/snip/cache.j2mod new file mode 100644 index 0000000..a26d072 --- /dev/null +++ b/angie/snip/cache.j2mod @@ -0,0 +1,55 @@ +{%- macro proxy_cache_path(name, size='1m') %} +{%- set path = '/run/ngx/cache'|join_prefix('proxy_' + name) -%} +{%- set zone_file = '/run/ngx/lib'|join_prefix('proxy_' + name + '.keys') -%} +proxy_cache_path {{ path | ngx_esc }} + {{ "keys_zone={}:{}:file={}".format(name, size, zone_file) | ngx_esc }} +{%- for v in varargs %} + {{ v | ngx_esc }} +{%- endfor %} +{%- for k, v in kwargs|dictsort %} + {{ "{}={}".format(k, v) | ngx_esc }} +{%- endfor %} +; +{%- endmacro %} + + +{%- macro fastcgi_cache_path(name, size='1m') %} +{%- set path = '/run/ngx/cache'|join_prefix('fastcgi_' + name) -%} +fastcgi_cache_path {{ path | ngx_esc }} + {{ "keys_zone={}:{}".format(name, size) | ngx_esc }} +{%- for v in varargs %} + {{ v | ngx_esc }} +{%- endfor %} +{%- for k, v in kwargs|dictsort %} + {{ "{}={}".format(k, v) | ngx_esc }} +{%- endfor %} +; +{%- endmacro %} + + +{%- macro scgi_cache(name, size='1m') %} +{%- set path = '/run/ngx/cache'|join_prefix('scgi_' + name) -%} +scgi_cache {{ path | ngx_esc }} + {{ "keys_zone={}:{}".format(name, size) | ngx_esc }} +{%- for v in varargs %} + {{ v | ngx_esc }} +{%- endfor %} +{%- for k, v in kwargs|dictsort %} + {{ "{}={}".format(k, v) | ngx_esc }} +{%- endfor %} +; +{%- endmacro %} + + +{%- macro uwsgi_cache(name, size='1m') %} +{%- set path = '/run/ngx/cache'|join_prefix('uwsgi_' + name) -%} +uwsgi_cache {{ path | ngx_esc }} + {{ "keys_zone={}:{}".format(name, size) | ngx_esc }} +{%- for v in varargs %} + {{ v | ngx_esc }} +{%- endfor %} +{%- for k, v in kwargs|dictsort %} + {{ "{}={}".format(k, v) | ngx_esc }} +{%- endfor %} +; +{%- endmacro %} diff --git a/angie/snip/deny-dotfiles b/angie/snip/deny-dotfiles new file mode 100644 index 0000000..4ef08c7 --- /dev/null +++ b/angie/snip/deny-dotfiles @@ -0,0 +1,3 @@ +location ~ /\. { + include snip/internal-area; +} \ No newline at end of file diff --git a/angie/snip/disable-compression.j2 b/angie/snip/disable-compression.j2 new file mode 100644 index 0000000..8d17673 --- /dev/null +++ b/angie/snip/disable-compression.j2 @@ -0,0 +1,8 @@ +# safe to specify all the time +gzip off; +{%- set extra_comp_modules = ['brotli', 'zstd'] -%} +{%- set modules = ( env.NGX_HTTP_MODULES or '' ) | str_split_to_list -%} +{%- set comp_modules = modules | list_intersect(extra_comp_modules) | sort -%} +{%- for comp in comp_modules %} +{{ comp }} off; +{%- endfor %} \ No newline at end of file diff --git a/angie/snip/empty-favicon b/angie/snip/empty-favicon new file mode 100644 index 0000000..bc796c3 --- /dev/null +++ b/angie/snip/empty-favicon @@ -0,0 +1,4 @@ +location = /favicon.ico { + empty_gif; + expires 1d; +} \ No newline at end of file diff --git a/angie/snip/fastcgi-location b/angie/snip/fastcgi-location new file mode 100644 index 0000000..9e99f1e --- /dev/null +++ b/angie/snip/fastcgi-location @@ -0,0 +1,5 @@ +try_files $fastcgi_script_name =444; + +## bypass the fact that try_files resets $fastcgi_path_info +## see: https://trac.nginx.org/nginx/ticket/321 +set $path_info $fastcgi_path_info; \ No newline at end of file diff --git a/angie/snip/fastcgi-request-headers.j2 b/angie/snip/fastcgi-request-headers.j2 new file mode 100644 index 0000000..fa57e3a --- /dev/null +++ b/angie/snip/fastcgi-request-headers.j2 @@ -0,0 +1,6 @@ +## sourced by conf/fastcgi/headers.conf +## set/remove request headers +{%- set req_hdr_dict = j2cfg.request_headers or {} -%} +{%- for h, v in req_hdr_dict|dictsort %} +fastcgi_param {{ h | as_cgi_hdr }} {{ v | ngx_esc }}; +{%- endfor %} \ No newline at end of file diff --git a/angie/snip/fastcgi-response-headers.j2 b/angie/snip/fastcgi-response-headers.j2 new file mode 100644 index 0000000..bf35af6 --- /dev/null +++ b/angie/snip/fastcgi-response-headers.j2 @@ -0,0 +1,7 @@ +## sourced by conf/fastcgi/headers.conf +## hide response headers +{%- set resp_hdr_dict = j2cfg.response_headers or {} -%} +{%- set resp_hdr_list = resp_hdr_dict | dict_keys -%} +{%- for h in resp_hdr_list %} +fastcgi_hide_header {{ h }}; +{%- endfor %} \ No newline at end of file diff --git a/angie/snip/grpc-request-headers.j2 b/angie/snip/grpc-request-headers.j2 new file mode 100644 index 0000000..b4f3eb8 --- /dev/null +++ b/angie/snip/grpc-request-headers.j2 @@ -0,0 +1,6 @@ +## sourced by conf/grpc/headers.conf +## set/remove request headers +{%- set req_hdr_dict = j2cfg.request_headers or {} -%} +{%- for h, v in req_hdr_dict|dictsort %} +grpc_set_header {{ h }} {{ v | ngx_esc }}; +{%- endfor %} \ No newline at end of file diff --git a/angie/snip/grpc-response-headers.j2 b/angie/snip/grpc-response-headers.j2 new file mode 100644 index 0000000..f3717a2 --- /dev/null +++ b/angie/snip/grpc-response-headers.j2 @@ -0,0 +1,7 @@ +## sourced by conf/grpc/headers.conf +## hide response headers +{%- set resp_hdr_dict = j2cfg.response_headers or {} -%} +{%- set resp_hdr_list = resp_hdr_dict | dict_keys -%} +{%- for h in resp_hdr_list %} +grpc_hide_header {{ h }}; +{%- endfor %} \ No newline at end of file diff --git a/angie/snip/http-alt-svc.j2 b/angie/snip/http-alt-svc.j2 new file mode 100644 index 0000000..1300788 --- /dev/null +++ b/angie/snip/http-alt-svc.j2 @@ -0,0 +1,13 @@ +## sourced by autoconf/http-alt-svc.conf +{#- prologue -#} +{%- set extra_proto = ['v3', 'v2'] -%} +{%- set confload = ( env.NGX_HTTP_CONFLOAD or '' ) | str_split_to_list -%} +{%- set proto = confload | list_intersect(extra_proto) -%} +{#- ALPN mapping -#} +## TODO: make this configurable +{%- set proto = proto | re_sub('^v2$', 'h2=":443"; ma=3600') -%} +{%- set proto = proto | re_sub('^v3$', 'h3=":443"; ma=3600') -%} +{#- main part -#} +{%- if proto %} +add_header Alt-Svc {{ (proto | join(', ')) | ngx_esc }}; +{%- endif %} \ No newline at end of file diff --git a/angie/snip/http-response-headers.j2 b/angie/snip/http-response-headers.j2 new file mode 100644 index 0000000..541b42a --- /dev/null +++ b/angie/snip/http-response-headers.j2 @@ -0,0 +1,6 @@ +## sourced by autoconf/http-response-headers.conf +## add response headers +{%- set resp_hdr_dict = j2cfg.response_headers or {} -%} +{%- for h, v in resp_hdr_dict|dictsort %} +add_header {{ h }} {{ v | ngx_esc }}; +{%- endfor %} \ No newline at end of file diff --git a/angie/snip/internal-area b/angie/snip/internal-area new file mode 100644 index 0000000..b5b3e6a --- /dev/null +++ b/angie/snip/internal-area @@ -0,0 +1,5 @@ +## always sourced by snip/deny-dotfiles + +access_log off; +log_not_found off; +internal; \ No newline at end of file diff --git a/angie/snip/log.j2mod b/angie/snip/log.j2mod new file mode 100644 index 0000000..4936496 --- /dev/null +++ b/angie/snip/log.j2mod @@ -0,0 +1,12 @@ +{%- macro error_log(dest='error.log', level='warn') %} +error_log {{ '/run/ngx/log'|join_prefix(dest) | ngx_esc }} {{ level | ngx_esc }}; +{%- endmacro %} + + +{%- macro access_log(dest='access.log', format='main') %} +access_log {{ '/run/ngx/log'|join_prefix(dest) | ngx_esc }} {{ format | ngx_esc }} +{%- for k, v in kwargs|dictsort %} + {{ "{}={}".format(k, v) | ngx_esc }} +{%- endfor %} +; +{%- endmacro %} diff --git a/angie/snip/proxy-request-headers.j2 b/angie/snip/proxy-request-headers.j2 new file mode 100644 index 0000000..0fbca28 --- /dev/null +++ b/angie/snip/proxy-request-headers.j2 @@ -0,0 +1,6 @@ +## sourced by conf/proxy-http/headers.conf +## set/remove request headers +{%- set req_hdr_dict = j2cfg.request_headers or {} -%} +{%- for h, v in req_hdr_dict|dictsort %} +proxy_set_header {{ h }} {{ v | ngx_esc }}; +{%- endfor %} \ No newline at end of file diff --git a/angie/snip/proxy-response-headers.j2 b/angie/snip/proxy-response-headers.j2 new file mode 100644 index 0000000..93d0caa --- /dev/null +++ b/angie/snip/proxy-response-headers.j2 @@ -0,0 +1,7 @@ +## sourced by conf/proxy-http/headers.conf +## hide response headers +{%- set resp_hdr_dict = j2cfg.response_headers or {} -%} +{%- set resp_hdr_list = resp_hdr_dict | dict_keys -%} +{%- for h in resp_hdr_list %} +proxy_hide_header {{ h }}; +{%- endfor %} \ No newline at end of file diff --git a/angie/snip/resolver.j2inc b/angie/snip/resolver.j2inc new file mode 100644 index 0000000..338ac3a --- /dev/null +++ b/angie/snip/resolver.j2inc @@ -0,0 +1,13 @@ +{%- if env.NGX_RESOLVERS %} + {%- if env.NGX_RESOLVER_STACK == 'any' %} +resolver {{ env.NGX_RESOLVERS }} status_zone={{ resolver_status_zone }}; + {%- elif env.NGX_RESOLVER_STACK == 'ipv4' %} +resolver {{ env.NGX_RESOLVERS }} status_zone={{ resolver_status_zone }} ipv4=on ipv6=off; + {%- elif env.NGX_RESOLVER_STACK == 'ipv6' %} +resolver {{ env.NGX_RESOLVERS }} status_zone={{ resolver_status_zone }} ipv4=off ipv6=on; + {%- endif %} +{%- endif %} + +{%- if env.NGX_RESOLVER_TIMEOUT %} +resolver_timeout {{ env.NGX_RESOLVER_TIMEOUT }}; +{%- endif %} diff --git a/angie/snip/scgi-request-headers.j2 b/angie/snip/scgi-request-headers.j2 new file mode 100644 index 0000000..b8f6a2b --- /dev/null +++ b/angie/snip/scgi-request-headers.j2 @@ -0,0 +1,6 @@ +## sourced by conf/scgi/headers.conf +## set/remove request headers +{%- set req_hdr_dict = j2cfg.request_headers or {} -%} +{%- for h, v in req_hdr_dict|dictsort %} +scgi_param {{ h | as_cgi_hdr }} {{ v | ngx_esc }}; +{%- endfor %} \ No newline at end of file diff --git a/angie/snip/scgi-response-headers.j2 b/angie/snip/scgi-response-headers.j2 new file mode 100644 index 0000000..bbb0a31 --- /dev/null +++ b/angie/snip/scgi-response-headers.j2 @@ -0,0 +1,7 @@ +## sourced by conf/scgi/headers.conf +## hide response headers +{%- set resp_hdr_dict = j2cfg.response_headers or {} -%} +{%- set resp_hdr_list = resp_hdr_dict | dict_keys -%} +{%- for h in resp_hdr_list %} +scgi_hide_header {{ h }}; +{%- endfor %} \ No newline at end of file diff --git a/angie/snip/ssl-intermediate.j2 b/angie/snip/ssl-intermediate.j2 new file mode 100644 index 0000000..92deb3d --- /dev/null +++ b/angie/snip/ssl-intermediate.j2 @@ -0,0 +1,2 @@ +{%- set ssl_profile = j2cfg.tls.profiles.intermediate -%} +{% include 'ssl-profile.j2inc' %} diff --git a/angie/snip/ssl-modern.j2 b/angie/snip/ssl-modern.j2 new file mode 100644 index 0000000..4ab7735 --- /dev/null +++ b/angie/snip/ssl-modern.j2 @@ -0,0 +1,2 @@ +{%- set ssl_profile = j2cfg.tls.profiles.modern -%} +{% include 'ssl-profile.j2inc' %} diff --git a/angie/snip/ssl-old.j2 b/angie/snip/ssl-old.j2 new file mode 100644 index 0000000..3831a5a --- /dev/null +++ b/angie/snip/ssl-old.j2 @@ -0,0 +1,2 @@ +{%- set ssl_profile = j2cfg.tls.profiles.old -%} +{% include 'ssl-profile.j2inc' %} diff --git a/angie/snip/ssl-profile.j2inc b/angie/snip/ssl-profile.j2inc new file mode 100644 index 0000000..c96e98a --- /dev/null +++ b/angie/snip/ssl-profile.j2inc @@ -0,0 +1,28 @@ +{%- if ssl_profile.protocols %} +ssl_protocols {{ ssl_profile.protocols }}; +{%- endif %} +{%- if ssl_profile.prefer_server_ciphers %} +ssl_prefer_server_ciphers {{ ssl_profile.prefer_server_ciphers }}; +{%- endif %} +{%- if ssl_profile.ciphers %} +ssl_ciphers {{ ssl_profile.ciphers }}; +{%- endif %} +{%- if ssl_profile.dhparam %} +ssl_dhparam {{ ssl_profile.dhparam }}; +{%- endif %} +{%- if ssl_profile.ecdh_curve %} +ssl_ecdh_curve {{ ssl_profile.ecdh_curve }}; +{%- endif %} + +{%- if ssl_profile.session_cache %} +ssl_session_cache {{ ssl_profile.session_cache }}; +{%- endif %} +{%- if ssl_profile.session_timeout %} +ssl_session_timeout {{ ssl_profile.session_timeout }}; +{%- endif %} +{%- if ssl_profile.session_tickets %} +ssl_session_tickets {{ ssl_profile.session_tickets }}; +{%- endif %} +{%- if ssl_profile.session_ticket_key %} +ssl_session_ticket_key {{ ssl_profile.session_ticket_key }}; +{%- endif %} diff --git a/angie/snip/uwsgi-request-headers.j2 b/angie/snip/uwsgi-request-headers.j2 new file mode 100644 index 0000000..33e430e --- /dev/null +++ b/angie/snip/uwsgi-request-headers.j2 @@ -0,0 +1,6 @@ +## sourced by conf/uwsgi/headers.conf +## set/remove request headers +{%- set req_hdr_dict = j2cfg.request_headers or {} -%} +{%- for h, v in req_hdr_dict|dictsort %} +uwsgi_param {{ h | as_cgi_hdr }} {{ v | ngx_esc }}; +{%- endfor %} \ No newline at end of file diff --git a/angie/snip/uwsgi-response-headers.j2 b/angie/snip/uwsgi-response-headers.j2 new file mode 100644 index 0000000..85832b4 --- /dev/null +++ b/angie/snip/uwsgi-response-headers.j2 @@ -0,0 +1,7 @@ +## sourced by conf/uwsgi/headers.conf +## hide response headers +{%- set resp_hdr_dict = j2cfg.response_headers or {} -%} +{%- set resp_hdr_list = resp_hdr_dict | dict_keys -%} +{%- for h in resp_hdr_list %} +uwsgi_hide_header {{ h }}; +{%- endfor %} \ No newline at end of file diff --git a/angie/static/robots.txt b/angie/static/robots.txt new file mode 100644 index 0000000..1f53798 --- /dev/null +++ b/angie/static/robots.txt @@ -0,0 +1,2 @@ +User-agent: * +Disallow: / diff --git a/angie/tls/dh1024.pem b/angie/tls/dh1024.pem new file mode 100644 index 0000000..7275836 --- /dev/null +++ b/angie/tls/dh1024.pem @@ -0,0 +1,5 @@ +-----BEGIN DH PARAMETERS----- +MIGLAoGBAI59dNaK0KaM+Hn+H1chgUgThCJ4eJmVFYNGH7rfOsE9Bl+s3l9riTAl +oC6AN6Pbo2Rr2J6/I+zmCnwK8MfM2MEu1Vo3EtzK51eztyLicdyuGvvQ98qqeMoW +QhXP4L7DCckPisuEeycCDhVryQ3yh1q6mcZA0x2QMwkwbRqN8xy3AgECAgIArw== +-----END DH PARAMETERS----- diff --git a/angie/tls/dh2048.pem b/angie/tls/dh2048.pem new file mode 100644 index 0000000..8c37fd2 --- /dev/null +++ b/angie/tls/dh2048.pem @@ -0,0 +1,8 @@ +-----BEGIN DH PARAMETERS----- +MIIBDAKCAQEAkdE1Nvd8uW149tSpSo9/KxpC3dkkXT0y5fg42jc6iOiBSJlQRW5G +bjF8e8N4+Gohtn6X+RgZRjzDJ9Fkz9BitDPnbyraReQocCEzUS9k8GBAWG1C50xy +InDpfmreEpRz7OBzbd3HA6X3J+8ns8+YOtpT2KEOyVV8VEluU1hmTct74TuTKtmx +DP77GG4eBgWnQU6Vgb2gdsX8en91qM/sO5q2cvlSZ42FlRtMdEy67NNJmvh8r7nj +KjDc9iM20OvI+Eg4BBga2xC9xc3ek56w4MsQI6/ZPMaqqYLiaSLNyug5CVYxH094 +SpNmucDDJ2REbr+VUypAJtjOTLCZQkPHVwIBAgICAOE= +-----END DH PARAMETERS----- diff --git a/angie/tls/ffdhe2048.pem b/angie/tls/ffdhe2048.pem new file mode 100644 index 0000000..9b182b7 --- /dev/null +++ b/angie/tls/ffdhe2048.pem @@ -0,0 +1,8 @@ +-----BEGIN DH PARAMETERS----- +MIIBCAKCAQEA//////////+t+FRYortKmq/cViAnPTzx2LnFg84tNpWp4TZBFGQz ++8yTnc4kmz75fS/jY2MMddj2gbICrsRhetPfHtXV/WVhJDP1H18GbtCFY2VVPe0a +87VXE15/V8k1mE8McODmi3fipona8+/och3xWKE2rec1MKzKT0g6eXq8CrGCsyT7 +YdEIqUuyyOP7uWrat2DX9GgdT0Kj3jlN9K5W7edjcrsZCwenyO4KbXCeAvzhzffi +7MA0BM0oNC9hkXL+nOmFg/+OTxIy7vKBg8P+OxtMb61zO7X8vC7CIAXFjvGDfRaD +ssbzSibBsu/6iGtCOGEoXJf//////////wIBAg== +-----END DH PARAMETERS----- diff --git a/angie/tls/ffdhe3072.pem b/angie/tls/ffdhe3072.pem new file mode 100644 index 0000000..fb31ccd --- /dev/null +++ b/angie/tls/ffdhe3072.pem @@ -0,0 +1,11 @@ +-----BEGIN DH PARAMETERS----- +MIIBiAKCAYEA//////////+t+FRYortKmq/cViAnPTzx2LnFg84tNpWp4TZBFGQz ++8yTnc4kmz75fS/jY2MMddj2gbICrsRhetPfHtXV/WVhJDP1H18GbtCFY2VVPe0a +87VXE15/V8k1mE8McODmi3fipona8+/och3xWKE2rec1MKzKT0g6eXq8CrGCsyT7 +YdEIqUuyyOP7uWrat2DX9GgdT0Kj3jlN9K5W7edjcrsZCwenyO4KbXCeAvzhzffi +7MA0BM0oNC9hkXL+nOmFg/+OTxIy7vKBg8P+OxtMb61zO7X8vC7CIAXFjvGDfRaD +ssbzSibBsu/6iGtCOGEfz9zeNVs7ZRkDW7w09N75nAI4YbRvydbmyQd62R0mkff3 +7lmMsPrBhtkcrv4TCYUTknC0EwyTvEN5RPT9RFLi103TZPLiHnH1S/9croKrnJ32 +nuhtK8UiNjoNq8Uhl5sN6todv5pC1cRITgq80Gv6U93vPBsg7j/VnXwl5B0rZsYu +N///////////AgEC +-----END DH PARAMETERS----- diff --git a/angie/tls/ffdhe4096.pem b/angie/tls/ffdhe4096.pem new file mode 100644 index 0000000..3cf0fcb --- /dev/null +++ b/angie/tls/ffdhe4096.pem @@ -0,0 +1,13 @@ +-----BEGIN DH PARAMETERS----- +MIICCAKCAgEA//////////+t+FRYortKmq/cViAnPTzx2LnFg84tNpWp4TZBFGQz ++8yTnc4kmz75fS/jY2MMddj2gbICrsRhetPfHtXV/WVhJDP1H18GbtCFY2VVPe0a +87VXE15/V8k1mE8McODmi3fipona8+/och3xWKE2rec1MKzKT0g6eXq8CrGCsyT7 +YdEIqUuyyOP7uWrat2DX9GgdT0Kj3jlN9K5W7edjcrsZCwenyO4KbXCeAvzhzffi +7MA0BM0oNC9hkXL+nOmFg/+OTxIy7vKBg8P+OxtMb61zO7X8vC7CIAXFjvGDfRaD +ssbzSibBsu/6iGtCOGEfz9zeNVs7ZRkDW7w09N75nAI4YbRvydbmyQd62R0mkff3 +7lmMsPrBhtkcrv4TCYUTknC0EwyTvEN5RPT9RFLi103TZPLiHnH1S/9croKrnJ32 +nuhtK8UiNjoNq8Uhl5sN6todv5pC1cRITgq80Gv6U93vPBsg7j/VnXwl5B0rZp4e +8W5vUsMWTfT7eTDp5OWIV7asfV9C1p9tGHdjzx1VA0AEh/VbpX4xzHpxNciG77Qx +iu1qHgEtnmgyqQdgCpGBMMRtx3j5ca0AOAkpmaMzy4t6Gh25PXFAADwqTs6p+Y0K +zAqCkc3OyX3Pjsm1Wn+IpGtNtahR9EGC4caKAH5eZV9q//////////8CAQI= +-----END DH PARAMETERS----- diff --git a/apt/prefs.backports b/apt/prefs.backports new file mode 100644 index 0000000..12cd445 --- /dev/null +++ b/apt/prefs.backports @@ -0,0 +1,27 @@ +Package: src:curl +Pin: release n=bookworm-backports +Pin-Priority: 600 + +Package: src:debhelper +Pin: release n=bookworm-backports +Pin-Priority: 600 + +Package: src:elfutils +Pin: release n=bookworm-backports +Pin-Priority: 600 + +Package: src:iproute2 +Pin: release n=bookworm-backports +Pin-Priority: 600 + +Package: src:libbpf +Pin: release n=bookworm-backports +Pin-Priority: 600 + +Package: src:systemd +Pin: release n=bookworm-backports +Pin-Priority: 600 + +Package: src:sysvinit +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..75c083a --- /dev/null +++ b/apt/sources.debian @@ -0,0 +1,11 @@ +Types: deb +URIs: http://deb.debian.org/debian +Suites: bookworm bookworm-updates bookworm-proposed-updates bookworm-backports +Components: main +Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg + +Types: deb +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..634a895 --- /dev/null +++ b/build-scripts/image-base.sh @@ -0,0 +1,50 @@ +#!/bin/sh +set -ef +cd "$(dirname "$0")/.." + +IMAGE_VERSION="${IMAGE_VERSION:-v0.0.1}" + +set -a +BUILDAH_FORMAT="${BUILDAH_FORMAT:-docker}" +BUILDAH_ISOLATION="${BUILDAH_ISOLATION:-chroot}" +BUILDAH_NETWORK="${BUILDAH_NETWORK:-host}" +set +a + +PYTHONTAG="${PYTHONTAG:-3.12.11-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:${IMAGE_VERSION}-base" + +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_SHA256 \ + + +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..9356990 --- /dev/null +++ b/build-scripts/image-deps.sh @@ -0,0 +1,19 @@ +#!/bin/sh +set -ef +cd "$(dirname "$0")/.." + +IMAGE_VERSION="${IMAGE_VERSION:-v0.0.1}" + +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:${IMAGE_VERSION}-deps" + +exec buildah bud \ + -f ./Dockerfile.deps \ + -t "${img}" \ + --pull=missing --no-cache \ + --build-arg "IMAGE_VERSION=${IMAGE_VERSION}" \ diff --git a/build-scripts/image.sh b/build-scripts/image.sh new file mode 100755 index 0000000..f0c758a --- /dev/null +++ b/build-scripts/image.sh @@ -0,0 +1,46 @@ +#!/bin/sh +set -ef +cd "$(dirname "$0")/.." + +IMAGE_VERSION="${IMAGE_VERSION:-v0.0.1}" + +set -a +BUILDAH_FORMAT="${BUILDAH_FORMAT:-docker}" +BUILDAH_ISOLATION="${BUILDAH_ISOLATION:-chroot}" +BUILDAH_NETWORK="${BUILDAH_NETWORK:-host}" +set +a + +ANGIE_VERSION="${ANGIE_VERSION:-1.9.1}" + +## 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 "${NGX_DEBUG:-1}" 1) +case "${NGX_DEBUG}" in +0 ) + img="docker.io/rockdrilla/angie-conv:${IMAGE_VERSION}-nodebug" + img_fq="docker.io/rockdrilla/angie-conv:${IMAGE_VERSION}-${ANGIE_VERSION}-nodebug" +;; +1 ) + img="docker.io/rockdrilla/angie-conv:${IMAGE_VERSION}" + img_fq="docker.io/rockdrilla/angie-conv:${IMAGE_VERSION}-${ANGIE_VERSION}" +;; +esac + +buildah bud \ + -f ./Dockerfile \ + -t "${img_fq}" \ + --env "ANGIE_VERSION=${ANGIE_VERSION}" \ + --env "NGX_DEBUG=${NGX_DEBUG}" \ + --pull=missing --no-cache \ + --build-arg "IMAGE_VERSION=${IMAGE_VERSION}" \ + +podman tag "${img_fq}" "${img}" diff --git a/doc/README.md b/doc/README.md new file mode 100644 index 0000000..7658c6c --- /dev/null +++ b/doc/README.md @@ -0,0 +1,3 @@ +#TBD + +[Examples](examples/README.md) diff --git a/doc/examples/README.md b/doc/examples/README.md new file mode 100644 index 0000000..3223727 --- /dev/null +++ b/doc/examples/README.md @@ -0,0 +1,9 @@ +# Examples + +- [simple static site](basic/README.md) +- [static site with templates](static-template/README.md) +- [print env via NJS](njs/README.md) +- [print env via Perl](perl/README.md) +- [SSL with subdomains](ssl/README.md) +- [generating config with templates](config-template/README.md) +- [container configuration override](j2cfg-override/README.md) diff --git a/doc/examples/basic/Dockerfile b/doc/examples/basic/Dockerfile new file mode 100644 index 0000000..e8c8470 --- /dev/null +++ b/doc/examples/basic/Dockerfile @@ -0,0 +1,4 @@ +FROM docker.io/rockdrilla/angie-conv:v0.0.1 + +COPY /site/ /etc/angie/site/ +COPY /static/ /etc/angie/static/ diff --git a/doc/examples/basic/README.md b/doc/examples/basic/README.md new file mode 100644 index 0000000..3a52697 --- /dev/null +++ b/doc/examples/basic/README.md @@ -0,0 +1,15 @@ +# simple static site + +consult [Dockerfile](Dockerfile) or [docker-compose.yml](docker-compose.yml) - both are simple and fine enough. + +--- + +configuration: + +```nginx +server { + listen 8080; +} +``` + +simple as that! :) diff --git a/doc/examples/basic/docker-compose.yml b/doc/examples/basic/docker-compose.yml new file mode 100644 index 0000000..5b35dec --- /dev/null +++ b/doc/examples/basic/docker-compose.yml @@ -0,0 +1,12 @@ +version: "3.8" + +services: + + angie-conv-example-basic: + container_name: angie-conv-example-basic + image: docker.io/rockdrilla/angie-conv:v0.0.1 + ports: + - "127.0.0.1:8080:8080" + volumes: + - "./site:/angie/site:ro" + - "./static:/angie/static:ro" diff --git a/doc/examples/basic/site/http-site.conf b/doc/examples/basic/site/http-site.conf new file mode 100644 index 0000000..8ff8ce6 --- /dev/null +++ b/doc/examples/basic/site/http-site.conf @@ -0,0 +1,3 @@ +server { + listen 8080; +} \ No newline at end of file diff --git a/doc/examples/basic/static/index.html b/doc/examples/basic/static/index.html new file mode 100644 index 0000000..da78911 --- /dev/null +++ b/doc/examples/basic/static/index.html @@ -0,0 +1,5 @@ + + +

Hello World

+ + \ No newline at end of file diff --git a/doc/examples/config-template/README.md b/doc/examples/config-template/README.md new file mode 100644 index 0000000..0add8ce --- /dev/null +++ b/doc/examples/config-template/README.md @@ -0,0 +1,113 @@ +# generating config with templates + +configuration: + +```nginx +{%- import 'snip/cache.j2mod' as ngx_cache -%} + +{%- set my_caches = (j2cfg.my_caches or []) -%} + +{%- for h in my_caches %} +{{ ngx_cache.proxy_cache_path(h.name, size='10m', levels='1:2', inactive=h.max_time) }} +{%- endfor %} + +server { + listen 8888; + + location / { return 204; } + + {%- for h in my_caches %} + location /{{ h.name }}/ { + proxy_pass {{ h.uri }}/; + + proxy_cache {{ h.name }}; + + expires {{ h.valid_time }}; + + proxy_cache_valid 200 {{ h.valid_time }}; + proxy_cache_valid any 30s; + } + {%- endfor %} +} +``` + +--- + +site configuration (via `j2cfg/my-caches.yml`): + +```yml +my_caches: + + - name: apt_debian + uri: https://deb.debian.org/debian + valid_time: 180m + max_time: 1440h + + - name: apt_debian_security + uri: https://deb.debian.org/debian-security + valid_time: 180m + max_time: 1440h + +## and so on ... +``` + +--- + +docker-compose.yml: + +```yml +version: "3.8" + +services: + + angie-conv-example-config-template: + container_name: angie-conv-example-config-template + image: docker.io/rockdrilla/angie-conv:v0.0.1 + restart: always + privileged: true + stop_grace_period: 15s + network_mode: host + environment: + NGX_HTTP_TRANSPARENT_PROXY: 1 + volumes: + - "./conf:/etc/angie:ro" + - "./data:/angie" +``` + +--- + +final configuration looks like this: + +```nginx +proxy_cache_path /run/ngx/cache/proxy_apt_debian + keys_zone=apt_debian:10m:file=/run/ngx/lib/proxy_apt_debian.keys + inactive=1440h + levels=1:2 +; + +# ... + +server { +# ... + + location /apt_debian/ { + proxy_pass https://deb.debian.org/debian/; + + proxy_cache apt_debian; + + expires 180m; + + proxy_cache_valid 200 180m; + proxy_cache_valid any 30s; + } + +# ... +} +``` + +--- + +Test URI e.g. with `curl`: +```sh +curl -v http://localhost:8888/apt_debian/dists/bookworm/main/binary-all/Release +``` diff --git a/doc/examples/config-template/conf/j2cfg/my-caches.yml b/doc/examples/config-template/conf/j2cfg/my-caches.yml new file mode 100644 index 0000000..389b21f --- /dev/null +++ b/doc/examples/config-template/conf/j2cfg/my-caches.yml @@ -0,0 +1,16 @@ +my_caches: + + - name: apt_debian + uri: https://deb.debian.org/debian + valid_time: 180m + max_time: 1440h + + - name: apt_debian_security + uri: https://deb.debian.org/debian-security + valid_time: 180m + max_time: 1440h + + - name: apt_ubuntu + uri: https://archive.ubuntu.com/ubuntu + valid_time: 180m + max_time: 1440h diff --git a/doc/examples/config-template/conf/site/http-my-cache.conf.j2 b/doc/examples/config-template/conf/site/http-my-cache.conf.j2 new file mode 100644 index 0000000..21c5687 --- /dev/null +++ b/doc/examples/config-template/conf/site/http-my-cache.conf.j2 @@ -0,0 +1,79 @@ +{%- import 'snip/cache.j2mod' as ngx_cache -%} +{%- set my_caches = (j2cfg.my_caches or []) -%} + +map $uri + $to_proxy_uri +{ + ~^/[^/]+/(.*)$ $1; +} + +map $request_method + $to_proxy_method +{ + default GET; + ## already handled by "proxy_cache_convert_head on;" (default setting) + # HEAD GET; + OPTIONS OPTIONS; +} + +## quirks + +chunked_transfer_encoding off; +proxy_method $to_proxy_method; +proxy_ignore_client_abort on; +proxy_ignore_headers Cache-Control Expires Set-Cookie Vary X-Accel-Buffering X-Accel-Expires X-Accel-Limit-Rate; + +## tuning + +proxy_cache_key $to_proxy_uri$is_args$args; + +proxy_cache_lock on; +proxy_cache_lock_age 20s; +proxy_cache_lock_timeout 25s; +proxy_cache_use_stale error timeout invalid_header updating http_429 http_500 http_502 http_503 http_504; +proxy_cache_revalidate on; + +{%- for h in my_caches %} +{{ ngx_cache.proxy_cache_path(h.name, size='10m', levels='1:2', inactive=h.max_time) }} +{%- endfor %} + +server { + listen 8888; + + location / { return 204; } + + location /console/ { + # allow 127.0.0.0/8; + # deny all; + + auto_redirect on; + + alias /usr/share/angie-console-light/html/; + index index.html; + + location /console/api/ + { + access_log off; + api /status/; + } + + location /console/api/config/ + { + access_log off; + api /config/; + } + } + + {%- for h in my_caches %} + location /{{ h.name }}/ { + proxy_pass {{ h.uri }}/; + + proxy_cache {{ h.name }}; + + expires {{ h.valid_time }}; + + proxy_cache_valid 200 {{ h.valid_time }}; + proxy_cache_valid any 30s; + } + {%- endfor %} +} diff --git a/doc/examples/config-template/data/cache/.gitkeep b/doc/examples/config-template/data/cache/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/doc/examples/config-template/data/lib/.gitkeep b/doc/examples/config-template/data/lib/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/doc/examples/config-template/docker-compose.yml b/doc/examples/config-template/docker-compose.yml new file mode 100644 index 0000000..928bb1e --- /dev/null +++ b/doc/examples/config-template/docker-compose.yml @@ -0,0 +1,16 @@ +version: "3.8" + +services: + + angie-conv-example-config-template: + container_name: angie-conv-example-config-template + image: docker.io/rockdrilla/angie-conv:v0.0.1 + restart: always + privileged: true + stop_grace_period: 15s + network_mode: host + environment: + NGX_HTTP_TRANSPARENT_PROXY: 1 + volumes: + - "./conf:/etc/angie:ro" + - "./data:/angie" diff --git a/doc/examples/j2cfg-override/Dockerfile b/doc/examples/j2cfg-override/Dockerfile new file mode 100644 index 0000000..827bb88 --- /dev/null +++ b/doc/examples/j2cfg-override/Dockerfile @@ -0,0 +1,13 @@ +FROM docker.io/rockdrilla/angie-conv:v0.0.1 + +COPY /j2cfg/ /etc/angie/j2cfg/ +COPY /site/ /etc/angie/site/ +COPY /static/ /etc/angie/static/ + +ENV NGX_HTTP_CONFLOAD='gzip' + +## same as above (adjusted to above variant by entrypoint): +## ENV NGX_HTTP_MODULES='gzip' + +## enable support for brotli and zstd: +## ENV NGX_HTTP_MODULES='gzip brotli zstd' diff --git a/doc/examples/j2cfg-override/README.md b/doc/examples/j2cfg-override/README.md new file mode 100644 index 0000000..1cf4ce9 --- /dev/null +++ b/doc/examples/j2cfg-override/README.md @@ -0,0 +1,32 @@ +# container configuration override + +consult [Dockerfile](Dockerfile) or [docker-compose.yml](docker-compose.yml) - both are simple and fine enough. + +--- + +mostly same as [simple static site](../basic/README.md) except container configuration file `j2cfg/override-compress-types.yml`. + +`j2cfg/override-compress-types.yml`: +```yml +compress_types: [] + +--- + +compress_types: +- text/plain +``` + +this effectively disables response compression for all mime types except `text/plain`. + +--- + +in order to enable (!) response compression specify environment variable `NGX_HTTP_CONFLOAD='gzip'` or `NGX_HTTP_MODULES='gzip brotli zstd'` (for gzip, brotli and zstd). + +--- + +Test URI e.g. with `curl`: +```sh +curl -v --compressed http://127.0.0.1:8080/index.html + +curl -v --compressed http://127.0.0.1:8080/index.txt +``` diff --git a/doc/examples/j2cfg-override/docker-compose.yml b/doc/examples/j2cfg-override/docker-compose.yml new file mode 100644 index 0000000..2afe015 --- /dev/null +++ b/doc/examples/j2cfg-override/docker-compose.yml @@ -0,0 +1,15 @@ +version: "3.8" + +services: + + angie-conv-example-cfg-override: + container_name: angie-conv-example-cfg-override + image: docker.io/rockdrilla/angie-conv:v0.0.1 + environment: + NGX_HTTP_MODULES: 'gzip brotli zstd' + ports: + - "127.0.0.1:8080:8080" + volumes: + - "./j2cfg:/angie/j2cfg:ro" + - "./site:/angie/site:ro" + - "./static:/angie/static:ro" diff --git a/doc/examples/j2cfg-override/j2cfg/override-compress-types.yml b/doc/examples/j2cfg-override/j2cfg/override-compress-types.yml new file mode 100644 index 0000000..1ca263d --- /dev/null +++ b/doc/examples/j2cfg-override/j2cfg/override-compress-types.yml @@ -0,0 +1,6 @@ +compress_types: [] + +--- + +compress_types: +- text/plain diff --git a/doc/examples/j2cfg-override/site/http-site.conf b/doc/examples/j2cfg-override/site/http-site.conf new file mode 100644 index 0000000..8ff8ce6 --- /dev/null +++ b/doc/examples/j2cfg-override/site/http-site.conf @@ -0,0 +1,3 @@ +server { + listen 8080; +} \ No newline at end of file diff --git a/doc/examples/j2cfg-override/static/index.html b/doc/examples/j2cfg-override/static/index.html new file mode 100644 index 0000000..da78911 --- /dev/null +++ b/doc/examples/j2cfg-override/static/index.html @@ -0,0 +1,5 @@ + + +

Hello World

+ + \ No newline at end of file diff --git a/doc/examples/j2cfg-override/static/index.txt b/doc/examples/j2cfg-override/static/index.txt new file mode 100644 index 0000000..0fed1a3 --- /dev/null +++ b/doc/examples/j2cfg-override/static/index.txt @@ -0,0 +1,5 @@ +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. diff --git a/doc/examples/njs/Dockerfile b/doc/examples/njs/Dockerfile new file mode 100644 index 0000000..b8dc082 --- /dev/null +++ b/doc/examples/njs/Dockerfile @@ -0,0 +1,6 @@ +FROM docker.io/rockdrilla/angie-conv:v0.0.1 + +COPY /site/ /etc/angie/site/ + +## load ngx_http_js_module +ENV NGX_HTTP_MODULES='njs' diff --git a/doc/examples/njs/README.md b/doc/examples/njs/README.md new file mode 100644 index 0000000..10a240c --- /dev/null +++ b/doc/examples/njs/README.md @@ -0,0 +1,47 @@ +# print env via NJS + +consult [Dockerfile](Dockerfile) or [docker-compose.yml](docker-compose.yml) - both are simple and fine enough. + +--- + +configuration: + +```nginx +server { + listen 8080; + + location / { return 204; } + + js_import ngx_env.js; + location = /env + { + js_content ngx_env.report; + } +} +``` + +--- + +NJS script: + +```js +function report(r) { + var s = ""; + const keys = Object.keys(process.env).sort(); + for (const i in keys) { + const k = keys[i]; + const v = process.env[k]; + s += k + '=' + v + "\n"; + } + r.return(200, s); +} + +export default { report }; +``` + +--- + +Test URI e.g. with `curl`: +```sh +curl http://127.0.0.1:8080/env +``` diff --git a/doc/examples/njs/docker-compose.yml b/doc/examples/njs/docker-compose.yml new file mode 100644 index 0000000..8d2d3f1 --- /dev/null +++ b/doc/examples/njs/docker-compose.yml @@ -0,0 +1,13 @@ +version: "3.8" + +services: + + angie-conv-example-njs: + container_name: angie-conv-example-njs + image: docker.io/rockdrilla/angie-conv:v0.0.1 + environment: + NGX_HTTP_MODULES: 'njs' + ports: + - "127.0.0.1:8080:8080" + volumes: + - "./site:/angie/site:ro" diff --git a/doc/examples/njs/site/http-env-js.conf b/doc/examples/njs/site/http-env-js.conf new file mode 100644 index 0000000..7656033 --- /dev/null +++ b/doc/examples/njs/site/http-env-js.conf @@ -0,0 +1,11 @@ +server { + listen 8080; + + location / { return 204; } + + js_import ngx_env.js; + location = /env + { + js_content ngx_env.report; + } +} \ No newline at end of file diff --git a/doc/examples/njs/site/ngx_env.js b/doc/examples/njs/site/ngx_env.js new file mode 100644 index 0000000..38892f3 --- /dev/null +++ b/doc/examples/njs/site/ngx_env.js @@ -0,0 +1,12 @@ +function report(r) { + var s = ""; + const keys = Object.keys(process.env).sort(); + for (const i in keys) { + const k = keys[i]; + const v = process.env[k]; + s += k + '=' + v + "\n"; + } + r.return(200, s); +} + +export default { report }; \ No newline at end of file diff --git a/doc/examples/perl/Dockerfile b/doc/examples/perl/Dockerfile new file mode 100644 index 0000000..ad02f0f --- /dev/null +++ b/doc/examples/perl/Dockerfile @@ -0,0 +1,11 @@ +FROM docker.io/rockdrilla/angie-conv:v0.0.1 +SHELL [ "/bin/sh", "-ec" ] + +COPY /site/ /etc/angie/site/ + +## install 'angie-module-perl' and process package contents +RUN apt-install-angie-mod.sh perl ; \ + apt-clean.sh + +## load ngx_http_perl_module +ENV NGX_HTTP_MODULES='perl' diff --git a/doc/examples/perl/README.md b/doc/examples/perl/README.md new file mode 100644 index 0000000..332264e --- /dev/null +++ b/doc/examples/perl/README.md @@ -0,0 +1,70 @@ +# print env via Perl + +Dockerfile: + +```dockerfile +FROM docker.io/rockdrilla/angie-conv:v0.0.1 + +COPY /site/ /etc/angie/site/ + +## install 'angie-module-perl' and process package contents +RUN apt-install-angie-mod.sh perl ; \ + apt-clean.sh + +## load ngx_http_perl_module +ENV NGX_HTTP_MODULES='perl' +``` + +--- + +configuration: + +```nginx +perl_require ngx_env.pm; +server { + listen 8080; + + location / { return 204; } + + location = /env + { + perl ngx_env::report; + } +} +``` + +--- + +Perl script: + +```perl +package ngx_env; + +use nginx; + +sub report { + my $r = shift; + + my $s = ""; + for (sort keys %ENV) { + $s = $s . "$_=$ENV{$_}\n"; + } + + $r->discard_request_body; + $r->send_http_header; + $r->print($s); + + return OK; +} + +1; + +__END__ +``` + +--- + +Test URI e.g. with `curl`: +```sh +curl http://127.0.0.1:8080/env +``` diff --git a/doc/examples/perl/site/http-env-perl.conf b/doc/examples/perl/site/http-env-perl.conf new file mode 100644 index 0000000..45082d4 --- /dev/null +++ b/doc/examples/perl/site/http-env-perl.conf @@ -0,0 +1,11 @@ +perl_require ngx_env.pm; +server { + listen 8080; + + location / { return 204; } + + location = /env + { + perl ngx_env::report; + } +} \ No newline at end of file diff --git a/doc/examples/perl/site/ngx_env.pm b/doc/examples/perl/site/ngx_env.pm new file mode 100644 index 0000000..fb86cec --- /dev/null +++ b/doc/examples/perl/site/ngx_env.pm @@ -0,0 +1,22 @@ +package ngx_env; + +use nginx; + +sub report { + my $r = shift; + + my $s = ""; + for (sort keys %ENV) { + $s = $s . "$_=$ENV{$_}\n"; + } + + $r->discard_request_body; + $r->send_http_header; + $r->print($s); + + return OK; +} + +1; + +__END__ \ No newline at end of file diff --git a/doc/examples/ssl/Dockerfile b/doc/examples/ssl/Dockerfile new file mode 100644 index 0000000..171efcb --- /dev/null +++ b/doc/examples/ssl/Dockerfile @@ -0,0 +1,15 @@ +FROM docker.io/rockdrilla/angie-conv:v0.0.1 + +COPY /site/ /etc/angie/site/ +COPY /static/ /etc/angie/static/ +COPY /tls/ /etc/angie/tls/ + +ENV NGX_HTTP_CONFLOAD='ssl' + +## same as above (adjusted to above variant by entrypoint): +## ENV NGX_HTTP_MODULES='ssl' + +## serve with HTTP/2 (disabled by default): +## ENV NGX_HTTP_CONFLOAD='ssl v2' +## or +## ENV NGX_HTTP_MODULES='ssl v2' diff --git a/doc/examples/ssl/README.md b/doc/examples/ssl/README.md new file mode 100644 index 0000000..69b2605 --- /dev/null +++ b/doc/examples/ssl/README.md @@ -0,0 +1,67 @@ +# SSL with subdomains + +consult [Dockerfile](Dockerfile) or [docker-compose.yml](docker-compose.yml) - both are simple and fine enough. + +--- + +configuration: + +```nginx +server { + listen 8443 ssl; + + server_name www.example.org; + + ssl_certificate tls/www.example.org.chain.crt; + ssl_certificate_key tls/www.example.org.pem; + + root static/www.example.org; +} +``` + +--- + +configuration for wildcard certificate: + +```nginx +server { + listen 8443 ssl; + + server_name .example.org; + + ssl_certificate tls/example.org.chain.crt; + ssl_certificate_key tls/example.org.pem; + + root static/example.org; +} +``` + +*Note: certificate must have* `X509v3 Subject Alternative Name` *property with value like* `DNS:example.org, DNS:*.example.org` . + +--- + +(optional) configuration for cut-off SSL server block (see [documentation](https://angie.software/en/configuration/modules/http/http_ssl/#ssl-reject-handshake) for rationale): + +```nginx +server { + listen 8443 ssl default_server bind deferred; + + server_name _; + + ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; + + ## reject connections early + ssl_reject_handshake on; +} +``` + +--- + +Test URI e.g. with `curl`: +```sh +curl --insecure --resolve example.org:8443:127.0.0.1 https://example.org:8443/ + +curl --insecure --resolve www.example.org:8443:127.0.0.1 https://www.example.org:8443/ + +curl --insecure --resolve test.example.org:8443:127.0.0.1 https://test.example.org:8443/ +``` diff --git a/doc/examples/ssl/demo-ca/0-CA-Root.crt b/doc/examples/ssl/demo-ca/0-CA-Root.crt new file mode 100644 index 0000000..1c7284c --- /dev/null +++ b/doc/examples/ssl/demo-ca/0-CA-Root.crt @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDBjCCAe6gAwIBAgIITliyKcJbVmEwDQYJKoZIhvcNAQELBQAwEjEQMA4GA1UE +AxMHQ0EgUm9vdDAeFw0yNDA5MTkwMDAwMDBaFw0zNjA5MTgyMzU5NTlaMBIxEDAO +BgNVBAMTB0NBIFJvb3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDP +0HWgtUwQ+OMm30ANf8Iy4H3tfGnfrDd4oQXqMjuW6Eh0nxzlWMIcvrN1l2Y2QscI +i+/6CNq6tirbkN3PIYFdboejROXPDRsh7ck+92PyJiEcbK0SbI/S/3bKGpeqmTy+ +HvbkMvzlUJ/+SH6FgU3sCkYga43QDE8DT3PRf0zd7mBF2ij/OXtv69JehdTJBDa2 +hW09Ivjfq5cHoMIEfIvTp8847TGIQDqU8k1N8A5brrU+2gHJ+H3GoV09ej5/cv6Q +9FU9DE/mTW7iDHjNVNgq4JQXJWyCYH6TfoKet+/8Q1odhe+4dG22lO6EgHdp5IIN +J5322FKKsuwZ1JhA/ZJ9AgMBAAGjYDBeMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0O +BBYEFDHRuuHS1LwoRcTtUhLcp+DaEa/IMAsGA1UdDwQEAwIBBjAfBgNVHSUEGDAW +BgorBgEEAYI3CgMBBggrBgEFBQcDCTANBgkqhkiG9w0BAQsFAAOCAQEAxRGAJ3nV +/ycyml5mm4q330Mnsa8Rc0DoVaQXfEyIIBkgYD2dIhvjnA5cK6AVStAJ/16lx77T +v5bG5/AyC2D7ISd8PLcpWrAtxo06cYM3OJjpWwl18oH1tS1L2hi6L8I2LNkW4TKQ +yFjRCYJvsM2QUnRL99S4JKiXACDMCTP/ZP87fQvmfi4lXCnUlQqgtnCq0+iCwXVJ +oR1SdOrmPz/NI23RA41U15LePwFuK5cTE0WhtyZej8ksv6V+5Z1aiIBTt/cMl+KH +2K9dmO+dNp1DJeSaeH+8rsDd44FkPvDi1nMjm4G51U2JVrbjift70DM/Ia/DPH72 +bYJLgeFDhdfzMg== +-----END CERTIFICATE----- diff --git a/doc/examples/ssl/demo-ca/0-CA-Root.key b/doc/examples/ssl/demo-ca/0-CA-Root.key new file mode 100644 index 0000000..efef163 --- /dev/null +++ b/doc/examples/ssl/demo-ca/0-CA-Root.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEogIBAAKCAQEAz9B1oLVMEPjjJt9ADX/CMuB97Xxp36w3eKEF6jI7luhIdJ8c +5VjCHL6zdZdmNkLHCIvv+gjaurYq25DdzyGBXW6Ho0Tlzw0bIe3JPvdj8iYhHGyt +EmyP0v92yhqXqpk8vh725DL85VCf/kh+hYFN7ApGIGuN0AxPA09z0X9M3e5gRdoo +/zl7b+vSXoXUyQQ2toVtPSL436uXB6DCBHyL06fPOO0xiEA6lPJNTfAOW661PtoB +yfh9xqFdPXo+f3L+kPRVPQxP5k1u4gx4zVTYKuCUFyVsgmB+k36Cnrfv/ENaHYXv +uHRttpTuhIB3aeSCDSed9thSirLsGdSYQP2SfQIDAQABAoIBADAlMD9DiWQsOToO +AlGuTeBKHLqato+cnzxZ99wWd7JCTdkA7OmgitYsgzik0wgSp/htrTL8/qm/nwW/ +1feRIF60RwXXJjO2KllNtPBa0cOtvEGQ1vb8AkUkrNFtYEJknotAcrwTKP56k5qO +kotdFd+v0KfdqxzuYRbX/zHjv3vxvQ/GxPAjyliAfrY+kt4I7tL30ZSCnLbmUEE5 +22v5zAeehi5QnFx6P3jnuv6f5Nn84fl0BV5xHOVrAj3WHRVb5UiHrZc1N+A2hKAC +5SPYwLpf5RYQ4oJFCeUL3zZSSCSQnwa3jcSmCMV3flqL7ZsDI+EWJ9PwAEb1t7HF +gCAF1skCgYEA+iXXOB0IR5N1nHVVGUWJSCL8JihDE/xyw1EbXGJPfz2648R7sOEq +RqjnUNa7ODPlZ09Mf0zFhbWxAVEw4lCH++ZHCAw5JQ8mWCXwqd4zbcjJ6jzHtnn5 +U7JoEwPfPXZ5bx1avSaXHoOjgHXeGCMuLEF2faJd8iqnGh9epGq4PEkCgYEA1K0U +SovG/1OoJw13LK5BQhSdcko5B03kAKROMVA6tvOekCYGC+MAqJtj/+lxkbLlTEtr +FUzFdAIv+djU7yd0CxGcsiic25AKUq/Ko3Etxe89agpI3I+kSfb2zzOiRqAPyYBP +AF9Crm6uJFZdATc2j/60DIElFlhD2qdDO6rWnJUCgYAhQqrNMT8KlVbMCOXZyF8q +kkxIno3cuHJh+gTTUdcUKhcRdeykZiwC3S50lzipjmzwQaEARCr3TmNMs4j2bpLG +MaY8MbPfc5Y7nj/TtlHMRShj1tUPNESslko0TQ1/1KLs3VBVWi45xnMU/5caSoM3 +KzUgG1i2fGlfldA1uGLq0QKBgFENuasDhI7wwihIEIBd1Q8rLipNsVhgTiIUfJx8 +uDPbuzWy2CEVnb2ko0L4JElkBdHC+IfAn4wr/T7abaTLw4UobDDWG5nuVpDW4ILT +8p76I8zTKJuuvu1VixDC2/jQrdOc6/73T0GNex7sLzv0X/4XE2Wkno7aitm9X3lR +DcPVAoGAUvleRG0hFMOFJH6jdX61/FW9tPZc+nPY3JXNYVvQLj9oEArgoT6crKfa +cBgRLYd3AZUfz6CY+8Ln81oNKWM2iTkv/+y8Mtt35r1+GeBHXvfgsDcJmKsaZ+Oh +/avdDrMT9UnLbIImYmhmixrMrypGtBMN5f2EVQZmk2CGe1lG5rw= +-----END RSA PRIVATE KEY----- diff --git a/doc/examples/ssl/demo-ca/1-CA-Internal.crt b/doc/examples/ssl/demo-ca/1-CA-Internal.crt new file mode 100644 index 0000000..7843986 --- /dev/null +++ b/doc/examples/ssl/demo-ca/1-CA-Internal.crt @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDLDCCAhSgAwIBAgIIc9z+Nze+1ngwDQYJKoZIhvcNAQELBQAwEjEQMA4GA1UE +AxMHQ0EgUm9vdDAeFw0yNDA5MTkwMDAwMDBaFw0zNTA5MTgyMzU5NTlaMBYxFDAS +BgNVBAMTC0NBIEludGVybmFsMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC +AQEAs0SnuACpBbkmoPrdnrMKuGhA+dUML/PoF8RZI9BdQvnSa3r1mINw3442Hcrh +Nij0BeVqjK6DTk8yrLA3l9/bpXIubimXDWvzeMRRVduWPdEc9cdDhuksbrIApQow +3cP68U95pqwYbDsXtGqXOzDWnKQppok42OjaKL6zwNsM6Qs/UKVADJ7rmPSoZSa/ +RCywhurnZt3eIDQjQqfJCnNifUXnLOD8JwyhSACBvxdQQnn2ibh78KA6LuECUDX9 +jKOdgJvffwl1XaXqX9pfM9KwmoNs+utVOm9weENC0tnss/BftqzBo6szAeyIKzkk +xOjppCNz2Uou3UsVEVyCA6GAjQIDAQABo4GBMH8wDwYDVR0TAQH/BAUwAwEB/zAd +BgNVHQ4EFgQUSsSalxeTY1qmvFLILIS3gZ4ynGMwHwYDVR0jBBgwFoAUMdG64dLU +vChFxO1SEtyn4NoRr8gwCwYDVR0PBAQDAgEGMB8GA1UdJQQYMBYGCisGAQQBgjcK +AwEGCCsGAQUFBwMJMA0GCSqGSIb3DQEBCwUAA4IBAQDJyKWDN6lD4/UTx12HrVqI +taOkOzFIBho+FSvCRI/ZLpeMj01WZIb9XkdhLZvUAh+c7jC/caMghGX5N8Kqunmr +x1HYnLm+C6QOdYy2djEml3ZwnbEn9yT1YYhRIZC993ipEzeNFm39J433l1PXYsLa +XNC99j58tVPFELcpimqe8eoUW2hYKZqFvswuta2PhX9mNYOSVk5ICl3rs0kr8gDR +3PC6vKmMxmTWTlg94JuTRCT0L5LD5Ode76iR7q0TY3XOzeDeEw3H99nPv3i69d2D +15pEo78xeNOZhbJ1OGUqBO45JrwhhJ4x9N+5SSnLSEvgv+qghVK+mkxAtvM/6fsp +-----END CERTIFICATE----- diff --git a/doc/examples/ssl/demo-ca/1-CA-Internal.key b/doc/examples/ssl/demo-ca/1-CA-Internal.key new file mode 100644 index 0000000..d40322c --- /dev/null +++ b/doc/examples/ssl/demo-ca/1-CA-Internal.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEAs0SnuACpBbkmoPrdnrMKuGhA+dUML/PoF8RZI9BdQvnSa3r1 +mINw3442HcrhNij0BeVqjK6DTk8yrLA3l9/bpXIubimXDWvzeMRRVduWPdEc9cdD +huksbrIApQow3cP68U95pqwYbDsXtGqXOzDWnKQppok42OjaKL6zwNsM6Qs/UKVA +DJ7rmPSoZSa/RCywhurnZt3eIDQjQqfJCnNifUXnLOD8JwyhSACBvxdQQnn2ibh7 +8KA6LuECUDX9jKOdgJvffwl1XaXqX9pfM9KwmoNs+utVOm9weENC0tnss/BftqzB +o6szAeyIKzkkxOjppCNz2Uou3UsVEVyCA6GAjQIDAQABAoIBABwMEkeGBuvHz4Th +KvpQ3c0DNqM/02PPP9E0HZQuXeEEMAkz2Cfv7KF1YP8hRkIQfzlK7vQqu41tc6qz ++UawNe+5IQ2IQUNOz+1lnaoWrHdod6T2c5iwc4ywGcy4fvO7XVAS1KAgjcOlLSzD +fny6w+EHCCMvle7N44/7Yik6vFbbcThUiaHhg52rfWVkxydEzSRLj/SnBwQTGuk2 +vE/N2W3OkRWGpKmdjnrRsCJhF3XbHBqMQq9NoTxRUV9Uil2iGIvggW4cqT6hc0w6 +gC+P/9/5atpOlbtKgEDMCMUM+ltwgDN12SnYwJDbp/pXNm6v7818bccueJ+W8KTX +kgRyF9ECgYEA+7/nRzFrFAkXItlASjuhtbachwu0bXBBYJMADHtN5V8scosX43vp +0Q150W6b8pJB/HYCrCpVv+9tSlT20PPwQR/UcpZKrEWkuAcNjQs/UNSRuZ2qyibc +nIp+jk9Rdt86BEj5UFmiylIUnHsHgW6O3tE+phedK0zH11d6mdwzT1kCgYEAtkt6 +GZpFnmH8VFOoU006fPPETVnNNSNkn4ysNHzRC1OBDynWhiFWUW+23SdRaayCYTl9 +IWpUTRVuW8Y4B1qO+rUd1C68+p8FlIaFFIT5Z1bCJ7e/M0hl6TSuIhrNa/ItUmEB +Ax330guUVr9IIGZiROliIcSdoGf9T9UqhvG8aFUCgYAmZt6TuJEZ7E0QLs2kxTXk +rydvXjS2oPIIFkRiowh93ae9DUSmmcdP8VtMvC+jr/XK2gGMW6Ta813bgdxogV17 +waw5kn1vi+wVelXx1u5gmRxlkQx1a7opUuL9OFI37NM/xhXp0NKJRD4KpKW/c6rt +iEOjOGTsLvko+xojkDhveQKBgFY9Rrot/Zl8CX9rREqEUpMiT0+4mBf3cnb4ec8q +7UpKatfdlxtFUiyciQn+u8keT1/nbocMYm1FIjxQfdkcwl9gp4flxIlcCavGJ9cZ +QVPd+2QGzXFZYrz8qxR/UYcrvr0mHvB2kPLRf4+6VkjdpserET2gYmGsUG4gDkpg +uh0xAoGBAOcZk8EGQUiesG8mk3r1ylVpxjlpbVyQ0dwuehSsyKqYlFDAF/TWF6EQ +1k1GjwjXZmL5FuOhW1Ozh5m1kkg0tBW2jCevniRzLrUzBFRImuwfrOHH6FYyXBBs +q3+fn1htEiDB3xelFGPyFEMzUrEvUQNU3jkiypR8JNPoE09X5XtD +-----END RSA PRIVATE KEY----- diff --git a/doc/examples/ssl/demo-ca/2-example.org.crt b/doc/examples/ssl/demo-ca/2-example.org.crt new file mode 100644 index 0000000..aeb47ad --- /dev/null +++ b/doc/examples/ssl/demo-ca/2-example.org.crt @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDXTCCAkWgAwIBAgIIKr9NphMObcAwDQYJKoZIhvcNAQELBQAwFjEUMBIGA1UE +AxMLQ0EgSW50ZXJuYWwwHhcNMjQwOTE5MDAwMDAwWhcNMzQwOTE4MjM1OTU5WjAW +MRQwEgYDVQQDEwtleGFtcGxlLm9yZzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC +AQoCggEBAJ+WCrO6ML91DNYfgzrc6XiZvOFNxBfbuPkPdFw6dx4wwJCFg0VcdH1W +BpVMMvczlntphpARGZ+bsktKRhsbDHNfbExo6Sn6b0x/xnUkFg34ukYhRdFs+xHC +/PO9t2a9LDcMsBr0yLBugbMYGZj2Ln+sp478aKyWNkQLBKOEfeijtg3qrIx0B/we +vd8Tx49ahQYB8XELiZa1mntqRpHewMr+ul6sf5z6JR3Jrokvzu7kGLjt1FN1VAQR +pkzqNYJX/vUJ3KIdZxWHyzyoIbra/VjeP/POIY22eQGDWwbg9sNVUyVKFPzkaSwV ++BAytQiEn2cEQtmxoPZ+iLkyiGgGFWUCAwEAAaOBrjCBqzAMBgNVHRMBAf8EAjAA +MB0GA1UdDgQWBBRrZV8OY2075agO7UQfsDcPKmt87TAfBgNVHSMEGDAWgBRKxJqX +F5NjWqa8UsgshLeBnjKcYzALBgNVHQ8EBAMCA/gwJwYDVR0lBCAwHgYIKwYBBQUH +AwEGCCsGAQUFBwMCBggrBgEFBQcDCDAlBgNVHREEHjAcggtleGFtcGxlLm9yZ4IN +Ki5leGFtcGxlLm9yZzANBgkqhkiG9w0BAQsFAAOCAQEApijiwX+Y6XR25Z0yyv9P +gAkZkE+X/rQuk+9PyuSXLWUg9x0p3G0RUwPHHwiUpYHnTmAf3iKoPHLltX+KxqkO +W3Kx07TId5FmK8UrCZ+Xs1yuvMHtwdAvf8DA8QCaWSa8N9QeYmbzArjM8035j16+ +rFiMaO8mLEftqnVZksYt3pPWsus0UnhK9gnTw9PSxqWpC1EoTyiuDwTLdVqqYAeM +oqqdpHfSFPBXmCRZc5dbptnrJmLiMHoVeeKjdYXLr1GgIVYPN+Dbldwb8gcIQ+TM +zN+J7p0W7rHhsGSleackNlWWfodjnc0WHZWkyplg4W48V6KbLxsK+LOpvnuoy+mu +vw== +-----END CERTIFICATE----- diff --git a/doc/examples/ssl/demo-ca/2-example.org.pem b/doc/examples/ssl/demo-ca/2-example.org.pem new file mode 100644 index 0000000..192ffd7 --- /dev/null +++ b/doc/examples/ssl/demo-ca/2-example.org.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEogIBAAKCAQEAn5YKs7owv3UM1h+DOtzpeJm84U3EF9u4+Q90XDp3HjDAkIWD +RVx0fVYGlUwy9zOWe2mGkBEZn5uyS0pGGxsMc19sTGjpKfpvTH/GdSQWDfi6RiFF +0Wz7EcL88723Zr0sNwywGvTIsG6BsxgZmPYuf6ynjvxorJY2RAsEo4R96KO2Deqs +jHQH/B693xPHj1qFBgHxcQuJlrWae2pGkd7Ayv66Xqx/nPolHcmuiS/O7uQYuO3U +U3VUBBGmTOo1glf+9Qncoh1nFYfLPKghutr9WN4/884hjbZ5AYNbBuD2w1VTJUoU +/ORpLBX4EDK1CISfZwRC2bGg9n6IuTKIaAYVZQIDAQABAoIBAA52Ufz3VCCdp8P7 +Mht9AU7Txolie0awO63yfRiN7H/uFMgOxBaJP5NLiagxB3Nd7Pa9LvEnuOXn0xC9 +/Twf8ju9u4+ceE48wFEInqsR/J+tLpsEET2JPfgzmVSGGQn0qH5KpjujJabQ35cj +3s9SYWS3owMIaSdZgOHKCn8TwYykriGYgagV3c+tMdqDaqvrrWAu9mkCp9/MzREb +X3XBJ9NE/5dlMeMfXJiRKvLc16hQCSrXVdCLLm3U3/sHncDKOEOF4kvEBR5ciXXN +zXkuvbE4GKjC0rCGZtocbC2EkkHRMD96Y2cNwmNnWp0fJ0KOVYGw3S3YsWNMex0u +n57vEUcCgYEAy+x9NB4pCF/5Va82yRYYRUZCyDWLAYAC7kJ933R2wugUgs8FUQzK +xh0d7PuGDYB/zrsZfdQoZGmEbffZ/pze6pdIpaFKFnQoHPtS92aQdBPwyCdBnJUy +cuVQekPBsDtRFSOaznzOej+bkFH43RujYjDPPRAIP2Oily20CRcXzb8CgYEAyFb6 +X2mQn2EM+vIxGIIUe09YsT9OhZyzI2TG4M4yvapFF8FVA5qZJTQh7mxJzlACev3J +Rmrpo/36j4co9x8Ph9ojcQzd0qhiwYPaFc4sBjFRPm8k22+mf2zIm5VV1xHqML65 +ciGGCLxfBO+j1y0ktUL6g9QGwyr/0RaFo7UhrdsCgYBcEyipGqEeVe9Hn/hVrTNC +PCo1Ke/cuocYO0+IUJa7BH0WXxEgem0oLMdxVFQ/znBm0JX++YdPZ1FTMeDtFLmW +JL65gmzoXIQsKdJZQKcisko6pXb7k2YW+LFwsx1GTFIdAFmKuFGmYwgDju+WLj+E +O1OnV5DRxlQIfKtYm2O1EwKBgHb1hrgPFAw/cZi1JUf7PbQ22mBtSe/2qzxyA9HL +Pr/2kg5YA2Yfb55yxU8wx+aVBdQITHLe2xtAnX6KcF3E+NDfS7o+PJ1w1Ss5Ys8d +3HBU6nwbPRR7yK7TZo9T7mPFxHzrU2yc0Vzwla91qKEFsk1blyueQ+Gx77M1H2el +D3mPAoGAfd9V3aNVaNjjj/ILQlX6ypPeqWLjzsHRnsIedDXgGRh5/ZgXetSgp+f9 +MtZdaHkYiBMYJJXFqn3KcIaRGAgxQbaqnZtSmHpUg4AXDmxEbu01ryVjPIW/EvZb +4oTikKQyUW8jqWS3irvxZga+nwJFNNYMyVgK+W29UYZzW53GMV4= +-----END RSA PRIVATE KEY----- diff --git a/doc/examples/ssl/demo-ca/3-www.example.org.crt b/doc/examples/ssl/demo-ca/3-www.example.org.crt new file mode 100644 index 0000000..e65e3ae --- /dev/null +++ b/doc/examples/ssl/demo-ca/3-www.example.org.crt @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDVjCCAj6gAwIBAgIIKpxUKA9KjPcwDQYJKoZIhvcNAQELBQAwFjEUMBIGA1UE +AxMLQ0EgSW50ZXJuYWwwHhcNMjQwOTE5MDAwMDAwWhcNMzQwOTE4MjM1OTU5WjAa +MRgwFgYDVQQDEw93d3cuZXhhbXBsZS5vcmcwggEiMA0GCSqGSIb3DQEBAQUAA4IB +DwAwggEKAoIBAQC7IBurNJ8FFtPgMBX8n8wQuAypCfFucM3reIblnPpHsWifRlk/ +XrOmD1FDhErz4V38ouloS7q4jxAbbrPlLW93u4En5UZ5jPzN3T2h0vIrPl6sCKf1 +edEju5lu7WIcNUH8VF4j6kDP71LwBW99kdFrYHSRCtWIXeJrB1MLx7c9lekrm89/ +lz08UdGd7udJOcKDrsvsIj4cJYImHENLYB4LoWVMsiD7ap+zKud9Zf9YsRNG7XTT +HtZSTdrgN5opAU/0vA10Jzp1S3HDfPL5+7Xw6Xq62ZJXOyAofpItxc9dGOOoNiZi +wtYCMZwz2H3LujDgc5wMbPV9k1UxU/Y6QHrjAgMBAAGjgaMwgaAwDAYDVR0TAQH/ +BAIwADAdBgNVHQ4EFgQUDhDi+m6Iocrd9LJ6ZJBFMfsp3F0wHwYDVR0jBBgwFoAU +SsSalxeTY1qmvFLILIS3gZ4ynGMwCwYDVR0PBAQDAgP4MCcGA1UdJQQgMB4GCCsG +AQUFBwMBBggrBgEFBQcDAgYIKwYBBQUHAwgwGgYDVR0RBBMwEYIPd3d3LmV4YW1w +bGUub3JnMA0GCSqGSIb3DQEBCwUAA4IBAQBr03L9bIY5unzvN8psu+a410Gwgkma +8D37SeJ23fV0FR0gemgIJsq0SoVRuwbHQMum1Rs9MC+fRIcN5UZGKDTb6WQIb+In +1qnX3A5OU/rTOjkWELQLxJ+ejqJT86pHuODwpX+YME1nDo+3nmb/OLAzrXjgfY3j +w5GCU4dobXU3RvbQAbvpw4ECOBPbuizq+fngIGmeJt7kcdJ6vZw3OvKlk451REGI +gt3TrELsmvH7D2qNyPYgEn7ifdVKEbiMcFcHoMz/zZ2ZxlGSQ7YnWOZd0++uB2od +iBWE+faUZ2ApEWEnX1FPcPIQ7x7dDvYHkmGVh0tAwXcgPs1NCX7rJcVF +-----END CERTIFICATE----- diff --git a/doc/examples/ssl/demo-ca/3-www.example.org.pem b/doc/examples/ssl/demo-ca/3-www.example.org.pem new file mode 100644 index 0000000..ff0da53 --- /dev/null +++ b/doc/examples/ssl/demo-ca/3-www.example.org.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEAuyAbqzSfBRbT4DAV/J/MELgMqQnxbnDN63iG5Zz6R7Fon0ZZ +P16zpg9RQ4RK8+Fd/KLpaEu6uI8QG26z5S1vd7uBJ+VGeYz8zd09odLyKz5erAin +9XnRI7uZbu1iHDVB/FReI+pAz+9S8AVvfZHRa2B0kQrViF3iawdTC8e3PZXpK5vP +f5c9PFHRne7nSTnCg67L7CI+HCWCJhxDS2AeC6FlTLIg+2qfsyrnfWX/WLETRu10 +0x7WUk3a4DeaKQFP9LwNdCc6dUtxw3zy+fu18Ol6utmSVzsgKH6SLcXPXRjjqDYm +YsLWAjGcM9h9y7ow4HOcDGz1fZNVMVP2OkB64wIDAQABAoIBAD+Cd0GVO397ru+B +AoVaKuVlwg5BLKsCKDGKF6aor51TjiG4u6OxXaG3wyn6JYI+dCrBlBxsz3PCQoI9 +AVuAHzvw7LYAr/mjK04nj7pzoPOiWHlk+rRq4tuQ2VN3x/uw67NbYxQndlXccTa7 +cYqZygz6kLWFitGco7MVqk7uOrfwqu4O5GbktR4Vm6apEh2eFsSfgFE4LN3z7bbf +A7dzePBy2BOEvy/CjNX5stkLKJuWzuVTcYXB82bqp/VsSXSQG9o/9VmR6OUhXWjA +Clf+m3HqVmZ1IivOFz48LHVzvUj26AclvwwXkaGptbA6s08QWJMS2mpZlWbWNwzr +Mqgl/3ECgYEA8xpGCQrxfw7LTI9sMVnQo3S1rbB23DMuP2PmX6Aad0U3eGSxrv6B +RweXpb1Kduu8FeLMngihPjv/UsHJkNMX6IRILc2kLbRYoQOLdJ5k42bYzhDKYN33 +e8jr6twJJsCgva4DoLM0woZTvgaRzLJBzMWoL1BT6JJkKGUukgwCiskCgYEAxQ2S +1oN1ZHc454y2bJ0JYg6c/MGEKHFjIRLKMMxwhNXQzOr8EzzwwF+5IsJORQfJNu+P +DaFT/3QAuByKG+Dyx4C0ssIhj6u75g1Thzgv8qHwE6DYh1VUUYO656kFTtexlFyg +gJPYXCOWPelSNNNQXbZTV929R1Wx95+LfqLqQksCgYEApmCj8ApqT3AbmVFLVddH +YKc+tBnirz/j9gR0JZwYoOphVWds5/xNFATRN+B+NzeNKVloevwjBsnfK49vWUvv +v/XQxHBKXfFg+wnHBtBk8fFwjk+VgohHmZNgSwO+y6PoHwkaeIBNqphudc5fOL4D +JJdeTMtoMfMG01K0dcX8c2kCgYEAwE1n0GqIJNxoryfWW5bBMm2abNwZsjI9kGg6 +43aQFEJpu6FTER82wDZqgW5oXdukVTViQOYBCFpX3VUUvvI/W8zSC2WCxSfOfkrh +CiQePsYkebNNvHzchZRt0WhUYsYCagwfInul+P1NwOuzKxRR6LJnEWe3MSeDP2n3 +A0XQIZMCgYAzubLPZnJjFihAX0M9k8Cjc+q6KKnA6Fp1JqnPmzoTO/r46o1shmCZ +kRS8iqnKfTCW/MWGSPyRc0OubIVbR9hAdCZjR8wmeVdkiV+VfBRzxpcYpcZbxjmy +6F0xz1Fv0UeKdjHQyb9UNO6Y1qVaNVVYo3tyD6VGaMdboddHPPxLDw== +-----END RSA PRIVATE KEY----- diff --git a/doc/examples/ssl/demo-ca/Makefile b/doc/examples/ssl/demo-ca/Makefile new file mode 100644 index 0000000..91c17a0 --- /dev/null +++ b/doc/examples/ssl/demo-ca/Makefile @@ -0,0 +1,12 @@ +#!/usr/bin/make -f + +intermediate_ca_certs := 1-CA-Internal.crt + +certs := $(patsubst %.pem,%.crt,$(sort $(wildcard *.pem))) +target_certs := $(patsubst %.crt,%.chain.crt,$(certs)) + +.PHONY: all +all: $(target_certs) + +%.chain.crt: %.crt $(intermediate_ca_certs) + cat $+ | tee $@ >/dev/null diff --git a/doc/examples/ssl/docker-compose.yml b/doc/examples/ssl/docker-compose.yml new file mode 100644 index 0000000..861e80a --- /dev/null +++ b/doc/examples/ssl/docker-compose.yml @@ -0,0 +1,15 @@ +version: "3.8" + +services: + + angie-conv-example-ssl: + container_name: angie-conv-example-ssl + image: docker.io/rockdrilla/angie-conv:v0.0.1 + environment: + NGX_HTTP_CONFLOAD: 'ssl' + ports: + - "127.0.0.1:8443:8443" + volumes: + - "./site:/angie/site:ro" + - "./static:/angie/static:ro" + - "./tls:/angie/tls:ro" diff --git a/doc/examples/ssl/site/http-site.conf b/doc/examples/ssl/site/http-site.conf new file mode 100644 index 0000000..43485c8 --- /dev/null +++ b/doc/examples/ssl/site/http-site.conf @@ -0,0 +1,33 @@ +server { + listen 8443 ssl; + + server_name .example.org; + + ssl_certificate tls/example.org.chain.crt; + ssl_certificate_key tls/example.org.pem; + + root static/example.org; +} + +server { + listen 8443 ssl; + + server_name www.example.org; + + ssl_certificate tls/www.example.org.chain.crt; + ssl_certificate_key tls/www.example.org.pem; + + root static/www.example.org; +} + +## optional: cut-off server +server { + listen 8443 ssl default_server bind deferred; + + server_name _; + + ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; + + ## reject connections early + ssl_reject_handshake on; +} diff --git a/doc/examples/ssl/static/example.org/index.html b/doc/examples/ssl/static/example.org/index.html new file mode 100644 index 0000000..4c90c29 --- /dev/null +++ b/doc/examples/ssl/static/example.org/index.html @@ -0,0 +1,5 @@ + + +

This is main site.

+ + \ No newline at end of file diff --git a/doc/examples/ssl/static/www.example.org/index.html b/doc/examples/ssl/static/www.example.org/index.html new file mode 100644 index 0000000..6430b97 --- /dev/null +++ b/doc/examples/ssl/static/www.example.org/index.html @@ -0,0 +1,5 @@ + + +

This is WWW site.

+ + \ No newline at end of file diff --git a/doc/examples/ssl/tls/ca/internal-ca.crt b/doc/examples/ssl/tls/ca/internal-ca.crt new file mode 100644 index 0000000..7843986 --- /dev/null +++ b/doc/examples/ssl/tls/ca/internal-ca.crt @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDLDCCAhSgAwIBAgIIc9z+Nze+1ngwDQYJKoZIhvcNAQELBQAwEjEQMA4GA1UE +AxMHQ0EgUm9vdDAeFw0yNDA5MTkwMDAwMDBaFw0zNTA5MTgyMzU5NTlaMBYxFDAS +BgNVBAMTC0NBIEludGVybmFsMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC +AQEAs0SnuACpBbkmoPrdnrMKuGhA+dUML/PoF8RZI9BdQvnSa3r1mINw3442Hcrh +Nij0BeVqjK6DTk8yrLA3l9/bpXIubimXDWvzeMRRVduWPdEc9cdDhuksbrIApQow +3cP68U95pqwYbDsXtGqXOzDWnKQppok42OjaKL6zwNsM6Qs/UKVADJ7rmPSoZSa/ +RCywhurnZt3eIDQjQqfJCnNifUXnLOD8JwyhSACBvxdQQnn2ibh78KA6LuECUDX9 +jKOdgJvffwl1XaXqX9pfM9KwmoNs+utVOm9weENC0tnss/BftqzBo6szAeyIKzkk +xOjppCNz2Uou3UsVEVyCA6GAjQIDAQABo4GBMH8wDwYDVR0TAQH/BAUwAwEB/zAd +BgNVHQ4EFgQUSsSalxeTY1qmvFLILIS3gZ4ynGMwHwYDVR0jBBgwFoAUMdG64dLU +vChFxO1SEtyn4NoRr8gwCwYDVR0PBAQDAgEGMB8GA1UdJQQYMBYGCisGAQQBgjcK +AwEGCCsGAQUFBwMJMA0GCSqGSIb3DQEBCwUAA4IBAQDJyKWDN6lD4/UTx12HrVqI +taOkOzFIBho+FSvCRI/ZLpeMj01WZIb9XkdhLZvUAh+c7jC/caMghGX5N8Kqunmr +x1HYnLm+C6QOdYy2djEml3ZwnbEn9yT1YYhRIZC993ipEzeNFm39J433l1PXYsLa +XNC99j58tVPFELcpimqe8eoUW2hYKZqFvswuta2PhX9mNYOSVk5ICl3rs0kr8gDR +3PC6vKmMxmTWTlg94JuTRCT0L5LD5Ode76iR7q0TY3XOzeDeEw3H99nPv3i69d2D +15pEo78xeNOZhbJ1OGUqBO45JrwhhJ4x9N+5SSnLSEvgv+qghVK+mkxAtvM/6fsp +-----END CERTIFICATE----- diff --git a/doc/examples/ssl/tls/ca/root-ca.crt b/doc/examples/ssl/tls/ca/root-ca.crt new file mode 100644 index 0000000..1c7284c --- /dev/null +++ b/doc/examples/ssl/tls/ca/root-ca.crt @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDBjCCAe6gAwIBAgIITliyKcJbVmEwDQYJKoZIhvcNAQELBQAwEjEQMA4GA1UE +AxMHQ0EgUm9vdDAeFw0yNDA5MTkwMDAwMDBaFw0zNjA5MTgyMzU5NTlaMBIxEDAO +BgNVBAMTB0NBIFJvb3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDP +0HWgtUwQ+OMm30ANf8Iy4H3tfGnfrDd4oQXqMjuW6Eh0nxzlWMIcvrN1l2Y2QscI +i+/6CNq6tirbkN3PIYFdboejROXPDRsh7ck+92PyJiEcbK0SbI/S/3bKGpeqmTy+ +HvbkMvzlUJ/+SH6FgU3sCkYga43QDE8DT3PRf0zd7mBF2ij/OXtv69JehdTJBDa2 +hW09Ivjfq5cHoMIEfIvTp8847TGIQDqU8k1N8A5brrU+2gHJ+H3GoV09ej5/cv6Q +9FU9DE/mTW7iDHjNVNgq4JQXJWyCYH6TfoKet+/8Q1odhe+4dG22lO6EgHdp5IIN +J5322FKKsuwZ1JhA/ZJ9AgMBAAGjYDBeMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0O +BBYEFDHRuuHS1LwoRcTtUhLcp+DaEa/IMAsGA1UdDwQEAwIBBjAfBgNVHSUEGDAW +BgorBgEEAYI3CgMBBggrBgEFBQcDCTANBgkqhkiG9w0BAQsFAAOCAQEAxRGAJ3nV +/ycyml5mm4q330Mnsa8Rc0DoVaQXfEyIIBkgYD2dIhvjnA5cK6AVStAJ/16lx77T +v5bG5/AyC2D7ISd8PLcpWrAtxo06cYM3OJjpWwl18oH1tS1L2hi6L8I2LNkW4TKQ +yFjRCYJvsM2QUnRL99S4JKiXACDMCTP/ZP87fQvmfi4lXCnUlQqgtnCq0+iCwXVJ +oR1SdOrmPz/NI23RA41U15LePwFuK5cTE0WhtyZej8ksv6V+5Z1aiIBTt/cMl+KH +2K9dmO+dNp1DJeSaeH+8rsDd44FkPvDi1nMjm4G51U2JVrbjift70DM/Ia/DPH72 +bYJLgeFDhdfzMg== +-----END CERTIFICATE----- diff --git a/doc/examples/ssl/tls/example.org.chain.crt b/doc/examples/ssl/tls/example.org.chain.crt new file mode 100644 index 0000000..188c875 --- /dev/null +++ b/doc/examples/ssl/tls/example.org.chain.crt @@ -0,0 +1,40 @@ +-----BEGIN CERTIFICATE----- +MIIDXTCCAkWgAwIBAgIIKr9NphMObcAwDQYJKoZIhvcNAQELBQAwFjEUMBIGA1UE +AxMLQ0EgSW50ZXJuYWwwHhcNMjQwOTE5MDAwMDAwWhcNMzQwOTE4MjM1OTU5WjAW +MRQwEgYDVQQDEwtleGFtcGxlLm9yZzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC +AQoCggEBAJ+WCrO6ML91DNYfgzrc6XiZvOFNxBfbuPkPdFw6dx4wwJCFg0VcdH1W +BpVMMvczlntphpARGZ+bsktKRhsbDHNfbExo6Sn6b0x/xnUkFg34ukYhRdFs+xHC +/PO9t2a9LDcMsBr0yLBugbMYGZj2Ln+sp478aKyWNkQLBKOEfeijtg3qrIx0B/we +vd8Tx49ahQYB8XELiZa1mntqRpHewMr+ul6sf5z6JR3Jrokvzu7kGLjt1FN1VAQR +pkzqNYJX/vUJ3KIdZxWHyzyoIbra/VjeP/POIY22eQGDWwbg9sNVUyVKFPzkaSwV ++BAytQiEn2cEQtmxoPZ+iLkyiGgGFWUCAwEAAaOBrjCBqzAMBgNVHRMBAf8EAjAA +MB0GA1UdDgQWBBRrZV8OY2075agO7UQfsDcPKmt87TAfBgNVHSMEGDAWgBRKxJqX +F5NjWqa8UsgshLeBnjKcYzALBgNVHQ8EBAMCA/gwJwYDVR0lBCAwHgYIKwYBBQUH +AwEGCCsGAQUFBwMCBggrBgEFBQcDCDAlBgNVHREEHjAcggtleGFtcGxlLm9yZ4IN +Ki5leGFtcGxlLm9yZzANBgkqhkiG9w0BAQsFAAOCAQEApijiwX+Y6XR25Z0yyv9P +gAkZkE+X/rQuk+9PyuSXLWUg9x0p3G0RUwPHHwiUpYHnTmAf3iKoPHLltX+KxqkO +W3Kx07TId5FmK8UrCZ+Xs1yuvMHtwdAvf8DA8QCaWSa8N9QeYmbzArjM8035j16+ +rFiMaO8mLEftqnVZksYt3pPWsus0UnhK9gnTw9PSxqWpC1EoTyiuDwTLdVqqYAeM +oqqdpHfSFPBXmCRZc5dbptnrJmLiMHoVeeKjdYXLr1GgIVYPN+Dbldwb8gcIQ+TM +zN+J7p0W7rHhsGSleackNlWWfodjnc0WHZWkyplg4W48V6KbLxsK+LOpvnuoy+mu +vw== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDLDCCAhSgAwIBAgIIc9z+Nze+1ngwDQYJKoZIhvcNAQELBQAwEjEQMA4GA1UE +AxMHQ0EgUm9vdDAeFw0yNDA5MTkwMDAwMDBaFw0zNTA5MTgyMzU5NTlaMBYxFDAS +BgNVBAMTC0NBIEludGVybmFsMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC +AQEAs0SnuACpBbkmoPrdnrMKuGhA+dUML/PoF8RZI9BdQvnSa3r1mINw3442Hcrh +Nij0BeVqjK6DTk8yrLA3l9/bpXIubimXDWvzeMRRVduWPdEc9cdDhuksbrIApQow +3cP68U95pqwYbDsXtGqXOzDWnKQppok42OjaKL6zwNsM6Qs/UKVADJ7rmPSoZSa/ +RCywhurnZt3eIDQjQqfJCnNifUXnLOD8JwyhSACBvxdQQnn2ibh78KA6LuECUDX9 +jKOdgJvffwl1XaXqX9pfM9KwmoNs+utVOm9weENC0tnss/BftqzBo6szAeyIKzkk +xOjppCNz2Uou3UsVEVyCA6GAjQIDAQABo4GBMH8wDwYDVR0TAQH/BAUwAwEB/zAd +BgNVHQ4EFgQUSsSalxeTY1qmvFLILIS3gZ4ynGMwHwYDVR0jBBgwFoAUMdG64dLU +vChFxO1SEtyn4NoRr8gwCwYDVR0PBAQDAgEGMB8GA1UdJQQYMBYGCisGAQQBgjcK +AwEGCCsGAQUFBwMJMA0GCSqGSIb3DQEBCwUAA4IBAQDJyKWDN6lD4/UTx12HrVqI +taOkOzFIBho+FSvCRI/ZLpeMj01WZIb9XkdhLZvUAh+c7jC/caMghGX5N8Kqunmr +x1HYnLm+C6QOdYy2djEml3ZwnbEn9yT1YYhRIZC993ipEzeNFm39J433l1PXYsLa +XNC99j58tVPFELcpimqe8eoUW2hYKZqFvswuta2PhX9mNYOSVk5ICl3rs0kr8gDR +3PC6vKmMxmTWTlg94JuTRCT0L5LD5Ode76iR7q0TY3XOzeDeEw3H99nPv3i69d2D +15pEo78xeNOZhbJ1OGUqBO45JrwhhJ4x9N+5SSnLSEvgv+qghVK+mkxAtvM/6fsp +-----END CERTIFICATE----- diff --git a/doc/examples/ssl/tls/example.org.pem b/doc/examples/ssl/tls/example.org.pem new file mode 100644 index 0000000..192ffd7 --- /dev/null +++ b/doc/examples/ssl/tls/example.org.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEogIBAAKCAQEAn5YKs7owv3UM1h+DOtzpeJm84U3EF9u4+Q90XDp3HjDAkIWD +RVx0fVYGlUwy9zOWe2mGkBEZn5uyS0pGGxsMc19sTGjpKfpvTH/GdSQWDfi6RiFF +0Wz7EcL88723Zr0sNwywGvTIsG6BsxgZmPYuf6ynjvxorJY2RAsEo4R96KO2Deqs +jHQH/B693xPHj1qFBgHxcQuJlrWae2pGkd7Ayv66Xqx/nPolHcmuiS/O7uQYuO3U +U3VUBBGmTOo1glf+9Qncoh1nFYfLPKghutr9WN4/884hjbZ5AYNbBuD2w1VTJUoU +/ORpLBX4EDK1CISfZwRC2bGg9n6IuTKIaAYVZQIDAQABAoIBAA52Ufz3VCCdp8P7 +Mht9AU7Txolie0awO63yfRiN7H/uFMgOxBaJP5NLiagxB3Nd7Pa9LvEnuOXn0xC9 +/Twf8ju9u4+ceE48wFEInqsR/J+tLpsEET2JPfgzmVSGGQn0qH5KpjujJabQ35cj +3s9SYWS3owMIaSdZgOHKCn8TwYykriGYgagV3c+tMdqDaqvrrWAu9mkCp9/MzREb +X3XBJ9NE/5dlMeMfXJiRKvLc16hQCSrXVdCLLm3U3/sHncDKOEOF4kvEBR5ciXXN +zXkuvbE4GKjC0rCGZtocbC2EkkHRMD96Y2cNwmNnWp0fJ0KOVYGw3S3YsWNMex0u +n57vEUcCgYEAy+x9NB4pCF/5Va82yRYYRUZCyDWLAYAC7kJ933R2wugUgs8FUQzK +xh0d7PuGDYB/zrsZfdQoZGmEbffZ/pze6pdIpaFKFnQoHPtS92aQdBPwyCdBnJUy +cuVQekPBsDtRFSOaznzOej+bkFH43RujYjDPPRAIP2Oily20CRcXzb8CgYEAyFb6 +X2mQn2EM+vIxGIIUe09YsT9OhZyzI2TG4M4yvapFF8FVA5qZJTQh7mxJzlACev3J +Rmrpo/36j4co9x8Ph9ojcQzd0qhiwYPaFc4sBjFRPm8k22+mf2zIm5VV1xHqML65 +ciGGCLxfBO+j1y0ktUL6g9QGwyr/0RaFo7UhrdsCgYBcEyipGqEeVe9Hn/hVrTNC +PCo1Ke/cuocYO0+IUJa7BH0WXxEgem0oLMdxVFQ/znBm0JX++YdPZ1FTMeDtFLmW +JL65gmzoXIQsKdJZQKcisko6pXb7k2YW+LFwsx1GTFIdAFmKuFGmYwgDju+WLj+E +O1OnV5DRxlQIfKtYm2O1EwKBgHb1hrgPFAw/cZi1JUf7PbQ22mBtSe/2qzxyA9HL +Pr/2kg5YA2Yfb55yxU8wx+aVBdQITHLe2xtAnX6KcF3E+NDfS7o+PJ1w1Ss5Ys8d +3HBU6nwbPRR7yK7TZo9T7mPFxHzrU2yc0Vzwla91qKEFsk1blyueQ+Gx77M1H2el +D3mPAoGAfd9V3aNVaNjjj/ILQlX6ypPeqWLjzsHRnsIedDXgGRh5/ZgXetSgp+f9 +MtZdaHkYiBMYJJXFqn3KcIaRGAgxQbaqnZtSmHpUg4AXDmxEbu01ryVjPIW/EvZb +4oTikKQyUW8jqWS3irvxZga+nwJFNNYMyVgK+W29UYZzW53GMV4= +-----END RSA PRIVATE KEY----- diff --git a/doc/examples/ssl/tls/www.example.org.chain.crt b/doc/examples/ssl/tls/www.example.org.chain.crt new file mode 100644 index 0000000..a00d852 --- /dev/null +++ b/doc/examples/ssl/tls/www.example.org.chain.crt @@ -0,0 +1,39 @@ +-----BEGIN CERTIFICATE----- +MIIDVjCCAj6gAwIBAgIIKpxUKA9KjPcwDQYJKoZIhvcNAQELBQAwFjEUMBIGA1UE +AxMLQ0EgSW50ZXJuYWwwHhcNMjQwOTE5MDAwMDAwWhcNMzQwOTE4MjM1OTU5WjAa +MRgwFgYDVQQDEw93d3cuZXhhbXBsZS5vcmcwggEiMA0GCSqGSIb3DQEBAQUAA4IB +DwAwggEKAoIBAQC7IBurNJ8FFtPgMBX8n8wQuAypCfFucM3reIblnPpHsWifRlk/ +XrOmD1FDhErz4V38ouloS7q4jxAbbrPlLW93u4En5UZ5jPzN3T2h0vIrPl6sCKf1 +edEju5lu7WIcNUH8VF4j6kDP71LwBW99kdFrYHSRCtWIXeJrB1MLx7c9lekrm89/ +lz08UdGd7udJOcKDrsvsIj4cJYImHENLYB4LoWVMsiD7ap+zKud9Zf9YsRNG7XTT +HtZSTdrgN5opAU/0vA10Jzp1S3HDfPL5+7Xw6Xq62ZJXOyAofpItxc9dGOOoNiZi +wtYCMZwz2H3LujDgc5wMbPV9k1UxU/Y6QHrjAgMBAAGjgaMwgaAwDAYDVR0TAQH/ +BAIwADAdBgNVHQ4EFgQUDhDi+m6Iocrd9LJ6ZJBFMfsp3F0wHwYDVR0jBBgwFoAU +SsSalxeTY1qmvFLILIS3gZ4ynGMwCwYDVR0PBAQDAgP4MCcGA1UdJQQgMB4GCCsG +AQUFBwMBBggrBgEFBQcDAgYIKwYBBQUHAwgwGgYDVR0RBBMwEYIPd3d3LmV4YW1w +bGUub3JnMA0GCSqGSIb3DQEBCwUAA4IBAQBr03L9bIY5unzvN8psu+a410Gwgkma +8D37SeJ23fV0FR0gemgIJsq0SoVRuwbHQMum1Rs9MC+fRIcN5UZGKDTb6WQIb+In +1qnX3A5OU/rTOjkWELQLxJ+ejqJT86pHuODwpX+YME1nDo+3nmb/OLAzrXjgfY3j +w5GCU4dobXU3RvbQAbvpw4ECOBPbuizq+fngIGmeJt7kcdJ6vZw3OvKlk451REGI +gt3TrELsmvH7D2qNyPYgEn7ifdVKEbiMcFcHoMz/zZ2ZxlGSQ7YnWOZd0++uB2od +iBWE+faUZ2ApEWEnX1FPcPIQ7x7dDvYHkmGVh0tAwXcgPs1NCX7rJcVF +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDLDCCAhSgAwIBAgIIc9z+Nze+1ngwDQYJKoZIhvcNAQELBQAwEjEQMA4GA1UE +AxMHQ0EgUm9vdDAeFw0yNDA5MTkwMDAwMDBaFw0zNTA5MTgyMzU5NTlaMBYxFDAS +BgNVBAMTC0NBIEludGVybmFsMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC +AQEAs0SnuACpBbkmoPrdnrMKuGhA+dUML/PoF8RZI9BdQvnSa3r1mINw3442Hcrh +Nij0BeVqjK6DTk8yrLA3l9/bpXIubimXDWvzeMRRVduWPdEc9cdDhuksbrIApQow +3cP68U95pqwYbDsXtGqXOzDWnKQppok42OjaKL6zwNsM6Qs/UKVADJ7rmPSoZSa/ +RCywhurnZt3eIDQjQqfJCnNifUXnLOD8JwyhSACBvxdQQnn2ibh78KA6LuECUDX9 +jKOdgJvffwl1XaXqX9pfM9KwmoNs+utVOm9weENC0tnss/BftqzBo6szAeyIKzkk +xOjppCNz2Uou3UsVEVyCA6GAjQIDAQABo4GBMH8wDwYDVR0TAQH/BAUwAwEB/zAd +BgNVHQ4EFgQUSsSalxeTY1qmvFLILIS3gZ4ynGMwHwYDVR0jBBgwFoAUMdG64dLU +vChFxO1SEtyn4NoRr8gwCwYDVR0PBAQDAgEGMB8GA1UdJQQYMBYGCisGAQQBgjcK +AwEGCCsGAQUFBwMJMA0GCSqGSIb3DQEBCwUAA4IBAQDJyKWDN6lD4/UTx12HrVqI +taOkOzFIBho+FSvCRI/ZLpeMj01WZIb9XkdhLZvUAh+c7jC/caMghGX5N8Kqunmr +x1HYnLm+C6QOdYy2djEml3ZwnbEn9yT1YYhRIZC993ipEzeNFm39J433l1PXYsLa +XNC99j58tVPFELcpimqe8eoUW2hYKZqFvswuta2PhX9mNYOSVk5ICl3rs0kr8gDR +3PC6vKmMxmTWTlg94JuTRCT0L5LD5Ode76iR7q0TY3XOzeDeEw3H99nPv3i69d2D +15pEo78xeNOZhbJ1OGUqBO45JrwhhJ4x9N+5SSnLSEvgv+qghVK+mkxAtvM/6fsp +-----END CERTIFICATE----- diff --git a/doc/examples/ssl/tls/www.example.org.pem b/doc/examples/ssl/tls/www.example.org.pem new file mode 100644 index 0000000..ff0da53 --- /dev/null +++ b/doc/examples/ssl/tls/www.example.org.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEAuyAbqzSfBRbT4DAV/J/MELgMqQnxbnDN63iG5Zz6R7Fon0ZZ +P16zpg9RQ4RK8+Fd/KLpaEu6uI8QG26z5S1vd7uBJ+VGeYz8zd09odLyKz5erAin +9XnRI7uZbu1iHDVB/FReI+pAz+9S8AVvfZHRa2B0kQrViF3iawdTC8e3PZXpK5vP +f5c9PFHRne7nSTnCg67L7CI+HCWCJhxDS2AeC6FlTLIg+2qfsyrnfWX/WLETRu10 +0x7WUk3a4DeaKQFP9LwNdCc6dUtxw3zy+fu18Ol6utmSVzsgKH6SLcXPXRjjqDYm +YsLWAjGcM9h9y7ow4HOcDGz1fZNVMVP2OkB64wIDAQABAoIBAD+Cd0GVO397ru+B +AoVaKuVlwg5BLKsCKDGKF6aor51TjiG4u6OxXaG3wyn6JYI+dCrBlBxsz3PCQoI9 +AVuAHzvw7LYAr/mjK04nj7pzoPOiWHlk+rRq4tuQ2VN3x/uw67NbYxQndlXccTa7 +cYqZygz6kLWFitGco7MVqk7uOrfwqu4O5GbktR4Vm6apEh2eFsSfgFE4LN3z7bbf +A7dzePBy2BOEvy/CjNX5stkLKJuWzuVTcYXB82bqp/VsSXSQG9o/9VmR6OUhXWjA +Clf+m3HqVmZ1IivOFz48LHVzvUj26AclvwwXkaGptbA6s08QWJMS2mpZlWbWNwzr +Mqgl/3ECgYEA8xpGCQrxfw7LTI9sMVnQo3S1rbB23DMuP2PmX6Aad0U3eGSxrv6B +RweXpb1Kduu8FeLMngihPjv/UsHJkNMX6IRILc2kLbRYoQOLdJ5k42bYzhDKYN33 +e8jr6twJJsCgva4DoLM0woZTvgaRzLJBzMWoL1BT6JJkKGUukgwCiskCgYEAxQ2S +1oN1ZHc454y2bJ0JYg6c/MGEKHFjIRLKMMxwhNXQzOr8EzzwwF+5IsJORQfJNu+P +DaFT/3QAuByKG+Dyx4C0ssIhj6u75g1Thzgv8qHwE6DYh1VUUYO656kFTtexlFyg +gJPYXCOWPelSNNNQXbZTV929R1Wx95+LfqLqQksCgYEApmCj8ApqT3AbmVFLVddH +YKc+tBnirz/j9gR0JZwYoOphVWds5/xNFATRN+B+NzeNKVloevwjBsnfK49vWUvv +v/XQxHBKXfFg+wnHBtBk8fFwjk+VgohHmZNgSwO+y6PoHwkaeIBNqphudc5fOL4D +JJdeTMtoMfMG01K0dcX8c2kCgYEAwE1n0GqIJNxoryfWW5bBMm2abNwZsjI9kGg6 +43aQFEJpu6FTER82wDZqgW5oXdukVTViQOYBCFpX3VUUvvI/W8zSC2WCxSfOfkrh +CiQePsYkebNNvHzchZRt0WhUYsYCagwfInul+P1NwOuzKxRR6LJnEWe3MSeDP2n3 +A0XQIZMCgYAzubLPZnJjFihAX0M9k8Cjc+q6KKnA6Fp1JqnPmzoTO/r46o1shmCZ +kRS8iqnKfTCW/MWGSPyRc0OubIVbR9hAdCZjR8wmeVdkiV+VfBRzxpcYpcZbxjmy +6F0xz1Fv0UeKdjHQyb9UNO6Y1qVaNVVYo3tyD6VGaMdboddHPPxLDw== +-----END RSA PRIVATE KEY----- diff --git a/doc/examples/static-template/Dockerfile b/doc/examples/static-template/Dockerfile new file mode 100644 index 0000000..e8c8470 --- /dev/null +++ b/doc/examples/static-template/Dockerfile @@ -0,0 +1,4 @@ +FROM docker.io/rockdrilla/angie-conv:v0.0.1 + +COPY /site/ /etc/angie/site/ +COPY /static/ /etc/angie/static/ diff --git a/doc/examples/static-template/README.md b/doc/examples/static-template/README.md new file mode 100644 index 0000000..d51ebcd --- /dev/null +++ b/doc/examples/static-template/README.md @@ -0,0 +1,26 @@ +# static site with templates + +consult [Dockerfile](Dockerfile) or [docker-compose.yml](docker-compose.yml) - both are simple and fine enough. + +--- + +same as [simple static site](../basic/README.md). + +configuration: + +```nginx +server { + listen 8080; +} +``` + +Also note that there's no `index.html` but `index.html.j2`: + +```jinja + + +

Hello World

+ This image is powered by Angie {% if env.NGX_PRO == '1' %}PRO{% else %}OSS{% endif %} {{ env.ANGIE_VERSION }} and Python {{ env.PYTHON_VERSION }}. + +
+``` diff --git a/doc/examples/static-template/docker-compose.yml b/doc/examples/static-template/docker-compose.yml new file mode 100644 index 0000000..dd80104 --- /dev/null +++ b/doc/examples/static-template/docker-compose.yml @@ -0,0 +1,12 @@ +version: "3.8" + +services: + + angie-conv-example-static-template: + container_name: angie-conv-example-static-template + image: docker.io/rockdrilla/angie-conv:v0.0.1 + ports: + - "127.0.0.1:8080:8080" + volumes: + - "./site:/angie/site:ro" + - "./static:/angie/static:ro" diff --git a/doc/examples/static-template/site/http-site.conf b/doc/examples/static-template/site/http-site.conf new file mode 100644 index 0000000..8ff8ce6 --- /dev/null +++ b/doc/examples/static-template/site/http-site.conf @@ -0,0 +1,3 @@ +server { + listen 8080; +} \ No newline at end of file diff --git a/doc/examples/static-template/static/index.html.j2 b/doc/examples/static-template/static/index.html.j2 new file mode 100644 index 0000000..16378a7 --- /dev/null +++ b/doc/examples/static-template/static/index.html.j2 @@ -0,0 +1,6 @@ + + +

Hello World

+ This image is powered by Angie {% if env.NGX_PRO == '1' %}PRO{% else %}OSS{% endif %} {{ env.ANGIE_VERSION }} and Python {{ env.PYTHON_VERSION }}. + + \ No newline at end of file diff --git a/image-entry.d/00-common.envsh b/image-entry.d/00-common.envsh new file mode 100644 index 0000000..a7cdab8 --- /dev/null +++ b/image-entry.d/00-common.envsh @@ -0,0 +1,245 @@ +#!/bin/sh + +volume_root='/run/ngx' +target_root="${volume_root}/conf" +persist_dirs='cache lib log' +empty_dir='/var/lib/empty' + +## unexporting variable in (POSIX) sh is PITA =/ +# have_envvar() { +# [ -n "$1" ] || return 1 +# grep -Ezq "^$1=" /proc/$$/environ || return +# } +# unexport() { +# local ___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}\")" +# done +# } + +## 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" + +log_always() { + if [ "${IEP_DEBUG}" = 1 ] ; then + echo "# $(date +'%Y-%m-%d %H:%M:%S.%03N %z'): ${__IEP_SRC}${*:+: $*}" + else + echo "# ${__IEP_SRC}${*:+: $*}" + fi >&2 +} + +log() { + [ "${IEP_VERBOSE}" = 0 ] || log_always "$@" +} + +ln_s() { + if [ "${IEP_VERBOSE}" = 0 ] ; then + ln -s "$@" || return + else + ln -sv "$@" || return + fi +} +cp_a() { + if [ "${IEP_VERBOSE}" = 0 ] ; then + cp -a "$@" || return + else + cp -av "$@" || return + fi +} + +ln_cp() { + if [ -h "$1" ] ; then + ln_s "$(readlink -e "$1")" "$2" + else + cp_a "$1" "$2" + fi +} + +have_cmd() { command -v "$1" >/dev/null 2>&1 || return ; } + +strip_suffix() { printf '%s' "${1%"$2"}" | tr -s '/' ; } + +user_install() { + if [ "${IEP_ROOT}" = 1 ] ; then + install -o "${NGX_USER}" -g "${NGX_GROUP}" "$@" + else + install "$@" + fi +} + +expand_file_envsubst() { + local __ret __src __dst + + __ret=0 + for __src ; do + [ -n "${__src}" ] || continue + + if ! [ -f "${__src}" ] ; then + __ret=1 + log_always "file not found: ${__src}" + continue + fi + + case "${__src}" in + *.in ) ;; + * ) + __ret=1 + log "expand_file_envsubst: file name extension mismatch: ${__src}" + continue + ;; + esac + + __dst=$(strip_suffix "${__src}" '.in') + if [ -e "${__dst}" ] ; then + __ret=1 + log "expand_file_envsubst: destination file already exists: ${__dst}" + continue + fi + + log "Running envsubst: ${__src} -> ${__dst}" + envsubst.sh < "${__src}" > "${__dst}" || __ret=1 + done + return ${__ret} +} + +expand_file_j2cfg() { + j2cfg-single "$@" || return $? +} + +expand_dir_envsubst() { + local __template_list __have_args __ret __orig_file + + __template_list=$(mktemp) || return + + find "$@" -follow -name '*.in' -type f \ + | sort -uV > "${__template_list}" + + __ret=0 + if [ -s "${__template_list}" ] ; then + __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 + expand_file_envsubst "${__orig_file}" || __ret=1 + done < "${__template_list}" + + if [ -z "${__have_args}" ] ; then + rm -f "${ENVSUBST_ARGS}" ; unset ENVSUBST_ARGS + fi + unset __have_args + fi + + rm -f "${__template_list}" ; unset __template_list + + return ${__ret} +} + +expand_dir_j2cfg() { + local __template_list __ret + + __template_list=$(mktemp) || return + + find "$@" -follow -name '*.j2' -type f -printf '%p\0' \ + | sort -zuV > "${__template_list}" + + __ret=0 + if [ -s "${__template_list}" ] ; then + xargs -0r -n 1000 -a "${__template_list}" \ + j2cfg-multi < /dev/null || __ret=1 + fi + + rm -f "${__template_list}" ; unset __template_list + + return ${__ret} +} + +is_builtin_module() { + [ -n "${1:-}" ] || return 1 + [ -n "${2:-}" ] || return 1 + + [ -f "/etc/angie.dist/builtin.$1" ] || return 1 + [ -s "/etc/angie.dist/builtin.$1" ] || return 1 + + grep -Fxq -e "$2" "/etc/angie.dist/builtin.$1" || return 1 +} + +append_list() { + if [ -n "$2" ] ; then + printf '%s' "${1:-}${1:+ }$2" + else + printf '%s' "$1" + fi +} + +prepend_list() { + if [ -n "$2" ] ; then + printf '%s' "$2${1:+ }${1:-}" + else + printf '%s' "$1" + fi +} + +list_have_item() { + [ -n "$1" ] || return 1 + [ -n "$2" ] || return 1 + case " $1 " in + *" $2 "* ) return 0 ;; + esac + return 1 +} + +normalize_list() { + [ -n "$1" ] || return 0 + + printf '%s' "$1" \ + | sed -zE 's/[[:space:]]+/ /g;s/^ //;s/ $//' +} + +sort_dedup_list() { + [ -n "$1" ] || return 0 + + printf '%s' "$1" \ + | tr -s '[:space:]' '\n' | sort -uV \ + | sed -zE 's/[[:space:]]+/ /g;s/^ //;s/ $//' +} + +float_div() { + mawk -v "a=$1" -v "b=$2" 'BEGIN{print a/b;exit;}' &1 | mawk '$1=="DEBUG:" {print $2;exit;}') +NGX_DEBUG="${NGX_DEBUG:-0}" +case "${NGX_DEBUG}" in +0 | 1 ) ;; +* ) NGX_DEBUG=1 ;; +esac +export NGX_DEBUG + +unset NGX_PRO +NGX_PRO=$(/usr/sbin/angie -v 2>&1 | mawk 'NR==1 {print $4;exit;}' | tr '[:upper:]' '[:lower:]') +NGX_PRO="${NGX_PRO:-0}" +case "${NGX_PRO}" in +'(pro)' ) NGX_PRO=1 ;; +* ) NGX_PRO=0 ;; +esac +export NGX_PRO + +set -a +NGX_STRICT_LOAD=$(gobool_to_int "${NGX_STRICT_LOAD:-1}" 1) + +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) +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-nonroot.envsh b/image-entry.d/02-nonroot.envsh new file mode 100755 index 0000000..504d73f --- /dev/null +++ b/image-entry.d/02-nonroot.envsh @@ -0,0 +1,6 @@ +#!/bin/sh + +unset IEP_ROOT ; IEP_ROOT=1 +# [ "$(env stat -Lc %u /proc/$$)" = 0 ] || IEP_ROOT=0 +[ "$(id -u)" = 0 ] || IEP_ROOT=0 +export IEP_ROOT diff --git a/image-entry.d/03-local-ip-addresses.envsh b/image-entry.d/03-local-ip-addresses.envsh new file mode 100755 index 0000000..aa55215 --- /dev/null +++ b/image-entry.d/03-local-ip-addresses.envsh @@ -0,0 +1,30 @@ +#!/bin/sh + +## allow these addresses to be provided in case of: +## - local development/testing +## - `hostname -I' random failures or misbehavior +if [ -z "${NGX_IP_ADDRESSES:-}" ] ; then + NGX_IP_ADDRESSES=$(hostname -I) +fi +NGX_IP_ADDRESSES=$(normalize_list "${NGX_IP_ADDRESSES}") +export NGX_IP_ADDRESSES + +unset i NGX_IPV4_ADDRESSES NGX_IPV6_ADDRESSES +for i in ${NGX_IP_ADDRESSES} ; do + case "$i" in + *:* ) + ## TODO: IPv6 address validation + NGX_IPV6_ADDRESSES=$(append_list "${NGX_IPV6_ADDRESSES}" "$i") + ;; + * ) + if ! is_ipv4_address "$i" ; then + log_always "invalid IPv4 address: $i" + continue + fi + NGX_IPV4_ADDRESSES=$(append_list "${NGX_IPV4_ADDRESSES}" "$i") + ;; + esac +done +unset i + +export NGX_IPV4_ADDRESSES NGX_IPV6_ADDRESSES diff --git a/image-entry.d/04-resolver.envsh b/image-entry.d/04-resolver.envsh new file mode 100755 index 0000000..5709411 --- /dev/null +++ b/image-entry.d/04-resolver.envsh @@ -0,0 +1,109 @@ +#!/bin/sh + +unset _NGX_RESOLVER_STACK _NGX_RESOLVER_TIMEOUT +## here should be SANE defaults (!) +_NGX_RESOLVER_STACK=ipv4 +_NGX_RESOLVER_TIMEOUT=10s + +if [ -z "${NGX_RESOLVER_STACK:-}" ] ; then + NGX_RESOLVER_STACK=${_NGX_RESOLVER_STACK} +else + NGX_RESOLVER_STACK=$(printf '%s' "${NGX_RESOLVER_STACK}" | tr '[:upper:]' '[:lower:]') + case "${NGX_RESOLVER_STACK}" in + none | ipv4 | ipv6 | any ) ;; + ## adjust + 0 | no ) + NGX_RESOLVER_STACK=none + ;; + 4 | ip4 | v4 ) + NGX_RESOLVER_STACK=ipv4 + ;; + 6 | ip6 | v6 ) + NGX_RESOLVER_STACK=ipv6 + ;; + all | dual ) + NGX_RESOLVER_STACK=any + ;; + * ) + log_always "NGX_RESOLVER_STACK: unrecognized value: ${NGX_RESOLVER_STACK}" + log_always "setting NGX_RESOLVER_STACK=${_NGX_RESOLVER_STACK}" + NGX_RESOLVER_STACK=${_NGX_RESOLVER_STACK} + ;; + esac +fi +export NGX_RESOLVER_STACK + +if [ "${NGX_RESOLVER_STACK}" = 'none' ] ; then + unset NGX_RESOLV_CONF NGX_RESOLVER_TIMEOUT NGX_RESOLVERS +else + if [ -z "${NGX_RESOLVER_TIMEOUT:-}" ] ; then + NGX_RESOLVER_TIMEOUT=${_NGX_RESOLVER_TIMEOUT} + else + case "${NGX_RESOLVER_TIMEOUT}" in + [1-9] | [1-9][0-9] ) + ## convert implicit "seconds" to explicit + NGX_RESOLVER_TIMEOUT="${NGX_RESOLVER_TIMEOUT}s" + ;; + ## adjust + [1-9][Ss] | [1-9][0-9][Ss] ) + NGX_RESOLVER_TIMEOUT="${NGX_RESOLVER_TIMEOUT%?}s" + ;; + [1-9][Mm][Ss] | [1-9][0-9][Mm][Ss] | [1-9][0-9][0-9][Mm][Ss] | [1-9][0-9][0-9][0-9][Mm][Ss] | [1-9][0-9][0-9][0-9][0-9][Mm][Ss] ) + NGX_RESOLVER_TIMEOUT="${NGX_RESOLVER_TIMEOUT%??}ms" + ;; + * ) + log_always "NGX_RESOLVER_TIMEOUT: unrecognized value: ${NGX_RESOLVER_TIMEOUT}" + log_always "setting NGX_RESOLVER_TIMEOUT=${_NGX_RESOLVER_TIMEOUT}" + NGX_RESOLVER_TIMEOUT=${_NGX_RESOLVER_TIMEOUT} + ;; + esac + fi + export NGX_RESOLVER_TIMEOUT + + unset _resolv_conf + while [ -z "${NGX_RESOLVERS+x}" ] ; do + _resolv_conf="${NGX_RESOLV_CONF-/etc/resolv.conf}" + [ -n "${_resolv_conf}" ] || break + [ -f "${_resolv_conf}" ] || break + [ -s "${_resolv_conf}" ] || break + + unset i + while read -r i ; do + [ -n "$i" ] || continue + + case "$i" in + ## NB: /etc/resolv.conf allows (!) IPv6 addresses in dotted form (RFC 2373) but this is discouraged + *:* ) + ## TODO: IPv6 address validation + i="[$i]" + + case "${NGX_RESOLVER_STACK}" in + any | ipv6 ) + NGX_RESOLVERS=$(append_list "${NGX_RESOLVERS}" "$i") + ;; + esac + ;; + * ) + if ! is_ipv4_address "$i" ; then + log_always "invalid IPv4 address: $i" + continue + fi + + case "${NGX_RESOLVER_STACK}" in + any | ipv4 ) + NGX_RESOLVERS=$(append_list "${NGX_RESOLVERS}" "$i") + ;; + esac + ;; + esac + done <<-EOF + $(mawk '$1 == "nameserver" {print $2}' < "${_resolv_conf}") + EOF + unset i + done + unset _resolv_conf + + [ -z "${NGX_RESOLVERS}" ] || export NGX_RESOLVERS +fi + +unset _NGX_RESOLVER_STACK _NGX_RESOLVER_TIMEOUT diff --git a/image-entry.d/05-ca-certificates.envsh b/image-entry.d/05-ca-certificates.envsh new file mode 100755 index 0000000..23b6df7 --- /dev/null +++ b/image-entry.d/05-ca-certificates.envsh @@ -0,0 +1,12 @@ +#!/bin/sh + +if [ -z "${NGX_SSL_CERT_FILE:-}" ] ; then + unset NGX_SSL_CERT_FILE + if [ -n "${SSL_CERT_FILE:-}" ] ; then + log_always "SSL_CERT_FILE is already set (=${SSL_CERT_FILE})" + NGX_SSL_CERT_FILE=${SSL_CERT_FILE} + else + NGX_SSL_CERT_FILE="${volume_root}/ca.pem" + fi +fi +export NGX_SSL_CERT_FILE diff --git a/image-entry.d/10-core.envsh b/image-entry.d/10-core.envsh new file mode 100755 index 0000000..8c61f75 --- /dev/null +++ b/image-entry.d/10-core.envsh @@ -0,0 +1,9 @@ +#!/bin/sh + +set -a + +NGX_CORE_MODULES="${NGX_CORE_MODULES:-}" +NGX_CORE_CONFLOAD="${NGX_CORE_CONFLOAD:-}" +NGX_CORE_EVENTS_CONFLOAD="${NGX_CORE_EVENTS_CONFLOAD:-}" + +set +a diff --git a/image-entry.d/11-core-modules.envsh b/image-entry.d/11-core-modules.envsh new file mode 100755 index 0000000..0656526 --- /dev/null +++ b/image-entry.d/11-core-modules.envsh @@ -0,0 +1,56 @@ +#!/bin/sh + +unset core_modules core_confload +core_modules= +core_confload="${NGX_CORE_CONFLOAD:-}" + +## filter out builtin core modules +unset i +for i in ${NGX_CORE_MODULES:-} ; do + [ -n "$i" ] || continue + + case "$i" in + */* | *\** | *\?* ) + log_always "module '$i' is not legal, skipping" + continue + ;; + esac + + if is_builtin_module core "$i" ; then + log "$i is builtin module, moving to NGX_CORE_CONFLOAD" + core_confload=$(append_list "${core_confload}" "$i") + continue + fi + + ## naive deduplication + if list_have_item "${core_modules}" "$i" ; then + log "$i is already specified" + continue + fi + + core_modules=$(append_list "${core_modules}" "$i") +done ; unset i + +if [ -n "${core_modules:-}" ] ; then + ## angie-module-wamr: depends on angie-module-wasm + ## angie-module-wasmtime: depends on angie-module-wasm + unset want_wasm ; want_wasm=0 + if list_have_item "${core_modules}" wamr ; then + want_wasm=1 + elif list_have_item "${core_modules}" wasmtime ; then + want_wasm=1 + fi + if [ ${want_wasm} = 1 ] ; then + ## forcefully move 'wasm' to beginning of list + core_modules=$(printf '%s' " ${core_modules} " | sed -zE 's/ wasm / /;s/^/wasm/;s/ $//') + fi + unset want_wasm +fi + +set -a +NGX_CORE_MODULES="${core_modules}" +NGX_CORE_CONFLOAD=$(sort_dedup_list "${core_confload}") +NGX_CORE_EVENTS_CONFLOAD=$(sort_dedup_list "${NGX_CORE_EVENTS_CONFLOAD}") +set +a + +unset core_modules core_confload diff --git a/image-entry.d/12-core-user.envsh b/image-entry.d/12-core-user.envsh new file mode 100755 index 0000000..97a442d --- /dev/null +++ b/image-entry.d/12-core-user.envsh @@ -0,0 +1,76 @@ +#!/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 + +if [ -z "${NGX_USER:-}" ] ; then + NGX_USER=${_NGX_USER} +else + case "${NGX_USER}" in + "${_NGX_USER}" ) ;; + [1-9]* ) + ## numeric id - remap to name + _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 +fi +export NGX_USER + +if [ -z "${NGX_GROUP:-}" ] ; then + NGX_GROUP=${_NGX_GROUP} +else + case "${NGX_GROUP}" in + "${_NGX_GROUP}" ) ;; + [1-9]* ) + ## numeric id - remap to name + _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 +fi +export NGX_GROUP + +unset _NGX_USER _NGX_GROUP diff --git a/image-entry.d/13-core-worker.envsh b/image-entry.d/13-core-worker.envsh new file mode 100755 index 0000000..43822b0 --- /dev/null +++ b/image-entry.d/13-core-worker.envsh @@ -0,0 +1,206 @@ +#!/bin/sh + +unset _NGX_WORKER_PROCESSES _NGX_WORKER_PRIORITY _NGX_WORKER_RLIMIT_NOFILE _NGX_WORKER_CONNECTIONS _NGX_WORKER_AIO_REQUESTS +## here should be SANE defaults (!) +_NGX_WORKER_PROCESSES=2 +_NGX_WORKER_PRIORITY=0 +_NGX_WORKER_RLIMIT_NOFILE=16384 +_NGX_WORKER_CONNECTIONS=4096 +_NGX_WORKER_AIO_REQUESTS=32 + +if [ -z "${NGX_WORKER_PROCESSES:-}" ] ; then + NGX_WORKER_PROCESSES=${_NGX_WORKER_PROCESSES} +else + case "${NGX_WORKER_PROCESSES}" in + ## allow values within [1;999] + [1-9] | [1-9][0-9] | [1-9][0-9][0-9] ) ;; + [Aa][Uu][Tt][Oo] ) + ## adjust + log_always "NGX_WORKER_PROCESSES: \"auto\" isn't supported by container yet" + log_always "offloading decision to Angie (this could be a problem!)" + NGX_WORKER_PROCESSES=auto + ;; + 0 ) + log_always "NGX_WORKER_PROCESSES: \"0\" isn't supported by container 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 +fi +export NGX_WORKER_PROCESSES + +if [ -z "${NGX_WORKER_CPU_AFFINITY:-}" ] ; then + unset NGX_WORKER_CPU_AFFINITY +else + ## let Angie handle this + set -a + NGX_WORKER_CPU_AFFINITY=$(normalize_list "${NGX_WORKER_CPU_AFFINITY}") + set +a +fi + +if [ -z "${NGX_WORKER_CONNECTIONS:-}" ] ; then + NGX_WORKER_CONNECTIONS=${_NGX_WORKER_CONNECTIONS} +else + case "${NGX_WORKER_CONNECTIONS}" in + [0-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 +fi +export NGX_WORKER_CONNECTIONS + +if [ -z "${NGX_WORKER_PRIORITY:-}" ] ; then + unset NGX_WORKER_PRIORITY +else + case "${NGX_WORKER_PRIORITY}" in + -[1-9] | -1[0-9] | -20 ) ;; + [0-9] | 1[0-9] | 20 ) ;; + -0 ) + log_always "NGX_WORKER_PRIORITY: likely an error: '-0'" + log_always "adjusting NGX_WORKER_PRIORITY=0" + NGX_WORKER_PRIORITY=0 + ;; + * ) + 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 + export NGX_WORKER_PRIORITY +fi + +if [ -z "${NGX_WORKER_RLIMIT_NOFILE:-}" ] ; then + unset NGX_WORKER_RLIMIT_NOFILE +else + case "${NGX_WORKER_RLIMIT_NOFILE}" in + [0-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 + export NGX_WORKER_RLIMIT_NOFILE +fi + +if [ -z "${NGX_WORKER_AIO_REQUESTS:-}" ] ; then + unset NGX_WORKER_AIO_REQUESTS +else + case "${NGX_WORKER_AIO_REQUESTS}" in + [0-9] ) + log_always "NGX_WORKER_AIO_REQUESTS: too low: ${NGX_WORKER_AIO_REQUESTS}" + log_always "setting NGX_WORKER_AIO_REQUESTS=${_NGX_WORKER_AIO_REQUESTS}" + NGX_WORKER_AIO_REQUESTS=${_NGX_WORKER_AIO_REQUESTS} + ;; + ## allow values within [10;99999] + [1-9][0-9] ) ;; + [1-9][0-9][0-9] ) ;; + [1-9][0-9][0-9][0-9] ) ;; + [1-9][0-9][0-9][0-9][0-9] ) ;; + * ) + log_always "NGX_WORKER_AIO_REQUESTS: unrecognized value: ${NGX_WORKER_AIO_REQUESTS}" + log_always "setting NGX_WORKER_AIO_REQUESTS=${_NGX_WORKER_AIO_REQUESTS}" + NGX_WORKER_AIO_REQUESTS=${_NGX_WORKER_AIO_REQUESTS} + ;; + esac + export NGX_WORKER_AIO_REQUESTS +fi + +if [ -n "${NGX_WORKER_RLIMIT_NOFILE:-}" ] ; then + unset nofile_soft nofile_hard + nofile_soft=$(ulimit -Sn) + nofile_hard=$(ulimit -Hn) + + if [ "${nofile_hard}" = unlimited ] ; then + ## minor hack (if applicable) :) + nofile_hard=$((NGX_WORKER_RLIMIT_NOFILE + 1)) + fi + + unset nofile_ok ; 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} due to hard limit" + 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_RLIMIT_NOFILE +fi + +unset nofile_limit nofile_kind +if [ -z "${NGX_WORKER_RLIMIT_NOFILE:-}" ] ; then + nofile_limit=$(ulimit -Hn) + nofile_kind="'ulimit:nofile'" +else + nofile_limit=${NGX_WORKER_RLIMIT_NOFILE} + nofile_kind='NGX_WORKER_RLIMIT_NOFILE' +fi +if [ ${nofile_limit} -lt ${NGX_WORKER_CONNECTIONS} ] ; then + log_always "WARNING: ${nofile_kind} is less than NGX_WORKER_CONNECTIONS (${nofile_limit} < ${NGX_WORKER_CONNECTIONS})" + log_always "NGX_WORKER_CONNECTIONS is recommended to be at least twice larger than ${nofile_kind}" +else + unset ratio + ratio=$(float_div "${nofile_limit}" "${NGX_WORKER_CONNECTIONS}") + case "${ratio}" in + 1 | 1.* ) + log_always "WARNING: \"${nofile_kind}/NGX_WORKER_CONNECTIONS\" ratio is too low (=${ratio})" + log_always "NGX_WORKER_CONNECTIONS is recommended to be at least twice larger than ${nofile_kind}" + ;; + esac + unset ratio +fi +unset nofile_limit nofile_kind + +unset _NGX_WORKER_PROCESSES _NGX_WORKER_PRIORITY _NGX_WORKER_RLIMIT_NOFILE _NGX_WORKER_CONNECTIONS _NGX_WORKER_AIO_REQUESTS diff --git a/image-entry.d/14-core-loglevel.envsh b/image-entry.d/14-core-loglevel.envsh new file mode 100755 index 0000000..09330e0 --- /dev/null +++ b/image-entry.d/14-core-loglevel.envsh @@ -0,0 +1,32 @@ +#!/bin/sh + +unset _NGX_LOGLEVEL +## here should be SANE defaults (!) +_NGX_LOGLEVEL=warn + +if [ -z "${NGX_LOGLEVEL:-}" ] ; then + NGX_LOGLEVEL=${_NGX_LOGLEVEL} +else + NGX_LOGLEVEL=$(printf '%s' "${NGX_LOGLEVEL}" | tr '[:upper:]' '[:lower:]') + case "${NGX_LOGLEVEL}" in + alert | crit | emerg | error | notice | info | warn ) ;; + debug ) + if [ "${NGX_DEBUG}" = 1 ] ; then + NGX_LOGLEVEL=debug + else + NGX_LOGLEVEL=info + + log_always "NGX_LOGLEVEL: using 'debug' in non-debug image" + log_always "setting NGX_LOGLEVEL=${NGX_LOGLEVEL}" + fi + ;; + * ) + log_always "NGX_LOGLEVEL: unrecognized value: ${NGX_LOGLEVEL}" + log_always "setting NGX_LOGLEVEL=${_NGX_LOGLEVEL}" + NGX_LOGLEVEL=${_NGX_LOGLEVEL} + ;; + esac +fi +export NGX_LOGLEVEL + +unset _NGX_LOGLEVEL diff --git a/image-entry.d/20-http.envsh b/image-entry.d/20-http.envsh new file mode 100755 index 0000000..1f486b7 --- /dev/null +++ b/image-entry.d/20-http.envsh @@ -0,0 +1,18 @@ +#!/bin/sh + +if [ "${NGX_HTTP}" = 0 ] ; then + unset NGX_HTTP_MODULES NGX_HTTP_CONFLOAD NGX_HTTP_CACHES NGX_HTTP_STATIC_TEMPLATE NGX_HTTP_STATIC_MERGE +else + unset default_caches + default_caches='temp_client_body' + + set -a + NGX_HTTP_MODULES="${NGX_HTTP_MODULES:-}" + NGX_HTTP_CONFLOAD="${NGX_HTTP_CONFLOAD:-}" + NGX_HTTP_CACHES=$(sort_dedup_list "${default_caches} ${NGX_HTTP_CACHES:-}") + NGX_HTTP_STATIC_TEMPLATE=$(gobool_to_int "${NGX_HTTP_STATIC_TEMPLATE:-1}" 1) + NGX_HTTP_STATIC_MERGE=$(gobool_to_int "${NGX_HTTP_STATIC_MERGE:-1}" 1) + set +a + + unset default_caches +fi diff --git a/image-entry.d/21-http-modules.envsh b/image-entry.d/21-http-modules.envsh new file mode 100755 index 0000000..d29f666 --- /dev/null +++ b/image-entry.d/21-http-modules.envsh @@ -0,0 +1,104 @@ +#!/bin/sh + +if [ "${NGX_HTTP}" = 0 ] ; then + unset NGX_HTTP_V2 NGX_HTTP_V3 NGX_HTTP_PROXY +else + set -a + NGX_HTTP_V2=$(gobool_to_int "${NGX_HTTP_V2:-0}" 0) + NGX_HTTP_V3=$(gobool_to_int "${NGX_HTTP_V3:-0}" 0) + NGX_HTTP_PROXY=$(gobool_to_int "${NGX_HTTP_PROXY:-1}" 1) + set +a + + unset http_modules http_confload + http_modules= + http_confload="${NGX_HTTP_CONFLOAD:-}" + + ## filter out builtin http modules + unset i + for i in ${NGX_HTTP_MODULES:-} ; do + [ -n "$i" ] || continue + + case "$i" in + */* | *\** | *\?* ) + log_always "module '$i' is not legal, skipping" + continue + ;; + esac + + if is_builtin_module http "$i" ; then + log "$i is builtin module, moving to NGX_HTTP_CONFLOAD" + http_confload=$(append_list "${http_confload}" "$i") + continue + fi + + ## naive deduplication + if list_have_item "${http_modules}" "$i" ; then + log "$i is already specified" + continue + fi + + http_modules=$(append_list "${http_modules}" "$i") + done ; unset i + + ## grpc depends on http/2 + if list_have_item "${http_confload}" grpc ; then + http_confload="${http_confload} v2" + fi + + ## fixes + if list_have_item "${http_confload}" v2 ; then + export NGX_HTTP_V2=1 + fi + if list_have_item "${http_confload}" v3 ; then + export NGX_HTTP_V3=1 + fi + if list_have_item "${http_confload}" proxy ; then + export NGX_HTTP_PROXY=1 + fi + + ## adjustments + [ "${NGX_HTTP_V2}" = 0 ] || http_confload="${http_confload} v2" + [ "${NGX_HTTP_V3}" = 0 ] || http_confload="${http_confload} v3" + [ "${NGX_HTTP_PROXY}" = 0 ] || http_confload="${http_confload} proxy" + + if [ -n "${http_modules:-}" ] ; then + ## angie-module-lua: depends on angie-module-ndk + ## angie-module-set-misc: depends on angie-module-ndk + unset want_ndk ; want_ndk=0 + if list_have_item "${http_modules}" lua ; then + want_ndk=1 + elif list_have_item "${http_modules}" set-misc ; then + want_ndk=1 + fi + if [ ${want_ndk} = 1 ] ; then + ## forcefully move 'ndk' to beginning of list + http_modules=$(printf '%s' " ${http_modules} " | sed -zE 's/ ndk / /;s/^/ndk/;s/ $//') + fi + unset want_ndk + + ## angie-module-wasm: http module requires core module to be loaded too + while : ; do + list_have_item "${http_modules}" wasm || break + if list_have_item "${NGX_CORE_MODULES}" wasm ; then break ; fi + + log_always "adjusting NGX_CORE_MODULES to include 'wasm'" + NGX_CORE_MODULES=$(append_list "${NGX_CORE_MODULES}" wasm) + export NGX_CORE_MODULES + break ; done + fi + + set -a + NGX_HTTP_MODULES="${http_modules}" + NGX_HTTP_CONFLOAD=$(sort_dedup_list "${http_confload}") + set +a + + unset http_modules http_confload + + ## adjust caches + unset m + for m in fastcgi proxy scgi uwsgi ; do + list_have_item "${NGX_HTTP_CONFLOAD}" $m || continue + NGX_HTTP_CACHES="${NGX_HTTP_CACHES} temp_${m}" + done ; unset m + NGX_HTTP_CACHES=$(sort_dedup_list "${NGX_HTTP_CACHES}") +fi diff --git a/image-entry.d/22-http-ssl.envsh b/image-entry.d/22-http-ssl.envsh new file mode 100755 index 0000000..6fc45ad --- /dev/null +++ b/image-entry.d/22-http-ssl.envsh @@ -0,0 +1,9 @@ +#!/bin/sh + +if [ "${NGX_HTTP}" = 0 ] ; then + unset NGX_HTTP_SSL_PROFILE +else + ## here should be SANE defaults (!) + NGX_HTTP_SSL_PROFILE="${NGX_HTTP_SSL_PROFILE:-intermediate}" + export NGX_HTTP_SSL_PROFILE +fi diff --git a/image-entry.d/23-http-max-ranges.envsh b/image-entry.d/23-http-max-ranges.envsh new file mode 100755 index 0000000..a8e2fc8 --- /dev/null +++ b/image-entry.d/23-http-max-ranges.envsh @@ -0,0 +1,30 @@ +#!/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 + + if [ -z "${NGX_HTTP_MAX_RANGES:-}" ] ; then + unset NGX_HTTP_MAX_RANGES + else + case "${NGX_HTTP_MAX_RANGES}" in + ## allow values within [1;999] + [1-9] | [1-9][0-9] | [1-9][0-9][0-9] ) ;; + 0 ) + log_always "HTTP: Range/If-Range/Accept-Ranges support is disabled by NGX_HTTP_MAX_RANGES=0" + ;; + * ) + 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 + fi + + unset _NGX_HTTP_MAX_RANGES +fi diff --git a/image-entry.d/24-http-forward-headers.envsh b/image-entry.d/24-http-forward-headers.envsh new file mode 100755 index 0000000..9edec4b --- /dev/null +++ b/image-entry.d/24-http-forward-headers.envsh @@ -0,0 +1,112 @@ +#!/bin/sh + +if [ "${NGX_HTTP}" = 0 ] ; then + unset NGX_HTTP_TRANSPARENT_PROXY NGX_HTTP_FAKE_UA NGX_HTTP_FORWARDED NGX_HTTP_X_FORWARDED NGX_HTTP_X_REAL_IP +else + unset _NGX_HTTP_FAKE_UA _NGX_HTTP_FORWARDED _NGX_HTTP_X_FORWARDED _NGX_HTTP_X_REAL_IP + ## here should be SANE defaults (!) + _NGX_HTTP_FAKE_UA='Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36' + _NGX_HTTP_FORWARDED=pass + _NGX_HTTP_X_FORWARDED=pass + _NGX_HTTP_X_REAL_IP=pass + + NGX_HTTP_TRANSPARENT_PROXY=$(gobool_to_int "${NGX_HTTP_TRANSPARENT_PROXY:-0}" 0) + export NGX_HTTP_TRANSPARENT_PROXY + + [ -n "${NGX_HTTP_FAKE_UA:-}" ] || NGX_HTTP_FAKE_UA=${_NGX_HTTP_FAKE_UA} + export NGX_HTTP_FAKE_UA + + if [ -n "${NGX_HTTP_FORWARDED:-}" ] ; then + NGX_HTTP_FORWARDED=$(printf '%s' "${NGX_HTTP_FORWARDED}" | tr '[:upper:]' '[:lower:]') + fi + if [ -n "${NGX_HTTP_X_FORWARDED:-}" ] ; then + NGX_HTTP_X_FORWARDED=$(printf '%s' "${NGX_HTTP_X_FORWARDED}" | tr '[:upper:]' '[:lower:]') + fi + if [ -n "${NGX_HTTP_X_REAL_IP:-}" ] ; then + NGX_HTTP_X_REAL_IP=$(printf '%s' "${NGX_HTTP_X_REAL_IP}" | tr '[:upper:]' '[:lower:]') + fi + + if [ "${NGX_HTTP_TRANSPARENT_PROXY}" = 1 ] ; then + if [ -n "${NGX_HTTP_FORWARDED:-}" ] ; then + log_always "NGX_HTTP_FORWARDED: overridden to 'remove' due to NGX_HTTP_TRANSPARENT_PROXY=1" + fi + NGX_HTTP_FORWARDED=remove + + if [ -n "${NGX_HTTP_X_FORWARDED:-}" ] ; then + log_always "NGX_HTTP_X_FORWARDED: overridden to 'remove' due to NGX_HTTP_TRANSPARENT_PROXY=1" + fi + NGX_HTTP_X_FORWARDED=remove + + if [ -n "${NGX_HTTP_X_REAL_IP:-}" ] ; then + log_always "NGX_HTTP_X_REAL_IP: overridden to 'remove' due to NGX_HTTP_TRANSPARENT_PROXY=1" + fi + NGX_HTTP_X_REAL_IP=remove + else + if [ -z "${NGX_HTTP_FORWARDED:-}" ] ; then + NGX_HTTP_FORWARDED=${_NGX_HTTP_FORWARDED} + fi + case "${NGX_HTTP_FORWARDED}" in + pass | remove ) ;; + * ) + unset x + x=$(gobool_to_int "${NGX_HTTP_FORWARDED}") + case "$x" in + 0 ) NGX_HTTP_FORWARDED=remove ;; + 1 ) NGX_HTTP_FORWARDED=pass ;; + * ) + log_always "NGX_HTTP_FORWARDED: unrecognized value: ${NGX_HTTP_FORWARDED}" + log_always "setting NGX_HTTP_FORWARDED=${_NGX_HTTP_FORWARDED}" + NGX_HTTP_FORWARDED=${_NGX_HTTP_FORWARDED} + ;; + esac + unset x + ;; + esac + + if [ -z "${NGX_HTTP_X_FORWARDED:-}" ] ; then + NGX_HTTP_X_FORWARDED=${_NGX_HTTP_X_FORWARDED} + fi + case "${NGX_HTTP_X_FORWARDED}" in + pass | remove ) ;; + * ) + unset x + x=$(gobool_to_int "${NGX_HTTP_X_FORWARDED}") + case "$x" in + 0 ) NGX_HTTP_X_FORWARDED=remove ;; + 1 ) NGX_HTTP_X_FORWARDED=pass ;; + * ) + log_always "NGX_HTTP_X_FORWARDED: unrecognized value: ${NGX_HTTP_X_FORWARDED}" + log_always "setting NGX_HTTP_X_FORWARDED=${_NGX_HTTP_X_FORWARDED}" + NGX_HTTP_X_FORWARDED=${_NGX_HTTP_X_FORWARDED} + ;; + esac + unset x + ;; + esac + + if [ -z "${NGX_HTTP_X_REAL_IP:-}" ] ; then + NGX_HTTP_X_REAL_IP=${_NGX_HTTP_X_REAL_IP} + fi + case "${NGX_HTTP_X_REAL_IP}" in + pass | remove ) ;; + * ) + unset x + x=$(gobool_to_int "${NGX_HTTP_X_REAL_IP}") + case "$x" in + 0 ) NGX_HTTP_X_REAL_IP=remove ;; + 1 ) NGX_HTTP_X_REAL_IP=pass ;; + * ) + log_always "NGX_HTTP_X_REAL_IP: unrecognized value: ${NGX_HTTP_X_REAL_IP}" + log_always "setting NGX_HTTP_X_REAL_IP=${_NGX_HTTP_X_REAL_IP}" + NGX_HTTP_X_REAL_IP=${_NGX_HTTP_X_REAL_IP} + ;; + esac + unset x + ;; + esac + fi + + export NGX_HTTP_FORWARDED NGX_HTTP_X_FORWARDED NGX_HTTP_X_REAL_IP + + unset _NGX_HTTP_FAKE_UA _NGX_HTTP_FORWARDED _NGX_HTTP_X_FORWARDED _NGX_HTTP_X_REAL_IP +fi diff --git a/image-entry.d/30-mail.envsh b/image-entry.d/30-mail.envsh new file mode 100755 index 0000000..9f84dc6 --- /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_CONFLOAD +else + set -a + NGX_MAIL_MODULES="${NGX_MAIL_MODULES:-}" + NGX_MAIL_CONFLOAD="${NGX_MAIL_CONFLOAD:-}" + set +a +fi diff --git a/image-entry.d/31-mail-modules.envsh b/image-entry.d/31-mail-modules.envsh new file mode 100755 index 0000000..0ec2182 --- /dev/null +++ b/image-entry.d/31-mail-modules.envsh @@ -0,0 +1,41 @@ +#!/bin/sh + +if [ "${NGX_MAIL}" = 1 ] ; then + unset mail_modules mail_confload + mail_modules= + mail_confload="${NGX_MAIL_CONFLOAD:-}" + + ## filter out builtin mail modules + unset i + for i in ${NGX_MAIL_MODULES:-} ; do + [ -n "$i" ] || continue + + case "$i" in + */* | *\** | *\?* ) + log_always "module '$i' is not legal, skipping" + continue + ;; + esac + + if is_builtin_module mail "$i" ; then + log "$i is builtin module, moving to NGX_MAIL_CONFLOAD" + mail_confload=$(append_list "${mail_confload}" "$i") + continue + fi + + ## naive deduplication + if list_have_item "${mail_modules}" "$i" ; then + log "$i is already specified" + continue + fi + + mail_modules=$(append_list "${mail_modules}" "$i") + done ; unset i + + set -a + NGX_MAIL_MODULES="${mail_modules}" + NGX_MAIL_CONFLOAD=$(sort_dedup_list "${mail_confload}") + set +a + + unset mail_modules mail_confload +fi diff --git a/image-entry.d/32-mail-ssl.envsh b/image-entry.d/32-mail-ssl.envsh new file mode 100755 index 0000000..170155b --- /dev/null +++ b/image-entry.d/32-mail-ssl.envsh @@ -0,0 +1,9 @@ +#!/bin/sh + +if [ "${NGX_MAIL}" = 0 ] ; then + unset NGX_MAIL_SSL_PROFILE +else + ## here should be SANE defaults (!) + NGX_MAIL_SSL_PROFILE="${NGX_MAIL_SSL_PROFILE:-intermediate}" + export NGX_MAIL_SSL_PROFILE +fi diff --git a/image-entry.d/40-stream.envsh b/image-entry.d/40-stream.envsh new file mode 100755 index 0000000..e1e90e7 --- /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_CONFLOAD +else + set -a + NGX_STREAM_MODULES="${NGX_STREAM_MODULES:-}" + NGX_STREAM_CONFLOAD="${NGX_STREAM_CONFLOAD:-}" + set +a +fi diff --git a/image-entry.d/41-stream-modules.envsh b/image-entry.d/41-stream-modules.envsh new file mode 100755 index 0000000..1ae0a56 --- /dev/null +++ b/image-entry.d/41-stream-modules.envsh @@ -0,0 +1,42 @@ +#!/bin/sh + +if [ "${NGX_STREAM}" = 1 ] ; then + unset stream_modules stream_confload + stream_modules= + ## stream module is almost meaningless without proxy [configuration] + stream_confload="proxy ${NGX_STREAM_CONFLOAD:-}" + + ## filter out builtin stream modules + unset i + for i in ${NGX_STREAM_MODULES:-} ; do + [ -n "$i" ] || continue + + case "$i" in + */* | *\** | *\?* ) + log_always "module '$i' is not legal, skipping" + continue + ;; + esac + + if is_builtin_module stream "$i" ; then + log "$i is builtin module, moving to NGX_STREAM_CONFLOAD" + stream_confload=$(append_list "${stream_confload}" "$i") + continue + fi + + ## naive deduplication + if list_have_item "${stream_modules}" "$i" ; then + log "$i is already specified" + continue + fi + + stream_modules=$(append_list "${stream_modules}" "$i") + done ; unset i + + set -a + NGX_STREAM_MODULES="${stream_modules}" + NGX_STREAM_CONFLOAD=$(sort_dedup_list "${stream_confload}") + set +a + + unset stream_modules stream_confload +fi diff --git a/image-entry.d/42-stream-ssl.envsh b/image-entry.d/42-stream-ssl.envsh new file mode 100755 index 0000000..3c660cb --- /dev/null +++ b/image-entry.d/42-stream-ssl.envsh @@ -0,0 +1,9 @@ +#!/bin/sh + +if [ "${NGX_STREAM}" = 0 ] ; then + unset NGX_STREAM_SSL_PROFILE +else + ## here should be SANE defaults (!) + NGX_STREAM_SSL_PROFILE="${NGX_STREAM_SSL_PROFILE:-intermediate}" + export NGX_STREAM_SSL_PROFILE +fi diff --git a/image-entry.d/70-merge-dirs.sh b/image-entry.d/70-merge-dirs.sh new file mode 100755 index 0000000..42caa4e --- /dev/null +++ b/image-entry.d/70-merge-dirs.sh @@ -0,0 +1,40 @@ +#!/bin/sh +set -ef + +. /run/ngx/iep/00-common.envsh + +## hack: override "cache", "lib" and "log" from /angie (and possibly from /etc/angie) +fake_dir=$(mktemp -d) +for n in ${persist_dirs} ; do touch "${fake_dir}/$n" ; done + +install -d "${target_root}" +overlaydirs --merge "${target_root}" /etc/angie.dist /etc/angie /angie "${fake_dir}" + +## fixup after merge +for n in ${persist_dirs} ; do rm -f "${target_root}/$n" ; done +rm -rf "${fake_dir}" + +if [ "${NGX_HTTP_STATIC_MERGE}" = 0 ] ; then + src0=/etc/angie.dist/static + dst="${target_root}/static" + + rm -rf "${dst}" + for r in /angie /etc/angie ; do + src="$r/static" + [ -d "${src}" ] || continue + if [ -h "${src}" ] ; then + log_always "${src} is a symbolic link, skipping!" + continue + fi + + install -d "${dst}" + overlaydirs --merge "${dst}" ${src0} "${src}" + done + [ -d "${dst}" ] || { + log_always "static directory is almost empty!" + install -d "${dst}" + overlaydirs --merge "${dst}" ${src0} + } +fi + +exit 0 diff --git a/image-entry.d/71-topmost-configs.sh b/image-entry.d/71-topmost-configs.sh new file mode 100755 index 0000000..d5d7799 --- /dev/null +++ b/image-entry.d/71-topmost-configs.sh @@ -0,0 +1,20 @@ +#!/bin/sh +set -f + +. /run/ngx/iep/00-common.envsh + +s="${target_root}" +d="${volume_root}/conf.ctx" + +comps= +[ "${NGX_HTTP}" = 0 ] || comps="${comps} http" +[ "${NGX_MAIL}" = 0 ] || comps="${comps} mail" +[ "${NGX_STREAM}" = 0 ] || comps="${comps} stream" + +install -d "$d" +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/72-fullfil-tree.sh b/image-entry.d/72-fullfil-tree.sh new file mode 100755 index 0000000..21fd320 --- /dev/null +++ b/image-entry.d/72-fullfil-tree.sh @@ -0,0 +1,58 @@ +#!/bin/sh +set -ef + +. /run/ngx/iep/00-common.envsh + +user_install -d "${volume_root}/lock" + +for n in ${persist_dirs} ; do + [ -n "$n" ] || continue + + s="/angie/$n" + d="${volume_root}/$n" + + while : ; do + [ -d "$s" ] || break + if [ -h "$s" ] ; then + log_always "$s is a symbolic link, skipping!" + break + fi + + ln_s "$s" "$d" + ## NB: we're NOT using "chmod -R" due to heavy and (potentially) unnecessary i/o + [ "${IEP_ROOT}" = 0 ] || chown "${NGX_USER}:${NGX_GROUP}" "$s" || : + break ; done + + [ -d "$d" ] || user_install -d "$d" +done + +## provide same symlinks as upstream (both Angie and nginx) docker images do +d="${volume_root}/log" +[ -e "$d/access.log" ] || ln -s /dev/stdout "$d/access.log" +[ -e "$d/error.log" ] || ln -s /dev/stderr "$d/error.log" + +d="${volume_root}/cache" +for n in ${NGX_HTTP_CACHES:-} ; do + [ -n "$n" ] || continue + + [ -d "$d/$n" ] || user_install -d "$d/$n" +done + +if list_have_item "${NGX_HTTP_CONFLOAD}" acme ; then + d="${volume_root}/lib/acme" + [ -d "$d" ] || user_install -d "$d" +fi + +if list_have_item "${NGX_HTTP_MODULES}" modsecurity ; then + d="${target_root}/lib/modsecurity" + [ -d "$d" ] || user_install -d "$d" + + d="${target_root}/log" + for n in modsecurity modsecurity/concurrent ; do + [ -n "$n" ] || continue + + [ -d "$d/$n" ] || user_install -d "$d/$n" + done +fi + +exit 0 diff --git a/image-entry.d/73-expand-templates.sh b/image-entry.d/73-expand-templates.sh new file mode 100755 index 0000000..fc3f2e3 --- /dev/null +++ b/image-entry.d/73-expand-templates.sh @@ -0,0 +1,114 @@ +#!/bin/sh +set -f + +. /run/ngx/iep/00-common.envsh + +## Angie: unset core variables +unset ANGIE ANGIE_BPF_MAPS + +[ "${NGX_STRICT_LOAD}" = 0 ] || set -e + +cd "${target_root}/" + +expand_error_delim() { + IEP_DEBUG=0 log_always ' ----------------------------------- ' +} +unset expand_error_seen +expand_error() { + [ "${expand_error_seen:-}" != 1 ] || return + expand_error_seen=1 + expand_error_delim + log_always 'template expansion has failed' + if [ "${NGX_STRICT_LOAD}" = 1 ] ; then + t=15 + log_always "injecting delay for $t seconds" + expand_error_delim + sleep $t + exit 1 + fi + expand_error_delim +} + +set +e +## NB: j2cfg/ and static/ are handled separately +merge_dirs=$(find ./ -follow -mindepth 1 -maxdepth 1 -type d -printf '%P/\n' | grep -Fxv -e j2cfg/ -e static/ | sort -uV) +[ "${NGX_STRICT_LOAD}" = 0 ] || set -e + +unset ENVSUBST_ARGS +ENVSUBST_ARGS="${volume_root}/diag.envsubst.txt" +envsubst-args.sh > "${ENVSUBST_ARGS}" +export ENVSUBST_ARGS + +## envsubst is simple and fast +## expand j2cfg/ first, then other directories +expand_dir_envsubst j2cfg/ || expand_error +expand_dir_envsubst ${merge_dirs} || expand_error + +## j2cfg is more complex + +unset J2CFG_CONFIG +set -a +J2CFG_PATH="${target_root}/j2cfg" +J2CFG_SEARCH_PATH="${target_root}" +set +a + +## expand j2cfg/ first +expand_dir_j2cfg j2cfg/ || expand_error + +## dump [merged] j2cfg config +j2cfg_dump="${volume_root}/diag.j2cfg.yml" +j2cfg-dump > "${j2cfg_dump}" || expand_error +export J2CFG_CONFIG="${j2cfg_dump}" + +## expand other directories +expand_dir_j2cfg ${merge_dirs} || expand_error + +## expand static/ +## remove template sources in order to avoid leaking sensitive data +if [ "${NGX_HTTP_STATIC_TEMPLATE}" = 1 ] ; then + template_list=$(mktemp) + + find static/ -follow -name '*.in' -type f \ + | { + set +e + if [ -n "${NGX_STATIC_EXCLUDE_REGEX:-}" ] ; then + grep -Ev -e "${NGX_STATIC_EXCLUDE_REGEX}" + elif [ -n "${NGX_STATIC_INCLUDE_REGEX:-}" ] ; then + grep -E -e "${NGX_STATIC_INCLUDE_REGEX}" + else + cat + fi + } \ + | sort -uV > "${template_list}" + + while read -r src ; do + [ -n "${src}" ] || continue + expand_file_envsubst "${src}" || expand_error + rm -fv "${src}" + done < "${template_list}" + + find static/ -follow -name '*.j2' -type f -printf '%p\0' \ + | { + set +e + if [ -n "${NGX_STATIC_EXCLUDE_REGEX:-}" ] ; then + grep -zEv -e "${NGX_STATIC_EXCLUDE_REGEX}" + elif [ -n "${NGX_STATIC_INCLUDE_REGEX:-}" ] ; then + grep -zE -e "${NGX_STATIC_INCLUDE_REGEX}" + else + cat + fi + } \ + | sort -zuV > "${template_list}" + + if [ -s "${template_list}" ] ; then + xargs -0r -n 1000 -a "${template_list}" \ + j2cfg-multi < /dev/null || expand_error + + xargs -0r -n 1000 -a "${template_list}" \ + rm -fv < /dev/null + fi + + rm -f "${template_list}" +fi + +exit 0 diff --git a/image-entry.d/74-combine-tree.sh b/image-entry.d/74-combine-tree.sh new file mode 100755 index 0000000..aec35d2 --- /dev/null +++ b/image-entry.d/74-combine-tree.sh @@ -0,0 +1,143 @@ +#!/bin/sh +set -f + +. /run/ngx/iep/00-common.envsh + +[ "${NGX_STRICT_LOAD}" = 0 ] || set -e + +load_error_delim() { + IEP_DEBUG=0 log_always ' ----------------------------------- ' +} +unset load_error_seen +load_error() { + [ "${load_error_seen:-}" != 1 ] || return + load_error_seen=1 + load_error_delim + log_always 'tree combine has failed' + if [ "${NGX_STRICT_LOAD}" = 1 ] ; then + t=15 + log_always "injecting delay for $t seconds" + load_error_delim + sleep $t + exit 1 + fi + load_error_delim +} + +install -d "${volume_root}/conf.load" + +## Angie modules are loaded in [strict] order! +combine_modules() { + [ -n "$1" ] || return 1 + local n + n="$1" ; shift + + [ $# -ne 0 ] || return 0 + + local i m src_dir dst_dir src_name dst_name src_path dst_path dst_dir + src_dir="${target_root}/mod" + dst_dir="${volume_root}/conf.load" + + i=0 + for m ; do + [ -n "$m" ] || continue + + case "$m" in + /* | */../* | *\** | *\?* ) + log_always "module config filename '$m' is not legal, skipping" + continue + ;; + esac + + case "$m" in + */* ) src_name="$m.conf" ;; + * ) src_name="$n-$m.conf" ;; + esac + dst_name=$(printf 'mod-%s-%02d-%s.conf' "$n" "$i" "$m" | tr -s '/_' '_') + + src_path="${src_dir}/${src_name}" + if ! [ -f "${src_path}" ] ; then + log_always "file ${src_name} is not found in ${src_dir}/" + load_error + log "file ${src_name} is skipped" + continue + fi + + dst_path="${dst_dir}/${dst_name}" + + ln_cp "${src_path}" "${dst_path}" + + i=$((i+1)) + done +} + +combine_confload() { + [ -n "$1" ] || return 1 + local n + n="$1" ; shift + + [ $# -ne 0 ] || return 0 + + local s src_dir dst_dir src_name dst_name src_path dst_path + src_dir="${target_root}/conf" + dst_dir="${volume_root}/conf.load" + + for s ; do + [ -n "$s" ] || continue + + case "$s" in + /* | */../* | *\** | *\?* ) + log_always "config filename '$s' is not legal, skipping" + continue + ;; + esac + + case "$s" in + */* ) src_name="$s.conf" ;; + * ) src_name="$n-$s.conf" ;; + esac + dst_name=$(printf '%s-%s.conf' "$n" "$s" | tr -s '/_' '_') + + dst_path="${dst_dir}/${dst_name}" + if [ -e "${dst_path}" ] ; then + log "${dst_path} already exists, skipping" + continue + fi + + src_path="${src_dir}/${src_name}" + if ! [ -f "${src_path}" ] ; then + log_always "file ${src_name} is not found in ${src_dir}/" + if [ "${NGX_ALLOW_MISSING_CONFLOAD:-}" != 1 ] ; then + load_error + log "file ${src_name} is skipped" + fi + continue + fi + + ln_cp "${src_path}" "${dst_path}" + 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:-} + +loose=$(( 1 - NGX_STRICT_LOAD )) +NGX_ALLOW_MISSING_CONFLOAD=$(gobool_to_int "${NGX_ALLOW_MISSING_CONFLOAD:-${loose}}" ${loose}) + +combine_confload core ${NGX_CORE_CONFLOAD:-} +combine_confload core_ev ${NGX_CORE_EVENTS_CONFLOAD:-} +combine_confload http ${NGX_HTTP_CONFLOAD:-} +combine_confload mail ${NGX_MAIL_CONFLOAD:-} +combine_confload stream ${NGX_STREAM_CONFLOAD:-} + +## some modules doesn't have configuration at all +NGX_ALLOW_MISSING_CONFLOAD=1 + +combine_confload core ${NGX_CORE_MODULES:-} +combine_confload http ${NGX_HTTP_MODULES:-} +combine_confload mail ${NGX_MAIL_MODULES:-} +combine_confload stream ${NGX_STREAM_MODULES:-} + +exit 0 diff --git a/image-entry.d/75-adjust-core-user.sh b/image-entry.d/75-adjust-core-user.sh new file mode 100755 index 0000000..d8e3711 --- /dev/null +++ b/image-entry.d/75-adjust-core-user.sh @@ -0,0 +1,16 @@ +#!/bin/sh +set -f + +. /run/ngx/iep/00-common.envsh + +conf=/run/ngx/conf/autoconf/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/76-openssl-ca-certs.envsh b/image-entry.d/76-openssl-ca-certs.envsh new file mode 100755 index 0000000..fe509bc --- /dev/null +++ b/image-entry.d/76-openssl-ca-certs.envsh @@ -0,0 +1,135 @@ +#!/bin/sh + +unset def_bundle def_bundle_fp +def_bundle='/etc/ssl/certs/ca-certificates.crt' +def_bundle_fp="${def_bundle}.fp" + +while : ; do + if [ -n "${SSL_CERT_FILE:-}" ] ; then + log_always "NOT merging CA certificates (if any): SSL_CERT_FILE is already set (=${SSL_CERT_FILE})" + break + fi + + [ -d "${target_root}/tls/ca" ] || break + + unset w + w=$(mktemp -d) || break + + find "${target_root}/tls/ca/" -follow -type f | sort -V > "$w/all.list" + [ -s "$w/all.list" ] || break + + ## entering processing section + touch "$w/processing" + + unset orig_ca_file + while read -r orig_ca_file ; do + [ -n "${orig_ca_file}" ] || continue + + openssl-cert-auto-pem.sh "${orig_ca_file}" + done < "$w/all.list" > "$w/all.pem" + unset orig_ca_file + [ -s "$w/all.pem" ] || break + + openssl-cert-auto-pem.sh "$w/all.pem" "$w/new.pem" "$w/new.fp" "$w/new.off" + [ -s "$w/new.pem" ] || break + [ -s "$w/new.fp" ] || break + [ -s "$w/new.off" ] || break + rm -f "$w/all.pem" + + ## leaving processing section + rm -f "$w/processing" + + unset def_bundle_bind_mount + def_bundle_bind_mount=1 + while : ; do + unset devno_root + devno_root=$(env stat -c '%d' / ) + + [ -f "${def_bundle}" ] || break + unset devno_bundle + devno_bundle=$(env stat -L -c '%d' "${def_bundle}") + [ "${devno_root}" = "${devno_bundle}" ] || break + + [ -f "${def_bundle_fp}" ] || break + unset devno_bundle_fp + devno_bundle_fp=$(env stat -L -c '%d' "${def_bundle_fp}") + [ "${devno_root}" = "${devno_bundle_fp}" ] || break + + def_bundle_bind_mount=0 + break ; done + unset devno_root devno_bundle devno_bundle_fp + + if [ "${def_bundle_bind_mount}" = 1 ] ; then + log_always "detected bind-mount inside ${def_bundle%/*}/" + log_always "this is merely misuse!" + + if [ -s "${def_bundle}" ] ; then + openssl-cert-auto-pem.sh "${def_bundle}" "$w/cacert.pem" "$w/cacert.fp" + fi + else + ln -s "${def_bundle}" "$w/cacert.pem" + ln -s "${def_bundle_fp}" "$w/cacert.fp" + fi + + unset with_def_bundle + with_def_bundle=0 + while : ; do + [ -s "$w/cacert.pem" ] || break + [ -s "$w/cacert.fp" ] || break + + with_def_bundle=1 + break ; done + + if [ "${with_def_bundle}" = 1 ] ; then + grep -Fxnv -f "$w/cacert.fp" "$w/new.fp" | cut -d : -f 1 > "$w/diff.ln" + [ -s "$w/diff.ln" ] || break + else + : > "$w/diff.ln" + fi + + : > "${volume_root}/ca.pem" + if [ "${with_def_bundle}" = 1 ] ; then + cat < "$w/cacert.pem" > "${volume_root}/ca.pem" + else + log_always "NOT using ${def_bundle} - empty or missing" + fi + + unset n off + while read -r n ; do + [ -n "$n" ] || continue + + off=$(sed -ne "${n}p" "$w/new.off") + [ -n "${off}" ] || continue + + sed -ne "${off}p" "$w/new.pem" + done < "$w/diff.ln" >> "${volume_root}/ca.pem" + unset n off + + set -a + SSL_CERT_FILE="${volume_root}/ca.pem" + ## merely a quirk + SSL_CERT_DIR="${empty_dir}" + set +a +break ; done +unset def_bundle_fp def_bundle_bind_mount with_def_bundle + +while ! [ -f "${volume_root}/ca.pem" ] ; do + [ -s "${def_bundle}" ] || break + ln -s "${def_bundle}" "${volume_root}/ca.pem" +break ; done +unset def_bundle +[ -f "${volume_root}/ca.pem" ] || : > "${volume_root}/ca.pem" + +if [ -n "${w:-}" ] ; then + if [ -f "$w/processing" ] ; then + rm -f "$w/processing" + log_always "unable to merge CA certificates (see below for details):" + log_always "directory listing:" + env -C "$w" ls -lA >&2 + log_always "directory listing (following symlinks):" + env -C "$w" ls -L -lA >&2 + log_always "consider contacting developers" + fi + rm -rf "$w" +fi +unset w 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..4a0facc --- /dev/null +++ b/image-entry.d/90-angie-config-test.sh @@ -0,0 +1,31 @@ +#!/bin/sh +set -f + +. /run/ngx/iep/00-common.envsh + +## Angie: unset core variables +unset ANGIE ANGIE_BPF_MAPS + +## merely debug test +log_always 'test Angie configuration:' +log_always '=========================' +( + exec 1>"${volume_root}/diag.angie.conf" + angie -T +) +r=$? +log_always '=========================' + +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 + +## cleanup after test +rm -f "${volume_root}/angie.pid" + +exit 0 diff --git a/image-entry.d/99-cleanup-env.envsh b/image-entry.d/99-cleanup-env.envsh new file mode 100755 index 0000000..428c84d --- /dev/null +++ b/image-entry.d/99-cleanup-env.envsh @@ -0,0 +1,67 @@ +#!/bin/sh + +## Angie: unset core variables +unset ANGIE ANGIE_BPF_MAPS + +IEP_RETAIN_ENV=$(gobool_to_int "${IEP_RETAIN_ENV:-0}" 0) + +if [ "${IEP_RETAIN_ENV}" = 1 ] ; then + log_always "NOT removing following variables:" + sed -E '/^./s,^, ,' >&2 + echo >&2 +else + unset __set + __set="$-" + set +e + + unset __env __env_print + while read -r __env ; do + [ -n "${__env}" ] || continue + + case "${__env}" in + \'* | \"* ) + log "skipping variable (malformed): ${__env}" >&2 + continue + ;; + esac + + if [ "${IEP_DEBUG}" = 1 ] ; then + __env_print="${__env}="$(printenv "${__env}") + __env_print=$(env printf '%q' "${__env_print}") + log_always "unsetting variable: ${__env_print}" + else + log "unsetting variable: ${__env}" + fi + + unset "${__env}" + done + unset __env __env_print + + [ -z "${__set}" ] || set -"${__set}" + unset __set +fi <<-EOF +$( + set +e + cat /proc/$$/environ \ + | sed -zEn '/^([^=]+).*$/s//\1/p' \ + | xargs -0r printf '%q\n' \ + | { + ## retain variables defined in ".core_worker_env" configuration key + ## (if it was specified somewhere in dictionaries - either yaml or json) + f="${target_root}/autoconf/core-worker-env.txt" + [ -s "$f" ] || exec cat + grep -Fxv -f "$f" + } \ + | { + ## remove environment variables: + ## 1. variables starting with "NGX" as they are used by configuration templates + ## 2. variables containing "_SERVICE" or "_PORT" as they are came from + ## container orchestration + grep -E \ + -e '^NGX' \ + -e '_(SERVICE|PORT)' \ + + } \ + | sort -uV +) +EOF diff --git a/image-entry.sh b/image-entry.sh new file mode 100755 index 0000000..2c3432a --- /dev/null +++ b/image-entry.sh @@ -0,0 +1,177 @@ +#!/bin/sh +set -f + +[ -n "${IEP_TRACE}" ] || IEP_TRACE=0 +[ "${IEP_TRACE}" = 1 ] || IEP_TRACE=0 +[ "${IEP_TRACE}" = 0 ] || echo "# trace: $(date +'%Y-%m-%d %H:%M:%S.%03N %z'): start" >&2 + +iep_prepare_env() { + ## Angie: unset core variables + unset ANGIE ANGIE_BPF_MAPS +} + +iep_preserve_env() { + ## preserve LD_PRELOAD + unset __IEP_LD_PRELOAD + __IEP_LD_PRELOAD="${LD_PRELOAD:-}" + unset LD_PRELOAD + + ## glibc: preserve GLIBC_TUNABLES + unset __IEP_GLIBC_TUNABLES + __IEP_GLIBC_TUNABLES="${GLIBC_TUNABLES:-}" + unset GLIBC_TUNABLES + + ## glibc: preserve MALLOC_ARENA_MAX + unset __IEP_MALLOC_ARENA_MAX + __IEP_MALLOC_ARENA_MAX="${MALLOC_ARENA_MAX:-4}" + export MALLOC_ARENA_MAX=2 + + ## jemalloc: preserve MALLOC_CONF + unset __IEP_MALLOC_CONF + __IEP_MALLOC_CONF="${MALLOC_CONF:-}" + unset MALLOC_CONF +} + +iep_restore_env() { + unset IEP_DEBUG IEP_VERBOSE IEP_TRACE IEP_ROOT IEP_RETAIN_ENV + + ## restore LD_PRELOAD + if [ -n "${__IEP_LD_PRELOAD:-}" ] ; then + export LD_PRELOAD="${__IEP_LD_PRELOAD}" + fi + unset __IEP_LD_PRELOAD + + ## glibc: restore GLIBC_TUNABLES + if [ -n "${__IEP_GLIBC_TUNABLES:-}" ] ; then + export GLIBC_TUNABLES="${__IEP_GLIBC_TUNABLES}" + fi + unset __IEP_GLIBC_TUNABLES + + ## glibc: restore MALLOC_ARENA_MAX + if [ -n "${__IEP_MALLOC_ARENA_MAX:-}" ] ; then + export MALLOC_ARENA_MAX="${__IEP_MALLOC_ARENA_MAX}" + fi + unset __IEP_MALLOC_ARENA_MAX + + ## jemalloc: restore MALLOC_CONF + if [ -n "${__IEP_MALLOC_CONF:-}" ] ; then + export MALLOC_CONF="${__IEP_MALLOC_CONF}" + fi + unset __IEP_MALLOC_CONF +} + +iep_flush_volume() { + ## try to flush volume twice (heisenbug) + unset i ; for i in 1 2 ; do + find /run/ngx/ -mindepth 1 -maxdepth 1 -exec rm -rf {} + || : + sleep 0.1 + done ; unset i + + if find /run/ngx/ -mindepth 1 -maxdepth 1 -printf . -quit | grep -Fq . ; then + exec 1>&2 + echo '========================================================================' + echo "unable to fully flush /run/ngx/:" + find /run/ngx/ -mindepth 1 -maxdepth 1 -exec ls -ld {} + + echo '========================================================================' + echo "injecting delay for 15 seconds" + echo '========================================================================' + sleep 15 + exit 1 + fi +} + +iep_prepare_env +iep_preserve_env +iep_flush_volume + +## early setup TMPDIR (affects "mktemp") +export TMPDIR=/run/ngx/tmp +install -d -m 03777 "${TMPDIR}" + +install -d /run/ngx/iep +overlaydirs --merge /run/ngx/iep /image-entry.dist /image-entry /image-entry.local + +unset __IEP_SRC ; __IEP_SRC="${0##*/}" +. /run/ngx/iep/00-common.envsh + +# IEP_TRACE=$(gobool_to_int "${IEP_TRACE:-0}" 0) +IEP_DEBUG=$(gobool_to_int "${IEP_DEBUG:-0}" 0) +IEP_VERBOSE=$(gobool_to_int "${IEP_VERBOSE:-${IEP_DEBUG}}" "${IEP_DEBUG}") +export IEP_TRACE IEP_DEBUG IEP_VERBOSE + +IEP_INIT=$(gobool_to_int "${IEP_INIT:-0}" 0) +## unexport IEP_INIT +unset x ; x="${IEP_INIT}" ; unset IEP_INIT ; IEP_INIT="$x" ; unset x + +## run parts (if any) +unset __IEP_SCRIPT +while read -r __IEP_SCRIPT ; do + [ -n "${__IEP_SCRIPT}" ] || continue + [ -f "${__IEP_SCRIPT}" ] || continue + + case "${__IEP_SCRIPT}" in + *.envsh ) + if ! [ -x "${__IEP_SCRIPT}" ] ; then + log "NOT sourcing ${__IEP_SCRIPT} - not executable" + continue + fi + if [ "${IEP_TRACE}" = 1 ] ; then + echo "# trace: $(date +'%Y-%m-%d %H:%M:%S.%03N %z'): source ${__IEP_SCRIPT}" >&2 + else + log "sourcing ${__IEP_SCRIPT}" + fi + __IEP_SRC="${__IEP_SCRIPT}" + . "${__IEP_SCRIPT}" + __IEP_SRC="${0##*/}" + ;; + * ) + if ! [ -x "${__IEP_SCRIPT}" ] ; then + log "NOT running ${__IEP_SCRIPT} - not executable" + continue + fi + if [ "${IEP_TRACE}" = 1 ] ; then + echo "# trace: $(date +'%Y-%m-%d %H:%M:%S.%03N %z'): run ${__IEP_SCRIPT}" >&2 + else + log "running ${__IEP_SCRIPT}" + fi + "${__IEP_SCRIPT}" + ;; + esac +done <&2 + +if [ "${IEP_DEBUG}" = 1 ] ; then + log_always "ready to run application: $*" +else + log_always "ready to run application" +fi +echo >&2 + +iep_restore_env + +## variables that are not so easily unsettable +unset IEP_ENV_CMD +for i in '_' 'SHLVL' ; do + IEP_ENV_CMD="${IEP_ENV_CMD:-}${IEP_ENV_CMD:+ }-u $i" +done + +## unexport IEP_INIT +unset x ; x="${IEP_INIT}" ; unset IEP_INIT ; IEP_INIT="$x" ; unset x + +if [ "${IEP_INIT}" = 1 ] ; then + exec \ + ${IEP_ENV_CMD:+ env ${IEP_ENV_CMD} } \ + catatonit \ + "$@" +else + exec \ + ${IEP_ENV_CMD:+ env ${IEP_ENV_CMD} } \ + "$@" +fi diff --git a/j2cfg/j2cfg-dump.py b/j2cfg/j2cfg-dump.py new file mode 100755 index 0000000..aaec791 --- /dev/null +++ b/j2cfg/j2cfg-dump.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 + +import os.path +import sys + + +def main(): + sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) + import j2cfg + + j = j2cfg.J2cfg(dump_only=True) + print(j.dump_config()) + + sys.exit(0) + + +if __name__ == "__main__": + main() diff --git a/j2cfg/j2cfg-multi.py b/j2cfg/j2cfg-multi.py new file mode 100755 index 0000000..b86050d --- /dev/null +++ b/j2cfg/j2cfg-multi.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python3 + +import os.path +import sys + + +def main(): + if len(sys.argv) < 2: + raise ValueError('not enough arguments (min: 1)') + + sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) + import j2cfg + + r = j2cfg.J2cfg(strict=False) + ret = 0 + for f in sys.argv[1:]: + if not r.render_file(f, None): + ret = 1 + + sys.exit(ret) + + +if __name__ == "__main__": + main() diff --git a/j2cfg/j2cfg-single.py b/j2cfg/j2cfg-single.py new file mode 100755 index 0000000..09f4e02 --- /dev/null +++ b/j2cfg/j2cfg-single.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python3 + +import os.path +import sys + + +def main(): + if len(sys.argv) < 2: + raise ValueError('not enough arguments (min: 1)') + if len(sys.argv) > 3: + raise ValueError('too many arguments (max: 2)') + + if not sys.argv[1]: + raise ValueError('specify input file') + + if (len(sys.argv) == 3) and (not sys.argv[2]): + raise ValueError('specify output file') + + sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) + import j2cfg + + r = j2cfg.J2cfg() + r.render_file(*sys.argv[1:]) + sys.exit(0) + + +if __name__ == "__main__": + main() diff --git a/j2cfg/j2cfg/__init__.py b/j2cfg/j2cfg/__init__.py new file mode 100644 index 0000000..5ec41a8 --- /dev/null +++ b/j2cfg/j2cfg/__init__.py @@ -0,0 +1,266 @@ +import importlib +import json +import os +import os.path +import sys + +import jinja2 +import wcmatch.wcmatch +import yaml + +from .functions import * +from .settings import * + + +J2CFG_CONFIG_EXT = ['yml', 'yaml', 'json'] + + +class J2cfg: + def __init__(self, strict=True, config_file=None, config_path=None, + modules=None, search_path=None, template_suffix=None, + dump_only=False): + + if dump_only is None: + self.dump_only = False + else: + self.dump_only = bool(dump_only) + + self.config_file = config_file or os.getenv('J2CFG_CONFIG') + if self.config_file is not None: + self.config_file = str(self.config_file) + + if config_path is not None: + self.config_path = any_to_str_list(config_path) + else: + self.config_path = os.getenv('J2CFG_PATH') + if self.config_path is not None: + self.config_path = str_split_to_list(self.config_path, ':') + if self.config_path is not None: + self.config_path = uniq_str_list(self.config_path) + else: + self.config_path = J2CFG_PATH + + self.kwargs = {'j2cfg': {}} + + def merge_dict_from_file(filename): + if filename is None: + return False + f = str(filename) + if f == '': + return False + if not os.path.exists(f): + return False + if not os.path.isfile(f): + print( + f'J2cfg: not a file, skipping: {filename}', + file=sys.stderr) + return False + + if f.endswith('.yml') or f.endswith('.yaml'): + with open(f, mode='r', encoding='utf-8') as fx: + for x in yaml.safe_load_all(fx): + if not x: + continue + self.kwargs['j2cfg'] = merge_dict_recurse( + self.kwargs['j2cfg'], x + ) + return True + + if f.endswith('.json'): + with open(f, mode='r', encoding='utf-8') as fx: + self.kwargs['j2cfg'] = merge_dict_recurse( + self.kwargs['j2cfg'], json.load(fx) + ) + return True + + print( + f'J2cfg: non-recognized name extension: {filename}', + file=sys.stderr) + return False + + def merge_dict_default(): + search_pattern = '|'.join(['*.' + ext for ext in J2CFG_CONFIG_EXT]) + search_flags = wcmatch.wcmatch.SYMLINKS + + for d in self.config_path: + if not os.path.isdir(d): + continue + m = wcmatch.wcmatch.WcMatch(d, search_pattern, + flags=search_flags) + for f in sorted(m.match()): + if self.dump_only: + real_f = os.path.realpath(f) + if f == real_f: + print( + f'J2cfg: try loading {f}', + file=sys.stderr + ) + else: + print( + f'J2cfg: try loading {f} <- {real_f}', + file=sys.stderr + ) + merge_dict_from_file(f) + + if self.config_file is None: + merge_dict_from_file(J2CFG_DEFAULTS_FILE) + merge_dict_default() + else: + if os.path.isfile(self.config_file): + merge_dict_from_file(self.config_file) + else: + print( + 'J2cfg: J2cfg config file does not exist, skipping: ' + + f'{self.config_file}', + file=sys.stderr + ) + + if self.dump_only: + return + + self.strict = strict + if not isinstance(self.strict, bool): + self.strict = True + + self.search_path = search_path + if self.search_path is None: + self.search_path = os.getenv('J2CFG_SEARCH_PATH') + if self.search_path is not None: + self.search_path = str_split_to_list(self.search_path, ':') + if self.search_path is None: + self.search_path = self.config_path.copy() + else: + self.search_path = uniq_str_list(any_to_str_list(self.search_path)) + # RFC: should we use the current working directory early? + for d in [os.getcwd()]: + if d not in self.search_path: + self.search_path.insert(0, d) + + self.modules = modules or os.getenv('J2CFG_MODULES') + if self.modules is None: + self.modules = J2CFG_PYTHON_MODULES + else: + if isinstance(self.modules, str): + self.modules = str_split_to_list(self.modules) + else: + self.modules = any_to_str_list(self.modules) + self.modules = uniq_str_list(self.modules) + + self.template_suffix = template_suffix or os.getenv('J2CFG_SUFFIX') + if self.template_suffix is None: + self.template_suffix = J2CFG_TEMPLATE_EXT + else: + self.template_suffix = str(self.template_suffix) + if self.template_suffix == '': + self.template_suffix = J2CFG_TEMPLATE_EXT + if not self.template_suffix.startswith('.'): + self.template_suffix = '.' + self.template_suffix + + self.kwargs.update({ + 'env': os.environ, + 'env_vars_preserve': J2CFG_PRESERVE_ENVS, + 'env_vars_passthrough': J2CFG_PASSTHROUGH_ENVS, + }) + for m in self.modules: + if m in self.kwargs: + print(f'J2cfg: kwargs already has {m} key', + file=sys.stderr) + continue + self.kwargs[m] = importlib.import_module(m) + + self.j2fs_loaders = { + d: jinja2.FileSystemLoader( + d, encoding='utf-8', followlinks=True, + ) for d in self.search_path + } + self.j2env = jinja2.Environment( + extensions=J2CFG_JINJA_EXTENSIONS, + loader=jinja2.ChoiceLoader([ + self.j2fs_loaders[d] for d in self.search_path + ]), + ) + + def init_env(e: jinja2.Environment): + for s in J2CFG_FILTERS: + n = s.__name__ + if n in e.filters: + print(f'J2cfg: filters already has {n} key', + file=sys.stderr) + continue + e.filters[n] = s + + init_env(self.j2env) + + def dump_config(self): + return yaml.safe_dump(self.kwargs['j2cfg']) + + def ensure_fs_loader_for(self, directory: str): + if self.dump_only: + raise ValueError('dump_only is True') + + if directory in self.j2fs_loaders: + return + self.j2fs_loaders[directory] = jinja2.FileSystemLoader( + directory, encoding='utf-8', followlinks=True, + ) + + def render_file(self, file_in, file_out=None) -> bool: + if self.dump_only: + raise ValueError('dump_only is True') + + def render_error(msg) -> bool: + if self.strict: + raise ValueError(msg) + print(f'J2cfg: {msg}', file=sys.stderr) + return False + + if file_in is None: + return render_error( + 'argument "file_in" is None') + f_in = str(file_in) + if f_in == '': + return render_error( + 'argument "file_in" is empty') + if not os.path.exists(f_in): + return render_error( + f'file is missing: {file_in}') + if not os.path.isfile(f_in): + return render_error( + f'not a file: {file_in}') + + f_out = file_out + if f_out is None: + if not f_in.endswith(self.template_suffix): + return render_error( + f'input file name extension mismatch: {file_in}') + f_out = os.path.splitext(f_in)[0] + + dirs = self.search_path.copy() + for d in [os.getcwd(), os.path.dirname(f_in)]: + if d in dirs: + continue + self.ensure_fs_loader_for(d) + dirs.insert(0, d) + if f_in.startswith('/'): + self.ensure_fs_loader_for('/') + dirs.append('/') + + j2_environ = self.j2env.overlay(loader=jinja2.ChoiceLoader([ + self.j2fs_loaders[d] for d in dirs + ])) + j2_template = j2_environ.get_template(f_in) + rendered = j2_template.render(**self.kwargs) + + if os.path.lexists(f_out): + if os.path.islink(f_out) or (not os.path.isfile(f_out)): + return render_error( + f'output file is not safely writable: {f_out}') + if os.path.exists(f_out): + if os.path.samefile(f_in, f_out): + return render_error( + f'unable to process template inplace: {file_in}') + + with open(f_out, mode='w', encoding='utf-8') as f: + f.write(rendered) + + return True diff --git a/j2cfg/j2cfg/functions.py b/j2cfg/j2cfg/functions.py new file mode 100644 index 0000000..3749fa2 --- /dev/null +++ b/j2cfg/j2cfg/functions.py @@ -0,0 +1,387 @@ +import collections.abc +import itertools +import os.path +import pathlib +import re +import sys + +import jinja2 + +from .settings import is_env_banned + + +def is_sequence(x) -> bool: + if isinstance(x, str): + return False + return isinstance(x, collections.abc.Sequence) + + +def is_mapping(x) -> bool: + return isinstance(x, collections.abc.Mapping) + + +def uniq(a: list | set) -> list: + return sorted(set(a)) + + +def remove_non_str(a: list | set) -> list: + return list(filter(lambda x: isinstance(x, str), a)) + + +def remove_empty_str(a: list | set) -> list: + return list(filter(None, a)) + + +def uniq_str_list(a: list | set) -> list: + return remove_empty_str(uniq(a)) + + +def str_split_to_list(s: str, sep=r'\s+') -> list: + return remove_empty_str(re.split(sep, s)) + + +def dict_to_env_str_list(x: dict) -> list: + r = [] + for k in sorted(x.keys()): + if x[k] is None: + r.append(f'{k}') + else: + r.append(f'{k}={str(x[k])}') + return r + + +def any_to_str_list(x) -> list: + if x is None: + return [] + if isinstance(x, str): + return [x] + if is_sequence(x): + return [str(e) for e in x] + if is_mapping(x): + return dict_to_env_str_list(x) + return [str(x)] + + +def is_re_match(x, pattern, flags=0) -> bool: + if isinstance(x, str): + return bool(re.match(pattern, x, flags)) + if is_sequence(x): + return any(is_re_match(v, pattern, flags) for v in x) + if is_mapping(x): + return any(is_re_match(v, pattern, flags) for v in x.keys()) + return False + + +def is_re_fullmatch(x, pattern, flags=0) -> bool: + if isinstance(x, str): + return bool(re.fullmatch(pattern, x, flags)) + if is_sequence(x): + return any(is_re_fullmatch(v, pattern, flags) for v in x) + if is_mapping(x): + return any(is_re_fullmatch(v, pattern, flags) for v in x.keys()) + return False + + +def re_match(x, pattern, flags=0): + if isinstance(x, str): + return re.match(pattern, x, flags) + if is_sequence(x): + return [v for v in x + if re_match(v, pattern, flags)] + if is_mapping(x): + return {k: v for k, v in x.items() + if re_match(k, pattern, flags)} + return None + + +def re_fullmatch(x, pattern, flags=0): + if isinstance(x, str): + return re.fullmatch(pattern, x, flags) + if is_sequence(x): + return [v for v in x + if re_fullmatch(v, pattern, flags)] + if is_mapping(x): + return {k: v for k, v in x.items() + if re_fullmatch(k, pattern, flags)} + return None + + +def re_match_negate(x, pattern, flags=0): + if isinstance(x, str): + return not bool(re.match(pattern, x, flags)) + if is_sequence(x): + return [v for v in x + if re_match_negate(v, pattern, flags)] + if is_mapping(x): + return {k: v for k, v in x.items() + if re_match_negate(k, pattern, flags)} + return x + + +def re_fullmatch_negate(x, pattern, flags=0): + if isinstance(x, str): + return not bool(re.fullmatch(pattern, x, flags)) + if is_sequence(x): + return [v for v in x + if re_fullmatch_negate(v, pattern, flags)] + if is_mapping(x): + return {k: v for k, v in x.items() + if re_fullmatch_negate(k, pattern, flags)} + return x + + +def dict_remap_keys(x: dict, key_map) -> dict: + if key_map is None: + return x + p = set(x.keys()) + m = {} + for k in x: + v = key_map(k) + if v == k: + continue + m[k] = v + p.discard(k) + p.discard(v) + return {k: x[k] for k in p} | {v: x[k] for k, v in m.items()} + + +def re_sub(x, pattern, repl, count=0, flags=0): + if isinstance(x, str): + return re.sub(pattern, repl, x, count, flags) + if is_sequence(x): + return [ + re_sub(v, pattern, repl, count, flags) + for v in x + ] + if is_mapping(x): + return dict_remap_keys( + x, lambda k: + re_sub(k, pattern, repl, count, flags) + ) + return x + + +def as_cgi_hdr(x): + if isinstance(x, str): + return 'HTTP_' + re.sub('[^A-Z0-9]+', '_', x.upper()).strip('_') + if is_sequence(x): + return uniq([ + as_cgi_hdr(v) + for v in x + ]) + if is_mapping(x): + return dict_remap_keys( + x, as_cgi_hdr + ) + return x + + +def as_ngx_var(x, pfx='custom'): + if isinstance(x, str): + parts = remove_empty_str( + [re.sub('[^a-z0-9]+', '_', str(i).lower()).strip('_') + for i in (pfx, x)] + ) + if len(parts) < 2: + print( + f'as_ngx_var: parts={parts}', + file=sys.stderr + ) + raise ValueError('as_ngx_var: incomplete string array') + return '$' + '_'.join(parts) + if is_sequence(x): + return uniq([ + as_ngx_var(v, pfx) + for v in x + ]) + if is_mapping(x): + return dict_remap_keys( + x, lambda k: + as_ngx_var(k, pfx) + ) + return x + + +def any_to_env_dict(x) -> dict: + if x is None: + return {} + + h = {} + + def feed(k, parse=False, v=None): + if v is None: + return + k = str(k) + if parse: + k2, m, v2 = k.partition('=') + if m == '=': + k = k2 + v = v2 + if not re.fullmatch(r'[a-zA-Z_][a-zA-Z0-9_]*', k): + return + if is_env_banned(k): + return + if k in h: + return + h[k] = v if v is None else str(v) + + if isinstance(x, str): + feed(x, True) + elif is_sequence(x): + for e in x: + feed(e, True) + elif is_mapping(x): + for k in x: + feed(k, False, x[k]) + else: + return {} + + return h + + +def dict_keys(x: dict) -> list: + return sorted([k for k in x.keys()]) + + +def dict_empty_keys(x: dict) -> list: + return sorted([k for k in x.keys() if x[k] is None]) + + +def dict_non_empty_keys(x: dict) -> list: + return sorted([k for k in x.keys() if x[k] is not None]) + + +def list_diff(a: list | set, b: list | set) -> list: + return list(set(a) - set(b)) + + +def list_intersect(a: list | set, b: list | set) -> list: + return list(set(a) & set(b)) + + +@jinja2.pass_environment +def sh_like_file_to_list(j2env, file_in: str) -> list: + tpl = j2env.get_template(file_in) + text = pathlib.Path(tpl.filename).read_text(encoding='utf-8') + lines = re.split(r'[\r\n]', text) + return list(itertools.filterfalse( + lambda x: re.match(r'\s*#', x), lines + )) + + +def ngx_esc(x): + if isinstance(x, str): + if x == "": + return "''" + if re.search(r'(?:\s|[;{}()\[\]\\\'"*?])', x): + return repr(x) + return x + if is_sequence(x): + return uniq([ + ngx_esc(v) + for v in x + ]) + if is_mapping(x): + return dict_remap_keys( + x, ngx_esc + ) + if x is None: + return None + return ngx_esc(str(x)) + + +def from_gobool(x) -> bool: + if isinstance(x, str): + return x.lower() in {'1', 't', 'true'} + return bool(x) + + +def merge_dict_recurse(d1, d2: dict) -> dict: + x = {} | d1 + + keys1 = set(x.keys()) + keys2 = set(d2.keys()) + common = keys1 & keys2 + missing = keys2 - common + + map1 = {k for k in common if is_mapping(x.get(k))} + seq1 = {k for k in common if is_sequence(x.get(k))} + misc1 = common - seq1 - map1 + + merge_safe = missing | misc1 + x.update({k: d2.get(k) for k in merge_safe}) + + map_common = {k for k in map1 if is_mapping(d2.get(k))} + for k in map_common: + y = d2.get(k) + if not y: + x[k] = {} + continue + x[k] = merge_dict_recurse(x.get(k), y) + + seq_common = {k for k in seq1 if is_sequence(d2.get(k))} + for k in seq_common: + y = d2.get(k) + if not y: + x[k] = [] + continue + x[k] = uniq(list(x.get(k)) + list(y)) + + unmerged = (map1 - map_common) | (seq1 - seq_common) + for k in unmerged: + t1 = type(x.get(k)) + t2 = type(d2.get(k)) + print( + f'merge_dict_recurse(): skipping key {k}' + + f' due to type mismatch: {t1} vs. {t2}', + file=sys.stderr) + + return x + + +def join_prefix(prefix: str, *paths) -> str: + pfx = prefix or '/' + pfx = '/' + pfx.strip('/') + rv = os.path.normpath(os.path.join(pfx, *paths).rstrip('/')).rstrip('/') + if rv == pfx: + raise ValueError('join_prefix: empty path after prefix') + common = os.path.commonpath([pfx, rv]) + if common == pfx: + return rv + # slowpath + rv = rv.removeprefix(common).lstrip('/') + rv = os.path.join(pfx, rv) + return rv + + +J2CFG_FILTERS = [ + any_to_env_dict, + any_to_str_list, + as_cgi_hdr, + as_ngx_var, + dict_empty_keys, + dict_keys, + dict_non_empty_keys, + dict_remap_keys, + dict_to_env_str_list, + from_gobool, + is_mapping, + is_re_fullmatch, + is_re_match, + is_sequence, + join_prefix, + list_diff, + list_intersect, + ngx_esc, + re_fullmatch, + re_fullmatch_negate, + re_match, + re_match_negate, + re_sub, + remove_empty_str, + remove_non_str, + sh_like_file_to_list, + str_split_to_list, + uniq, + uniq_str_list, +] diff --git a/j2cfg/j2cfg/settings.py b/j2cfg/j2cfg/settings.py new file mode 100644 index 0000000..473d20d --- /dev/null +++ b/j2cfg/j2cfg/settings.py @@ -0,0 +1,80 @@ +import re + + +J2CFG_TEMPLATE_EXT = '.j2' + +J2CFG_DEFAULTS_FILE = '/run/ngx/conf/j2cfg.yml' +J2CFG_PATH = [ + '/run/ngx/conf/j2cfg', +] + +J2CFG_PYTHON_MODULES = [ + 'itertools', + 'json', + 'os', + 'os.path', + 'pathlib', + 're', + 'sys', + # installed through pip + 'psutil', + 'wcmatch', +] + +J2CFG_JINJA_EXTENSIONS = [ + 'jinja2.ext.do', + 'jinja2.ext.loopcontrols', +] + +J2CFG_PRESERVE_ENVS = [ + # generic + 'PATH', + 'LD_LIBRARY_PATH', + 'LD_PRELOAD', + # glibc + 'GLIBC_TUNABLES', + 'MALLOC_ARENA_MAX', + # jemalloc + 'MALLOC_CONF', +] + +J2CFG_PASSTHROUGH_ENVS = [ + # openssl (man 7 openssl-env) + 'SSL_CERT_DIR', + 'SSL_CERT_FILE', + 'OPENSSL_CONF', + 'OPENSSL_CONF_INCLUDE', + 'OPENSSL_CONFIG', + 'OPENSSL_ENGINES', + 'OPENSSL_MODULES', + 'RANDFILE', + 'CTLOG_FILE', + 'QLOGDIR', + 'OSSL_QFILTER', + # openssl: processor capabilities + 'OPENSSL_armcap', + 'OPENSSL_ia32cap', + 'OPENSSL_ppccap', + 'OPENSSL_riscvcap', + 'OPENSSL_s390xcap', + 'OPENSSL_sparcv9cap', + # generic proxy settings + 'NO_PROXY', + 'HTTPS_PROXY', + 'HTTP_PROXY', +] + +J2CFG_BANNED_ENVS = [ + r'ANGIE(|_BPF_MAPS)(=|$)', + r'__IEP_', r'IEP_', + r'NGX_STATIC_', + r'ENVSUBST_', + r'J2CFG_', +] + + +def is_env_banned(k: str) -> bool: + for r in J2CFG_BANNED_ENVS: + if re.match(r, k): + return True + return False diff --git a/j2cfg/j2cfg/test.j2 b/j2cfg/j2cfg/test.j2 new file mode 100644 index 0000000..5fa5c8f --- /dev/null +++ b/j2cfg/j2cfg/test.j2 @@ -0,0 +1,173 @@ +j2cfg: +{{ j2cfg }} + +{% set x = [1,2,3,4] %} +x = {{ x }} +is_sequence: +{{ x | is_sequence }} + +{% set x = {1:2,3:4} %} +x = {{ x }} +is_sequence: +{{ x | is_sequence }} + +{% set x = [1,2,3,4] %} +x = {{ x }} +is_mapping: +{{ x | is_mapping }} + +{% set x = {1:2,3:4} %} +x = {{ x }} +is_mapping: +{{ x | is_mapping }} + +{% set x = [2,3,1,2] %} +x = {{ x }} +uniq: +{{ x | uniq }} + +{% set x = ['2',3,'1','2'] %} +x = {{ x }} +remove_non_str: +{{ x | remove_non_str }} + +{% set x = ['2','','1','2'] %} +x = {{ x }} +remove_empty_str: +{{ x | remove_empty_str }} + +{% set x = ['2','3','1','2'] %} +x = {{ x }} +uniq_str_list: +{{ x | uniq_str_list }} + +{% set x = '2 3 1 2 ' %} +x = {{ x.__repr__() }} +str_split_to_list: +{{ x | str_split_to_list }} + +{% set x = '2:3::1:2:' %} +x = {{ x.__repr__() }} +str_split_to_list(':'): +{{ x | str_split_to_list(':') }} + +{% set x = { 'VAR1': 'Etc/UTC', 'VAR2': '', 'VAR3': None, '4VAR4': 'yeah', 'VAR5=not': 'yeah', 'VAR5=real yeah': None, 'VAR6': {'pi': 3.1415926}, 'VAR7': ['pi', 3.1415926] } %} +x = {{ x }} +dict_to_env_str_list: +{{ x | dict_to_env_str_list }} + +{% set x = '1 2 3 4' %} +x = {{ x.__repr__() }} +any_to_str_list: +{{ x | any_to_str_list }} + +{% set x = [1,2,3,4] %} +x = {{ x }} +any_to_str_list: +{{ x | any_to_str_list }} + +{% set x = 3.1415926 %} +x = {{ x }} +any_to_str_list: +{{ x | any_to_str_list }} + +{% set x = ['a2','b3','c1','d2'] %} +x = {{ x }} +is_re_match('[ab]'): +{{ x | is_re_match('[ab]') }} +is_re_match('[mn]'): +{{ x | is_re_match('[mn]') }} + +{% set x = ['a2','b3','c1','d2'] %} +x = {{ x }} +is_re_fullmatch('[ab]'): +{{ x | is_re_fullmatch('[ab]') }} +is_re_fullmatch('[ab][12]'): +{{ x | is_re_fullmatch('[ab][12]') }} + +{% set x = ['a2','b3','c1','d2'] %} +x = {{ x }} +re_match('[ab]'): +{{ x | re_match('[ab]') }} +re_match('[mn]'): +{{ x | re_match('[mn]') }} + +{% set x = ['a2','b3','c1','d2'] %} +x = {{ x }} +re_fullmatch('[ab]'): +{{ x | re_fullmatch('[ab]') }} +re_fullmatch('[ab][12]'): +{{ x | re_fullmatch('[ab][12]') }} + +{% set x = ['a2','b3','c1','d2'] %} +x = {{ x }} +re_match_negate('[ab]'): +{{ x | re_match_negate('[ab]') }} +re_match_negate('[mn]'): +{{ x | re_match_negate('[mn]') }} + +{% set x = ['a2','b3','c1','d2'] %} +x = {{ x }} +re_fullmatch_negate('[ab]'): +{{ x | re_fullmatch_negate('[ab]') }} +re_fullmatch_negate('[ab][12]'): +{{ x | re_fullmatch_negate('[ab][12]') }} + +{% set x = ['a2b','b3b','c1f','d2g'] %} +x = {{ x }} +re_sub('[ab]', '_'): +{{ x | re_sub('[ab]', '_') }} +re_sub('[mn]', '_'): +{{ x | re_sub('[mn]', '_') }} + +{% set x = 'settings.py' %} +x = {{ x.__repr__() }} +sh_like_file_to_list: +{{ 'settings.py' | sh_like_file_to_list }} + +{% set x = 'Accept-Encoding' %} +x = {{ x.__repr__() }} +as_cgi_hdr: +{{ x | as_cgi_hdr }} + +{% set x = '_Permissions-Policy--' %} +x = {{ x.__repr__() }} +as_cgi_hdr: +{{ x | as_cgi_hdr }} + +{% set x = 'VAR1=Etc/UTC' %} +x = {{ x.__repr__() }} +any_to_env_dict: +{{ x | any_to_env_dict }} + +{% set x = ['VAR1=Etc/UTC', 'VAR2=', 'VAR3', '4VAR4=yeah', 'VAR5=yeah', 'VAR5=not-yeah'] %} +x = {{ x }} +any_to_env_dict: +{{ x | any_to_env_dict }} + +{% set x = { 'VAR1': 'Etc/UTC', 'VAR2': '', 'VAR3': None, '4VAR4': 'yeah', 'VAR5=not': 'yeah', 'VAR5=real yeah': None, 'VAR6': {'pi': 3.1415926}, 'VAR7': ['pi', 3.1415926] } %} +x = {{ x }} +any_to_env_dict: +{{ x | any_to_env_dict }} + +{% set x = { 'VAR1': 'Etc/UTC', 'VAR2': '', 'VAR3': None, '4VAR4': 'yeah', 'VAR5=not': 'yeah', 'VAR5=real yeah': None, 'VAR6': {'pi': 3.1415926}, 'VAR7': ['pi', 3.1415926] } %} +x = {{ x }} +dict_keys: +{{ x | dict_keys }} +dict_empty_keys: +{{ x | dict_empty_keys }} +dict_non_empty_keys: +{{ x | dict_non_empty_keys }} + +{% set x = [1,2,3,4] %} +{% set y = [3,4,5,6] %} +x = {{ x }} +y = {{ y }} +list_diff(x, y): +{{ x | list_diff(y) }} +list_diff(y, x): +{{ y | list_diff(x) }} +list_intersect(x, y): +{{ x | list_intersect(y) }} +list_intersect(y, x): +{{ y | list_intersect(x) }} diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..8af1298 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,4 @@ +jinja2==3.1.6 +psutil==7.0.0 +pyyaml==6.0.2 +wcmatch==10.0 diff --git a/scripts-extra/certifi-extras.sh b/scripts-extra/certifi-extras.sh new file mode 100755 index 0000000..595f726 --- /dev/null +++ b/scripts-extra/certifi-extras.sh @@ -0,0 +1,43 @@ +#!/bin/sh +set -ef + +dst_dir=/usr/local/share/ca-certificates + +w=$(mktemp -d) ; : "${w:?}" +w_cleanup() { + [ -z "$w" ] || ls -lA "$w/" >&2 + [ -z "$w" ] || rm -rf "$w" + unset w + exit "${1:-0}" +} + +def_bundle='/etc/ssl/certs/ca-certificates.crt' + +openssl-cert-auto-pem.sh "${def_bundle}" "$w/cacert.pem" "$w/cacert.fp" +[ -s "$w/cacert.pem" ] || w_cleanup 1 +[ -s "$w/cacert.fp" ] || w_cleanup 1 + +openssl-cert-auto-pem.sh "$1" "$w/certifi.pem" "$w/certifi.fp" "$w/certifi.off" +[ -s "$w/certifi.pem" ] || w_cleanup 1 +[ -s "$w/certifi.fp" ] || w_cleanup 1 +[ -s "$w/certifi.off" ] || w_cleanup 1 + +set +e +grep -Fxnv -f "$w/cacert.fp" "$w/certifi.fp" | cut -d : -f 1 > "$w/diff.ln" +set -e + +if [ -s "$w/diff.ln" ] ; then + terse_fingerprint() { cut -d = -f 2- | tr -cd '[:alnum:]' ; } + + while read -r n ; do + [ -n "$n" ] || continue + + fp=$(sed -ne "${n}p" "$w/certifi.fp" | terse_fingerprint) + off=$(sed -ne "${n}p" "$w/certifi.off") + sed -ne "${off}p" "$w/certifi.pem" > "${dst_dir}/certifi-${fp}.crt" + done < "$w/diff.ln" +fi + +rm -rf "$w" ; unset w + +exec update-ca-certificates --fresh diff --git a/scripts-extra/gpg-batch.sh b/scripts-extra/gpg-batch.sh new file mode 100755 index 0000000..244ea54 --- /dev/null +++ b/scripts-extra/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/scripts-extra/gpg-export.sh b/scripts-extra/gpg-export.sh new file mode 100755 index 0000000..dda0678 --- /dev/null +++ b/scripts-extra/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/scripts/angie b/scripts/angie new file mode 100755 index 0000000..5c8a41a --- /dev/null +++ b/scripts/angie @@ -0,0 +1,12 @@ +#!/bin/sh +set -ef + +## hardcoded configuration directory +d=/run/ngx/conf + +## reparse PATH: remove /usr/local/sbin +PATH=$(printf 'x:%s:x' "${PATH}" | sed -zE 's#:/usr/local/sbin/?:#:#g;s/^x://;s/:x$//') +export PATH + +exec env -C $d /usr/sbin/angie -p $d -c $d/angie.conf -e stderr --log-level=info -g 'error_log stderr info;' "$@" +exit 126 diff --git a/scripts/angie-builtin-modules.sh b/scripts/angie-builtin-modules.sh new file mode 100755 index 0000000..4c93a37 --- /dev/null +++ b/scripts/angie-builtin-modules.sh @@ -0,0 +1,26 @@ +#!/bin/sh +set -f + +conf_dir='/etc/angie.dist' + +## Angie: unset core variables +unset ANGIE ANGIE_BPF_MAPS + +t=$(mktemp) || exit $? + +/usr/sbin/angie -m 2>&1 | tee "$t" >/dev/null + +sed -En '/^ngx_(http|mail|stream)/d;/^ngx_(.+)_module$/{s//\1/;s/_filter$//;s/_/-/g;p}' < "$t" \ +| sort -uV > "${conf_dir}/builtin.core" + +for m in http mail stream ; do + sed -En '/^ngx_'"${m}"'_(.+)_module$/{s//\1/;s/_filter$//;s/_/-/g;p}' < "$t" \ + | sort -uV > "${conf_dir}/builtin.$m" +done + +rm -f "$t" ; unset t + +## merely debug output +for m in core http mail stream ; do + echo "${conf_dir}/builtin.$m" +done | xargs -r ls -ld >&2 diff --git a/scripts/angie-reload.sh b/scripts/angie-reload.sh new file mode 100755 index 0000000..f203d68 --- /dev/null +++ b/scripts/angie-reload.sh @@ -0,0 +1,15 @@ +#!/bin/sh +set -ef + +pid_file="${1:-/run/ngx/angie.pid}" +[ -f "${pid_file}" ] || { + echo "Angie is not running? (${pid_file} not found)" + exit 0 +} +[ -s "${pid_file}" ] || { + echo "Angie is not running? (${pid_file} is empty)" + exit 1 +} +pid=$(cat "${pid_file}") || exit 1 + +exec env kill -SIGHUP "${pid}" \ No newline at end of file diff --git a/scripts/apt-clean.sh b/scripts/apt-clean.sh new file mode 100755 index 0000000..270b574 --- /dev/null +++ b/scripts/apt-clean.sh @@ -0,0 +1,56 @@ +#!/bin/sh +set -f + +## apt +find /var/cache/apt/ ! -type d ! -name 'lock' -delete +find /var/lib/apt/ ! -type d -wholename '/var/lib/apt/listchanges*' -delete +find /var/lib/apt/lists/ ! -type d ! -name 'lock' -delete +find /var/log/ ! -type d -wholename '/var/log/apt/*' -delete +find /var/log/ ! -type d -wholename '/var/log/aptitude*' -delete + +## dpkg +: "${DPKG_ADMINDIR:=/var/lib/dpkg}" +truncate -s 0 "${DPKG_ADMINDIR}/available" +find "${DPKG_ADMINDIR}/" ! -type d -wholename "${DPKG_ADMINDIR}/*-old" -delete +find /var/log/ ! -type d -wholename '/var/log/alternatives.log' -delete +find /var/log/ ! -type d -wholename '/var/log/dpkg.log' -delete + +## DONT DO THIS AT HOME! +find "${DPKG_ADMINDIR}/" ! -type d -wholename "${DPKG_ADMINDIR}/info/*.symbols" -delete + +## debconf +find /var/cache/debconf/ ! -type d -wholename '/var/cache/debconf/*-old' -delete + +__t=$(mktemp) ; : "${__t:?}" +debconf_trim_i18n() { + mawk 'BEGIN { m = 0; } + $0 == "" { print; } + /^[^[:space:]]/ { + if ($1 ~ "\.[Uu][Tt][Ff]-?8:") { + m = 1; + next; + } + m = 0; + print $0; + } + /^[[:space:]]/ { + if (m == 1) next; + print $0; + }' < "$1" > "${__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 <&1 | mawk '$1=="DEBUG:" {print $2;exit;}') +NGX_DEBUG="${NGX_DEBUG:-0}" +case "${NGX_DEBUG}" in +0 | 1 ) ;; +* ) NGX_DEBUG=1 ;; +esac + +unset NGX_PRO +NGX_PRO=$(/usr/sbin/angie -v 2>&1 | mawk 'NR==1 {print $4;exit;}' | tr '[:upper:]' '[:lower:]') +NGX_PRO="${NGX_PRO:-0}" +case "${NGX_PRO}" in +'(pro)' ) NGX_PRO=1 ;; +* ) NGX_PRO=0 ;; +esac + +## "fqpn" stands for "Full Qualified Package Name" +## Angie OSS: angie-module- +## Angie PRO: angie-pro-module- +if [ ${NGX_PRO} = 1 ] ; then + fqpn() { echo "angie-pro-module-$1" ; } +else + fqpn() { echo "angie-module-$1" ; } +fi + +is_pkg_installed() { + case "$(dpkg-query -W -f='${Status}' "$1" 2>/dev/null)" in + "install ok installed" ) + ## package is installed normally + return 0 + ;; + "hold ok installed" ) + ## package is installed and marked as "hold" + return 0 + ;; + esac + return 1 +} + +is_mod_installed() { + is_pkg_installed "$(fqpn "$1")" || return 1 +} + +## produce package list +mods= +for i ; do + [ -n "$i" ] || continue + + printf '%s' "$i" | grep -zEq '^[a-z0-9.+-]+$' || { + env printf 'package name %q is not legal, quitting!\n' "$i" >&2 + exit 1 + } + + if is_mod_installed "$i" ; then + echo "package '$i' is already installed, skipping" >&2 + continue + fi + + mods="${mods}${mods:+ }$i" +done + +[ -n "${mods}" ] || exit 0 + +list_have_item() { + [ -n "$1" ] || return 1 + [ -n "$2" ] || return 1 + case " $1 " in + *" $2 "* ) return 0 ;; + esac + return 1 +} + +normalize_list() { + [ -n "$1" ] || return 0 + + printf '%s' "$1" \ + | sed -zE 's/[[:space:]]+/ /g;s/^ //;s/ $//' +} + +sort_dedup_list() { + [ -n "$1" ] || return 0 + + printf '%s' "$1" \ + | tr -s '[:space:]' '\n' | sort -uV \ + | sed -zE 's/[[:space:]]+/ /g;s/^ //;s/ $//' +} + +mods=$(sort_dedup_list "${mods}") + +## angie-module-lua: depends on angie-module-ndk +## angie-module-set-misc: depends on angie-module-ndk +unset want_ndk ; want_ndk=0 +if list_have_item "${mods}" lua ; then + want_ndk=1 +elif list_have_item "${mods}" set-misc ; then + want_ndk=1 +fi +if [ ${want_ndk} = 1 ] ; then + while : ; do + if list_have_item "${mods}" ndk ; then break ; fi + if is_mod_installed ndk ; then break ; fi + mods="${mods} ndk" + break ; done +fi + +## angie-module-wamr: depends on angie-module-wasm +## angie-module-wasmtime: depends on angie-module-wasm +unset want_wasm ; want_wasm=0 +if list_have_item "${mods}" wamr ; then + want_wasm=1 +elif list_have_item "${mods}" wasmtime ; then + want_wasm=1 +fi +if [ ${want_wasm} = 1 ] ; then + while : ; do + if list_have_item "${mods}" wasm ; then break ; fi + if is_mod_installed wasm ; then break ; fi + mods="${mods} wasm" + break ; done +fi + +## kind of quirk +dirs='cache lib log' +for n in ${dirs} ; do + d="/run/ngx/$n" + [ -d "$d" ] || install -d "$d" +done + +pkgs= +for m in ${mods} ; do + pkgs="${pkgs}${pkgs:+ }$(fqpn "$m")" +done +apt-install.sh ${pkgs} + +ANGIE_DIST_MODCONF_DIR=/etc/angie.dist/mod +ANGIE_MODCONF_DIR=/etc/angie/mod +[ -d "${ANGIE_MODCONF_DIR}" ] || install -d "${ANGIE_MODCONF_DIR}" + +have_preseed() { + if [ -e "${ANGIE_DIST_MODCONF_DIR}/.$1.preseed" ] ; then + return 0 + fi + if [ -e "${ANGIE_MODCONF_DIR}/.$1.preseed" ] ; then + return 0 + fi + return 1 +} + +list_ngx_modules() { + set +e + dpkg-query -L "$1" \ + | grep -F -e "${ANGIE_MODULES_DIR}/" \ + | grep -E -e '/[^/]+_module(-debug)?\.so$' \ + | sed -E '\,^(.+)-debug\.so$,{p;s//\1.so/;p;d}' \ + | sort -uV \ + | xargs -r ls -U1d 2>/dev/null + set -e +} + +is_same_file() { + find -L "$1" -samefile "$2" -printf . -quit 2>/dev/null | grep -Fq . || return 1 +} + +gen_mod_config() { + if [ -s "$2" ] ; then + printf '%s: configuration already exists: %s\n' "$1" "$2" >&2 + return + fi + + [ -n "$3" ] || return + + local __m + for __m in $3 ; do + echo "load_module ${__m};" + done > "$2" +} + +for m in ${mods} ; do + [ -n "$m" ] || continue + p=$(fqpn "$m") + + ## adjust modules: + ## - remove debug module if not in debug image + ## - move debug module to usual location otherwise + while read -r fmod_debug ; do + # [ -n "${fmod_debug}" ] || continue + case "${fmod_debug}" in + *-debug.so ) ;; + * ) continue ;; + esac + fmod="${fmod_debug%-debug.so}.so" + + if [ "${NGX_DEBUG}" = 0 ] ; then + if [ -f "${fmod}" ] ; then + fmod_real=$(readlink -f "${fmod}") + else + env printf 'missing (non-debug) file: %q\n' "${fmod}" >&2 + env printf 'falling back to (debug) file: %q\n' "${fmod_debug}" >&2 + fmod_real=$(readlink -f "${fmod_debug}") + fi + else + fmod_real=$(readlink -f "${fmod_debug}") + fi + [ -n "${fmod_real}" ] || exit 1 + + fmod_tmp=$(mktemp -u "${fmod}.XXXXXXXXXX") + ln "${fmod_real}" "${fmod_tmp}" + rm -f "${fmod}" "${fmod_debug}" + mv -f "${fmod_tmp}" "${fmod}" + + apt-mark hold "$p" || : + done <<-EOF + $(list_ngx_modules "$p") + EOF + + if have_preseed "$m" ; then + printf '%s: skipping generation of module load config (preseed is in effect)\n' "$p" >&2 + continue + fi + + ## produce attachable module configs + http_modules= ; mail_modules= ; stream_modules= + while read -r fmod ; do + [ -n "${fmod}" ] || continue + + fmod_short="modules/${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" "${ANGIE_MODCONF_DIR}/http-$m.conf" "${http_modules}" + [ -z "${mail_modules}" ] || gen_mod_config "$p" "${ANGIE_MODCONF_DIR}/mail-$m.conf" "${mail_modules}" + [ -z "${stream_modules}" ] || gen_mod_config "$p" "${ANGIE_MODCONF_DIR}/stream-$m.conf" "${stream_modules}" + +done + +echo "extra files/directories (if any):" >&2 +find /run/ngx/cache/ /run/ngx/lib/ /run/ngx/log/ -mindepth 1 -exec ls -ld {} + >&2 diff --git a/scripts/apt-install.sh b/scripts/apt-install.sh new file mode 100755 index 0000000..f98abb6 --- /dev/null +++ b/scripts/apt-install.sh @@ -0,0 +1,45 @@ +#!/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() { + : "${DPKG_ADMINDIR:=/var/lib/dpkg}" + VERSION_CODENAME=$(. /etc/os-release ; printf '%s' "${VERSION_CODENAME}") || : + f="${DPKG_ADMINDIR}/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}" 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..c70ee24 --- /dev/null +++ b/scripts/divert-rm.sh @@ -0,0 +1,7 @@ +#!/bin/sh +set -ef +: "${1:?}" +d=$(printf '%s' "/run/ngx/dpkg-divert/$1" | tr -s '/') +mkdir -p "${d%/*}" +dpkg-divert --divert "$d" --rename "$1" 2>/dev/null +rm -f "$d" diff --git a/scripts/dpkg-search.sh b/scripts/dpkg-search.sh new file mode 100755 index 0000000..304eac5 --- /dev/null +++ b/scripts/dpkg-search.sh @@ -0,0 +1,24 @@ +#!/bin/sh +set -ef +: "${1:?}" + +if dpkg-query --search "$1" ; then + exit 0 +fi + +case "$1" in +*\** | *\?* ) + env printf '%s does not support globs: %q\n' "${0##*/}" "$1" >&2 + exit 1 +;; +esac + +while read -r f ; do + [ -n "$f" ] || continue + dpkg-query --search "$f" || continue + exit 0 +done </dev/null | grep -Fxv -e "$1") +EOF + +exit 1 diff --git a/scripts/envsubst-args.sh b/scripts/envsubst-args.sh new file mode 100755 index 0000000..c7d0034 --- /dev/null +++ b/scripts/envsubst-args.sh @@ -0,0 +1,23 @@ +#!/bin/sh +set -f + +sed -znE '/^([^=]+)=.*$/s,,\1,p' /proc/$$/environ \ +| sed -zE \ + -e '/^_$/d' \ + -e '/^ANGIE(|_BPF_MAPS)$/d' \ + -e '/^__IEP_/d;/^IEP_$/d' \ + -e '/^NGX_STATIC_/d' \ + -e '/^ENVSUBST_/d' \ + -e '/^J2CFG_/d' \ +| { + if [ -n "${ENVSUBST_EXCLUDE_REGEX:-}" ] ; then + grep -zEv -e "${ENVSUBST_EXCLUDE_REGEX}" + elif [ -n "${ENVSUBST_INCLUDE_REGEX:-}" ] ; then + grep -zE -e "${ENVSUBST_INCLUDE_REGEX}" + else + cat + 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}" &2 + exit 1 +} +[ -s "$1" ] || exit 0 + +w=$(mktemp -d) || exit 1 +w_cleanup() { + [ -z "$w" ] || ls -lA "$w/" >&2 + [ -z "$w" ] || rm -rf "$w" + unset w + exit "${1:-0}" +} + +bundle_offsets() { + mawk 'BEGIN { OFS = ","; i_begin = 0; } + $0 == "-----BEGIN CERTIFICATE-----" { + i_begin = NR; + } + $0 == "-----END CERTIFICATE-----" { + if (i_begin > 0) { + print i_begin, NR; + i_begin = 0; + } + }' "$1" +} + +bundle_fingerprints() { + local x f + while read -r x ; do + [ -n "$x" ] || continue + + f=$(sed -ne "${x}p" "$1" | openssl x509 -noout -fingerprint -sha256) + [ -n "$f" ] || f=$(sed -ne "${x}p" "$1" | openssl x509 -noout -fingerprint) + [ -n "$f" ] || continue + + printf '%s\n' "$f" | tr '[:upper:]' '[:lower:]' + done < "$2" +} + +openssl storeutl -certs "$1" > "$w/cert.pem" || w_cleanup 1 +[ -s "$w/cert.pem" ] || w_cleanup 1 +tr -s '\r\n' '\n' < "$w/cert.pem" > "$w/cert.txt" +[ -s "$w/cert.txt" ] || w_cleanup 1 +rm -f "$w/cert.pem" + +bundle_offsets "$w/cert.txt" > "$w/cert.off" +[ -s "$w/cert.off" ] || w_cleanup 1 + +bundle_fingerprints "$w/cert.txt" "$w/cert.off" > "$w/cert.fp.all" +[ -s "$w/cert.fp.all" ] || w_cleanup 1 + +sort -uV < "$w/cert.fp.all" > "$w/cert.fp" +while read -r fp ; do + [ -n "${fp}" ] || continue + + n=$(grep -m1 -Fxn -e "${fp}" "$w/cert.fp.all" | cut -d : -f 1) + [ -n "$n" ] || continue + + off=$(sed -ne "${n}p" "$w/cert.off") + [ -n "${off}" ] || continue + + sed -ne "${off}p" "$w/cert.txt" +done < "$w/cert.fp" > "$w/cert.pem" +[ -s "$w/cert.pem" ] || w_cleanup 1 +rm -f "$w/cert.txt" "$w/cert.off" "$w/cert.fp.all" + +if [ -n "$2" ] ; then + while : ; do + if [ -e "$2" ] ; then + [ -f "$2" ] || break + fi + cat > "$2" + break ; done +else + cat +fi < "$w/cert.pem" + +while [ -n "$3" ] ; do + if [ -e "$3" ] ; then + [ -f "$3" ] || break + fi + cat "$w/cert.fp" > "$3" +break ; done + +while [ -n "$4" ] ; do + if [ -e "$4" ] ; then + [ -f "$4" ] || break + fi + bundle_offsets "$w/cert.pem" > "$4" +break ; done + +rm -rf "$w" ; unset w diff --git a/scripts/openssl-generate-dh-bundle.sh b/scripts/openssl-generate-dh-bundle.sh new file mode 100644 index 0000000..285452e --- /dev/null +++ b/scripts/openssl-generate-dh-bundle.sh @@ -0,0 +1,16 @@ +#!/bin/sh +set -ef + +for k in 1024 2048 ; do + f="dh${k}.pem" + echo "# openssl genpkey: $f" >&2 + timeout --kill-after=32s 30s \ + openssl genpkey -quiet -genparam -algorithm DH -out "./$f" -pkeyopt "dh_paramgen_prime_len:${k}" +done + +for k in 2048 3072 4096 ; do + f="ffdhe${k}.pem" + echo "# openssl genpkey: $f" >&2 + timeout --kill-after=32s 30s \ + openssl genpkey -quiet -genparam -algorithm DH -out "./$f" -pkeyopt "group:ffdhe${k}" +done diff --git a/scripts/openssl-ocsp.sh b/scripts/openssl-ocsp.sh new file mode 100755 index 0000000..4cf745e --- /dev/null +++ b/scripts/openssl-ocsp.sh @@ -0,0 +1,206 @@ +#!/bin/sh +set -ef + +ocsp_fetch_timeout=15 +ocsp_fetch_retries=3 +ocsp_fetch_retry_delay=5 +ocsp_valid_threshold=86400 + +usage() { + cat >&2 <<-EOF + # usage: ${0##*/} [args...] + # ${0##*/} get-uri + # ${0##*/} is-valid + # ${0##*/} is-expiring + # ${0##*/} fetch + EOF + exit "${1:-0}" +} +[ $# != 0 ] || usage + +## $1 - X509 in PEM format +ossl_x509_verify_fmt() { + openssl x509 -in "$1" -noout >/dev/null +} + +## $1 - cert +ossl_ocsp_uri() { + openssl x509 -in "$1" -noout -ocsp_uri \ + | head -n 1 +} + +## $1 - chain +## $2 - cert +## $3 - ocsp uri +## $4 - ocsp response +ossl_ocsp_fetch() { + openssl ocsp \ + -timeout "${ocsp_fetch_timeout}" \ + -nonce \ + -issuer "$1" -cert "$2" -url "$3" -respout "$4" +} + +## $1 - ocsp response +ossl_ocsp_verify_fmt() { + openssl ocsp \ + -noverify -respin "$1" +} + +## $1 - chain +## $2 - cert +## $3 - ocsp response +ossl_ocsp_read() { + openssl ocsp \ + -issuer "$1" -cert "$2" -respin "$3" -resp_text +} + +## $1 - chain +## $2 - cert +## $3 - ocsp response +ossl_ocsp_verify() { + ossl_ocsp_read "$@" >/dev/null +} + +## stdin - output of ossl_ocsp_read() +ossl_ocsp_next_update() { + sed -En '/^\s*[Nn]ext [Uu]pdate:\s*(\S.+\S)\s*$/{s//\1/;p;q}' +} + +unset arg_ok cmd chain cert ocsp_uri ocsp_resp +arg_ok= +while : ; do + [ -n "$1" ] || break + cmd="$1" + case "$1" in + get-uri ) + [ -n "$2" ] || break + [ -s "$2" ] || break + ossl_x509_verify_fmt "$2" || break + cert="$2" + ;; + is-valid | is-expiring | fetch ) + [ -n "$2" ] || break + [ -s "$2" ] || break + ossl_x509_verify_fmt "$2" || break + chain="$2" + [ -n "$3" ] || break + [ -s "$3" ] || break + ossl_x509_verify_fmt "$3" || break + cert="$3" + [ -n "$4" ] || break + ocsp_resp="$4" + ## OCSP response validation is handled later and in various ways (!) + ## e.g. "is-valid" (cmd_is_valid) validates OCSP response as expected + ## but "is-expiring" (cmd_is_expiring) returns success for invalid OCSP response + ## which means OCSP response should be updated ASAP + ;; + *) break ;; + esac + arg_ok=1 +break ; done +[ -n "${arg_ok}" ] || usage 1 +unset arg_ok + +## OCSP URI is used only in "get-uri" and "fetch" commands +## but implicitly required for all actions +ocsp_uri=$(ossl_ocsp_uri "${cert}") || exit 1 +if [ -z "${ocsp_uri}" ] ; then + env printf '%s: unable to extract OCSP URI from %q\n' "${0##*/}" "${cert}" >&2 + exit 1 +fi +## early command handling +if [ "${cmd}" = 'get-uri' ] ; then + printf '%s\n' "${ocsp_uri}" + exit 0 +fi + +## $1 - chain +## $2 - cert +## $3 - ocsp response +cmd_is_valid() { + ossl_ocsp_verify_fmt "$3" || return 1 + ossl_ocsp_verify "$1" "$2" "$3" || return 1 +} + +## $1 - chain +## $2 - cert +## $3 - ocsp response +cmd_is_expiring() { + cmd_is_valid "$1" "$2" "$3" || return 0 + + local need_update next ts_now ts_next ts_diff + + need_update=1 + while : ; do + next=$(ossl_ocsp_read "$1" "$2" "$3" 2>/dev/null | ossl_ocsp_next_update) + [ -n "${next}" ] || break + + ts_now=$(date '+%s') + ts_next=$(date -d "${next}" '+%s') + [ -n "${ts_next}" ] || break + [ ${ts_now} -lt ${ts_next} ] || break + + ts_diff=$((ts_next - ts_now)) + [ ${ts_diff} -le ${ocsp_valid_threshold} ] || need_update=0 + break ; done + + if [ "${need_update}" = 0 ] ; then + env printf '%q has valid and fresh OCSP response\n' "$2" >&2 + return 1 + fi + + return 0 +} + +## $1 - chain +## $2 - cert +## $3 - ocsp uri +## $4 - ocsp response +cmd_fetch() { + local t i r + + t=$(mktemp) ; : "${t:?}" + + for i in $(seq 1 "${ocsp_fetch_retries}") ; do + i= ## no-op + + if ossl_ocsp_fetch "$1" "$2" "$3" "$t" ; then + break + fi + : > "$t" + sleep "${ocsp_fetch_retry_delay}" + done + + r= + while : ; do + [ -s "$t" ] || break + cmd_is_valid "$1" "$2" "$t" || break + r=1 + break ; done + if [ -z "$r" ] ; then + env printf 'unable to fetch OCSP response for %q via %q\n' "$2" "$3" >&2 + rm -rf "$t" + return 1 + fi + + r= + while : ; do + touch "$4" || break + tee "$4" < "$t" >/dev/null || break + chmod 0644 "$4" || break + r=1 + break ; done + if [ -z "$r" ] ; then + env printf 'unable to save OCSP response for %q into %q\n' "$2" "$4" >&2 + rm -rf "$t" + return 1 + fi + + return 0 +} + +case "${cmd}" in +is-valid ) cmd_is_valid "${chain}" "${cert}" "${ocsp_resp}" ;; +is-expiring ) cmd_is_expiring "${chain}" "${cert}" "${ocsp_resp}" ;; +fetch ) cmd_fetch "${chain}" "${cert}" "${ocsp_uri}" "${ocsp_resp}" ;; +esac 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..a1b887e --- /dev/null +++ b/scripts/python-rm-cache.sh @@ -0,0 +1,9 @@ +#!/bin/sh +set -f +for i ; do + [ -n "$i" ] || continue + [ -d "$i" ] || continue + 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..6073167 --- /dev/null +++ b/scripts/static-compress.sh @@ -0,0 +1,54 @@ +#!/bin/sh +set -f + +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" || return ; comp_fixup "$1" "$1.gz" || rm -f "$1.gz" ; } +do_brotli() { [ -s "$1.br" ] || brotli -1kf "$1" || return ; comp_fixup "$1" "$1.br" || rm -f "$1.br" ; } +do_zstd() { [ -s "$1.zst" ] || zstd -q1kf "$1" || return ; comp_fixup "$1" "$1.zst" || rm -f "$1.zst" ; } + +float_div() { + mawk -v "a=$1" -v "b=$2" 'BEGIN{print a/b;exit;}'