├── Dockerfile ├── LICENSE ├── README.md ├── hooks └── build └── src ├── cron.sh ├── docker-entrypoint.sh ├── etc └── nginx │ ├── conf.d │ ├── 00-local.conf │ └── default.conf │ ├── nginx.conf │ └── snippets │ ├── brotli.conf │ ├── letsencrypt.conf │ ├── log.conf │ ├── policy_headers.conf │ ├── proxy_headers.conf │ ├── proxy_headers_base.conf │ ├── proxy_headers_ws.conf │ ├── resolver.conf │ └── ssl.conf └── usr └── local └── sbin └── certbot.default.sh /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM nginx:alpine 2 | 3 | MAINTAINER Vladimir Che 4 | 5 | # nginx:alpine contains NGINX_VERSION environment variable, like so: 6 | # ENV NGINX_VERSION 1.21.3 7 | 8 | # MODSECURITY version 9 | ENV VERSION=${NGINX_VERSION} \ 10 | MODSECURITY_VERSION=3.0.4 \ 11 | OWASPCRS_VERSION=3.3.0 12 | 13 | # Build-time metadata as defined at http://label-schema.org 14 | ARG BUILD_DATE 15 | ARG VCS_REF 16 | ARG VERSION 17 | ARG MODSECURITY_VERSION 18 | ARG OWASPCRS_VERSION 19 | LABEL maintainer="Vladimir Che " \ 20 | org.label-schema.build-date=$BUILD_DATE \ 21 | org.label-schema.name="NGINX with ModSecurity, Brotli, Certbot and lua support" \ 22 | org.label-schema.description="Provides nginx ${NGINX_VERSION} with ModSecurity v${MODSECURITY_VERSION} (OWASP ModSecurity CRS ${OWASPCRS_VERSION}) and lua support for certbot --nginx. Using Python for Let's Encrypt Certbot" \ 23 | org.label-schema.url="https://ncube.cloud/about/opensource" \ 24 | org.label-schema.vcs-ref=$VCS_REF \ 25 | org.label-schema.vcs-url="https://github.com/vlche/docker-nginx-waf" \ 26 | org.label-schema.vendor="Vladimir Che" \ 27 | org.label-schema.version=v$VERSION \ 28 | org.label-schema.schema-version="1.0" 29 | 30 | # For latest build deps, see https://github.com/nginxinc/docker-nginx/blob/master/mainline/alpine/Dockerfile 31 | RUN export WORKING_DIR="/src" && \ 32 | apk add --no-cache --virtual .build-deps \ 33 | gcc \ 34 | libc-dev \ 35 | make \ 36 | openssl-dev \ 37 | pcre-dev \ 38 | zlib-dev \ 39 | linux-headers \ 40 | libxslt-dev \ 41 | gd-dev \ 42 | geoip-dev \ 43 | perl-dev \ 44 | libedit-dev \ 45 | mercurial \ 46 | bash \ 47 | alpine-sdk \ 48 | findutils \ 49 | # modsecurity dependencies 50 | autoconf \ 51 | automake \ 52 | curl-dev \ 53 | libmaxminddb-dev \ 54 | libtool \ 55 | lmdb-dev \ 56 | yajl-dev && \ 57 | # 58 | echo "Downloading sources..." && \ 59 | mkdir ${WORKING_DIR} && cd ${WORKING_DIR} && \ 60 | git clone --depth 1 -b v${MODSECURITY_VERSION} --single-branch https://github.com/SpiderLabs/ModSecurity && \ 61 | git clone --depth 1 https://github.com/SpiderLabs/ModSecurity-nginx.git && \ 62 | git clone --recursive https://github.com/google/ngx_brotli.git && \ 63 | wget -qO modsecurity.conf https://raw.githubusercontent.com/SpiderLabs/ModSecurity/v${MODSECURITY_VERSION}/modsecurity.conf-recommended && \ 64 | wget -qO unicode.mapping https://raw.githubusercontent.com/SpiderLabs/ModSecurity/49495f1925a14f74f93cb0ef01172e5abc3e4c55/unicode.mapping && \ 65 | wget -qO - https://github.com/coreruleset/coreruleset/archive/v${OWASPCRS_VERSION}.tar.gz | tar xzf - -C ${WORKING_DIR} && \ 66 | wget -qO - https://nginx.org/download/nginx-${NGINX_VERSION}.tar.gz | tar xzf - -C ${WORKING_DIR} && \ 67 | # 68 | echo "building modsecurity..." && \ 69 | cd ModSecurity && \ 70 | git submodule init && git submodule update && \ 71 | ./build.sh && ./configure && make -j$(nproc) && make install && \ 72 | # 73 | echo "build nginx modules..." && \ 74 | cd ${WORKING_DIR} && \ 75 | CONFARGS=$(nginx -V 2>&1 | sed -n -e 's/^.*arguments: //p'| sed -e "s/--with-cc-opt='.*'//g") && \ 76 | MODSECURITYDIR="$(pwd)/ModSecurity-nginx" && \ 77 | cd ./nginx-$NGINX_VERSION && \ 78 | ./configure --with-compat $CONFARGS \ 79 | --with-cc-opt='-Os -fomit-frame-pointer' \ 80 | --add-dynamic-module=$MODSECURITYDIR \ 81 | --add-dynamic-module=${WORKING_DIR}/ngx_brotli && \ 82 | make modules && \ 83 | strip objs/*.so && \ 84 | mkdir -p /usr/lib/nginx/modules && \ 85 | cp objs/*.so /usr/lib/nginx/modules && \ 86 | # 87 | echo "configuring modsecurity rules..." && \ 88 | mkdir -p /etc/nginx/modsec/conf.d && \ 89 | echo "# Example placeholder" > /etc/nginx/modsec/conf.d/example.conf && \ 90 | # 91 | echo "# Include the recommended configuration" >> /etc/nginx/modsec/main.conf && \ 92 | echo "Include /etc/nginx/modsec/modsecurity.conf" >> /etc/nginx/modsec/main.conf && \ 93 | echo "# User generated" >> /etc/nginx/modsec/main.conf && \ 94 | echo "Include /etc/nginx/modsec/conf.d/*.conf" >> /etc/nginx/modsec/main.conf && \ 95 | echo "" >> /etc/nginx/modsec/main.conf && \ 96 | echo "# OWASP CRS v${MODSECURITY} rules" >> /etc/nginx/modsec/main.conf && \ 97 | echo "Include /usr/local/coreruleset-${OWASPCRS_VERSION}/crs-setup.conf" >> /etc/nginx/modsec/main.conf && \ 98 | echo "Include /usr/local/coreruleset-${OWASPCRS_VERSION}/rules/*.conf" >> /etc/nginx/modsec/main.conf && \ 99 | # 100 | echo "# For inclusion and centralized control" >> /etc/nginx/modsec/modsec_on.conf && \ 101 | echo "modsecurity on;" >> /etc/nginx/modsec/modsec_on.conf && \ 102 | # 103 | echo "# For inclusion and centralized control" >> /etc/nginx/modsec/modsec_rules.conf && \ 104 | echo "modsecurity_rules_file /etc/nginx/modsec/modsec_includes.conf;" >> /etc/nginx/modsec/modsec_rules.conf && \ 105 | # 106 | echo "# For inclusion and centralized control" >> /etc/nginx/modsec/modsec_includes.conf && \ 107 | echo "include /etc/nginx/modsec/modsecurity.conf" >> /etc/nginx/modsec/modsec_includes.conf && \ 108 | echo "include /usr/local/coreruleset-${OWASPCRS_VERSION}/crs-setup.conf" >> /etc/nginx/modsec/modsec_includes.conf && \ 109 | echo "include /usr/local/coreruleset-${OWASPCRS_VERSION}/rules/REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf" >> /etc/nginx/modsec/modsec_includes.conf && \ 110 | echo "include /usr/local/coreruleset-${OWASPCRS_VERSION}/rules/REQUEST-901-INITIALIZATION.conf" >> /etc/nginx/modsec/modsec_includes.conf && \ 111 | echo "include /usr/local/coreruleset-${OWASPCRS_VERSION}/rules/REQUEST-905-COMMON-EXCEPTIONS.conf" >> /etc/nginx/modsec/modsec_includes.conf && \ 112 | echo "include /usr/local/coreruleset-${OWASPCRS_VERSION}/rules/REQUEST-910-IP-REPUTATION.conf" >> /etc/nginx/modsec/modsec_includes.conf && \ 113 | echo "include /usr/local/coreruleset-${OWASPCRS_VERSION}/rules/REQUEST-911-METHOD-ENFORCEMENT.conf" >> /etc/nginx/modsec/modsec_includes.conf && \ 114 | echo "include /usr/local/coreruleset-${OWASPCRS_VERSION}/rules/REQUEST-912-DOS-PROTECTION.conf" >> /etc/nginx/modsec/modsec_includes.conf && \ 115 | echo "include /usr/local/coreruleset-${OWASPCRS_VERSION}/rules/REQUEST-913-SCANNER-DETECTION.conf" >> /etc/nginx/modsec/modsec_includes.conf && \ 116 | echo "include /usr/local/coreruleset-${OWASPCRS_VERSION}/rules/REQUEST-920-PROTOCOL-ENFORCEMENT.conf" >> /etc/nginx/modsec/modsec_includes.conf && \ 117 | echo "include /usr/local/coreruleset-${OWASPCRS_VERSION}/rules/REQUEST-921-PROTOCOL-ATTACK.conf" >> /etc/nginx/modsec/modsec_includes.conf && \ 118 | echo "include /usr/local/coreruleset-${OWASPCRS_VERSION}/rules/REQUEST-930-APPLICATION-ATTACK-LFI.conf" >> /etc/nginx/modsec/modsec_includes.conf && \ 119 | echo "include /usr/local/coreruleset-${OWASPCRS_VERSION}/rules/REQUEST-931-APPLICATION-ATTACK-RFI.conf" >> /etc/nginx/modsec/modsec_includes.conf && \ 120 | echo "include /usr/local/coreruleset-${OWASPCRS_VERSION}/rules/REQUEST-932-APPLICATION-ATTACK-RCE.conf" >> /etc/nginx/modsec/modsec_includes.conf && \ 121 | echo "include /usr/local/coreruleset-${OWASPCRS_VERSION}/rules/REQUEST-933-APPLICATION-ATTACK-PHP.conf" >> /etc/nginx/modsec/modsec_includes.conf && \ 122 | echo "include /usr/local/coreruleset-${OWASPCRS_VERSION}/rules/REQUEST-934-APPLICATION-ATTACK-NODEJS.conf" >> /etc/nginx/modsec/modsec_includes.conf && \ 123 | echo "include /usr/local/coreruleset-${OWASPCRS_VERSION}/rules/REQUEST-941-APPLICATION-ATTACK-XSS.conf" >> /etc/nginx/modsec/modsec_includes.conf && \ 124 | echo "include /usr/local/coreruleset-${OWASPCRS_VERSION}/rules/REQUEST-942-APPLICATION-ATTACK-SQLI.conf" >> /etc/nginx/modsec/modsec_includes.conf && \ 125 | echo "include /usr/local/coreruleset-${OWASPCRS_VERSION}/rules/REQUEST-943-APPLICATION-ATTACK-SESSION-FIXATION.conf" >> /etc/nginx/modsec/modsec_includes.conf && \ 126 | echo "include /usr/local/coreruleset-${OWASPCRS_VERSION}/rules/REQUEST-944-APPLICATION-ATTACK-JAVA.conf" >> /etc/nginx/modsec/modsec_includes.conf && \ 127 | echo "include /usr/local/coreruleset-${OWASPCRS_VERSION}/rules/REQUEST-949-BLOCKING-EVALUATION.conf" >> /etc/nginx/modsec/modsec_includes.conf && \ 128 | echo "include /usr/local/coreruleset-${OWASPCRS_VERSION}/rules/RESPONSE-950-DATA-LEAKAGES.conf" >> /etc/nginx/modsec/modsec_includes.conf && \ 129 | echo "include /usr/local/coreruleset-${OWASPCRS_VERSION}/rules/RESPONSE-951-DATA-LEAKAGES-SQL.conf" >> /etc/nginx/modsec/modsec_includes.conf && \ 130 | echo "include /usr/local/coreruleset-${OWASPCRS_VERSION}/rules/RESPONSE-952-DATA-LEAKAGES-JAVA.conf" >> /etc/nginx/modsec/modsec_includes.conf && \ 131 | echo "include /usr/local/coreruleset-${OWASPCRS_VERSION}/rules/RESPONSE-953-DATA-LEAKAGES-PHP.conf" >> /etc/nginx/modsec/modsec_includes.conf && \ 132 | echo "include /usr/local/coreruleset-${OWASPCRS_VERSION}/rules/RESPONSE-954-DATA-LEAKAGES-IIS.conf" >> /etc/nginx/modsec/modsec_includes.conf && \ 133 | echo "include /usr/local/coreruleset-${OWASPCRS_VERSION}/rules/RESPONSE-959-BLOCKING-EVALUATION.conf" >> /etc/nginx/modsec/modsec_includes.conf && \ 134 | echo "include /usr/local/coreruleset-${OWASPCRS_VERSION}/rules/RESPONSE-980-CORRELATION.conf" >> /etc/nginx/modsec/modsec_includes.conf && \ 135 | echo "include /usr/local/coreruleset-${OWASPCRS_VERSION}/rules/RESPONSE-999-EXCLUSION-RULES-AFTER-CRS.conf" >> /etc/nginx/modsec/modsec_includes.conf && \ 136 | # 137 | mv ${WORKING_DIR}/unicode.mapping /etc/nginx/modsec && \ 138 | mv ${WORKING_DIR}/modsecurity.conf /etc/nginx/modsec && \ 139 | sed -i 's/SecRuleEngine DetectionOnly/SecRuleEngine On/g' /etc/nginx/modsec/modsecurity.conf && \ 140 | sed -i 's!SecAuditLog /var/log/modsec_audit.log!SecAuditLog /var/log/nginx/modsec_audit.log!g' /etc/nginx/modsec/modsecurity.conf && \ 141 | mv ${WORKING_DIR}/coreruleset-${OWASPCRS_VERSION} /usr/local/ && \ 142 | mv /usr/local/coreruleset-${OWASPCRS_VERSION}/rules/REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf.example /usr/local/coreruleset-${OWASPCRS_VERSION}/rules/REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf && \ 143 | mv /usr/local/coreruleset-${OWASPCRS_VERSION}/rules/RESPONSE-999-EXCLUSION-RULES-AFTER-CRS.conf.example /usr/local/coreruleset-${OWASPCRS_VERSION}/rules/RESPONSE-999-EXCLUSION-RULES-AFTER-CRS.conf && \ 144 | mv /usr/local/coreruleset-${OWASPCRS_VERSION}/crs-setup.conf.example /usr/local/coreruleset-${OWASPCRS_VERSION}/crs-setup.conf && \ 145 | # 146 | echo "cleaning all after build..." && \ 147 | cd / && \ 148 | # 149 | echo "delete modsecurity archive and strip libmodsecurity (~130mb + ~70mb)" && \ 150 | rm /usr/local/modsecurity/lib/libmodsecurity.a && \ 151 | strip /usr/local/modsecurity/lib/libmodsecurity.so && \ 152 | apk del .build-deps && \ 153 | rm -rf ${WORKING_DIR} && \ 154 | unset WORKING_DIR && \ 155 | rm -f /usr/local/nginx/sbin/nginx && \ 156 | rm /etc/nginx/conf.d/default.conf && \ 157 | # 158 | echo "adding modsecurity dependency, certbot & openssl..." && \ 159 | apk add --no-cache libstdc++ yajl libmaxminddb luajit openssl && \ 160 | # 161 | echo "installing certbot..." && \ 162 | apk add --no-cache py3-pip py3-cffi py3-openssl py3-cryptography && \ 163 | pip3 install --no-cache-dir certbot-nginx && \ 164 | echo -e "#!/usr/bin/env sh\n\nif [ -f "/usr/bin/certbot" ]; then\n /usr/bin/certbot renew\nfi\n" > /etc/periodic/daily/certrenew && \ 165 | chmod 755 /etc/periodic/daily/certrenew && \ 166 | ln -s /etc/letsencrypt/html/.well-known /usr/share/nginx/html/ 167 | 168 | #COPY nginx.conf /etc/nginx/nginx.conf 169 | #COPY default.conf /etc/nginx/conf.d/default.conf 170 | #COPY certbot.default.sh /usr/local/sbin/ 171 | #COPY docker-entrypoint.sh / 172 | COPY src / 173 | ENTRYPOINT ["/docker-entrypoint.sh"] 174 | 175 | #HEALTHCHECK --interval=5m --timeout=5s \ 176 | # CMD wget --output-document=- --quiet --tries=1 http://127.0.0.1/ 177 | EXPOSE 80 443 178 | STOPSIGNAL SIGTERM 179 | CMD ["nginx", "-g", "daemon off;"] 180 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vlche/nginx-waf 2 | Docker alpine based container providing [nginx](https://www.nginx.com) with [modsecurity3](https://www.modsecurity.org), [brotli](https://github.com/google/brotli) compression and certbot for [Let's Encrypt](https://letsencrypt.org)'s SSL certificates auto-renewal. 3 | You can use it as all-in-one service, or as a SSL/Load-Balancer frontend and WAF backend/backends. 4 | Additionally preconfigured options are: 5 | 6 | [SSL/TLS](https://ssl-config.mozilla.org/) 7 | Optimized intermediate ssl settings. General-purpose servers with a variety of clients, recommended for almost all systems by Mozilla. 8 | Certificates are from Lets's encrypt, generated by certbot, auto renewed by separate container, hooks are preconfigured to reload nginx-waf container 9 | 10 | [brotli](https://github.com/google/brotli) 11 | Default preconfigured options for Brotli are: dynamic compression, level 6 12 | 13 | [Security Headers](https://securityheaders.com/) 14 | Preconfigured optimized http headers are included and enabled, but you should certainly know what you are doing! 15 | 16 | [lua](https://www.nginx.com/resources/wiki/modules/lua/) 17 | 18 | [![Docker Automated build](https://img.shields.io/docker/cloud/automated/vlche/nginx-waf.svg?style=for-the-badge)](https://hub.docker.com/r/vlche/nginx-waf/) 19 | [![Docker Build Status](https://img.shields.io/docker/cloud/build/vlche/nginx-waf.svg?style=for-the-badge)](https://hub.docker.com/r/vlche/nginx-waf/) 20 | [![GitHub issues](https://img.shields.io/github/issues/vlche/docker-nginx-waf.svg?style=for-the-badge)](https://github.com/vlche/docker-nginx-waf/issues) 21 | [![GitHub license](https://img.shields.io/github/license/vlche/docker-nginx-waf.svg?style=for-the-badge)](https://github.com/vlche/docker-nginx-waf/blob/master/LICENSE) 22 | [![Docker Pulls](https://img.shields.io/docker/pulls/vlche/nginx-waf.svg?style=for-the-badge)](https://hub.docker.com/r/vlche/nginx-waf/) 23 | [![MicroBadger Size](https://img.shields.io/docker/image-size/vlche/nginx-waf/latest.svg?style=for-the-badge)](https://hub.docker.com/r/vlche/nginx-waf/) 24 | 25 | Launch nginx-waf using the default config: 26 | ``` 27 | docker run --name nginx-waf \ 28 | --restart=always \ 29 | --net=host \ 30 | -e TZ=Europe/Berlin \ 31 | -e WORKERS=5 \ 32 | -v /data/nginx/conf.d:/etc/nginx/conf.d:rw \ 33 | -v /data/letsencrypt:/etc/letsencrypt:rw \ 34 | -v /data/www:/www:rw \ 35 | -p 80:80 -p 443:443 -d \ 36 | vlche/nginx-waf 37 | ``` 38 | - Env variable CRON starts cron embedded. 39 | - Env variable TZ sets your timezone. 40 | - Env variable WAF_INSTANCE points cron notify target to WAF instance 41 | - Env variable WORKERS sets nginx's worker_processes to your value. 42 | 43 | 44 | Certbot's autoupdater can be run as an embedded cron daemon or as a separate cron container. 45 | To run it embedded just set CRON variable: 46 | ``` 47 | docker run --name nginx-waf \ 48 | ... 49 | -e CRON=1 \ 50 | ... 51 | vlche/nginx-waf 52 | ``` 53 | __or__ 54 | To run certbot's cron updater as a separate container set WAF_INSTANCE variable to match your nginx-waf instance: 55 | ``` 56 | docker run --name nginx-waf-cron \ 57 | --restart=always \ 58 | -e TZ=Europe/Berlin \ 59 | -e WAF_INSTANCE=nginx-waf \ 60 | -v /data/nginx/conf.d:/etc/nginx/conf.d:rw \ 61 | -v /data/letsencrypt:/etc/letsencrypt:rw \ 62 | -v /data/www:/www:rw \ 63 | -v /run/docker.sock:/run/docker.sock \ 64 | vlche/nginx-waf 65 | /cron.sh 66 | ``` 67 | 68 | ModSecurity 69 | ----------- 70 | Pre-configured with rules from OWASP CRS on my default. 71 | If you want to disable it for a particular location simply set it to off 72 | ``` 73 | upstream backend { 74 | server 127.0.0.1:9000; 75 | } 76 | 77 | server { 78 | listen 80; 79 | #listen [::]80; 80 | server_name insecure.example.com; 81 | #listen 443 ssl http2; 82 | #listen [::]:443 ssl http2; 83 | 84 | #ssl_certificate /etc/letsencrypt/live/insecure.example.com/fullchain.pem; # managed by Certbot 85 | #ssl_certificate_key /etc/letsencrypt/live/insecure.example.com/privkey.pem; # managed by Certbot 86 | #ssl_trusted_certificate /etc/letsencrypt/live/insecure.example.com/chain.pem; # managed by Certbot 87 | #ssl_stapling on; # managed by Certbot 88 | #ssl_stapling_verify on; # managed by Certbot 89 | 90 | #access_log /var/log/nginx/host.access.log main; 91 | 92 | # modsec has already been enabled globally in 01-local.conf 93 | # 94 | #modsecurity on; 95 | #modsecurity_rules_file /etc/nginx/modsec/main.conf; 96 | 97 | # include letsencrypt endpoints to bypass proxy and be able to autoupdate: 98 | include snippets/letsencrypt.conf; 99 | # add some CSRF headers: 100 | include snippets/policy_headers.conf; 101 | 102 | location / { 103 | root /usr/share/nginx/html; 104 | } 105 | 106 | # serve static files with modsecurity disabled 107 | # 108 | #location /static/ { 109 | # modsecurity off; 110 | # root /usr/share/nginx/html; 111 | #} 112 | 113 | # disable SecRule # 949110 for /api/ route: 114 | # 115 | #location /api/ { 116 | # set proxy headers: X-Forwarded-Proto, Host, X-Forwarded-Host, X-Forwarded-For, X-Real-IP for upstreams: 117 | # include snippets/proxy_headers.conf; 118 | # proxy_pass $backend; 119 | # modsecurity_rules "SecRuleRemoveById 949110"; 120 | #} 121 | 122 | # proxy requests to remote WebSockets backends for /ws/ route: 123 | # 124 | #location /ws/ { 125 | # set proxy headers: X-Forwarded-Proto, Host, X-Forwarded-Host, X-Forwarded-For, X-Real-IP for upstreams, 126 | # enable connection upgrade: 127 | # include snippets/proxy_headers_ws.conf; 128 | # proxy_pass $backend; 129 | #} 130 | 131 | # proxy the PHP scripts to Apache listening on 127.0.0.1:80 132 | # 133 | #location ~ \.php$ { 134 | # set proxy headers: X-Forwarded-Proto, Host, X-Forwarded-Host, X-Forwarded-For, X-Real-IP for upstreams: 135 | # include snippets/proxy_headers.conf; 136 | # proxy_pass http://127.0.0.1; 137 | #} 138 | 139 | # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000 140 | # 141 | #location ~ \.php$ { 142 | # root html; 143 | # fastcgi_pass 127.0.0.1:9000; 144 | # fastcgi_index index.php; 145 | # fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name; 146 | # include fastcgi_params; 147 | #} 148 | 149 | # deny access to .htaccess files, if Apache's document root 150 | # concurs with nginx's one 151 | # 152 | #location ~ /\.ht { 153 | # deny all; 154 | #} 155 | 156 | } 157 | ``` 158 | 159 | If you have multiple server blocks, it is recommended to load ModSecurity rules file in http context, and then customize SecRules per endpoints, to ruduce memory footprint. 160 | Example of /etc/nginx/conf.d/00-local.conf: 161 | ``` 162 | # modsec is enabled for every server context. 163 | # If you want to enable it on a per server basis, please disable it here! 164 | modsecurity on; 165 | modsecurity_rules_file /etc/nginx/modsec/main.conf; 166 | 167 | # you should use reachable resolver for ssl stapling 168 | # this one is generated from /etc/resolv.conf's first nameserver 169 | include snippets/resolver.conf; 170 | # this one is whatever you say 171 | #resolver 8.8.8.8; 172 | 173 | include snippets/log.conf; 174 | include snippets/ssl.conf; 175 | include snippets/brotli.conf; 176 | # increased for nextcloud like apps 177 | client_max_body_size 512m; 178 | 179 | # define indexes here to reduce per vhost complexity 180 | index index.html index.htm; 181 | 182 | # map to proxify WebSockets 183 | map $http_upgrade $connection_upgrade { 184 | default upgrade; 185 | '' close; 186 | } 187 | ``` 188 | 189 | Certbot 190 | ------- 191 | Easily add SSL security to your nginx hosts with certbot. 192 | `docker exec -it nginx-waf /bin/sh` will bring up a prompt at which time you can `certbot` to your hearts content. 193 | 194 | __or__ 195 | 196 | `docker exec -it nginx-waf certbot --no-redirect --must-staple -d example.com` 197 | 198 | It even auto-renew's for you every day! 199 | -------------------------------------------------------------------------------- /hooks/build: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # $IMAGE_NAME var is injected into the build so the tag is correct. 4 | docker build --build-arg VCS_REF=`git rev-parse --short HEAD` \ 5 | --build-arg BUILD_DATE=`date -u +"%Y-%m-%dT%H:%M:%SZ"` \ 6 | -t $IMAGE_NAME . 7 | -------------------------------------------------------------------------------- /src/cron.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -eu 3 | 4 | if [ -z "${WAF_INSTANCE:-}" ];then 5 | WAF_INSTANCE='nginx-waf' 6 | fi 7 | 8 | # fix of certbot's deploy hook 9 | if [ ! -d /etc/letsencrypt/renewal-hooks/deploy ]; then 10 | mkdir -p /etc/letsencrypt/renewal-hooks/deploy 11 | else 12 | rm -rf /etc/letsencrypt/renewal-hooks/deploy/* 13 | fi 14 | 15 | if [ ! -d /etc/letsencrypt/html ]; then 16 | mkdir -p /etc/letsencrypt/html 17 | fi 18 | 19 | if [ ! -d /etc/letsencrypt/html/.well-known ]; then 20 | mkdir -p /etc/letsencrypt/html/.well-known 21 | fi 22 | 23 | cat </etc/letsencrypt/renewal-hooks/deploy/nginx-reload 24 | #!/bin/sh 25 | 26 | id=\$(curl -s -XPOST -H "Content-Type: application/json" -d '{ "Cmd": [ "nginx", "-s", "reload" ] }' --unix-socket /run/docker.sock http://localhost/containers/${WAF_INSTANCE}/exec|sed 's/[\"\{\}]//g'|cut -d: -f2) 27 | curl -s -XPOST -H "Content-Type: application/json" -d '{ "Detach": false, "Tty": false}' --unix-socket /run/docker.sock http://localhost/exec/\${id}/start 28 | EOF 29 | chmod a+x /etc/letsencrypt/renewal-hooks/deploy/nginx-reload 30 | 31 | exec busybox crond -f -l 0 -L /dev/stdout 32 | -------------------------------------------------------------------------------- /src/docker-entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # vim:sw=4:ts=4:et 3 | 4 | set -e 5 | 6 | if [ -z "${NGINX_ENTRYPOINT_QUIET_LOGS:-}" ]; then 7 | exec 3>&1 8 | else 9 | exec 3>/dev/null 10 | fi 11 | 12 | if [ ! -f "/etc/ssl/dhparam.pem" ]; then 13 | curl https://ssl-config.mozilla.org/ffdhe2048.txt > /etc/ssl/dhparam.pem 14 | RES=$? 15 | # curl returned error, generate dhparams ourselves 16 | if [ ${RES} -ne 0 ];then /usr/bin/openssl dhparam -out /etc/ssl/dhparam.pem 2048 ;fi 17 | unset RES 18 | echo "Done" 19 | fi 20 | 21 | # fix Let's Encrypt's key files permissions 22 | if [ -d /etc/letsencrypt/live ]; then 23 | [ -d /etc/letsencrypt/archive ] && chmod o+rx /etc/letsencrypt/archive 24 | chmod o+rx /etc/letsencrypt/live 25 | chown .nginx /etc/letsencrypt/live/*/privkey.pem 26 | chmod g+r /etc/letsencrypt/live/*/privkey.pem 27 | fi 28 | 29 | # check for Let's Encrypt's shared check dir 30 | if [ ! -d /etc/letsencrypt/html ]; then 31 | mkdir -p /etc/letsencrypt/html 32 | fi 33 | 34 | if [ ! -d /etc/letsencrypt/html/.well-known ]; then 35 | mkdir -p /etc/letsencrypt/html/.well-known 36 | fi 37 | 38 | if [ ! -z "${TZ}" ]; then 39 | if [ -f /usr/share/zoneinfo/${TZ} ]; then 40 | if [ -f /etc/localtime ]; then rm -f /etc/localtime; fi 41 | ln -s /usr/share/zoneinfo/${TZ} /etc/localtime && echo ${TZ} > /etc/timezone 42 | fi 43 | fi 44 | 45 | if [ -f "/etc/nginx/snippets/resolver.conf" ]; then 46 | NS=$(cat /etc/resolv.conf|grep nameserver|head -n 1|cut -d' ' -f 2) 47 | sed -i 's/127.0.0.11/'${NS}'/' /etc/nginx/snippets/resolver.conf 48 | fi 49 | 50 | if [ ! -z "${CRON}" ]; then 51 | if [ -f "/usr/sbin/crond" ]; then 52 | /usr/sbin/crond -b -S -l 2 53 | fi 54 | # fix of certbot's deploy hook 55 | if [ ! -d /etc/letsencrypt/renewal-hooks/deploy ]; then 56 | mkdir -p /etc/letsencrypt/renewal-hooks/deploy 57 | else 58 | rm -rf /etc/letsencrypt/renewal-hooks/deploy/* 59 | fi 60 | 61 | cat </etc/letsencrypt/renewal-hooks/deploy/nginx-reload 62 | #!/bin/sh 63 | 64 | /sbin/service nginx reload 65 | EOF 66 | chmod a+x /etc/letsencrypt/renewal-hooks/deploy/nginx-reload 67 | fi 68 | 69 | if [ ! -z "${WORKERS}" ]; then 70 | sed -i "s/worker_processes\s*\d\+;/worker_processes ${WORKERS};/" /etc/nginx/nginx.conf 71 | fi 72 | 73 | if [ "$1" = "nginx" -o "$1" = "nginx-debug" ]; then 74 | if /usr/bin/find "/docker-entrypoint.d/" -mindepth 1 -maxdepth 1 -type f -print -quit 2>/dev/null | read v; then 75 | echo >&3 "$0: /docker-entrypoint.d/ is not empty, will attempt to perform configuration" 76 | 77 | echo >&3 "$0: Looking for shell scripts in /docker-entrypoint.d/" 78 | find "/docker-entrypoint.d/" -follow -type f -print | sort -n | while read -r f; do 79 | case "$f" in 80 | *.sh) 81 | if [ -x "$f" ]; then 82 | echo >&3 "$0: Launching $f"; 83 | "$f" 84 | else 85 | # warn on shell scripts without exec bit 86 | echo >&3 "$0: Ignoring $f, not executable"; 87 | fi 88 | ;; 89 | *) echo >&3 "$0: Ignoring $f";; 90 | esac 91 | done 92 | 93 | echo >&3 "$0: Configuration complete; ready for start up" 94 | else 95 | echo >&3 "$0: No files found in /docker-entrypoint.d/, skipping configuration" 96 | fi 97 | fi 98 | 99 | exec "$@" 100 | -------------------------------------------------------------------------------- /src/etc/nginx/conf.d/00-local.conf: -------------------------------------------------------------------------------- 1 | # modsec is enabled for every server context. 2 | # If you want to enable it on a per server basis, please disable it here! 3 | modsecurity on; 4 | modsecurity_rules_file /etc/nginx/modsec/main.conf; 5 | 6 | # you should use reachable resolver for ssl stapling 7 | # this one is generated from /etc/resolv.conf's first nameserver 8 | include snippets/resolver.conf; 9 | # this one is whatever you say 10 | #resolver 8.8.8.8; 11 | 12 | include snippets/log.conf; 13 | include snippets/ssl.conf; 14 | include snippets/brotli.conf; 15 | # increased for nextcloud like apps 16 | client_max_body_size 512m; 17 | 18 | # define indexes here to reduce per vhost complexity 19 | index index.html index.htm; 20 | 21 | # map to proxify WebSockets 22 | map $http_upgrade $connection_upgrade { 23 | default upgrade; 24 | '' close; 25 | } 26 | -------------------------------------------------------------------------------- /src/etc/nginx/conf.d/default.conf: -------------------------------------------------------------------------------- 1 | upstream backend { 2 | server 127.0.0.1:9000; 3 | } 4 | 5 | server { 6 | listen 80; 7 | #listen [::]80; 8 | server_name insecure.example.com; 9 | #listen 443 ssl http2; 10 | #listen [::]:443 ssl http2; 11 | 12 | #ssl_certificate /etc/letsencrypt/live/insecure.example.com/fullchain.pem; # managed by Certbot 13 | #ssl_certificate_key /etc/letsencrypt/live/insecure.example.com/privkey.pem; # managed by Certbot 14 | #ssl_trusted_certificate /etc/letsencrypt/live/insecure.example.com/chain.pem; # managed by Certbot 15 | #ssl_stapling on; # managed by Certbot 16 | #ssl_stapling_verify on; # managed by Certbot 17 | 18 | #access_log /var/log/nginx/host.access.log main; 19 | 20 | # modsec has already been enabled globally in 01-local.conf 21 | # 22 | #modsecurity on; 23 | #modsecurity_rules_file /etc/nginx/modsec/main.conf; 24 | 25 | # include letsencrypt endpoints to bypass proxy and be able to autoupdate: 26 | include snippets/letsencrypt.conf; 27 | # add some CSRF headers: 28 | include snippets/policy_headers.conf; 29 | 30 | location / { 31 | root /usr/share/nginx/html; 32 | } 33 | 34 | # serve static files with modsecurity disabled 35 | # 36 | #location /static/ { 37 | # modsecurity off; 38 | # root /usr/share/nginx/html; 39 | #} 40 | 41 | # disable SecRule # 949110 for /api/ route: 42 | # 43 | #location /api/ { 44 | # set proxy headers: X-Forwarded-Proto, Host, X-Forwarded-Host, X-Forwarded-For, X-Real-IP for upstreams: 45 | # include snippets/proxy_headers.conf; 46 | # proxy_pass $backend; 47 | # modsecurity_rules "SecRuleRemoveById 949110"; 48 | #} 49 | 50 | # proxy requests to remote WebSockets backends for /ws/ route: 51 | # 52 | #location /ws/ { 53 | # set proxy headers: X-Forwarded-Proto, Host, X-Forwarded-Host, X-Forwarded-For, X-Real-IP for upstreams, 54 | # enable connection upgrade: 55 | # include snippets/proxy_headers_ws.conf; 56 | # proxy_pass $backend; 57 | #} 58 | 59 | # proxy the PHP scripts to Apache listening on 127.0.0.1:80 60 | # 61 | #location ~ \.php$ { 62 | # set proxy headers: X-Forwarded-Proto, Host, X-Forwarded-Host, X-Forwarded-For, X-Real-IP for upstreams: 63 | # include snippets/proxy_headers.conf; 64 | # proxy_pass http://127.0.0.1; 65 | #} 66 | 67 | # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000 68 | # 69 | #location ~ \.php$ { 70 | # root html; 71 | # fastcgi_pass 127.0.0.1:9000; 72 | # fastcgi_index index.php; 73 | # fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name; 74 | # include fastcgi_params; 75 | #} 76 | 77 | # deny access to .htaccess files, if Apache's document root 78 | # concurs with nginx's one 79 | # 80 | #location ~ /\.ht { 81 | # deny all; 82 | #} 83 | 84 | } 85 | -------------------------------------------------------------------------------- /src/etc/nginx/nginx.conf: -------------------------------------------------------------------------------- 1 | # Addition: load MODSECURITY 2 | load_module /etc/nginx/modules/ngx_http_modsecurity_module.so; 3 | # Addition: load BROTLI 4 | load_module /etc/nginx/modules/ngx_http_brotli_static_module.so; 5 | load_module /etc/nginx/modules/ngx_http_brotli_filter_module.so; 6 | 7 | user nginx; 8 | worker_processes 5; # Addition: multiple workers for MODSECURITY 9 | error_log /var/log/nginx/error.log warn; 10 | pid /var/run/nginx.pid; 11 | 12 | events { 13 | worker_connections 1024; 14 | } 15 | 16 | http { 17 | include /etc/nginx/mime.types; 18 | default_type application/octet-stream; 19 | 20 | sendfile on; 21 | tcp_nopush on; 22 | tcp_nodelay on; 23 | server_tokens off; 24 | 25 | keepalive_timeout 65; 26 | 27 | limit_req_status 429; 28 | limit_conn_status 429; 29 | 30 | limit_req_zone $http_x_forwarded_for zone=req_limit_per_ip_per_sec:10m rate=20r/s; 31 | 32 | client_body_buffer_size 10K; 33 | client_header_buffer_size 1k; 34 | large_client_header_buffers 2 1k; 35 | 36 | root /usr/share/nginx/html; 37 | 38 | gzip on; 39 | gzip_disable "msie6"; 40 | 41 | gzip_vary on; 42 | gzip_proxied any; 43 | gzip_comp_level 6; 44 | gzip_buffers 16 8k; 45 | gzip_http_version 1.1; 46 | gzip_min_length 2048; 47 | gzip_types text/plain text/css application/json application/x-javascript application/javascript text/xml application/xml application/xml+rss text/javascript; 48 | 49 | include /etc/nginx/conf.d/*.conf; 50 | } 51 | -------------------------------------------------------------------------------- /src/etc/nginx/snippets/brotli.conf: -------------------------------------------------------------------------------- 1 | brotli on; 2 | #brotli_static on; 3 | #brotli_comp_level 11; 4 | brotli_static off; 5 | brotli_comp_level 6; 6 | #brotli_types text/plain text/css application/json application/x-javascript application/javascript text/xml application/xml application/xml+rss text/javascript; 7 | 8 | brotli_types application/atom+xml application/javascript application/json application/rss+xml 9 | application/vnd.ms-fontobject application/x-font-opentype application/x-font-truetype 10 | application/x-font-ttf application/x-javascript application/xhtml+xml application/xml 11 | font/eot font/opentype font/otf font/truetype image/svg+xml image/vnd.microsoft.icon 12 | image/x-icon image/x-win-bitmap text/css text/javascript text/plain text/xml; 13 | -------------------------------------------------------------------------------- /src/etc/nginx/snippets/letsencrypt.conf: -------------------------------------------------------------------------------- 1 | location ^~ /.well-known/acme-challenge/ { 2 | default_type "text/plain"; 3 | root /etc/letsencrypt/html/; 4 | allow all; 5 | } 6 | location = /.well-known/acme-challenge/ { 7 | return 404; 8 | } 9 | -------------------------------------------------------------------------------- /src/etc/nginx/snippets/log.conf: -------------------------------------------------------------------------------- 1 | log_format main '$remote_addr - $remote_user [$time_local] "$request" ' 2 | '$status $body_bytes_sent "$http_referer" ' 3 | '"$http_user_agent" "$http_x_forwarded_for"'; 4 | 5 | log_format json_combined escape=json 6 | '{' 7 | '"time_local":"$time_local",' 8 | '"remote_addr":"$remote_addr",' 9 | '"remote_user":"$remote_user",' 10 | '"request":"$request",' 11 | '"status": "$status",' 12 | '"host": "$http_host",' 13 | '"body_bytes_sent":"$body_bytes_sent",' 14 | '"request_time":"$request_time",' 15 | '"http_referrer":"$http_referer",' 16 | '"http_user_agent":"$http_user_agent",' 17 | '"http_x_forwarded_for":"$http_x_forwarded_for"' 18 | '}'; 19 | access_log /var/log/nginx/access.log main buffer=32k; 20 | # already defined 21 | # error_log /var/log/nginx/error.log; 22 | -------------------------------------------------------------------------------- /src/etc/nginx/snippets/policy_headers.conf: -------------------------------------------------------------------------------- 1 | add_header X-Frame-Options "SAMEORIGIN"; 2 | add_header X-Content-Type-Options "nosniff"; 3 | add_header X-XSS-Protection "1; mode=block"; 4 | add_header Strict-Transport-Security "max-age=2592000" always; 5 | add_header feature-policy "autoplay 'self';camera 'none';fullscreen 'self';geolocation 'none';microphone 'none';payment 'none'"; 6 | add_header referrer-policy "no-referrer-when-downgrade"; 7 | add_header Content-Security-Policy "default-src https: data: 'unsafe-inline' 'unsafe-eval'" always; 8 | -------------------------------------------------------------------------------- /src/etc/nginx/snippets/proxy_headers.conf: -------------------------------------------------------------------------------- 1 | include snippets/proxy_headers_base.conf; 2 | # for keep-alives to upstream and to reduce time-wait connections 3 | proxy_set_header Connection ""; 4 | -------------------------------------------------------------------------------- /src/etc/nginx/snippets/proxy_headers_base.conf: -------------------------------------------------------------------------------- 1 | proxy_headers_hash_max_size 512; 2 | proxy_headers_hash_bucket_size 64; 3 | proxy_set_header Host $server_name; 4 | proxy_set_header X-Forwarded-Host $server_name; 5 | proxy_set_header X-Forwarded-Proto $scheme; 6 | proxy_set_header X-Forwarded-Port $server_port; 7 | proxy_set_header X-Real-IP $remote_addr; 8 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 9 | #proxy_set_header Origin ""; 10 | proxy_http_version 1.1; 11 | proxy_hide_header X-Powered-By; 12 | -------------------------------------------------------------------------------- /src/etc/nginx/snippets/proxy_headers_ws.conf: -------------------------------------------------------------------------------- 1 | include snippets/proxy_headers_base.conf; 2 | 3 | proxy_set_header Upgrade $http_upgrade; 4 | proxy_set_header Connection $connection_upgrade; 5 | -------------------------------------------------------------------------------- /src/etc/nginx/snippets/resolver.conf: -------------------------------------------------------------------------------- 1 | resolver 127.0.0.11; 2 | -------------------------------------------------------------------------------- /src/etc/nginx/snippets/ssl.conf: -------------------------------------------------------------------------------- 1 | ssl_dhparam /etc/ssl/dhparam.pem; 2 | ssl_protocols TLSv1.2 TLSv1.3; 3 | ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384; 4 | ssl_prefer_server_ciphers off; 5 | ssl_session_timeout 1d; 6 | ssl_session_cache shared:MozSSL:10m; # about 40000 sessions 7 | ssl_session_tickets off; 8 | -------------------------------------------------------------------------------- /src/usr/local/sbin/certbot.default.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | # 3 | 4 | set -euf -o pipefail 5 | 6 | # ************** USAGE ************** 7 | # 8 | # This is an example hook that can be used with Certbot. 9 | # 10 | # Example usage (with certbot-auto and this hook file saved in /root/): 11 | # 12 | # sudo ./certbot-auto -d example.org -d www.example.org -a manual -i nginx --preferred-challenges dns \ 13 | # --manual-auth-hook "/root/certbot.default.sh auth" --manual-cleanup-hook "/root/certbot.default.sh cleanup" 14 | # 15 | # This hook requires configuration, continue reading. 16 | # 17 | # ************** CONFIGURATION ************** 18 | # 19 | # Please configure PROVIDER and PROVIDER_CREDENTIALS. 20 | # 21 | # PROVIDER: 22 | # Set this to whatever DNS host your domain is using: 23 | # 24 | # route53 cloudflare cloudns cloudxns digitalocean 25 | # dnsimple dnsmadeeasy dnspark dnspod easydns gandi 26 | # glesys godaddy linode luadns memset namecheap namesilo 27 | # nsone ovh pointhq powerdns rackspace rage4 softlayer 28 | # transip vultr yandex zonomi 29 | # 30 | # The full list is in Lexicon's README. 31 | # Defaults to Cloudflare. 32 | # 33 | PROVIDER=${DNS_PROVIDER} 34 | # 35 | # PROVIDER_CREDENTIALS: 36 | # Lexicon needs to know how to authenticate to your DNS Host. 37 | # This will vary from DNS host to host. 38 | # To figure out which flags to use, you can look at the Lexicon help. 39 | # For example, for help with Cloudflare: 40 | # 41 | # lexicon cloudflare -h 42 | # 43 | #PROVIDER_CREDENTIALS=("--auth-username=${DNS_USERNAME}" "--auth-token=${DNS_TOKEN}") 44 | # 45 | # PROVIDER_UPDATE_DELAY: 46 | # How many seconds to wait after updating your DNS records. This may be required, 47 | # depending on how slow your DNS host is to begin serving new DNS records after updating 48 | # them via the API. 30 seconds is a safe default, but some providers can be very slow 49 | # (e.g. Linode). 50 | # 51 | # Defaults to 30 seconds. 52 | # 53 | PROVIDER_UPDATE_DELAY=30 54 | 55 | # To be invoked via Certbot's --manual-auth-hook 56 | function auth { 57 | lexicon "${PROVIDER}" --auth-username="${DNS_USERNAME}" --auth-token="${DNS_TOKEN}" \ 58 | create "${CERTBOT_DOMAIN}" TXT --name "_acme-challenge.${CERTBOT_DOMAIN}" --content "${CERTBOT_VALIDATION}" 59 | echo "[Lexicon] Added _acme-challenge.${CERTBOT_DOMAIN}" 60 | sleep "${PROVIDER_UPDATE_DELAY}" 61 | } 62 | 63 | # To be invoked via Certbot's --manual-cleanup-hook 64 | function cleanup { 65 | lexicon "${PROVIDER}" --auth-username="${DNS_USERNAME}" --auth-token="${DNS_TOKEN}" \ 66 | delete "${CERTBOT_DOMAIN}" TXT --name "_acme-challenge.${CERTBOT_DOMAIN}" --content "${CERTBOT_VALIDATION}" 67 | echo "[Lexicon] Deleted _acme-challenge.${CERTBOT_DOMAIN}" 68 | } 69 | 70 | HANDLER=$1; shift; 71 | if [ -n "$(type -t $HANDLER)" ] && [ "$(type -t $HANDLER)" = function ]; then 72 | $HANDLER "$@" 73 | fi 74 | --------------------------------------------------------------------------------