Compare commits
38 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
07be626842 | ||
|
|
3bb164395e | ||
|
|
bc2568a172 | ||
|
|
5ec74880d8 | ||
|
|
f84fd7c9a2 | ||
|
|
6521d7a27a | ||
|
|
813607fbc3 | ||
|
|
843644f806 | ||
|
|
19fa0eb25f | ||
|
|
b4df287228 | ||
|
|
5ce41edc03 | ||
|
|
a3cfb50b4d | ||
|
|
25494acace | ||
|
|
a98dae1fb6 | ||
|
|
1a7abab570 | ||
|
|
42b7a57f01 | ||
|
|
02f9fbe5fc | ||
|
|
69fe066777 | ||
|
|
74417abc9c | ||
|
|
ba7524a419 | ||
|
|
b55aafb997 | ||
|
|
deeb7a76a2 | ||
|
|
ee8aaa4e7e | ||
|
|
605d59a45c | ||
|
|
b85c991b6e | ||
|
|
0d3658adf0 | ||
|
|
0b22209c96 | ||
|
|
e44a1f3e14 | ||
|
|
aa614f82f9 | ||
|
|
c03d410b0a | ||
|
|
e190167bfc | ||
|
|
31e72dce1c | ||
|
|
b8105fc558 | ||
|
|
e73c10fd80 | ||
|
|
a122a259c0 | ||
|
|
7c4894d3b8 | ||
|
|
533c2a1034 | ||
|
|
5611d544d6 |
@@ -1,4 +1,4 @@
|
||||
FROM nginx:stable-alpine
|
||||
FROM nginx:1.20.0-alpine
|
||||
|
||||
COPY nginx-keys/ /tmp/nginx-keys
|
||||
COPY compile.sh /tmp/compile.sh
|
||||
@@ -16,10 +16,13 @@ COPY lua/ /opt/lua
|
||||
COPY prepare.sh /tmp/prepare.sh
|
||||
RUN chmod +x /tmp/prepare.sh && /tmp/prepare.sh && rm -f /tmp/prepare.sh
|
||||
|
||||
# fix CVE-2021-20205
|
||||
RUN apk add "libjpeg-turbo>=2.1.0-r0"
|
||||
|
||||
VOLUME /www /http-confs /server-confs /modsec-confs /modsec-crs-confs /cache /pre-server-confs /acme-challenge
|
||||
|
||||
EXPOSE 8080/tcp 8443/tcp
|
||||
|
||||
USER nginx
|
||||
USER nginx:nginx
|
||||
|
||||
ENTRYPOINT ["/opt/entrypoint/entrypoint.sh"]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM amd64/nginx:stable-alpine
|
||||
FROM amd64/nginx:1.20.0-alpine
|
||||
|
||||
COPY nginx-keys/ /tmp/nginx-keys
|
||||
COPY compile.sh /tmp/compile.sh
|
||||
@@ -16,10 +16,13 @@ COPY lua/ /opt/lua
|
||||
COPY prepare.sh /tmp/prepare.sh
|
||||
RUN chmod +x /tmp/prepare.sh && /tmp/prepare.sh && rm -f /tmp/prepare.sh
|
||||
|
||||
# fix CVE-2021-20205
|
||||
RUN apk add "libjpeg-turbo>=2.1.0-r0"
|
||||
|
||||
VOLUME /www /http-confs /server-confs /modsec-confs /modsec-crs-confs /cache /pre-server-confs /acme-challenge
|
||||
|
||||
EXPOSE 8080/tcp 8443/tcp
|
||||
|
||||
USER nginx
|
||||
USER nginx:nginx
|
||||
|
||||
ENTRYPOINT ["/opt/entrypoint/entrypoint.sh"]
|
||||
|
||||
@@ -3,7 +3,7 @@ FROM alpine AS builder
|
||||
ENV QEMU_URL https://github.com/balena-io/qemu/releases/download/v4.0.0%2Bbalena2/qemu-4.0.0.balena2-arm.tar.gz
|
||||
RUN apk add curl && curl -L ${QEMU_URL} | tar zxvf - -C . --strip-components 1
|
||||
|
||||
FROM arm32v7/nginx:stable-alpine
|
||||
FROM arm32v7/nginx:1.20.0-alpine
|
||||
|
||||
COPY --from=builder qemu-arm-static /usr/bin
|
||||
|
||||
@@ -23,10 +23,13 @@ COPY lua/ /opt/lua
|
||||
COPY prepare.sh /tmp/prepare.sh
|
||||
RUN chmod +x /tmp/prepare.sh && /tmp/prepare.sh && rm -f /tmp/prepare.sh
|
||||
|
||||
# fix CVE-2021-20205
|
||||
RUN apk add "libjpeg-turbo>=2.1.0-r0"
|
||||
|
||||
VOLUME /www /http-confs /server-confs /modsec-confs /modsec-crs-confs /cache /pre-server-confs /acme-challenge
|
||||
|
||||
EXPOSE 8080/tcp 8443/tcp
|
||||
|
||||
USER nginx
|
||||
USER nginx:nginx
|
||||
|
||||
ENTRYPOINT ["/opt/entrypoint/entrypoint.sh"]
|
||||
|
||||
@@ -3,7 +3,7 @@ FROM alpine AS builder
|
||||
ENV QEMU_URL https://github.com/balena-io/qemu/releases/download/v4.0.0%2Bbalena2/qemu-4.0.0.balena2-aarch64.tar.gz
|
||||
RUN apk add curl && curl -L ${QEMU_URL} | tar zxvf - -C . --strip-components 1
|
||||
|
||||
FROM arm64v8/nginx:stable-alpine
|
||||
FROM arm64v8/nginx:1.20.0-alpine
|
||||
|
||||
COPY --from=builder qemu-aarch64-static /usr/bin
|
||||
|
||||
@@ -23,10 +23,13 @@ COPY lua/ /opt/lua
|
||||
COPY prepare.sh /tmp/prepare.sh
|
||||
RUN chmod +x /tmp/prepare.sh && /tmp/prepare.sh && rm -f /tmp/prepare.sh
|
||||
|
||||
# fix CVE-2021-20205
|
||||
RUN apk add "libjpeg-turbo>=2.1.0-r0"
|
||||
|
||||
VOLUME /www /http-confs /server-confs /modsec-confs /modsec-crs-confs /cache /pre-server-confs /acme-challenge
|
||||
|
||||
EXPOSE 8080/tcp 8443/tcp
|
||||
|
||||
USER nginx
|
||||
USER nginx:nginx
|
||||
|
||||
ENTRYPOINT ["/opt/entrypoint/entrypoint.sh"]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM i386/nginx:stable-alpine
|
||||
FROM i386/nginx:1.20.0-alpine
|
||||
|
||||
COPY nginx-keys/ /tmp/nginx-keys
|
||||
COPY compile.sh /tmp/compile.sh
|
||||
@@ -16,10 +16,13 @@ COPY lua/ /opt/lua
|
||||
COPY prepare.sh /tmp/prepare.sh
|
||||
RUN chmod +x /tmp/prepare.sh && /tmp/prepare.sh && rm -f /tmp/prepare.sh
|
||||
|
||||
# fix CVE-2021-20205
|
||||
RUN apk add "libjpeg-turbo>=2.1.0-r0"
|
||||
|
||||
VOLUME /www /http-confs /server-confs /modsec-confs /modsec-crs-confs /cache /pre-server-confs /acme-challenge
|
||||
|
||||
EXPOSE 8080/tcp 8443/tcp
|
||||
|
||||
USER nginx
|
||||
USER nginx:nginx
|
||||
|
||||
ENTRYPOINT ["/opt/entrypoint/entrypoint.sh"]
|
||||
|
||||
102
README.md
102
README.md
@@ -3,13 +3,17 @@
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<img src="https://img.shields.io/badge/bunkerized--nginx-1.2.3-blue" />
|
||||
<img src="https://img.shields.io/badge/nginx-1.18.0-blue" />
|
||||
<img src="https://img.shields.io/github/last-commit/bunkerity/bunkerized-nginx" />
|
||||
<img src="https://img.shields.io/badge/bunkerized--nginx-1.2.4-blue" />
|
||||
<img src="https://img.shields.io/badge/nginx-1.20.0-blue" />
|
||||
<img src="https://img.shields.io/github/last-commit/bunkerity/bunkerized-nginx" />
|
||||
<img src="https://img.shields.io/github/workflow/status/bunkerity/bunkerized-nginx/Automatic%20test?label=automatic%20test" />
|
||||
<img src="https://img.shields.io/docker/cloud/build/bunkerity/bunkerized-nginx" />
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://matrix.to/#/#bunkerized-nginx:matrix.org"><img src="https://img.shields.io/badge/matrix%20chat-%23bunkerized--nginx%3Amatrix.org-blue" /></a>
|
||||
<img src="https://img.shields.io/github/workflow/status/bunkerity/bunkerized-nginx/Automatic%20test?label=automatic%20test" />
|
||||
<img src="https://img.shields.io/docker/cloud/build/bunkerity/bunkerized-nginx" />
|
||||
<a href="https://twitter.com/bunkerity"><img src="https://img.shields.io/twitter/follow/bunkerity?style=social" /></a>
|
||||
<a href="https://www.bunkerity.com"><img src="https://img.shields.io/badge/website-www.bunkerity.com-blue" /></a>
|
||||
<a href="https://twitter.com/bunkerity"><img src="https://img.shields.io/twitter/follow/bunkerity?style=social" /></a>
|
||||
</p>
|
||||
|
||||
nginx Docker image secure by default.
|
||||
@@ -34,9 +38,13 @@ Fooling automated tools/scanners :
|
||||
|
||||
<img src="https://github.com/bunkerity/bunkerized-nginx/blob/master/demo.gif?raw=true" />
|
||||
|
||||
You can find a live demo at https://demo-nginx.bunkerity.com, feel free to do some security tests.
|
||||
|
||||
# Table of contents
|
||||
<details>
|
||||
<summary>Click to show</summary>
|
||||
|
||||
- [Table of contents](#table-of-contents)
|
||||
- [Live demo](#live-demo)
|
||||
- [Quickstart guide](#quickstart-guide)
|
||||
* [Run HTTP server with default settings](#run-http-server-with-default-settings)
|
||||
* [In combination with PHP](#in-combination-with-php)
|
||||
@@ -85,9 +93,7 @@ Fooling automated tools/scanners :
|
||||
* [Logrotate](#logrotate)
|
||||
* [Cron jobs](#cron-jobs)
|
||||
* [Misc](#misc-2)
|
||||
|
||||
# Live demo
|
||||
You can find a live demo at https://demo-nginx.bunkerity.com.
|
||||
</details>
|
||||
|
||||
# Quickstart guide
|
||||
|
||||
@@ -440,15 +446,32 @@ When `USE_ANTIBOT` is set to *captcha*, every users visiting your website must c
|
||||
|
||||
## Hardening
|
||||
|
||||
### Drop capabilities
|
||||
By default, *bunkerized-nginx* runs as non-root user inside the container and should not use any of the default [capabilities](https://docs.docker.com/engine/security/#linux-kernel-capabilities) allowed by Docker. You can safely remove all capabilities to harden the container :
|
||||
|
||||
```shell
|
||||
docker run ... --drop-cap=all ... bunkerity/bunkerized-nginx
|
||||
```
|
||||
|
||||
### User namespace remap
|
||||
Another hardening trick is [user namespace remapping](https://docs.docker.com/engine/security/userns-remap/) : it allows you to map the UID/GID of users inside a container to another UID/GID on the host. For example, you can map the user nginx with UID/GID 101 inside the container to a non-existent user with UID/GID 100101 on the host.
|
||||
|
||||
Let's assume you have the /etc/subuid and /etc/subgid files like this :
|
||||
```
|
||||
user:100000:65536
|
||||
```
|
||||
It means that everything done inside the container will be remapped to UID/GID 100101 (100000 + 101) on the host.
|
||||
|
||||
Please note that you must set the rights on the volumes (e.g. : /etc/letsencrypt, /www, ...) according to the remapped UID/GID :
|
||||
```shell
|
||||
$ chown root:100101 /path/to/letsencrypt
|
||||
$ chmod 770 /path/to/letsencrypt
|
||||
$ docker run ... -v /path/to/letsencrypt:/etc/letsencrypt ... bunkerity/bunkerized-nginx
|
||||
```
|
||||
|
||||
# Tutorials and examples
|
||||
|
||||
You will find some docker-compose examples in the [examples directory](https://github.com/bunkerity/bunkerized-nginx/tree/master/examples).
|
||||
You will find some docker-compose examples in the [examples directory](https://github.com/bunkerity/bunkerized-nginx/tree/master/examples) and tutorials on our [blog](https://www.bunkerity.com/blog).
|
||||
|
||||
# Include custom configurations
|
||||
Custom configurations files (ending with .conf suffix) can be added in some directory inside the container :
|
||||
@@ -648,11 +671,10 @@ Only valid when `USE_REVERSE_PROXY` is set to *yes*. Set it to *yes* when the co
|
||||
You can set multiple url/host by adding a suffix number to the variable name like this : `REVERSE_PROXY_WS_1`, `REVERSE_PROXY_WS_2`, `REVERSE_PROXY_WS_3`, ...
|
||||
|
||||
`REVERSE_PROXY_HEADERS`
|
||||
Values : *\<list of custom headers separated with a semicolon\>*
|
||||
Examples : Access-Control-Allow-Origin 'https://mydomain.dev'; Custom_Api_Header 'test';
|
||||
Default value : ""
|
||||
Values : *\<list of custom headers separated with a semicolon like this : header1 value1;header2 value2...\>*
|
||||
Default value :
|
||||
Context : *global*, *multisite*
|
||||
Only valid when `USE_REVERSE_PROXY` is set to *yes*. Set it to *yes* when the corresponding `REVERSE_PROXY_HOST` is a WebSocket server.
|
||||
Only valid when `USE_REVERSE_PROXY` is set to *yes*.
|
||||
You can set multiple url/host by adding a suffix number to the variable name like this : `REVERSE_PROXY_HEADERS_1`, `REVERSE_PROXY_HEADERS_2`, `REVERSE_PROXY_HEADERS_3`, ...
|
||||
|
||||
`PROXY_REAL_IP`
|
||||
@@ -876,19 +898,19 @@ If set to yes, nginx will redirect all HTTP requests to HTTPS.
|
||||
`USE_CUSTOM_HTTPS`
|
||||
Values : *yes* | *no*
|
||||
Default value : *no*
|
||||
Context : *global*
|
||||
Context : *global*, *multisite*
|
||||
If set to yes, HTTPS will be enabled with certificate/key of your choice.
|
||||
|
||||
`CUSTOM_HTTPS_CERT`
|
||||
Values : *\<any valid path inside the container\>*
|
||||
Default value :
|
||||
Context : *global*
|
||||
Context : *global*, *multisite*
|
||||
Full path of the certificate file to use when `USE_CUSTOM_HTTPS` is set to yes.
|
||||
|
||||
`CUSTOM_HTTPS_KEY`
|
||||
Values : *\<any valid path inside the container\>*
|
||||
Default value :
|
||||
Context : *global*
|
||||
Context : *global*, *multisite*
|
||||
Full path of the key file to use when `USE_CUSTOM_HTTPS` is set to yes.
|
||||
|
||||
### Self-signed certificate
|
||||
@@ -1173,10 +1195,10 @@ Context : *global*, *multisite*
|
||||
If set to *yes*, lets you define custom IP addresses to be whitelisted through the `WHITELIST_IP_LIST` environment variable.
|
||||
|
||||
`WHITELIST_IP_LIST`
|
||||
Values : *\<list of IP addresses separated with spaces\>*
|
||||
Values : *\<list of IP addresses and/or network CIDR blocks separated with spaces\>*
|
||||
Default value : *23.21.227.69 40.88.21.235 50.16.241.113 50.16.241.114 50.16.241.117 50.16.247.234 52.204.97.54 52.5.190.19 54.197.234.188 54.208.100.253 54.208.102.37 107.21.1.8*
|
||||
Context : *global*
|
||||
The list of IP addresses to whitelist when `USE_WHITELIST_IP` is set to *yes*. The default list contains IP addresses of the [DuckDuckGo crawler](https://help.duckduckgo.com/duckduckgo-help-pages/results/duckduckbot/).
|
||||
The list of IP addresses and/or network CIDR blocks to whitelist when `USE_WHITELIST_IP` is set to *yes*. The default list contains IP addresses of the [DuckDuckGo crawler](https://help.duckduckgo.com/duckduckgo-help-pages/results/duckduckbot/).
|
||||
|
||||
`USE_WHITELIST_REVERSE`
|
||||
Values : *yes* | *no*
|
||||
@@ -1196,6 +1218,12 @@ Default value :
|
||||
Context : *global*, *multisite*
|
||||
Whitelist user agent from being blocked by `BLOCK_USER_AGENT`.
|
||||
|
||||
`WHITELIST_URI`
|
||||
Values : *\<list of URI separated with spaces\>*
|
||||
Default value :
|
||||
Context : *global*, *multisite*
|
||||
URI listed here have security checks like bad user-agents, bad IP, ... disabled. Useful when using callbacks for example.
|
||||
|
||||
### Custom blacklisting
|
||||
|
||||
`USE_BLACKLIST_IP`
|
||||
@@ -1205,10 +1233,10 @@ Context : *global*, *multisite*
|
||||
If set to *yes*, lets you define custom IP addresses to be blacklisted through the `BLACKLIST_IP_LIST` environment variable.
|
||||
|
||||
`BLACKLIST_IP_LIST`
|
||||
Values : *\<list of IP addresses separated with spaces\>*
|
||||
Values : *\<list of IP addresses and/or network CIDR blocks separated with spaces\>*
|
||||
Default value :
|
||||
Context : *global*
|
||||
The list of IP addresses to blacklist when `USE_BLACKLIST_IP` is set to *yes*.
|
||||
The list of IP addresses and/or network CIDR blocks to blacklist when `USE_BLACKLIST_IP` is set to *yes*.
|
||||
|
||||
`USE_BLACKLIST_REVERSE`
|
||||
Values : *yes* | *no*
|
||||
@@ -1228,18 +1256,18 @@ The list of reverse DNS suffixes to blacklist when `USE_BLACKLIST_REVERSE` is se
|
||||
Values : *yes* | *no*
|
||||
Default value : *yes*
|
||||
Context : *global*, *multisite*
|
||||
If set to yes, the amount of HTTP requests made by a user will be limited during a period of time.
|
||||
More info rate limiting [here](https://www.nginx.com/blog/rate-limiting-nginx/).
|
||||
If set to yes, the amount of HTTP requests made by a user for a given resource will be limited during a period of time.
|
||||
More info rate limiting [here](https://www.nginx.com/blog/rate-limiting-nginx/) (the key used is $binary_remote_addr$uri).
|
||||
|
||||
`LIMIT_REQ_RATE`
|
||||
Values : *Xr/s* | *Xr/m*
|
||||
Default value : *20r/s*
|
||||
Default value : *1r/s*
|
||||
Context : *global*, *multisite*
|
||||
The rate limit to apply when `USE_LIMIT_REQ` is set to *yes*. Default is 10 requests per second.
|
||||
The rate limit to apply when `USE_LIMIT_REQ` is set to *yes*. Default is 1 request to the same URI and from the same IP per second.
|
||||
|
||||
`LIMIT_REQ_BURST`
|
||||
Values : *<any valid integer\>*
|
||||
Default value : *40*
|
||||
Default value : *2*
|
||||
Context : *global*, *multisite*
|
||||
The number of requests to put in queue before rejecting requests.
|
||||
|
||||
@@ -1255,12 +1283,12 @@ The size of the cache to store information about request limiting.
|
||||
Values : *yes* | *no*
|
||||
Default value : *yes*
|
||||
Context : *global*, *multisite*
|
||||
If set to yes, the number of connections made by an ip will be limited during a period of time. (ie. Very small/weak ddos protection)
|
||||
If set to yes, the number of connections made by an ip will be limited during a period of time. (ie. very small/weak ddos protection)
|
||||
More info connections limiting [here](http://nginx.org/en/docs/http/ngx_http_limit_conn_module.html).
|
||||
|
||||
`LIMIT_CONN_MAX`
|
||||
Values : *<any valid integer\>*
|
||||
Default value : *40*
|
||||
Default value : *50*
|
||||
Context : *global*, *multisite*
|
||||
The maximum number of connections per ip to put in queue before rejecting requests.
|
||||
|
||||
@@ -1290,7 +1318,7 @@ Only allow specific countries accessing your website. Use 2 letters country code
|
||||
Values : *\<any valid IP/hostname\>*
|
||||
Default value :
|
||||
Context : *global*, *multisite*
|
||||
Set the IP/hostname address of a remote PHP-FPM to execute .php files. See `USE_PHP` if you want to run a PHP-FPM instance on the same container as bunkerized-nginx.
|
||||
Set the IP/hostname address of a remote PHP-FPM to execute .php files.
|
||||
|
||||
`REMOTE_PHP_PATH`
|
||||
Values : *\<any valid absolute path\>*
|
||||
@@ -1358,6 +1386,14 @@ Default value : *yes*
|
||||
Context : *global*
|
||||
If set to yes, ClamAV will automatically remove the detected files.
|
||||
|
||||
## Syslog
|
||||
|
||||
`REMOTE_SYSLOG`
|
||||
Values : *\<any IP/hostname\>*
|
||||
Default value :
|
||||
Context : *global*
|
||||
When defined, rsyslog will send logs (access.log and error.log) to the corresponding IP/hostname using syslog UDP protocol.
|
||||
|
||||
## Logrotate
|
||||
|
||||
`LOGROTATE_MINSIZE`
|
||||
@@ -1453,3 +1489,9 @@ Values : *random* | *\<any valid URI path\>*
|
||||
Default value : *random*
|
||||
Context : *global*
|
||||
Set it to a random path when you use *bunkerized-nginx* with *autoconf* feature in swarm mode. More info [here](#swarm-mode).
|
||||
|
||||
`API_WHITELIST_IP`
|
||||
Values : *\<list of IP/CIDR separated with space\>*
|
||||
Default value : *192.168.0.0/16 172.16.0.0/12 10.0.0.0/8*
|
||||
Context : *global*
|
||||
List of IP/CIDR block allowed to send API order using the `API_URI` uri.
|
||||
|
||||
@@ -11,6 +11,11 @@ class AutoConf :
|
||||
self.__sites = {}
|
||||
self.__config = Config(self.__swarm, api)
|
||||
|
||||
def get_server(self, id) :
|
||||
if id in self.__servers :
|
||||
return self.__servers[id]
|
||||
return False
|
||||
|
||||
def reload(self) :
|
||||
return self.__config.reload(self.__instances)
|
||||
|
||||
@@ -60,9 +65,9 @@ class AutoConf :
|
||||
self.__instances[id] = instance
|
||||
if self.__swarm and len(self.__instances) == 1 :
|
||||
if self.__config.initconf(self.__instances) :
|
||||
utils.log("[*] initial config succeeded")
|
||||
utils.log("[*] Initial config succeeded")
|
||||
else :
|
||||
utils.log("[!] initial config failed")
|
||||
utils.log("[!] Initial config failed")
|
||||
utils.log("[*] bunkerized-nginx instance created : " + name + " / " + id)
|
||||
elif event == "start" :
|
||||
self.__instances[id].reload()
|
||||
@@ -82,10 +87,12 @@ class AutoConf :
|
||||
def __process_server(self, instance, event, id, name, labels) :
|
||||
vars = { k.replace("bunkerized-nginx.", "", 1) : v for k, v in labels.items() if k.startswith("bunkerized-nginx.")}
|
||||
if event == "create" :
|
||||
utils.log("[*] Generating config for " + vars["SERVER_NAME"] + " ...")
|
||||
if self.__config.generate(self.__instances, vars) :
|
||||
utils.log("[*] Generated config for " + vars["SERVER_NAME"])
|
||||
self.__servers[id] = instance
|
||||
if self.__swarm :
|
||||
utils.log("[*] Activating config for " + vars["SERVER_NAME"] + " ...")
|
||||
if self.__config.activate(self.__instances, vars) :
|
||||
utils.log("[*] Activated config for " + vars["SERVER_NAME"])
|
||||
else :
|
||||
@@ -95,6 +102,7 @@ class AutoConf :
|
||||
elif event == "start" :
|
||||
if id in self.__servers :
|
||||
self.__servers[id].reload()
|
||||
utils.log("[*] Activating config for " + vars["SERVER_NAME"] + " ...")
|
||||
if self.__config.activate(self.__instances, vars) :
|
||||
utils.log("[*] Activated config for " + vars["SERVER_NAME"])
|
||||
else :
|
||||
@@ -102,6 +110,7 @@ class AutoConf :
|
||||
elif event == "die" :
|
||||
if id in self.__servers :
|
||||
self.__servers[id].reload()
|
||||
utils.log("[*] Deactivating config for " + vars["SERVER_NAME"])
|
||||
if self.__config.deactivate(self.__instances, vars) :
|
||||
utils.log("[*] Deactivated config for " + vars["SERVER_NAME"])
|
||||
else :
|
||||
@@ -109,11 +118,13 @@ class AutoConf :
|
||||
elif event == "destroy" or event == "remove" :
|
||||
if id in self.__servers :
|
||||
if self.__swarm :
|
||||
utils.log("[*] Deactivating config for " + vars["SERVER_NAME"])
|
||||
if self.__config.deactivate(self.__instances, vars) :
|
||||
utils.log("[*] Deactivated config for " + vars["SERVER_NAME"])
|
||||
else :
|
||||
utils.log("[!] Can't deactivate config for " + vars["SERVER_NAME"])
|
||||
del self.__servers[id]
|
||||
utils.log("[*] Removing config for " + vars["SERVER_NAME"])
|
||||
if self.__config.remove(vars) :
|
||||
utils.log("[*] Removed config for " + vars["SERVER_NAME"])
|
||||
else :
|
||||
|
||||
@@ -20,25 +20,42 @@ class Config :
|
||||
var = var_value.split("=")[0]
|
||||
value = var_value.replace(var + "=", "", 1)
|
||||
vars[var] = value
|
||||
if self.globalconf(instances) :
|
||||
i = 0
|
||||
started = False
|
||||
while i < 10 :
|
||||
if self.__ping(instances) :
|
||||
started = True
|
||||
break
|
||||
i = i + 1
|
||||
utils.log("[!] Waiting " + str(i) + " seconds before retrying to contact nginx instances")
|
||||
time.sleep(i)
|
||||
if started :
|
||||
proc = subprocess.run(["/bin/su", "-s", "/opt/entrypoint/jobs.sh", "nginx"], env=vars, capture_output=True)
|
||||
return proc.returncode == 0
|
||||
else :
|
||||
utils.log("[!] bunkerized-nginx instances are not started")
|
||||
|
||||
utils.log("[*] Generating global config ...")
|
||||
if not self.globalconf(instances) :
|
||||
utils.log("[!] Can't generate global config")
|
||||
return False
|
||||
utils.log("[*] Generated global config")
|
||||
|
||||
if "SERVER_NAME" in vars and vars["SERVER_NAME"] != "" :
|
||||
for server in vars["SERVER_NAME"].split(" ") :
|
||||
vars_site = vars.copy()
|
||||
vars_site["SERVER_NAME"] = server
|
||||
utils.log("[*] Generating config for " + vars["SERVER_NAME"] + " ...")
|
||||
if not self.generate(instances, vars_site) or not self.activate(instances, vars_site, reload=False) :
|
||||
utils.log("[!] Can't generate/activate site config for " + server)
|
||||
return False
|
||||
utils.log("[*] Generated config for " + vars["SERVER_NAME"])
|
||||
with open("/etc/nginx/autoconf", "w") as f :
|
||||
f.write("ok")
|
||||
|
||||
utils.log("[*] Waiting for bunkerized-nginx tasks ...")
|
||||
i = 1
|
||||
started = False
|
||||
while i <= 10 :
|
||||
time.sleep(i)
|
||||
if self.__ping(instances) :
|
||||
started = True
|
||||
break
|
||||
i = i + 1
|
||||
utils.log("[!] Waiting " + str(i) + " seconds before retrying to contact bunkerized-nginx tasks")
|
||||
if started :
|
||||
utils.log("[*] bunkerized-nginx tasks started")
|
||||
proc = subprocess.run(["/bin/su", "-s", "/opt/entrypoint/jobs.sh", "nginx"], env=vars, capture_output=True)
|
||||
return proc.returncode == 0
|
||||
else :
|
||||
utils.log("[!] Can't generate global conf")
|
||||
utils.log("[!] bunkerized-nginx tasks are not started")
|
||||
except Exception as e :
|
||||
traceback.print_exc()
|
||||
utils.log("[!] Error while initializing config : " + str(e))
|
||||
return False
|
||||
|
||||
@@ -54,12 +71,11 @@ class Config :
|
||||
vars[var] = value
|
||||
proc = subprocess.run(["/bin/su", "-s", "/opt/entrypoint/global-config.sh", "nginx"], env=vars, capture_output=True)
|
||||
if proc.returncode == 0 :
|
||||
with open("/etc/nginx/autoconf", "w") as f :
|
||||
f.write("ok")
|
||||
return True
|
||||
else :
|
||||
utils.log("[*] Error while generating global config : return code = " + str(proc.returncode))
|
||||
except Exception as e :
|
||||
traceback.print_exc()
|
||||
utils.log("[!] Error while generating global config : " + str(e))
|
||||
utils.log("[!] Exception while generating global config : " + str(e))
|
||||
return False
|
||||
|
||||
def generate(self, instances, vars) :
|
||||
@@ -79,61 +95,76 @@ class Config :
|
||||
vars_defaults.update(vars_instances)
|
||||
vars_defaults.update(vars)
|
||||
# Call site-config.sh to generate the config
|
||||
proc = subprocess.run(["/bin/su", "-s", "/bin/sh", "-c", "/opt/entrypoint/site-config.sh" + " " + vars["SERVER_NAME"], "nginx"], env=vars_defaults, capture_output=True)
|
||||
proc = subprocess.run(["/bin/su", "-s", "/bin/sh", "-c", "/opt/entrypoint/site-config.sh" + " \"" + vars["SERVER_NAME"] + "\"", "nginx"], env=vars_defaults, capture_output=True)
|
||||
if proc.returncode == 0 and vars_defaults["MULTISITE"] == "yes" and self.__swarm :
|
||||
proc = subprocess.run(["/bin/su", "-s", "/opt/entrypoint/multisite-config.sh", "nginx"], env=vars_defaults, capture_output=True)
|
||||
return proc.returncode == 0
|
||||
return proc.returncode == 0
|
||||
if proc.returncode == 0 :
|
||||
return True
|
||||
utils.log("[!] Error while generating site config for " + vars["SERVER_NAME"] + " : return code = " + str(proc.returncode))
|
||||
except Exception as e :
|
||||
traceback.print_exc()
|
||||
utils.log("[!] Error while generating site config : " + str(e))
|
||||
utils.log("[!] Exception while generating site config : " + str(e))
|
||||
return False
|
||||
|
||||
def activate(self, instances, vars) :
|
||||
def activate(self, instances, vars, reload=True) :
|
||||
try :
|
||||
# Get first server name
|
||||
first_server_name = vars["SERVER_NAME"].split(" ")[0]
|
||||
|
||||
# Check if file exists
|
||||
if not os.path.isfile("/etc/nginx/" + vars["SERVER_NAME"] + "/server.conf") :
|
||||
utils.log("[!] /etc/nginx/" + vars["SERVER_NAME"] + "/server.conf doesn't exist")
|
||||
if not os.path.isfile("/etc/nginx/" + first_server_name + "/server.conf") :
|
||||
utils.log("[!] /etc/nginx/" + first_server_name + "/server.conf doesn't exist")
|
||||
return False
|
||||
|
||||
# Include the server conf
|
||||
utils.replace_in_file("/etc/nginx/nginx.conf", "}", "include /etc/nginx/" + vars["SERVER_NAME"] + "/server.conf;\n}")
|
||||
utils.replace_in_file("/etc/nginx/nginx.conf", "}", "include /etc/nginx/" + first_server_name + "/server.conf;\n}")
|
||||
|
||||
# Reload
|
||||
if not reload or self.reload(instances) :
|
||||
return True
|
||||
|
||||
return self.reload(instances)
|
||||
except Exception as e :
|
||||
traceback.print_exc()
|
||||
utils.log("[!] Error while activating config : " + str(e))
|
||||
utils.log("[!] Exception while activating config : " + str(e))
|
||||
|
||||
return False
|
||||
|
||||
def deactivate(self, instances, vars) :
|
||||
try :
|
||||
# Get first server name
|
||||
first_server_name = vars["SERVER_NAME"].split(" ")[0]
|
||||
|
||||
# Check if file exists
|
||||
if not os.path.isfile("/etc/nginx/" + vars["SERVER_NAME"] + "/server.conf") :
|
||||
utils.log("[!] /etc/nginx/" + vars["SERVER_NAME"] + "/server.conf doesn't exist")
|
||||
if not os.path.isfile("/etc/nginx/" + first_server_name + "/server.conf") :
|
||||
utils.log("[!] /etc/nginx/" + first_server_name + "/server.conf doesn't exist")
|
||||
return False
|
||||
|
||||
# Remove the include
|
||||
utils.replace_in_file("/etc/nginx/nginx.conf", "include /etc/nginx/" + vars["SERVER_NAME"] + "/server.conf;\n", "")
|
||||
utils.replace_in_file("/etc/nginx/nginx.conf", "include /etc/nginx/" + first_server_name + "/server.conf;\n", "")
|
||||
|
||||
return self.reload(instances)
|
||||
# Reload
|
||||
if self.reload(instances) :
|
||||
return True
|
||||
|
||||
except Exception as e :
|
||||
traceback.print_exc()
|
||||
utils.log("[!] Error while deactivating config : " + str(e))
|
||||
utils.log("[!] Exception while deactivating config : " + str(e))
|
||||
|
||||
return False
|
||||
|
||||
def remove(self, instances, vars) :
|
||||
def remove(self, vars) :
|
||||
try :
|
||||
# Get first server name
|
||||
first_server_name = vars["SERVER_NAME"].split(" ")[0]
|
||||
|
||||
# Check if file exists
|
||||
if not os.path.isfile("/etc/nginx/" + vars["SERVER_NAME"] + "/server.conf") :
|
||||
utils.log("[!] /etc/nginx/" + vars["SERVER_NAME"] + "/server.conf doesn't exist")
|
||||
if not os.path.isfile("/etc/nginx/" + first_server_name + "/server.conf") :
|
||||
utils.log("[!] /etc/nginx/" + first_server_name + "/server.conf doesn't exist")
|
||||
return False
|
||||
|
||||
# Remove the folder
|
||||
shutil.rmtree("/etc/nginx/" + vars["SERVER_NAME"])
|
||||
shutil.rmtree("/etc/nginx/" + first_server_name)
|
||||
return True
|
||||
except Exception as e :
|
||||
utils.log("[!] Error while deactivating config : " + str(e))
|
||||
|
||||
return False
|
||||
|
||||
def reload(self, instances) :
|
||||
@@ -149,7 +180,7 @@ class Config :
|
||||
instance.reload()
|
||||
# Reload via API
|
||||
if self.__swarm :
|
||||
# Send POST request on http://serviceName.NodeID.TaskID:8000/reload
|
||||
# Send POST request on http://serviceName.NodeID.TaskID:8000/action
|
||||
name = instance.name
|
||||
for task in instance.tasks() :
|
||||
if task["Status"]["State"] != "running" :
|
||||
|
||||
@@ -12,6 +12,9 @@ RUN apk add py3-pip apache2-utils bash certbot curl logrotate openssl && \
|
||||
mkdir /opt/scripts && \
|
||||
addgroup -g 101 nginx && \
|
||||
adduser -h /var/cache/nginx -g nginx -s /sbin/nologin -G nginx -D -H -u 101 nginx && \
|
||||
mkdir /etc/letsencrypt && \
|
||||
chown root:nginx /etc/letsencrypt && \
|
||||
chmod 770 /etc/letsencrypt && \
|
||||
mkdir /var/log/letsencrypt && \
|
||||
chown root:nginx /var/log/letsencrypt && \
|
||||
chmod 770 /var/log/letsencrypt && \
|
||||
@@ -25,7 +28,11 @@ RUN apk add py3-pip apache2-utils bash certbot curl logrotate openssl && \
|
||||
chown root:nginx /var/log/jobs.log && \
|
||||
chmod 770 /var/log/jobs.log && \
|
||||
chown -R root:nginx /opt/confs/nginx && \
|
||||
chmod -R 770 /opt/confs/nginx
|
||||
chmod -R 770 /opt/confs/nginx && \
|
||||
mkdir /acme-challenge && \
|
||||
chown root:nginx /acme-challenge && \
|
||||
chmod 770 /acme-challenge
|
||||
|
||||
|
||||
COPY autoconf/misc/logrotate.conf /etc/logrotate.conf
|
||||
COPY scripts/* /opt/scripts/
|
||||
|
||||
@@ -7,10 +7,14 @@ COPY --from=builder /etc/nginx/ /opt/confs/nginx
|
||||
RUN apk add py3-pip apache2-utils bash certbot curl logrotate openssl && \
|
||||
pip3 install docker requests && \
|
||||
mkdir /opt/entrypoint && \
|
||||
mkdir -p /opt/confs/site && \
|
||||
mkdir -p /opt/confs/site && \
|
||||
mkdir -p /opt/confs/global && \
|
||||
mkdir /opt/scripts && \
|
||||
addgroup -g 101 nginx && \
|
||||
adduser -h /var/cache/nginx -g nginx -s /sbin/nologin -G nginx -D -H -u 101 nginx && \
|
||||
mkdir /etc/letsencrypt && \
|
||||
chown root:nginx /etc/letsencrypt && \
|
||||
chmod 770 /etc/letsencrypt && \
|
||||
mkdir /var/log/letsencrypt && \
|
||||
chown root:nginx /var/log/letsencrypt && \
|
||||
chmod 770 /var/log/letsencrypt && \
|
||||
@@ -24,7 +28,10 @@ RUN apk add py3-pip apache2-utils bash certbot curl logrotate openssl && \
|
||||
chown root:nginx /var/log/jobs.log && \
|
||||
chmod 770 /var/log/jobs.log && \
|
||||
chown -R root:nginx /opt/confs/nginx && \
|
||||
chmod -R 770 /opt/confs/nginx
|
||||
chmod -R 770 /opt/confs/nginx && \
|
||||
mkdir /acme-challenge && \
|
||||
chown root:nginx /acme-challenge && \
|
||||
chmod 770 /acme-challenge
|
||||
|
||||
COPY autoconf/misc/logrotate.conf /etc/logrotate.conf
|
||||
COPY scripts/* /opt/scripts/
|
||||
|
||||
@@ -15,8 +15,12 @@ RUN apk add py3-pip apache2-utils bash certbot curl logrotate openssl && \
|
||||
mkdir /opt/entrypoint && \
|
||||
mkdir -p /opt/confs/site && \
|
||||
mkdir -p /opt/confs/global && \
|
||||
mkdir /opt/scripts && \
|
||||
addgroup -g 101 nginx && \
|
||||
adduser -h /var/cache/nginx -g nginx -s /sbin/nologin -G nginx -D -H -u 101 nginx && \
|
||||
mkdir /etc/letsencrypt && \
|
||||
chown root:nginx /etc/letsencrypt && \
|
||||
chmod 770 /etc/letsencrypt && \
|
||||
mkdir /var/log/letsencrypt && \
|
||||
chown root:nginx /var/log/letsencrypt && \
|
||||
chmod 770 /var/log/letsencrypt && \
|
||||
@@ -30,7 +34,10 @@ RUN apk add py3-pip apache2-utils bash certbot curl logrotate openssl && \
|
||||
chown root:nginx /var/log/jobs.log && \
|
||||
chmod 770 /var/log/jobs.log && \
|
||||
chown -R root:nginx /opt/confs/nginx && \
|
||||
chmod -R 770 /opt/confs/nginx
|
||||
chmod -R 770 /opt/confs/nginx && \
|
||||
mkdir /acme-challenge && \
|
||||
chown root:nginx /acme-challenge && \
|
||||
chmod 770 /acme-challenge
|
||||
|
||||
COPY autoconf/misc/logrotate.conf /etc/logrotate.conf
|
||||
COPY scripts/* /opt/scripts/
|
||||
|
||||
@@ -15,8 +15,12 @@ RUN apk add py3-pip apache2-utils bash certbot curl logrotate openssl && \
|
||||
mkdir /opt/entrypoint && \
|
||||
mkdir -p /opt/confs/site && \
|
||||
mkdir -p /opt/confs/global && \
|
||||
mkdir /opt/scripts && \
|
||||
addgroup -g 101 nginx && \
|
||||
adduser -h /var/cache/nginx -g nginx -s /sbin/nologin -G nginx -D -H -u 101 nginx && \
|
||||
mkdir /etc/letsencrypt && \
|
||||
chown root:nginx /etc/letsencrypt && \
|
||||
chmod 770 /etc/letsencrypt && \
|
||||
mkdir /var/log/letsencrypt && \
|
||||
chown root:nginx /var/log/letsencrypt && \
|
||||
chmod 770 /var/log/letsencrypt && \
|
||||
@@ -30,7 +34,10 @@ RUN apk add py3-pip apache2-utils bash certbot curl logrotate openssl && \
|
||||
chown root:nginx /var/log/jobs.log && \
|
||||
chmod 770 /var/log/jobs.log && \
|
||||
chown -R root:nginx /opt/confs/nginx && \
|
||||
chmod -R 770 /opt/confs/nginx
|
||||
chmod -R 770 /opt/confs/nginx && \
|
||||
mkdir /acme-challenge && \
|
||||
chown root:nginx /acme-challenge && \
|
||||
chmod 770 /acme-challenge
|
||||
|
||||
COPY autoconf/misc/logrotate.conf /etc/logrotate.conf
|
||||
COPY scripts/* /opt/scripts/
|
||||
|
||||
@@ -9,8 +9,12 @@ RUN apk add py3-pip apache2-utils bash certbot curl logrotate openssl && \
|
||||
mkdir /opt/entrypoint && \
|
||||
mkdir -p /opt/confs/site && \
|
||||
mkdir -p /opt/confs/global && \
|
||||
mkdir /opt/scripts && \
|
||||
addgroup -g 101 nginx && \
|
||||
adduser -h /var/cache/nginx -g nginx -s /sbin/nologin -G nginx -D -H -u 101 nginx && \
|
||||
mkdir /etc/letsencrypt && \
|
||||
chown root:nginx /etc/letsencrypt && \
|
||||
chmod 770 /etc/letsencrypt && \
|
||||
mkdir /var/log/letsencrypt && \
|
||||
chown root:nginx /var/log/letsencrypt && \
|
||||
chmod 770 /var/log/letsencrypt && \
|
||||
@@ -24,7 +28,10 @@ RUN apk add py3-pip apache2-utils bash certbot curl logrotate openssl && \
|
||||
chown root:nginx /var/log/jobs.log && \
|
||||
chmod 770 /var/log/jobs.log && \
|
||||
chown -R root:nginx /opt/confs/nginx && \
|
||||
chmod -R 770 /opt/confs/nginx
|
||||
chmod -R 770 /opt/confs/nginx && \
|
||||
mkdir /acme-challenge && \
|
||||
chown root:nginx /acme-challenge && \
|
||||
chmod 770 /acme-challenge
|
||||
|
||||
COPY autoconf/misc/logrotate.conf /etc/logrotate.conf
|
||||
COPY scripts/* /opt/scripts/
|
||||
|
||||
@@ -44,6 +44,7 @@ with lock :
|
||||
|
||||
# Process events received from Docker
|
||||
try :
|
||||
utils.log("[*] Listening for Docker events ...")
|
||||
for event in client.events(decode=True) :
|
||||
|
||||
# Process only container/service events
|
||||
@@ -53,11 +54,15 @@ try :
|
||||
# Get Container/Service object
|
||||
try :
|
||||
if swarm :
|
||||
server = client.services.get(service_id=event["Actor"]["ID"])
|
||||
id = service_id=event["Actor"]["ID"]
|
||||
server = client.services.get(service_id=id)
|
||||
else :
|
||||
server = client.containers.get(event["id"])
|
||||
id = event["id"]
|
||||
server = client.containers.get(id)
|
||||
except docker.errors.NotFound as e :
|
||||
continue
|
||||
server = autoconf.get_server(id)
|
||||
if not server :
|
||||
continue
|
||||
|
||||
# Process the event
|
||||
with lock :
|
||||
|
||||
@@ -2,6 +2,12 @@
|
||||
|
||||
echo "[*] Starting autoconf ..."
|
||||
|
||||
# check permissions
|
||||
su -s "/opt/entrypoint/permissions.sh" nginx
|
||||
if [ "$?" -ne 0 ] ; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$SWARM_MODE" = "yes" ] ; then
|
||||
cp -r /opt/confs/nginx/* /etc/nginx
|
||||
chown -R root:nginx /etc/nginx
|
||||
|
||||
@@ -136,6 +136,10 @@ sed -i 's/^API_KEY=.*/API_KEY=%CROWDSEC_KEY%/' /usr/local/lib/lua/crowdsec/crowd
|
||||
sed -i 's/require "lrucache"/require "resty.lrucache"/' /usr/local/lib/lua/crowdsec/CrowdSec.lua
|
||||
sed -i 's/require "config"/require "crowdsec.config"/' /usr/local/lib/lua/crowdsec/CrowdSec.lua
|
||||
cd /tmp
|
||||
git_secure_clone https://github.com/hamishforbes/lua-resty-iputils.git 3151d6485e830421266eee5c0f386c32c835dba4
|
||||
cd lua-resty-iputils
|
||||
make LUA_LIB_DIR=/usr/local/lib/lua install
|
||||
cd /tmp
|
||||
git_secure_clone https://github.com/openresty/lua-nginx-module.git 2d23bc4f0a29ed79aaaa754c11bffb1080aa44ba
|
||||
export LUAJIT_LIB=/usr/local/lib
|
||||
export LUAJIT_INC=/usr/local/include/luajit-2.1
|
||||
@@ -153,7 +157,7 @@ fi
|
||||
tar -xvzf nginx-${NGINX_VERSION}.tar.gz
|
||||
cd nginx-$NGINX_VERSION
|
||||
CONFARGS=$(nginx -V 2>&1 | sed -n -e 's/^.*arguments: //p')
|
||||
CONFARGS=${CONFARGS/-Os -fomit-frame-pointer/-Os}
|
||||
CONFARGS=${CONFARGS/-Os -fomit-frame-pointer -g/-Os}
|
||||
./configure $CONFARGS --add-dynamic-module=/tmp/ModSecurity-nginx --add-dynamic-module=/tmp/headers-more-nginx-module --add-dynamic-module=/tmp/ngx_http_geoip2_module --add-dynamic-module=/tmp/nginx_cookie_flag_module --add-dynamic-module=/tmp/lua-nginx-module --add-dynamic-module=/tmp/ngx_brotli
|
||||
make -j $NTASK modules
|
||||
cp ./objs/*.so /usr/lib/nginx/modules
|
||||
|
||||
@@ -13,7 +13,7 @@ rewrite_by_lua_block {
|
||||
if api.is_api_call(api_uri) then
|
||||
ngx.header.content_type = 'text/plain'
|
||||
if api.do_api_call(api_uri) then
|
||||
ngx.log(ngx.WARN, "[API] API call " .. ngx.var.request_uri .. " successfull from " .. ngx.var.remote_addr)
|
||||
ngx.log(ngx.NOTICE, "[API] API call " .. ngx.var.request_uri .. " successfull from " .. ngx.var.remote_addr)
|
||||
ngx.say("ok")
|
||||
else
|
||||
ngx.log(ngx.WARN, "[API] API call " .. ngx.var.request_uri .. " failed from " .. ngx.var.remote_addr)
|
||||
|
||||
@@ -6,7 +6,7 @@ rewrite_by_lua_block {
|
||||
if api.is_api_call(api_uri) then
|
||||
ngx.header.content_type = 'text/plain'
|
||||
if api.do_api_call(api_uri) then
|
||||
ngx.log(ngx.WARN, "[API] API call " .. ngx.var.request_uri .. " successfull from " .. ngx.var.remote_addr)
|
||||
ngx.log(ngx.NOTICE, "[API] API call " .. ngx.var.request_uri .. " successfull from " .. ngx.var.remote_addr)
|
||||
ngx.say("ok")
|
||||
else
|
||||
ngx.log(ngx.WARN, "[API] API call " .. ngx.var.request_uri .. " failed from " .. ngx.var.remote_addr)
|
||||
|
||||
@@ -5,5 +5,5 @@ init_by_lua_block {
|
||||
ngx.log(ngx.ERR, "[Crowdsec] " .. err)
|
||||
error()
|
||||
end
|
||||
ngx.log(ngx.ERR, "[Crowdsec] Initialisation done")
|
||||
ngx.log(ngx.NOTICE, "[Crowdsec] Initialisation done")
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ http {
|
||||
# write logs to local syslog
|
||||
log_format logf '%LOG_FORMAT%';
|
||||
access_log syslog:server=unix:/tmp/log,nohostname,facility=local0,severity=notice logf;
|
||||
error_log syslog:server=unix:/tmp/log,nohostname,facility=local0 warn;
|
||||
error_log syslog:server=unix:/tmp/log,nohostname,facility=local0 notice;
|
||||
|
||||
# temp paths
|
||||
proxy_temp_path /tmp/proxy_temp;
|
||||
|
||||
@@ -7,7 +7,7 @@ location = %ANTIBOT_URI% {
|
||||
local cookie = require "cookie"
|
||||
local captcha = require "captcha"
|
||||
if not cookie.is_set("uri") then
|
||||
ngx.log(ngx.WARN, "[ANTIBOT] captcha fail (1) for " .. ngx.var.remote_addr)
|
||||
ngx.log(ngx.NOTICE, "[ANTIBOT] captcha fail (1) for " .. ngx.var.remote_addr)
|
||||
return ngx.exit(ngx.HTTP_FORBIDDEN)
|
||||
end
|
||||
local img, res = captcha.get_challenge()
|
||||
@@ -22,19 +22,19 @@ location = %ANTIBOT_URI% {
|
||||
local cookie = require "cookie"
|
||||
local captcha = require "captcha"
|
||||
if not cookie.is_set("captchares") then
|
||||
ngx.log(ngx.WARN, "[ANTIBOT] captcha fail (2) for " .. ngx.var.remote_addr)
|
||||
ngx.log(ngx.NOTICE, "[ANTIBOT] captcha fail (2) for " .. ngx.var.remote_addr)
|
||||
return ngx.exit(ngx.HTTP_FORBIDDEN)
|
||||
end
|
||||
ngx.req.read_body()
|
||||
local args, err = ngx.req.get_post_args(1)
|
||||
if err == "truncated" or not args or not args["captcha"] then
|
||||
ngx.log(ngx.WARN, "[ANTIBOT] captcha fail (3) for " .. ngx.var.remote_addr)
|
||||
ngx.log(ngx.NOTICE, "[ANTIBOT] captcha fail (3) for " .. ngx.var.remote_addr)
|
||||
return ngx.exit(ngx.HTTP_FORBIDDEN)
|
||||
end
|
||||
local captcha_user = args["captcha"]
|
||||
local check = captcha.check(captcha_user, cookie.get("captchares"))
|
||||
if not check then
|
||||
ngx.log(ngx.WARN, "[ANTIBOT] captcha fail (4) for " .. ngx.var.remote_addr)
|
||||
ngx.log(ngx.NOTICE, "[ANTIBOT] captcha fail (4) for " .. ngx.var.remote_addr)
|
||||
return ngx.redirect("%ANTIBOT_URI%")
|
||||
end
|
||||
cookie.set({captcha = "ok"})
|
||||
|
||||
@@ -7,7 +7,7 @@ location = %ANTIBOT_URI% {
|
||||
local cookie = require "cookie"
|
||||
local recaptcha = require "recaptcha"
|
||||
if not cookie.is_set("uri") then
|
||||
ngx.log(ngx.WARN, "[ANTIBOT] recaptcha fail (1) for " .. ngx.var.remote_addr)
|
||||
ngx.log(ngx.NOTICE, "[ANTIBOT] recaptcha fail (1) for " .. ngx.var.remote_addr)
|
||||
return ngx.exit(ngx.HTTP_FORBIDDEN)
|
||||
end
|
||||
local code = recaptcha.get_code("%ANTIBOT_URI%", "%ANTIBOT_RECAPTCHA_SITEKEY%")
|
||||
@@ -20,19 +20,19 @@ location = %ANTIBOT_URI% {
|
||||
local cookie = require "cookie"
|
||||
local recaptcha = require "recaptcha"
|
||||
if not cookie.is_set("uri") then
|
||||
ngx.log(ngx.WARN, "[ANTIBOT] recaptcha fail (2) for " .. ngx.var.remote_addr)
|
||||
ngx.log(ngx.NOTICE, "[ANTIBOT] recaptcha fail (2) for " .. ngx.var.remote_addr)
|
||||
return ngx.exit(ngx.HTTP_FORBIDDEN)
|
||||
end
|
||||
ngx.req.read_body()
|
||||
local args, err = ngx.req.get_post_args(1)
|
||||
if err == "truncated" or not args or not args["token"] then
|
||||
ngx.log(ngx.WARN, "[ANTIBOT] recaptcha fail (3) for " .. ngx.var.remote_addr)
|
||||
ngx.log(ngx.NOTICE, "[ANTIBOT] recaptcha fail (3) for " .. ngx.var.remote_addr)
|
||||
return ngx.exit(ngx.HTTP_FORBIDDEN)
|
||||
end
|
||||
local token = args["token"]
|
||||
local check = recaptcha.check(token, "%ANTIBOT_RECAPTCHA_SECRET%")
|
||||
if check < %ANTIBOT_RECAPTCHA_SCORE% then
|
||||
ngx.log(ngx.WARN, "[ANTIBOT] recaptcha fail (4) for " .. ngx.var.remote_addr .. " (score = " .. tostring(check) .. ")")
|
||||
ngx.log(ngx.NOTICE, "[ANTIBOT] recaptcha fail (4) for " .. ngx.var.remote_addr .. " (score = " .. tostring(check) .. ")")
|
||||
return ngx.exit(ngx.HTTP_FORBIDDEN)
|
||||
end
|
||||
cookie.set({recaptcha = "ok"})
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
location ~* \.(%CLIENT_CACHE_EXTENSIONS%)$ {
|
||||
etag %CLIENT_CACHE_ETAG%;
|
||||
add_header Cache-Control "%CLIENT_CACHE_CONTROL%";
|
||||
etag %CLIENT_CACHE_ETAG%;
|
||||
set $cache "";
|
||||
if ($uri ~* \.(%CLIENT_CACHE_EXTENSIONS%)$) {
|
||||
set $cache "%CLIENT_CACHE_CONTROL%";
|
||||
}
|
||||
add_header Cache-Control $cache;
|
||||
|
||||
@@ -19,7 +19,6 @@ local use_antibot_captcha = %USE_ANTIBOT_CAPTCHA%
|
||||
local use_antibot_recaptcha = %USE_ANTIBOT_RECAPTCHA%
|
||||
|
||||
-- include LUA code
|
||||
|
||||
local whitelist = require "whitelist"
|
||||
local blacklist = require "blacklist"
|
||||
local dnsbl = require "dnsbl"
|
||||
@@ -31,11 +30,7 @@ local recaptcha = require "recaptcha"
|
||||
-- user variables
|
||||
local antibot_uri = "%ANTIBOT_URI%"
|
||||
local whitelist_user_agent = {%WHITELIST_USER_AGENT%}
|
||||
|
||||
-- check if it's let's encrypt bot
|
||||
if use_lets_encrypt and string.match(ngx.var.request_uri, "^/.well-known/acme-challenge/") then
|
||||
ngx.exit(ngx.OK)
|
||||
end
|
||||
local whitelist_uri = {%WHITELIST_URI%}
|
||||
|
||||
-- check if already in whitelist cache
|
||||
if use_whitelist_ip and whitelist.ip_cached_ok() then
|
||||
@@ -72,6 +67,19 @@ if use_whitelist_reverse and not whitelist.reverse_cached() then
|
||||
end
|
||||
end
|
||||
|
||||
-- check if URI is whitelisted
|
||||
for k, v in pairs(whitelist_uri) do
|
||||
if ngx.var.request_uri == v then
|
||||
ngx.log(ngx.NOTICE, "[WHITELIST] URI " .. v .. " is whitelisted")
|
||||
ngx.exit(ngx.OK)
|
||||
end
|
||||
end
|
||||
|
||||
-- check if it's certbot
|
||||
if use_lets_encrypt and string.match(ngx.var.request_uri, "^/.well-known/acme-challenge/") then
|
||||
ngx.exit(ngx.OK)
|
||||
end
|
||||
|
||||
-- check if IP is blacklisted (only if not in cache)
|
||||
if use_blacklist_ip and not blacklist.ip_cached() then
|
||||
if blacklist.check_ip() then
|
||||
@@ -88,32 +96,29 @@ end
|
||||
|
||||
-- check if user-agent is allowed
|
||||
if use_user_agent and ngx.var.bad_user_agent == "yes" then
|
||||
local headers = ngx.req.get_headers()
|
||||
local ua = headers["User-Agent"]
|
||||
if not whitelist_user_agent ~= "" then
|
||||
local k_ua_white, v_ua_white = next(whitelist_user_agent, nil)
|
||||
while v_ua_white do
|
||||
local rst_whitelist = string.match(ua, v_ua_white)
|
||||
if rst_whitelist ~= nil and rst_whitelist ~= "" then
|
||||
ngx.log(ngx.WARN, "[ALLOW] User-Agent " .. ngx.var.http_user_agent .. " is whitelisted")
|
||||
ngx.exit(ngx.OK)
|
||||
end
|
||||
k_ua_white, v_ua_white = next(whitelist_user_agent, k_ua_white)
|
||||
local block = false
|
||||
for k, v in pairs(whitelist_user_agent) do
|
||||
if string.match(ngx.var.http_user_agent, v) then
|
||||
ngx.log(ngx.NOTICE, "[ALLOW] User-Agent " .. ngx.var.http_user_agent .. " is whitelisted")
|
||||
block = false
|
||||
break
|
||||
end
|
||||
end
|
||||
ngx.log(ngx.WARN, "[BLOCK] User-Agent " .. ngx.var.http_user_agent .. " is blacklisted")
|
||||
ngx.exit(ngx.HTTP_FORBIDDEN)
|
||||
if block then
|
||||
ngx.log(ngx.NOTICE, "[BLOCK] User-Agent " .. ngx.var.http_user_agent .. " is blacklisted")
|
||||
ngx.exit(ngx.HTTP_FORBIDDEN)
|
||||
end
|
||||
end
|
||||
|
||||
-- check if referrer is allowed
|
||||
if use_referrer and ngx.var.bad_referrer == "yes" then
|
||||
ngx.log(ngx.WARN, "[BLOCK] Referrer " .. ngx.var.http_referer .. " is blacklisted")
|
||||
ngx.log(ngx.NOTICE, "[BLOCK] Referrer " .. ngx.var.http_referer .. " is blacklisted")
|
||||
ngx.exit(ngx.HTTP_FORBIDDEN)
|
||||
end
|
||||
|
||||
-- check if country is allowed
|
||||
if use_country and ngx.var.allowed_country == "no" then
|
||||
ngx.log(ngx.WARN, "[BLOCK] Country of " .. ngx.var.remote_addr .. " is blacklisted")
|
||||
ngx.log(ngx.NOTICE, "[BLOCK] Country of " .. ngx.var.remote_addr .. " is blacklisted")
|
||||
ngx.exit(ngx.HTTP_FORBIDDEN)
|
||||
end
|
||||
|
||||
@@ -131,7 +136,7 @@ if use_crowdsec then
|
||||
ngx.log(ngx.ERR, "[Crowdsec] " .. err)
|
||||
end
|
||||
if not ok then
|
||||
ngx.log(ngx.ERR, "[Crowdsec] denied '" .. ngx.var.remote_addr .. "'")
|
||||
ngx.log(ngx.NOTICE, "[Crowdsec] denied '" .. ngx.var.remote_addr .. "'")
|
||||
ngx.exit(ngx.HTTP_FORBIDDEN)
|
||||
end
|
||||
end
|
||||
@@ -143,7 +148,7 @@ if use_antibot_cookie then
|
||||
cookie.set({uri = ngx.var.request_uri})
|
||||
return ngx.redirect(antibot_uri)
|
||||
end
|
||||
ngx.log(ngx.WARN, "[ANTIBOT] cookie fail for " .. ngx.var.remote_addr)
|
||||
ngx.log(ngx.NOTICE, "[ANTIBOT] cookie fail for " .. ngx.var.remote_addr)
|
||||
return ngx.exit(ngx.HTTP_FORBIDDEN)
|
||||
else
|
||||
if ngx.var.request_uri == antibot_uri then
|
||||
|
||||
@@ -50,7 +50,6 @@ SecResponseBodyLimitAction ProcessPartial
|
||||
|
||||
# log usefull stuff
|
||||
SecAuditEngine RelevantOnly
|
||||
SecAuditLogRelevantStatus "^(?:5|4(?!04))"
|
||||
SecAuditLogType Serial
|
||||
SecAuditLog /var/log/nginx/modsec_audit.log
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
location %REVERSE_PROXY_URL% {
|
||||
etag off;
|
||||
proxy_pass %REVERSE_PROXY_HOST%;
|
||||
%REVERSE_PROXY_HEADERS%
|
||||
%REVERSE_PROXY_WS%
|
||||
|
||||
@@ -102,11 +102,11 @@ BLACKLIST_REVERSE_LIST="${BLACKLIST_REVERSE_LIST-.shodan.io}"
|
||||
USE_DNSBL="${USE_DNSBL-yes}"
|
||||
DNSBL_LIST="${DNSBL_LIST-bl.blocklist.de problems.dnsbl.sorbs.net sbl.spamhaus.org xbl.spamhaus.org}"
|
||||
USE_LIMIT_REQ="${USE_LIMIT_REQ-yes}"
|
||||
LIMIT_REQ_RATE="${LIMIT_REQ_RATE-20r/s}"
|
||||
LIMIT_REQ_BURST="${LIMIT_REQ_BURST-40}"
|
||||
LIMIT_REQ_RATE="${LIMIT_REQ_RATE-1r/s}"
|
||||
LIMIT_REQ_BURST="${LIMIT_REQ_BURST-2}"
|
||||
LIMIT_REQ_CACHE="${LIMIT_REQ_CACHE-10m}"
|
||||
USE_LIMIT_CONN="${USE_LIMIT_CONN-yes}"
|
||||
LIMIT_CONN_MAX="${LIMIT_CONN_MAX-40}"
|
||||
LIMIT_CONN_MAX="${LIMIT_CONN_MAX-50}"
|
||||
LIMIT_CONN_CACHE="${LIMIT_CONN_CACHE-10m}"
|
||||
PROXY_REAL_IP="${PROXY_REAL_IP-no}"
|
||||
PROXY_REAL_IP_FROM="${PROXY_REAL_IP_FROM-192.168.0.0/16 172.16.0.0/12 10.0.0.0/8}"
|
||||
@@ -128,4 +128,5 @@ ANTIBOT_SESSION_SECRET="${ANTIBOT_SESSION_SECRET-random}"
|
||||
USE_CROWDSEC="${USE_CROWDSEC-no}"
|
||||
USE_API="${USE_API-no}"
|
||||
API_URI="${API_URI-random}"
|
||||
API_WHITELIST_IP="${API_WHITELIST_IP-192.168.0.0/16 172.16.0.0/12 10.0.0.0/8}"
|
||||
SWARM_MODE="${SWARM_MODE-no}"
|
||||
|
||||
@@ -52,6 +52,16 @@ if [ ! -f "/opt/installed" ] ; then
|
||||
|
||||
echo "[*] Configuring bunkerized-nginx ..."
|
||||
|
||||
# check permissions
|
||||
if [ "$SWARM_MODE" = "no" ] ; then
|
||||
/opt/entrypoint/permissions.sh
|
||||
else
|
||||
/opt/entrypoint/permissions-swarm.sh
|
||||
fi
|
||||
if [ "$?" -ne 0 ] ; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# logs config
|
||||
/opt/entrypoint/logs.sh
|
||||
|
||||
@@ -122,7 +132,7 @@ else
|
||||
fi
|
||||
|
||||
# list of log files to display
|
||||
LOGS="/var/log/access.log /var/log/error.log /var/log/jobs.log"
|
||||
LOGS="/var/log/access.log /var/log/error.log /var/log/jobs.log /var/log/nginx/modsec_audit.log"
|
||||
|
||||
# start fail2ban
|
||||
if [ "$USE_FAIL2BAN" = "yes" ] ; then
|
||||
|
||||
@@ -10,14 +10,18 @@
|
||||
cp /opt/confs/global/* /etc/nginx/
|
||||
|
||||
# include server block(s)
|
||||
if [ "$MULTISITE" = "yes" ] ; then
|
||||
includes=""
|
||||
for server in $SERVER_NAME ; do
|
||||
includes="${includes}include /etc/nginx/${server}/server.conf;\n"
|
||||
done
|
||||
replace_in_file "/etc/nginx/nginx.conf" "%INCLUDE_SERVER%" "$includes"
|
||||
if [ "$SWARM_MODE" = "no" ] ; then
|
||||
if [ "$MULTISITE" = "yes" ] ; then
|
||||
includes=""
|
||||
for server in $SERVER_NAME ; do
|
||||
includes="${includes}include /etc/nginx/${server}/server.conf;\n"
|
||||
done
|
||||
replace_in_file "/etc/nginx/nginx.conf" "%INCLUDE_SERVER%" "$includes"
|
||||
else
|
||||
replace_in_file "/etc/nginx/nginx.conf" "%INCLUDE_SERVER%" "include /etc/nginx/server.conf;"
|
||||
fi
|
||||
else
|
||||
replace_in_file "/etc/nginx/nginx.conf" "%INCLUDE_SERVER%" "include /etc/nginx/server.conf;"
|
||||
replace_in_file "/etc/nginx/nginx.conf" "%INCLUDE_SERVER%" ""
|
||||
fi
|
||||
|
||||
# setup default server block if multisite
|
||||
@@ -171,7 +175,7 @@ fi
|
||||
|
||||
# request limiting
|
||||
if [ "$(has_value USE_LIMIT_REQ yes)" != "" ] ; then
|
||||
replace_in_file "/etc/nginx/nginx.conf" "%LIMIT_REQ_ZONE%" "limit_req_zone \$binary_remote_addr zone=limit:${LIMIT_REQ_CACHE} rate=${LIMIT_REQ_RATE};"
|
||||
replace_in_file "/etc/nginx/nginx.conf" "%LIMIT_REQ_ZONE%" "limit_req_zone \$binary_remote_addr\$uri zone=limit:${LIMIT_REQ_CACHE} rate=${LIMIT_REQ_RATE};"
|
||||
else
|
||||
replace_in_file "/etc/nginx/nginx.conf" "%LIMIT_REQ_ZONE%" ""
|
||||
fi
|
||||
|
||||
@@ -19,3 +19,10 @@ touch /var/log/jobs.log
|
||||
replace_in_file "/etc/logrotate.conf" "%LOGROTATE_MAXAGE%" "$LOGROTATE_MAXAGE"
|
||||
replace_in_file "/etc/logrotate.conf" "%LOGROTATE_MINSIZE%" "$LOGROTATE_MINSIZE"
|
||||
echo "$LOGROTATE_CRON /opt/scripts/logrotate.sh > /dev/null 2>&1" >> /etc/crontabs/nginx
|
||||
|
||||
# setup rsyslog
|
||||
if [ "$REMOTE_SYSLOG" != "" ] ; then
|
||||
replace_in_file "/etc/rsyslog.conf" "%REMOTE_SYSLOG%" "local0.* @${REMOTE_SYSLOG};rawFormat"
|
||||
else
|
||||
replace_in_file "/etc/rsyslog.conf" "%REMOTE_SYSLOG%" ""
|
||||
fi
|
||||
|
||||
@@ -38,3 +38,9 @@ if [ "$(has_value USE_CROWDSEC yes)" != "" ] ; then
|
||||
replace_in_file "/usr/local/lib/lua/crowdsec/crowdsec.conf" "%CROWDSEC_HOST%" "$CROWDSEC_HOST"
|
||||
replace_in_file "/usr/local/lib/lua/crowdsec/crowdsec.conf" "%CROWDSEC_KEY%" "$CROWDSEC_KEY"
|
||||
fi
|
||||
|
||||
# Whitelist IP for API
|
||||
if [ "$USE_API" = "yes" ] ; then
|
||||
list=$(spaces_to_lua "$API_WHITELIST_IP")
|
||||
replace_in_file "/usr/local/lib/lua/api.lua" "%API_WHITELIST_IP%" "$list"
|
||||
fi
|
||||
|
||||
@@ -14,7 +14,8 @@ if [ "$MULTISITE" = "yes" ] ; then
|
||||
fi
|
||||
SERVER_PREFIX="/etc/nginx/${server}/"
|
||||
if grep "/etc/letsencrypt/live" ${SERVER_PREFIX}https.conf > /dev/null && [ ! -f /etc/letsencrypt/live/${server}/fullchain.pem ] ; then
|
||||
/opt/scripts/certbot-new.sh "$server" "$(cat ${SERVER_PREFIX}email-lets-encrypt.txt)"
|
||||
domains=$(cat ${SERVER_PREFIX}server.conf | sed -nE 's/^.*server_name (.*);$/\1/p' | sed "s/ /,/g")
|
||||
/opt/scripts/certbot-new.sh "$domains" "$(cat ${SERVER_PREFIX}email-lets-encrypt.txt)"
|
||||
fi
|
||||
if grep "modsecurity.conf" ${SERVER_PREFIX}server.conf > /dev/null ; then
|
||||
modsec_custom=""
|
||||
|
||||
25
entrypoint/permissions-swarm.sh
Normal file
25
entrypoint/permissions-swarm.sh
Normal file
@@ -0,0 +1,25 @@
|
||||
#!/bin/bash
|
||||
|
||||
# /etc/letsencrypt
|
||||
if [ ! -r "/etc/letsencrypt" ] || [ ! -x "/etc/letsencrypt" ] ; then
|
||||
echo "[!] WARNING - wrong permissions on /etc/letsencrypt"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# /www
|
||||
if [ ! -r "/www" ] || [ ! -x "/www" ] ; then
|
||||
echo "[!] ERROR - wrong permissions on /www"
|
||||
exit 2
|
||||
fi
|
||||
|
||||
# /etc/nginx
|
||||
if [ ! -r "/etc/nginx" ] || [ ! -x "/etc/nginx" ] ; then
|
||||
echo "[!] ERROR - wrong permissions on /etc/nginx"
|
||||
exit 3
|
||||
fi
|
||||
|
||||
# /acme-challenge
|
||||
if [ ! -r "/acme-challenge" ] || [ ! -x "/acme-challenge" ] ; then
|
||||
echo "[!] ERROR - wrong permissions on /acme-challenge"
|
||||
exit 4
|
||||
fi
|
||||
29
entrypoint/permissions.sh
Normal file
29
entrypoint/permissions.sh
Normal file
@@ -0,0 +1,29 @@
|
||||
#!/bin/bash
|
||||
|
||||
# /etc/letsencrypt
|
||||
if [ ! -w "/etc/letsencrypt" ] || [ ! -r "/etc/letsencrypt" ] || [ ! -x "/etc/letsencrypt" ] ; then
|
||||
echo "[!] WARNING - wrong permissions on /etc/letsencrypt"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -f "/usr/sbin/nginx" ] ; then
|
||||
# /www
|
||||
if [ ! -r "/www" ] || [ ! -x "/www" ] ; then
|
||||
echo "[!] ERROR - wrong permissions on /www"
|
||||
exit 2
|
||||
fi
|
||||
|
||||
fi
|
||||
|
||||
# /acme-challenge
|
||||
if [ ! -w "/acme-challenge" ] || [ ! -r "/acme-challenge" ] || [ ! -x "/acme-challenge" ] ; then
|
||||
echo "[!] ERROR - wrong permissions on /acme-challenge"
|
||||
exit 3
|
||||
fi
|
||||
|
||||
# /etc/nginx
|
||||
if [ ! -w "/etc/nginx" ] || [ ! -r "/etc/nginx" ] || [ ! -x "/etc/nginx" ] ; then
|
||||
echo "[!] ERROR - wrong permissions on /etc/nginx"
|
||||
exit 4
|
||||
fi
|
||||
|
||||
@@ -9,16 +9,14 @@
|
||||
# get nginx path and override multisite variables
|
||||
NGINX_PREFIX="/etc/nginx/"
|
||||
if [ "$MULTISITE" = "yes" ] ; then
|
||||
NGINX_PREFIX="${NGINX_PREFIX}${1}/"
|
||||
first_server=$(echo "$1" | cut -d ' ' -f 1)
|
||||
NGINX_PREFIX="${NGINX_PREFIX}${first_server}/"
|
||||
if [ ! -d "$NGINX_PREFIX" ] ; then
|
||||
mkdir "$NGINX_PREFIX"
|
||||
fi
|
||||
ROOT_FOLDER="${ROOT_FOLDER}/$1"
|
||||
fi
|
||||
|
||||
if [ "$MULTISITE" = "yes" ] ; then
|
||||
for var in $(env | cut -d '=' -f 1 | grep -E "^${1}_") ; do
|
||||
repl_name=$(echo "$var" | sed "s~${1}_~~")
|
||||
ROOT_FOLDER="${ROOT_FOLDER}/$first_server"
|
||||
for var in $(env | cut -d '=' -f 1 | grep -E "^${first_server}_") ; do
|
||||
repl_name=$(echo "$var" | sed "s~${first_server}_~~")
|
||||
repl_value=$(env | grep -E "^${var}=" | sed "s~^${var}=~~")
|
||||
read -r "$repl_name" <<< $repl_value
|
||||
done
|
||||
@@ -27,9 +25,9 @@ fi
|
||||
set | grep -E -v "^(HOSTNAME|PWD|PKG_RELEASE|NJS_VERSION|SHLVL|PATH|_|NGINX_VERSION|HOME)=" > "${NGINX_PREFIX}nginx.env"
|
||||
if [ "$MULTISITE" = "yes" ] ; then
|
||||
for server in $SERVER_NAME ; do
|
||||
sed -i "~^${server}_.*=.*~d" "${NGINX_PREFIX}nginx.env"
|
||||
sed -i "/^${server}_.*=.*/d" "${NGINX_PREFIX}nginx.env"
|
||||
done
|
||||
sed -i "~^SERVER_NAME=.*~SERVER_NAME=${1}~" "${NGINX_PREFIX}nginx.env"
|
||||
sed -i "s~^SERVER_NAME=.*~SERVER_NAME=${1}~" "${NGINX_PREFIX}nginx.env"
|
||||
fi
|
||||
|
||||
# copy stub confs
|
||||
@@ -38,8 +36,8 @@ cp /opt/confs/site/* "$NGINX_PREFIX"
|
||||
# replace paths
|
||||
replace_in_file "${NGINX_PREFIX}server.conf" "%MAIN_LUA%" "include ${NGINX_PREFIX}main-lua.conf;"
|
||||
if [ "$MULTISITE" = "yes" ] ; then
|
||||
replace_in_file "${NGINX_PREFIX}server.conf" "%SERVER_CONF%" "include /server-confs/*.conf;\ninclude /server-confs/${1}/*.conf;"
|
||||
replace_in_file "${NGINX_PREFIX}server.conf" "%PRE_SERVER_CONF%" "include /pre-server-confs/*.conf;\ninclude /pre-server-confs/${1}/*.conf;"
|
||||
replace_in_file "${NGINX_PREFIX}server.conf" "%SERVER_CONF%" "include /server-confs/*.conf;\ninclude /server-confs/${first_server}/*.conf;"
|
||||
replace_in_file "${NGINX_PREFIX}server.conf" "%PRE_SERVER_CONF%" "include /pre-server-confs/*.conf;\ninclude /pre-server-confs/${first_server}/*.conf;"
|
||||
else
|
||||
replace_in_file "${NGINX_PREFIX}server.conf" "%SERVER_CONF%" "include /server-confs/*.conf;"
|
||||
replace_in_file "${NGINX_PREFIX}server.conf" "%PRE_SERVER_CONF%" "include /pre-server-confs/*.conf;"
|
||||
@@ -67,10 +65,10 @@ if [ "$USE_REVERSE_PROXY" = "yes" ] ; then
|
||||
replace_in_file "${NGINX_PREFIX}reverse-proxy-${i}.conf" "%REVERSE_PROXY_URL%" "$url_value"
|
||||
replace_in_file "${NGINX_PREFIX}reverse-proxy-${i}.conf" "%REVERSE_PROXY_HOST%" "$host_value"
|
||||
if [ "$custom_headers_value" != "" ] ; then
|
||||
IFS_=$IFS
|
||||
IFS_=$IFS
|
||||
IFS=';'
|
||||
for header_value in $(echo "$custom_headers_value") ; do
|
||||
replace_in_file "${NGINX_PREFIX}reverse-proxy-${i}.conf" "%REVERSE_PROXY_CUSTOM_HEADERS%" "more_set_headers $header_value;\n%REVERSE_PROXY_CUSTOM_HEADERS%"
|
||||
for header_value in $(echo $custom_headers_value) ; do
|
||||
replace_in_file "${NGINX_PREFIX}reverse-proxy-${i}.conf" "%REVERSE_PROXY_CUSTOM_HEADERS%" "proxy_set_header $header_value;\n%REVERSE_PROXY_CUSTOM_HEADERS%"
|
||||
done
|
||||
IFS=$IFS_
|
||||
fi
|
||||
@@ -288,6 +286,14 @@ else
|
||||
replace_in_file "${NGINX_PREFIX}main-lua.conf" "%WHITELIST_USER_AGENT%" ""
|
||||
fi
|
||||
|
||||
# whitelist URI
|
||||
if [ "$WHITELIST_URI" != "" ] ; then
|
||||
list=$(spaces_to_lua "$WHITELIST_URI")
|
||||
replace_in_file "${NGINX_PREFIX}main-lua.conf" "%WHITELIST_URI%" "$list"
|
||||
else
|
||||
replace_in_file "${NGINX_PREFIX}main-lua.conf" "%WHITELIST_URI%" ""
|
||||
fi
|
||||
|
||||
# block bad referrer
|
||||
if [ "$BLOCK_REFERRER" = "yes" ] ; then
|
||||
replace_in_file "${NGINX_PREFIX}main-lua.conf" "%USE_REFERRER%" "true"
|
||||
@@ -343,8 +349,8 @@ if [ "$AUTO_LETS_ENCRYPT" = "yes" ] || [ "$USE_CUSTOM_HTTPS" = "yes" ] || [ "$GE
|
||||
if [ "$MULTISITE" = "no" ] ; then
|
||||
FIRST_SERVER_NAME=$(echo "$SERVER_NAME" | cut -d " " -f 1)
|
||||
else
|
||||
FIRST_SERVER_NAME="$1"
|
||||
EMAIL_LETS_ENCRYPT="${EMAIL_LETS_ENCRYPT-contact@$1}"
|
||||
FIRST_SERVER_NAME="$first_server"
|
||||
EMAIL_LETS_ENCRYPT="${EMAIL_LETS_ENCRYPT-contact@$first_server}"
|
||||
echo -n "$EMAIL_LETS_ENCRYPT" > ${NGINX_PREFIX}email-lets-encrypt.txt
|
||||
fi
|
||||
replace_in_file "${NGINX_PREFIX}https.conf" "%HTTPS_CERT%" "/etc/letsencrypt/live/${FIRST_SERVER_NAME}/fullchain.pem"
|
||||
|
||||
@@ -20,7 +20,6 @@ services:
|
||||
- DISABLE_DEFAULT_SERVER=yes
|
||||
- USE_CLIENT_CACHE=yes
|
||||
- USE_GZIP=yes
|
||||
- USE_BROTLI=yes
|
||||
labels:
|
||||
- "bunkerized-nginx.AUTOCONF"
|
||||
|
||||
|
||||
@@ -18,8 +18,8 @@ services:
|
||||
- REDIRECT_HTTP_TO_HTTPS=yes
|
||||
- DISABLE_DEFAULT_SERVER=yes
|
||||
- USE_CLIENT_CACHE=yes
|
||||
- USE_PROXY_CACHE=yes
|
||||
- USE_GZIP=yes
|
||||
- USE_BROTLI=yes
|
||||
- USE_REVERSE_PROXY=yes
|
||||
labels:
|
||||
- "bunkerized-nginx.AUTOCONF"
|
||||
|
||||
@@ -18,7 +18,6 @@ services:
|
||||
- DISABLE_DEFAULT_SERVER=yes
|
||||
- USE_CLIENT_CACHE=yes
|
||||
- USE_GZIP=yes
|
||||
- USE_BROTLI=yes
|
||||
- REMOTE_PHP=myphp
|
||||
- REMOTE_PHP_PATH=/app
|
||||
|
||||
|
||||
@@ -23,7 +23,6 @@ services:
|
||||
- PROXY_REAL_IP=yes
|
||||
- USE_CLIENT_CACHE=yes
|
||||
- USE_GZIP=yes
|
||||
- USE_BROTLI=yes
|
||||
- REMOTE_PHP=myphp1
|
||||
- REMOTE_PHP_PATH=/app
|
||||
labels:
|
||||
@@ -41,7 +40,6 @@ services:
|
||||
- PROXY_REAL_IP=yes
|
||||
- USE_CLIENT_CACHE=yes
|
||||
- USE_GZIP=yes
|
||||
- USE_BROTLI=yes
|
||||
- REMOTE_PHP=myphp2
|
||||
- REMOTE_PHP_PATH=/app
|
||||
labels:
|
||||
|
||||
26
examples/certbot-wildcard/certbot-wildcard.sh
Executable file
26
examples/certbot-wildcard/certbot-wildcard.sh
Executable file
@@ -0,0 +1,26 @@
|
||||
#!/bin/sh
|
||||
|
||||
# you need to run it before starting bunkerized-nginx
|
||||
# since it's manual there is no auto renew, you need to run it again before it expires
|
||||
|
||||
DOMAIN="*.website.com"
|
||||
SERVICE="mywww"
|
||||
|
||||
# ask for wildcard certificate
|
||||
# it's interactive and you will need to add a DNS entry
|
||||
docker run --rm -it -v "${PWD}/letsencrypt:/etc/letsencrypt" certbot/certbot certonly --manual -d $DOMAIN --agree-tos
|
||||
if [ $? -ne 0 ] ; then
|
||||
echo "error while getting certificate for $DOMAIN"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# fix permissions
|
||||
chown -R 101:101 "${PWD}/letsencrypt"
|
||||
|
||||
# reload nginx if it's already running (in case of a "renew")
|
||||
if [ -z `docker-compose ps -q $SERVICE` ] || [ -z `docker ps -q --no-trunc | grep $(docker-compose ps -q $SERVICE)` ]; then
|
||||
echo "bunkerized-nginx is not running, skipping nginx reload"
|
||||
else
|
||||
echo "bunkerized-nginx is running, sending reload order"
|
||||
docker-compose exec $SERVICE nginx -s reload
|
||||
fi
|
||||
39
examples/certbot-wildcard/docker-compose.yml
Normal file
39
examples/certbot-wildcard/docker-compose.yml
Normal file
@@ -0,0 +1,39 @@
|
||||
version: '3'
|
||||
|
||||
services:
|
||||
|
||||
mywww:
|
||||
image: bunkerity/bunkerized-nginx
|
||||
restart: always
|
||||
ports:
|
||||
- 80:8080
|
||||
- 443:8443
|
||||
volumes:
|
||||
- ./web-files:/www:ro
|
||||
- ./letsencrypt:/letsencrypt:ro
|
||||
environment:
|
||||
- SERVER_NAME=app1.website.com app2.website.com # replace with your domains
|
||||
- MULTISITE=yes
|
||||
- USE_CUSTOM_HTTPS=yes
|
||||
- CUSTOM_HTTPS_CERT=/letsencrypt/live/website.com/fullchain.pem
|
||||
- CUSTOM_HTTPS_KEY=/letsencrypt/live/website.com/privkey.pem
|
||||
- REDIRECT_HTTP_TO_HTTPS=yes
|
||||
- DISABLE_DEFAULT_SERVER=yes
|
||||
- USE_CLIENT_CACHE=yes
|
||||
- USE_GZIP=yes
|
||||
- app1.website.com_REMOTE_PHP=myapp1
|
||||
- app1.website.com_REMOTE_PHP_PATH=/app
|
||||
- app2.website.com_REMOTE_PHP=myapp2
|
||||
- app2.website.com_REMOTE_PHP_PATH=/app
|
||||
|
||||
myapp1:
|
||||
image: php:fpm
|
||||
restart: always
|
||||
volumes:
|
||||
- ./web-files/app1.website.com:/app
|
||||
|
||||
myapp2:
|
||||
image: php:fpm
|
||||
restart: always
|
||||
volumes:
|
||||
- ./web-files/app2.website.com:/app
|
||||
@@ -0,0 +1,5 @@
|
||||
<?php
|
||||
|
||||
echo "hello from app1 !";
|
||||
|
||||
?>
|
||||
@@ -0,0 +1,5 @@
|
||||
<?php
|
||||
|
||||
echo "hello from app2 !";
|
||||
|
||||
?>
|
||||
@@ -1,3 +1,23 @@
|
||||
#!/bin/sh
|
||||
|
||||
# first, you need to run the crowdsec service
|
||||
echo "running crowdsec service ..."
|
||||
docker-compose up -d mycrowdsec
|
||||
|
||||
# wait a little until it's up
|
||||
sleep 10
|
||||
|
||||
# get the bouncer key
|
||||
docker-compose exec mycrowdsec cscli bouncers add MyBouncer
|
||||
|
||||
# enter the key into the CROWDSEC_KEY environment variable
|
||||
read -p -s "edit CROWDSEC_KEY env var in docker-compose.yml file and press enter"
|
||||
|
||||
# start all services
|
||||
docker-compose up -d
|
||||
|
||||
# wait a little until it's up
|
||||
sleep 10
|
||||
|
||||
# restart crowdsec so it reads the log files
|
||||
docker-compose restart mycrowdsec
|
||||
|
||||
@@ -20,7 +20,6 @@ services:
|
||||
- DISABLE_DEFAULT_SERVER=yes
|
||||
- USE_CLIENT_CACHE=yes
|
||||
- USE_GZIP=yes
|
||||
- USE_BROTLI=yes
|
||||
- USE_CROWDSEC=yes
|
||||
- CROWDSEC_HOST=http://mycrowdsec:8080
|
||||
- CROWDSEC_KEY= # you need to generate it (see bouncer_key.sh)
|
||||
@@ -34,7 +33,7 @@ services:
|
||||
- net2
|
||||
|
||||
mycrowdsec:
|
||||
image: crowdsecurity/crowdsec:v1.0.2
|
||||
image: crowdsecurity/crowdsec:v1.0.13
|
||||
restart: always
|
||||
volumes:
|
||||
- ./acquis.yaml:/etc/crowdsec/acquis.yaml
|
||||
|
||||
30
examples/ghost/docker-compose.yml
Normal file
30
examples/ghost/docker-compose.yml
Normal file
@@ -0,0 +1,30 @@
|
||||
version: '3'
|
||||
|
||||
services:
|
||||
|
||||
myreverse:
|
||||
image: bunkerity/bunkerized-nginx
|
||||
restart: always
|
||||
ports:
|
||||
- 80:8080
|
||||
- 443:8443
|
||||
volumes:
|
||||
- ./letsencrypt:/etc/letsencrypt
|
||||
environment:
|
||||
- SERVER_NAME=www.website.com # replace with your domain
|
||||
- SERVE_FILES=no
|
||||
- DISABLE_DEFAULT_SERVER=yes
|
||||
- REDIRECT_HTTP_TO_HTTPS=yes
|
||||
- AUTO_LETS_ENCRYPT=yes
|
||||
- USE_PROXY_CACHE=yes
|
||||
- USE_GZIP=yes
|
||||
- USE_REVERSE_PROXY=yes
|
||||
- REVERSE_PROXY_URL=/
|
||||
- REVERSE_PROXY_HOST=http://myghost:2368/
|
||||
|
||||
myghost:
|
||||
image: ghost:alpine
|
||||
volumes:
|
||||
- ./data-ghost:/var/lib/ghost/content
|
||||
environment:
|
||||
- url=https://www.website.com # replace with your domain
|
||||
30
examples/gogs/docker-compose.yml
Normal file
30
examples/gogs/docker-compose.yml
Normal file
@@ -0,0 +1,30 @@
|
||||
version: '3'
|
||||
|
||||
services:
|
||||
|
||||
myreverse:
|
||||
image: bunkerity/bunkerized-nginx
|
||||
restart: always
|
||||
ports:
|
||||
- 80:8080
|
||||
- 443:8443
|
||||
volumes:
|
||||
- ./letsencrypt:/etc/letsencrypt
|
||||
- ./modsec-crs-confs:/modsec-crs-confs:ro # fix FP with CRS
|
||||
environment:
|
||||
- SERVER_NAME=www.website.com # replace with your domain
|
||||
- SERVE_FILES=no
|
||||
- DISABLE_DEFAULT_SERVER=yes
|
||||
- REDIRECT_HTTP_TO_HTTPS=yes
|
||||
- AUTO_LETS_ENCRYPT=yes
|
||||
- USE_PROXY_CACHE=yes
|
||||
- USE_CLIENT_CACHE=yes
|
||||
- USE_GZIP=yes
|
||||
- USE_REVERSE_PROXY=yes
|
||||
- REVERSE_PROXY_URL=/
|
||||
- REVERSE_PROXY_HOST=http://mygogs:3000/
|
||||
|
||||
mygogs:
|
||||
image: gogs/gogs
|
||||
volumes:
|
||||
- ./data-gogs:/data
|
||||
7
examples/gogs/modsec-crs-confs/gogs.conf
Normal file
7
examples/gogs/modsec-crs-confs/gogs.conf
Normal file
@@ -0,0 +1,7 @@
|
||||
SecAction \
|
||||
"id:900220,\
|
||||
phase:1,\
|
||||
nolog,\
|
||||
pass,\
|
||||
t:none,\
|
||||
setvar:'tx.allowed_request_content_type=|application/x-www-form-urlencoded| |multipart/form-data| |multipart/related| |text/xml| |application/xml| |application/soap+xml| |application/x-amf| |application/json| |application/cloudevents+json| |application/cloudevents-batch+json| |application/octet-stream| |application/csp-report| |application/xss-auditor-report| |text/plain| |application/x-git-upload-pack-request| |application/x-git-receive-pack-request|'"
|
||||
45
examples/joomla/docker-compose.yml
Normal file
45
examples/joomla/docker-compose.yml
Normal file
@@ -0,0 +1,45 @@
|
||||
version: '3'
|
||||
|
||||
services:
|
||||
|
||||
mywww:
|
||||
image: bunkerity/bunkerized-nginx
|
||||
restart: always
|
||||
ports:
|
||||
- 80:8080
|
||||
- 443:8443
|
||||
volumes:
|
||||
- ./joomla-files:/www:ro
|
||||
- ./letsencrypt:/etc/letsencrypt
|
||||
environment:
|
||||
- SERVER_NAME=www.website.com # replace with your domain
|
||||
- AUTO_LETS_ENCRYPT=yes
|
||||
- REDIRECT_HTTP_TO_HTTPS=yes
|
||||
- DISABLE_DEFAULT_SERVER=yes
|
||||
- MAX_CLIENT_SIZE=50m
|
||||
- USE_CLIENT_CACHE=yes
|
||||
- USE_GZIP=yes
|
||||
- REMOTE_PHP=myjoomla
|
||||
- REMOTE_PHP_PATH=/var/www/html
|
||||
|
||||
myjoomla:
|
||||
image: joomla:fpm-alpine
|
||||
restart: always
|
||||
volumes:
|
||||
- ./joomla-files:/var/www/html
|
||||
environment:
|
||||
- JOOMLA_DB_HOST=mydb
|
||||
- JOOMLA_DB_NAME=joomladb
|
||||
- JOOMLA_DB_USER=user
|
||||
- JOOMLA_DB_PASSWORD=db-user-pwd # replace with a stronger password (must match MYSQL_PASSWORD)
|
||||
|
||||
mydb:
|
||||
image: mariadb
|
||||
restart: always
|
||||
volumes:
|
||||
- ./db-data:/var/lib/mysql
|
||||
environment:
|
||||
- MYSQL_ROOT_PASSWORD=db-root-pwd # replace with a stronger password
|
||||
- MYSQL_DATABASE=joomladb
|
||||
- MYSQL_USER=user
|
||||
- MYSQL_PASSWORD=db-user-pwd # replace with a stronger password (must match JOOMLA_DB_PASSWORD)
|
||||
@@ -18,8 +18,8 @@ services:
|
||||
- REDIRECT_HTTP_TO_HTTPS=yes
|
||||
- AUTO_LETS_ENCRYPT=yes
|
||||
- USE_PROXY_CACHE=yes
|
||||
- USE_CLIENT_CACHE=yes
|
||||
- USE_GZIP=yes
|
||||
- USE_BROTLI=yes
|
||||
- USE_REVERSE_PROXY=yes
|
||||
- REVERSE_PROXY_URL=/
|
||||
- REVERSE_PROXY_HOST=http://app
|
||||
|
||||
@@ -18,8 +18,8 @@ services:
|
||||
- MAX_CLIENT_SIZE=50m
|
||||
- SERVE_FILES=no
|
||||
- USE_PROXY_CACHE=yes
|
||||
- USE_CLIENT_CACHE=yes
|
||||
- USE_GZIP=yes
|
||||
- USE_BROTLI=yes
|
||||
- USE_REVERSE_PROXY=yes
|
||||
- REVERSE_PROXY_URL=/
|
||||
- REVERSE_PROXY_HOST=https://mymoodle:8443
|
||||
|
||||
@@ -19,13 +19,11 @@ services:
|
||||
- DISABLE_DEFAULT_SERVER=yes
|
||||
- USE_CLIENT_CACHE=yes
|
||||
- USE_GZIP=yes
|
||||
- USE_BROTLI=yes
|
||||
- app1.website.com_REMOTE_PHP=myapp1
|
||||
- app1.website.com_REMOTE_PHP_PATH=/app
|
||||
- app2.website.com_REMOTE_PHP=myapp2
|
||||
- app2.website.com_REMOTE_PHP_PATH=/app
|
||||
- app3.website.com_SERVE_FILES=no
|
||||
- app3.website.com_USE_CLIENT_CACHE=no
|
||||
- app3.website.com_USE_PROXY_CACHE=yes
|
||||
- app3.website.com_USE_REVERSE_PROXY=yes
|
||||
- app3.website.com_REVERSE_PROXY_URL=/
|
||||
|
||||
@@ -22,14 +22,13 @@ services:
|
||||
- DISABLE_DEFAULT_SERVER=yes
|
||||
- USE_CLIENT_CACHE=yes
|
||||
- USE_GZIP=yes
|
||||
- USE_BROTLI=yes
|
||||
- wp.website.com_REMOTE_PHP=mywp
|
||||
- wp.website.com_REMOTE_PHP_PATH=/var/www/html
|
||||
- nc.website.com_REMOTE_PHP=mync
|
||||
- nc.website.com_REMOTE_PHP_PATH=/var/www/html
|
||||
- nc.website.com_LIMIT_REQ_RATE=40r/s
|
||||
- nc.website.com_LIMIT_REQ_BURST=60
|
||||
- nc.website.com_ALLOWED_METHODS=GET|POST|HEAD|PROPFIND|DELETE|PUT|MKCOL|MOVE|COPY|PROPPATCH|REPORT
|
||||
- nc.website.com_LIMIT_REQ_RATE=5r/s
|
||||
- nc.website.com_LIMIT_REQ_BURST=10
|
||||
- nc.website.com_ALLOWED_METHODS=GET|POST|HEAD|COPY|DELETE|LOCK|MKCOL|MOVE|PROPFIND|PROPPATCH|PUT|UNLOCK|OPTIONS
|
||||
- nc.website.com_X_FRAME_OPTIONS=SAMEORIGIN
|
||||
- nc.website.com_FAIL2BAN_STATUS_CODE=400|401|403|405|444
|
||||
networks:
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
SecRuleRemoveById 921110
|
||||
SecRule REQUEST_FILENAME "@contains /remote.php/webdav" "id:1,ctl:ruleRemoveByTag=OWASP_CRS"
|
||||
|
||||
@@ -1 +1,4 @@
|
||||
SecRule REQUEST_FILENAME "/wp-admin/admin-ajax.php" "id:1,ctl:ruleRemoveByTag=attack-xss,ctl:ruleRemoveByTag=attack-rce"
|
||||
SecRule REQUEST_FILENAME "/wp-admin/options.php" "id:2,ctl:ruleRemoveByTag=attack-xss"
|
||||
SecRule REQUEST_FILENAME "^/wp-json/yoast" "id:3,ctl:ruleRemoveById=930120"
|
||||
SecRuleRemoveById 953120
|
||||
|
||||
@@ -12,4 +12,4 @@ SecAction \
|
||||
nolog,\
|
||||
pass,\
|
||||
t:none,\
|
||||
setvar:'tx.allowed_methods=GET HEAD POST PROPFIND DELETE PUT MKCOL MOVE COPY PROPPATCH REPORT'"
|
||||
setvar:'tx.allowed_methods=GET POST HEAD COPY DELETE LOCK MKCOL MOVE PROPFIND PROPPATCH PUT UNLOCK OPTIONS'"
|
||||
|
||||
@@ -23,16 +23,15 @@ services:
|
||||
- USE_CLIENT_CACHE=yes
|
||||
- REMOTE_PHP=mync
|
||||
- REMOTE_PHP_PATH=/var/www/html
|
||||
- LIMIT_REQ_RATE=40r/s
|
||||
- LIMIT_REQ_BURST=60
|
||||
- ALLOWED_METHODS=GET|POST|HEAD|PROPFIND|DELETE|PUT|MKCOL|MOVE|COPY|PROPPATCH|REPORT
|
||||
- LIMIT_REQ_RATE=5r/s
|
||||
- LIMIT_REQ_BURST=10
|
||||
- ALLOWED_METHODS=GET|POST|HEAD|COPY|DELETE|LOCK|MKCOL|MOVE|PROPFIND|PROPPATCH|PUT|UNLOCK|OPTIONS
|
||||
- X_FRAME_OPTIONS=SAMEORIGIN
|
||||
- USE_GZIP=yes
|
||||
- USE_BROTLI=yes
|
||||
- FAIL2BAN_STATUS_CODE=400|401|403|405|444
|
||||
|
||||
mync:
|
||||
image: nextcloud:20-fpm
|
||||
image: nextcloud:21-fpm
|
||||
restart: always
|
||||
volumes:
|
||||
- ./nc-files:/var/www/html
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
SecRuleRemoveById 921110
|
||||
SecRule REQUEST_FILENAME "@contains /remote.php/webdav" "id:1,ctl:ruleRemoveByTag=OWASP_CRS"
|
||||
|
||||
@@ -12,4 +12,4 @@ SecAction \
|
||||
nolog,\
|
||||
pass,\
|
||||
t:none,\
|
||||
setvar:'tx.allowed_methods=GET HEAD POST PROPFIND DELETE PUT MKCOL MOVE COPY PROPPATCH REPORT'"
|
||||
setvar:'tx.allowed_methods=GET POST HEAD COPY DELETE LOCK MKCOL MOVE PROPFIND PROPPATCH PUT UNLOCK OPTIONS'"
|
||||
|
||||
@@ -20,8 +20,8 @@ services:
|
||||
- ALLOWED_METHODS=GET|POST|HEAD|PUT|DELETE
|
||||
- SERVE_FILES=no
|
||||
- USE_PROXY_CACHE=yes
|
||||
- USE_CLIENT_CACHE=yes
|
||||
- USE_GZIP=yes
|
||||
- USE_BROTLI=yes
|
||||
- USE_REVERSE_PROXY=yes
|
||||
- REVERSE_PROXY_URL=/
|
||||
- REVERSE_PROXY_HOST=https://mypassbolt
|
||||
|
||||
@@ -20,9 +20,10 @@ services:
|
||||
- MAX_CLIENT_SIZE=50m
|
||||
- USE_CLIENT_CACHE=yes
|
||||
- USE_GZIP=yes
|
||||
- USE_BROTLI=yes
|
||||
- REMOTE_PHP=myprestashop
|
||||
- REMOTE_PHP_PATH=/var/www/html
|
||||
- LIMIT_REQ_RATE=5r/s
|
||||
- LIMIT_REQ_BURST=10
|
||||
|
||||
myprestashop:
|
||||
image: prestashop/prestashop:1.7-fpm
|
||||
|
||||
46
examples/redmine/docker-compose.yml
Normal file
46
examples/redmine/docker-compose.yml
Normal file
@@ -0,0 +1,46 @@
|
||||
version: '3'
|
||||
|
||||
services:
|
||||
|
||||
myreverse:
|
||||
image: bunkerity/bunkerized-nginx
|
||||
restart: always
|
||||
ports:
|
||||
- 80:8080
|
||||
- 443:8443
|
||||
volumes:
|
||||
- ./letsencrypt:/etc/letsencrypt
|
||||
environment:
|
||||
- SERVER_NAME=www.website.com # replace with your domain
|
||||
- SERVE_FILES=no
|
||||
- DISABLE_DEFAULT_SERVER=yes
|
||||
- REDIRECT_HTTP_TO_HTTPS=yes
|
||||
- AUTO_LETS_ENCRYPT=yes
|
||||
- USE_PROXY_CACHE=yes
|
||||
- USE_CLIENT_CACHE=yes
|
||||
- USE_GZIP=yes
|
||||
- USE_REVERSE_PROXY=yes
|
||||
- REVERSE_PROXY_URL=/
|
||||
- REVERSE_PROXY_HOST=http://myredmine:3000/
|
||||
|
||||
myredmine:
|
||||
image: redmine
|
||||
restart: always
|
||||
volumes:
|
||||
- ./redmine-data:/usr/src/redmine/files
|
||||
environment:
|
||||
- REDMINE_DB_MYSQL=mydb
|
||||
- REDMINE_DB_DATABASE=redminedb
|
||||
- REDMINE_DB_USERNAME=user
|
||||
- REDMINE_DB_PASSWORD=db-user-pwd # replace with a stronger password (must match MYSQL_PASSWORD)
|
||||
|
||||
mydb:
|
||||
image: mariadb
|
||||
restart: always
|
||||
volumes:
|
||||
- ./db-data:/var/lib/mysql
|
||||
environment:
|
||||
- MYSQL_ROOT_PASSWORD=db-root-pwd # replace with a stronger password
|
||||
- MYSQL_DATABASE=redminedb
|
||||
- MYSQL_USER=user
|
||||
- MYSQL_PASSWORD=db-user-pwd # replace with a stronger password (must match REDMINE_DB_PASSWORD)
|
||||
@@ -18,8 +18,8 @@ services:
|
||||
- REDIRECT_HTTP_TO_HTTPS=yes
|
||||
- AUTO_LETS_ENCRYPT=yes
|
||||
- USE_PROXY_CACHE=yes
|
||||
- USE_CLIENT_CACHE=yes
|
||||
- USE_GZIP=yes
|
||||
- USE_BROTLI=yes
|
||||
- USE_REVERSE_PROXY=yes
|
||||
- app1.website.com_REVERSE_PROXY_URL=/
|
||||
- app1.website.com_REVERSE_PROXY_HOST=http://app1:3000
|
||||
|
||||
@@ -18,8 +18,8 @@ services:
|
||||
- REDIRECT_HTTP_TO_HTTPS=yes
|
||||
- AUTO_LETS_ENCRYPT=yes
|
||||
- USE_PROXY_CACHE=yes
|
||||
- USE_CLIENT_CACHE=yes
|
||||
- USE_GZIP=yes
|
||||
- USE_BROTLI=yes
|
||||
- USE_REVERSE_PROXY=yes
|
||||
- REVERSE_PROXY_URL_1=/app1/
|
||||
- REVERSE_PROXY_HOST_1=http://app1:3000/
|
||||
|
||||
@@ -17,8 +17,8 @@ services:
|
||||
- REDIRECT_HTTP_TO_HTTPS=yes
|
||||
- AUTO_LETS_ENCRYPT=yes
|
||||
- USE_PROXY_CACHE=yes
|
||||
- USE_CLIENT_CACHE=yes
|
||||
- USE_GZIP=yes
|
||||
- USE_BROTLI=yes
|
||||
- USE_REVERSE_PROXY=yes
|
||||
- REVERSE_PROXY_URL=/ws/
|
||||
- REVERSE_PROXY_HOST=http://myws:8010/
|
||||
|
||||
@@ -32,7 +32,7 @@ services:
|
||||
mode: host
|
||||
protocol: tcp
|
||||
volumes:
|
||||
- /shared/confs:/etc/nginx:ro
|
||||
- /shared/confs:/etc/nginx
|
||||
- /shared/letsencrypt:/etc/letsencrypt:ro
|
||||
- /shared/acme-challenge:/acme-challenge:ro
|
||||
- /shared/www:/www:ro
|
||||
@@ -45,6 +45,7 @@ services:
|
||||
- AUTO_LETS_ENCRYPT=yes
|
||||
- REDIRECT_HTTP_TO_HTTPS=yes
|
||||
- DISABLE_DEFAULT_SERVER=yes
|
||||
- USE_CLIENT_CACHE=yes
|
||||
networks:
|
||||
- net_config
|
||||
- net_services
|
||||
@@ -86,6 +87,7 @@ services:
|
||||
- "node.role==worker"
|
||||
labels:
|
||||
- "bunkerized-nginx.SERVER_NAME=app2.website.com"
|
||||
- "bunkerized-nginx.USE_PROXY_CACHE=yes"
|
||||
- "bunkerized-nginx.USE_REVERSE_PROXY=yes"
|
||||
- "bunkerized-nginx.REVERSE_PROXY_URL=/"
|
||||
- "bunkerized-nginx.REVERSE_PROXY_HOST=http://app2"
|
||||
|
||||
@@ -17,8 +17,8 @@ services:
|
||||
- REDIRECT_HTTP_TO_HTTPS=yes
|
||||
- AUTO_LETS_ENCRYPT=yes
|
||||
- USE_PROXY_CACHE=yes
|
||||
- USE_CLIENT_CACHE=yes
|
||||
- USE_GZIP=yes
|
||||
- USE_BROTLI=yes
|
||||
- USE_REVERSE_PROXY=yes
|
||||
- REVERSE_PROXY_URL=/
|
||||
- REVERSE_PROXY_HOST=http://mytomcat:8080/sample/
|
||||
|
||||
@@ -29,7 +29,6 @@ services:
|
||||
- USE_ANTIBOT=captcha
|
||||
- USE_CLIENT_CACHE=yes
|
||||
- USE_GZIP=yes
|
||||
- USE_BROTLI=yes
|
||||
- REMOTE_PHP=myphp
|
||||
- REMOTE_PHP_PATH=/app
|
||||
|
||||
|
||||
@@ -18,8 +18,8 @@ services:
|
||||
- AUTO_LETS_ENCRYPT=yes
|
||||
- REDIRECT_HTTP_TO_HTTPS=yes
|
||||
- DISABLE_DEFAULT_SERVER=yes
|
||||
- USE_CLIENT_CACHE=yes
|
||||
- USE_GZIP=yes
|
||||
- USE_BROTLI=yes
|
||||
- admin.website.com_SERVE_FILES=no
|
||||
- admin.website.com_USE_AUTH_BASIC=yes
|
||||
- admin.website.com_AUTH_BASIC_USER=admin # change it to something hard to guess
|
||||
|
||||
@@ -13,6 +13,7 @@ services:
|
||||
- ./letsencrypt:/etc/letsencrypt
|
||||
- ./server-confs:/server-confs:ro # custom confs at server context for permalinks
|
||||
- ./modsec-crs-confs:/modsec-crs-confs:ro # custom Core Rule Set confs to add Wordpress exclusions
|
||||
- ./modsec-confs:/modsec-confs:ro # avoid some FP with CRS
|
||||
environment:
|
||||
- SERVER_NAME=www.website.com # replace with your domain
|
||||
- AUTO_LETS_ENCRYPT=yes
|
||||
@@ -21,7 +22,6 @@ services:
|
||||
- MAX_CLIENT_SIZE=50m
|
||||
- USE_CLIENT_CACHE=yes
|
||||
- USE_GZIP=yes
|
||||
- USE_BROTLI=yes
|
||||
- REMOTE_PHP=mywp
|
||||
- REMOTE_PHP_PATH=/var/www/html
|
||||
|
||||
|
||||
4
examples/wordpress/modsec-confs/wordpress.conf
Normal file
4
examples/wordpress/modsec-confs/wordpress.conf
Normal file
@@ -0,0 +1,4 @@
|
||||
SecRule REQUEST_FILENAME "/wp-admin/admin-ajax.php" "id:1,ctl:ruleRemoveByTag=attack-xss,ctl:ruleRemoveByTag=attack-rce"
|
||||
SecRule REQUEST_FILENAME "/wp-admin/options.php" "id:2,ctl:ruleRemoveByTag=attack-xss"
|
||||
SecRule REQUEST_FILENAME "^/wp-json/yoast" "id:3,ctl:ruleRemoveById=930120"
|
||||
SecRuleRemoveById 953120
|
||||
@@ -25,3 +25,5 @@ module(load="imuxsock" SysSock.Name="/tmp/log")
|
||||
$template rawFormat,"%msg:2:2048%\n"
|
||||
local0.=notice /var/log/access.log;rawFormat
|
||||
local0.*;local0.!=notice /var/log/error.log;rawFormat
|
||||
|
||||
%REMOTE_SYSLOG%
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
local M = {}
|
||||
local api_list = {}
|
||||
local M = {}
|
||||
local api_list = {}
|
||||
local api_whitelist_ip = {%API_WHITELIST_IP%}
|
||||
local iputils = require "resty.iputils"
|
||||
local whitelist = iputils.parse_cidrs(api_whitelist_ip)
|
||||
|
||||
api_list["^/ping$"] = function ()
|
||||
return true
|
||||
@@ -10,7 +13,7 @@ api_list["^/reload$"] = function ()
|
||||
end
|
||||
|
||||
function M.is_api_call (api_uri)
|
||||
if ngx.var.request_uri:sub(1, #api_uri) .. "/" == api_uri .. "/" then
|
||||
if iputils.ip_in_cidrs(ngx.var.remote_addr, whitelist) and ngx.var.request_uri:sub(1, #api_uri) .. "/" == api_uri .. "/" then
|
||||
for uri, code in pairs(api_list) do
|
||||
if string.match(ngx.var.request_uri:sub(#api_uri + 1), uri) then
|
||||
return true
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
local M = {}
|
||||
local dns = require "dns"
|
||||
local iputils = require "resty.iputils"
|
||||
local ip_list = {%BLACKLIST_IP_LIST%}
|
||||
local blacklist = iputils.parse_cidrs(ip_list)
|
||||
local reverse_list = {%BLACKLIST_REVERSE_LIST%}
|
||||
local ip = ngx.var.remote_addr
|
||||
|
||||
@@ -21,10 +23,10 @@ function M.reverse_cached ()
|
||||
end
|
||||
|
||||
function M.check_ip ()
|
||||
for k, v in ipairs(ip_list) do
|
||||
if v == ip then
|
||||
if #ip_list > 0 then
|
||||
if iputils.ip_in_cidrs(ip, blacklist) then
|
||||
ngx.shared.blacklist_ip_cache:set(ip, "ko", 86400)
|
||||
ngx.log(ngx.WARN, "ip " .. ip .. " is in blacklist")
|
||||
ngx.log(ngx.NOTICE, "ip " .. ip .. " is in blacklist")
|
||||
return true
|
||||
end
|
||||
end
|
||||
@@ -33,13 +35,15 @@ function M.check_ip ()
|
||||
end
|
||||
|
||||
function M.check_reverse ()
|
||||
local rdns = dns.get_reverse()
|
||||
if rdns ~= "" then
|
||||
for k, v in ipairs(reverse_list) do
|
||||
if rdns:sub(-#v) == v then
|
||||
ngx.shared.blacklist_reverse_cache:set(ip, "ko", 86400)
|
||||
ngx.log(ngx.WARN, "reverse " .. rdns .. " is in blacklist")
|
||||
return true
|
||||
if #reverse_list > 0 then
|
||||
local rdns = dns.get_reverse()
|
||||
if rdns ~= "" then
|
||||
for k, v in ipairs(reverse_list) do
|
||||
if rdns:sub(-#v) == v then
|
||||
ngx.shared.blacklist_reverse_cache:set(ip, "ko", 86400)
|
||||
ngx.log(ngx.NOTICE, "reverse " .. rdns .. " is in blacklist")
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -20,7 +20,7 @@ function M.check ()
|
||||
local a,b,c,d = v2:match("([%d]+).([%d]+).([%d]+).([%d]+)")
|
||||
if a == "127" then
|
||||
ngx.shared.dnsbl_cache:set(ip, "ko", 86400)
|
||||
ngx.log(ngx.WARN, "ip " .. ip .. " is in DNSBL " .. v)
|
||||
ngx.log(ngx.NOTICE, "ip " .. ip .. " is in DNSBL " .. v)
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
local M = {}
|
||||
local dns = require "dns"
|
||||
local iputils = require "resty.iputils"
|
||||
local ip_list = {%WHITELIST_IP_LIST%}
|
||||
local reverse_list = {%WHITELIST_REVERSE_LIST%}
|
||||
local whitelist = iputils.parse_cidrs(ip_list)
|
||||
local ip = ngx.var.remote_addr
|
||||
|
||||
function M.ip_cached_ok ()
|
||||
@@ -21,10 +23,10 @@ function M.reverse_cached ()
|
||||
end
|
||||
|
||||
function M.check_ip ()
|
||||
for k, v in ipairs(ip_list) do
|
||||
if v == ip then
|
||||
if #ip_list > 0 then
|
||||
if iputils.ip_in_cidrs(ip, whitelist) then
|
||||
ngx.shared.whitelist_ip_cache:set(ip, "ok", 86400)
|
||||
ngx.log(ngx.WARN, "ip " .. ip .. " is in whitelist")
|
||||
ngx.log(ngx.NOTICE, "ip " .. ip .. " is in whitelist")
|
||||
return true
|
||||
end
|
||||
end
|
||||
@@ -33,22 +35,24 @@ function M.check_ip ()
|
||||
end
|
||||
|
||||
function M.check_reverse ()
|
||||
local rdns = dns.get_reverse()
|
||||
if rdns ~= "" then
|
||||
local whitelisted = false
|
||||
for k, v in ipairs(reverse_list) do
|
||||
if rdns:sub(-#v) == v then
|
||||
whitelisted = true
|
||||
break
|
||||
if #reverse_list > 0 then
|
||||
local rdns = dns.get_reverse()
|
||||
if rdns ~= "" then
|
||||
local whitelisted = false
|
||||
for k, v in ipairs(reverse_list) do
|
||||
if rdns:sub(-#v) == v then
|
||||
whitelisted = true
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
if whitelisted then
|
||||
local ips = dns.get_ips(rdns)
|
||||
for k, v in ipairs(ips) do
|
||||
if v == ip then
|
||||
ngx.shared.whitelist_reverse_cache:set(ip, "ok", 86400)
|
||||
ngx.log(ngx.WARN, "reverse " .. rdns .. " is in whitelist")
|
||||
return true
|
||||
if whitelisted then
|
||||
local ips = dns.get_ips(rdns)
|
||||
for k, v in ipairs(ips) do
|
||||
if v == ip then
|
||||
ngx.shared.whitelist_reverse_cache:set(ip, "ok", 86400)
|
||||
ngx.log(ngx.NOTICE, "reverse " .. rdns .. " is in whitelist")
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -40,6 +40,7 @@ chmod 770 /var/log/letsencrypt
|
||||
touch /var/log/clamav.log
|
||||
chown root:nginx /var/log/clamav.log
|
||||
chmod 770 /var/log/clamav.log
|
||||
find /var/log -type f -exec chmod 0774 {} \;
|
||||
|
||||
# prepare /acme-challenge
|
||||
mkdir /acme-challenge
|
||||
@@ -67,8 +68,11 @@ chown -R root:nginx /var/run/fail2ban /var/lib/fail2ban
|
||||
chmod -R 770 /var/run/fail2ban /var/lib/fail2ban
|
||||
|
||||
# prepare /usr/local/lib/lua
|
||||
chown root:nginx /usr/local/lib/lua
|
||||
chown -R root:nginx /usr/local/lib/lua
|
||||
chmod 770 /usr/local/lib/lua
|
||||
find /usr/local/lib/lua -type f -name "*.conf" -exec chmod 0760 {} \;
|
||||
find /usr/local/lib/lua -type f -name "*.lua" -exec chmod 0760 {} \;
|
||||
find /usr/local/lib/lua -type d -exec chmod 0770 {} \;
|
||||
|
||||
# prepare /cache
|
||||
mkdir /cache
|
||||
|
||||
Reference in New Issue
Block a user