commit 0e5948b0dd9d86312ea5a6691a1708686e8c3792 Author: Sangbum Kim Date: Sun Mar 17 00:30:57 2024 +0900 added initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4946b3d --- /dev/null +++ b/.gitignore @@ -0,0 +1,133 @@ +### Git ### +# Created by git for backups. To disable backups in Git: +# $ git config --global mergetool.keepBackup false +*.orig + +# Created by git when using merge tools for conflicts +*.BACKUP.* +*.BASE.* +*.LOCAL.* +*.REMOTE.* +*_BACKUP_*.txt +*_BASE_*.txt +*_LOCAL_*.txt +*_REMOTE_*.txt +### Intellij ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# File-based project format +*.iws + +# IntelliJ +out/ + + +### JetBrains+all Patch ### +.idea/ +*.iml +modules.xml +.idea/misc.xml +*.ipr + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### OSX ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### VisualStudioCode ### +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json + +### VisualStudioCode Patch ### +# Ignore all local history of files +.history + +### Windows ### +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +### VisualStudioCode ### +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +!.vscode/*.code-snippets + +# Local History for Visual Studio Code +.history/ + +# Built Visual Studio Code Extensions +*.vsix + +### VisualStudioCode Patch ### +# Ignore all local history of files +.history +.ionide + +# misc +.DS_Store diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..0497854 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,202 @@ +# syntax=docker/dockerfile:1 + +## +## Build +## +FROM harbor.repository.lb.home.dc.internal.amuz.es/infrastructure/alpine-base:3.19-latest AS build + +WORKDIR /usr/local/src +ARG VERSION="1.25.4" + +### Fetch Build Dependencies +COPY patch /usr/local/src/patch + +RUN set -xeu && \ + apk --no-cache add \ + autoconf \ + automake \ + build-base \ + libtool \ + musl-utils \ + util-linux \ + util-linux-dev \ + tar \ + openssl-dev \ + pcre2-dev \ + linux-headers \ + libatomic_ops-dev \ + zlib-dev \ + libaio-dev \ + bash \ + alpine-sdk \ + cmake \ + git \ + findutils + + +RUN set -xeu && \ + git clone --recurse-submodules -j8 https://github.com/google/ngx_brotli && \ + cd ngx_brotli/deps/brotli && \ + mkdir out && \ + cd out && \ + # -DCMAKE_CXX_FLAGS="-O3 -march=native -mtune=native -flto -funroll-loops -ffunction-sections -fdata-sections -Wl,--gc-sections" \ + cmake \ + -DCMAKE_BUILD_TYPE=Release \ + -DBUILD_SHARED_LIBS=OFF \ + -DCMAKE_C_FLAGS="-O3 -march=native -mtune=native -flto -funroll-loops -ffunction-sections -fdata-sections -Wl,--gc-sections" \ + -DCMAKE_INSTALL_PREFIX=./installed .. \ + && \ + cmake --build . --config Release --target brotlienc + + # git clone https://github.com/yaoweibin/nginx_upstream_check_module && \ +RUN set -xeu && \ + git clone https://github.com/openresty/headers-more-nginx-module + +RUN set -xeu && \ + mkdir -p /usr/local/src/nginx && \ + wget -O - "https://nginx.org/download/nginx-${VERSION}.tar.gz" | tar -zxf - --strip-components=1 -C /usr/local/src/nginx && \ + cd /usr/local/src/nginx && \ + for i in /usr/local/src/patch/*.patch; do \ + echo "Applying ${i}..." && \ + patch -Np1 -i "$i"; \ + done && \ + # patch -Np1 -i /usr/local/src/nginx_upstream_check_module/check_1.16.1+.patch + # --add-module=/usr/local/src/nginx_http_upstream_check_module \ + ./configure \ + --build="amazing from here" \ + --with-cc-opt="-Wno-error -DTCP_FASTOPEN=23 -O2 -flto -ffat-lto-objects -funsafe-math-optimizations -fstack-protector -fcode-hoisting -funroll-loops -ffunction-sections -fdata-sections -Wl,--gc-sections --param=ssp-buffer-size=4 -Wformat -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2" \ + --with-ld-opt='-Wl,-Bsymbolic-functions -flto=auto -Wl,--as-needed -pie -Wl,-z,relro -Wl,-z,now -Wl,-Bsymbolic -Wl,--gc-sections -fPIC -flto=auto -ffat-lto-objects' \ + --prefix=/opt/nginx \ + --add-module=/usr/local/src/headers-more-nginx-module \ + --add-module=/usr/local/src/ngx_brotli \ + --conf-path=etc/nginx.conf \ + --pid-path=tmp/nginx.pid \ + --lock-path=tmp/nginx.lock \ + --without-select_module \ + --without-poll_module \ + --with-threads \ + --with-file-aio \ + --with-http_ssl_module \ + --with-http_v2_module \ + --with-http_v3_module \ + --with-http_realip_module \ + --with-http_gzip_static_module \ + --without-http_ssi_module \ + --without-http_userid_module \ + --without-http_auth_basic_module \ + --without-http_fastcgi_module \ + --without-http_uwsgi_module \ + --without-http_scgi_module \ + --without-http_memcached_module \ + --without-http_browser_module \ + --http-client-body-temp-path=tmp/client_body \ + --http-proxy-temp-path=tmp/proxy \ + --without-mail_smtp_module \ + --without-mail_imap_module \ + --without-mail_pop3_module \ + --with-stream \ + --with-stream_ssl_module \ + --with-stream_realip_module \ + --with-stream_ssl_preread_module \ + --with-pcre \ + --with-pcre-jit \ + --with-libatomic && \ + make -j `nproc` && \ + make install && \ + ldd /opt/nginx/sbin/nginx && \ + find /opt/nginx && \ + cat /opt/nginx/etc/nginx.conf && \ + rm -r /opt/nginx/html/ && \ + mkdir /opt/nginx/etc/conf.d/ && \ + mkdir -p /opt/nginx/html/ && \ + install -m644 html/index.html /opt/nginx/html/ && \ + install -m644 html/50x.html /opt/nginx/html/ && \ + ln -sf /dev/stdout /opt/nginx/logs/access.log && \ + ln -sf /dev/stderr /opt/nginx/logs/error.log && \ + cd /opt/nginx && \ + rm \ + etc/fastcgi_params \ + etc/fastcgi_params.default \ + etc/fastcgi.conf \ + etc/fastcgi.conf.default \ + etc/uwsgi_params \ + etc/uwsgi_params.default \ + etc/scgi_params \ + etc/scgi_params.default && \ + ldd sbin/nginx && \ + strip -X -x -s -v sbin/nginx + +## +## PKG +## +FROM harbor.repository.lb.home.dc.internal.amuz.es/infrastructure/alpine-base:3.19-latest AS pkg + +RUN set -xeu && \ + mkdir pkgs output &&\ + apk --no-cache \ + fetch -R -o pkgs \ + openssl \ + pcre2 \ + zlib \ + musl-utils \ + && \ + rm -f \ + pkgs/busybox-*.apk \ + pkgs/ca-certificates-*.apk \ + pkgs/libcrypto3-*.apk \ + pkgs/libssl3-*.apk \ + pkgs/ssl_client-*.apk && \ + find pkgs -type f -name '*.apk' -print0 |awk -F'\0' '{printf("echo \"extracting %s\";tar -zxf \"%s\" -C output || exit 1\n",$0,$0)}' |sh && \ + find output -type f -name '.*' -delete + +## +## Deploy +## +FROM harbor.repository.lb.home.dc.internal.amuz.es/infrastructure/minimal-toolbox:3.19-latest + +COPY --from=build /opt/nginx /opt/nginx +COPY --from=pkg output /pkg + + +RUN set -xeu && \ + cp -af /pkg/* / && \ + rm -rf /pkg + +RUN set -xeu && \ + mkdir /docker-entrypoint.d + +COPY nginx.conf /opt/nginx/etc/nginx.conf +COPY nginx.vh.no-default.conf /opt/nginx/etc/conf.d/default.conf +COPY docker-entrypoint.sh / +COPY /docker-entrypoint.d /docker-entrypoint.d + +RUN set -xeu && \ + mkdir -p /usr/local/sbin && \ + ln -sf /opt/nginx/sbin/nginx /usr/local/sbin/nginx && \ + mkdir -p /opt/nginx/etc/templates \ + /opt/nginx/etc/stream-conf.d \ + /opt/nginx/etc/conf.d \ + /opt/nginx/tmp \ + /opt/nginx/logs && \ + chown -R 1000:1000 \ + /opt/nginx/etc \ + /opt/nginx/tmp \ + /opt/nginx/logs + + +WORKDIR /opt/nginx + +LABEL org.opencontainers.image.authors="Sangbum Kim " + +EXPOSE 8080 8443 8443/udp + +STOPSIGNAL SIGQUIT + + +ENTRYPOINT [ "/sbin/tini", "-s", "--", "/docker-entrypoint.sh"] + +USER 1000:1000 + +VOLUME ["/opt/nginx/etc/conf.d", "/opt/nginx/html", "/opt/nginx/tmp", "/opt/nginx/logs"] +ENV PATH="/opt/nginx/sbin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" +CMD ["nginx", "-g", "daemon off;"]ca diff --git a/docker-entrypoint.d/10-listen-on-ipv6-by-default.sh b/docker-entrypoint.d/10-listen-on-ipv6-by-default.sh new file mode 100755 index 0000000..8cfaa18 --- /dev/null +++ b/docker-entrypoint.d/10-listen-on-ipv6-by-default.sh @@ -0,0 +1,39 @@ +#!/bin/sh +# vim:sw=4:ts=4:et + +set -e + +entrypoint_log() { + if [ -z "${NGINX_ENTRYPOINT_QUIET_LOGS:-}" ]; then + echo "$@" + fi +} + +ME=$(basename "$0") +DEFAULT_CONF_FILE="/opt/nginx/etc/conf.d/default.conf" + +# check if we have ipv6 available +if [ ! -f "/proc/net/if_inet6" ]; then + entrypoint_log "$ME: info: ipv6 not available" + exit 0 +fi + +if [ ! -f "/$DEFAULT_CONF_FILE" ]; then + entrypoint_log "$ME: info: /$DEFAULT_CONF_FILE is not a file or does not exist" + exit 0 +fi + +# check if the file can be modified, e.g. not on a r/o filesystem +touch /$DEFAULT_CONF_FILE 2>/dev/null || { entrypoint_log "$ME: info: can not modify /$DEFAULT_CONF_FILE (read-only file system?)"; exit 0; } + +# check if the file is already modified, e.g. on a container restart +grep -q "listen \[::]\:8080;" /$DEFAULT_CONF_FILE && { entrypoint_log "$ME: info: IPv6 listen already enabled"; exit 0; } + +entrypoint_log "$ME: info: Getting the checksum of /$DEFAULT_CONF_FILE" + +# enable ipv6 on default.conf listen sockets +sed -i -E 's,listen 8080;,listen 8080;\n listen [::]:8080;,' /$DEFAULT_CONF_FILE + +entrypoint_log "$ME: info: Enabled listen on IPv6 in /$DEFAULT_CONF_FILE" + +exit 0 diff --git a/docker-entrypoint.d/15-local-resolvers.envsh b/docker-entrypoint.d/15-local-resolvers.envsh new file mode 100755 index 0000000..450a999 --- /dev/null +++ b/docker-entrypoint.d/15-local-resolvers.envsh @@ -0,0 +1,12 @@ +#!/bin/sh +# vim:sw=2:ts=2:sts=2:et + +set -eu + +LC_ALL=C +PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin + +[ "${NGINX_ENTRYPOINT_LOCAL_RESOLVERS:-}" ] || return 0 + +NGINX_LOCAL_RESOLVERS=$(awk 'BEGIN{ORS=" "} $1=="nameserver" {if ($2 ~ ":") {print "["$2"]"} else {print $2}}' /etc/resolv.conf) +export NGINX_LOCAL_RESOLVERS diff --git a/docker-entrypoint.d/20-envsubst-on-templates.sh b/docker-entrypoint.d/20-envsubst-on-templates.sh new file mode 100755 index 0000000..f0c12ee --- /dev/null +++ b/docker-entrypoint.d/20-envsubst-on-templates.sh @@ -0,0 +1,78 @@ +#!/bin/sh + +set -e + +ME=$(basename "$0") + +entrypoint_log() { + if [ -z "${NGINX_ENTRYPOINT_QUIET_LOGS:-}" ]; then + echo "$@" + fi +} + +add_stream_block() { + local conffile="/opt/nginx/etc/nginx.conf" + + if grep -q -E "\s*stream\s*\{" "$conffile"; then + entrypoint_log "$ME: $conffile contains a stream block; include $stream_output_dir/*.conf to enable stream templates" + else + # check if the file can be modified, e.g. not on a r/o filesystem + touch "$conffile" 2>/dev/null || { entrypoint_log "$ME: info: can not modify $conffile (read-only file system?)"; exit 0; } + entrypoint_log "$ME: Appending stream block to $conffile to include $stream_output_dir/*.conf" + cat << END >> "$conffile" +# added by "$ME" on "$(date)" +stream { + include $stream_output_dir/*.conf; +} +END + fi +} + +auto_renvsubst() { + local template_dir="${NGINX_RENVSUBST_TEMPLATE_DIR:-/opt/nginx/etc/templates}" + local suffix="${NGINX_RENVSUBST_TEMPLATE_SUFFIX:-.template}" + local output_dir="${NGINX_RENVSUBST_OUTPUT_DIR:-/opt/nginx/etc/conf.d}" + local stream_suffix="${NGINX_RENVSUBST_STREAM_TEMPLATE_SUFFIX:-.stream-template}" + local stream_output_dir="${NGINX_RENVSUBST_STREAM_OUTPUT_DIR:-/opt/nginx/etc/stream-conf.d}" + local filter="${NGINX_RENVSUBST_FILTER:-}" + + local template defined_envs relative_path output_path subdir + defined_envs=$(printf '${%s} ' $(awk "END { for (name in ENVIRON) { print ( name ~ /${filter}/ ) ? name : \"\" } }" < /dev/null )) + [ -d "$template_dir" ] || return 0 + if [ ! -w "$output_dir" ]; then + entrypoint_log "$ME: ERROR: $template_dir exists, but $output_dir is not writable" + return 0 + fi + find "$template_dir" -follow -type f -name "*$suffix" -print | while read -r template; do + relative_path="${template#"$template_dir/"}" + output_path="$output_dir/${relative_path%"$suffix"}" + subdir=$(dirname "$relative_path") + # create a subdirectory where the template file exists + mkdir -p "$output_dir/$subdir" + entrypoint_log "$ME: Running renvsubst on $template to $output_path" + renvsubst "$defined_envs" < "$template" > "$output_path" + done + + # Print the first file with the stream suffix, this will be false if there are none + if test -n "$(find "$template_dir" -name "*$stream_suffix" -print -quit)"; then + mkdir -p "$stream_output_dir" + if [ ! -w "$stream_output_dir" ]; then + entrypoint_log "$ME: ERROR: $template_dir exists, but $stream_output_dir is not writable" + return 0 + fi + add_stream_block + find "$template_dir" -follow -type f -name "*$stream_suffix" -print | while read -r template; do + relative_path="${template#"$template_dir/"}" + output_path="$stream_output_dir/${relative_path%"$stream_suffix"}" + subdir=$(dirname "$relative_path") + # create a subdirectory where the template file exists + mkdir -p "$stream_output_dir/$subdir" + entrypoint_log "$ME: Running renvsubst on $template to $output_path" + renvsubst "$defined_envs" < "$template" > "$output_path" + done + fi +} + +auto_renvsubst + +exit 0 diff --git a/docker-entrypoint.d/30-tune-worker-processes.sh b/docker-entrypoint.d/30-tune-worker-processes.sh new file mode 100755 index 0000000..3a33a0d --- /dev/null +++ b/docker-entrypoint.d/30-tune-worker-processes.sh @@ -0,0 +1,188 @@ +#!/bin/sh +# vim:sw=2:ts=2:sts=2:et + +set -eu + +LC_ALL=C +ME=$(basename "$0") +PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin + +[ "${NGINX_ENTRYPOINT_WORKER_PROCESSES_AUTOTUNE:-}" ] || exit 0 + +touch /opt/nginx/etc/nginx.conf 2>/dev/null || { echo >&2 "$ME: error: can not modify /opt/nginx/etc/nginx.conf (read-only file system?)"; exit 0; } + +ceildiv() { + num=$1 + div=$2 + echo $(( (num + div - 1) / div )) +} + +get_cpuset() { + cpusetroot=$1 + cpusetfile=$2 + ncpu=0 + [ -f "$cpusetroot/$cpusetfile" ] || return 1 + for token in $( tr ',' ' ' < "$cpusetroot/$cpusetfile" ); do + case "$token" in + *-*) + count=$( seq $(echo "$token" | tr '-' ' ') | wc -l ) + ncpu=$(( ncpu+count )) + ;; + *) + ncpu=$(( ncpu+1 )) + ;; + esac + done + echo "$ncpu" +} + +get_quota() { + cpuroot=$1 + ncpu=0 + [ -f "$cpuroot/cpu.cfs_quota_us" ] || return 1 + [ -f "$cpuroot/cpu.cfs_period_us" ] || return 1 + cfs_quota=$( cat "$cpuroot/cpu.cfs_quota_us" ) + cfs_period=$( cat "$cpuroot/cpu.cfs_period_us" ) + [ "$cfs_quota" = "-1" ] && return 1 + [ "$cfs_period" = "0" ] && return 1 + ncpu=$( ceildiv "$cfs_quota" "$cfs_period" ) + [ "$ncpu" -gt 0 ] || return 1 + echo "$ncpu" +} + +get_quota_v2() { + cpuroot=$1 + ncpu=0 + [ -f "$cpuroot/cpu.max" ] || return 1 + cfs_quota=$( cut -d' ' -f 1 < "$cpuroot/cpu.max" ) + cfs_period=$( cut -d' ' -f 2 < "$cpuroot/cpu.max" ) + [ "$cfs_quota" = "max" ] && return 1 + [ "$cfs_period" = "0" ] && return 1 + ncpu=$( ceildiv "$cfs_quota" "$cfs_period" ) + [ "$ncpu" -gt 0 ] || return 1 + echo "$ncpu" +} + +get_cgroup_v1_path() { + needle=$1 + found= + foundroot= + mountpoint= + + [ -r "/proc/self/mountinfo" ] || return 1 + [ -r "/proc/self/cgroup" ] || return 1 + + while IFS= read -r line; do + case "$needle" in + "cpuset") + case "$line" in + *cpuset*) + found=$( echo "$line" | cut -d ' ' -f 4,5 ) + break + ;; + esac + ;; + "cpu") + case "$line" in + *cpuset*) + ;; + *cpu,cpuacct*|*cpuacct,cpu|*cpuacct*|*cpu*) + found=$( echo "$line" | cut -d ' ' -f 4,5 ) + break + ;; + esac + esac + done << __EOF__ +$( grep -F -- '- cgroup ' /proc/self/mountinfo ) +__EOF__ + + while IFS= read -r line; do + controller=$( echo "$line" | cut -d: -f 2 ) + case "$needle" in + "cpuset") + case "$controller" in + cpuset) + mountpoint=$( echo "$line" | cut -d: -f 3 ) + break + ;; + esac + ;; + "cpu") + case "$controller" in + cpu,cpuacct|cpuacct,cpu|cpuacct|cpu) + mountpoint=$( echo "$line" | cut -d: -f 3 ) + break + ;; + esac + ;; + esac +done << __EOF__ +$( grep -F -- 'cpu' /proc/self/cgroup ) +__EOF__ + + case "${found%% *}" in + "/") + foundroot="${found##* }$mountpoint" + ;; + "$mountpoint") + foundroot="${found##* }" + ;; + esac + echo "$foundroot" +} + +get_cgroup_v2_path() { + found= + foundroot= + mountpoint= + + [ -r "/proc/self/mountinfo" ] || return 1 + [ -r "/proc/self/cgroup" ] || return 1 + + while IFS= read -r line; do + found=$( echo "$line" | cut -d ' ' -f 4,5 ) + done << __EOF__ +$( grep -F -- '- cgroup2 ' /proc/self/mountinfo ) +__EOF__ + + while IFS= read -r line; do + mountpoint=$( echo "$line" | cut -d: -f 3 ) +done << __EOF__ +$( grep -F -- '0::' /proc/self/cgroup ) +__EOF__ + + case "${found%% *}" in + "") + return 1 + ;; + "/") + foundroot="${found##* }$mountpoint" + ;; + "$mountpoint" | /../*) + foundroot="${found##* }" + ;; + esac + echo "$foundroot" +} + +ncpu_online=$( getconf _NPROCESSORS_ONLN ) +ncpu_cpuset= +ncpu_quota= +ncpu_cpuset_v2= +ncpu_quota_v2= + +cpuset=$( get_cgroup_v1_path "cpuset" ) && ncpu_cpuset=$( get_cpuset "$cpuset" "cpuset.effective_cpus" ) || ncpu_cpuset=$ncpu_online +cpu=$( get_cgroup_v1_path "cpu" ) && ncpu_quota=$( get_quota "$cpu" ) || ncpu_quota=$ncpu_online +cgroup_v2=$( get_cgroup_v2_path ) && ncpu_cpuset_v2=$( get_cpuset "$cgroup_v2" "cpuset.cpus.effective" ) || ncpu_cpuset_v2=$ncpu_online +cgroup_v2=$( get_cgroup_v2_path ) && ncpu_quota_v2=$( get_quota_v2 "$cgroup_v2" ) || ncpu_quota_v2=$ncpu_online + +ncpu=$( printf "%s\n%s\n%s\n%s\n%s\n" \ + "$ncpu_online" \ + "$ncpu_cpuset" \ + "$ncpu_quota" \ + "$ncpu_cpuset_v2" \ + "$ncpu_quota_v2" \ + | sort -n \ + | head -n 1 ) + +sed -i.bak -r 's/^(worker_processes)(.*)$/# Commented out by '"$ME"' on '"$(date)"'\n#\1\2\n\1 '"$ncpu"';/' /opt/nginx/etc/nginx.conf diff --git a/docker-entrypoint.d/40-make-sure-directories.sh b/docker-entrypoint.d/40-make-sure-directories.sh new file mode 100755 index 0000000..378bb88 --- /dev/null +++ b/docker-entrypoint.d/40-make-sure-directories.sh @@ -0,0 +1,25 @@ +#!/bin/sh +# vim:sw=2:ts=2:sts=2:et + +set -eu + +LC_ALL=C +PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin + +make_tmp_directories() { + local temp_dir="${NGINX_RENVSUBST_TEMPORARY_DIR:-/opt/nginx/tmp}" + local client_body_temp_dir="${NGINX_RENVSUBST_CLIENT_BODY_TEMP_DIR:-${temp_dir}/client_temp}" + local proxy_temp_dir="${NGINX_RENVSUBST_PROXY_TEMP_DIR:-${temp_dir}/proxy}" + + local temp_dir client_body_temp_dir proxy_temp_dir + if [ ! -w "$temp_dir" ]; then + entrypoint_log "$ME: ERROR: $temp_dir is not writable" + return 0 + fi + [ ! -d "$client_body_temp_dir" ] || mkdir -p "${client_body_temp_dir}" + [ ! -d "$proxy_temp_dir" ] || mkdir -p "${proxy_temp_dir}" +} + +make_tmp_directories + +exit 0 diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh new file mode 100755 index 0000000..8ea04f2 --- /dev/null +++ b/docker-entrypoint.sh @@ -0,0 +1,47 @@ +#!/bin/sh +# vim:sw=4:ts=4:et + +set -e + +entrypoint_log() { + if [ -z "${NGINX_ENTRYPOINT_QUIET_LOGS:-}" ]; then + echo "$@" + fi +} + +if [ "$1" = "nginx" ] || [ "$1" = "nginx-debug" ]; then + if /usr/bin/find "/docker-entrypoint.d/" -mindepth 1 -maxdepth 1 -type f -print -quit 2>/dev/null | read v; then + entrypoint_log "$0: /docker-entrypoint.d/ is not empty, will attempt to perform configuration" + + entrypoint_log "$0: Looking for shell scripts in /docker-entrypoint.d/" + find "/docker-entrypoint.d/" -follow -type f -print | sort -V | while read -r f; do + case "$f" in + *.envsh) + if [ -x "$f" ]; then + entrypoint_log "$0: Sourcing $f"; + . "$f" + else + # warn on shell scripts without exec bit + entrypoint_log "$0: Ignoring $f, not executable"; + fi + ;; + *.sh) + if [ -x "$f" ]; then + entrypoint_log "$0: Launching $f"; + "$f" + else + # warn on shell scripts without exec bit + entrypoint_log "$0: Ignoring $f, not executable"; + fi + ;; + *) entrypoint_log "$0: Ignoring $f";; + esac + done + + entrypoint_log "$0: Configuration complete; ready for start up" + else + entrypoint_log "$0: No files found in /docker-entrypoint.d/, skipping configuration" + fi +fi + +exec "$@" diff --git a/nginx.conf b/nginx.conf new file mode 100644 index 0000000..82aa25c --- /dev/null +++ b/nginx.conf @@ -0,0 +1,114 @@ +#user me; +worker_processes 1; + +error_log /opt/nginx/logs/error.log warn; +pid /opt/nginx/tmp/nginx.pid; + +pcre_jit on; +timer_resolution 100ms; + +events { + use epoll; + worker_aio_requests 128; + worker_connections 1024; + multi_accept on; +} + +http { + include mime.types; + charset utf-8; + default_type application/octet-stream; + + client_body_temp_path /opt/nginx/tmp/client_temp; + proxy_temp_path /opt/nginx/tmp/proxy; + + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + + access_log /opt/nginx/logs/access.log main; + + sendfile on; + aio threads; + aio_write on; + tcp_nopush on; + tcp_nodelay on; + + open_file_cache max=1000 inactive=20s; + open_file_cache_valid 30s; + open_file_cache_min_uses 2; + open_file_cache_errors on; + + keepalive_timeout 15s; + send_timeout 60s; + client_header_timeout 15s; + client_body_timeout 600s; + client_max_body_size 500m; + client_body_buffer_size 8K; + client_header_buffer_size 1k; + large_client_header_buffers 4 8k; + output_buffers 5 8m; + resolver_timeout 5s; + read_ahead 256k; + + keepalive_disable msie6; + disable_symlinks if_not_owner; + if_modified_since before; + reset_timedout_connection on; + server_tokens off; + more_set_headers 'Server: AmazingFromHere'; + + add_header X-Frame-Options SAMEORIGIN; + add_header X-Content-Type-Options nosniff; + add_header X-XSS-Protection "1; mode=block"; + + # ssl_dyn_rec_enable on; + ssl_protocols TLSv1.2 TLSv1.3; + # ssl_ecdh_curve X25519:P-521:P-384; + # ssl_ciphers [ECDHE-ECDSA-CHACHA20-POLY1305|ECDHE-RSA-CHACHA20-POLY1305|ECDHE-ECDSA-AES256-GCM-SHA384|ECDHE-RSA-AES256-GCM-SHA384]:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256; + ssl_prefer_server_ciphers on; + ssl_session_cache builtin:1000 shared:SSL:50m; + ssl_session_timeout 1d; + ssl_session_tickets off; + + gzip_static on; + gzip on; + gzip_comp_level 5; + gzip_min_length 512; + gzip_proxied any; + gzip_vary on; + gzip_disable "msie6"; + gzip_types + text/plain + text/css + text/x-component + text/javascript application/x-javascript application/javascript text/x-js + image/svg+xml + text/plaintext/xsd text/xsl + text/xml application/xml application/rss+xml + application/json + font/truetype font/opentype application/x-font-otf application/x-font-ttf application/vnd.ms-opentype application/vnd.ms-fontobject application/font-woff application/font-woff2 + audio/ogg + application/pdf + image/bmp image/x-icon image/webp image/tiff + audio/wav; + + brotli_static on; + brotli on; + brotli_types + text/plain + text/css + text/x-component + text/javascript application/x-javascript application/javascript text/x-js + image/svg+xml + text/plaintext/xsd text/xsl + text/xml application/xml application/rss+xml + application/json + font/truetype font/opentype application/x-font-otf application/x-font-ttf application/vnd.ms-opentype application/vnd.ms-fontobject application/font-woff application/font-woff2 + audio/ogg + application/pdf + image/bmp image/x-icon image/webp image/tiff + audio/wav; + + include /opt/nginx/etc/conf.d/*.conf; +} diff --git a/nginx.vh.no-default.conf b/nginx.vh.no-default.conf new file mode 100644 index 0000000..eace8b4 --- /dev/null +++ b/nginx.vh.no-default.conf @@ -0,0 +1,20 @@ +# Drop requests for unknown hosts +# +# If no default server is defined, angie will use the first found server. +# To prevent host header attacks, or other potential problems when an unknown +# servername is used in a request, it's recommended to drop the request +# returning 444 "no response". + +server { + listen 8080 default_server deferred reuseport; + server_name _ ""; + return 444; +} + +server { + listen 8443 default_server quic reuseport; + listen 8443 default_server deferred ssl reuseport; + http2 on; + server_name _ ""; + ssl_reject_handshake on; +} diff --git a/patch/0002-nginx_dynamic_tls_records.patch b/patch/0002-nginx_dynamic_tls_records.patch new file mode 100644 index 0000000..55a0488 --- /dev/null +++ b/patch/0002-nginx_dynamic_tls_records.patch @@ -0,0 +1,254 @@ +What we do now: +We use a static record size of 4K. This gives a good balance of latency and +throughput. + +Optimize latency: +By initialy sending small (1 TCP segment) sized records, we are able to avoid +HoL blocking of the first byte. This means TTFB is sometime lower by a whole +RTT. + +Optimizing throughput: +By sending increasingly larger records later in the connection, when HoL is not +a problem, we reduce the overhead of TLS record (29 bytes per record with +GCM/CHACHA-POLY). + +Logic: +Start each connection with small records (1369 byte default, change with +ssl_dyn_rec_size_lo). After a given number of records (40, change with +ssl_dyn_rec_threshold) start sending larger records (4229, ssl_dyn_rec_size_hi). +Eventually after the same number of records, start sending the largest records +(ssl_buffer_size). +In case the connection idles for a given amount of time (1s, +ssl_dyn_rec_timeout), the process repeats itself (i.e. begin sending small +records again). + + +diff --color -uNr a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c +--- a/src/event/ngx_event_openssl.c 2023-06-13 23:08:10.000000000 +0800 ++++ b/src/event/ngx_event_openssl.c 2023-06-14 15:43:05.834243714 +0800 +@@ -1674,6 +1674,7 @@ + + sc->buffer = ((flags & NGX_SSL_BUFFER) != 0); + sc->buffer_size = ssl->buffer_size; ++ sc->dyn_rec = ssl->dyn_rec; + + sc->session_ctx = ssl->ctx; + +@@ -2645,6 +2646,41 @@ + + for ( ;; ) { + ++ /* Dynamic record resizing: ++ We want the initial records to fit into one TCP segment ++ so we don't get TCP HoL blocking due to TCP Slow Start. ++ A connection always starts with small records, but after ++ a given amount of records sent, we make the records larger ++ to reduce header overhead. ++ After a connection has idled for a given timeout, begin ++ the process from the start. The actual parameters are ++ configurable. If dyn_rec_timeout is 0, we assume dyn_rec is off. */ ++ ++ if (c->ssl->dyn_rec.timeout > 0 ) { ++ ++ if (ngx_current_msec - c->ssl->dyn_rec_last_write > ++ c->ssl->dyn_rec.timeout) ++ { ++ buf->end = buf->start + c->ssl->dyn_rec.size_lo; ++ c->ssl->dyn_rec_records_sent = 0; ++ ++ } else { ++ if (c->ssl->dyn_rec_records_sent > ++ c->ssl->dyn_rec.threshold * 2) ++ { ++ buf->end = buf->start + c->ssl->buffer_size; ++ ++ } else if (c->ssl->dyn_rec_records_sent > ++ c->ssl->dyn_rec.threshold) ++ { ++ buf->end = buf->start + c->ssl->dyn_rec.size_hi; ++ ++ } else { ++ buf->end = buf->start + c->ssl->dyn_rec.size_lo; ++ } ++ } ++ } ++ + while (in && buf->last < buf->end && send < limit) { + if (in->buf->last_buf || in->buf->flush) { + flush = 1; +@@ -2784,6 +2820,9 @@ + + if (n > 0) { + ++ c->ssl->dyn_rec_records_sent++; ++ c->ssl->dyn_rec_last_write = ngx_current_msec; ++ + if (c->ssl->saved_read_handler) { + + c->read->handler = c->ssl->saved_read_handler; +diff --color -uNr a/src/event/ngx_event_openssl.h b/src/event/ngx_event_openssl.h +--- a/src/event/ngx_event_openssl.h 2023-06-13 23:08:10.000000000 +0800 ++++ b/src/event/ngx_event_openssl.h 2023-06-14 15:43:05.834243714 +0800 +@@ -86,10 +86,19 @@ + typedef struct ngx_ssl_ocsp_s ngx_ssl_ocsp_t; + + ++typedef struct { ++ ngx_msec_t timeout; ++ ngx_uint_t threshold; ++ size_t size_lo; ++ size_t size_hi; ++} ngx_ssl_dyn_rec_t; ++ ++ + struct ngx_ssl_s { + SSL_CTX *ctx; + ngx_log_t *log; + size_t buffer_size; ++ ngx_ssl_dyn_rec_t dyn_rec; + }; + + +@@ -128,6 +137,10 @@ + unsigned in_ocsp:1; + unsigned early_preread:1; + unsigned write_blocked:1; ++ ++ ngx_ssl_dyn_rec_t dyn_rec; ++ ngx_msec_t dyn_rec_last_write; ++ ngx_uint_t dyn_rec_records_sent; + }; + + +@@ -137,7 +150,7 @@ + #define NGX_SSL_DFLT_BUILTIN_SCACHE -5 + + +-#define NGX_SSL_MAX_SESSION_SIZE 4096 ++#define NGX_SSL_MAX_SESSION_SIZE 16384 + + typedef struct ngx_ssl_sess_id_s ngx_ssl_sess_id_t; + +diff --color -uNr a/src/http/modules/ngx_http_ssl_module.c b/src/http/modules/ngx_http_ssl_module.c +--- a/src/http/modules/ngx_http_ssl_module.c 2023-06-13 23:08:10.000000000 +0800 ++++ b/src/http/modules/ngx_http_ssl_module.c 2023-06-14 15:43:05.834243714 +0800 +@@ -290,6 +290,41 @@ + offsetof(ngx_http_ssl_srv_conf_t, reject_handshake), + NULL }, + ++ { ngx_string("ssl_dyn_rec_enable"), ++ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG, ++ ngx_conf_set_flag_slot, ++ NGX_HTTP_SRV_CONF_OFFSET, ++ offsetof(ngx_http_ssl_srv_conf_t, dyn_rec_enable), ++ NULL }, ++ ++ { ngx_string("ssl_dyn_rec_timeout"), ++ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG, ++ ngx_conf_set_msec_slot, ++ NGX_HTTP_SRV_CONF_OFFSET, ++ offsetof(ngx_http_ssl_srv_conf_t, dyn_rec_timeout), ++ NULL }, ++ ++ { ngx_string("ssl_dyn_rec_size_lo"), ++ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG, ++ ngx_conf_set_size_slot, ++ NGX_HTTP_SRV_CONF_OFFSET, ++ offsetof(ngx_http_ssl_srv_conf_t, dyn_rec_size_lo), ++ NULL }, ++ ++ { ngx_string("ssl_dyn_rec_size_hi"), ++ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG, ++ ngx_conf_set_size_slot, ++ NGX_HTTP_SRV_CONF_OFFSET, ++ offsetof(ngx_http_ssl_srv_conf_t, dyn_rec_size_hi), ++ NULL }, ++ ++ { ngx_string("ssl_dyn_rec_threshold"), ++ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG, ++ ngx_conf_set_num_slot, ++ NGX_HTTP_SRV_CONF_OFFSET, ++ offsetof(ngx_http_ssl_srv_conf_t, dyn_rec_threshold), ++ NULL }, ++ + ngx_null_command + }; + +@@ -629,6 +664,11 @@ + sscf->ocsp_cache_zone = NGX_CONF_UNSET_PTR; + sscf->stapling = NGX_CONF_UNSET; + sscf->stapling_verify = NGX_CONF_UNSET; ++ sscf->dyn_rec_enable = NGX_CONF_UNSET; ++ sscf->dyn_rec_timeout = NGX_CONF_UNSET_MSEC; ++ sscf->dyn_rec_size_lo = NGX_CONF_UNSET_SIZE; ++ sscf->dyn_rec_size_hi = NGX_CONF_UNSET_SIZE; ++ sscf->dyn_rec_threshold = NGX_CONF_UNSET_UINT; + + return sscf; + } +@@ -694,6 +734,20 @@ + ngx_conf_merge_str_value(conf->stapling_responder, + prev->stapling_responder, ""); + ++ ngx_conf_merge_value(conf->dyn_rec_enable, prev->dyn_rec_enable, 0); ++ ngx_conf_merge_msec_value(conf->dyn_rec_timeout, prev->dyn_rec_timeout, ++ 1000); ++ /* Default sizes for the dynamic record sizes are defined to fit maximal ++ TLS + IPv6 overhead in a single TCP segment for lo and 3 segments for hi: ++ 1369 = 1500 - 40 (IP) - 20 (TCP) - 10 (Time) - 61 (Max TLS overhead) */ ++ ngx_conf_merge_size_value(conf->dyn_rec_size_lo, prev->dyn_rec_size_lo, ++ 1369); ++ /* 4229 = (1500 - 40 - 20 - 10) * 3 - 61 */ ++ ngx_conf_merge_size_value(conf->dyn_rec_size_hi, prev->dyn_rec_size_hi, ++ 4229); ++ ngx_conf_merge_uint_value(conf->dyn_rec_threshold, prev->dyn_rec_threshold, ++ 40); ++ + conf->ssl.log = cf->log; + + if (conf->certificates) { +@@ -890,6 +944,28 @@ + return NGX_CONF_ERROR; + } + ++ if (conf->dyn_rec_enable) { ++ conf->ssl.dyn_rec.timeout = conf->dyn_rec_timeout; ++ conf->ssl.dyn_rec.threshold = conf->dyn_rec_threshold; ++ ++ if (conf->buffer_size > conf->dyn_rec_size_lo) { ++ conf->ssl.dyn_rec.size_lo = conf->dyn_rec_size_lo; ++ ++ } else { ++ conf->ssl.dyn_rec.size_lo = conf->buffer_size; ++ } ++ ++ if (conf->buffer_size > conf->dyn_rec_size_hi) { ++ conf->ssl.dyn_rec.size_hi = conf->dyn_rec_size_hi; ++ ++ } else { ++ conf->ssl.dyn_rec.size_hi = conf->buffer_size; ++ } ++ ++ } else { ++ conf->ssl.dyn_rec.timeout = 0; ++ } ++ + return NGX_CONF_OK; + } + +diff --color -uNr a/src/http/modules/ngx_http_ssl_module.h b/src/http/modules/ngx_http_ssl_module.h +--- a/src/http/modules/ngx_http_ssl_module.h 2023-06-13 23:08:10.000000000 +0800 ++++ b/src/http/modules/ngx_http_ssl_module.h 2023-06-14 15:43:38.264102815 +0800 +@@ -62,6 +62,12 @@ + ngx_flag_t stapling_verify; + ngx_str_t stapling_file; + ngx_str_t stapling_responder; ++ ++ ngx_flag_t dyn_rec_enable; ++ ngx_msec_t dyn_rec_timeout; ++ size_t dyn_rec_size_lo; ++ size_t dyn_rec_size_hi; ++ ngx_uint_t dyn_rec_threshold; + } ngx_http_ssl_srv_conf_t; + + diff --git a/patch_src b/patch_src new file mode 100644 index 0000000..042991a --- /dev/null +++ b/patch_src @@ -0,0 +1 @@ +https://github.com/kn007/patch/blob/master/