Compare commits
87 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d5d699252c | ||
|
|
50f95420b5 | ||
|
|
dc382c3e04 | ||
|
|
0026328f25 | ||
|
|
9023ab5aed | ||
|
|
124474ad66 | ||
|
|
eac9c8f513 | ||
|
|
1ee490de6d | ||
|
|
825e6a747e | ||
|
|
09a984c86b | ||
|
|
fd7afa17b3 | ||
|
|
b9b7fdfcc4 | ||
|
|
58e1d66bc7 | ||
|
|
7026643f8a | ||
|
|
06f688fe97 | ||
|
|
c65b78b1cc | ||
|
|
f9b9b9546f | ||
|
|
b5fe6335c7 | ||
|
|
951f3957fd | ||
|
|
0f520b8914 | ||
|
|
569ad75c42 | ||
|
|
bd7b6af668 | ||
|
|
459bb8ea1c | ||
|
|
208b5acb30 | ||
|
|
59b2fed416 | ||
|
|
a4871a915e | ||
|
|
026783f018 | ||
|
|
8115853453 | ||
|
|
c5f283b00e | ||
|
|
03ce7a6483 | ||
|
|
3f7e2c54b3 | ||
|
|
bb0f46d8af | ||
|
|
c5b32dfc4c | ||
|
|
9a4f96ad18 | ||
|
|
f258426f55 | ||
|
|
119e963612 | ||
|
|
373988670a | ||
|
|
2a956f2cd3 | ||
|
|
15a37a8682 | ||
|
|
3a3d527907 | ||
|
|
e6b5f460c9 | ||
|
|
002e3ed2ba | ||
|
|
7b55acbe8b | ||
|
|
559b7835d4 | ||
|
|
4ea01bd93f | ||
|
|
a73891a3b8 | ||
|
|
26199f52c8 | ||
|
|
5c3f94a84f | ||
|
|
043fcdc136 | ||
|
|
b86ded3d1c | ||
|
|
92569679b6 | ||
|
|
15e74e4860 | ||
|
|
fd0a6412d0 | ||
|
|
419fdfc86e | ||
|
|
0bc1f652b4 | ||
|
|
6c7461e298 | ||
|
|
d01bc5e014 | ||
|
|
75c69c8105 | ||
|
|
e26b8482aa | ||
|
|
f618c73e6c | ||
|
|
78c1e5c676 | ||
|
|
481e10d3ef | ||
|
|
aae2a71983 | ||
|
|
f3bf04e390 | ||
|
|
36cbb927c0 | ||
|
|
95153dbc5d | ||
|
|
26947179a4 | ||
|
|
88f27bfeb8 | ||
|
|
3cc1615c4d | ||
|
|
8bacf722a6 | ||
|
|
2bfc4b41fa | ||
|
|
587d4a92eb | ||
|
|
c311d0c825 | ||
|
|
0d03f49ebc | ||
|
|
2112c306a8 | ||
|
|
8f9dcc5ab8 | ||
|
|
2fe05d3fd3 | ||
|
|
db04c0345c | ||
|
|
ed8bd902b1 | ||
|
|
3a7aa5d9c0 | ||
|
|
9ec9de6ca2 | ||
|
|
791342cbe6 | ||
|
|
2f23671c3b | ||
|
|
e350a717ff | ||
|
|
e818acb0d1 | ||
|
|
b92f74ed98 | ||
|
|
9688e66508 |
26
.github/workflows/autotest-bunkerized-nginx-autoconf.yml
vendored
Normal file
26
.github/workflows/autotest-bunkerized-nginx-autoconf.yml
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
name: Automatic test on autoconf
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [dev, master]
|
||||
pull_request:
|
||||
branches: [dev, master]
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout source code
|
||||
uses: actions/checkout@v2
|
||||
- name: Build the image
|
||||
run: docker build -t autotest-autoconf -f autoconf/Dockerfile .
|
||||
- name: Run Trivy security scanner
|
||||
uses: aquasecurity/trivy-action@master
|
||||
with:
|
||||
image-ref: 'autotest-autoconf'
|
||||
format: 'table'
|
||||
exit-code: '1'
|
||||
ignore-unfixed: true
|
||||
severity: 'UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL'
|
||||
|
||||
|
||||
26
.github/workflows/autotest-bunkerized-nginx-ui.yml
vendored
Normal file
26
.github/workflows/autotest-bunkerized-nginx-ui.yml
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
name: Automatic test on ui
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [dev, master]
|
||||
pull_request:
|
||||
branches: [dev, master]
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout source code
|
||||
uses: actions/checkout@v2
|
||||
- name: Build the image
|
||||
run: docker build -t autotest-ui -f ui/Dockerfile .
|
||||
- name: Run Trivy security scanner
|
||||
uses: aquasecurity/trivy-action@master
|
||||
with:
|
||||
image-ref: 'autotest-ui'
|
||||
format: 'table'
|
||||
exit-code: '1'
|
||||
ignore-unfixed: true
|
||||
severity: 'UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL'
|
||||
|
||||
|
||||
@@ -23,6 +23,6 @@ jobs:
|
||||
format: 'table'
|
||||
exit-code: '1'
|
||||
ignore-unfixed: true
|
||||
severity: 'CRITICAL,HIGH'
|
||||
severity: 'UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL'
|
||||
|
||||
|
||||
19
Dockerfile
19
Dockerfile
@@ -6,27 +6,18 @@ RUN chmod +x /tmp/compile.sh && \
|
||||
/tmp/compile.sh && \
|
||||
rm -rf /tmp/*
|
||||
|
||||
COPY crowdsec/install.sh /tmp/install.sh
|
||||
RUN chmod +x /tmp/install.sh && \
|
||||
/tmp/install.sh && \
|
||||
rm -rf /tmp/*
|
||||
|
||||
COPY entrypoint/ /opt/entrypoint
|
||||
COPY confs/ /opt/confs
|
||||
COPY scripts/ /opt/scripts
|
||||
COPY fail2ban/ /opt/fail2ban
|
||||
COPY logs/ /opt/logs
|
||||
COPY lua/ /opt/lua
|
||||
COPY crowdsec/ /opt/crowdsec
|
||||
|
||||
RUN apk --no-cache add certbot libstdc++ libmaxminddb geoip pcre yajl fail2ban clamav apache2-utils rsyslog openssl lua libgd go jq mariadb-connector-c bash brotli && \
|
||||
chmod +x /opt/entrypoint/* /opt/scripts/* && \
|
||||
mkdir /opt/entrypoint.d && \
|
||||
rm -f /var/log/nginx/* && \
|
||||
chown root:nginx /var/log/nginx && \
|
||||
chmod 750 /var/log/nginx && \
|
||||
touch /var/log/nginx/error.log /var/log/nginx/modsec_audit.log && \
|
||||
chown nginx:nginx /var/log/nginx/*.log
|
||||
COPY prepare.sh /tmp/prepare.sh
|
||||
RUN chmod +x /tmp/prepare.sh && /tmp/prepare.sh && rm -f /tmp/prepare.sh
|
||||
|
||||
# Fix CVE-2020-28928, CVE-2020-8231 & CVE-2020-1971
|
||||
RUN apk --no-cache add "musl-utils>1.1.24-r2" "curl>7.67.0-r1" "libcrypto1.1>1.1.1g-r0" "libssl1.1>1.1.1g-r0"
|
||||
|
||||
VOLUME /www /http-confs /server-confs /modsec-confs /modsec-crs-confs /cache
|
||||
|
||||
|
||||
@@ -6,27 +6,18 @@ RUN chmod +x /tmp/compile.sh && \
|
||||
/tmp/compile.sh && \
|
||||
rm -rf /tmp/*
|
||||
|
||||
COPY crowdsec/install.sh /tmp/install.sh
|
||||
RUN chmod +x /tmp/install.sh && \
|
||||
/tmp/install.sh && \
|
||||
rm -rf /tmp/*
|
||||
|
||||
COPY entrypoint/ /opt/entrypoint
|
||||
COPY confs/ /opt/confs
|
||||
COPY scripts/ /opt/scripts
|
||||
COPY fail2ban/ /opt/fail2ban
|
||||
COPY logs/ /opt/logs
|
||||
COPY lua/ /opt/lua
|
||||
COPY crowdsec/ /opt/crowdsec
|
||||
|
||||
RUN apk --no-cache add certbot libstdc++ libmaxminddb geoip pcre yajl fail2ban clamav apache2-utils rsyslog openssl lua libgd go jq mariadb-connector-c bash brotli && \
|
||||
chmod +x /opt/entrypoint/* /opt/scripts/* && \
|
||||
mkdir /opt/entrypoint.d && \
|
||||
rm -f /var/log/nginx/* && \
|
||||
chown root:nginx /var/log/nginx && \
|
||||
chmod 750 /var/log/nginx && \
|
||||
touch /var/log/nginx/error.log /var/log/nginx/modsec_audit.log && \
|
||||
chown nginx:nginx /var/log/nginx/*.log
|
||||
COPY prepare.sh /tmp/prepare.sh
|
||||
RUN chmod +x /tmp/prepare.sh && /tmp/prepare.sh && rm -f /tmp/prepare.sh
|
||||
|
||||
# Fix CVE-2020-28928, CVE-2020-8231 & CVE-2020-1971
|
||||
RUN apk --no-cache add "musl-utils>1.1.24-r2" "curl>7.67.0-r1" "libcrypto1.1>1.1.1g-r0" "libssl1.1>1.1.1g-r0"
|
||||
|
||||
VOLUME /www /http-confs /server-confs /modsec-confs /modsec-crs-confs /cache
|
||||
|
||||
|
||||
@@ -13,27 +13,18 @@ RUN chmod +x /tmp/compile.sh && \
|
||||
/tmp/compile.sh && \
|
||||
rm -rf /tmp/*
|
||||
|
||||
COPY crowdsec/install.sh /tmp/install.sh
|
||||
RUN chmod +x /tmp/install.sh && \
|
||||
/tmp/install.sh && \
|
||||
rm -rf /tmp/*
|
||||
|
||||
COPY entrypoint/ /opt/entrypoint
|
||||
COPY confs/ /opt/confs
|
||||
COPY scripts/ /opt/scripts
|
||||
COPY fail2ban/ /opt/fail2ban
|
||||
COPY logs/ /opt/logs
|
||||
COPY lua/ /opt/lua
|
||||
COPY crowdsec/ /opt/crowdsec
|
||||
|
||||
RUN apk --no-cache add certbot libstdc++ libmaxminddb geoip pcre yajl fail2ban clamav apache2-utils rsyslog openssl lua libgd go jq mariadb-connector-c bash brotli && \
|
||||
chmod +x /opt/entrypoint/* /opt/scripts/* && \
|
||||
mkdir /opt/entrypoint.d && \
|
||||
rm -f /var/log/nginx/* && \
|
||||
chown root:nginx /var/log/nginx && \
|
||||
chmod 750 /var/log/nginx && \
|
||||
touch /var/log/nginx/error.log /var/log/nginx/modsec_audit.log && \
|
||||
chown nginx:nginx /var/log/nginx/*.log
|
||||
COPY prepare.sh /tmp/prepare.sh
|
||||
RUN chmod +x /tmp/prepare.sh && /tmp/prepare.sh && rm -f /tmp/prepare.sh
|
||||
|
||||
# Fix CVE-2020-28928, CVE-2020-8231 & CVE-2020-1971
|
||||
RUN apk --no-cache add "musl-utils>1.1.24-r2" "curl>7.67.0-r1" "libcrypto1.1>1.1.1g-r0" "libssl1.1>1.1.1g-r0"
|
||||
|
||||
VOLUME /www /http-confs /server-confs /modsec-confs /modsec-crs-confs /cache
|
||||
|
||||
|
||||
@@ -13,27 +13,18 @@ RUN chmod +x /tmp/compile.sh && \
|
||||
/tmp/compile.sh && \
|
||||
rm -rf /tmp/*
|
||||
|
||||
COPY crowdsec/install.sh /tmp/install.sh
|
||||
RUN chmod +x /tmp/install.sh && \
|
||||
/tmp/install.sh && \
|
||||
rm -rf /tmp/*
|
||||
|
||||
COPY entrypoint/ /opt/entrypoint
|
||||
COPY confs/ /opt/confs
|
||||
COPY scripts/ /opt/scripts
|
||||
COPY fail2ban/ /opt/fail2ban
|
||||
COPY logs/ /opt/logs
|
||||
COPY lua/ /opt/lua
|
||||
COPY crowdsec/ /opt/crowdsec
|
||||
|
||||
RUN apk --no-cache add certbot libstdc++ libmaxminddb geoip pcre yajl fail2ban clamav apache2-utils rsyslog openssl lua libgd go jq mariadb-connector-c bash brotli && \
|
||||
chmod +x /opt/entrypoint/* /opt/scripts/* && \
|
||||
mkdir /opt/entrypoint.d && \
|
||||
rm -f /var/log/nginx/* && \
|
||||
chown root:nginx /var/log/nginx && \
|
||||
chmod 750 /var/log/nginx && \
|
||||
touch /var/log/nginx/error.log /var/log/nginx/modsec_audit.log && \
|
||||
chown nginx:nginx /var/log/nginx/*.log
|
||||
COPY prepare.sh /tmp/prepare.sh
|
||||
RUN chmod +x /tmp/prepare.sh && /tmp/prepare.sh && rm -f /tmp/prepare.sh
|
||||
|
||||
# Fix CVE-2020-28928, CVE-2020-8231 & CVE-2020-1971
|
||||
RUN apk --no-cache add "musl-utils>1.1.24-r2" "curl>7.67.0-r1" "libcrypto1.1>1.1.1g-r0" "libssl1.1>1.1.1g-r0"
|
||||
|
||||
VOLUME /www /http-confs /server-confs /modsec-confs /modsec-crs-confs /cache
|
||||
|
||||
|
||||
@@ -6,27 +6,18 @@ RUN chmod +x /tmp/compile.sh && \
|
||||
/tmp/compile.sh && \
|
||||
rm -rf /tmp/*
|
||||
|
||||
COPY crowdsec/install.sh /tmp/install.sh
|
||||
RUN chmod +x /tmp/install.sh && \
|
||||
/tmp/install.sh && \
|
||||
rm -rf /tmp/*
|
||||
|
||||
COPY entrypoint/ /opt/entrypoint
|
||||
COPY confs/ /opt/confs
|
||||
COPY scripts/ /opt/scripts
|
||||
COPY fail2ban/ /opt/fail2ban
|
||||
COPY logs/ /opt/logs
|
||||
COPY lua/ /opt/lua
|
||||
COPY crowdsec/ /opt/crowdsec
|
||||
|
||||
RUN apk --no-cache add certbot libstdc++ libmaxminddb geoip pcre yajl fail2ban clamav apache2-utils rsyslog openssl lua libgd go jq mariadb-connector-c bash brotli && \
|
||||
chmod +x /opt/entrypoint/* /opt/scripts/* && \
|
||||
mkdir /opt/entrypoint.d && \
|
||||
rm -f /var/log/nginx/* && \
|
||||
chown root:nginx /var/log/nginx && \
|
||||
chmod 750 /var/log/nginx && \
|
||||
touch /var/log/nginx/error.log /var/log/nginx/modsec_audit.log && \
|
||||
chown nginx:nginx /var/log/nginx/*.log
|
||||
COPY prepare.sh /tmp/prepare.sh
|
||||
RUN chmod +x /tmp/prepare.sh && /tmp/prepare.sh && rm -f /tmp/prepare.sh
|
||||
|
||||
# Fix CVE-2020-28928, CVE-2020-8231 & CVE-2020-1971
|
||||
RUN apk --no-cache add "musl-utils>1.1.24-r2" "curl>7.67.0-r1" "libcrypto1.1>1.1.1g-r0" "libssl1.1>1.1.1g-r0"
|
||||
|
||||
VOLUME /www /http-confs /server-confs /modsec-confs /modsec-crs-confs /cache
|
||||
|
||||
|
||||
197
README.md
197
README.md
@@ -2,7 +2,7 @@
|
||||
|
||||
<img src="https://github.com/bunkerity/bunkerized-nginx/blob/master/logo.png?raw=true" width="425" />
|
||||
|
||||
<img src="https://img.shields.io/badge/bunkerized--nginx-1.2.0-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/github/workflow/status/bunkerity/bunkerized-nginx/Automatic%20test?label=automatic%20test" /> <img src="https://img.shields.io/docker/cloud/build/bunkerity/bunkerized-nginx" />
|
||||
<img src="https://img.shields.io/badge/bunkerized--nginx-1.2.2-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" /> <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" />
|
||||
|
||||
nginx Docker image secure by default.
|
||||
|
||||
@@ -18,7 +18,8 @@ Non-exhaustive list of features :
|
||||
- Block known bad IP with DNSBL and CrowdSec
|
||||
- Prevent bruteforce attacks with rate limiting
|
||||
- Detect bad files with ClamAV
|
||||
- Easy to configure with environment variables
|
||||
- Easy to configure with environment variables or web UI
|
||||
- Automatic configuration with container labels
|
||||
|
||||
Fooling automated tools/scanners :
|
||||
|
||||
@@ -35,6 +36,8 @@ Fooling automated tools/scanners :
|
||||
* [As a reverse proxy](#as-a-reverse-proxy)
|
||||
* [Behind a reverse proxy](#behind-a-reverse-proxy)
|
||||
* [Multisite](#multisite)
|
||||
* [Automatic configuration](#automatic-configuration)
|
||||
* [Web UI](#web-ui)
|
||||
* [Antibot challenge](#antibot-challenge)
|
||||
- [Tutorials and examples](#tutorials-and-examples)
|
||||
- [List of environment variables](#list-of-environment-variables)
|
||||
@@ -95,7 +98,7 @@ docker run --network mynet \
|
||||
-e REMOTE_PHP_PATH=/app \
|
||||
bunkerity/bunkerized-nginx
|
||||
docker run --network mynet \
|
||||
--name=myphp \
|
||||
--name myphp \
|
||||
-v /path/to/web/files:/app \
|
||||
php:fpm
|
||||
```
|
||||
@@ -172,10 +175,10 @@ docker run -p 80:8080 \
|
||||
-e AUTO_LETS_ENCRYPT=yes \
|
||||
-e REDIRECT_HTTP_TO_HTTPS=yes \
|
||||
-e USE_REVERSE_PROXY=yes \
|
||||
-e app1.domain.com_PROXY_URL=/ \
|
||||
-e app1.domain.com_PROXY_HOST=http://myapp1:8000 \
|
||||
-e app2.domain.com_PROXY_URL=/ \
|
||||
-e app2.domain.com_PROXY_HOST=http://myapp2:8000 \
|
||||
-e app1.domain.com_REVERSE_PROXY_URL=/ \
|
||||
-e app1.domain.com_REVERSE_PROXY_HOST=http://myapp1:8000 \
|
||||
-e app2.domain.com_REVERSE_PROXY_URL=/ \
|
||||
-e app2.domain.com_REVERSE_PROXY_HOST=http://myapp2:8000 \
|
||||
bunkerity/bunkerized-nginx
|
||||
```
|
||||
|
||||
@@ -211,6 +214,124 @@ The */where/are/web/files* directory should have a structure like this :
|
||||
└── ...
|
||||
```
|
||||
|
||||
## Automatic configuration
|
||||
|
||||
The downside of using environment variables is that you need to recreate a new container each time you want to add or remove a web service. An alternative is to use the *bunkerized-nginx-autoconf* image which listens for Docker events and "automagically" generates the configuration.
|
||||
|
||||
First we need a volume that will store the configurations :
|
||||
|
||||
```shell
|
||||
docker volume create nginx_conf
|
||||
```
|
||||
|
||||
Then we run bunkerized-nginx with the `bunkerized-nginx.AUTOCONF` label, mount the created volume at /etc/nginx and set some default configurations for our services (e.g. : automatic Let's Encrypt and HTTP to HTTPS redirect) :
|
||||
|
||||
```shell
|
||||
docker network create mynet
|
||||
|
||||
docker run -p 80:8080 \
|
||||
-p 443:8443 \
|
||||
--network mynet \
|
||||
-v /where/to/save/certificates:/etc/letsencrypt \
|
||||
-v /where/are/web/files:/www:ro \
|
||||
-v nginx_conf:/etc/nginx \
|
||||
-e SERVER_NAME= \
|
||||
-e MULTISITE=yes \
|
||||
-e AUTO_LETS_ENCRYPT=yes \
|
||||
-e REDIRECT_HTTP_TO_HTTPS=yes \
|
||||
-l bunkerized.nginx.AUTOCONF \
|
||||
bunkerity/bunkerized-nginx
|
||||
```
|
||||
|
||||
When setting `SERVER_NAME` to nothing bunkerized-nginx won't create any server block (in case we only want automatic configuration).
|
||||
|
||||
Once bunkerized-nginx is created, let's setup the autoconf container :
|
||||
|
||||
```shell
|
||||
docker run -v /var/run/docker.sock:/var/run/docker.sock:ro \
|
||||
-v nginx_conf:/etc/nginx \
|
||||
bunkerity/bunkerized-nginx-autoconf
|
||||
```
|
||||
|
||||
We can now create a new container and use labels to dynamically configure bunkerized-nginx. Labels for automatic configuration are the same as environment variables but with the "bunkerized-nginx." prefix.
|
||||
|
||||
Here is a PHP example :
|
||||
|
||||
```shell
|
||||
docker run --network mynet \
|
||||
--name myapp \
|
||||
-v /where/are/web/files/app.domain.com:/app \
|
||||
-l bunkerized-nginx.SERVER_NAME=app.domain.com \
|
||||
-l bunkerized-nginx.REMOTE_PHP=myapp \
|
||||
-l bunkerized-nginx.REMOTE_PHP_PATH=/app \
|
||||
php:fpm
|
||||
```
|
||||
|
||||
And a reverse proxy example :
|
||||
|
||||
```shell
|
||||
docker run --network mynet \
|
||||
--name anotherapp \
|
||||
-l bunkerized-nginx.SERVER_NAME=app2.domain.com \
|
||||
-l bunkerized-nginx.USE_REVERSE_PROXY=yes \
|
||||
-l bunkerized-nginx.REVERSE_PROXY_URL=/ \
|
||||
-l bunkerized-nginx.REVERSE_PROXY_HOST=http://anotherapp
|
||||
tutum/hello-world
|
||||
```
|
||||
|
||||
## Web UI
|
||||
|
||||
**This feature exposes, for now, a security risk because you need to mount the docker socket inside a container exposing a web application. You can test it but you should not use it in servers facing the internet.**
|
||||
|
||||
A dedicated image, *bunkerized-nginx-ui*, lets you manage bunkerized-nginx instances and services configurations through a web user interface. This feature is still in beta, feel free to open a new issue if you find a bug and/or you have an idea to improve it.
|
||||
|
||||
First we need a volume that will store the configurations :
|
||||
|
||||
```shell
|
||||
docker volume create nginx_conf
|
||||
```
|
||||
|
||||
Then, we can create the bunkerized-nginx instance with the `bunkerized-nginx.UI` label and a reverse proxy configuration for our web UI :
|
||||
|
||||
```shell
|
||||
docker network create mynet
|
||||
|
||||
docker run -p 80:8080 \
|
||||
-p 443:8443 \
|
||||
--network mynet \
|
||||
-v nginx_conf:/etc/nginx \
|
||||
-v /where/are/web/files:/www:ro \
|
||||
-v /where/to/save/certificates:/etc/letsencrypt \
|
||||
-e SERVER_NAME=admin.domain.com \
|
||||
-e MULTISITE=yes \
|
||||
-e AUTO_LETS_ENCRYPT=yes \
|
||||
-e REDIRECT_HTTP_TO_HTTPS=yes \
|
||||
-e DISABLE_DEFAULT_SERVER=yes \
|
||||
-e admin.domain.com_SERVE_FILES=no \
|
||||
-e admin.domain.com_USE_AUTH_BASIC=yes \
|
||||
-e admin.domain.com_AUTH_BASIC_USER=admin \
|
||||
-e admin.domain.com_AUTH_BASIC_PASSWORD=password \
|
||||
-e admin.domain.com_USE_REVERSE_PROXY=yes \
|
||||
-e admin.domain.com_REVERSE_PROXY_URL=/webui/ \
|
||||
-e admin.domain.com_REVERSE_PROXY_HOST=http://myui:5000/ \
|
||||
-l bunkerized-nginx.UI \
|
||||
bunkerity/bunkerized-nginx
|
||||
```
|
||||
|
||||
The `AUTH_BASIC` environment variables let you define a login/password that must be provided before accessing to the web UI. At the moment, there is no authentication mechanism integrated into bunkerized-nginx-ui.
|
||||
|
||||
We can now create the bunkerized-nginx-ui container that will host the web UI behind bunkerized-nginx :
|
||||
|
||||
```shell
|
||||
docker run --network mynet \
|
||||
-v /var/run/docker.sock:/var/run/docker.sock:ro \
|
||||
-v nginx_conf:/etc/nginx \
|
||||
-e ABSOLUTE_URI=https://admin.domain.com/webui/ \
|
||||
bunkerity/bunkerized-nginx-ui
|
||||
```
|
||||
|
||||
After that, the web UI should be accessible from https://admin.domain.com/webui/.
|
||||
|
||||
## Antibot challenge
|
||||
|
||||
```shell
|
||||
@@ -221,7 +342,7 @@ When `USE_ANTIBOT` is set to *captcha*, every users visiting your website must c
|
||||
|
||||
# Tutorials and examples
|
||||
|
||||
You will find some docker-compose.yml examples in the [examples directory](https://github.com/bunkerity/bunkerized-nginx/tree/master/examples) and tutorials about bunkerized-nginx in our [blog](https://www.bunkerity.com/category/bunkerized-nginx/).
|
||||
You will find some docker-compose.yml examples in the [examples directory](https://github.com/bunkerity/bunkerized-nginx/tree/master/examples).
|
||||
|
||||
# List of environment variables
|
||||
|
||||
@@ -268,11 +389,11 @@ Values : *yes* | *no*
|
||||
Default value : *yes*
|
||||
Context : *global*, *multisite*
|
||||
If set to yes, nginx will serve files from /www directory within the container.
|
||||
A use case to not serving files is when you setup bunkerized-nginx as a reverse proxy via a custom configuration.
|
||||
A use case to not serving files is when you setup bunkerized-nginx as a reverse proxy.
|
||||
|
||||
`DNS_RESOLVERS`
|
||||
Values : *\<two IP addresses separated with a space\>*
|
||||
Default value : *127.0.0.11 8.8.8.8*
|
||||
Default value : *127.0.0.11*
|
||||
Context : *global*
|
||||
The IP addresses of the DNS resolvers to use when performing DNS lookups.
|
||||
|
||||
@@ -282,6 +403,12 @@ Default value : */www*
|
||||
Context : *global*
|
||||
The default folder where nginx will search for web files. Don't change it unless you want to make your own image.
|
||||
|
||||
`LOG_FORMAT`
|
||||
Values : *\<any values accepted by the log_format directive\>*
|
||||
Default value : *$host $remote_addr - $remote_user \[$time_local\] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent"*
|
||||
Context : *global*
|
||||
The log format used by nginx to generate logs. More info [here](http://nginx.org/en/docs/http/ngx_http_log_module.html#log_format).
|
||||
|
||||
`HTTP_PORT`
|
||||
Values : *\<any valid port greater than 1024\>*
|
||||
Default value : *8080*
|
||||
@@ -371,6 +498,13 @@ Context : *global*, *multisite*
|
||||
Only valid when `USE_REVERSE_PROXY` is set to *yes*. Let's you define the proxy_pass destination to use when acting as a reverse proxy.
|
||||
You can set multiple url/host by adding a suffix number to the variable name like this : `REVERSE_PROXY_HOST_1`, `REVERSE_PROXY_HOST_2`, `REVERSE_PROXY_HOST_3`, ...
|
||||
|
||||
`REVERSE_PROXY_WS`
|
||||
Values : *yes* | *no*
|
||||
Default value : *no*
|
||||
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.
|
||||
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`, ...
|
||||
|
||||
`PROXY_REAL_IP`
|
||||
Values : *yes* | *no*
|
||||
Default value : *no*
|
||||
@@ -539,7 +673,7 @@ The key used to uniquely identify a cached response when `USE_PROXY_CACHE` is se
|
||||
|
||||
`PROXY_CACHE_VALID`
|
||||
Values : \<*status=time list separated with space*\>
|
||||
Default value : *200=10m 301=10m 301=1h any=1m*
|
||||
Default value : *200=10m 301=10m 302=1h*
|
||||
Context : *global*, *multisite*
|
||||
Define the caching time depending on the HTTP status code (list of status=time separated with space) when `USE_PROXY_CACHE` is set to *yes*.
|
||||
|
||||
@@ -562,10 +696,16 @@ Conditions that must be met to bypass the cache when `USE_PROXY_CACHE` is set to
|
||||
`AUTO_LETS_ENCRYPT`
|
||||
Values : *yes* | *no*
|
||||
Default value : *no*
|
||||
Context : *global*
|
||||
Context : *global*, *multisite*
|
||||
If set to yes, automatic certificate generation and renewal will be setup through Let's Encrypt. This will enable HTTPS on your website for free.
|
||||
You will need to redirect the 80 port to 8080 port inside container and also set the `SERVER_NAME` environment variable.
|
||||
|
||||
`EMAIL_LETS_ENCRYPT`
|
||||
Values : *contact@yourdomain.com*
|
||||
Default value : *contact@yourdomain.com*
|
||||
Context : *global*, *multisite*
|
||||
Define the contact email address declare in the certificate.
|
||||
|
||||
### HTTP
|
||||
|
||||
`LISTEN_HTTP`
|
||||
@@ -745,7 +885,7 @@ Default value : *yes*
|
||||
Context : *global*, *multisite*
|
||||
When set to *yes*, the *secure* will be automatically added to cookies when using HTTPS.
|
||||
|
||||
`STRICT_TRANSPORT_POLICY`
|
||||
`STRICT_TRANSPORT_SECURITY`
|
||||
Values : *max-age=expireTime [; includeSubDomains] [; preload]*
|
||||
Default value : *max-age=31536000*
|
||||
Context : *global*, *multisite*
|
||||
@@ -754,7 +894,7 @@ More info [here](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Stric
|
||||
|
||||
`CONTENT_SECURITY_POLICY`
|
||||
Values : *\<directive 1\>; \<directive 2\>; ...*
|
||||
Default value : *default-src 'self'; frame-ancestors 'self'; form-action 'self'; block-all-mixed-content; sandbox allow-forms allow-same-origin allow-scripts; reflected-xss block; base-uri 'self'; referrer no-referrer*
|
||||
Default value : *object-src 'none'; frame-ancestors 'self'; form-action 'self'; block-all-mixed-content; sandbox allow-forms allow-same-origin allow-scripts allow-popups; base-uri 'self';*
|
||||
Context : *global*, *multisite*
|
||||
Policy to be used when loading resources (scripts, forms, frames, ...).
|
||||
More info [here](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy).
|
||||
@@ -810,7 +950,7 @@ Values : *yes* | *no*
|
||||
Default value : *yes*
|
||||
Context : *global*, *multisite*
|
||||
If set to yes, block clients with "bad" user agent.
|
||||
Blacklist can be found [here](https://raw.githubusercontent.com/mitchellkrogza/nginx-ultimate-bad-bot-blocker/master/_generator_lists/bad-user-agents.list).
|
||||
Blacklist can be found [here](https://raw.githubusercontent.com/mitchellkrogza/nginx-ultimate-bad-bot-blocker/master/_generator_lists/bad-user-agents.list) and [here](https://raw.githubusercontent.com/JayBizzle/Crawler-Detect/master/raw/Crawlers.txt).
|
||||
|
||||
`BLOCK_TOR_EXIT_NODE`
|
||||
Values : *yes* | *no*
|
||||
@@ -833,6 +973,13 @@ Context : *global*, *multisite*
|
||||
Is set to yes, will block known abusers.
|
||||
Blacklist can be found [here](https://iplists.firehol.org/?ipset=firehol_abusers_30d).
|
||||
|
||||
`BLOCK_REFERRER`
|
||||
Values : *yes* | *no*
|
||||
Default value : *yes*
|
||||
Context : *global*, *multisite*
|
||||
Is set to yes, will block known bad referrer header.
|
||||
Blacklist can be found [here](https://raw.githubusercontent.com/mitchellkrogza/nginx-ultimate-bad-bot-blocker/master/_generator_lists/bad-referrers.list).
|
||||
|
||||
### DNSBL
|
||||
|
||||
`USE_DNSBL`
|
||||
@@ -853,7 +1000,19 @@ The list of DNSBL zones to query when `USE_DNSBL` is set to *yes*.
|
||||
Values : *yes* | *no*
|
||||
Default value : *no*
|
||||
Context : *global*, *multisite*
|
||||
If set to *yes*, [CrowdSec](https://github.com/crowdsecurity/crowdsec) will be enabled with the [nginx collection](https://hub.crowdsec.net/author/crowdsecurity/collections/nginx). API pulls will be done automaticaly.
|
||||
If set to *yes*, [CrowdSec](https://github.com/crowdsecurity/crowdsec) will be enabled. Please note that you need a CrowdSec instance running see example [here](https://github.com/bunkerity/bunkerized-nginx/tree/master/examples/crowdsec).
|
||||
|
||||
`CROWDSEC_HOST`
|
||||
Values : *\<full URL to the CrowdSec instance API\>*
|
||||
Default value :
|
||||
Context : *global*
|
||||
The full URL to the CrowdSec API.
|
||||
|
||||
`CROWDSEC_KEY`
|
||||
Values : *\<CrowdSec bouncer key\>*
|
||||
Default value :
|
||||
Context : *global*
|
||||
The CrowdSec key given by *cscli bouncer add BouncerName*.
|
||||
|
||||
### Custom whitelisting
|
||||
|
||||
@@ -996,6 +1155,12 @@ Default : value : *15*
|
||||
Context : *global*
|
||||
The number of "strange" HTTP status codes to find between the time interval.
|
||||
|
||||
`FAIL2BAN_IGNOREIP`
|
||||
Values : *\<list of IP addresses or subnet separated with spaces\>*
|
||||
Default value : 127.0.0.1/8 192.168.0.0/16 172.16.0.0/12 10.0.0.0/8
|
||||
Context : *global*
|
||||
IPs or subnet which should never be ban by fail2ban.
|
||||
|
||||
## ClamAV
|
||||
|
||||
`USE_CLAMAV_UPLOAD`
|
||||
|
||||
18
autoconf/Dockerfile
Normal file
18
autoconf/Dockerfile
Normal file
@@ -0,0 +1,18 @@
|
||||
FROM alpine
|
||||
|
||||
RUN apk add py3-pip apache2-utils bash && \
|
||||
pip3 install docker && \
|
||||
mkdir /opt/entrypoint && \
|
||||
mkdir -p /opt/confs/site
|
||||
|
||||
COPY confs/site/ /opt/confs/site
|
||||
COPY entrypoint/* /opt/entrypoint/
|
||||
COPY autoconf/* /opt/entrypoint/
|
||||
RUN chmod +x /opt/entrypoint/*.py /opt/entrypoint/*.sh
|
||||
|
||||
# Fix CVE-2020-1971
|
||||
RUN apk add "libcrypto1.1>1.1.1g-r0" "libssl1.1>1.1.1g-r0"
|
||||
|
||||
VOLUME /etc/nginx
|
||||
|
||||
ENTRYPOINT ["/opt/entrypoint/entrypoint.py"]
|
||||
18
autoconf/Dockerfile-amd64
Normal file
18
autoconf/Dockerfile-amd64
Normal file
@@ -0,0 +1,18 @@
|
||||
FROM amd64/alpine
|
||||
|
||||
RUN apk add py3-pip apache2-utils bash && \
|
||||
pip3 install docker && \
|
||||
mkdir /opt/entrypoint && \
|
||||
mkdir -p /opt/confs/site
|
||||
|
||||
COPY confs/site/ /opt/confs/site
|
||||
COPY entrypoint/* /opt/entrypoint/
|
||||
COPY autoconf/* /opt/entrypoint/
|
||||
RUN chmod +x /opt/entrypoint/*.py /opt/entrypoint/*.sh
|
||||
|
||||
# Fix CVE-2020-1971
|
||||
RUN apk add "libcrypto1.1>1.1.1g-r0" "libssl1.1>1.1.1g-r0"
|
||||
|
||||
VOLUME /etc/nginx
|
||||
|
||||
ENTRYPOINT ["/opt/entrypoint/entrypoint.py"]
|
||||
25
autoconf/Dockerfile-arm32v7
Normal file
25
autoconf/Dockerfile-arm32v7
Normal file
@@ -0,0 +1,25 @@
|
||||
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/alpine
|
||||
|
||||
COPY --from=builder qemu-arm-static /usr/bin
|
||||
|
||||
RUN apk add py3-pip apache2-utils bash && \
|
||||
pip3 install docker && \
|
||||
mkdir /opt/entrypoint && \
|
||||
mkdir -p /opt/confs/site
|
||||
|
||||
COPY confs/site/ /opt/confs/site
|
||||
COPY entrypoint/* /opt/entrypoint/
|
||||
COPY autoconf/* /opt/entrypoint/
|
||||
RUN chmod +x /opt/entrypoint/*.py /opt/entrypoint/*.sh
|
||||
|
||||
# Fix CVE-2020-1971
|
||||
RUN apk add "libcrypto1.1>1.1.1g-r0" "libssl1.1>1.1.1g-r0"
|
||||
|
||||
VOLUME /etc/nginx
|
||||
|
||||
ENTRYPOINT ["/opt/entrypoint/entrypoint.py"]
|
||||
25
autoconf/Dockerfile-arm64v8
Normal file
25
autoconf/Dockerfile-arm64v8
Normal file
@@ -0,0 +1,25 @@
|
||||
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/alpine
|
||||
|
||||
COPY --from=builder qemu-aarch64-static /usr/bin
|
||||
|
||||
RUN apk add py3-pip apache2-utils bash && \
|
||||
pip3 install docker && \
|
||||
mkdir /opt/entrypoint && \
|
||||
mkdir -p /opt/confs/site
|
||||
|
||||
COPY confs/site/ /opt/confs/site
|
||||
COPY entrypoint/* /opt/entrypoint/
|
||||
COPY autoconf/* /opt/entrypoint/
|
||||
RUN chmod +x /opt/entrypoint/*.py /opt/entrypoint/*.sh
|
||||
|
||||
# Fix CVE-2020-1971
|
||||
RUN apk add "libcrypto1.1>1.1.1g-r0" "libssl1.1>1.1.1g-r0"
|
||||
|
||||
VOLUME /etc/nginx
|
||||
|
||||
ENTRYPOINT ["/opt/entrypoint/entrypoint.py"]
|
||||
18
autoconf/Dockerfile-i386
Normal file
18
autoconf/Dockerfile-i386
Normal file
@@ -0,0 +1,18 @@
|
||||
FROM i386/alpine
|
||||
|
||||
RUN apk add py3-pip apache2-utils bash && \
|
||||
pip3 install docker && \
|
||||
mkdir /opt/entrypoint && \
|
||||
mkdir -p /opt/confs/site
|
||||
|
||||
COPY confs/site/ /opt/confs/site
|
||||
COPY entrypoint/* /opt/entrypoint/
|
||||
COPY autoconf/* /opt/entrypoint/
|
||||
RUN chmod +x /opt/entrypoint/*.py /opt/entrypoint/*.sh
|
||||
|
||||
# Fix CVE-2020-1971
|
||||
RUN apk add "libcrypto1.1>1.1.1g-r0" "libssl1.1>1.1.1g-r0"
|
||||
|
||||
VOLUME /etc/nginx
|
||||
|
||||
ENTRYPOINT ["/opt/entrypoint/entrypoint.py"]
|
||||
85
autoconf/config.py
Normal file
85
autoconf/config.py
Normal file
@@ -0,0 +1,85 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
import utils
|
||||
import subprocess, shutil, os, traceback
|
||||
|
||||
def generate(instances, vars) :
|
||||
try :
|
||||
# Get env vars from bunkerized-nginx instances
|
||||
vars_instances = {}
|
||||
for instance_id, instance in instances.items() :
|
||||
for var_value in instance.attrs["Config"]["Env"] :
|
||||
var = var_value.split("=")[0]
|
||||
value = var_value.replace(var + "=", "", 1)
|
||||
vars_instances[var] = value
|
||||
vars_defaults = vars.copy()
|
||||
vars_defaults.update(vars_instances)
|
||||
vars_defaults.update(vars)
|
||||
# Call site-config.sh to generate the config
|
||||
proc = subprocess.run(["/opt/entrypoint/site-config.sh", vars["SERVER_NAME"]], env=vars_defaults, capture_output=True)
|
||||
if proc.returncode == 0 :
|
||||
return True
|
||||
except Exception as e :
|
||||
traceback.print_exc()
|
||||
utils.log("[!] Error while generating config : " + str(e))
|
||||
return False
|
||||
|
||||
def activate(instances, vars) :
|
||||
try :
|
||||
# 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")
|
||||
return False
|
||||
|
||||
# Include the server conf
|
||||
utils.replace_in_file("/etc/nginx/nginx.conf", "}", "include /etc/nginx/" + vars["SERVER_NAME"] + "/server.conf;\n}")
|
||||
|
||||
# Send SIGHUP to all running instances
|
||||
for instance_id, instance in instances.items() :
|
||||
if instance.status == "running" :
|
||||
try :
|
||||
instance.kill("SIGHUP")
|
||||
utils.log("[*] Sent SIGHUP signal to bunkerized-nginx instance " + instance.name + " / " + instance.id)
|
||||
except docker.errors.APIError as e :
|
||||
utils.log("[!] Docker error while sending SIGHUP signal : " + str(e))
|
||||
return True
|
||||
except Exception as e :
|
||||
utils.log("[!] Error while activating config : " + str(e))
|
||||
return False
|
||||
|
||||
def deactivate(instances, vars) :
|
||||
try :
|
||||
# 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")
|
||||
return False
|
||||
|
||||
# Remove the include
|
||||
utils.replace_in_file("/etc/nginx/nginx.conf", "include /etc/nginx/" + vars["SERVER_NAME"] + "/server.conf;\n", "")
|
||||
|
||||
# Send SIGHUP to all running instances
|
||||
for instance_id, instance in instances.items() :
|
||||
if instance.status == "running" :
|
||||
try :
|
||||
instance.kill("SIGHUP")
|
||||
utils.log("[*] Sent SIGHUP signal to bunkerized-nginx instance " + instance.name + " / " + instance.id)
|
||||
except docker.errors.APIError as e :
|
||||
utils.log("[!] Docker error while sending SIGHUP signal : " + str(e))
|
||||
return True
|
||||
except Exception as e :
|
||||
utils.log("[!] Error while deactivating config : " + str(e))
|
||||
return False
|
||||
|
||||
def remove(instances, vars) :
|
||||
try :
|
||||
# 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")
|
||||
return False
|
||||
|
||||
# Remove the folder
|
||||
shutil.rmtree("/etc/nginx/" + vars["SERVER_NAME"])
|
||||
return True
|
||||
except Exception as e :
|
||||
utils.log("[!] Error while deactivating config : " + str(e))
|
||||
return False
|
||||
118
autoconf/entrypoint.py
Normal file
118
autoconf/entrypoint.py
Normal file
@@ -0,0 +1,118 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
import utils, config
|
||||
import docker, os, stat, sys
|
||||
|
||||
def process(container, event) :
|
||||
global instances, containers
|
||||
|
||||
# Process instance event
|
||||
if "bunkerized-nginx.AUTOCONF" in container.labels :
|
||||
if event == "create" :
|
||||
instances[container.id] = container
|
||||
utils.log("[*] bunkerized-nginx instance created : " + container.name + " / " + container.id)
|
||||
elif event == "start" :
|
||||
instances[container.id].reload()
|
||||
utils.log("[*] bunkerized-nginx instance started : " + container.name + " / " + container.id)
|
||||
elif event == "die" :
|
||||
instances[container.id].reload()
|
||||
utils.log("[*] bunkerized-nginx instance stopped : " + container.name + " / " + container.id)
|
||||
elif event == "destroy" :
|
||||
del instances[container.id]
|
||||
utils.log("[*] bunkerized-nginx instance removed : " + container.name + " / " + container.id)
|
||||
|
||||
# Process container event
|
||||
elif "bunkerized-nginx.SERVER_NAME" in container.labels :
|
||||
# Convert labels to env vars
|
||||
vars = { k.replace("bunkerized-nginx.", "", 1) : v for k, v in container.labels.items() if k.startswith("bunkerized-nginx.")}
|
||||
if event == "create" :
|
||||
if config.generate(instances, vars) :
|
||||
utils.log("[*] Generated config for " + vars["SERVER_NAME"])
|
||||
containers[container.id] = container
|
||||
else :
|
||||
utils.log("[!] Can't generate config for " + vars["SERVER_NAME"])
|
||||
elif event == "start" :
|
||||
if container.id in containers :
|
||||
containers[container.id].reload()
|
||||
if config.activate(instances, vars) :
|
||||
utils.log("[*] Activated config for " + vars["SERVER_NAME"])
|
||||
else :
|
||||
utils.log("[!] Can't activate config for " + vars["SERVER_NAME"])
|
||||
elif event == "die" :
|
||||
if container.id in containers :
|
||||
containers[container.id].reload()
|
||||
if config.deactivate(instances, vars) :
|
||||
utils.log("[*] Deactivated config for " + vars["SERVER_NAME"])
|
||||
else :
|
||||
utils.log("[!] Can't deactivate config for " + vars["SERVER_NAME"])
|
||||
elif event == "destroy" :
|
||||
if container.id in containers :
|
||||
del containers[container.id]
|
||||
if config.remove(vars) :
|
||||
utils.log("[*] Removed config for " + vars["SERVER_NAME"])
|
||||
else :
|
||||
utils.log("[!] Can't remove config for " + vars["SERVER_NAME"])
|
||||
|
||||
# Connect to the endpoint
|
||||
endpoint = "/var/run/docker.sock"
|
||||
if not os.path.exists(endpoint) or not stat.S_ISSOCK(os.stat(endpoint).st_mode) :
|
||||
utils.log("[!] /var/run/docker.sock not found (is it mounted ?)")
|
||||
sys.exit(1)
|
||||
try :
|
||||
client = docker.DockerClient(base_url='unix:///var/run/docker.sock')
|
||||
except Exception as e :
|
||||
utils.log("[!] Can't instantiate DockerClient : " + str(e))
|
||||
sys.exit(2)
|
||||
|
||||
# Get all bunkerized-nginx instances and web services created before
|
||||
instances = {}
|
||||
containers = {}
|
||||
try :
|
||||
before = client.containers.list(all=True, filters={"label" : "bunkerized-nginx.AUTOCONF"}) + client.containers.list(all=True, filters={"label" : "bunkerized-nginx.SERVER_NAME"})
|
||||
except docker.errors.APIError as e :
|
||||
utils.log("[!] Docker API error " + str(e))
|
||||
sys.exit(3)
|
||||
# Process instances first
|
||||
for instance in before :
|
||||
if "bunkerized-nginx.AUTOCONF" in instance.labels :
|
||||
if instance.status in ("restarting", "running", "created", "exited") :
|
||||
process(instance, "create")
|
||||
if instance.status == "running" :
|
||||
process(instance, "start")
|
||||
# Containers after
|
||||
for container in before :
|
||||
if "bunkerized-nginx.SERVER_NAME" in container.labels :
|
||||
if container.status in ("restarting", "running", "created", "exited") :
|
||||
process(container, "create")
|
||||
if container.status == "running" :
|
||||
process(container, "start")
|
||||
|
||||
# Process events received from Docker
|
||||
try :
|
||||
for event in client.events(decode=True) :
|
||||
|
||||
# Process only container events
|
||||
if event["Type"] != "container" :
|
||||
continue
|
||||
|
||||
# Get Container object
|
||||
try :
|
||||
container = client.containers.get(event["id"])
|
||||
except docker.errors.NotFound as e :
|
||||
continue
|
||||
|
||||
# Check if there is an interesting label
|
||||
interesting = False
|
||||
for label in container.labels :
|
||||
if label in ("bunkerized-nginx.SERVER_NAME", "bunkerized-nginx.AUTOCONF") :
|
||||
interesting = True
|
||||
break
|
||||
if not interesting :
|
||||
continue
|
||||
|
||||
# Process the event
|
||||
process(container, event["Action"])
|
||||
|
||||
except docker.errors.APIError as e :
|
||||
utils.log("[!] Docker API error " + str(e))
|
||||
sys.exit(4)
|
||||
12
autoconf/hooks/post_push
Normal file
12
autoconf/hooks/post_push
Normal file
@@ -0,0 +1,12 @@
|
||||
#!/bin/bash
|
||||
|
||||
curl -Lo manifest-tool https://github.com/estesp/manifest-tool/releases/download/v1.0.3/manifest-tool-linux-amd64
|
||||
chmod +x manifest-tool
|
||||
|
||||
VERSION=$(cat VERSION | tr -d '\n')
|
||||
if [ "$SOURCE_BRANCH" = "dev" ] ; then
|
||||
./manifest-tool push from-args --ignore-missing --platforms linux/amd64,linux/386,linux/arm/v7,linux/arm64/v8 --template bunkerity/bunkerized-nginx-autoconf:dev-ARCHVARIANT --target bunkerity/bunkerized-nginx-autoconf:dev
|
||||
elif [ "$SOURCE_BRANCH" = "master" ] ; then
|
||||
./manifest-tool push from-args --ignore-missing --platforms linux/amd64,linux/386,linux/arm/v7,linux/arm64/v8 --template bunkerity/bunkerized-nginx-autoconf:ARCHVARIANT --target bunkerity/bunkerized-nginx-autoconf:${VERSION}
|
||||
./manifest-tool push from-args --ignore-missing --platforms linux/amd64,linux/386,linux/arm/v7,linux/arm64/v8 --template bunkerity/bunkerized-nginx-autoconf:ARCHVARIANT --target bunkerity/bunkerized-nginx-autoconf:latest
|
||||
fi
|
||||
5
autoconf/hooks/pre_build
Normal file
5
autoconf/hooks/pre_build
Normal file
@@ -0,0 +1,5 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Register qemu-*-static for all supported processors except the
|
||||
# current one, but also remove all registered binfmt_misc before
|
||||
docker run --rm --privileged multiarch/qemu-user-static:register --reset
|
||||
13
autoconf/utils.py
Normal file
13
autoconf/utils.py
Normal file
@@ -0,0 +1,13 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
import datetime
|
||||
|
||||
def log(event) :
|
||||
print("[" + str(datetime.datetime.now().replace(microsecond=0)) + "] " + event, flush=True)
|
||||
|
||||
def replace_in_file(file, old_str, new_str) :
|
||||
with open(file) as f :
|
||||
data = f.read()
|
||||
data = data[::-1].replace(old_str[::-1], new_str[::-1], 1)[::-1]
|
||||
with open(file, "w") as f :
|
||||
f.write(data)
|
||||
26
compile.sh
26
compile.sh
@@ -30,7 +30,7 @@ function git_secure_clone() {
|
||||
NTASK=$(nproc)
|
||||
|
||||
# install build dependencies
|
||||
apk add --no-cache --virtual build autoconf libtool automake git geoip-dev yajl-dev g++ curl-dev libxml2-dev pcre-dev make linux-headers libmaxminddb-dev musl-dev lua-dev gd-dev gnupg brotli-dev
|
||||
apk add --no-cache --virtual build autoconf libtool automake git geoip-dev yajl-dev g++ gcc curl-dev libxml2-dev pcre-dev make linux-headers libmaxminddb-dev musl-dev lua-dev gd-dev gnupg brotli-dev openssl-dev
|
||||
|
||||
# compile and install ModSecurity library
|
||||
cd /tmp
|
||||
@@ -111,6 +111,30 @@ git_secure_clone https://github.com/ledgetech/lua-resty-http.git 984fdc260543763
|
||||
cd lua-resty-http
|
||||
make install
|
||||
cd /tmp
|
||||
git_secure_clone https://github.com/Neopallium/lualogging.git cadc4e8fd652be07a65b121a3e024838db330c15
|
||||
cd lualogging
|
||||
cp -r src/* /usr/local/lib/lua
|
||||
cd /tmp
|
||||
git_secure_clone https://github.com/diegonehab/luasocket.git 5b18e475f38fcf28429b1cc4b17baee3b9793a62
|
||||
cd luasocket
|
||||
make -j $NTASK
|
||||
make CDIR_linux=lib/lua/5.1 LDIR_linux=lib/lua install
|
||||
cd /tmp
|
||||
git_secure_clone https://github.com/brunoos/luasec.git c6704919bdc85f3324340bdb35c2795a02f7d625
|
||||
cd luasec
|
||||
make linux -j $NTASK
|
||||
make LUACPATH=/usr/local/lib/lua/5.1 LUAPATH=/usr/local/lib/lua install
|
||||
cd /tmp
|
||||
git_secure_clone https://github.com/crowdsecurity/lua-cs-bouncer.git 71c4247d6b66234e3f3426b2ea721ad50c741579
|
||||
cd lua-cs-bouncer
|
||||
mkdir /usr/local/lib/lua/crowdsec
|
||||
cp lib/*.lua /usr/local/lib/lua/crowdsec
|
||||
cp template.conf /usr/local/lib/lua/crowdsec/crowdsec.conf
|
||||
sed -i 's/^API_URL=.*/API_URL=%CROWDSEC_HOST%/' /usr/local/lib/lua/crowdsec/crowdsec.conf
|
||||
sed -i 's/^API_KEY=.*/API_KEY=%CROWDSEC_KEY%/' /usr/local/lib/lua/crowdsec/crowdsec.conf
|
||||
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/openresty/lua-nginx-module.git 2d23bc4f0a29ed79aaaa754c11bffb1080aa44ba
|
||||
export LUAJIT_LIB=/usr/local/lib
|
||||
export LUAJIT_INC=/usr/local/include/luajit-2.1
|
||||
|
||||
0
confs/global/block-abusers.conf
Normal file
0
confs/global/block-abusers.conf
Normal file
0
confs/global/block-proxies.conf
Normal file
0
confs/global/block-proxies.conf
Normal file
0
confs/global/block-tor-exit-node.conf
Normal file
0
confs/global/block-tor-exit-node.conf
Normal file
1
confs/global/map-referrer.conf
Normal file
1
confs/global/map-referrer.conf
Normal file
@@ -0,0 +1 @@
|
||||
map $http_referer $bad_referrer { hostnames; default no; }
|
||||
@@ -1,4 +1 @@
|
||||
map $http_user_agent $bad_user_agent {
|
||||
default no;
|
||||
%BLOCK_USER_AGENT%
|
||||
}
|
||||
map $http_user_agent $bad_user_agent { default no; }
|
||||
|
||||
11
confs/global/multisite-default-server-https.conf
Normal file
11
confs/global/multisite-default-server-https.conf
Normal file
@@ -0,0 +1,11 @@
|
||||
listen 0.0.0.0:%HTTPS_PORT% default_server ssl %HTTP2%;
|
||||
ssl_certificate /etc/nginx/default-cert.pem;
|
||||
ssl_certificate_key /etc/nginx/default-key.pem;
|
||||
ssl_protocols %HTTPS_PROTOCOLS%;
|
||||
ssl_prefer_server_ciphers off;
|
||||
ssl_session_tickets off;
|
||||
ssl_session_timeout 1d;
|
||||
ssl_session_cache shared:MozSSL:10m;
|
||||
%SSL_DHPARAM%
|
||||
%SSL_CIPHERS%
|
||||
%LETS_ENCRYPT_WEBROOT%
|
||||
@@ -0,0 +1,3 @@
|
||||
location ~ ^/.well-known/acme-challenge/ {
|
||||
root /acme-challenge;
|
||||
}
|
||||
6
confs/global/multisite-default-server.conf
Normal file
6
confs/global/multisite-default-server.conf
Normal file
@@ -0,0 +1,6 @@
|
||||
server {
|
||||
%LISTEN_HTTP%
|
||||
server_name _;
|
||||
%USE_HTTPS%
|
||||
%MULTISITE_DISABLE_DEFAULT_SERVER%
|
||||
}
|
||||
3
confs/global/multisite-disable-default-server.conf
Normal file
3
confs/global/multisite-disable-default-server.conf
Normal file
@@ -0,0 +1,3 @@
|
||||
location / {
|
||||
return 444;
|
||||
}
|
||||
21
confs/global/nginx-temp.conf
Normal file
21
confs/global/nginx-temp.conf
Normal file
@@ -0,0 +1,21 @@
|
||||
daemon on;
|
||||
|
||||
pid /tmp/nginx-temp.pid;
|
||||
|
||||
events {
|
||||
worker_connections 1024;
|
||||
use epoll;
|
||||
}
|
||||
|
||||
http {
|
||||
server {
|
||||
listen 0.0.0.0:%HTTP_PORT% default_server;
|
||||
server_name _;
|
||||
location ~ ^/.well-known/acme-challenge/ {
|
||||
root /acme-challenge;
|
||||
}
|
||||
location / {
|
||||
return 444;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -48,7 +48,8 @@ http {
|
||||
default_type application/octet-stream;
|
||||
|
||||
# write logs to local syslog
|
||||
access_log syslog:server=unix:/dev/log,nohostname,facility=local0,severity=notice combined;
|
||||
log_format logf '%LOG_FORMAT%';
|
||||
access_log syslog:server=unix:/dev/log,nohostname,facility=local0,severity=notice logf;
|
||||
error_log syslog:server=unix:/dev/log,nohostname,facility=local0 warn;
|
||||
|
||||
# temp paths
|
||||
@@ -70,6 +71,9 @@ http {
|
||||
# resolvers to use
|
||||
resolver %DNS_RESOLVERS% ipv6=off;
|
||||
|
||||
# remove ports when sending redirects
|
||||
port_in_redirect off;
|
||||
|
||||
# lua path and dicts
|
||||
lua_package_path "/usr/local/lib/lua/?.lua;;";
|
||||
%WHITELIST_IP_CACHE%
|
||||
@@ -90,12 +94,18 @@ http {
|
||||
# list of blocked user agents
|
||||
%BLOCK_USER_AGENT%
|
||||
|
||||
# list of blocked referrers
|
||||
%BLOCK_REFERRER%
|
||||
|
||||
# zone for proxy_cache
|
||||
%PROXY_CACHE_PATH%
|
||||
|
||||
# custom http confs
|
||||
include /http-confs/*.conf;
|
||||
|
||||
# default server when MULTISITE=yes
|
||||
%MULTISITE_DEFAULT_SERVER%
|
||||
|
||||
# server config(s)
|
||||
%INCLUDE_SERVER%
|
||||
}
|
||||
|
||||
@@ -7,6 +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)
|
||||
return ngx.exit(ngx.HTTP_FORBIDDEN)
|
||||
end
|
||||
local img, res = captcha.get_challenge()
|
||||
@@ -21,16 +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)
|
||||
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)
|
||||
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)
|
||||
return ngx.redirect("%ANTIBOT_URI%")
|
||||
end
|
||||
cookie.set({captcha = "ok"})
|
||||
|
||||
@@ -7,6 +7,7 @@ location = %ANTIBOT_URI% {
|
||||
local cookie = require "cookie"
|
||||
local javascript = require "javascript"
|
||||
if not cookie.is_set("challenge") then
|
||||
ngx.log(ngx.WARN, "[ANTIBOT] javascript fail (1) for " .. ngx.var.remote_addr)
|
||||
return ngx.exit(ngx.HTTP_FORBIDDEN)
|
||||
end
|
||||
local challenge = cookie.get("challenge")
|
||||
@@ -20,16 +21,19 @@ location = %ANTIBOT_URI% {
|
||||
local cookie = require "cookie"
|
||||
local javascript = require "javascript"
|
||||
if not cookie.is_set("challenge") then
|
||||
ngx.log(ngx.WARN, "[ANTIBOT] javascript 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["challenge"] then
|
||||
ngx.log(ngx.WARN, "[ANTIBOT] javascript fail (3) for " .. ngx.var.remote_addr)
|
||||
return ngx.exit(ngx.HTTP_FORBIDDEN)
|
||||
end
|
||||
local challenge = args["challenge"]
|
||||
local check = javascript.check(cookie.get("challenge"), challenge)
|
||||
if not check then
|
||||
ngx.log(ngx.WARN, "[ANTIBOT] javascript fail (4) for " .. ngx.var.remote_addr)
|
||||
return ngx.exit(ngx.HTTP_FORBIDDEN)
|
||||
end
|
||||
cookie.set({javascript = "ok"})
|
||||
|
||||
@@ -7,6 +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)
|
||||
return ngx.exit(ngx.HTTP_FORBIDDEN)
|
||||
end
|
||||
local code = recaptcha.get_code("%ANTIBOT_URI%", "%ANTIBOT_RECAPTCHA_SITEKEY%")
|
||||
@@ -19,17 +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)
|
||||
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)
|
||||
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, "client has recaptcha score of " .. tostring(check))
|
||||
ngx.log(ngx.WARN, "[ANTIBOT] recaptcha fail (4) for " .. ngx.var.remote_addr .. " (score = " .. tostring(check) .. ")")
|
||||
return ngx.exit(ngx.HTTP_FORBIDDEN)
|
||||
end
|
||||
cookie.set({recaptcha = "ok"})
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
auth_basic "%AUTH_BASIC_TEXT%";
|
||||
auth_basic_user_file /etc/nginx/.htpasswd;
|
||||
auth_basic_user_file %NGINX_PREFIX%.htpasswd;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
location %AUTH_BASIC_LOCATION% {
|
||||
auth_basic "%AUTH_BASIC_TEXT%";
|
||||
auth_basic_user_file /etc/nginx/.htpasswd;
|
||||
auth_basic_user_file %NGINX_PREFIX%.htpasswd;
|
||||
}
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
if ($bad_user_agent = yes) {
|
||||
return 444;
|
||||
}
|
||||
25
confs/site/fastcgi.conf
Normal file
25
confs/site/fastcgi.conf
Normal file
@@ -0,0 +1,25 @@
|
||||
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||
fastcgi_param QUERY_STRING $query_string;
|
||||
fastcgi_param REQUEST_METHOD $request_method;
|
||||
fastcgi_param CONTENT_TYPE $content_type;
|
||||
fastcgi_param CONTENT_LENGTH $content_length;
|
||||
|
||||
fastcgi_param SCRIPT_NAME $fastcgi_script_name;
|
||||
fastcgi_param REQUEST_URI $request_uri;
|
||||
fastcgi_param DOCUMENT_URI $document_uri;
|
||||
fastcgi_param DOCUMENT_ROOT $document_root;
|
||||
fastcgi_param SERVER_PROTOCOL $server_protocol;
|
||||
fastcgi_param REQUEST_SCHEME $scheme;
|
||||
fastcgi_param HTTPS $https if_not_empty;
|
||||
|
||||
fastcgi_param GATEWAY_INTERFACE CGI/1.1;
|
||||
fastcgi_param SERVER_SOFTWARE nginx/$nginx_version;
|
||||
|
||||
fastcgi_param REMOTE_ADDR $remote_addr;
|
||||
fastcgi_param REMOTE_PORT $remote_port;
|
||||
fastcgi_param SERVER_ADDR $server_addr;
|
||||
fastcgi_param SERVER_PORT $server_port;
|
||||
fastcgi_param SERVER_NAME $server_name;
|
||||
|
||||
# PHP only, required if PHP was built with --enable-force-cgi-redirect
|
||||
fastcgi_param REDIRECT_STATUS 200;
|
||||
@@ -9,3 +9,4 @@ ssl_session_cache shared:MozSSL:10m;
|
||||
%STRICT_TRANSPORT_SECURITY%
|
||||
%SSL_DHPARAM%
|
||||
%SSL_CIPHERS%
|
||||
%LETS_ENCRYPT_WEBROOT%
|
||||
|
||||
3
confs/site/lets-encrypt-webroot.conf
Normal file
3
confs/site/lets-encrypt-webroot.conf
Normal file
@@ -0,0 +1,3 @@
|
||||
location ~ ^/.well-known/acme-challenge/ {
|
||||
root /acme-challenge;
|
||||
}
|
||||
@@ -5,6 +5,8 @@ access_by_lua_block {
|
||||
|
||||
local use_whitelist_ip = %USE_WHITELIST_IP%
|
||||
local use_whitelist_reverse = %USE_WHITELIST_REVERSE%
|
||||
local use_user_agent = %USE_USER_AGENT%
|
||||
local use_referrer = %USE_REFERRER%
|
||||
local use_country = %USE_COUNTRY%
|
||||
local use_blacklist_ip = %USE_BLACKLIST_IP%
|
||||
local use_blacklist_reverse = %USE_BLACKLIST_REVERSE%
|
||||
@@ -35,11 +37,6 @@ if use_whitelist_reverse and whitelist.reverse_cached_ok() then
|
||||
ngx.exit(ngx.OK)
|
||||
end
|
||||
|
||||
-- check if country is allowed
|
||||
if use_country and ngx.var.allowed_country == "no" then
|
||||
ngx.exit(ngx.HTTP_FORBIDDEN)
|
||||
end
|
||||
|
||||
-- check if already in blacklist cache
|
||||
if use_blacklist_ip and blacklist.ip_cached_ko() then
|
||||
ngx.exit(ngx.HTTP_FORBIDDEN)
|
||||
@@ -81,6 +78,24 @@ if use_blacklist_reverse and not blacklist.reverse_cached() then
|
||||
end
|
||||
end
|
||||
|
||||
-- check if user-agent is allowed
|
||||
if use_user_agent and ngx.var.bad_user_agent == "yes" then
|
||||
ngx.log(ngx.WARN, "[BLOCK] User-Agent " .. ngx.var.http_user_agent .. " is blacklisted")
|
||||
ngx.exit(ngx.HTTP_FORBIDDEN)
|
||||
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.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.exit(ngx.HTTP_FORBIDDEN)
|
||||
end
|
||||
|
||||
-- check if IP is in DNSBLs (only if not in cache)
|
||||
if use_dnsbl and not dnsbl.cached() then
|
||||
if dnsbl.check() then
|
||||
@@ -107,6 +122,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)
|
||||
return ngx.exit(ngx.HTTP_FORBIDDEN)
|
||||
else
|
||||
if ngx.var.request_uri == antibot_uri then
|
||||
@@ -128,7 +144,7 @@ end
|
||||
-- captcha check
|
||||
if use_antibot_captcha then
|
||||
if not cookie.is_set("captcha") then
|
||||
if ngx.var.request_uri ~= antibot_uri and ngx.var.request_uri ~= "/favicon.ico" then
|
||||
if ngx.var.request_uri ~= antibot_uri then
|
||||
cookie.set({uri = ngx.var.request_uri})
|
||||
return ngx.redirect(antibot_uri)
|
||||
end
|
||||
@@ -138,7 +154,7 @@ end
|
||||
-- recaptcha check
|
||||
if use_antibot_recaptcha then
|
||||
if not cookie.is_set("recaptcha") then
|
||||
if ngx.var.request_uri ~= antibot_uri and ngx.var.request_uri ~= "/favicon.ico" then
|
||||
if ngx.var.request_uri ~= antibot_uri then
|
||||
cookie.set({uri = ngx.var.request_uri})
|
||||
return ngx.redirect(antibot_uri)
|
||||
end
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Protocol $scheme;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
location %REVERSE_PROXY_URL% {
|
||||
proxy_pass %REVERSE_PROXY_HOST%;
|
||||
%REVERSE_PROXY_HEADERS%
|
||||
%REVERSE_PROXY_WS%
|
||||
}
|
||||
|
||||
@@ -24,7 +24,6 @@ server {
|
||||
%REFERRER_POLICY%
|
||||
%FEATURE_POLICY%
|
||||
%PERMISSIONS_POLICY%
|
||||
%BLOCK_USER_AGENT%
|
||||
%BLOCK_TOR_EXIT_NODE%
|
||||
%BLOCK_PROXIES%
|
||||
%BLOCK_ABUSERS%
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
function git_secure_clone() {
|
||||
repo="$1"
|
||||
commit="$2"
|
||||
folder=$(echo "$repo" | sed -E "s@https://github.com/.*/(.*)\.git@\1@")
|
||||
git clone "$repo"
|
||||
cd "$folder"
|
||||
git checkout "${commit}^{commit}"
|
||||
if [ $? -ne 0 ] ; then
|
||||
echo "[!] Commit hash $commit is absent from repository $repo !"
|
||||
exit 1
|
||||
fi
|
||||
cd ..
|
||||
}
|
||||
|
||||
NTASK=$(nproc)
|
||||
|
||||
# install build dependencies
|
||||
apk add --no-cache --virtual build git bash lua-dev mariadb-dev sqlite-dev gettext make go jq
|
||||
|
||||
# build and install crowdsec
|
||||
cd /tmp
|
||||
git_secure_clone https://github.com/crowdsecurity/crowdsec.git 2fdf7624da381af605baa46f319f2ed3015807e4
|
||||
cd crowdsec
|
||||
make -j $NTASK build
|
||||
./wizard.sh --bininstall
|
||||
sed -i 's/^machine_id:.*//' /etc/crowdsec/config/api.yaml
|
||||
sed -i 's/^password:.*//' /etc/crowdsec/config/api.yaml
|
||||
|
||||
# install nginx collection
|
||||
cscli update
|
||||
cscli install collection crowdsecurity/nginx
|
||||
sed -i "s/^filter:.*$/filter: \"evt.Line.Labels.type == 'nginx'\"/" /etc/crowdsec/config/parsers/s01-parse/nginx-logs.yaml
|
||||
sed -i 's/apply_on: message/apply_on: Line.Raw/g' /etc/crowdsec/config/parsers/s01-parse/nginx-logs.yaml
|
||||
|
||||
# build and install luasql
|
||||
cd /tmp
|
||||
git_secure_clone https://github.com/keplerproject/luasql.git 22d4a911f35cf851af9db71124e3998d96fb3fa1
|
||||
cd luasql
|
||||
make -j $NTASK sqlite3 mysql
|
||||
mkdir /usr/local/lib/lua/5.1/luasql
|
||||
cp src/*.so /usr/local/lib/lua/5.1/luasql
|
||||
|
||||
# install lualogging
|
||||
cd /tmp
|
||||
git_secure_clone https://github.com/Neopallium/lualogging.git cadc4e8fd652be07a65b121a3e024838db330c15
|
||||
cd lualogging
|
||||
cp -r src/* /usr/local/lib/lua
|
||||
|
||||
# install cs-lua-lib
|
||||
cd /tmp
|
||||
git_secure_clone https://github.com/crowdsecurity/cs-lua-lib.git 97e55a555a8f6d46c1c2032825a4578090283301
|
||||
cd cs-lua-lib
|
||||
mkdir /usr/local/lib/lua/crowdsec
|
||||
cp lib/*.lua /usr/local/lib/lua/crowdsec
|
||||
cp template.conf /usr/local/lib/lua/crowdsec/crowdsec.conf
|
||||
rm /usr/local/lib/lua/crowdsec/lrucache.lua
|
||||
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
|
||||
|
||||
# remove build dependencies
|
||||
apk del build
|
||||
@@ -1,6 +1,7 @@
|
||||
#!/bin/bash
|
||||
|
||||
MULTISITE="${MULTISITE-no}"
|
||||
LOG_FORMAT="${LOG_FORMAT-\$host \$remote_addr - \$remote_user [\$time_local] \"\$request\" \$status \$body_bytes_sent \"\$http_referer\" \"\$http_user_agent\"}"
|
||||
HTTP_PORT="${HTTP_PORT-8080}"
|
||||
HTTPS_PORT="${HTTPS_PORT-8443}"
|
||||
MAX_CLIENT_SIZE="${MAX_CLIENT_SIZE-10m}"
|
||||
@@ -20,7 +21,7 @@ PROXY_CACHE_PATH_PARAMS="${PROXY_CACHE_PATH_PARAMS-max_size=100m}"
|
||||
PROXY_CACHE_METHODS="${PROXY_CACHE_METHODS-GET HEAD}"
|
||||
PROXY_CACHE_MIN_USES="${PROXY_CACHE_MIN_USES-2}"
|
||||
PROXY_CACHE_KEY="${PROXY_CACHE_KEY-\$scheme\$host\$request_uri}"
|
||||
PROXY_CACHE_VALID="${PROXY_CACHE_VALID-200=10m 301=10m 301=1h any=1m}"
|
||||
PROXY_CACHE_VALID="${PROXY_CACHE_VALID-200=10m 301=10m 302=1h}"
|
||||
PROXY_NO_CACHE="${PROXY_NO_CACHE-\$http_authorization}"
|
||||
PROXY_CACHE_BYPASS="${PROXY_CACHE_BYPASS-\$http_authorization}"
|
||||
USE_GZIP="${USE_GZIP-no}"
|
||||
@@ -44,6 +45,7 @@ DISABLE_DEFAULT_SERVER="${DISABLE_DEFAULT_SERVER-no}"
|
||||
SERVER_NAME="${SERVER_NAME-www.bunkerity.com}"
|
||||
ALLOWED_METHODS="${ALLOWED_METHODS-GET|POST|HEAD}"
|
||||
BLOCK_USER_AGENT="${BLOCK_USER_AGENT-yes}"
|
||||
BLOCK_REFERRER="${BLOCK_REFERRER-yes}"
|
||||
BLOCK_TOR_EXIT_NODE="${BLOCK_TOR_EXIT_NODE-yes}"
|
||||
BLOCK_PROXIES="${BLOCK_PROXIES-yes}"
|
||||
BLOCK_ABUSERS="${BLOCK_ABUSERS-yes}"
|
||||
@@ -53,7 +55,7 @@ HTTPS_PROTOCOLS="${HTTPS_PROTOCOLS-TLSv1.2 TLSv1.3}"
|
||||
STRICT_TRANSPORT_SECURITY="${STRICT_TRANSPORT_SECURITY-max-age=31536000}"
|
||||
USE_MODSECURITY="${USE_MODSECURITY-yes}"
|
||||
USE_MODSECURITY_CRS="${USE_MODSECURITY_CRS-yes}"
|
||||
CONTENT_SECURITY_POLICY="${CONTENT_SECURITY_POLICY-object-src 'none'; frame-ancestors 'self'; form-action 'self'; block-all-mixed-content; sandbox allow-forms allow-same-origin allow-scripts; base-uri 'self';}"
|
||||
CONTENT_SECURITY_POLICY="${CONTENT_SECURITY_POLICY-object-src 'none'; frame-ancestors 'self'; form-action 'self'; block-all-mixed-content; sandbox allow-forms allow-same-origin allow-scripts allow-popups; base-uri 'self';}"
|
||||
COOKIE_FLAGS="${COOKIE_FLAGS-* HttpOnly SameSite=Lax}"
|
||||
COOKIE_AUTO_SECURE_FLAG="${COOKIE_AUTO_SECURE_FLAG-yes}"
|
||||
SERVE_FILES="${SERVE_FILES-yes}"
|
||||
@@ -65,6 +67,7 @@ FAIL2BAN_STATUS_CODES="${FAIL2BAN_STATUS_CODES-400|401|403|404|405|444}"
|
||||
FAIL2BAN_BANTIME="${FAIL2BAN_BANTIME-3600}"
|
||||
FAIL2BAN_FINDTIME="${FAIL2BAN_FINDTIME-60}"
|
||||
FAIL2BAN_MAXRETRY="${FAIL2BAN_MAXRETRY-15}"
|
||||
FAIL2BAN_IGNOREIP="${FAIL2BAN_IGNOREIP-127.0.0.1/8 192.168.0.0/16 172.16.0.0/12 10.0.0.0/8}"
|
||||
USE_CLAMAV_UPLOAD="${USE_CLAMAV_UPLOAD-yes}"
|
||||
USE_CLAMAV_SCAN="${USE_CLAMAV_SCAN-yes}"
|
||||
CLAMAV_SCAN_REMOVE="${CLAMAV_SCAN_REMOVE-yes}"
|
||||
@@ -77,9 +80,9 @@ USE_CUSTOM_HTTPS="${USE_CUSTOM_HTTPS-no}"
|
||||
ROOT_FOLDER="${ROOT_FOLDER-/www}"
|
||||
LOGROTATE_MINSIZE="${LOGROTATE_MINSIZE-10M}"
|
||||
LOGROTATE_MAXAGE="${LOGROTATE_MAXAGE-7}"
|
||||
DNS_RESOLVERS="${DNS_RESOLVERS-127.0.0.11 8.8.8.8}"
|
||||
DNS_RESOLVERS="${DNS_RESOLVERS-127.0.0.11}"
|
||||
USE_WHITELIST_IP="${USE_WHITELIST_IP-yes}"
|
||||
WHITELIST_IP_LIST="${WHITELIST_IP_LIST-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}"
|
||||
WHITELIST_IP_LIST="${WHITELIST_IP_LIST-127.0.0.1 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}"
|
||||
USE_WHITELIST_REVERSE="${USE_WHITELIST_REVERSE-yes}"
|
||||
WHITELIST_REVERSE_LIST="${WHITELIST_REVERSE_LIST-.googlebot.com .google.com .search.msn.com .crawl.yahoot.net .crawl.baidu.jp .crawl.baidu.com .yandex.com .yandex.ru .yandex.net}"
|
||||
USE_BLACKLIST_IP="${USE_BLACKLIST_IP-yes}"
|
||||
|
||||
@@ -12,6 +12,7 @@ done
|
||||
|
||||
# trap SIGTERM and SIGINT
|
||||
function trap_exit() {
|
||||
rm -f "/opt/running" 2> /dev/null
|
||||
echo "[*] Catched stop operation"
|
||||
echo "[*] Stopping crond ..."
|
||||
pkill -TERM crond
|
||||
@@ -25,7 +26,27 @@ function trap_exit() {
|
||||
pkill -TERM rsyslogd
|
||||
pkill -TERM tail
|
||||
}
|
||||
trap "trap_exit" TERM INT
|
||||
trap "trap_exit" TERM INT QUIT
|
||||
|
||||
# trap SIGHUP
|
||||
function trap_reload() {
|
||||
echo "[*] Catched reload operation"
|
||||
if [ "$MULTISITE" = "yes" ] ; then
|
||||
/opt/entrypoint/multisite-config.sh
|
||||
fi
|
||||
if [ -f /tmp/nginx.pid ] ; then
|
||||
echo "[*] Reloading nginx ..."
|
||||
/usr/sbin/nginx -s reload
|
||||
if [ $? -eq 0 ] ; then
|
||||
echo "[*] Reload successfull"
|
||||
else
|
||||
echo "[!] Reload failed"
|
||||
fi
|
||||
else
|
||||
echo "[!] Ignored reload operation because nginx is not running"
|
||||
fi
|
||||
}
|
||||
trap "trap_reload" HUP
|
||||
|
||||
# do the configuration magic if needed
|
||||
if [ ! -f "/opt/installed" ] ; then
|
||||
@@ -36,6 +57,7 @@ if [ ! -f "/opt/installed" ] ; then
|
||||
/opt/entrypoint/site-config.sh "$server"
|
||||
echo "[*] Multi site - $server configuration done"
|
||||
done
|
||||
/opt/entrypoint/multisite-config.sh
|
||||
else
|
||||
/opt/entrypoint/site-config.sh
|
||||
echo "[*] Single site - $SERVER_NAME configuration done"
|
||||
@@ -50,13 +72,6 @@ chown -R root:nginx /etc/nginx/
|
||||
chmod -R 740 /etc/nginx/
|
||||
find /etc/nginx -type d -exec chmod 750 {} \;
|
||||
|
||||
# fix let's encrypt rights
|
||||
if [ "$AUTO_LETS_ENCRYPT" = "yes" ] ; then
|
||||
chown -R root:nginx /etc/letsencrypt
|
||||
chmod -R 740 /etc/letsencrypt
|
||||
find /etc/letsencrypt -type d -exec chmod 750 {} \;
|
||||
fi
|
||||
|
||||
# start rsyslogd
|
||||
rsyslogd
|
||||
|
||||
@@ -64,19 +79,25 @@ rsyslogd
|
||||
crond
|
||||
|
||||
# start nginx
|
||||
if [ -f "/tmp/nginx-temp.pid" ] ; then
|
||||
nginx -c /etc/nginx/nginx-temp.conf -s quit
|
||||
fi
|
||||
echo "[*] Running nginx ..."
|
||||
su -s "/usr/sbin/nginx" nginx
|
||||
if [ "$?" -eq 0 ] ; then
|
||||
touch "/opt/running"
|
||||
else
|
||||
rm -f "/opt/running" 2> /dev/null
|
||||
fi
|
||||
|
||||
# list of log files to display
|
||||
LOGS="/var/log/access.log /var/log/error.log /var/log/jobs.log"
|
||||
|
||||
# start fail2ban
|
||||
if [ "$USE_FAIL2BAN" = "yes" ] ; then
|
||||
echo "[*] Running fail2ban ..."
|
||||
fail2ban-server > /dev/null
|
||||
fi
|
||||
|
||||
# start crowdsec
|
||||
if [ "$USE_CROWDSEC" = "yes" ] ; then
|
||||
echo "[*] Running crowdsec ..."
|
||||
crowdsec
|
||||
LOGS="$LOGS /var/log/fail2ban.log"
|
||||
fi
|
||||
|
||||
# autotest
|
||||
@@ -91,12 +112,11 @@ if [ "$1" == "test" ] ; then
|
||||
fi
|
||||
|
||||
# display logs
|
||||
LOGS="/var/log/access.log /var/log/error.log"
|
||||
if [ "$USE_FAIL2BAN" = "yes" ] ; then
|
||||
LOGS="$LOGS /var/log/fail2ban.log"
|
||||
fi
|
||||
tail -F $LOGS &
|
||||
wait $!
|
||||
pid="$!"
|
||||
while [ -f "/opt/running" ] ; do
|
||||
wait "$pid"
|
||||
done
|
||||
|
||||
# sigterm trapped
|
||||
echo "[*] bunkerized-nginx stopped"
|
||||
|
||||
@@ -20,6 +20,12 @@ if [ "$ADDITIONAL_MODULES" != "" ] ; then
|
||||
apk add $ADDITIONAL_MODULES
|
||||
fi
|
||||
|
||||
# start nginx with temp conf for let's encrypt challenges
|
||||
if [ "$(has_value AUTO_LETS_ENCRYPT yes)" != "" ] ; then
|
||||
replace_in_file "/etc/nginx/nginx-temp.conf" "%HTTP_PORT%" "$HTTP_PORT"
|
||||
nginx -c /etc/nginx/nginx-temp.conf
|
||||
fi
|
||||
|
||||
# include server block(s)
|
||||
if [ "$MULTISITE" = "yes" ] ; then
|
||||
includes=""
|
||||
@@ -31,6 +37,51 @@ else
|
||||
replace_in_file "/etc/nginx/nginx.conf" "%INCLUDE_SERVER%" "include /etc/nginx/server.conf;"
|
||||
fi
|
||||
|
||||
# setup default server block if multisite
|
||||
if [ "$MULTISITE" = "yes" ] ; then
|
||||
replace_in_file "/etc/nginx/nginx.conf" "%MULTISITE_DEFAULT_SERVER%" "include /etc/nginx/multisite-default-server.conf;"
|
||||
if [ "$(has_value LISTEN_HTTP yes)" != "" ] ; then
|
||||
replace_in_file "/etc/nginx/multisite-default-server.conf" "%LISTEN_HTTP%" "listen 0.0.0.0:${HTTP_PORT} default_server;"
|
||||
else
|
||||
replace_in_file "/etc/nginx/multisite-default-server.conf" "%LISTEN_HTTP%" ""
|
||||
fi
|
||||
if [ "$(has_value AUTO_LETS_ENCRYPT yes)" != "" ] || [ "$(has_value USE_CUSTOM_HTTPS yes)" != "" ] || [ "$(has_value GENERATE_SELF_SIGNED_SSL yes)" != "" ] ; then
|
||||
replace_in_file "/etc/nginx/multisite-default-server.conf" "%USE_HTTPS%" "include /etc/nginx/multisite-default-server-https.conf;"
|
||||
replace_in_file "/etc/nginx/multisite-default-server-https.conf" "%HTTPS_PORT%" "$HTTPS_PORT"
|
||||
if [ "$(has_value HTTP2 yes)" != "" ] ; then
|
||||
replace_in_file "/etc/nginx/multisite-default-server-https.conf" "%HTTP2%" "http2"
|
||||
else
|
||||
replace_in_file "/etc/nginx/multisite-default-server-https.conf" "%HTTP2%" ""
|
||||
fi
|
||||
replace_in_file "/etc/nginx/multisite-default-server-https.conf" "%HTTPS_PROTOCOLS%" "$HTTPS_PROTOCOLS"
|
||||
if [ "$(echo $HTTPS_PROTOCOLS | grep TLSv1.2)" != "" ] ; then
|
||||
replace_in_file "/etc/nginx/multisite-default-server-https.conf" "%SSL_DHPARAM%" "ssl_dhparam /etc/nginx/dhparam;"
|
||||
replace_in_file "/etc/nginx/multisite-default-server-https.conf" "%SSL_CIPHERS%" "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;"
|
||||
else
|
||||
replace_in_file "/etc/nginx/multisite-default-server-https.conf" "%SSL_DHPARAM%" ""
|
||||
replace_in_file "/etc/nginx/multisite-default-server-https.conf" "%SSL_CIPHERS%" ""
|
||||
fi
|
||||
openssl req -nodes -x509 -newkey rsa:4096 -keyout /etc/nginx/default-key.pem -out /etc/nginx/default-cert.pem -days $SELF_SIGNED_SSL_EXPIRY -subj "/C=$SELF_SIGNED_SSL_COUNTRY/ST=$SELF_SIGNED_SSL_STATE/L=$SELF_SIGNED_SSL_CITY/O=$SELF_SIGNED_SSL_ORG/OU=$SELF_SIGNED_SSL_OU/CN=$SELF_SIGNED_SSL_CN"
|
||||
if [ "$(has_value AUTO_LETS_ENCRYPT yes)" != "" ] ; then
|
||||
replace_in_file "/etc/nginx/multisite-default-server-https.conf" "%LETS_ENCRYPT_WEBROOT%" "include /etc/nginx/multisite-default-server-lets-encrypt-webroot.conf;"
|
||||
else
|
||||
replace_in_file "/etc/nginx/multisite-default-server-https.conf" "%LETS_ENCRYPT_WEBROOT%" ""
|
||||
fi
|
||||
else
|
||||
replace_in_file "/etc/nginx/multisite-default-server.conf" "%USE_HTTPS%" ""
|
||||
fi
|
||||
if [ "$DISABLE_DEFAULT_SERVER" = "yes" ] ; then
|
||||
replace_in_file "/etc/nginx/multisite-default-server.conf" "%MULTISITE_DISABLE_DEFAULT_SERVER%" "include /etc/nginx/multisite-disable-default-server.conf;"
|
||||
else
|
||||
replace_in_file "/etc/nginx/multisite-default-server.conf" "%MULTISITE_DISABLE_DEFAULT_SERVER%" ""
|
||||
fi
|
||||
else
|
||||
replace_in_file "/etc/nginx/nginx.conf" "%MULTISITE_DEFAULT_SERVER%" ""
|
||||
fi
|
||||
|
||||
# custom log format
|
||||
replace_in_file "/etc/nginx/nginx.conf" "%LOG_FORMAT%" "$LOG_FORMAT"
|
||||
|
||||
# proxy_cache zone
|
||||
if [ "$(has_value USE_PROXY_CACHE yes)" != "" ] ; then
|
||||
replace_in_file "/etc/nginx/nginx.conf" "%PROXY_CACHE_PATH%" "proxy_cache_path /tmp/proxy_cache keys_zone=proxycache:${PROXY_CACHE_PATH_ZONE_SIZE} ${PROXY_CACHE_PATH_PARAMS};"
|
||||
@@ -40,12 +91,14 @@ fi
|
||||
|
||||
# let's encrypt setup
|
||||
if [ "$AUTO_LETS_ENCRYPT" = "yes" ] ; then
|
||||
if [ "$MULTISITE" = "no" ] ; then
|
||||
FIRST_SERVER_NAME=$(echo "$SERVER_NAME" | cut -d " " -f 1)
|
||||
DOMAINS_LETS_ENCRYPT=$(echo "$SERVER_NAME" | sed "s/ /,/g")
|
||||
EMAIL_LETS_ENCRYPT="${EMAIL_LETS_ENCRYPT-contact@$FIRST_SERVER_NAME}"
|
||||
if [ ! -f /etc/letsencrypt/live/${FIRST_SERVER_NAME}/fullchain.pem ] ; then
|
||||
echo "[*] Performing Let's Encrypt challenge ..."
|
||||
certbot certonly --standalone -n --preferred-challenges http -d "$DOMAINS_LETS_ENCRYPT" --email "$EMAIL_LETS_ENCRYPT" --agree-tos --http-01-port $HTTP_PORT
|
||||
echo "[*] Performing Let's Encrypt challenge for $SERVER_NAME ..."
|
||||
/opt/scripts/certbot-new.sh "$DOMAINS_LETS_ENCRYPT" "$EMAIL_LETS_ENCRYPT"
|
||||
fi
|
||||
fi
|
||||
echo "0 0 * * * /opt/scripts/certbot-renew.sh > /dev/null 2>&1" >> /etc/crontabs/root
|
||||
fi
|
||||
@@ -93,6 +146,21 @@ else
|
||||
replace_in_file "/etc/nginx/nginx.conf" "%BLOCK_USER_AGENT%" ""
|
||||
fi
|
||||
|
||||
# block bad refferer
|
||||
if [ "$(has_value BLOCK_REFERRER yes)" != "" ] ; then
|
||||
replace_in_file "/etc/nginx/nginx.conf" "%BLOCK_REFERRER%" "include /etc/nginx/map-referrer.conf;"
|
||||
echo "0 0 * * * /opt/scripts/referrers.sh" >> /etc/crontabs/root
|
||||
if [ -f "/cache/map-referrer.conf" ] ; then
|
||||
echo "[*] Copying cached map-referrer.conf ..."
|
||||
cp /cache/map-referrer.conf /etc/nginx/map-referrer.conf
|
||||
else
|
||||
echo "[*] Downloading bad referrer list (in background) ..."
|
||||
/opt/scripts/referrers.sh &
|
||||
fi
|
||||
else
|
||||
replace_in_file "/etc/nginx/nginx.conf" "%BLOCK_REFERRER%" ""
|
||||
fi
|
||||
|
||||
# block TOR exit nodes
|
||||
if [ "$(has_value BLOCK_TOR_EXIT_NODE yes)" != "" ] ; then
|
||||
echo "0 * * * * /opt/scripts/exit-nodes.sh" >> /etc/crontabs/root
|
||||
@@ -186,8 +254,16 @@ fi
|
||||
list=$(spaces_to_lua "$DNSBL_LIST")
|
||||
replace_in_file "/usr/local/lib/lua/dnsbl.lua" "%DNSBL_LIST%" "$list"
|
||||
|
||||
# disable default site
|
||||
if [ "$DISABLE_DEFAULT_SERVER" = "yes" ] && [ "$MULTISITE" = "yes" ] ; then
|
||||
replace_in_file "/etc/nginx/multisite-default-server.conf" "%MULTISITE_DISABLE_DEFAULT_SERVER%" "include /etc/nginx/multisite-disable-default-server.conf;"
|
||||
else
|
||||
replace_in_file "/etc/nginx/multisite-default-server.conf" "%MULTISITE_DISABLE_DEFAULT_SERVER%" ""
|
||||
fi
|
||||
|
||||
# fail2ban setup
|
||||
if [ "$(has_value USE_FAIL2BAN yes)" != "" ] ; then
|
||||
echo "" > /etc/nginx/fail2ban-ip.conf
|
||||
rm -rf /etc/fail2ban/jail.d/*.conf
|
||||
cp /opt/fail2ban/nginx-action.local /etc/fail2ban/action.d/nginx-action.local
|
||||
cp /opt/fail2ban/nginx-filter.local /etc/fail2ban/filter.d/nginx-filter.local
|
||||
@@ -195,6 +271,7 @@ if [ "$(has_value USE_FAIL2BAN yes)" != "" ] ; then
|
||||
replace_in_file "/etc/fail2ban/jail.d/nginx-jail.local" "%FAIL2BAN_BANTIME%" "$FAIL2BAN_BANTIME"
|
||||
replace_in_file "/etc/fail2ban/jail.d/nginx-jail.local" "%FAIL2BAN_FINDTIME%" "$FAIL2BAN_FINDTIME"
|
||||
replace_in_file "/etc/fail2ban/jail.d/nginx-jail.local" "%FAIL2BAN_MAXRETRY%" "$FAIL2BAN_MAXRETRY"
|
||||
replace_in_file "/etc/fail2ban/jail.d/nginx-jail.local" "%FAIL2BAN_IGNOREIP%" "$FAIL2BAN_IGNOREIP"
|
||||
replace_in_file "/etc/fail2ban/filter.d/nginx-filter.local" "%FAIL2BAN_STATUS_CODES%" "$FAIL2BAN_STATUS_CODES"
|
||||
fi
|
||||
|
||||
@@ -215,10 +292,8 @@ fi
|
||||
# CrowdSec setup
|
||||
if [ "$(has_value USE_CROWDSEC yes)" != "" ] ; then
|
||||
replace_in_file "/etc/nginx/nginx.conf" "%USE_CROWDSEC%" "include /etc/nginx/crowdsec.conf;"
|
||||
cp /opt/crowdsec/acquis.yaml /etc/crowdsec/config/acquis.yaml
|
||||
cscli api register >> /etc/crowdsec/config/api.yaml
|
||||
cscli api pull
|
||||
echo "0 0 * * * /usr/local/bin/cscli api pull > /dev/null 2>&1" >> /etc/crontabs/root
|
||||
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"
|
||||
else
|
||||
replace_in_file "/etc/nginx/nginx.conf" "%USE_CROWDSEC%" ""
|
||||
fi
|
||||
|
||||
45
entrypoint/multisite-config.sh
Normal file
45
entrypoint/multisite-config.sh
Normal file
@@ -0,0 +1,45 @@
|
||||
#!/bin/sh
|
||||
|
||||
# load default values
|
||||
. /opt/entrypoint/defaults.sh
|
||||
|
||||
# load some functions
|
||||
. /opt/entrypoint/utils.sh
|
||||
|
||||
# fix nginx configs rights (and modules through the symlink)
|
||||
chown -R root:nginx /etc/nginx/
|
||||
chmod -R 740 /etc/nginx/
|
||||
find /etc/nginx -type d -exec chmod 750 {} \;
|
||||
|
||||
if [ "$MULTISITE" = "yes" ] ; then
|
||||
servers=$(find /etc/nginx -name "server.conf" | cut -d '/' -f 4)
|
||||
for server in $servers ; do
|
||||
if [ "$server" = "server.conf" ] ; then
|
||||
continue
|
||||
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)"
|
||||
fi
|
||||
if grep "modsecurity.conf" ${SERVER_PREFIX}server.conf > /dev/null ; then
|
||||
modsec_custom=""
|
||||
if ls /modsec-confs/*.conf > /dev/null 2>&1 ; then
|
||||
modsec_custom="include /modsec-confs/*.conf\n"
|
||||
fi
|
||||
if ls /modsec-confs/${server}/*.conf > /dev/null 2>&1 ; then
|
||||
modsec_custom="${modsec_custom}include /modsec-confs/${server}/*.conf\n"
|
||||
fi
|
||||
replace_in_file "${SERVER_PREFIX}modsecurity-rules.conf" "%MODSECURITY_INCLUDE_CUSTOM_RULES%" "$modsec_custom"
|
||||
if grep "owasp-crs.conf" ${SERVER_PREFIX}modsecurity-rules.conf > /dev/null ; then
|
||||
modsec_crs_custom=""
|
||||
if ls /modsec-crs-confs/*.conf > /dev/null 2>&1 ; then
|
||||
modsec_crs_custom="include /modsec-crs-confs/*.conf\n"
|
||||
fi
|
||||
if ls /modsec-crs-confs/${server}/*.conf > /dev/null 2>&1 ; then
|
||||
modsec_crs_custom="${modsec_crs_custom}include /modsec-crs-confs/${server}/*.conf\n"
|
||||
fi
|
||||
fi
|
||||
replace_in_file "${SERVER_PREFIX}modsecurity-rules.conf" "%MODSECURITY_INCLUDE_CUSTOM_CRS%" "$modsec_crs_custom"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
@@ -1,7 +1,9 @@
|
||||
#!/bin/bash
|
||||
|
||||
# load default values
|
||||
set -a
|
||||
. /opt/entrypoint/defaults.sh
|
||||
set +a
|
||||
|
||||
# load some functions
|
||||
. /opt/entrypoint/utils.sh
|
||||
@@ -10,6 +12,20 @@
|
||||
NGINX_PREFIX="/etc/nginx/"
|
||||
if [ "$MULTISITE" = "yes" ] ; then
|
||||
NGINX_PREFIX="${NGINX_PREFIX}${1}/"
|
||||
if [ ! -d "$NGINX_PREFIX" ] ; then
|
||||
mkdir "$NGINX_PREFIX"
|
||||
fi
|
||||
ROOT_FOLDER="${ROOT_FOLDER}/$1"
|
||||
fi
|
||||
env | grep -E -v "^(HOSTNAME|PWD|PKG_RELEASE|NJS_VERSION|SHLVL|PATH|_|NGINX_VERSION)=" > "${NGINX_PREFIX}nginx.env"
|
||||
|
||||
if [ "$MULTISITE" = "yes" ] ; then
|
||||
sed -i "s~^SERVER_NAME=.*~SERVER_NAME=$1~" "${NGINX_PREFIX}nginx.env"
|
||||
for server in $SERVER_NAME ; do
|
||||
if [ "$server" != "$1" ] ; then
|
||||
sed -i "/^${server}_.*=.*/d" "${NGINX_PREFIX}nginx.env"
|
||||
fi
|
||||
done
|
||||
for var in $(env) ; do
|
||||
name=$(echo "$var" | cut -d '=' -f 1)
|
||||
check=$(echo "$name" | grep "^$1_")
|
||||
@@ -17,15 +33,14 @@ if [ "$MULTISITE" = "yes" ] ; then
|
||||
repl_name=$(echo "$name" | sed "s~${1}_~~")
|
||||
repl_value=$(echo "$var" | sed "s~${name}=~~")
|
||||
read -r "$repl_name" <<< $repl_value
|
||||
sed -i "/^${repl_name}=.*/d" "${NGINX_PREFIX}nginx.env"
|
||||
sed -i "/^${name}=.*/d" "${NGINX_PREFIX}nginx.env"
|
||||
echo "${repl_name}=${repl_value}" >> "${NGINX_PREFIX}nginx.env"
|
||||
fi
|
||||
done
|
||||
ROOT_FOLDER="${ROOT_FOLDER}/$1"
|
||||
fi
|
||||
|
||||
# copy stub confs
|
||||
if [ "$MULTISITE" = "yes" ] ; then
|
||||
mkdir "$NGINX_PREFIX"
|
||||
fi
|
||||
cp /opt/confs/site/* "$NGINX_PREFIX"
|
||||
|
||||
# replace paths
|
||||
@@ -53,9 +68,17 @@ if [ "$USE_REVERSE_PROXY" = "yes" ] ; then
|
||||
value=$(echo "$var" | sed "s/${name}=//")
|
||||
host=$(echo "$name" | sed "s/URL/HOST/")
|
||||
host_value=$(env | grep "^${host}=" | sed "s/${host}=//")
|
||||
ws=$(echo "$name" | sed "s/URL/WS/")
|
||||
ws_value=$(env | grep "^${ws}=" | sed "s/${ws}=//")
|
||||
cp "${NGINX_PREFIX}reverse-proxy.conf" "${NGINX_PREFIX}reverse-proxy-${i}.conf"
|
||||
replace_in_file "${NGINX_PREFIX}reverse-proxy-${i}.conf" "%REVERSE_PROXY_URL%" "$value"
|
||||
replace_in_file "${NGINX_PREFIX}reverse-proxy-${i}.conf" "%REVERSE_PROXY_HOST%" "$host_value"
|
||||
replace_in_file "${NGINX_PREFIX}reverse-proxy-${i}.conf" "%REVERSE_PROXY_HEADERS%" "include ${NGINX_PREFIX}reverse-proxy-headers.conf;"
|
||||
if [ "$ws_value" = "yes" ] ; then
|
||||
replace_in_file "${NGINX_PREFIX}reverse-proxy-${i}.conf" "%REVERSE_PROXY_WS%" "proxy_http_version 1.1;\nproxy_set_header Upgrade \$http_upgrade;\nproxy_set_header Connection \"Upgrade\";\n"
|
||||
else
|
||||
replace_in_file "${NGINX_PREFIX}reverse-proxy-${i}.conf" "%REVERSE_PROXY_WS%" ""
|
||||
fi
|
||||
i=$(($i + 1))
|
||||
fi
|
||||
done
|
||||
@@ -130,9 +153,6 @@ if [ "$REMOTE_PHP" != "" ] ; then
|
||||
replace_in_file "${NGINX_PREFIX}server.conf" "%USE_PHP%" "include ${NGINX_PREFIX}php.conf;"
|
||||
replace_in_file "${NGINX_PREFIX}server.conf" "%FASTCGI_PATH%" "include ${NGINX_PREFIX}fastcgi.conf;"
|
||||
replace_in_file "${NGINX_PREFIX}php.conf" "%REMOTE_PHP%" "$REMOTE_PHP"
|
||||
if [ "$MULTISITE" = "yes" ] ; then
|
||||
cp /etc/nginx/fastcgi.conf ${NGINX_PREFIX}fastcgi.conf
|
||||
fi
|
||||
replace_in_file "${NGINX_PREFIX}fastcgi.conf" "\$document_root" "${REMOTE_PHP_PATH}/"
|
||||
else
|
||||
replace_in_file "${NGINX_PREFIX}server.conf" "%USE_PHP%" ""
|
||||
@@ -228,14 +248,10 @@ else
|
||||
fi
|
||||
|
||||
# disable default server
|
||||
if [ "$DISABLE_DEFAULT_SERVER" = "yes" ] ; then
|
||||
if [ "$DISABLE_DEFAULT_SERVER" = "yes" ] && [ "$MULTISITE" != "yes" ] ; then
|
||||
replace_in_file "${NGINX_PREFIX}server.conf" "%DISABLE_DEFAULT_SERVER%" "include ${NGINX_PREFIX}disable-default-server.conf;"
|
||||
if [ "$MULTISITE" == "yes" ] ; then
|
||||
replace_in_file "${NGINX_PREFIX}disable-default-server.conf" "%SERVER_NAME%" "$1"
|
||||
else
|
||||
SERVER_NAME_PIPE=$(echo $SERVER_NAME | sed "s/ /|/g")
|
||||
replace_in_file "${NGINX_PREFIX}disable-default-server.conf" "%SERVER_NAME%" "$SERVER_NAME_PIPE"
|
||||
fi
|
||||
else
|
||||
replace_in_file "${NGINX_PREFIX}server.conf" "%DISABLE_DEFAULT_SERVER%" ""
|
||||
fi
|
||||
@@ -259,9 +275,16 @@ fi
|
||||
|
||||
# block bad UA
|
||||
if [ "$BLOCK_USER_AGENT" = "yes" ] ; then
|
||||
replace_in_file "${NGINX_PREFIX}server.conf" "%BLOCK_USER_AGENT%" "include ${NGINX_PREFIX}block-user-agent.conf;"
|
||||
replace_in_file "${NGINX_PREFIX}main-lua.conf" "%USE_USER_AGENT%" "true"
|
||||
else
|
||||
replace_in_file "${NGINX_PREFIX}server.conf" "%BLOCK_USER_AGENT%" ""
|
||||
replace_in_file "${NGINX_PREFIX}main-lua.conf" "%USE_USER_AGENT%" "false"
|
||||
fi
|
||||
|
||||
# block bad referrer
|
||||
if [ "$BLOCK_REFERRER" = "yes" ] ; then
|
||||
replace_in_file "${NGINX_PREFIX}main-lua.conf" "%USE_REFERRER%" "true"
|
||||
else
|
||||
replace_in_file "${NGINX_PREFIX}main-lua.conf" "%USE_REFERRER%" "false"
|
||||
fi
|
||||
|
||||
# block TOR exit nodes
|
||||
@@ -308,15 +331,24 @@ if [ "$AUTO_LETS_ENCRYPT" = "yes" ] || [ "$USE_CUSTOM_HTTPS" = "yes" ] || [ "$GE
|
||||
replace_in_file "${NGINX_PREFIX}https.conf" "%STRICT_TRANSPORT_SECURITY%" ""
|
||||
fi
|
||||
if [ "$AUTO_LETS_ENCRYPT" = "yes" ] ; then
|
||||
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}"
|
||||
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"
|
||||
replace_in_file "${NGINX_PREFIX}https.conf" "%HTTPS_KEY%" "/etc/letsencrypt/live/${FIRST_SERVER_NAME}/privkey.pem"
|
||||
replace_in_file "${NGINX_PREFIX}https.conf" "%LETS_ENCRYPT_WEBROOT%" "include ${NGINX_PREFIX}lets-encrypt-webroot.conf;"
|
||||
elif [ "$USE_CUSTOM_HTTPS" = "yes" ] ; then
|
||||
replace_in_file "${NGINX_PREFIX}https.conf" "%HTTPS_CERT%" "$CUSTOM_HTTPS_CERT"
|
||||
replace_in_file "${NGINX_PREFIX}https.conf" "%HTTPS_KEY%" "$CUSTOM_HTTPS_KEY"
|
||||
replace_in_file "${NGINX_PREFIX}https.conf" "%LETS_ENCRYPT_WEBROOT%" ""
|
||||
elif [ "$GENERATE_SELF_SIGNED_SSL" = "yes" ] ; then
|
||||
replace_in_file "${NGINX_PREFIX}https.conf" "%HTTPS_CERT%" "/etc/nginx/self-signed-ssl/cert.pem"
|
||||
replace_in_file "${NGINX_PREFIX}https.conf" "%HTTPS_KEY%" "/etc/nginx/self-signed-ssl/key.pem"
|
||||
replace_in_file "${NGINX_PREFIX}https.conf" "%LETS_ENCRYPT_WEBROOT%" ""
|
||||
fi
|
||||
else
|
||||
replace_in_file "${NGINX_PREFIX}server.conf" "%USE_HTTPS%" ""
|
||||
@@ -338,26 +370,24 @@ fi
|
||||
|
||||
# ModSecurity config
|
||||
if [ "$USE_MODSECURITY" = "yes" ] ; then
|
||||
replace_in_file "${NGINX_PREFIX}modsecurity.conf" "%MODSEC_RULES_FILE%" "${NGINX_PREFIX}/modsecurity-rules.conf"
|
||||
replace_in_file "${NGINX_PREFIX}modsecurity.conf" "%MODSEC_RULES_FILE%" "${NGINX_PREFIX}modsecurity-rules.conf"
|
||||
replace_in_file "${NGINX_PREFIX}server.conf" "%USE_MODSECURITY%" "include ${NGINX_PREFIX}modsecurity.conf;"
|
||||
if [ "$MULTISITE" != "yes" ] ; then
|
||||
modsec_custom=""
|
||||
if ls /modsec-confs/*.conf > /dev/null 2>&1 ; then
|
||||
modsec_custom="include /modsec-confs/*.conf\n"
|
||||
fi
|
||||
if [ "$MULTISITE" = "yes" ] && ls /modsec-confs/${1}/*.conf > /dev/null 2>&1 ; then
|
||||
modsec_custom="${modsec_custom}include /modsec-confs/${1}/*.conf\n"
|
||||
fi
|
||||
replace_in_file "${NGINX_PREFIX}modsecurity-rules.conf" "%MODSECURITY_INCLUDE_CUSTOM_RULES%" "$modsec_custom"
|
||||
fi
|
||||
if [ "$USE_MODSECURITY_CRS" = "yes" ] ; then
|
||||
replace_in_file "${NGINX_PREFIX}modsecurity-rules.conf" "%MODSECURITY_INCLUDE_CRS%" "include /etc/nginx/owasp-crs.conf"
|
||||
if [ "$MULTISITE" != "yes" ] ; then
|
||||
modsec_crs_custom=""
|
||||
if ls /modsec-crs-confs/*.conf > /dev/null 2>&1 ; then
|
||||
modsec_crs_custom="include /modsec-crs-confs/*.conf\n"
|
||||
fi
|
||||
if [ "$MULTISITE" = "yes" ] && ls /modsec-crs-confs/${1}/*.conf > /dev/null 2>&1 ; then
|
||||
modsec_crs_custom="${modsec_custom}include /modsec-crs-confs/${1}/*.conf\n"
|
||||
fi
|
||||
replace_in_file "${NGINX_PREFIX}modsecurity-rules.conf" "%MODSECURITY_INCLUDE_CUSTOM_CRS%" "$modsec_crs_custom"
|
||||
fi
|
||||
replace_in_file "${NGINX_PREFIX}modsecurity-rules.conf" "%MODSECURITY_INCLUDE_CRS_RULES%" "include /etc/nginx/owasp-crs/*.conf"
|
||||
else
|
||||
replace_in_file "${NGINX_PREFIX}modsecurity-rules.conf" "%MODSECURITY_INCLUDE_CRS%" ""
|
||||
@@ -402,11 +432,13 @@ replace_in_file "${NGINX_PREFIX}server.conf" "%ERRORS%" "$ERRORS"
|
||||
if [ "$USE_AUTH_BASIC" = "yes" ] ; then
|
||||
if [ "$AUTH_BASIC_LOCATION" = "sitewide" ] ; then
|
||||
replace_in_file "${NGINX_PREFIX}server.conf" "%AUTH_BASIC%" "include ${NGINX_PREFIX}auth-basic-sitewide.conf;"
|
||||
replace_in_file "${NGINX_PREFIX}auth-basic-sitewide.conf" "%AUTH_BASIC_TEXT%" "$AUTH_BASIC_TEXT";
|
||||
replace_in_file "${NGINX_PREFIX}auth-basic-sitewide.conf" "%AUTH_BASIC_TEXT%" "$AUTH_BASIC_TEXT"
|
||||
replace_in_file "${NGINX_PREFIX}auth-basic-sitewide.conf" "%NGINX_PREFIX%" "$NGINX_PREFIX"
|
||||
else
|
||||
replace_in_file "${NGINX_PREFIX}server.conf" "%AUTH_BASIC%" "include ${NGINX_PREFIX}auth-basic.conf;"
|
||||
replace_in_file "${NGINX_PREFIX}auth-basic.conf" "%AUTH_BASIC_LOCATION%" "$AUTH_BASIC_LOCATION";
|
||||
replace_in_file "${NGINX_PREFIX}auth-basic.conf" "%AUTH_BASIC_TEXT%" "$AUTH_BASIC_TEXT";
|
||||
replace_in_file "${NGINX_PREFIX}auth-basic.conf" "%AUTH_BASIC_LOCATION%" "$AUTH_BASIC_LOCATION"
|
||||
replace_in_file "${NGINX_PREFIX}auth-basic.conf" "%AUTH_BASIC_TEXT%" "$AUTH_BASIC_TEXT"
|
||||
replace_in_file "${NGINX_PREFIX}auth-basic.conf" "%NGINX_PREFIX%" "$NGINX_PREFIX"
|
||||
fi
|
||||
htpasswd -b -B -c ${NGINX_PREFIX}.htpasswd "$AUTH_BASIC_USER" "$AUTH_BASIC_PASSWORD"
|
||||
else
|
||||
@@ -517,8 +549,7 @@ fi
|
||||
|
||||
# fail2ban
|
||||
if [ "$USE_FAIL2BAN" = "yes" ] ; then
|
||||
echo "" > ${NGINX_PREFIX}fail2ban-ip.conf
|
||||
replace_in_file "${NGINX_PREFIX}server.conf" "%USE_FAIL2BAN%" "include ${NGINX_PREFIX}fail2ban-ip.conf;"
|
||||
replace_in_file "${NGINX_PREFIX}server.conf" "%USE_FAIL2BAN%" "include /etc/nginx/fail2ban-ip.conf;"
|
||||
else
|
||||
replace_in_file "${NGINX_PREFIX}server.conf" "%USE_FAIL2BAN%" ""
|
||||
fi
|
||||
|
||||
67
examples/autoconf-php/docker-compose.yml
Normal file
67
examples/autoconf-php/docker-compose.yml
Normal file
@@ -0,0 +1,67 @@
|
||||
version: '3'
|
||||
|
||||
services:
|
||||
|
||||
mywww:
|
||||
image: bunkerity/bunkerized-nginx
|
||||
restart: always
|
||||
ports:
|
||||
- 80:8080
|
||||
- 443:8443
|
||||
volumes:
|
||||
- ./letsencrypt:/etc/letsencrypt
|
||||
- ./web-files:/www:ro
|
||||
- autoconf:/etc/nginx
|
||||
environment:
|
||||
- SERVER_NAME= # must be left blank if you don't want to setup "static" conf
|
||||
- MULTISITE=yes
|
||||
- AUTO_LETS_ENCRYPT=yes
|
||||
- REDIRECT_HTTP_TO_HTTPS=yes
|
||||
- DISABLE_DEFAULT_SERVER=yes
|
||||
- USE_CLIENT_CACHE=yes
|
||||
- USE_GZIP=yes
|
||||
- USE_BROTLI=yes
|
||||
labels:
|
||||
- "bunkerized-nginx.AUTOCONF"
|
||||
|
||||
myautoconf:
|
||||
image: bunkerity/bunkerized-nginx-autoconf
|
||||
restart: always
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||
- autoconf:/etc/nginx
|
||||
depends_on:
|
||||
- mywww
|
||||
|
||||
myapp1:
|
||||
image: php:fpm
|
||||
restart: always
|
||||
volumes:
|
||||
- ./web-files/app1.website.com:/app
|
||||
labels:
|
||||
- "bunkerized-nginx.SERVER_NAME=app1.website.com" # replace with your domain
|
||||
- "bunkerized-nginx.REMOTE_PHP=myapp1"
|
||||
- "bunkerized-nginx.REMOTE_PHP_PATH=/app"
|
||||
|
||||
myapp2:
|
||||
image: php:fpm
|
||||
restart: always
|
||||
volumes:
|
||||
- ./web-files/app2.website.com:/app
|
||||
labels:
|
||||
- "bunkerized-nginx.SERVER_NAME=app2.website.com" # replace with your domain
|
||||
- "bunkerized-nginx.REMOTE_PHP=myapp2"
|
||||
- "bunkerized-nginx.REMOTE_PHP_PATH=/app"
|
||||
|
||||
myapp3:
|
||||
image: php:fpm
|
||||
restart: always
|
||||
volumes:
|
||||
- ./web-files/app3.website.com:/app
|
||||
labels:
|
||||
- "bunkerized-nginx.SERVER_NAME=app3.website.com" # replace with your domain
|
||||
- "bunkerized-nginx.REMOTE_PHP=myapp3"
|
||||
- "bunkerized-nginx.REMOTE_PHP_PATH=/app"
|
||||
|
||||
volumes:
|
||||
autoconf:
|
||||
@@ -0,0 +1,5 @@
|
||||
<?php
|
||||
|
||||
echo "Hello from app1 !";
|
||||
|
||||
?>
|
||||
@@ -0,0 +1,5 @@
|
||||
<?php
|
||||
|
||||
echo "Hello from app2 !";
|
||||
|
||||
?>
|
||||
@@ -0,0 +1,5 @@
|
||||
<?php
|
||||
|
||||
echo "Hello from app3 !";
|
||||
|
||||
?>
|
||||
67
examples/autoconf-reverse-proxy/docker-compose.yml
Normal file
67
examples/autoconf-reverse-proxy/docker-compose.yml
Normal file
@@ -0,0 +1,67 @@
|
||||
version: '3'
|
||||
|
||||
services:
|
||||
|
||||
mywww:
|
||||
image: bunkerity/bunkerized-nginx
|
||||
restart: always
|
||||
ports:
|
||||
- 80:8080
|
||||
- 443:8443
|
||||
volumes:
|
||||
- ./letsencrypt:/etc/letsencrypt
|
||||
- autoconf:/etc/nginx
|
||||
environment:
|
||||
- SERVER_NAME= # must be left blank if you don't want to setup "static" conf
|
||||
- MULTISITE=yes
|
||||
- AUTO_LETS_ENCRYPT=yes
|
||||
- REDIRECT_HTTP_TO_HTTPS=yes
|
||||
- DISABLE_DEFAULT_SERVER=yes
|
||||
- USE_CLIENT_CACHE=yes
|
||||
- USE_GZIP=yes
|
||||
- USE_BROTLI=yes
|
||||
- USE_REVERSE_PROXY=yes
|
||||
labels:
|
||||
- "bunkerized-nginx.AUTOCONF"
|
||||
|
||||
myautoconf:
|
||||
image: bunkerity/bunkerized-nginx-autoconf
|
||||
restart: always
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||
- autoconf:/etc/nginx
|
||||
depends_on:
|
||||
- mywww
|
||||
|
||||
myapp1:
|
||||
build: js-app
|
||||
restart: always
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
labels:
|
||||
- "bunkerized-nginx.SERVER_NAME=app1.website.com" # replace with your domain
|
||||
- "bunkerized-nginx.REVERSE_PROXY_URL=/"
|
||||
- "bunkerized-nginx.REVERSE_PROXY_HOST=http://myapp1:3000"
|
||||
|
||||
myapp2:
|
||||
build: js-app
|
||||
restart: always
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
labels:
|
||||
- "bunkerized-nginx.SERVER_NAME=app2.website.com" # replace with your domain
|
||||
- "bunkerized-nginx.REVERSE_PROXY_URL=/"
|
||||
- "bunkerized-nginx.REVERSE_PROXY_HOST=http://myapp2:3000"
|
||||
|
||||
myapp3:
|
||||
build: js-app
|
||||
restart: always
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
labels:
|
||||
- "bunkerized-nginx.SERVER_NAME=app3.website.com" # replace with your domain
|
||||
- "bunkerized-nginx.REVERSE_PROXY_URL=/"
|
||||
- "bunkerized-nginx.REVERSE_PROXY_HOST=http://myapp3:3000"
|
||||
|
||||
volumes:
|
||||
autoconf:
|
||||
11
examples/autoconf-reverse-proxy/js-app/Dockerfile
Normal file
11
examples/autoconf-reverse-proxy/js-app/Dockerfile
Normal file
@@ -0,0 +1,11 @@
|
||||
FROM node
|
||||
|
||||
COPY app/ /home/node/app
|
||||
|
||||
RUN cd /home/node/app && npm install && chown -R root:node /home/node/app && chmod -R 770 /home/node/app
|
||||
|
||||
WORKDIR /home/node/app
|
||||
|
||||
USER node
|
||||
|
||||
CMD ["node", "index.js"]
|
||||
13
examples/autoconf-reverse-proxy/js-app/app/index.js
Normal file
13
examples/autoconf-reverse-proxy/js-app/app/index.js
Normal file
@@ -0,0 +1,13 @@
|
||||
const express = require('express')
|
||||
const app = express()
|
||||
const port = 3000
|
||||
var os = require("os");
|
||||
|
||||
app.get('/', (req, res) => {
|
||||
res.send('Container id = ' + os.hostname())
|
||||
})
|
||||
|
||||
app.listen(port, () => {
|
||||
console.log(`Example app listening at http://localhost:${port}`)
|
||||
})
|
||||
|
||||
14
examples/autoconf-reverse-proxy/js-app/app/package.json
Normal file
14
examples/autoconf-reverse-proxy/js-app/app/package.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"name": "js-app",
|
||||
"version": "1.0.0",
|
||||
"description": "demo",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"express": "^4.17.1"
|
||||
}
|
||||
}
|
||||
3
examples/crowdsec/bouncer_key.sh
Normal file
3
examples/crowdsec/bouncer_key.sh
Normal file
@@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
docker-compose exec mycrowdsec cscli bouncers add MyBouncer
|
||||
70
examples/crowdsec/docker-compose.yml
Normal file
70
examples/crowdsec/docker-compose.yml
Normal file
@@ -0,0 +1,70 @@
|
||||
version: '3'
|
||||
|
||||
services:
|
||||
|
||||
mywww:
|
||||
image: bunkerity/bunkerized-nginx
|
||||
restart: always
|
||||
ports:
|
||||
- 80:8080
|
||||
- 443:8443
|
||||
volumes:
|
||||
- ./web-files:/www:ro
|
||||
- ./letsencrypt:/etc/letsencrypt
|
||||
- nginx_logs:/var/log
|
||||
environment:
|
||||
- SERVER_NAME=app1.website.com app2.website.com # replace with your domains
|
||||
- MULTISITE=yes
|
||||
- AUTO_LETS_ENCRYPT=yes
|
||||
- REDIRECT_HTTP_TO_HTTPS=yes
|
||||
- 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)
|
||||
- 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
|
||||
networks:
|
||||
- net0
|
||||
- net1
|
||||
- net2
|
||||
|
||||
mycrowdsec:
|
||||
image: crowdsecurity/crowdsec:v1.0.2
|
||||
restart: always
|
||||
volumes:
|
||||
- ./acquis.yaml:/etc/crowdsec/acquis.yaml
|
||||
- nginx_logs:/var/log:ro
|
||||
environment:
|
||||
- COLLECTIONS=crowdsecurity/nginx
|
||||
- REGISTER_TO_ONLINE_API=true
|
||||
networks:
|
||||
- net0
|
||||
|
||||
myapp1:
|
||||
image: php:fpm
|
||||
restart: always
|
||||
volumes:
|
||||
- ./web-files/app1.website.com:/app
|
||||
networks:
|
||||
- net1
|
||||
|
||||
myapp2:
|
||||
image: php:fpm
|
||||
restart: always
|
||||
volumes:
|
||||
- ./web-files/app2.website.com:/app
|
||||
networks:
|
||||
- net2
|
||||
|
||||
networks:
|
||||
net0:
|
||||
net1:
|
||||
net2:
|
||||
|
||||
volumes:
|
||||
nginx_logs:
|
||||
5
examples/crowdsec/web-files/app1.website.com/index.php
Normal file
5
examples/crowdsec/web-files/app1.website.com/index.php
Normal file
@@ -0,0 +1,5 @@
|
||||
<?php
|
||||
|
||||
echo "hello from app1 !";
|
||||
|
||||
?>
|
||||
5
examples/crowdsec/web-files/app2.website.com/index.php
Normal file
5
examples/crowdsec/web-files/app2.website.com/index.php
Normal file
@@ -0,0 +1,5 @@
|
||||
<?php
|
||||
|
||||
echo "hello from app2 !";
|
||||
|
||||
?>
|
||||
@@ -1,6 +0,0 @@
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
|
||||
location / {
|
||||
proxy_pass http://myapp3:3000;
|
||||
}
|
||||
53
examples/prestashop/docker-compose.yml
Normal file
53
examples/prestashop/docker-compose.yml
Normal file
@@ -0,0 +1,53 @@
|
||||
version: '3'
|
||||
|
||||
services:
|
||||
|
||||
mywww:
|
||||
image: bunkerity/bunkerized-nginx
|
||||
restart: always
|
||||
ports:
|
||||
- 80:8080
|
||||
- 443:8443
|
||||
volumes:
|
||||
- ./prestashop-files:/www:ro
|
||||
- ./letsencrypt:/etc/letsencrypt
|
||||
- ./server-confs:/server-confs:ro # custom confs at server context for prestashop
|
||||
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
|
||||
- USE_BROTLI=yes
|
||||
- REMOTE_PHP=myprestashop
|
||||
- REMOTE_PHP_PATH=/var/www/html
|
||||
|
||||
myprestashop:
|
||||
image: prestashop/prestashop:1.7-fpm
|
||||
restart: always
|
||||
volumes:
|
||||
- ./prestashop-files:/var/www/html
|
||||
environment:
|
||||
- DB_SERVER=mydb
|
||||
- DB_NAME=prestashop
|
||||
- DB_USER=user
|
||||
- DB_PASSWD=db-user-pwd # replace with a stronger password (must match MYSQL_PASSWORD)
|
||||
- PS_INSTALL_AUTO=1
|
||||
- PS_DOMAIN=www.website.com # replace with your domain
|
||||
- PS_FOLDER_ADMIN=myadmin # replace with your admin folder
|
||||
- PS_ENABLE_SSL=1
|
||||
- ADMIN_MAIL=admin@website.com # replace with your mail
|
||||
- ADMIN_PASSWD=admin # replace with a stronger 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=prestashop
|
||||
- MYSQL_USER=user
|
||||
- MYSQL_PASSWORD=db-user-pwd # replace with a stronger password (must match DB_PASSWD)
|
||||
84
examples/prestashop/server-confs/prestashop.conf
Normal file
84
examples/prestashop/server-confs/prestashop.conf
Normal file
@@ -0,0 +1,84 @@
|
||||
# remove ports in redirects
|
||||
port_in_redirect off;
|
||||
|
||||
# Redirect 404 errors to prestashop
|
||||
error_page 404 /index.php?controller=404;
|
||||
|
||||
# Force pdf files to be downloaded
|
||||
location ~* \.pdf$ {
|
||||
add_header Content-Disposition Attachment;
|
||||
}
|
||||
|
||||
# Force files inupload directory to be downloaded
|
||||
location ~ ^/upload/ {
|
||||
add_header Content-Disposition Attachment;
|
||||
}
|
||||
|
||||
# Images
|
||||
rewrite ^/([0-9])(-[_a-zA-Z0-9-]*)?(-[0-9]+)?/.+.jpg$ /img/p/$1/$1$2$3.jpg last;
|
||||
rewrite ^/([0-9])([0-9])(-[_a-zA-Z0-9-]*)?(-[0-9]+)?/.+.jpg$ /img/p/$1/$2/$1$2$3$4.jpg last;
|
||||
rewrite ^/([0-9])([0-9])([0-9])(-[_a-zA-Z0-9-]*)?(-[0-9]+)?/.+.jpg$ /img/p/$1/$2/$3/$1$2$3$4$5.jpg last;
|
||||
rewrite ^/([0-9])([0-9])([0-9])([0-9])(-[_a-zA-Z0-9-]*)?(-[0-9]+)?/.+.jpg$ /img/p/$1/$2/$3/$4/$1$2$3$4$5$6.jpg last;
|
||||
rewrite ^/([0-9])([0-9])([0-9])([0-9])([0-9])(-[_a-zA-Z0-9-]*)?(-[0-9]+)?/.+.jpg$ /img/p/$1/$2/$3/$4/$5/$1$2$3$4$5$6$7.jpg last;
|
||||
rewrite ^/([0-9])([0-9])([0-9])([0-9])([0-9])([0-9])(-[_a-zA-Z0-9-]*)?(-[0-9]+)?/.+.jpg$ /img/p/$1/$2/$3/$4/$5/$6/$1$2$3$4$5$6$7$8.jpg last;
|
||||
rewrite ^/([0-9])([0-9])([0-9])([0-9])([0-9])([0-9])([0-9])(-[_a-zA-Z0-9-]*)?(-[0-9]+)?/.+.jpg$ /img/p/$1/$2/$3/$4/$5/$6/$7/$1$2$3$4$5$6$7$8$9.jpg last;
|
||||
rewrite ^/([0-9])([0-9])([0-9])([0-9])([0-9])([0-9])([0-9])([0-9])(-[_a-zA-Z0-9-]*)?(-[0-9]+)?/.+.jpg$ /img/p/$1/$2/$3/$4/$5/$6/$7/$8/$1$2$3$4$5$6$7$8$9$10.jpg last;
|
||||
rewrite ^/c/([0-9]+)(-[.*_a-zA-Z0-9-]*)(-[0-9]+)?/.+.jpg$ /img/c/$1$2$3.jpg last;
|
||||
rewrite ^/c/([a-zA-Z_-]+)(-[0-9]+)?/.+.jpg$ /img/c/$1$2.jpg last;
|
||||
|
||||
# AlphaImageLoader for IE and fancybox
|
||||
rewrite ^images_ie/?([^/]+)\.(jpe?g|png|gif)$ js/jquery/plugins/fancybox/images/$1.$2 last;
|
||||
|
||||
# Web service API
|
||||
rewrite ^/api/?(.*)$ /webservice/dispatcher.php?url=$1 last;
|
||||
|
||||
# Installation sandbox
|
||||
rewrite ^(/install(?:-dev)?/sandbox)/(.*) /$1/test.php last;
|
||||
|
||||
# [REQUIRED EDIT] Change this block to your admin folder
|
||||
location /myadmin/ {
|
||||
if (!-e $request_filename) {
|
||||
rewrite ^/.*$ /myadmin/index.php last;
|
||||
}
|
||||
}
|
||||
|
||||
# File security
|
||||
# .htaccess .DS_Store .htpasswd etc
|
||||
location ~ /\. {
|
||||
deny all;
|
||||
}
|
||||
# Source code directories
|
||||
location ~ ^/(app|bin|cache|classes|config|controllers|docs|localization|override|src|tests|tools|translations|travis-scripts|vendor|var)/ {
|
||||
deny all;
|
||||
}
|
||||
# vendor in modules directory
|
||||
location ~ ^/modules/.*/vendor/ {
|
||||
deny all;
|
||||
}
|
||||
# Prevent exposing other sensitive files
|
||||
location ~ \.(yml|log|tpl|twig|sass)$ {
|
||||
deny all;
|
||||
}
|
||||
|
||||
# Prevent injection of php files
|
||||
location /upload {
|
||||
location ~ \.php$ {
|
||||
deny all;
|
||||
}
|
||||
}
|
||||
location /img {
|
||||
location ~ \.php$ {
|
||||
deny all;
|
||||
}
|
||||
}
|
||||
|
||||
# [REQUIRED EDIT] PHP FPM part
|
||||
location ~ \.php$ {
|
||||
try_files $fastcgi_script_name /index.php$uri&$args =404;
|
||||
fastcgi_split_path_info ^(.+\.php)(/.+)$;
|
||||
include fastcgi_params;
|
||||
fastcgi_param PATH_INFO $fastcgi_path_info;
|
||||
fastcgi_param PATH_TRANSLATED /var/www/html/$fastcgi_path_info;
|
||||
fastcgi_param SCRIPT_FILENAME /var/www/html/$fastcgi_script_name;
|
||||
fastcgi_pass myprestashop:9000;
|
||||
}
|
||||
29
examples/reverse-proxy-websocket/docker-compose.yml
Normal file
29
examples/reverse-proxy-websocket/docker-compose.yml
Normal file
@@ -0,0 +1,29 @@
|
||||
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_BROTLI=yes
|
||||
- USE_REVERSE_PROXY=yes
|
||||
- REVERSE_PROXY_URL=/ws/
|
||||
- REVERSE_PROXY_HOST=http://myws:8010/
|
||||
- REVERSE_PROXY_WS=yes
|
||||
|
||||
myws:
|
||||
image: ksdn117/web-socket-test
|
||||
restart: always
|
||||
@@ -1,6 +0,0 @@
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
|
||||
location / {
|
||||
proxy_pass http://mytomcat:8080/sample;
|
||||
}
|
||||
43
examples/web-ui/docker-compose.yml
Normal file
43
examples/web-ui/docker-compose.yml
Normal file
@@ -0,0 +1,43 @@
|
||||
version: '3'
|
||||
|
||||
services:
|
||||
|
||||
mywww:
|
||||
image: bunkerity/bunkerized-nginx
|
||||
restart: always
|
||||
ports:
|
||||
- 80:8080
|
||||
- 443:8443
|
||||
volumes:
|
||||
- ./letsencrypt:/etc/letsencrypt
|
||||
- ./web-files:/www:ro
|
||||
- autoconf:/etc/nginx
|
||||
environment:
|
||||
- SERVER_NAME=admin.website.com # replace with your domain
|
||||
- MULTISITE=yes
|
||||
- AUTO_LETS_ENCRYPT=yes
|
||||
- REDIRECT_HTTP_TO_HTTPS=yes
|
||||
- DISABLE_DEFAULT_SERVER=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
|
||||
- admin.website.com_AUTH_BASIC_PASSWORD=admin # change it to something hard to guess
|
||||
- admin.website.com_USE_REVERSE_PROXY=yes
|
||||
- admin.website.com_REVERSE_PROXY_URL=/admin/ # change it to something hard to guess
|
||||
- admin.website.com_REVERSE_PROXY_HOST=http://myui:5000/
|
||||
labels:
|
||||
- "bunkerized-nginx.UI"
|
||||
|
||||
myui:
|
||||
image: bunkerity/bunkerized-nginx-ui
|
||||
restart: always
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||
- autoconf:/etc/nginx
|
||||
environment:
|
||||
- ABSOLUTE_URI=https://admin.website.com/admin/ # change it to your full URI
|
||||
|
||||
volumes:
|
||||
autoconf:
|
||||
@@ -2,6 +2,7 @@
|
||||
bantime = %FAIL2BAN_BANTIME%
|
||||
findtime = %FAIL2BAN_FINDTIME%
|
||||
maxretry = %FAIL2BAN_MAXRETRY%
|
||||
ignoreip = %FAIL2BAN_IGNOREIP%
|
||||
enabled = true
|
||||
action = nginx-action
|
||||
logpath = /var/log/access.log
|
||||
|
||||
@@ -10,7 +10,7 @@ function M.get_reverse()
|
||||
end
|
||||
local rdns = ""
|
||||
local answers, err = r:reverse_query(ip)
|
||||
if not answers.errcode then
|
||||
if answers ~= nil and not answers.errcode then
|
||||
for ak, av in ipairs(answers) do
|
||||
if av.ptrdname then
|
||||
rdns = av.ptrdname
|
||||
@@ -28,11 +28,13 @@ function M.get_ips(fqdn)
|
||||
end
|
||||
local ips = {}
|
||||
local answers, err, tries = r:query(fqdn, nil, {})
|
||||
if answers ~= nil then
|
||||
for ak, av in ipairs(answers) do
|
||||
if av.address then
|
||||
table.insert(ips, av.address)
|
||||
end
|
||||
end
|
||||
end
|
||||
return ips
|
||||
end
|
||||
|
||||
|
||||
20
prepare.sh
Normal file
20
prepare.sh
Normal file
@@ -0,0 +1,20 @@
|
||||
#!/bin/sh
|
||||
|
||||
# install dependencies
|
||||
apk --no-cache add certbot libstdc++ libmaxminddb geoip pcre yajl fail2ban clamav apache2-utils rsyslog openssl lua libgd go jq mariadb-connector-c bash brotli
|
||||
|
||||
# make scripts executable
|
||||
chmod +x /opt/entrypoint/* /opt/scripts/*
|
||||
mkdir /opt/entrypoint.d
|
||||
|
||||
# log files/folders rights
|
||||
rm -f /var/log/nginx/*
|
||||
chown root:nginx /var/log/nginx
|
||||
chmod 750 /var/log/nginx
|
||||
touch /var/log/nginx/error.log /var/log/nginx/modsec_audit.log /var/log/jobs.log
|
||||
chown nginx:nginx /var/log/nginx/*.log
|
||||
|
||||
# let's encrypt webroot
|
||||
mkdir /acme-challenge
|
||||
chown root:nginx /acme-challenge
|
||||
chmod 750 /acme-challenge
|
||||
@@ -1,14 +1,43 @@
|
||||
#!/bin/sh
|
||||
|
||||
echo "" > /etc/nginx/block-abusers.conf
|
||||
# load some functions
|
||||
. /opt/scripts/utils.sh
|
||||
|
||||
# copy old conf to cache
|
||||
cp /etc/nginx/block-abusers.conf /cache
|
||||
|
||||
# generate the new conf
|
||||
curl -s "https://iplists.firehol.org/files/firehol_abusers_30d.netset" | grep -v "^\#.*" |
|
||||
while read entry ; do
|
||||
check=$(echo $entry | grep -E "^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}/?[0-9]*$")
|
||||
if [ "$check" != "" ] ; then
|
||||
echo "deny ${entry};" >> /etc/nginx/block-abusers.conf
|
||||
echo "deny ${entry};" >> /tmp/block-abusers.conf
|
||||
fi
|
||||
done
|
||||
cp /etc/nginx/block-abusers.conf /cache
|
||||
|
||||
# check if we have at least 1 line
|
||||
lines="$(wc -l /tmp/block-abusers.conf | cut -d ' ' -f 1)"
|
||||
if [ "$lines" -gt 1 ] ; then
|
||||
job_log "[BLACKLIST] abusers list updated ($lines entries)"
|
||||
# reload nginx with the new config
|
||||
mv /tmp/block-abusers.conf /etc/nginx/block-abusers.conf
|
||||
if [ -f /tmp/nginx.pid ] ; then
|
||||
/usr/sbin/nginx -s reload
|
||||
/usr/sbin/nginx -s reload > /dev/null 2>&1
|
||||
# new config is ok : save it in the cache
|
||||
if [ "$?" -eq 0 ] ; then
|
||||
cp /etc/nginx/block-abusers.conf /cache
|
||||
job_log "[NGINX] successfull nginx reload after abusers list update"
|
||||
else
|
||||
job_log "[NGINX] failed nginx reload after abusers list update fallback to old list"
|
||||
cp /cache/block-abusers.conf /etc/nginx
|
||||
/usr/sbin/nginx -s reload > /dev/null 2>&1
|
||||
fi
|
||||
else
|
||||
cp /etc/nginx/block-abusers.conf /cache
|
||||
fi
|
||||
else
|
||||
job_log "[BLACKLIST] can't update abusers list"
|
||||
fi
|
||||
|
||||
rm -f /tmp/block-abusers.conf 2> /dev/null
|
||||
|
||||
|
||||
13
scripts/certbot-new.sh
Normal file
13
scripts/certbot-new.sh
Normal file
@@ -0,0 +1,13 @@
|
||||
#!/bin/sh
|
||||
|
||||
# generate certificate
|
||||
certbot certonly --webroot -w /acme-challenge -n -d "$1" --email "$2" --agree-tos
|
||||
if [ "$?" -ne 0 ] ; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# fix rights
|
||||
chown -R root:nginx /etc/letsencrypt
|
||||
chmod -R 740 /etc/letsencrypt
|
||||
find /etc/letsencrypt -type d -exec chmod 750 {} \;
|
||||
exit 0
|
||||
21
scripts/certbot-renew-hook.sh
Normal file
21
scripts/certbot-renew-hook.sh
Normal file
@@ -0,0 +1,21 @@
|
||||
#!/bin/sh
|
||||
|
||||
# load some functions
|
||||
. /opt/scripts/utils.sh
|
||||
|
||||
job_log "[CERTBOT] certificates have been renewed"
|
||||
|
||||
# fix rights
|
||||
chown -R root:nginx /etc/letsencrypt
|
||||
chmod -R 740 /etc/letsencrypt
|
||||
find /etc/letsencrypt -type d -exec chmod 750 {} \;
|
||||
|
||||
# reload nginx
|
||||
if [ -f /tmp/nginx.pid ] ; then
|
||||
/usr/sbin/nginx -s reload > /dev/null 2>&1
|
||||
if [ "$?" -eq 0 ] ; then
|
||||
job_log "[NGINX] successfull nginx reload after certbot renew"
|
||||
else
|
||||
job_log "[NGINX] failed nginx reload after certbot renew"
|
||||
fi
|
||||
fi
|
||||
@@ -1,35 +1,13 @@
|
||||
#!/bin/sh
|
||||
|
||||
function replace_in_file() {
|
||||
# escape slashes
|
||||
pattern=$(echo "$2" | sed "s/\//\\\\\//g")
|
||||
replace=$(echo "$3" | sed "s/\//\\\\\//g")
|
||||
sed -i "s/$pattern/$replace/g" "$1"
|
||||
}
|
||||
# load some functions
|
||||
. /opt/scripts/utils.sh
|
||||
|
||||
# disable HTTP
|
||||
servers="$(find /etc/nginx -name server.conf)"
|
||||
for f in $servers ; do
|
||||
replace_in_file "$f" "listen" "#listen"
|
||||
done
|
||||
if [ -f /tmp/nginx.pid ] ; then
|
||||
/usr/sbin/nginx -s reload
|
||||
sleep 10
|
||||
fi
|
||||
|
||||
# ask a new certificate if needed
|
||||
certbot renew
|
||||
|
||||
# enable HTTP again
|
||||
for f in $servers ; do
|
||||
replace_in_file "$f" "#listen" "listen"
|
||||
done
|
||||
|
||||
chown -R root:nginx /etc/letsencrypt
|
||||
chmod -R 740 /etc/letsencrypt
|
||||
find /etc/letsencrypt -type d -exec chmod 750 {} \;
|
||||
|
||||
# reload nginx
|
||||
if [ -f /tmp/nginx.pid ] ; then
|
||||
/usr/sbin/nginx -s reload
|
||||
# ask new certificates if needed
|
||||
certbot renew --deploy-hook /opt/scripts/certbot-renew-hook.sh
|
||||
|
||||
if [ "$?" -eq 0 ] ; then
|
||||
job_log "[CERTBOT] renew operation done"
|
||||
else
|
||||
job_log "[CERTBOT] renew operation failed"
|
||||
fi
|
||||
|
||||
@@ -1,14 +1,43 @@
|
||||
#!/bin/sh
|
||||
|
||||
echo "" > /etc/nginx/block-tor-exit-node.conf
|
||||
# load some functions
|
||||
. /opt/scripts/utils.sh
|
||||
|
||||
# copy old conf to cache
|
||||
cp /etc/nginx/block-tor-exit-node.conf /cache
|
||||
|
||||
# generate the new conf
|
||||
curl -s "https://iplists.firehol.org/files/tor_exits.ipset" | grep -v "^\#.*" |
|
||||
while read entry ; do
|
||||
check=$(echo $entry | grep -E "^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}/?[0-9]*$")
|
||||
if [ "$check" != "" ] ; then
|
||||
echo "deny ${entry};" >> /etc/nginx/block-tor-exit-node.conf
|
||||
echo "deny ${entry};" >> /tmp/block-tor-exit-node.conf
|
||||
fi
|
||||
done
|
||||
cp /etc/nginx/block-tor-exit-node.conf /cache
|
||||
|
||||
# check if we have at least 1 line
|
||||
lines="$(wc -l /tmp/block-tor-exit-node.conf | cut -d ' ' -f 1)"
|
||||
if [ "$lines" -gt 1 ] ; then
|
||||
job_log "[BLACKLIST] TOR exit node list updated ($lines entries)"
|
||||
# reload nginx with the new config
|
||||
mv /tmp/block-tor-exit-node.conf /etc/nginx/block-tor-exit-node.conf
|
||||
if [ -f /tmp/nginx.pid ] ; then
|
||||
/usr/sbin/nginx -s reload
|
||||
/usr/sbin/nginx -s reload > /dev/null 2>&1
|
||||
# new config is ok : save it in the cache
|
||||
if [ "$?" -eq 0 ] ; then
|
||||
cp /etc/nginx/block-tor-exit-node.conf /cache
|
||||
job_log "[NGINX] successfull nginx reload after TOR exit node list update"
|
||||
else
|
||||
job_log "[NGINX] failed nginx reload after TOR exit node list update fallback to old list"
|
||||
cp /cache/block-tor-exit-node.conf /etc/nginx
|
||||
/usr/sbin/nginx -s reload > /dev/null 2>&1
|
||||
fi
|
||||
else
|
||||
cp /etc/nginx/block-tor-exit-node.conf /cache
|
||||
fi
|
||||
else
|
||||
job_log "[BLACKLIST] can't update TOR exit node list"
|
||||
fi
|
||||
|
||||
rm -f /tmp/block-tor-exit-node.conf 2> /dev/null
|
||||
|
||||
|
||||
@@ -1,12 +1,35 @@
|
||||
#!/bin/sh
|
||||
|
||||
# load some functions
|
||||
. /opt/scripts/utils.sh
|
||||
|
||||
# MMDB from https://db-ip.com/db/download/ip-to-country-lite
|
||||
URL="https://download.db-ip.com/free/dbip-country-lite-$(date +%Y-%m).mmdb.gz"
|
||||
wget -O /etc/nginx/geoip.mmdb.gz "$URL" > /dev/null 2>&1
|
||||
if [ -f /etc/nginx/geoip.mmdb.gz ] ; then
|
||||
gunzip -f /etc/nginx/geoip.mmdb.gz
|
||||
cp /etc/nginx/geoip.mmdb /cache
|
||||
wget -O /tmp/geoip.mmdb.gz "$URL" > /dev/null 2>&1
|
||||
if [ "$?" -eq 0 ] && [ -f /tmp/geoip.mmdb.gz ] ; then
|
||||
gunzip -f /tmp/geoip.mmdb.gz > /dev/null 2>&1
|
||||
if [ "$?" -ne 0 ] ; then
|
||||
job_log "[GEOIP] can't extract DB from $URL"
|
||||
exit 1
|
||||
fi
|
||||
mv /tmp/geoip.mmdb /etc/nginx
|
||||
if [ -f /tmp/nginx.pid ] ; then
|
||||
/usr/sbin/nginx -s reload
|
||||
/usr/sbin/nginx -s reload > /dev/null 2>&1
|
||||
if [ "$?" -eq 0 ] ; then
|
||||
cp /etc/nginx/geoip.mmdb /cache
|
||||
job_log "[NGINX] successfull nginx reload after GeoIP DB update"
|
||||
else
|
||||
job_log "[NGINX] failed nginx reload after GeoIP DB update"
|
||||
if [ -f /cache/geoip.mmdb ] ; then
|
||||
cp /cache/geoip.mmdb /etc/nginx/geoip.mmdb
|
||||
/usr/sbin/nginx -s reload > /dev/null 2>&1
|
||||
fi
|
||||
fi
|
||||
else
|
||||
cp /etc/nginx/geoip.mmdb /cache
|
||||
fi
|
||||
else
|
||||
job_log "[GEOIP] can't download DB from $URL"
|
||||
fi
|
||||
|
||||
rm -f /tmp/geoip* 2> /dev/null
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
#!/bin/sh
|
||||
|
||||
# load some functions
|
||||
. /opt/scripts/utils.sh
|
||||
|
||||
logrotate -f /etc/logrotate.conf > /dev/null 2>&1
|
||||
|
||||
pkill -HUP rsyslogd
|
||||
@@ -7,5 +10,10 @@ pkill -HUP rsyslogd
|
||||
fail2ban-client flushlogs
|
||||
|
||||
if [ -f /tmp/nginx.pid ] ; then
|
||||
/usr/sbin/nginx -s reload
|
||||
/usr/sbin/nginx -s reload > /dev/null 2>&1
|
||||
if [ "$?" -eq 0 ] ; then
|
||||
job_log "[NGINX] successfull nginx reload after logrotate"
|
||||
else
|
||||
job_log "[NGINX] failed nginx reload after logrotate"
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -1,14 +1,43 @@
|
||||
#!/bin/sh
|
||||
|
||||
echo "" > /etc/nginx/block-proxies.conf
|
||||
# load some functions
|
||||
. /opt/scripts/utils.sh
|
||||
|
||||
# copy old conf to cache
|
||||
cp /etc/nginx/block-proxies.conf /cache
|
||||
|
||||
# generate the new conf
|
||||
curl -s "https://iplists.firehol.org/files/firehol_proxies.netset" | grep -v "^\#.*" |
|
||||
while read entry ; do
|
||||
check=$(echo $entry | grep -E "^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}/?[0-9]*$")
|
||||
if [ "$check" != "" ] ; then
|
||||
echo "deny ${entry};" >> /etc/nginx/block-proxies.conf
|
||||
echo "deny ${entry};" >> /tmp/block-proxies.conf
|
||||
fi
|
||||
done
|
||||
cp /etc/nginx/block-proxies.conf /cache
|
||||
|
||||
# check if we have at least 1 line
|
||||
lines="$(wc -l /tmp/block-proxies.conf | cut -d ' ' -f 1)"
|
||||
if [ "$lines" -gt 1 ] ; then
|
||||
job_log "[BLACKLIST] proxies list updated ($lines entries)"
|
||||
# reload nginx with the new config
|
||||
mv /tmp/block-proxies.conf /etc/nginx/block-proxies.conf
|
||||
if [ -f /tmp/nginx.pid ] ; then
|
||||
/usr/sbin/nginx -s reload
|
||||
/usr/sbin/nginx -s reload > /dev/null 2>&1
|
||||
# new config is ok : save it in the cache
|
||||
if [ "$?" -eq 0 ] ; then
|
||||
cp /etc/nginx/block-proxies.conf /cache
|
||||
job_log "[NGINX] successfull nginx reload after proxies list update"
|
||||
else
|
||||
job_log "[NGINX] failed nginx reload after proxies list update fallback to old list"
|
||||
cp /cache/block-proxies.conf /etc/nginx
|
||||
/usr/sbin/nginx -s reload > /dev/null 2>&1
|
||||
fi
|
||||
else
|
||||
cp /etc/nginx/block-proxies.conf /cache
|
||||
fi
|
||||
else
|
||||
job_log "[BLACKLIST] can't update proxies list"
|
||||
fi
|
||||
|
||||
rm -f /tmp/block-proxies.conf 2> /dev/null
|
||||
|
||||
|
||||
43
scripts/referrers.sh
Executable file
43
scripts/referrers.sh
Executable file
@@ -0,0 +1,43 @@
|
||||
#!/bin/sh
|
||||
|
||||
# load some functions
|
||||
. /opt/scripts/utils.sh
|
||||
|
||||
# save old conf
|
||||
cp /etc/nginx/map-referrer.conf /cache
|
||||
|
||||
# generate new conf
|
||||
BLACKLIST="$(curl -s https://raw.githubusercontent.com/mitchellkrogza/nginx-ultimate-bad-bot-blocker/master/_generator_lists/bad-referrers.list)"
|
||||
if [ "$?" -ne 0 ] ; then
|
||||
job_log "[BLACKLIST] can't update referrers list"
|
||||
fi
|
||||
DATA=""
|
||||
IFS=$'\n'
|
||||
for ref in $BLACKLIST ; do
|
||||
DATA="${DATA}\"~${ref}\" yes;\n"
|
||||
done
|
||||
echo -e "map \$http_referer \$bad_referrer { hostnames; default no; $DATA }" > /tmp/map-referrer.conf
|
||||
|
||||
# check number of lines
|
||||
lines="$(wc -l /tmp/map-referrer.conf | cut -d ' ' -f 1)"
|
||||
if [ "$lines" -gt 1 ] ; then
|
||||
mv /tmp/map-referrer.conf /etc/nginx/map-referrer.conf
|
||||
job_log "[BLACKLIST] referrers list updated ($lines entries)"
|
||||
if [ -f /tmp/nginx.pid ] ; then
|
||||
/usr/sbin/nginx -s reload > /dev/null 2>&1
|
||||
if [ "$?" -eq 0 ] ; then
|
||||
cp /etc/nginx/map-referrer.conf /cache
|
||||
job_log "[NGINX] successfull nginx reload after referrers list update"
|
||||
else
|
||||
cp /cache/map-referrer.conf /etc/nginx
|
||||
job_log "[NGINX] failed nginx reload after referrers list update fallback to old list"
|
||||
/usr/sbin/nginx -s reload > /dev/null 2>&1
|
||||
fi
|
||||
else
|
||||
cp /etc/nginx/map-referrer.conf /cache
|
||||
fi
|
||||
else
|
||||
job_log "[BLACKLIST] can't update referrers list"
|
||||
fi
|
||||
|
||||
rm -f /tmp/map-referrer.conf 2> /dev/null
|
||||
@@ -1,26 +1,45 @@
|
||||
#!/bin/sh
|
||||
|
||||
# replace pattern in file
|
||||
function replace_in_file() {
|
||||
# escape slashes
|
||||
pattern=$(echo "$2" | sed "s/\//\\\\\//g")
|
||||
replace=$(echo "$3" | sed "s/\//\\\\\//g")
|
||||
replace=$(echo "$replace" | sed "s/\\ /\\\\ /g")
|
||||
sed -i "s/$pattern/$replace/g" "$1"
|
||||
}
|
||||
# load some functions
|
||||
. /opt/scripts/utils.sh
|
||||
|
||||
BLACKLIST="$(curl -s https://raw.githubusercontent.com/mitchellkrogza/nginx-ultimate-bad-bot-blocker/master/_generator_lists/bad-user-agents.list)"
|
||||
# save old conf
|
||||
cp /etc/nginx/map-user-agent.conf /cache
|
||||
|
||||
# generate new conf
|
||||
BLACKLIST="$(curl -s https://raw.githubusercontent.com/mitchellkrogza/nginx-ultimate-bad-bot-blocker/master/_generator_lists/bad-user-agents.list)
|
||||
$(curl -s https://raw.githubusercontent.com/JayBizzle/Crawler-Detect/master/raw/Crawlers.txt)"
|
||||
if [ "$?" -ne 0 ] ; then
|
||||
job_log "[BLACKLIST] can't update user-agent list"
|
||||
fi
|
||||
DATA=""
|
||||
IFS=$'\n'
|
||||
for ua in $BLACKLIST ; do
|
||||
DATA="${DATA}~*(?:\\\\b)${ua}\(?:\\\\b) yes;\n"
|
||||
DATA="${DATA}~*${ua} yes;\n"
|
||||
done
|
||||
DATA_ESCAPED=$(echo "$DATA" | sed 's: :\\\\ :g' | sed 's:\\\\ yes;: yes;:g' | sed 's:\\\\\\ :\\\\ :g')
|
||||
echo -e "map \$http_user_agent \$bad_user_agent { default no; $DATA_ESCAPED }" > /tmp/map-user-agent.conf
|
||||
|
||||
cp /opt/confs/global/map-user-agent.conf /etc/nginx/map-user-agent.conf
|
||||
replace_in_file "/etc/nginx/map-user-agent.conf" "%BLOCK_USER_AGENT%" "$DATA_ESCAPED"
|
||||
cp /etc/nginx/map-user-agent.conf /cache
|
||||
|
||||
# check number of lines
|
||||
lines="$(wc -l /tmp/map-user-agent.conf | cut -d ' ' -f 1)"
|
||||
if [ "$lines" -gt 1 ] ; then
|
||||
mv /tmp/map-user-agent.conf /etc/nginx/map-user-agent.conf
|
||||
job_log "[BLACKLIST] user-agent list updated ($lines entries)"
|
||||
if [ -f /tmp/nginx.pid ] ; then
|
||||
/usr/sbin/nginx -s reload
|
||||
/usr/sbin/nginx -s reload > /dev/null 2>&1
|
||||
if [ "$?" -eq 0 ] ; then
|
||||
cp /etc/nginx/map-user-agent.conf /cache
|
||||
job_log "[NGINX] successfull nginx reload after user-agent list update"
|
||||
else
|
||||
cp /cache/map-user-agent.conf /etc/nginx
|
||||
job_log "[NGINX] failed nginx reload after user-agent list update fallback to old list"
|
||||
/usr/sbin/nginx -s reload > /dev/null 2>&1
|
||||
fi
|
||||
else
|
||||
cp /etc/nginx/map-user-agent.conf /cache
|
||||
fi
|
||||
else
|
||||
job_log "[BLACKLIST] can't update user-agent list"
|
||||
fi
|
||||
|
||||
rm -f /tmp/map-user-agent.conf 2> /dev/null
|
||||
|
||||
8
scripts/utils.sh
Normal file
8
scripts/utils.sh
Normal file
@@ -0,0 +1,8 @@
|
||||
#!/bin/sh
|
||||
|
||||
function job_log() {
|
||||
when="$(date '+[%d/%m/%Y %H:%M:%S]')"
|
||||
what="$1"
|
||||
echo "$when $what" >> /var/log/jobs.log
|
||||
}
|
||||
|
||||
22
ui/Dockerfile
Normal file
22
ui/Dockerfile
Normal file
@@ -0,0 +1,22 @@
|
||||
FROM alpine
|
||||
|
||||
RUN apk add py3-pip apache2-utils bash && \
|
||||
pip3 install docker flask && \
|
||||
mkdir /opt/entrypoint && \
|
||||
mkdir -p /opt/confs/site
|
||||
|
||||
COPY confs/site/ /opt/confs/site
|
||||
COPY entrypoint/* /opt/entrypoint/
|
||||
COPY ui/ /opt/entrypoint/
|
||||
RUN chmod +x /opt/entrypoint/*.py /opt/entrypoint/*.sh
|
||||
|
||||
# Fix CVE-2020-1971
|
||||
RUN apk add "libcrypto1.1>1.1.1g-r0" "libssl1.1>1.1.1g-r0"
|
||||
|
||||
VOLUME /etc/nginx
|
||||
|
||||
EXPOSE 5000
|
||||
|
||||
WORKDIR /opt/entrypoint
|
||||
ENV FLASK_APP entrypoint.py
|
||||
ENTRYPOINT ["/usr/bin/python3", "-m", "flask", "run", "--host=0.0.0.0"]
|
||||
22
ui/Dockerfile-amd64
Normal file
22
ui/Dockerfile-amd64
Normal file
@@ -0,0 +1,22 @@
|
||||
FROM amd64/alpine
|
||||
|
||||
RUN apk add py3-pip apache2-utils bash && \
|
||||
pip3 install docker flask && \
|
||||
mkdir /opt/entrypoint && \
|
||||
mkdir -p /opt/confs/site
|
||||
|
||||
COPY confs/site/ /opt/confs/site
|
||||
COPY entrypoint/* /opt/entrypoint/
|
||||
COPY ui/ /opt/entrypoint/
|
||||
RUN chmod +x /opt/entrypoint/*.py /opt/entrypoint/*.sh
|
||||
|
||||
# Fix CVE-2020-1971
|
||||
RUN apk add "libcrypto1.1>1.1.1g-r0" "libssl1.1>1.1.1g-r0"
|
||||
|
||||
VOLUME /etc/nginx
|
||||
|
||||
EXPOSE 5000
|
||||
|
||||
WORKDIR /opt/entrypoint
|
||||
ENV FLASK_APP entrypoint.py
|
||||
ENTRYPOINT ["/usr/bin/python3", "-m", "flask", "run", "--host=0.0.0.0"]
|
||||
29
ui/Dockerfile-arm32v7
Normal file
29
ui/Dockerfile-arm32v7
Normal file
@@ -0,0 +1,29 @@
|
||||
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/alpine
|
||||
|
||||
COPY --from=builder qemu-arm-static /usr/bin
|
||||
|
||||
RUN apk add py3-pip apache2-utils bash && \
|
||||
pip3 install docker flask && \
|
||||
mkdir /opt/entrypoint && \
|
||||
mkdir -p /opt/confs/site
|
||||
|
||||
COPY confs/site/ /opt/confs/site
|
||||
COPY entrypoint/* /opt/entrypoint/
|
||||
COPY ui/ /opt/entrypoint/
|
||||
RUN chmod +x /opt/entrypoint/*.py /opt/entrypoint/*.sh
|
||||
|
||||
# Fix CVE-2020-1971
|
||||
RUN apk add "libcrypto1.1>1.1.1g-r0" "libssl1.1>1.1.1g-r0"
|
||||
|
||||
VOLUME /etc/nginx
|
||||
|
||||
EXPOSE 5000
|
||||
|
||||
WORKDIR /opt/entrypoint
|
||||
ENV FLASK_APP entrypoint.py
|
||||
ENTRYPOINT ["/usr/bin/python3", "-m", "flask", "run", "--host=0.0.0.0"]
|
||||
29
ui/Dockerfile-arm64v8
Normal file
29
ui/Dockerfile-arm64v8
Normal file
@@ -0,0 +1,29 @@
|
||||
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/alpine
|
||||
|
||||
COPY --from=builder qemu-aarch64-static /usr/bin
|
||||
|
||||
RUN apk add py3-pip apache2-utils bash && \
|
||||
pip3 install docker flask && \
|
||||
mkdir /opt/entrypoint && \
|
||||
mkdir -p /opt/confs/site
|
||||
|
||||
COPY confs/site/ /opt/confs/site
|
||||
COPY entrypoint/* /opt/entrypoint/
|
||||
COPY ui/ /opt/entrypoint/
|
||||
RUN chmod +x /opt/entrypoint/*.py /opt/entrypoint/*.sh
|
||||
|
||||
# Fix CVE-2020-1971
|
||||
RUN apk add "libcrypto1.1>1.1.1g-r0" "libssl1.1>1.1.1g-r0"
|
||||
|
||||
VOLUME /etc/nginx
|
||||
|
||||
EXPOSE 5000
|
||||
|
||||
WORKDIR /opt/entrypoint
|
||||
ENV FLASK_APP entrypoint.py
|
||||
ENTRYPOINT ["/usr/bin/python3", "-m", "flask", "run", "--host=0.0.0.0"]
|
||||
22
ui/Dockerfile-i386
Normal file
22
ui/Dockerfile-i386
Normal file
@@ -0,0 +1,22 @@
|
||||
FROM i386/alpine
|
||||
|
||||
RUN apk add py3-pip apache2-utils bash && \
|
||||
pip3 install docker flask && \
|
||||
mkdir /opt/entrypoint && \
|
||||
mkdir -p /opt/confs/site
|
||||
|
||||
COPY confs/site/ /opt/confs/site
|
||||
COPY entrypoint/* /opt/entrypoint/
|
||||
COPY ui/ /opt/entrypoint/
|
||||
RUN chmod +x /opt/entrypoint/*.py /opt/entrypoint/*.sh
|
||||
|
||||
# Fix CVE-2020-1971
|
||||
RUN apk add "libcrypto1.1>1.1.1g-r0" "libssl1.1>1.1.1g-r0"
|
||||
|
||||
VOLUME /etc/nginx
|
||||
|
||||
EXPOSE 5000
|
||||
|
||||
WORKDIR /opt/entrypoint
|
||||
ENV FLASK_APP entrypoint.py
|
||||
ENTRYPOINT ["/usr/bin/python3", "-m", "flask", "run", "--host=0.0.0.0"]
|
||||
795
ui/config.json
Normal file
795
ui/config.json
Normal file
@@ -0,0 +1,795 @@
|
||||
{
|
||||
"Misc":{
|
||||
"id":"misc",
|
||||
"params":[
|
||||
{
|
||||
"type":"text",
|
||||
"label":"Server name",
|
||||
"env":"SERVER_NAME",
|
||||
"regex":"^([a-z\\-0-9]+\\.?)+$",
|
||||
"id":"server-name",
|
||||
"default":"www.bunkerity.com"
|
||||
},
|
||||
{
|
||||
"type":"text",
|
||||
"label":"Max client size",
|
||||
"env":"MAX_CLIENT_SIZE",
|
||||
"regex":"^[0-9]+(k|K|m|M|g|G)?$",
|
||||
"id":"max-client-size",
|
||||
"default":"10m"
|
||||
},
|
||||
{
|
||||
"type":"text",
|
||||
"label":"Allowed methods",
|
||||
"env":"ALLOWED_METHODS",
|
||||
"regex":"^((GET|POST|HEAD|PUT|DELETE|CONNECT|OPTIONS|TRACE)\\|?)+$",
|
||||
"id":"allowed-methods",
|
||||
"default":"GET|POST|HEAD"
|
||||
},
|
||||
{
|
||||
"type":"checkbox",
|
||||
"label":"Serve files",
|
||||
"env":"SERVE_FILES",
|
||||
"regex":"^(yes|no)$",
|
||||
"id":"serve-files",
|
||||
"default":"yes"
|
||||
}
|
||||
]
|
||||
},
|
||||
"Info leak":{
|
||||
"id":"info-leak",
|
||||
"params":[
|
||||
{
|
||||
"type":"text",
|
||||
"label":"Remove headers",
|
||||
"env":"REMOVE_HEADERS",
|
||||
"regex":"^([A-Za-z0-9\\-] ?)*$",
|
||||
"id":"remove-headers",
|
||||
"default":"Server X-Powered-By X-AspNet-Version X-AspNetMvc-Version"
|
||||
}
|
||||
]
|
||||
},
|
||||
"Basic auth":{
|
||||
"id":"auth-basic",
|
||||
"params":[
|
||||
{
|
||||
"type":"checkbox",
|
||||
"label":"Use auth basic",
|
||||
"env":"USE_AUTH_BASIC",
|
||||
"regex":"^(yes|no)$",
|
||||
"id":"use-auth-basic",
|
||||
"default":"no"
|
||||
},
|
||||
{
|
||||
"type":"text",
|
||||
"label":"Auth basic location",
|
||||
"env":"AUTH_BASIC_LOCATION",
|
||||
"regex":"^(sitewide|/[A-Za-z0-9/]*)$",
|
||||
"id":"auth-basic-location",
|
||||
"default":"sitewide"
|
||||
},
|
||||
{
|
||||
"type":"text",
|
||||
"label":"Auth basic user",
|
||||
"env":"AUTH_BASIC_USER",
|
||||
"regex":"^([A-Za-z0-9\\-_]+)$",
|
||||
"id":"auth-basic-user",
|
||||
"default":"changeme"
|
||||
},
|
||||
{
|
||||
"type":"text",
|
||||
"label":"Auth basic password",
|
||||
"env":"AUTH_BASIC_PASSWORD",
|
||||
"regex":"^([\\S]+)$",
|
||||
"id":"auth-basic-password",
|
||||
"default":"changeme"
|
||||
},
|
||||
{
|
||||
"type":"text",
|
||||
"label":"Auth basic text",
|
||||
"regex":"^([\\S ]+)$",
|
||||
"env":"AUTH_BASIC_TEXT",
|
||||
"id":"auth-basic-text",
|
||||
"default":"Restricted area"
|
||||
}
|
||||
]
|
||||
},
|
||||
"Reverse proxy":{
|
||||
"id":"reverse-proxy",
|
||||
"params":[
|
||||
{
|
||||
"type":"checkbox",
|
||||
"label":"Use reverse proxy",
|
||||
"env":"USE_REVERSE_PROXY",
|
||||
"regex":"^(yes|no)$",
|
||||
"id":"use-reverse-proxy",
|
||||
"default":"no"
|
||||
},
|
||||
{
|
||||
"type":"multiple",
|
||||
"label":"Reverse proxy",
|
||||
"id":"reverse-proxy-params",
|
||||
"params":[
|
||||
{
|
||||
"type":"text",
|
||||
"label":"Reverse proxy url",
|
||||
"env":"REVERSE_PROXY_URL",
|
||||
"regex":".*",
|
||||
"id":"reverse-proxy-url",
|
||||
"multiple":"Reverse proxy",
|
||||
"default":""
|
||||
},
|
||||
{
|
||||
"type":"text",
|
||||
"label":"Reverse proxy host",
|
||||
"env":"REVERSE_PROXY_HOST",
|
||||
"regex":".*",
|
||||
"id":"reverse-proxy-host",
|
||||
"multiple":"Reverse proxy",
|
||||
"default":""
|
||||
},
|
||||
{
|
||||
"type":"checkbox",
|
||||
"label":"Reverse proxy ws",
|
||||
"env":"REVERSE_PROXY_WS",
|
||||
"regex":"^(yes|no)$",
|
||||
"id":"reverse-proxy-ws",
|
||||
"multiple":"Reverse proxy",
|
||||
"default":""
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type":"checkbox",
|
||||
"label":"Proxy real ip",
|
||||
"env":"PROXY_REAL_IP",
|
||||
"regex":"^(yes|no)$",
|
||||
"id":"proxy-real-ip",
|
||||
"default":"no"
|
||||
},
|
||||
{
|
||||
"type":"text",
|
||||
"label":"Proxy real ip from",
|
||||
"env":"PROXY_REAL_IP_FROM",
|
||||
"regex":"^(\\d+.\\d+.\\d+.\\d+(/\\d+)? ?)*$",
|
||||
"id":"proxy-real-ip-from",
|
||||
"default":"192.168.0.0/16 172.16.0.0/12 10.0.0.0/8"
|
||||
},
|
||||
{
|
||||
"type":"text",
|
||||
"label":"Proxy real ip header",
|
||||
"env":"PROXY_REAL_IP_HEADER",
|
||||
"regex":"^([A-Za-z0-9\\-])+$",
|
||||
"id":"proxy-real-ip-header",
|
||||
"default":"X-Forwarded-For"
|
||||
},
|
||||
{
|
||||
"type":"text",
|
||||
"label":"Proxy real ip recursive",
|
||||
"env":"PROXY_REAL_IP_RECURSIVE",
|
||||
"regex":"^(on|off)$",
|
||||
"id":"proxy-real-ip-recursive",
|
||||
"default":"on"
|
||||
}
|
||||
]
|
||||
},
|
||||
"Compression":{
|
||||
"id":"compression",
|
||||
"params":[
|
||||
{
|
||||
"type":"checkbox",
|
||||
"label":"Use gzip",
|
||||
"env":"USE_GZIP",
|
||||
"regex":"^(yes|no)$",
|
||||
"id":"use-gzip",
|
||||
"default":"no"
|
||||
},
|
||||
{
|
||||
"type":"text",
|
||||
"label":"Gzip comp level",
|
||||
"env":"GZIP_COMP_LEVEL",
|
||||
"regex":"^[1-9]$",
|
||||
"id":"gzip-comp-level",
|
||||
"default":"5"
|
||||
},
|
||||
{
|
||||
"type":"text",
|
||||
"label":"Gzip min length",
|
||||
"env":"GZIP_MIN_LENGTH",
|
||||
"regex":"^[0-9]+$",
|
||||
"id":"gzip-min-length",
|
||||
"default":"1000"
|
||||
},
|
||||
{
|
||||
"type":"text",
|
||||
"label":"Gzip types",
|
||||
"env":"GZIP_TYPES",
|
||||
"regex":"^([a-z/\\+\\-\\.] ?)*$",
|
||||
"id":"gzip-types",
|
||||
"default":"application/atom+xml application/javascript application/json application/rss+xml application/vnd.ms-fontobject application/x-font-opentype application/x-font-truetype application/x-font-ttf application/x-javascript application/xhtml+xml application/xml font/eot font/opentype font/otf font/truetype image/svg+xml image/vnd.microsoft.icon image/x-icon image/x-win-bitmap text/css text/javascript text/plain text/xml"
|
||||
},
|
||||
{
|
||||
"type":"checkbox",
|
||||
"label":"Use brotli",
|
||||
"env":"USE_BROTLI",
|
||||
"regex":"^(yes|no)$",
|
||||
"id":"use-brotli",
|
||||
"default":"no"
|
||||
},
|
||||
{
|
||||
"type":"text",
|
||||
"label":"Brotli comp level",
|
||||
"env":"BROTLI_COMP_LEVEL",
|
||||
"regex":"^[1-9]$",
|
||||
"id":"brotli-comp-level",
|
||||
"default":"6"
|
||||
},
|
||||
{
|
||||
"type":"text",
|
||||
"label":"Brotli min length",
|
||||
"env":"BROTLI_MIN_LENGTH",
|
||||
"regex":"^[0-9]+$",
|
||||
"id":"brotli-min-length",
|
||||
"default":"1000"
|
||||
},
|
||||
{
|
||||
"type":"text",
|
||||
"label":"Brotli types",
|
||||
"env":"BROTLI_TYPES",
|
||||
"regex":"^([a-z/\\+\\-\\.] ?)*$",
|
||||
"id":"brotli-types",
|
||||
"default":"application/atom+xml application/javascript application/json application/rss+xml application/vnd.ms-fontobject application/x-font-opentype application/x-font-truetype application/x-font-ttf application/x-javascript application/xhtml+xml application/xml font/eot font/opentype font/otf font/truetype image/svg+xml image/vnd.microsoft.icon image/x-icon image/x-win-bitmap text/css text/javascript text/plain text/xml"
|
||||
}
|
||||
]
|
||||
},
|
||||
"Cache":{
|
||||
"id":"cache",
|
||||
"params":[
|
||||
{
|
||||
"type":"checkbox",
|
||||
"label":"Use client cache",
|
||||
"env":"USE_CLIENT_CACHE",
|
||||
"regex":"^(yes|no)$",
|
||||
"id":"use-client-cache",
|
||||
"default":"no"
|
||||
},
|
||||
{
|
||||
"type":"text",
|
||||
"label":"Client cache extensions",
|
||||
"env":"CLIENT_CACHE_EXTENSIONS",
|
||||
"regex":"^([a-z0-9]\\|?)*$",
|
||||
"id":"client-cache-extensions",
|
||||
"default":"jpg|jpeg|png|bmp|ico|svg|tif|css|js|otf|ttf|eot|woff|woff2"
|
||||
},
|
||||
{
|
||||
"type":"text",
|
||||
"label":"Client cache control",
|
||||
"env":"CLIENT_CACHE_CONTROL",
|
||||
"regex":"^([\\S ]*)$",
|
||||
"id":"client-cache-control",
|
||||
"default":"public, max-age=15552000"
|
||||
},
|
||||
{
|
||||
"type":"text",
|
||||
"label":"Client cache etag",
|
||||
"env":"CLIENT_CACHE_ETAG",
|
||||
"regex":"^(on|off)$",
|
||||
"id":"client-cache-etag",
|
||||
"default":"on"
|
||||
},
|
||||
{
|
||||
"type":"checkbox",
|
||||
"label":"Use open file cache",
|
||||
"env":"USE_OPEN_FILE_CACHE",
|
||||
"regex":"^(yes|no)$",
|
||||
"id":"use-open-file-cache",
|
||||
"default":"no"
|
||||
},
|
||||
{
|
||||
"type":"text",
|
||||
"label":"Open file cache",
|
||||
"env":"OPEN_FILE_CACHE",
|
||||
"regex":"^([\\S ]*)$",
|
||||
"id":"open-file-cache",
|
||||
"default":"max=1000 inactive=20s"
|
||||
},
|
||||
{
|
||||
"type":"text",
|
||||
"label":"Open file cache errors",
|
||||
"env":"OPEN_FILE_CACHE_ERRORS",
|
||||
"regex":"^(on|off)$",
|
||||
"id":"open-file-cache-errors",
|
||||
"default":"on"
|
||||
},
|
||||
{
|
||||
"type":"text",
|
||||
"label":"Open file cache min uses",
|
||||
"env":"OPEN_FILE_CACHE_MIN_USES",
|
||||
"regex":"^([1-9]+)$",
|
||||
"id":"open-file-cache-min-uses",
|
||||
"default":"2"
|
||||
},
|
||||
{
|
||||
"type":"text",
|
||||
"label":"Open file cache valid",
|
||||
"env":"OPEN_FILE_CACHE_VALID",
|
||||
"regex":"^\\d+(ms|s|m|h|d|w|M|y)$",
|
||||
"id":"open-file-cache-valid",
|
||||
"default":"30s"
|
||||
},
|
||||
{
|
||||
"type":"checkbox",
|
||||
"label":"Use proxy cache",
|
||||
"env":"USE_PROXY_CACHE",
|
||||
"regex":"^(yes|no)$",
|
||||
"id":"use-proxy-cache",
|
||||
"default":"no"
|
||||
},
|
||||
{
|
||||
"type":"text",
|
||||
"label":"Proxy cache path zone size",
|
||||
"env":"PROXY_CACHE_PATH_ZONE_SIZE",
|
||||
"regex":"^[0-9]+(k|K|m|M|g|G)?$",
|
||||
"id":"proxy-cache-path-zone-size",
|
||||
"default":"10m"
|
||||
},
|
||||
{
|
||||
"type":"text",
|
||||
"label":"Proxy cache path params",
|
||||
"env":"PROXY_CACHE_PATH_PARAMS",
|
||||
"regex":"^([\\S ]*)$",
|
||||
"id":"proxy-cache-path-params",
|
||||
"default":"max_size=100m"
|
||||
},
|
||||
{
|
||||
"type":"text",
|
||||
"label":"Proxy cache methods",
|
||||
"env":"PROXY_CACHE_METHODS",
|
||||
"regex":"^((GET|POST|HEAD|PUT|DELETE|CONNECT|OPTIONS|TRACE) ?)+$",
|
||||
"id":"proxy-cache-methods",
|
||||
"default":"GET HEAD"
|
||||
},
|
||||
{
|
||||
"type":"text",
|
||||
"label":"Proxy cache min uses",
|
||||
"env":"PROXY_CACHE_MIN_USES",
|
||||
"regex":"^([1-9]+)$",
|
||||
"id":"proxy-cache-min-uses",
|
||||
"default":"2"
|
||||
},
|
||||
{
|
||||
"type":"text",
|
||||
"label":"Proxy cache key",
|
||||
"env":"PROXY_CACHE_KEY",
|
||||
"regex":"^([\\S ]*)$",
|
||||
"id":"proxy-cache-key",
|
||||
"default":"\\$scheme\\$host\\$request_uri"
|
||||
},
|
||||
{
|
||||
"type":"text",
|
||||
"label":"Proxy cache valid",
|
||||
"env":"PROXY_CACHE_VALID",
|
||||
"regex":"^(\\d{3}=\\d+(ms|s|m|h|d|w|M|y) ?)+$",
|
||||
"id":"proxy-cache-valid",
|
||||
"default":"200=10m 301=10m 302=1h"
|
||||
},
|
||||
{
|
||||
"type":"text",
|
||||
"label":"Proxy no cache",
|
||||
"env":"PROXY_NO_CACHE",
|
||||
"regex":"^([\\S ]*)$",
|
||||
"id":"proxy-no-cache",
|
||||
"default":"\\$http_authorization"
|
||||
},
|
||||
{
|
||||
"type":"text",
|
||||
"label":"Proxy cache bypass",
|
||||
"env":"PROXY_CACHE_BYPASS",
|
||||
"regex":"^([\\S ]*)$",
|
||||
"id":"proxy-cache-bypass",
|
||||
"default":"\\$http_authorization"
|
||||
}
|
||||
]
|
||||
},
|
||||
"HTTPS":{
|
||||
"id":"https",
|
||||
"params":[
|
||||
{
|
||||
"type":"checkbox",
|
||||
"label":"Auto lets encrypt",
|
||||
"env":"AUTO_LETS_ENCRYPT",
|
||||
"regex":"^(yes|no)$",
|
||||
"id":"auto-lets-encrypt",
|
||||
"default":"no"
|
||||
},
|
||||
{
|
||||
"type":"text",
|
||||
"label":"Email lets encrypt",
|
||||
"env":"EMAIL_LETS_ENCRYPT",
|
||||
"regex":"^([a-z0-9\\-\\.]+@([a-z\\-0-9]+\\.?)|.{0})$",
|
||||
"id":"email-lets-encrypt",
|
||||
"default":""
|
||||
},
|
||||
{
|
||||
"type":"checkbox",
|
||||
"label":"Redirect http to https",
|
||||
"env":"REDIRECT_HTTP_TO_HTTPS",
|
||||
"regex":"^(yes|no)$",
|
||||
"id":"redirect-http-to-https",
|
||||
"default":"no"
|
||||
},
|
||||
{
|
||||
"type":"checkbox",
|
||||
"label":"HTTP2",
|
||||
"env":"HTTP2",
|
||||
"regex":"^(yes|no)$",
|
||||
"id":"http2",
|
||||
"default":"yes"
|
||||
},
|
||||
{
|
||||
"type":"text",
|
||||
"label":"HTTPS protocols",
|
||||
"env":"HTTPS_PROTOCOLS",
|
||||
"regex":"^([\\S ]*)$",
|
||||
"id":"https-protocols",
|
||||
"default":"TLSv1.2 TLSv1.3"
|
||||
},
|
||||
{
|
||||
"type":"checkbox",
|
||||
"label":"Listen http",
|
||||
"env":"LISTEN_HTTP",
|
||||
"regex":"^(yes|no)$",
|
||||
"id":"listen-http",
|
||||
"default":"yes"
|
||||
}
|
||||
]
|
||||
},
|
||||
"ModSecurity":{
|
||||
"id":"modsecurity",
|
||||
"params":[
|
||||
{
|
||||
"type":"checkbox",
|
||||
"label":"Use modsecurity",
|
||||
"env":"USE_MODSECURITY",
|
||||
"regex":"^(yes|no)$",
|
||||
"id":"use-modsecurity",
|
||||
"default":"yes"
|
||||
},
|
||||
{
|
||||
"type":"checkbox",
|
||||
"label":"Use modsecurity crs",
|
||||
"env":"USE_MODSECURITY_CRS",
|
||||
"regex":"^(yes|no)$",
|
||||
"id":"use-modsecurity-crs",
|
||||
"default":"yes"
|
||||
}
|
||||
]
|
||||
},
|
||||
"Headers":{
|
||||
"id":"headers",
|
||||
"params":[
|
||||
{
|
||||
"type":"text",
|
||||
"label":"X frame options",
|
||||
"env":"X_FRAME_OPTIONS",
|
||||
"regex":"^([\\S ]*)$",
|
||||
"id":"x-frame-options",
|
||||
"default":"DENY"
|
||||
},
|
||||
{
|
||||
"type":"text",
|
||||
"label":"X xss protection",
|
||||
"env":"X_XSS_PROTECTION",
|
||||
"regex":"^([\\S ]*)$",
|
||||
"id":"x-xss-protection",
|
||||
"default":"1; mode=block"
|
||||
},
|
||||
{
|
||||
"type":"text",
|
||||
"label":"X content type options",
|
||||
"env":"X_CONTENT_TYPE_OPTIONS",
|
||||
"regex":"^([\\S ]*)$",
|
||||
"id":"x-content-type-options",
|
||||
"default":"nosniff"
|
||||
},
|
||||
{
|
||||
"type":"text",
|
||||
"label":"Referrer policy",
|
||||
"env":"REFERRER_POLICY",
|
||||
"regex":"^([\\S ]*)$",
|
||||
"id":"referrer-policy",
|
||||
"default":"no-referrer"
|
||||
},
|
||||
{
|
||||
"type":"text",
|
||||
"label":"Feature policy",
|
||||
"env":"FEATURE_POLICY",
|
||||
"regex":"^([\\S ]*)$",
|
||||
"id":"feature-policy",
|
||||
"default":"accelerometer 'none'; ambient-light-sensor 'none'; autoplay 'none'; camera 'none'; display-capture 'none'; document-domain 'none'; encrypted-media 'none'; fullscreen 'none'; geolocation 'none'; gyroscope 'none'; magnetometer 'none'; microphone 'none'; midi 'none'; payment 'none'; picture-in-picture 'none'; speaker 'none'; sync-xhr 'none'; usb 'none'; vibrate 'none'; vr 'none'"
|
||||
},
|
||||
{
|
||||
"type":"text",
|
||||
"label":"Permissions policy",
|
||||
"env":"PERMISSIONS_POLICY",
|
||||
"regex":"^([\\S ]*)$",
|
||||
"id":"permissions-policy",
|
||||
"default":"accelerometer=(), ambient-light-sensor=(), autoplay=(), camera=(), display-capture=(), document-domain=(), encrypted-media=(), fullscreen=(), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), midi=(), payment=(), picture-in-picture=(), speaker=(), sync-xhr=(), usb=(), vibrate=(), vr=()"
|
||||
},
|
||||
{
|
||||
"type":"text",
|
||||
"label":"Cookie flags",
|
||||
"env":"COOKIE_FLAGS",
|
||||
"regex":"^([\\S ]*)$",
|
||||
"id":"cookie-flags",
|
||||
"default":"* HttpOnly SameSite=Lax"
|
||||
},
|
||||
{
|
||||
"type":"checkbox",
|
||||
"label":"Cookie auto secure flag",
|
||||
"env":"COOKIE_AUTO_SECURE_FLAG",
|
||||
"regex":"^(yes|no)$",
|
||||
"id":"cookie-auto-secure-flag",
|
||||
"default":"yes"
|
||||
},
|
||||
{
|
||||
"type":"text",
|
||||
"label":"Strict transport security",
|
||||
"env":"STRICT_TRANSPORT_SECURITY",
|
||||
"regex":"^([\\S ]*)$",
|
||||
"id":"strict-transport-security",
|
||||
"default":"max-age=31536000"
|
||||
},
|
||||
{
|
||||
"type":"text",
|
||||
"label":"Content security policy",
|
||||
"env":"CONTENT_SECURITY_POLICY",
|
||||
"regex":"^([\\S ]*)$",
|
||||
"id":"content-security-policy",
|
||||
"default":"object-src 'none'; frame-ancestors 'self'; form-action 'self'; block-all-mixed-content; sandbox allow-forms allow-same-origin allow-scripts allow-popups; base-uri 'self';"
|
||||
}
|
||||
]
|
||||
},
|
||||
"Antibot":{
|
||||
"id":"Antibot",
|
||||
"params":[
|
||||
{
|
||||
"type":"text",
|
||||
"label":"Use antibot",
|
||||
"env":"USE_ANTIBOT",
|
||||
"regex":"^(no|cookie|javascript|captcha|recaptcha)$",
|
||||
"id":"use-antibot",
|
||||
"default":"no"
|
||||
},
|
||||
{
|
||||
"type":"text",
|
||||
"label":"Antibot uri",
|
||||
"env":"ANTIBOT_URI",
|
||||
"regex":"^/([A-Za-z0-9\\-]/?)*$",
|
||||
"id":"antibot-uri",
|
||||
"default":"/challenge"
|
||||
},
|
||||
{
|
||||
"type":"text",
|
||||
"label":"Antibot session secret",
|
||||
"env":"ANTIBOT_SESSION_SECRET",
|
||||
"regex":"^([\\S]+)$",
|
||||
"id":"antibot-session-secret",
|
||||
"default":"random"
|
||||
},
|
||||
{
|
||||
"type":"text",
|
||||
"label":"Antibot recaptcha score",
|
||||
"env":"ANTIBOT_RECAPTCHA_SCORE",
|
||||
"regex":"^0\\.\\d$",
|
||||
"id":"antibot-recaptcha-score",
|
||||
"default":"0.7"
|
||||
}
|
||||
]
|
||||
},
|
||||
"Block":{
|
||||
"id":"block",
|
||||
"params":[
|
||||
{
|
||||
"type":"checkbox",
|
||||
"label":"Block user agent",
|
||||
"env":"BLOCK_USER_AGENT",
|
||||
"regex":"^(yes|no)$",
|
||||
"id":"block-user-agent",
|
||||
"default":"yes"
|
||||
},
|
||||
{
|
||||
"type":"checkbox",
|
||||
"label":"Block tor exit node",
|
||||
"env":"BLOCK_TOR_EXIT_NODE",
|
||||
"regex":"^(yes|no)$",
|
||||
"id":"block-tor-exit-node",
|
||||
"default":"yes"
|
||||
},
|
||||
{
|
||||
"type":"checkbox",
|
||||
"label":"Block proxies",
|
||||
"env":"BLOCK_PROXIES",
|
||||
"regex":"^(yes|no)$",
|
||||
"id":"block-proxies",
|
||||
"default":"yes"
|
||||
},
|
||||
{
|
||||
"type":"checkbox",
|
||||
"label":"Block abusers",
|
||||
"env":"BLOCK_ABUSERS",
|
||||
"regex":"^(yes|no)$",
|
||||
"id":"block-abusers",
|
||||
"default":"yes"
|
||||
},
|
||||
{
|
||||
"type":"checkbox",
|
||||
"label":"Block referrer",
|
||||
"env":"BLOCK_REFERRER",
|
||||
"regex":"^(yes|no)$",
|
||||
"id":"block-referrer",
|
||||
"default":"yes"
|
||||
}
|
||||
]
|
||||
},
|
||||
"DNSBL":{
|
||||
"id":"dnsbl",
|
||||
"params":[
|
||||
{
|
||||
"type":"checkbox",
|
||||
"label":"Use dnsbl",
|
||||
"env":"USE_DNSBL",
|
||||
"regex":"^(yes|no)$",
|
||||
"id":"use-dnsbl",
|
||||
"default":"yes"
|
||||
}
|
||||
]
|
||||
},
|
||||
"CrowdSec":{
|
||||
"id":"use-crowdsec",
|
||||
"params":[
|
||||
{
|
||||
"type":"checkbox",
|
||||
"label":"Use crowdsec",
|
||||
"env":"USE_CROWDSEC",
|
||||
"regex":"^(yes|no)$",
|
||||
"id":"use-crowdsec",
|
||||
"default":"no"
|
||||
}
|
||||
]
|
||||
},
|
||||
"Whitelist":{
|
||||
"id":"whitelist",
|
||||
"params":[
|
||||
{
|
||||
"type":"checkbox",
|
||||
"label":"Use whitelist ip",
|
||||
"env":"USE_WHITELIST_IP",
|
||||
"regex":"^(yes|no)$",
|
||||
"id":"use-whitelist-ip",
|
||||
"default":"yes"
|
||||
},
|
||||
{
|
||||
"type":"checkbox",
|
||||
"label":"Use whitelist reverse",
|
||||
"env":"USE_WHITELIST_REVERSE",
|
||||
"regex":"^(yes|no)$",
|
||||
"id":"use-whitelist-reverse",
|
||||
"default":"yes"
|
||||
},
|
||||
{
|
||||
"type":"text",
|
||||
"label":"Whitelist country",
|
||||
"env":"WHITELIST_COUNTRY",
|
||||
"regex":"^([A-Z]{2} ?)*$",
|
||||
"id":"whitelist-country",
|
||||
"default":""
|
||||
}
|
||||
]
|
||||
},
|
||||
"Blacklist":{
|
||||
"id":"blacklist",
|
||||
"params":[
|
||||
{
|
||||
"type":"checkbox",
|
||||
"label":"Use blacklist ip",
|
||||
"env":"USE_BLACKLIST_IP",
|
||||
"regex":"^(yes|no)$",
|
||||
"id":"use-blacklist-ip",
|
||||
"default":"yes"
|
||||
},
|
||||
{
|
||||
"type":"checkbox",
|
||||
"label":"Use blacklist reverse",
|
||||
"env":"USE_BLACKLIST_REVERSE",
|
||||
"regex":"^(yes|no)$",
|
||||
"id":"use-blacklist-reverse",
|
||||
"default":"yes"
|
||||
},
|
||||
{
|
||||
"type":"text",
|
||||
"label":"Blacklist country",
|
||||
"env":"BLACKLIST_COUNTRY",
|
||||
"regex":"^([A-Z]{2} ?)*$",
|
||||
"id":"blacklist-country",
|
||||
"default":""
|
||||
}
|
||||
]
|
||||
},
|
||||
"Limit req":{
|
||||
"id":"limit-req",
|
||||
"params":[
|
||||
{
|
||||
"type":"checkbox",
|
||||
"label":"Use limit req",
|
||||
"env":"USE_LIMIT_REQ",
|
||||
"regex":"^(yes|no)$",
|
||||
"id":"use-limit-req",
|
||||
"default":"yes"
|
||||
},
|
||||
{
|
||||
"type":"text",
|
||||
"label":"Limit req rate",
|
||||
"env":"LIMIT_REQ_RATE",
|
||||
"regex":"^\\d+r/(ms|s|m|h|d)$",
|
||||
"id":"limit-req-rate",
|
||||
"default":"20r/s"
|
||||
},
|
||||
{
|
||||
"type":"text",
|
||||
"label":"Limit req burst",
|
||||
"env":"LIMIT_REQ_BURST",
|
||||
"regex":"^\\d+$",
|
||||
"id":"limit-req-burst",
|
||||
"default":"40"
|
||||
}
|
||||
]
|
||||
},
|
||||
"PHP":{
|
||||
"id":"php",
|
||||
"params":[
|
||||
{
|
||||
"type":"text",
|
||||
"label":"Remote php",
|
||||
"env":"REMOTE_PHP",
|
||||
"regex":"^([a-z\\-0-9]+\\.?)*$",
|
||||
"id":"remote-php",
|
||||
"default":""
|
||||
},
|
||||
{
|
||||
"type":"text",
|
||||
"label":"Remote php path",
|
||||
"env":"REMOTE_PHP_PATH",
|
||||
"regex":"^/([A-Za-z0-9\\-]/?)*$",
|
||||
"id":"remote-php-path",
|
||||
"default":"/app"
|
||||
}
|
||||
]
|
||||
},
|
||||
"Fail2ban":{
|
||||
"id":"fail2ban",
|
||||
"params":[
|
||||
{
|
||||
"type":"checkbox",
|
||||
"label":"Use fail2ban",
|
||||
"env":"USE_FAIL2BAN",
|
||||
"regex":"^(yes|no)$",
|
||||
"id":"use-fail2ban",
|
||||
"default":"yes"
|
||||
}
|
||||
]
|
||||
},
|
||||
"ClamAV":{
|
||||
"id":"clamav",
|
||||
"params":[
|
||||
{
|
||||
"type":"checkbox",
|
||||
"label":"Use clamav upload",
|
||||
"env":"USE_CLAMAV_UPLOAD",
|
||||
"regex":"^(yes|no)$",
|
||||
"id":"use-clamav-upload",
|
||||
"default":"yes"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
85
ui/config.py
Normal file
85
ui/config.py
Normal file
@@ -0,0 +1,85 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
import utils
|
||||
import subprocess, shutil, os, traceback
|
||||
|
||||
def generate(instances, vars) :
|
||||
try :
|
||||
# Get env vars from bunkerized-nginx instances
|
||||
vars_instances = {}
|
||||
for instance_id, instance in instances.items() :
|
||||
for var_value in instance.attrs["Config"]["Env"] :
|
||||
var = var_value.split("=")[0]
|
||||
value = var_value.replace(var + "=", "", 1)
|
||||
vars_instances[var] = value
|
||||
vars_defaults = vars.copy()
|
||||
vars_defaults.update(vars_instances)
|
||||
vars_defaults.update(vars)
|
||||
# Call site-config.sh to generate the config
|
||||
proc = subprocess.run(["/opt/entrypoint/site-config.sh", vars["SERVER_NAME"]], env=vars_defaults, capture_output=True)
|
||||
if proc.returncode == 0 :
|
||||
return True
|
||||
except Exception as e :
|
||||
traceback.print_exc()
|
||||
utils.log("[!] Error while generating config : " + str(e))
|
||||
return False
|
||||
|
||||
def activate(instances, vars) :
|
||||
try :
|
||||
# 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")
|
||||
return False
|
||||
|
||||
# Include the server conf
|
||||
utils.replace_in_file("/etc/nginx/nginx.conf", "}", "include /etc/nginx/" + vars["SERVER_NAME"] + "/server.conf;\n}")
|
||||
|
||||
# Send SIGHUP to all running instances
|
||||
for instance_id, instance in instances.items() :
|
||||
if instance.status == "running" :
|
||||
try :
|
||||
instance.kill("SIGHUP")
|
||||
utils.log("[*] Sent SIGHUP signal to bunkerized-nginx instance " + instance.name + " / " + instance.id)
|
||||
except docker.errors.APIError as e :
|
||||
utils.log("[!] Docker error while sending SIGHUP signal : " + str(e))
|
||||
return True
|
||||
except Exception as e :
|
||||
utils.log("[!] Error while activating config : " + str(e))
|
||||
return False
|
||||
|
||||
def deactivate(instances, vars) :
|
||||
try :
|
||||
# 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")
|
||||
return False
|
||||
|
||||
# Remove the include
|
||||
utils.replace_in_file("/etc/nginx/nginx.conf", "include /etc/nginx/" + vars["SERVER_NAME"] + "/server.conf;\n", "")
|
||||
|
||||
# Send SIGHUP to all running instances
|
||||
for instance_id, instance in instances.items() :
|
||||
if instance.status == "running" :
|
||||
try :
|
||||
instance.kill("SIGHUP")
|
||||
utils.log("[*] Sent SIGHUP signal to bunkerized-nginx instance " + instance.name + " / " + instance.id)
|
||||
except docker.errors.APIError as e :
|
||||
utils.log("[!] Docker error while sending SIGHUP signal : " + str(e))
|
||||
return True
|
||||
except Exception as e :
|
||||
utils.log("[!] Error while deactivating config : " + str(e))
|
||||
return False
|
||||
|
||||
def remove(instances, vars) :
|
||||
try :
|
||||
# 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")
|
||||
return False
|
||||
|
||||
# Remove the folder
|
||||
shutil.rmtree("/etc/nginx/" + vars["SERVER_NAME"])
|
||||
return True
|
||||
except Exception as e :
|
||||
utils.log("[!] Error while deactivating config : " + str(e))
|
||||
return False
|
||||
109
ui/entrypoint-autoconf.py
Normal file
109
ui/entrypoint-autoconf.py
Normal file
@@ -0,0 +1,109 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
import utils, config
|
||||
import docker, os, stat, sys
|
||||
|
||||
def process(container, event) :
|
||||
global instances, containers
|
||||
|
||||
# Process instance event
|
||||
if "bunkerized-nginx.AUTOCONF" in container.labels :
|
||||
if event == "create" :
|
||||
instances[container.id] = container
|
||||
utils.log("[*] bunkerized-nginx instance created : " + container.name + " / " + container.id)
|
||||
elif event == "start" :
|
||||
instances[container.id].reload()
|
||||
utils.log("[*] bunkerized-nginx instance started : " + container.name + " / " + container.id)
|
||||
elif event == "die" :
|
||||
instances[container.id].reload()
|
||||
utils.log("[*] bunkerized-nginx instance stopped : " + container.name + " / " + container.id)
|
||||
elif event == "destroy" :
|
||||
del instances[container.id]
|
||||
utils.log("[*] bunkerized-nginx instance removed : " + container.name + " / " + container.id)
|
||||
|
||||
# Process container event
|
||||
elif "bunkerized-nginx.SERVER_NAME" in container.labels :
|
||||
# Convert labels to env vars
|
||||
vars = { k.replace("bunkerized-nginx.", "", 1) : v for k, v in container.labels.items() if k.startswith("bunkerized-nginx.")}
|
||||
if event == "create" :
|
||||
if config.generate(instances, vars) :
|
||||
utils.log("[*] Generated config for " + vars["SERVER_NAME"])
|
||||
containers[container.id] = container
|
||||
else :
|
||||
utils.log("[!] Can't generate config for " + vars["SERVER_NAME"])
|
||||
elif event == "start" :
|
||||
if container.id in containers :
|
||||
containers[container.id].reload()
|
||||
if config.activate(instances, vars) :
|
||||
utils.log("[*] Activated config for " + vars["SERVER_NAME"])
|
||||
else :
|
||||
utils.log("[!] Can't activate config for " + vars["SERVER_NAME"])
|
||||
elif event == "die" :
|
||||
if container.id in containers :
|
||||
containers[container.id].reload()
|
||||
if config.deactivate(instances, vars) :
|
||||
utils.log("[*] Deactivated config for " + vars["SERVER_NAME"])
|
||||
else :
|
||||
utils.log("[!] Can't deactivate config for " + vars["SERVER_NAME"])
|
||||
elif event == "destroy" :
|
||||
if container.id in containers :
|
||||
del containers[container.id]
|
||||
if config.remove(vars) :
|
||||
utils.log("[*] Removed config for " + vars["SERVER_NAME"])
|
||||
else :
|
||||
utils.log("[!] Can't remove config for " + vars["SERVER_NAME"])
|
||||
|
||||
# Connect to the endpoint
|
||||
endpoint = "/var/run/docker.sock"
|
||||
if not os.path.exists(endpoint) or not stat.S_ISSOCK(os.stat(endpoint).st_mode) :
|
||||
utils.log("[!] /var/run/docker.sock not found (is it mounted ?)")
|
||||
sys.exit(1)
|
||||
try :
|
||||
client = docker.DockerClient(base_url='unix:///var/run/docker.sock')
|
||||
except Exception as e :
|
||||
utils.log("[!] Can't instantiate DockerClient : " + str(e))
|
||||
sys.exit(2)
|
||||
|
||||
# Get all bunkerized-nginx instances and web services created before
|
||||
instances = {}
|
||||
containers = {}
|
||||
try :
|
||||
before = client.containers.list(all=True, filters={"label" : "bunkerized-nginx.AUTOCONF"}) + client.containers.list(all=True, filters={"label" : "bunkerized-nginx.SERVER_NAME"})
|
||||
except docker.errors.APIError as e :
|
||||
utils.log("[!] Docker API error " + str(e))
|
||||
sys.exit(3)
|
||||
for container in before :
|
||||
if container.status in ("restarting", "running", "created", "exited") :
|
||||
process(container, "create")
|
||||
if container.status == "running" :
|
||||
process(container, "start")
|
||||
|
||||
# Process events received from Docker
|
||||
try :
|
||||
for event in client.events(decode=True) :
|
||||
|
||||
# Process only container events
|
||||
if event["Type"] != "container" :
|
||||
continue
|
||||
|
||||
# Get Container object
|
||||
try :
|
||||
container = client.containers.get(event["id"])
|
||||
except docker.errors.NotFound as e :
|
||||
continue
|
||||
|
||||
# Check if there is an interesting label
|
||||
interesting = False
|
||||
for label in container.labels :
|
||||
if label in ("bunkerized-nginx.SERVER_NAME", "bunkerized-nginx.AUTOCONF") :
|
||||
interesting = True
|
||||
break
|
||||
if not interesting :
|
||||
continue
|
||||
|
||||
# Process the event
|
||||
process(container, event["Action"])
|
||||
|
||||
except docker.errors.APIError as e :
|
||||
utils.log("[!] Docker API error " + str(e))
|
||||
sys.exit(4)
|
||||
123
ui/entrypoint.py
Normal file
123
ui/entrypoint.py
Normal file
@@ -0,0 +1,123 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
from flask import Flask, render_template, current_app, request
|
||||
|
||||
import wrappers, utils
|
||||
import os, json, re
|
||||
|
||||
app = Flask(__name__, static_url_path="/", static_folder="static", template_folder="templates")
|
||||
ABSOLUTE_URI = ""
|
||||
if "ABSOLUTE_URI" in os.environ :
|
||||
ABSOLUTE_URI = os.environ["ABSOLUTE_URI"]
|
||||
app.config["ABSOLUTE_URI"] = ABSOLUTE_URI
|
||||
with open("/opt/entrypoint/config.json", "r") as f :
|
||||
app.config["CONFIG"] = json.loads(f.read())
|
||||
app.jinja_env.globals.update(env_to_summary_class=utils.env_to_summary_class)
|
||||
app.jinja_env.globals.update(form_service_gen=utils.form_service_gen)
|
||||
app.jinja_env.globals.update(form_service_gen_multiple=utils.form_service_gen_multiple)
|
||||
app.jinja_env.globals.update(form_service_gen_multiple_values=utils.form_service_gen_multiple_values)
|
||||
|
||||
@app.route('/')
|
||||
@app.route('/home')
|
||||
def home():
|
||||
check, client = wrappers.get_client()
|
||||
if not check :
|
||||
return render_template("error.html", title="Error", error=client)
|
||||
check, instances = wrappers.get_instances(client)
|
||||
if not check :
|
||||
return render_template("error.html", title="Error", error=instances)
|
||||
check, services = wrappers.get_services()
|
||||
if not check :
|
||||
return render_template("error.html", title="Error", error=services)
|
||||
return render_template("home.html", title="Home", instances_number=len(instances), services_number=len(services))
|
||||
|
||||
@app.route('/instances', methods=["GET", "POST"])
|
||||
def instances():
|
||||
|
||||
# Get the client
|
||||
check, client = wrappers.get_client()
|
||||
if not check :
|
||||
return render_template("error.html", title="Error", error=client)
|
||||
|
||||
# Manage instances
|
||||
operation = ""
|
||||
if request.method == "POST" :
|
||||
|
||||
# Check operation
|
||||
if not "operation" in request.form or not request.form["operation"] in ["reload", "start", "stop", "restart", "delete"] :
|
||||
return render_template("error.html", title="Error", error="Missing operation parameter on /instances.")
|
||||
|
||||
# Check that all fields are present
|
||||
if not "INSTANCE_ID" in request.form :
|
||||
return render_template("error.html", title="Error", error="Missing INSTANCE_ID parameter.")
|
||||
|
||||
# Do the operation
|
||||
check, operation = wrappers.operation_instance(client, request.form)
|
||||
if not check :
|
||||
return render_template("error.html", title="Error", error=operation)
|
||||
|
||||
# Display instances
|
||||
check, instances = wrappers.get_instances(client)
|
||||
if not check :
|
||||
return render_template("error.html", title="Error", error=instances)
|
||||
return render_template("instances.html", title="Instances", instances=instances, operation=operation)
|
||||
|
||||
@app.route('/services', methods=["GET", "POST"])
|
||||
def services():
|
||||
|
||||
# Get the client
|
||||
check, client = wrappers.get_client()
|
||||
if not check :
|
||||
return render_template("error.html", title="Error", error=client)
|
||||
|
||||
# Manage services
|
||||
operation = ""
|
||||
if request.method == "POST" :
|
||||
|
||||
# Check operation
|
||||
if not "operation" in request.form or not request.form["operation"] in ["new", "edit", "delete"] :
|
||||
return render_template("error.html", title="Error", error="Missing operation parameter on /services.")
|
||||
|
||||
# Check that all fields are present and they match the corresponding regex
|
||||
env = {}
|
||||
env["MULTISITE"] = "yes"
|
||||
if request.form["operation"] in ["new", "edit"] :
|
||||
for category in current_app.config["CONFIG"] :
|
||||
for param in current_app.config["CONFIG"][category]["params"] :
|
||||
if param["type"] == "multiple" :
|
||||
for param_user in request.form :
|
||||
if param_user.startswith(param["params"][0]["env"]) :
|
||||
suffix = param_user.replace(param["params"][0]["env"], "")
|
||||
for param_multiple in param["params"] :
|
||||
if not param_multiple["env"] + suffix in request.form :
|
||||
return render_template("error.html", title="Error", error="Missing " + param["env"] + " parameter.")
|
||||
if not re.search(param_multiple["regex"], request.form[param_multiple["env"] + suffix]) :
|
||||
return render_template("error.html", title="Error", error="Parameter " + param["env"] + " doesn't match regex.")
|
||||
env[param_multiple["env"] + suffix] = request.form[param_multiple["env"] + suffix]
|
||||
else :
|
||||
if not param["env"] in request.form :
|
||||
return render_template("error.html", title="Error", error="Missing " + param["env"] + " parameter.")
|
||||
if not re.search(param["regex"], request.form[param["env"]]) :
|
||||
return render_template("error.html", title="Error", error="Parameter " + param["env"] + " doesn't match regex.")
|
||||
env[param["env"]] = request.form[param["env"]]
|
||||
if request.form["operation"] == "edit" :
|
||||
if not "OLD_SERVER_NAME" in request.form :
|
||||
return render_template("error.html", title="Error", error="Missing OLD_SERVER_NAME parameter.")
|
||||
if not re.search("^([a-z\-0-9]+\.?)+$", request.form["OLD_SERVER_NAME"]) :
|
||||
return render_template("error.html", title="Error", error="Parameter OLD_SERVER_NAME doesn't match regex.")
|
||||
elif request.form["operation"] == "delete" :
|
||||
if not "SERVER_NAME" in request.form :
|
||||
return render_template("error.html", title="Error", error="Missing SERVER_NAME parameter.")
|
||||
if not re.search("^([a-z\-0-9]+\.?)+$", request.form["SERVER_NAME"]) :
|
||||
return render_template("error.html", title="Error", error="Parameter SERVER_NAME doesn't match regex.")
|
||||
|
||||
# Do the operation
|
||||
check, operation = wrappers.operation_service(client, request.form, env)
|
||||
if not check :
|
||||
render_template("error.html", title="Error", error=operation)
|
||||
|
||||
# Display services
|
||||
check, services = wrappers.get_services()
|
||||
if not check :
|
||||
return render_template("error.html", title="Error", error=services)
|
||||
return render_template("services.html", title="Services", services=services, operation=operation)
|
||||
12
ui/hooks/post_push
Normal file
12
ui/hooks/post_push
Normal file
@@ -0,0 +1,12 @@
|
||||
#!/bin/bash
|
||||
|
||||
curl -Lo manifest-tool https://github.com/estesp/manifest-tool/releases/download/v1.0.3/manifest-tool-linux-amd64
|
||||
chmod +x manifest-tool
|
||||
|
||||
VERSION=$(cat VERSION | tr -d '\n')
|
||||
if [ "$SOURCE_BRANCH" = "dev" ] ; then
|
||||
./manifest-tool push from-args --ignore-missing --platforms linux/amd64,linux/386,linux/arm/v7,linux/arm64/v8 --template bunkerity/bunkerized-nginx-ui:dev-ARCHVARIANT --target bunkerity/bunkerized-nginx-ui:dev
|
||||
elif [ "$SOURCE_BRANCH" = "master" ] ; then
|
||||
./manifest-tool push from-args --ignore-missing --platforms linux/amd64,linux/386,linux/arm/v7,linux/arm64/v8 --template bunkerity/bunkerized-nginx-ui:ARCHVARIANT --target bunkerity/bunkerized-nginx-ui:${VERSION}
|
||||
./manifest-tool push from-args --ignore-missing --platforms linux/amd64,linux/386,linux/arm/v7,linux/arm64/v8 --template bunkerity/bunkerized-nginx-ui:ARCHVARIANT --target bunkerity/bunkerized-nginx-ui:latest
|
||||
fi
|
||||
5
ui/hooks/pre_build
Normal file
5
ui/hooks/pre_build
Normal file
@@ -0,0 +1,5 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Register qemu-*-static for all supported processors except the
|
||||
# current one, but also remove all registered binfmt_misc before
|
||||
docker run --rm --privileged multiarch/qemu-user-static:register --reset
|
||||
6
ui/static/css/bootstrap.min.css
vendored
Normal file
6
ui/static/css/bootstrap.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
17
ui/static/css/custom.css
Normal file
17
ui/static/css/custom.css
Normal file
@@ -0,0 +1,17 @@
|
||||
.bd-placeholder-img {
|
||||
font-size: 1.125rem;
|
||||
text-anchor: middle;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.bd-placeholder-img-lg {
|
||||
font-size: 3.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
main > .container {
|
||||
padding: 100px 15px 0;
|
||||
}
|
||||
5
ui/static/css/fa.min.css
vendored
Normal file
5
ui/static/css/fa.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
BIN
ui/static/img/logo.png
Normal file
BIN
ui/static/img/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 76 KiB |
6
ui/static/js/bootstrap.bundle.min.js
vendored
Normal file
6
ui/static/js/bootstrap.bundle.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user