From cb4ae2603c7ff1ad5a8eb400ea0e3a83270189f3 Mon Sep 17 00:00:00 2001 From: Anatolii Zimovskii Date: Fri, 29 Oct 2021 15:28:15 +0300 Subject: [PATCH] nginx quic (http3) support --- .gitmodules | 3 + .../ecosystem/epicmorg/devel/main/Dockerfile | 20 +- linux/ecosystem/epicmorg/devel/main/Makefile | 1 + linux/ecosystem/nginx/latest/quic/.env | 2 + linux/ecosystem/nginx/latest/quic/Dockerfile | 26 ++ .../nginx/latest/quic/Dockerfile.experimental | 325 ++++++++++++++++++ linux/ecosystem/nginx/latest/quic/Makefile | 5 + linux/ecosystem/nginx/latest/quic/README.md | 291 ++++++++++++++++ .../nginx/latest/quic/docker-compose.yml | 9 + .../nginx/latest/quic/pre/boringssl-build.sh | 111 ++++++ .../quic/pre/ip2location-description-pak | 1 + .../latest/quic/pre/luajit2-description-pak | 1 + .../latest/quic/pre/nginx-description-pak | 1 + .../nginx/latest/quic/pre/ngninx.pre.tar.gz | Bin 0 -> 9573 bytes 14 files changed, 794 insertions(+), 2 deletions(-) create mode 100644 .gitmodules create mode 100644 linux/ecosystem/nginx/latest/quic/.env create mode 100644 linux/ecosystem/nginx/latest/quic/Dockerfile create mode 100644 linux/ecosystem/nginx/latest/quic/Dockerfile.experimental create mode 100644 linux/ecosystem/nginx/latest/quic/Makefile create mode 100644 linux/ecosystem/nginx/latest/quic/README.md create mode 100644 linux/ecosystem/nginx/latest/quic/docker-compose.yml create mode 100755 linux/ecosystem/nginx/latest/quic/pre/boringssl-build.sh create mode 100644 linux/ecosystem/nginx/latest/quic/pre/ip2location-description-pak create mode 100644 linux/ecosystem/nginx/latest/quic/pre/luajit2-description-pak create mode 100644 linux/ecosystem/nginx/latest/quic/pre/nginx-description-pak create mode 100644 linux/ecosystem/nginx/latest/quic/pre/ngninx.pre.tar.gz diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 000000000..b544ba297 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "linux/advanced/redash/redash-repo"] + path = linux/advanced/redash/redash-repo + url = git@github.com:getredash/redash.git diff --git a/linux/ecosystem/epicmorg/devel/main/Dockerfile b/linux/ecosystem/epicmorg/devel/main/Dockerfile index 386a6601f..3bcc12471 100644 --- a/linux/ecosystem/epicmorg/devel/main/Dockerfile +++ b/linux/ecosystem/epicmorg/devel/main/Dockerfile @@ -9,6 +9,9 @@ ENV BuildDocker true ARG BUILDS_DIR=/builds ARG SRC_DIR=${BUILDS_DIR}/src ARG EXPORT_DIR=${BUILDS_DIR}/export +ARG NINJA_ARCH=linux +ARG NINJA_VERSION=latest +ARG NINJA_RELEASE_URL=https://api.github.com/repos/ninja-build/ninja/releases/${NINJA_VERSION} ################################################################## # Files and folders @@ -30,6 +33,9 @@ RUN apt-get update && \ build-essential \ autoconf-archive \ gnu-standards \ + cmake \ + libunwind-dev \ + golang \ at \ autopkgtest \ gcc-multilib \ @@ -40,6 +46,7 @@ RUN apt-get update && \ libegl1-mesa-dev \ libgles2-mesa-dev \ libgbm-dev \ + uuid-dev \ nvidia-cg-toolkit \ nvidia-cg-dev \ libavcodec-dev \ @@ -47,7 +54,6 @@ RUN apt-get update && \ libsdl-image1.2-dev \ libxml2-dev yasm \ devscripts \ - autoconf \ automake \ libtool \ autotools-dev \ @@ -84,6 +90,15 @@ RUN apt-get update && \ libvpx6 \ tcl +################################################################## +# Get NINJA binary +################################################################## +RUN curl -s ${NINJA_RELEASE_URL} | jq -r ".assets[] | select(.name | test(\"${NINJA_ARCH}\")) | .browser_download_url" > /tmp/ninja-url.txt && \ + cat /tmp/ninja-url.txt && \ + cd /tmp && \ + wget -q -c --input-file=/tmp/ninja-url.txt && \ + unzip -o /tmp/ninja-linux.zip -d /bin && \ + printf "\n--------------------------------\nninja version: $(ninja --version)\n--------------------------------\n\n" ################################################################## # other customisations @@ -97,4 +112,5 @@ RUN apt purge policykit-1 -y && \ apt clean -y && \ apt autoclean -y && \ rm -rfv /var/lib/apt/lists/* && \ - rm -rfv /var/cache/apt/archives/*.deb + rm -rfv /var/cache/apt/archives/*.deb && \ + rm -rfv /tmp/* diff --git a/linux/ecosystem/epicmorg/devel/main/Makefile b/linux/ecosystem/epicmorg/devel/main/Makefile index 82c5a2de6..39e5c5f1b 100644 --- a/linux/ecosystem/epicmorg/devel/main/Makefile +++ b/linux/ecosystem/epicmorg/devel/main/Makefile @@ -3,3 +3,4 @@ all: app app: docker-compose build --compress docker-compose push + diff --git a/linux/ecosystem/nginx/latest/quic/.env b/linux/ecosystem/nginx/latest/quic/.env new file mode 100644 index 000000000..d5ad4e3df --- /dev/null +++ b/linux/ecosystem/nginx/latest/quic/.env @@ -0,0 +1,2 @@ +NGINX_VERSION=quic +NGINX_DOWNLOAD_URL=https://github.com/VKCOM/nginx-quic/archive/refs/heads/master.tar.gz diff --git a/linux/ecosystem/nginx/latest/quic/Dockerfile b/linux/ecosystem/nginx/latest/quic/Dockerfile new file mode 100644 index 000000000..d7bb918cf --- /dev/null +++ b/linux/ecosystem/nginx/latest/quic/Dockerfile @@ -0,0 +1,26 @@ +FROM nginx AS build + +WORKDIR /src +RUN apt-get update && \ + apt-get install -y git gcc make g++ cmake perl libunwind-dev golang && \ + git clone https://boringssl.googlesource.com/boringssl && \ + mkdir boringssl/build && \ + cd boringssl/build && \ + cmake .. && \ + make + +RUN apt-get install -y mercurial libperl-dev libpcre3-dev zlib1g-dev libxslt1-dev libgd-ocaml-dev libgeoip-dev && \ + hg clone https://hg.nginx.org/nginx-quic && \ + hg clone http://hg.nginx.org/njs && \ + cd nginx-quic && \ + hg update quic && \ + auto/configure `nginx -V 2>&1 | sed "s/ \-\-/ \\\ \n\t--/g" | grep "\-\-" | grep -ve opt= -e param= -e build=` \ + --build=nginx-quic --with-debug \ + --with-http_v3_module --with-http_quic_module --with-stream_quic_module \ + --with-cc-opt="-I/src/boringssl/include" --with-ld-opt="-L/src/boringssl/build/ssl -L/src/boringssl/build/crypto" && \ + make + +FROM nginx +COPY --from=build /src/nginx-quic/objs/nginx /usr/sbin +RUN /usr/sbin/nginx -V > /dev/stderr +EXPOSE 80 443 diff --git a/linux/ecosystem/nginx/latest/quic/Dockerfile.experimental b/linux/ecosystem/nginx/latest/quic/Dockerfile.experimental new file mode 100644 index 000000000..da1919e7c --- /dev/null +++ b/linux/ecosystem/nginx/latest/quic/Dockerfile.experimental @@ -0,0 +1,325 @@ +################################################################## +# Set Global ARG to build process +################################################################## +ARG NGINX_VERSION + +################################################################## +# Start build process +################################################################## +FROM epicmorg/devel AS builder +LABEL maintainer="EpicMorg DevTeam, developer@epicm.org" +ARG DEBIAN_FRONTEND=noninteractive + +################################################################## +# ARGuments +################################################################## +ENV BuildDocker true +ARG BUILDS_DIR=/builds +ARG SRC_DIR=${BUILDS_DIR}/src +ARG EXPORT_DIR=${BUILDS_DIR}/export +ARG PRE_DIR=${BUILDS_DIR}/pre +ARG BSSL_SRC_DIR=${SRC_DIR}/boringssl +ARG NGINX_SRC_DIR=${SRC_DIR}/nginx +ARG NGINX_VERSION +ARG NGINX_DOWNLOAD_URL +ARG LUAJIT_INC=/usr/local/include/luajit-2.1 +ARG LUAJIT_LIB=/usr/local/lib +ARG DCMAKE_BUILD_TYPE=Release + +################################################################## +# Files and folders +################################################################## +RUN mkdir -p ${PRE_DIR} ${NGINX_SRC_DIR} /usr/lib/nginx +ADD pre/luajit2-description-pak ${PRE_DIR} +ADD pre/nginx-description-pak ${PRE_DIR} +ADD pre/ip2location-description-pak ${PRE_DIR} +ADD pre/boringssl-build.sh ${SRC_DIR} + +################################################################## +# IP2Location support for prod nginx module +################################################################## +RUN cd ${SRC_DIR} && \ + git clone https://github.com/chrislim2888/IP2Location-C-Library.git ip2 && \ + cp -fv ${PRE_DIR}/ip2location-description-pak ${SRC_DIR}/ip2/description-pak && \ + cd ${SRC_DIR}/ip2 && \ + ls -las && \ + autoreconf -i -v --force && \ + aclocal && \ + automake --gnu --add-missing && \ + autoconf && \ + autoreconf -i -v --force && \ + ./configure && \ + ls -las && \ + make clean && \ + make && \ + make -C data convert && \ + make check && \ + ls -las && \ + fakeroot checkinstall -D --pakdir=${EXPORT_DIR} --maintainer="EpicMorg, developer@epicm.org" --pkgname=ip2-custom --conflicts=ip2 --install=yes -y && \ + ln -s /usr/local/lib/libIP2Location.so /usr/lib/libIP2Location.so && \ + ln -s /usr/local/lib/libIP2Location.so.1 /usr/lib/libIP2Location.so.1 && \ + ln -s /usr/local/lib/libIP2Location.so.2 /usr/lib/libIP2Location.so.2 && \ + ln -s /usr/local/lib/libIP2Location.so /lib/libIP2Location.so && \ + ln -s /usr/local/lib/libIP2Location.so.1 /lib/libIP2Location.so.1 && \ + ln -s /usr/local/lib/libIP2Location.so.2 /lib/libIP2Location.so.2 && \ + ln -s /lib/x86_64-linux-gnu/libcrypto.so.1.1 /lib/x86_64-linux-gnu/libcrypto.so.1 && \ + dpkg --force-all -i ${EXPORT_DIR}/*.deb + +################################################################## +# luaJIT 2 support for prod nginx module +################################################################## +RUN cd ${SRC_DIR} && \ + git clone https://github.com/openresty/luajit2.git luajit2 && \ + cp -fv ${PRE_DIR}/luajit2-description-pak ${SRC_DIR}/luajit2/description-pak && \ + cd ${SRC_DIR}/luajit2 && \ + make && \ + make install && \ + fakeroot checkinstall -D --pakdir=${EXPORT_DIR} --maintainer="EpicMorg, developer@epicm.org" --pkgname=luajit2-custom --conflicts=luajit2 --install=no -y + + +################################################################## +# BotingSSL - google fork with quic +################################################################## + +# compile from sources +RUN cd ${SRC_DIR} && \ + ./boringssl-build.sh + +# git clone https://github.com/google/boringssl.git boringssl && \ +# apt-get update && \ +# apt-get install -y git gcc make g++ cmake perl libunwind-dev golang && \ +# cd boringssl && \ +# mkdir build && \ +# cd build && \ +# pwd && \ +# cmake .. && \ +# make +# cmake -GNinja .. && \ +# ninja +# cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=0 -GNinja .. && \ +# ninja +# cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=0 .. && \ +# make + +# Make an .openssl directory for nginx and then symlink BoringSSL's include directory tree +#RUN mkdir -p ${BSSL_SRC_DIR}/.openssl/lib && \ +# ln -sf ${BSSL_SRC_DIR}/include ${BSSL_SRC_DIR}/.openssl/include + +# Copy the BoringSSL crypto libraries to .openssl/lib so nginx can find them +#RUN cp -rfv ${BSSL_SRC_DIR}/build/crypto/libcrypto.a ${BSSL_SRC_DIR}/.openssl/lib && \ +# cp -rfv ${BSSL_SRC_DIR}/build/ssl/libssl.a ${BSSL_SRC_DIR}/.openssl/lib + +# Fix "Error 127" during build +#RUN touch ${BSSL_SRC_DIR}/include/openssl/ssl.h +#RUN touch ${BSSL_SRC_DIR}/.openssl/include/openssl/ssl.h + + +################################################################## +# quictls/openssl - community fork with quic +################################################################## + +#RUN printf "\n--------------------------------\nPreinstlalled openssl version is: $(openssl version)\n--------------------------------\n\n" +#RUN cd ${SRC_DIR} && \ +# git clone https://github.com/quictls/openssl.git openssl && \ +# cd openssl && \ +# pwd && \ +# ./Configure +# && \ +# make && \ +# make test +# && \ +# make install + +#RUN printf "\n--------------------------------\nCurrent openssl version is: $(openssl version)\n--------------------------------\n\n" + +#RUN openssl fipsinstall + +#RUN openssl version + +################################################################## +# nginx preparing +################################################################## +#RUN wget -qO - ${NGINX_DOWNLOAD_URL} | tar -zxv --strip-components=1 -C ${NGINX_SRC_DIR} && \ +RUN cd ${SRC_DIR} && \ + hg clone https://hg.nginx.org/nginx-quic nginx && \ + cd ${NGINX_SRC_DIR} && \ + hg update quic && \ + hg clone http://hg.nginx.org/njs && \ + git clone https://github.com/openresty/headers-more-nginx-module.git http-headers-more-filter && \ + git clone https://github.com/sto/ngx_http_auth_pam_module.git http-auth-pam && \ + git clone https://github.com/arut/nginx-dav-ext-module.git http-dav-ext && \ + git clone https://github.com/openresty/echo-nginx-module.git http-echo && \ + git clone https://github.com/aperezdc/ngx-fancyindex.git http-fancyindex && \ + git clone https://github.com/slact/nchan.git nchan && \ + git clone https://github.com/masterzen/nginx-upload-progress-module.git http-uploadprogress && \ + git clone https://github.com/yaoweibin/ngx_http_substitutions_filter_module http-subs-filter && \ + git clone https://github.com/grahamedgecombe/nginx-ct.git ssl-ct && \ + git clone https://github.com/stnoonan/spnego-http-auth-nginx-module.git spnego-http-auth-nginx-module && \ + git clone https://github.com/leev/ngx_http_geoip2_module http-geoip2 && \ + git clone https://github.com/flavioribeiro/nginx-audio-track-for-hls-module.git nginx-audio-track-for-hls-module && \ + git clone https://github.com/chrislim2888/ip2location-nginx.git ip2location-nginx && \ + git clone https://github.com/kaltura/nginx-vod-module.git nginx-vod-module && \ + git clone https://github.com/vozlt/nginx-module-vts.git nginx-module-vts && \ + git clone https://github.com/evanmiller/mod_zip.git mod-zip && \ + git clone https://github.com/alibaba/nginx-http-user-agent.git nginx-http-user-agent && \ + git clone https://github.com/youzee/nginx-unzip-module.git nginx-unzip-module && \ + git clone https://github.com/vladbondarenko/ngx_webp.git ngx-webp && \ + git clone https://github.com/openresty/xss-nginx-module.git xss-nginx-module && \ + git clone https://github.com/openresty/set-misc-nginx-module.git set-misc-nginx-module && \ + git clone https://github.com/arut/nginx-rtmp-module.git rtmp && \ + git clone https://github.com/kvspb/nginx-auth-ldap.git http-auth-ldap && \ + git clone https://github.com/simplresty/ngx_devel_kit.git http-ndk && \ + git clone https://github.com/chrislim2888/IP2Location-C-Library.git ip2location-c-7.0.0 && \ + git clone https://github.com/itoffshore/nginx-upstream-fair.git http-upstream-fair && \ + git clone https://github.com/yaoweibin/nginx_upstream_check_module.git nginx-upstream-check-module && \ + git clone https://github.com/openresty/lua-nginx-module http-lua + +################################################################## +# nginx compilling +################################################################## +RUN cd ${NGINX_SRC_DIR} && \ + ./auto/configure `nginx -V 2>&1 | sed "s/ \-\-/ \\\ \n\t--/g" | grep "\-\-" | grep -ve opt= -e param= -e build=` \ + --build=nginx-quic \ + --add-module=./njs/nginx \ + --with-openssl=/builds/src/boringssl \ + --with-http_v3_module \ + --with-http_quic_module \ + --with-stream_quic_module \ + --sbin-path=/usr/sbin/nginx \ + --prefix=/usr/share/nginx \ + --conf-path=/etc/nginx/nginx.conf \ + --http-log-path=/var/log/nginx/access.log \ + --error-log-path=/var/log/nginx/error.log \ + --lock-path=/var/lock/nginx.lock \ + --pid-path=/run/nginx.pid \ + --modules-path=/usr/lib/nginx/modules \ + --http-client-body-temp-path=/var/lib/nginx/body \ + --http-fastcgi-temp-path=/var/lib/nginx/fastcgi \ + --http-proxy-temp-path=/var/lib/nginx/proxy \ + --http-scgi-temp-path=/var/lib/nginx/scgi \ + --http-uwsgi-temp-path=/var/lib/nginx/uwsgi \ +# --with-cc-opt='-I/usr/local/include/luajit-2.1 -I/builds/src/nginx/boringssl/include -g -O2 -lz -fstack-protector-strong -Wformat -Wno-error=date-time -Wno-error=implicit-fallthrough= -Wno-error=cast-function-type -Wno-error=format-security -Wno-error=implicit-function-declaration -Wno-error=deprecated-declarations -Wno-error=unused-result -fPIC -Wdate-time -D_FORTIFY_SOURCE=2' \ +# --with-ld-opt='-Wl,-z,relro -Wl,-z,now -lz -fPIC -L/usr/local/lib -L /builds/src/nginx/boringssl/build/ssl -L/builds/src/nginx/boringssl/build/crypto' \ + --with-cc-opt='-I/usr/local/include/luajit-2.1 -I/builds/src/boringssl/include -g -O2 -lz -fstack-protector-strong -Wformat -Wno-error=date-time -Wno-error=implicit-fallthrough= -Wno-error=cast-function-type -Wno-error=format-security -Wno-error=implicit-function-declaration -Wno-error=deprecated-declarations -Wno-error=unused-result -fPIC -Wdate-time -D_FORTIFY_SOURCE=2' \ + --with-ld-opt='-Wl,-z,relro -Wl,-z,now -lz -fPIC -L/builds/src/boringssl/build/ssl -L/builds/src/boringssl/build/crypto' \ + --with-file-aio \ + --with-compat \ + --with-debug \ + --with-threads \ + --with-pcre-jit \ + --with-http_ssl_module \ + --with-http_stub_status_module \ + --with-http_realip_module \ + --with-http_auth_request_module \ + --with-http_v2_module \ + --with-http_dav_module \ + --with-http_slice_module \ + --with-http_addition_module \ + --with-http_flv_module \ + --with-http_geoip_module=dynamic \ + --with-http_gunzip_module \ + --with-http_gzip_static_module \ + --with-http_image_filter_module=dynamic \ + --with-http_mp4_module \ + --with-http_perl_module=dynamic \ + --with-http_random_index_module \ + --with-http_secure_link_module \ + --with-http_sub_module \ + --with-http_xslt_module=dynamic \ + --with-mail=dynamic \ + --with-mail_ssl_module \ + --with-stream=dynamic \ + --with-stream_ssl_module \ + --with-stream_ssl_preread_module \ + --add-dynamic-module=http-headers-more-filter \ + --add-dynamic-module=http-auth-pam \ + --add-dynamic-module=http-dav-ext \ + --add-dynamic-module=http-ndk \ + --add-dynamic-module=http-echo \ + --add-dynamic-module=http-fancyindex \ + --add-dynamic-module=nchan \ + --add-dynamic-module=http-uploadprogress \ + --add-dynamic-module=http-subs-filter \ + --add-dynamic-module=ssl-ct \ + --add-dynamic-module=http-geoip2 \ + --add-dynamic-module=spnego-http-auth-nginx-module \ + --add-dynamic-module=http-auth-ldap \ +# --add-dynamic-module=nginx-audio-track-for-hls-module \ + --add-dynamic-module=ip2location-nginx \ + --add-dynamic-module=nginx-vod-module \ +# --add-dynamic-module=nginx-module-vts \ + --add-dynamic-module=mod-zip \ + --add-dynamic-module=nginx-http-user-agent \ + --add-dynamic-module=nginx-unzip-module \ + --add-dynamic-module=ngx-webp \ + --add-dynamic-module=set-misc-nginx-module \ + --add-dynamic-module=rtmp \ + --add-dynamic-module=http-upstream-fair \ + --add-dynamic-module=nginx-upstream-check-module \ + --add-dynamic-module=http-lua && \ + cp -fv ${PRE_DIR}/nginx-description-pak ${NGINX_SRC_DIR}/description-pak && \ +# dpkg-buildpackage -b && \ + make && \ +# fakeroot checkinstall -D --pakdir=/builds/export --maintainer="EpicMorg, developer@epicm.org" --pkgname=nginx-custom --install=no -y && \ + apt clean -y && \ + apt autoclean -y && \ + rm -rfv /var/lib/apt/lists/* && \ + rm -rfv /var/cache/apt/archives/*.deb && \ + rm -rfv /tmp/* + +################################################################## +################################################################## +################################################################## + +FROM epicmorg/edge +LABEL maintainer="EpicMorg DevTeam, developer@epicm.org" +ARG DEBIAN_FRONTEND=noninteractive + +################################################################## +# LDAP Fix +################################################################## +RUN echo "TLS_REQCERT never" >> /etc/ldap/ldap.conf + +################################################################## +# Installing nginx from deb +################################################################## +ADD pre/ngninx.pre.tar.gz / +COPY --from=builder /builds/export /tmp/deb +RUN apt-get update && \ + apt-get install -y --allow-unauthenticated \ + geoip-database \ + geoip-bin \ + libgeoip1 \ + libmaxminddb0 \ + libgd3 \ + libxslt1.1 && \ + dpkg --force-all -i /tmp/deb/*.deb && \ + ln -s /usr/local/lib/libIP2Location.so /usr/lib/libIP2Location.so && \ + ln -s /usr/local/lib/libIP2Location.so.1 /usr/lib/libIP2Location.so.1 && \ + ln -s /usr/local/lib/libIP2Location.so.2 /usr/lib/libIP2Location.so.2 && \ + ln -s /usr/local/lib/libIP2Location.so.3 /usr/lib/libIP2Location.so.3 && \ + ln -s /usr/local/lib/libIP2Location.so.4 /usr/lib/libIP2Location.so.4 && \ + ln -s /usr/local/lib/libIP2Location.so.5 /usr/lib/libIP2Location.so.5 && \ + ln -s /usr/local/lib/libIP2Location.so /lib/libIP2Location.so && \ + ln -s /usr/local/lib/libIP2Location.so.1 /lib/libIP2Location.so.1 && \ + ln -s /usr/local/lib/libIP2Location.so.2 /lib/libIP2Location.so.2 && \ + ln -s /usr/local/lib/libIP2Location.so.3 /lib/libIP2Location.so.3 && \ + ln -s /usr/local/lib/libIP2Location.so.4 /lib/libIP2Location.so.4 && \ + ln -s /usr/local/lib/libIP2Location.so.5 /lib/libIP2Location.so.5 && \ + ln -sf /dev/stdout /var/log/nginx/access.log && \ + ln -sf /dev/stderr /var/log/nginx/error.log && \ + ln -sf /etc/ssl/dhparam.pem /etc/nginx/dhparam.pem && \ + apt clean -y && \ + apt autoclean -y && \ + rm -rf /var/lib/apt/lists/* && \ + rm -rf /var/cache/apt/archives/*.deb && \ + rm -rf /tmp/deb/* && \ + rm -rf /builds/* && \ + rm -rf /valve/* && \ + rm -rfv /tmp/* + +#Final config +VOLUME ["/var/cache/nginx"] +EXPOSE 80 443 + +CMD ["nginx", "-g", "daemon off;"] diff --git a/linux/ecosystem/nginx/latest/quic/Makefile b/linux/ecosystem/nginx/latest/quic/Makefile new file mode 100644 index 000000000..82c5a2de6 --- /dev/null +++ b/linux/ecosystem/nginx/latest/quic/Makefile @@ -0,0 +1,5 @@ +all: app + +app: + docker-compose build --compress + docker-compose push diff --git a/linux/ecosystem/nginx/latest/quic/README.md b/linux/ecosystem/nginx/latest/quic/README.md new file mode 100644 index 000000000..49377400f --- /dev/null +++ b/linux/ecosystem/nginx/latest/quic/README.md @@ -0,0 +1,291 @@ +# nginx quic + +Experimental QUIC support for nginx +----------------------------------- + +1. Introduction +2. Installing +3. Configuration +4. Clients +5. Troubleshooting +6. Contributing +7. Links + +1. Introduction + + This is an experimental QUIC [1] / HTTP/3 [2] support for nginx. + + The code is developed in a separate "quic" branch available + at https://hg.nginx.org/nginx-quic. Currently it is based + on nginx mainline 1.21.x. We merge new nginx releases into + this branch regularly. + + The project code base is under the same BSD license as nginx. + + The code is currently at a beta level of quality and should not + be used in production. + + We are working on improving HTTP/3 support with the goal of + integrating it to the main NGINX codebase. Expect frequent + updates of this code and don't rely on it for whatever purpose. + + We'll be grateful for any feedback and code submissions however + we don't bear any responsibilities for any issues with this code. + + You can always contact us via nginx-devel mailing list [3]. + + What works now: + + Currently we support IETF-QUIC draft-29 through final RFC documents. + Earlier drafts are NOT supported as they have incompatible wire format. + + nginx should be able to respond to HTTP/3 requests over QUIC and + it should be possible to upload and download big files without errors. + + + The handshake completes successfully + + One endpoint can update keys and its peer responds correctly + + 0-RTT data is being received and acted on + + Connection is established using TLS Resume Ticket + + A handshake that includes a Retry packet completes successfully + + Stream data is being exchanged and ACK'ed + + An H3 transaction succeeded + + One or both endpoints insert entries into dynamic table and + subsequently reference them from header blocks + + Version Negotiation packet is sent to client with unknown version + + Lost packets are detected and retransmitted properly + + Clients may migrate to new address + + Not (yet) supported features: + + - Explicit Congestion Notification (ECN) as specified in quic-recovery [5] + - A connection with the spin bit succeeds and the bit is spinning + - Structured Logging + + Since the code is experimental and still under development, + a lot of things may not work as expected, for example: + + - Flow control mechanism is basic and intended to avoid CPU hog and make + simple interactions possible + + - Not all protocol requirements are strictly followed; some of checks are + omitted for the sake of simplicity of initial implementation + +2. Installing + + You will need a BoringSSL [4] library that provides QUIC support + + $ hg clone -b quic https://hg.nginx.org/nginx-quic + $ cd nginx-quic + $ ./auto/configure --with-debug --with-http_v3_module \ + --with-cc-opt="-I../boringssl/include" \ + --with-ld-opt="-L../boringssl/build/ssl \ + -L../boringssl/build/crypto" + $ make + + When configuring nginx, you can enable QUIC and HTTP/3 using the + following new configuration options: + + --with-http_v3_module - enable QUIC and HTTP/3 + --with-http_quic_module - enable QUIC for older HTTP versions + --with-stream_quic_module - enable QUIC in Stream + +3. Configuration + + The HTTP "listen" directive got two new options: "http3" and "quic". + The "http3" option enables HTTP/3 over QUIC on the specified port. + The "quic" option enables QUIC for older HTTP versions on this port. + + The Stream "listen" directive got a new option "quic" which enables + QUIC as client transport protocol instead of TCP or plain UDP. + + Along with "http3" or "quic", you also have to specify "reuseport" + option [6] to make it work properly with multiple workers. + + A number of directives were added that specify transport parameter values: + + quic_max_idle_timeout + quic_max_ack_delay + quic_max_udp_payload_size + quic_initial_max_data + quic_initial_max_stream_data_bidi_local + quic_initial_max_stream_data_bidi_remote + quic_initial_max_stream_data_uni + quic_initial_max_streams_bidi + quic_initial_max_streams_uni + quic_ack_delay_exponent + quic_disable_active_migration + quic_active_connection_id_limit + + To enable address validation: + + quic_retry on; + + To enable 0-RTT: + + ssl_early_data on; + + Make sure that TLS 1.3 is configured which is required for QUIC: + + ssl_protocols TLSv1.3; + + To enable GSO (Generic Segmentation Offloading): + + quic_gso on; + + By default this Linux-specific optimization [8] is disabled. + Enable if your network interface is configured to support GSO. + + A number of directives were added that configure HTTP/3: + + http3_max_table_capacity + http3_max_blocked_streams + http3_max_concurrent_pushes + http3_push + http3_push_preload + + An additional variable is available: $quic. + The value of $quic is "quic" if QUIC connection is used, + or an empty string otherwise. + +Example configuration: + + http { + log_format quic '$remote_addr - $remote_user [$time_local] ' + '"$request" $status $body_bytes_sent ' + '"$http_referer" "$http_user_agent" "$quic"'; + + access_log logs/access.log quic; + + server { + # for better compatibility it's recommended + # to use the same port for quic and https + listen 8443 http3 reuseport; + listen 8443 ssl; + + ssl_certificate certs/example.com.crt; + ssl_certificate_key certs/example.com.key; + ssl_protocols TLSv1.3; + + location / { + # required for browsers to direct them into quic port + add_header Alt-Svc 'h3=":8443"; ma=86400'; + } + } + } + +4. Clients + + * Browsers + + Known to work: Firefox 80+ and Chrome 85+ (QUIC draft 29+) + + Beware of strange issues: sometimes browser may decide to ignore QUIC + Cache clearing/restart might help. Always check access.log and + error.log to make sure you are using HTTP/3 and not TCP https. + + + to enable QUIC in Firefox, set the following in 'about:config': + network.http.http3.enabled = true + + + to enable QUIC in Chrome, enable it on command line and force it + on your site: + + $ ./chrome --enable-quic --quic-version=h3-29 \ + --origin-to-force-quic-on=example.com:8443 + + * Console clients + + Known to work: ngtcp2, firefox's neqo and chromium's console clients: + + $ examples/client 127.0.0.1 8443 https://example.com:8443/index.html + + $ ./neqo-client https://127.0.0.1:8443/ + + $ chromium-build/out/my_build/quic_client http://example.com:8443 \ + --quic_version=h3-29 \ + --allow_unknown_root_cert \ + --disable_certificate_verification + + + If you've got it right, in the access log you should see something like: + + 127.0.0.1 - - [24/Apr/2020:11:27:29 +0300] "GET / HTTP/3" 200 805 "-" + "nghttp3/ngtcp2 client" "quic" + + +5. Troubleshooting + + Here are some tips that may help you to identify problems: + + + Ensure you are building with proper SSL library that supports QUIC + + + Ensure you are using the proper SSL library in runtime + (`nginx -V` will show you what you are using) + + + Ensure your client is actually sending QUIC requests + (see "Clients" section about browsers and cache) + + We recommend to start with simple console client like ngtcp2 + to ensure you've got server configured properly before trying + with real browsers that may be very picky with certificates, + for example. + + + Build nginx with debug support [7] and check your debug log. + It should contain all details about connection and why it + failed. All related messages contain "quic " prefix and can + be easily filtered out. + + + If you want to investigate deeper, you may want to enable + additional debugging in src/event/quic/ngx_event_quic_connection.h: + + #define NGX_QUIC_DEBUG_PACKETS + #define NGX_QUIC_DEBUG_FRAMES + #define NGX_QUIC_DEBUG_ALLOC + #define NGX_QUIC_DEBUG_CRYPTO + +6. Contributing + + If you are willing to contribute, please refer to + http://nginx.org/en/docs/contributing_changes.html + +7. Links + + [1] https://datatracker.ietf.org/doc/html/rfc9000 + [2] https://datatracker.ietf.org/doc/html/draft-ietf-quic-http + [3] https://mailman.nginx.org/mailman/listinfo/nginx-devel + [4] https://boringssl.googlesource.com/boringssl/ + [5] https://datatracker.ietf.org/doc/html/rfc9002 + [6] https://nginx.org/en/docs/http/ngx_http_core_module.html#listen + [7] https://nginx.org/en/docs/debugging_log.html + [8] http://vger.kernel.org/lpc_net2018_talks/willemdebruijn-lpc2018-udpgso-paper-DRAFT-1.pdf + + +# Compose example + +```yml +version: '3.7' +services: + balancer: + image: epicmorg/balancer + restart: unless-stopped + ports: + - "0.0.0.0:80:80" + - "0.0.0.0:443:443" + volumes: + - /etc/localtime:/etc/localtime + - /etc/timezone:/etc/timezone + - /etc/letsencrypt:/etc/letsencrypt + - nginx:/etc/nginx + - nginx-usr:/usr/share/nginx/html + - /var/lib/nginx +# extra_hosts: +# - "example.com:192.168.0.11" + depends_on: + - websites + tmpfs: + - /tmp +volumes: + nginx: + external: true + nginx-usr: + external: true +``` diff --git a/linux/ecosystem/nginx/latest/quic/docker-compose.yml b/linux/ecosystem/nginx/latest/quic/docker-compose.yml new file mode 100644 index 000000000..4d5d761fb --- /dev/null +++ b/linux/ecosystem/nginx/latest/quic/docker-compose.yml @@ -0,0 +1,9 @@ +version: '3.9' +services: + app: + image: "epicmorg/nginx:${NGINX_VERSION}" + build: + context: . + args: + NGINX_VERSION: ${NGINX_VERSION} + NGINX_DOWNLOAD_URL: ${NGINX_DOWNLOAD_URL} \ No newline at end of file diff --git a/linux/ecosystem/nginx/latest/quic/pre/boringssl-build.sh b/linux/ecosystem/nginx/latest/quic/pre/boringssl-build.sh new file mode 100755 index 000000000..3232503e9 --- /dev/null +++ b/linux/ecosystem/nginx/latest/quic/pre/boringssl-build.sh @@ -0,0 +1,111 @@ +#!/bin/sh +WORKDIRECTORY=$PWD +ARCH=$(uname -m) +if command -v git > /dev/null 2>&1; then + echo "Checking git: OK" +else + echo "Checking git: FAILED, please install git" + exit 1 +fi + +if command -v cmake > /dev/null 2>&1; then + echo "Checking cmake: OK" +else + echo "Checking cmake: FAILED, please install cmake" + exit 1 +fi + +if command -v curl > /dev/null 2>&1; then + echo "Checking curl: OK" +else + echo "Checking curl: FAILED, please install curl" + exit 1 +fi + +if [ -d $WORKDIRECTORY/go ]; then +PATH=$WORKDIRECTORY/go/bin:$PATH +GOROOT=$WORKDIRECTORY/go +if [ -z $GOROOT ];then +NO_GOROOT_SYSTEM=true +fi +else +if [ -z $GOROOT ];then +if [ "$ARCH" = "x86_64" ]; then +GOURL="https://dl.google.com/go/$(curl https://golang.org/VERSION?m=text).linux-amd64.tar.gz" +fi +if [ "$ARCH" = "i386" ]; then +GOURL="https://dl.google.com/go/$(curl https://golang.org/VERSION?m=text).linux-386.tar.gz" +fi +if [ "$ARCH" = "armv6l" ]; then +GOURL="https://dl.google.com/go/$(curl https://golang.org/VERSION?m=text).linux-armv6l.tar.gz" +fi +if [ "$ARCH" = "armv7l" ]; then +GOURL="https://dl.google.com/go/$(curl https://golang.org/VERSION?m=text).linux-armv6l.tar.gz" +fi +if [ "$ARCH" = "" ]; then +echo "Your architecture is not supported" +fi +echo "Downloading golang" +curl -so $WORKDIRECTORY/go.tar.gz $GOURL +tar -xzf $WORKDIRECTORY/go.tar.gz +rm -rf $WORKDIRECTORY/go.tar.gz +PATH=$WORKDIRECTORY/go/bin:$PATH +GOROOT=$WORKDIRECTORY/go +NO_GOROOT_SYSTEM=true +fi +fi + +NETWORK_CHECK=$(curl -I -s --connect-timeout 5 https://github.com -w %{http_code} | tail -n1) + +if [ -d $WORKDIRECTORY/boringssl ]; then +cd $WORKDIRECTORY/boringssl +git pull +git reset --hard origin/master +git am $WORKDIRECTORY/*.patch +rm -rf $WORKDIRECTORY/boringssl/build +rm -rf $WORKDIRECTORY/boringssl/build2 +rm -rf $WORKDIRECTORY/boringssl/.openssl +else +if [ "$NETWORK_CHECK" = "200" ]; then + git clone --depth 1 https://github.com/google/boringssl.git $WORKDIRECTORY/boringssl + cd $WORKDIRECTORY/boringssl + git am $WORKDIRECTORY/*.patch +else + echo "Unable to connect to GitHub, please check your Internet availability" + exit 1 +fi +fi + +mkdir $WORKDIRECTORY/boringssl/build +cd $WORKDIRECTORY/boringssl/build +echo "Building Static libraries" +cmake .. -DCMAKE_BUILD_TYPE=Release +make -j`nproc` +mkdir $WORKDIRECTORY/boringssl/build2 +cd $WORKDIRECTORY/boringssl/build2 +echo "Building Shared objects" +cmake .. -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=1 +make -j`nproc` +mkdir $WORKDIRECTORY/boringssl/.openssl +mkdir $WORKDIRECTORY/boringssl/.openssl/include +mkdir $WORKDIRECTORY/boringssl/.openssl/include/openssl +cd $WORKDIRECTORY/boringssl/.openssl/include/openssl +ln $WORKDIRECTORY/boringssl/include/openssl/* . +mkdir $WORKDIRECTORY/boringssl/.openssl/lib +mkdir $WORKDIRECTORY/boringssl/lib +cp $WORKDIRECTORY/boringssl/build/crypto/libcrypto.a $WORKDIRECTORY/boringssl/.openssl/lib/libcrypto.a +cp $WORKDIRECTORY/boringssl/build/ssl/libssl.a $WORKDIRECTORY/boringssl/.openssl/lib/libssl.a +cp $WORKDIRECTORY/boringssl/build2/crypto/libcrypto.so $WORKDIRECTORY/boringssl/.openssl/lib/libcrypto.so +cp $WORKDIRECTORY/boringssl/build2/ssl/libssl.so $WORKDIRECTORY/boringssl/.openssl/lib/libssl.so + +echo "If you want to compile nginx" +echo "git am nginx-boringssl/*.patch in nginx source directory" +echo "and" +echo "Configure nginx with \"--with-openssl=$WORKDIRECTORY/boringssl\". Use nginx version >= 1.15 for best result." +echo "" +#if [ "$NO_GOROOT_SYSTEM" = "true" ]; then +#echo "Runing" +#echo "export PATH=$WORKDIRECTORY/go/bin:\$PATH" +#echo "export GOROOT=$WORKDIRECTORY/go" +#echo "If you want to compile nginx" +#fi diff --git a/linux/ecosystem/nginx/latest/quic/pre/ip2location-description-pak b/linux/ecosystem/nginx/latest/quic/pre/ip2location-description-pak new file mode 100644 index 000000000..e93eb7783 --- /dev/null +++ b/linux/ecosystem/nginx/latest/quic/pre/ip2location-description-pak @@ -0,0 +1 @@ +Custom build of ip2location lib by EpicMorg. diff --git a/linux/ecosystem/nginx/latest/quic/pre/luajit2-description-pak b/linux/ecosystem/nginx/latest/quic/pre/luajit2-description-pak new file mode 100644 index 000000000..4305e8e88 --- /dev/null +++ b/linux/ecosystem/nginx/latest/quic/pre/luajit2-description-pak @@ -0,0 +1 @@ +Custom build of luajit2 for Nginx module, by EpicMorg. diff --git a/linux/ecosystem/nginx/latest/quic/pre/nginx-description-pak b/linux/ecosystem/nginx/latest/quic/pre/nginx-description-pak new file mode 100644 index 000000000..b6c186ed8 --- /dev/null +++ b/linux/ecosystem/nginx/latest/quic/pre/nginx-description-pak @@ -0,0 +1 @@ +Custom build of Nginx with some modules by EpicMorg. \ No newline at end of file diff --git a/linux/ecosystem/nginx/latest/quic/pre/ngninx.pre.tar.gz b/linux/ecosystem/nginx/latest/quic/pre/ngninx.pre.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..bf9c2735172faf460d34cb157f13291f42cdef88 GIT binary patch literal 9573 zcmV-rC7RkFiwFRv!iZe}1MEF(QyaOm`_=O+wAi%?yZCKP4ivk^f|F2(00)~*ZDn(O zh8fw`Wjr%G(g5C&``d4KYsT}i%_9MCPF*V%u=VI}b+=klt0gMc@18x?AZ=}K(r-xl z-}JfO+-x=Kt$J;<4f$JJ^~QH>^Z7~p?z>PbGhpny!1L5y_3kVGFHM!|l^Hy<4m?o) zpaJczMg#Ie3+lC%{Fjlm{2g)ej5_dm`PUm@23GQ4LQ3TC4uyO3EL!k*`8Qg%`bz%G zNRj-#;Wsw^w^s6BN=oGaZH@nWYbF0>BrX5z>+5f8{5P8``7b3U@*k?V2 zTdn_>k}A)<_Q&*i`PW+Q)%t%aNy}eOq~c@yne^Zb#(#aa|65MV%3uF}YBhMg{F|G# zmH%%kX|DWfD^QU{U0{qv}Ee5aBp12whjW!wnYcC{yMom(229 z6?hInGhI8Oqt`im$6gLhshAvv%J#0^DIH@|xM?!>hy>I1piq<26Jzd$3cJ?j*6!x| z20<5tgecPyS3Dtx5Cbeg{m;XrBSd8a(TFbKh!9ARaRSvq02W0VY#4Z<&tCo$`uWbY z`R-WUaC^N%Jk}Vc7`mn-0oZ^C9A#vC);1K6l=8Q$(Ma`zVU@d8D3aBPF%?|S1E3G* zu23J111_yV_)2*0?j9S7;fVP>0C|r|@Yno;;czE@*vtfc@L3Y2H&v1p+RCL&oXh z!E530-6}{s>XR>QL+hCtsM7$-LK#%$g@`J!vSQ^wS$W7_*d`x)F7wUI*U9cUbd)HEAl6tgf43Q0rN1dvNUNV0#{<`Y z$@wq*Y&2KvzhxvX|8L^_FD3t#|9@F2k^kTB|4+$(<^Nw+s#LkMz76}I@&9eD?Eg}d zmOt!MROPwce_wL`({8W)|4T_3`O_5e^f>O8f4#QVYUcgFTg{dKXDO-peU-MOBf}^b zi|p6Vo5N#vczoD{AFof0B0CMdD`9iFU0_q+&>4rVYQXI>ZL7B#q>|%VrqdrtRtjJ{ zt2lj(90IH)C(`kTkYSEtPnvk-!$8Mhzq|job8vpt*iqH`bS;#K#7_2t z2|C{RjS5Ul#r{U^E4REZjGd~cnVx+}v{~7$uSb0S zi>;M_hP8y3NKwv-gPhdWU8sJ3bolPDmudl8%M}Y9F$NAnJ^U#_Llrs{=Ln_{RgEAK zcv8^5cG#`6PXrXRNbR-CRwIu;lwt81S7G4FZTyVm2M|ZDs*x$#1?R5TdKivWqn@g9 zZKA6*0A5UD53a7%NL8}D(6O28DFBv$n&%m#yp)G5_Jtv5;VZx4)>MV}TP{xAv!P_TeH#OhChgJ4Eq`zNQpE^GX}2w}tctQEeG8YhM^|9ePhVs&(37?69_ zC`@rFmcf%k)A;#^I>N?|)2GDc=rRyaqn5*kILaMtPlws!=U>6bOY;BXl0pa79%)=Ii~4Y{a2 zwOKyCj_eGu5-f;5W-!s)|MvVeK3B+d_>OL9cRs_$3l%L*d_-oA$n%s5lOjxlGN$f~ zvKY>b2thss_j&iM{&?h}KMYKpXPI;2I>O~FDvPuj$4RJYVktcIm|}raYJiDOh8B9( z2cY?r7-?EVr)M;%c(X=_4qk+ z5IIzP5X&16VR>xsfrR%am~T9eyMPfxRN%Rc%dcZHd{vXjO{_og5|2+|L_|>U?up-eq#0iU^dN6lv5rmR<9)! z6Qrq)gU>L}z|ZL*E7+dPsXHB5N=?)V7fJ$;M8JA%u=upl(Uv5~Z=>)q=Hdcl4s-Jz zpUdY&#R~=QNS?}S8oE1Cb~1H9CX5Hml!&9g1~YIp>eitejKsdCvp<$Ywnj57_PT`2 zvNdRd_`whrQqwVfi@^P&!4(R%+xj{V>pmD9f>dKWJ6O7qIGkizVbxOJJG5nv}$AW*{bQGGfGu@fNsm@v{ChU!+(vm1tIaUm@Qjwdjq6 zjEy^Y6>J{CZde|y9AL>0TQG}j1LBr#AuprJ0EWI9OsM_XoG@Dq24Fh}fj6eg!Yz-1 ze%L;Mq1s>;8_#xT`PB(;8Xwi&6kJNK2LSnVR)5a~ca#=*FUR%vq>ayo^H(|td zv6V)WTAM9GK|_#RBM-=x=8$jexrlwDL3@i@e;e7$+c^X7RnLyv{3#} zcou*Hz9as#w%Kmyz#fylu!=eea|mgR|f;Wq|yy4UN`Ji z5Mg(0I*wj4;ogq<9_*+w^b;3Bd@vA}fK^)BlkR(glDn^JRb}~xk;2=(2XXglFt=LG z4C>b*#>Cx;8Fs)=NWiPwMoh!sE%hW-GVbH&!SQ(e->BEOR`!1xsWN+f@Z>n|v;Xbd zX8!(1y}dgBT}mplV^6_mF0oZ*Z=i;-u`c4{MX9jgD5g0>pA z5gj#^Z8oEoIIY;_Y1Q}$icZ=KLFkr!3dy;z-3~Pv2>gYIxkLky;7OIx;9hx`yc}2+ zJ8~mOIP)j(DF~mxp(XvJQY94?^ISL{Z~yD<`s)7oQc_y}(iOhXmHY8;dC5KDP=wsq?rDKA z@0XRIe)*#U8nphhTKRFkw1cjf{U~xGax9&`J!Kj^p7l#5W8ac*(zb&MWvF1%*CBgz zDcWt-S_Jyn2{zLHDvScxNT!Wpe*&7FC7vkNzMk#aZ-pV`DZiB-7)n@|TveNmx`9F0 zrKFp)@OF$OD=^0lWB&UX{-0_F1jm(xYXkS`Co*ft5K+W_RDs6dGg`Cs_#cylPL{cg zp0}s-1U-KJ*J`ice_BpT%Rju9vD(U~#Bq=P$JhR5&i_~Uzm}7Xl+YRb*Lmmc_kOo` zc6j_Iy6aT>Gvr`Jr3%0x?_{f=b)Z4F*MHaPy*)Y5)dLOPN1Aw{!Me=d6EvcG5f9KRKfMdPR(3aLThhX8}X;z~G(c zPzjGG#(B=ru{6u163$?FwX5%Xs!vY1S?;_$>2>;h2M1>dVyO1%$yqO7 z8xOP>bT(Z(?(D+a6akm3jnn#S`M#`FnR_elX>r_R{~P$nK63t_HvavOmHmJIgpWW? z-Say}bU2&5SZ0O_maBNZBzt8sS*YHzfc!C9y&C)qOr*qfg$M!UyIkMkWLxc5J9zDe zUZv`rmc?N|;JG_^&jM{4G=pNgBJ`^%g@s4Oc=9YM*C^nvEV}c7Z3@cr!2tT99Hqb0 zIfc%+VBw{~)sV71>5xGgAVJ%TcijX+n0=-I&&7CQ5$>5-*k^s1hvIG#)g+#K&r zIn?bQ&G1J$)A>fS-ck3eu76hI-wnJ*a1cas_W$+^?D8A7UD4mg-Au5cQeuJ8`Q&M;qya*wwigMNcKXYUwQyJZ~s4i8sLdM0FU4Q zZ?59Mmz2`-&p580&;xMa{(oyT@BeLWuJ-@SNjY!j13Un^1`qK8?1z6DFPCr1d zO?Ut7@U)lRVa{_`=fLQ<%q{2`;(yt4J91in;jM#ziO`#jasZgpZKWBCM=0 z2I2`_yqlQi!@=QMXCLI>+v}Z^bQ`tW9JfkkW})}gv;UX*|5x!J%Sm_1KiYbJHI94c z|JLSaYv%p`*2@30lvKpG<}vtRj_7@p`Emc}XGbsS^?EO`^&R+OU`n5vOnQ#6S?EGG zFw(_K*Z|NQu;bY`jiRSl(qN*;TwI5na-^SV!P`_*0F~&edx_h|>+4GFq8wKPF1--U zprq}jeowvnxZ29|g(a&hR9+xVhaRN?YWu!W1Ji-;X>hn_wfTiGUD~t~b=3nhzFsit zsvxvf7;t*K|IlS)+1$9ElIQo6#Z86k-+`cZ=HRvVAo0UGcIY6{p! zr~eLsaHW8Kx;J3CVau*Z__mEu8WFCOgd1|?_63IQgua~)>WN-Uqlg~5&cV&G{tE=X zDQyG@J%Mc__-o}UEtquu#x_xqoj2%H(_ct86K|dX_8L^x`U^vb)2qx z=m+&ME*YOE()O^7pSZqTBCE*_51T9fibh-p(27R#>R|kr6teGm6^-ehKi=`bs>L^2 zBB$EUwCKb3_Q&lx<*|z|_f{A=exjzWRudz&WIxvLP#w z3eaf0?pV<;)I~uQI9e{kp-hjKt*vIW*_gib1gaCFtBz5CSL8^<7yMj_FM@$p;TC?# z^zs2%+M8RiVl4Tvwui*C6&@VWrg6m1viX6NC@q{dSmsacX&LU>b`tavKM;cAiSIjs zCPpty!a@+;a!Hs7LP1vQX{#oKfM0!{J%R;7s$D%fNv2N_zWAS=ti18}{uRjI+h z`u0C+xIQlwHA8IfPMG$NBQHRr(HG+525O0Rk;1ebZyp&c8#aa>!|;*X8j@nXKtDa7 z;bHX;0IXT45ju`0fjqofPjb%IL;oW4hx4luuOciHr#_n4OwuPadUOZrqq&5Pc7D#P z>c8SM89Tz&@nHr%o0FRl$wcT|fr?Cc%8fd;sXNJ+$cpY@)y!Z>k**7~<1|}5Gx&6q z%vdVkspVhgp?%(zS^qzW^Y6R+{eShX{QAFDTg87bCmHhp+Pk(L$BiSJSMFEvAqX5K z9P2JI8w6-dq`kf_c5Nf;7lR{lB+iH;ElMNJ=I2wVn;KTPni{XM7~3!lkat|Cy4hXT zcOH@-c$R0gZ#*k2Kj>t!{Gc;J&HU+8IIDH@5nQPqC4Tl;ze>7>#VPmn6Kh(nb7oL+N#U!!GDo7`rDO`b}I= zg_8X?S4w{^k&f49X)HEEaekW@)WWYV{qq{?7S29Z6Fw913lI>EqdQsy^1cd4wX^rFYC zf`??CZ}(*>4e?zGcds%GI`Stgx=5B)EvcBUP_>-LMY^M{)w^#MLiH3vL+T>D3#;U) zX|KWPl`^5ail`}{S5-c~{K05LOP*~mk4Y+wJRyb+8AxAzrtIL0u4ZTP#`jgG5gDrs zelq>L(oM-jQOF~{SNg8&h8?DlmAXgjE>mACHF-1|G4-xgdh%z;1G-RZYOUdrUo=sA z@@&M-ZQj^aku6|HXpOMoe9*eC~!|6O0%I7ok zdBnTNPN~V~5qF}B^Nd^`^2ohcPMpE#JePZ=hR=EZWm}Z(R>I`^hmwehvbr|;KH}a~tz$;Mk9^I1UL_#+M?C+Np8Oy2*wPrgV7-hQIMQkZ!S@R3%C5l? zp@5$38(N1`KwmnO1K+(>$Ut@kj?5G=lwCo)e5jC-j3Y-P1&n29Uc~_w6B=dEt+Z$#sJ28s3H?$}GOZGY zvZr(GghaC2a-{A&zt1DIKeSv~dFLPZ8c(eM$HURt`~OD6_We)KA}v;W0q#7N>M;BavXGY`_<1!i+Op-Y29FvMLs__FBVPhi4coTp z^LI_xAmrQ}?G^u@TxB#=?YDLCv;KZ!x2HCh9OsGAPL6BKxK)`W+WGY@o=7`MlJ6SL zB|WRiQ`WDGqQSfxFXnn-pt0L8^L)8ZJZdew)zw|LRt^5{G_X8j{$6EIf1H~iz43eR z^qRtiw~DdVdY;eo*b=Atsmtr;ya#xTdT65dzesIerb=?VSr?wXEB+`@+3d6UE-682 zFkZ%~$#S?hupzZ`Pm^KAIn_?k{)yo165!!ewe{%SUfFw~xHg z@9u0Vj>C@c&11zcVgm9*k!0?CYrW91NH??~U7MF9y~P~sFYByutXF1Qg0i@=&mut1 z?ZNf33n7-7gFlk0+ta{}F9W)ZwWV0i$rg#FE`m)K{6fX!`10#nXRuL$vdu`m@E_7)0vh9zNlBOl7ymu zCI4lSU6;QqQ*`Uo+pQ^A{=ag_Kc4EpU!*y09T#asQ&E0P7p}CqdmJ28x+fOt8J?r8 z&GY5uRU+ZsoQBPTeH5v3AH=#j`Ef1(wwiC_s?H#|=AZVL#l_{7#OXgTS(>cqH7tmg z>`L8waLJgwGtkYSawg%~X~(0|{Jc-+cX$xKDNTOQ&1uP)HCRdk&h3vh9N%BCCsa4j z2A9kUHOwzAxwFmEFfYBhY}`1+<&l0jXGsoOW0?pt&E;QBSGZ4K{=QDJ`1#Kv-FW-m zM}GOoU!FYw8IH&2_kV}&^B>P570n-af2)rx#`2*I{TA@Q|Kn1_n855`c(P!TC(`(Z zB%AJV@bEU-f_p59oL|S7NaQ`kb+X$f+w#hJ#lGWEU4nA^Cmu*BCDVJiO|L)QZ)jJ& zNP1d>RHdVJ5zTT}FZdm6hnLbJQ*R>Q7BcBMVQ$VSt=(y^T(1)X$3hD#!Q(sm+v@)Y1V1mBUVcP8wNP_sks8ai%?Z z*sU1_j_5kG?j%&oi+7ou*eigyk+BC!z>82_j#)7JFGpJ`(xlzm2LxJZ3LnCqexCn(C zHx}*{xi~;DgHtdiJ;DjP&{g#>*89@S(#^iC3J~;=>!>N$S7gygfJZ#QoCo3r1B9+? z$19G96AV#^p)$(S`H30f1c+!*kXs96Rlhf6az1NZ*CtXq5r!5f8to>wh49 zANszoryemKKyNhJ8R>`64~r!EO(B>e1i}cx z8`2{L!U(}z(jx@I2*G!xM+k%w0vZ8l2!Sv{5Ro1s5Jm{TBu~!}0%3&UJ6std5Jm{5 zNP!p#BL;5)APB-Y0htDI5Jnv88@|pA@eoEl-jN>h5Jo)eo7~O}aS=vbsA0_z7h%NZ z9sGm92qQ4>1Vtu9WKP4?iIe1nh)bY#@N3c{DnXBH?@5c81bwch(I!Mh0)r8@?|uFT5~bJP7{rx)?7S?H3s;J=3^{xA|GmB zS4(@0?-R|>teHmvP|e>Dq@6;m`I|NIXa-dCJ2jLjWQ(FlW}OpZ4wz_;e~7UYHTl24 z#r|*5JOBP4soMSDGsug^4PeCuZrb{tHhwR##yxtAZ7kmuAfMyQv!r_Z5qq_GE_Z;g z8zgvSy;KjqCy{ zK_QuCe%CdYmJ%CCY@lIw#xxeo4Q4fK8mq>pyclyD3+o0mgFTHsm}YsIL5*F)$q71( z8ha)8Y_lZMfkX!eYG+epQ_*NxGpezpY0i(as@2-QnOTk9V`D}JyBedoVSAZjjcuY^ z?-QjSENhn08PggI8x5=3)>z$WP|di;H0c`iq{O7}1HJv#%xkP`^n%pvYfRG`Rx_}% zbkd-jg^g)kgBTMVDEaZu}adgnyHO>Tz6y5*2XGI!)nGhmQos2v$nCG(&MS1p@ekj*X(VqM>U8sxUmS< zu$sk<6^&kwGbT5dLV67qq(Y|;Nj0Mz%P~FUn$?Ykl7`jHZY?{Y{%h1Fae0 z*xVZ+V}W;snJ;64V|Q+#H5(jTbOWpz;n=5}`6^I$NivKHe*66Ivxw#WzaRYz5Krv?dz~}>|DfA#`Tx%%4X@3OUVRmw zUfiI+8siteM7Mp5aQhbF_ASDHp0^0M@<$au|I6=6JpcFqI=#`^`@edlaXbG%hp3q2 ze0!C|Ag9Zh{mGI0Cwz$H<%=_m|9Wqdc