diff --git a/Dockerfile b/Dockerfile index 0497854..2383734 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,6 +8,10 @@ FROM harbor.repository.lb.home.dc.internal.amuz.es/infrastructure/alpine-base:3. WORKDIR /usr/local/src ARG VERSION="1.25.4" +ARG MODSEC_VER=3.0.12 +ARG MODSEC_NGX_VER=1.0.3 +ARG OWASP_CRS_VER=4.0.0 + ### Fetch Build Dependencies COPY patch /usr/local/src/patch @@ -31,7 +35,56 @@ RUN set -xeu && \ alpine-sdk \ cmake \ git \ - findutils + findutils \ + pkgconf \ + #mod security + byacc \ + flex \ + libxml2-dev \ + lmdb-dev \ + yajl-dev \ + zlib-dev \ + libfuzzy2-dev + +RUN set -xeu && \ + mkdir -p /usr/local/src/modsecurity && \ + wget -qO - "https://github.com/owasp-modsecurity/ModSecurity/releases/download/v${MODSEC_VER}/modsecurity-v${MODSEC_VER}.tar.gz" | tar -zxf - --strip-components=1 -C /usr/local/src/modsecurity && \ + cd /usr/local/src/modsecurity && \ + ./build.sh && \ + ./configure \ + --prefix=/opt/modsecurity \ + # --disable-debug-logs \ + --with-pcre2=yes \ + --with-libxml=yes \ + --with-curl=no \ + --with-ssdeep=yes \ + --with-lmdb=yes \ + --with-yajl=yes \ + --with-lua=no \ + --with-geoip=no \ + --with-maxmind=no \ + --disable-examples \ + --disable-doxygen-doc \ + && \ + CFLAGS="-Wno-error -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" \ + CPPFLAGS="-Wno-error -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" \ + LDLAGS='-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' \ + make -j `nproc` && \ + make install && \ + find /opt/modsecurity && \ + ldd /opt/modsecurity/lib/libmodsecurity.so && \ + rm -fr \ + /opt/modsecurity/lib/libmodsecurity.a && \ + /opt/modsecurity/lib/libmodsecurity.la + +RUN set -xeu && \ + mkdir -p /usr/local/src/modsecurity-nginx && \ + wget -qO - "https://github.com/owasp-modsecurity/ModSecurity-nginx/releases/download/v${MODSEC_NGX_VER}/modsecurity-nginx-v${MODSEC_NGX_VER}.tar.gz" | tar -zxf - --strip-components=1 -C /usr/local/src/modsecurity-nginx + +RUN set -xeu && \ + mkdir -p /opt/owasp-crs && \ + wget -qO - "https://github.com/coreruleset/coreruleset/archive/refs/tags/v${OWASP_CRS_VER}.tar.gz" | tar -zxf - --strip-components=1 -C /opt/owasp-crs && \ + mv -v /opt/owasp-crs/crs-setup.conf.example /opt/owasp-crs/crs-setup.conf RUN set -xeu && \ @@ -54,14 +107,15 @@ RUN set -xeu && \ 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 && \ + wget -qO - "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 \ + + MODSECURITY_LIB="/opt/modsecurity/lib" \ + MODSECURITY_INC="/opt/modsecurity/include" \ ./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" \ @@ -69,6 +123,8 @@ RUN set -xeu && \ --prefix=/opt/nginx \ --add-module=/usr/local/src/headers-more-nginx-module \ --add-module=/usr/local/src/ngx_brotli \ + --add-dynamic-module=/usr/local/src/modsecurity-nginx \ + --modules-path=modules \ --conf-path=etc/nginx.conf \ --pid-path=tmp/nginx.pid \ --lock-path=tmp/nginx.lock \ @@ -80,6 +136,7 @@ RUN set -xeu && \ --with-http_v2_module \ --with-http_v3_module \ --with-http_realip_module \ + --with-http_gunzip_module \ --with-http_gzip_static_module \ --without-http_ssi_module \ --without-http_userid_module \ @@ -94,7 +151,7 @@ RUN set -xeu && \ --without-mail_smtp_module \ --without-mail_imap_module \ --without-mail_pop3_module \ - --with-stream \ + --with-stream=dynamic \ --with-stream_ssl_module \ --with-stream_realip_module \ --with-stream_ssl_preread_module \ @@ -104,6 +161,7 @@ RUN set -xeu && \ make -j `nproc` && \ make install && \ ldd /opt/nginx/sbin/nginx && \ + # find /opt/nginx/modules -type f -name '*.so' -print0 |awk -F'\0' '{printf("echo \"library %s\";ldd \"%s\"\n",$0,$0)}' |sh && \ find /opt/nginx && \ cat /opt/nginx/etc/nginx.conf && \ rm -r /opt/nginx/html/ && \ @@ -126,6 +184,12 @@ RUN set -xeu && \ ldd sbin/nginx && \ strip -X -x -s -v sbin/nginx +RUN set -xeu && \ + mkdir /opt/modsecurity/etc && \ + mkdir -p /opt/nginx/logs/audit && \ + cp -af /usr/local/src/modsecurity/modsecurity.conf-recommended /opt/modsecurity/etc/modsecurity.conf && \ + cp -af /usr/local/src/modsecurity/unicode.mapping /opt/modsecurity/etc/unicode.mapping + ## ## PKG ## @@ -139,6 +203,13 @@ RUN set -xeu && \ pcre2 \ zlib \ musl-utils \ + #modsecurity + xz-libs \ + libxml2 \ + libfuzzy2 \ + lmdb \ + yajl \ + libstdc++ \ && \ rm -f \ pkgs/busybox-*.apk \ @@ -155,7 +226,11 @@ RUN set -xeu && \ FROM harbor.repository.lb.home.dc.internal.amuz.es/infrastructure/minimal-toolbox:3.19-latest COPY --from=build /opt/nginx /opt/nginx +COPY --from=build /opt/owasp-crs /opt/owasp-crs +COPY --from=build /opt/modsecurity /opt/modsecurity COPY --from=pkg output /pkg +COPY --from=build /opt/nginx /opt/nginx +COPY modsecurity.d /opt/modsecurity/etc RUN set -xeu && \ @@ -181,8 +256,8 @@ RUN set -xeu && \ chown -R 1000:1000 \ /opt/nginx/etc \ /opt/nginx/tmp \ - /opt/nginx/logs - + /opt/nginx/logs && \ + ln -sf ./conf.d/core-extra /opt/nginx/etc/nginx.extra.conf WORKDIR /opt/nginx @@ -199,4 +274,4 @@ 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 +CMD ["nginx", "-g", "daemon off;"] \ No newline at end of file diff --git a/docker-entrypoint.d/10-listen-on-ipv6-by-default.sh b/docker-entrypoint.d/10-listen-on-ipv6-by-default.sh old mode 100644 new mode 100755 diff --git a/docker-entrypoint.d/40-make-sure-directories.sh b/docker-entrypoint.d/40-make-sure-directories.sh index 378bb88..29b317f 100755 --- a/docker-entrypoint.d/40-make-sure-directories.sh +++ b/docker-entrypoint.d/40-make-sure-directories.sh @@ -10,14 +10,21 @@ 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 modsec_temp_data_dir="${NGINX_RENVSUBST_PROXY_TEMP_DIR:-${temp_dir}/modsecurity/data}" + local modsec_temp_dir="${NGINX_RENVSUBST_PROXY_TEMP_DIR:-${temp_dir}/modsecurity/tmp}" + local modsec_temp_upload_dir="${NGINX_RENVSUBST_PROXY_TEMP_DIR:-${temp_dir}/modsecurity/upload}" 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}" + [ -d "$client_body_temp_dir" ] || mkdir -p "${client_body_temp_dir}" + [ -d "$proxy_temp_dir" ] || mkdir -p "${proxy_temp_dir}" + [ -d "$modsec_temp_data_dir" ] || mkdir -p "${modsec_temp_data_dir}" + [ -d "$modsec_temp_dir" ] || mkdir -p "${modsec_temp_dir}" + [ -d "$modsec_temp_upload_dir" ] || mkdir -p "${modsec_temp_upload_dir}" } make_tmp_directories diff --git a/docker-entrypoint.d/50-apply-extra-conf.sh b/docker-entrypoint.d/50-apply-extra-conf.sh new file mode 100755 index 0000000..eddd771 --- /dev/null +++ b/docker-entrypoint.d/50-apply-extra-conf.sh @@ -0,0 +1,25 @@ +#!/bin/sh + +set -e + +ME=$(basename "$0") + +entrypoint_log() { + if [ -z "${NGINX_ENTRYPOINT_QUIET_LOGS:-}" ]; then + echo "$@" + fi +} + +apply_extra_config() { + + if [ ! -f "/opt/nginx/etc/nginx.extra.conf" ]; then + return 0 + fi + + entrypoint_log "Info: extra config found" + sed -i.old '1s;^;include nginx.extra.conf\;\n;' /opt/nginx/etc/nginx.conf +} + +apply_extra_config + +exit 0 diff --git a/docker-entrypoint.d/91-update-resolver.sh b/docker-entrypoint.d/91-update-resolver.sh new file mode 100755 index 0000000..ff04ce4 --- /dev/null +++ b/docker-entrypoint.d/91-update-resolver.sh @@ -0,0 +1,11 @@ +#!/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 + +DNS_SERVER="${DNS_SERVER:-$(grep -i '^nameserver' /etc/resolv.conf | head -n1 | cut -d ' ' -f2)}" + +sed -i.bak -r 's/__DNS_SERVER__/'"${DNS_SERVER}"'/' /opt/nginx/etc/nginx.conf \ No newline at end of file diff --git a/modsecurity.d/modsecurity-override.conf b/modsecurity.d/modsecurity-override.conf new file mode 100644 index 0000000..3fadbd4 --- /dev/null +++ b/modsecurity.d/modsecurity-override.conf @@ -0,0 +1,35 @@ +# Original of the latest recommended version: +# https://github.com/owasp-modsecurity/ModSecurity/blob/v3/master/modsecurity.conf-recommended + +SecArgumentSeparator & +SecAuditEngine RelevantOnly +SecAuditLog /dev/stdout +SecAuditLogFormat JSON +SecAuditLogParts ABIJDEFHZ +SecAuditLogRelevantStatus "^(?:5|4(?!04))" +SecAuditLogStorageDir /opt/nginx/logs/audit +SecAuditLogType Concurrent +SecCookieFormat 0 +SecDataDir /opt/nginx/tmp/modsecurity/data +SecDebugLog /dev/null +SecDebugLogLevel 0 +# Comment out the SecDisableBackendCompression option since it is not supported in V3 +#SecDisableBackendCompression ${MODSEC_DISABLE_BACKEND_COMPRESSION} +SecPcreMatchLimit 100000 +SecPcreMatchLimitRecursion 100000 +SecRequestBodyAccess on +SecRequestBodyLimit 13107200 +SecRequestBodyJsonDepthLimit 512 +SecRequestBodyLimitAction Reject +SecRequestBodyNoFilesLimit 131072 +SecResponseBodyAccess on +SecResponseBodyLimit 1048576 +SecResponseBodyLimitAction ProcessPartial +SecResponseBodyMimeType text/plain text/html text/xml +SecRuleEngine on +SecStatusEngine off +SecTmpDir /opt/nginx/tmp/modsecurity/tmp +SecTmpSaveUploadedFiles on +SecUnicodeMapFile unicode.mapping 20127 +SecUploadDir /opt/nginx/tmp/modsecurity/upload +SecUploadFileMode 0644 \ No newline at end of file diff --git a/modsecurity.d/setup.conf b/modsecurity.d/setup.conf new file mode 100644 index 0000000..6bdaf15 --- /dev/null +++ b/modsecurity.d/setup.conf @@ -0,0 +1,18 @@ +# Note: the plugin rules will be uncommented when the container starts, +# depending on whether the respective files exist. This works around +# the issue that ModSecurity doesn't support optional includes on NGiNX. + +# Allow custom rules to be specified in: +# /opt/modsecurity/rules/{before,after}-crs/*.conf + +Include /opt/modsecurity/etc/modsecurity.conf +Include /opt/modsecurity/etc/modsecurity-override.conf + +Include /opt/owasp-crs/crs-setup.conf + +Include /opt/owasp-crs/plugins/*-config.conf +Include /opt/owasp-crs/plugins/*-before.conf + +Include /opt/owasp-crs/rules/*.conf + +Include /opt/owasp-crs/plugins/*-after.conf \ No newline at end of file diff --git a/nginx.conf b/nginx.conf index 82aa25c..4921cab 100644 --- a/nginx.conf +++ b/nginx.conf @@ -1,4 +1,6 @@ +#extra config End #user me; +#quic_bpf on; worker_processes 1; error_log /opt/nginx/logs/error.log warn; @@ -10,7 +12,7 @@ timer_resolution 100ms; events { use epoll; worker_aio_requests 128; - worker_connections 1024; + worker_connections 5120; multi_accept on; } @@ -21,7 +23,10 @@ http { client_body_temp_path /opt/nginx/tmp/client_temp; proxy_temp_path /opt/nginx/tmp/proxy; - + proxy_ssl_trusted_certificate /etc/ssl/certs/ca-certificates.crt; + proxy_ssl_server_name on; + proxy_ssl_verify on; + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; @@ -33,7 +38,8 @@ http { aio_write on; tcp_nopush on; tcp_nodelay on; - + resolver __DNS_SERVER__ valid=5s; + open_file_cache max=1000 inactive=20s; open_file_cache_valid 30s; open_file_cache_min_uses 2; @@ -58,14 +64,15 @@ http { 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"; + more_set_headers 'X-Frame-Options: SAMEORIGIN'; + more_set_headers 'X-Content-Type-Options: nosniff'; + more_set_headers '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_protocols TLSv1.3; + ssl_ecdh_curve X25519:P-521:P-384; + ssl_stapling on; # Requires nginx >= 1.3.7 + ssl_trusted_certificate /etc/ssl/certs/ca-certificates.crt; + ssl_stapling_verify on; # Requires nginx => 1.3.7 ssl_prefer_server_ciphers on; ssl_session_cache builtin:1000 shared:SSL:50m; ssl_session_timeout 1d; @@ -92,7 +99,7 @@ http { application/pdf image/bmp image/x-icon image/webp image/tiff audio/wav; - + brotli_static on; brotli on; brotli_types diff --git a/nginx.vh.no-default.conf b/nginx.vh.no-default.conf index bd2cabd..a4ce503 100644 --- a/nginx.vh.no-default.conf +++ b/nginx.vh.no-default.conf @@ -18,7 +18,6 @@ server { http3 on; quic_gso on; quic_retry on; - http2 on; server_name _ ""; ssl_reject_handshake on; }