Compare commits
72 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b0ca85ff75 | ||
|
|
2f115c444d | ||
|
|
7f15741ea2 | ||
|
|
288b8eb851 | ||
|
|
61c08fb97b | ||
|
|
01ef47a669 | ||
|
|
71515a9101 | ||
|
|
a33d0658c6 | ||
|
|
0b3ff6a9f4 | ||
|
|
eb2d0d330d | ||
|
|
5bcbb38638 | ||
|
|
ca660b2501 | ||
|
|
3a34436cd8 | ||
|
|
b1d03cd11c | ||
|
|
42c3fb8740 | ||
|
|
f1c043604a | ||
|
|
fd61df205f | ||
|
|
009d6fb5ae | ||
|
|
ba4185a42e | ||
|
|
70976d0fbc | ||
|
|
062a39c63a | ||
|
|
83841b290a | ||
|
|
10dc58cb6d | ||
|
|
668754686c | ||
|
|
84b1933f63 | ||
|
|
15f6d0a32a | ||
|
|
e628361a89 | ||
|
|
f8d71e067e | ||
|
|
02ae3b6bd3 | ||
|
|
2fb0e7c473 | ||
|
|
9adcc2f1a7 | ||
|
|
7b98db4d14 | ||
|
|
ddb2b85916 | ||
|
|
da1a460a64 | ||
|
|
07be626842 | ||
|
|
3bb164395e | ||
|
|
bc2568a172 | ||
|
|
5ec74880d8 | ||
|
|
f84fd7c9a2 | ||
|
|
6521d7a27a | ||
|
|
813607fbc3 | ||
|
|
843644f806 | ||
|
|
19fa0eb25f | ||
|
|
b4df287228 | ||
|
|
5ce41edc03 | ||
|
|
a3cfb50b4d | ||
|
|
25494acace | ||
|
|
a98dae1fb6 | ||
|
|
1a7abab570 | ||
|
|
42b7a57f01 | ||
|
|
02f9fbe5fc | ||
|
|
69fe066777 | ||
|
|
74417abc9c | ||
|
|
ba7524a419 | ||
|
|
b55aafb997 | ||
|
|
deeb7a76a2 | ||
|
|
ee8aaa4e7e | ||
|
|
605d59a45c | ||
|
|
b85c991b6e | ||
|
|
0d3658adf0 | ||
|
|
0b22209c96 | ||
|
|
e44a1f3e14 | ||
|
|
aa614f82f9 | ||
|
|
c03d410b0a | ||
|
|
e190167bfc | ||
|
|
31e72dce1c | ||
|
|
b8105fc558 | ||
|
|
e73c10fd80 | ||
|
|
a122a259c0 | ||
|
|
7c4894d3b8 | ||
|
|
533c2a1034 | ||
|
|
5611d544d6 |
17
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
17
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Something is not working as expected
|
||||
title: "[BUG]"
|
||||
labels: bug
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Description**
|
||||
Concise description of what you're trying to do, the expected behavior and the current bug.
|
||||
|
||||
**How to reproduce**
|
||||
Give steps on how to reproduce the bug (e.g. : commands, configs, tests, environment, version, ...).
|
||||
|
||||
**Logs**
|
||||
The logs generated by bunkerized-nginx. **DON'T FORGET TO REMOVE PRIVATE DATA LIKE IP ADDRESSES !**
|
||||
@@ -24,5 +24,5 @@ jobs:
|
||||
exit-code: '1'
|
||||
ignore-unfixed: true
|
||||
severity: 'UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL'
|
||||
|
||||
skip-dirs: '/usr/lib/go'
|
||||
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1 +1,2 @@
|
||||
.idea/
|
||||
docs/_build/
|
||||
|
||||
18
Dockerfile
18
Dockerfile
@@ -1,4 +1,4 @@
|
||||
FROM nginx:stable-alpine
|
||||
FROM nginx:1.20.0-alpine
|
||||
|
||||
COPY nginx-keys/ /tmp/nginx-keys
|
||||
COPY compile.sh /tmp/compile.sh
|
||||
@@ -6,20 +6,28 @@ RUN chmod +x /tmp/compile.sh && \
|
||||
/tmp/compile.sh && \
|
||||
rm -rf /tmp/*
|
||||
|
||||
COPY dependencies.sh /tmp/dependencies.sh
|
||||
RUN chmod +x /tmp/dependencies.sh && \
|
||||
/tmp/dependencies.sh && \
|
||||
rm -rf /tmp/dependencies.sh
|
||||
|
||||
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 prepare.sh /tmp/prepare.sh
|
||||
RUN chmod +x /tmp/prepare.sh && /tmp/prepare.sh && rm -f /tmp/prepare.sh
|
||||
RUN chmod +x /tmp/prepare.sh && \
|
||||
/tmp/prepare.sh && \
|
||||
rm -f /tmp/prepare.sh
|
||||
|
||||
# fix CVE-2021-20205
|
||||
RUN apk add "libjpeg-turbo>=2.1.0-r0"
|
||||
|
||||
VOLUME /www /http-confs /server-confs /modsec-confs /modsec-crs-confs /cache /pre-server-confs /acme-challenge
|
||||
|
||||
EXPOSE 8080/tcp 8443/tcp
|
||||
|
||||
USER nginx
|
||||
USER nginx:nginx
|
||||
|
||||
ENTRYPOINT ["/opt/entrypoint/entrypoint.sh"]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM amd64/nginx:stable-alpine
|
||||
FROM amd64/nginx:1.20.0-alpine
|
||||
|
||||
COPY nginx-keys/ /tmp/nginx-keys
|
||||
COPY compile.sh /tmp/compile.sh
|
||||
@@ -6,20 +6,28 @@ RUN chmod +x /tmp/compile.sh && \
|
||||
/tmp/compile.sh && \
|
||||
rm -rf /tmp/*
|
||||
|
||||
COPY dependencies.sh /tmp/dependencies.sh
|
||||
RUN chmod +x /tmp/dependencies.sh && \
|
||||
/tmp/dependencies.sh && \
|
||||
rm -rf /tmp/dependencies.sh
|
||||
|
||||
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 prepare.sh /tmp/prepare.sh
|
||||
RUN chmod +x /tmp/prepare.sh && /tmp/prepare.sh && rm -f /tmp/prepare.sh
|
||||
RUN chmod +x /tmp/prepare.sh && \
|
||||
/tmp/prepare.sh && \
|
||||
rm -f /tmp/prepare.sh
|
||||
|
||||
# fix CVE-2021-20205
|
||||
RUN apk add "libjpeg-turbo>=2.1.0-r0"
|
||||
|
||||
VOLUME /www /http-confs /server-confs /modsec-confs /modsec-crs-confs /cache /pre-server-confs /acme-challenge
|
||||
|
||||
EXPOSE 8080/tcp 8443/tcp
|
||||
|
||||
USER nginx
|
||||
USER nginx:nginx
|
||||
|
||||
ENTRYPOINT ["/opt/entrypoint/entrypoint.sh"]
|
||||
|
||||
@@ -3,7 +3,7 @@ FROM alpine AS builder
|
||||
ENV QEMU_URL https://github.com/balena-io/qemu/releases/download/v4.0.0%2Bbalena2/qemu-4.0.0.balena2-arm.tar.gz
|
||||
RUN apk add curl && curl -L ${QEMU_URL} | tar zxvf - -C . --strip-components 1
|
||||
|
||||
FROM arm32v7/nginx:stable-alpine
|
||||
FROM arm32v7/nginx:1.20.0-alpine
|
||||
|
||||
COPY --from=builder qemu-arm-static /usr/bin
|
||||
|
||||
@@ -13,20 +13,28 @@ RUN chmod +x /tmp/compile.sh && \
|
||||
/tmp/compile.sh && \
|
||||
rm -rf /tmp/*
|
||||
|
||||
COPY dependencies.sh /tmp/dependencies.sh
|
||||
RUN chmod +x /tmp/dependencies.sh && \
|
||||
/tmp/dependencies.sh && \
|
||||
rm -rf /tmp/dependencies.sh
|
||||
|
||||
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 prepare.sh /tmp/prepare.sh
|
||||
RUN chmod +x /tmp/prepare.sh && /tmp/prepare.sh && rm -f /tmp/prepare.sh
|
||||
RUN chmod +x /tmp/prepare.sh && \
|
||||
/tmp/prepare.sh && \
|
||||
rm -f /tmp/prepare.sh
|
||||
|
||||
# fix CVE-2021-20205
|
||||
RUN apk add "libjpeg-turbo>=2.1.0-r0"
|
||||
|
||||
VOLUME /www /http-confs /server-confs /modsec-confs /modsec-crs-confs /cache /pre-server-confs /acme-challenge
|
||||
|
||||
EXPOSE 8080/tcp 8443/tcp
|
||||
|
||||
USER nginx
|
||||
USER nginx:nginx
|
||||
|
||||
ENTRYPOINT ["/opt/entrypoint/entrypoint.sh"]
|
||||
|
||||
@@ -3,7 +3,7 @@ FROM alpine AS builder
|
||||
ENV QEMU_URL https://github.com/balena-io/qemu/releases/download/v4.0.0%2Bbalena2/qemu-4.0.0.balena2-aarch64.tar.gz
|
||||
RUN apk add curl && curl -L ${QEMU_URL} | tar zxvf - -C . --strip-components 1
|
||||
|
||||
FROM arm64v8/nginx:stable-alpine
|
||||
FROM arm64v8/nginx:1.20.0-alpine
|
||||
|
||||
COPY --from=builder qemu-aarch64-static /usr/bin
|
||||
|
||||
@@ -13,20 +13,28 @@ RUN chmod +x /tmp/compile.sh && \
|
||||
/tmp/compile.sh && \
|
||||
rm -rf /tmp/*
|
||||
|
||||
COPY dependencies.sh /tmp/dependencies.sh
|
||||
RUN chmod +x /tmp/dependencies.sh && \
|
||||
/tmp/dependencies.sh && \
|
||||
rm -rf /tmp/dependencies.sh
|
||||
|
||||
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 prepare.sh /tmp/prepare.sh
|
||||
RUN chmod +x /tmp/prepare.sh && /tmp/prepare.sh && rm -f /tmp/prepare.sh
|
||||
RUN chmod +x /tmp/prepare.sh && \
|
||||
/tmp/prepare.sh && \
|
||||
rm -f /tmp/prepare.sh
|
||||
|
||||
# fix CVE-2021-20205
|
||||
RUN apk add "libjpeg-turbo>=2.1.0-r0"
|
||||
|
||||
VOLUME /www /http-confs /server-confs /modsec-confs /modsec-crs-confs /cache /pre-server-confs /acme-challenge
|
||||
|
||||
EXPOSE 8080/tcp 8443/tcp
|
||||
|
||||
USER nginx
|
||||
USER nginx:nginx
|
||||
|
||||
ENTRYPOINT ["/opt/entrypoint/entrypoint.sh"]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM i386/nginx:stable-alpine
|
||||
FROM i386/nginx:1.20.0-alpine
|
||||
|
||||
COPY nginx-keys/ /tmp/nginx-keys
|
||||
COPY compile.sh /tmp/compile.sh
|
||||
@@ -6,20 +6,28 @@ RUN chmod +x /tmp/compile.sh && \
|
||||
/tmp/compile.sh && \
|
||||
rm -rf /tmp/*
|
||||
|
||||
COPY dependencies.sh /tmp/dependencies.sh
|
||||
RUN chmod +x /tmp/dependencies.sh && \
|
||||
/tmp/dependencies.sh && \
|
||||
rm -rf /tmp/dependencies.sh
|
||||
|
||||
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 prepare.sh /tmp/prepare.sh
|
||||
RUN chmod +x /tmp/prepare.sh && /tmp/prepare.sh && rm -f /tmp/prepare.sh
|
||||
RUN chmod +x /tmp/prepare.sh && \
|
||||
/tmp/prepare.sh && \
|
||||
rm -f /tmp/prepare.sh
|
||||
|
||||
# fix CVE-2021-20205
|
||||
RUN apk add "libjpeg-turbo>=2.1.0-r0"
|
||||
|
||||
VOLUME /www /http-confs /server-confs /modsec-confs /modsec-crs-confs /cache /pre-server-confs /acme-challenge
|
||||
|
||||
EXPOSE 8080/tcp 8443/tcp
|
||||
|
||||
USER nginx
|
||||
USER nginx:nginx
|
||||
|
||||
ENTRYPOINT ["/opt/entrypoint/entrypoint.sh"]
|
||||
|
||||
@@ -11,6 +11,11 @@ class AutoConf :
|
||||
self.__sites = {}
|
||||
self.__config = Config(self.__swarm, api)
|
||||
|
||||
def get_server(self, id) :
|
||||
if id in self.__servers :
|
||||
return self.__servers[id]
|
||||
return False
|
||||
|
||||
def reload(self) :
|
||||
return self.__config.reload(self.__instances)
|
||||
|
||||
@@ -60,9 +65,9 @@ class AutoConf :
|
||||
self.__instances[id] = instance
|
||||
if self.__swarm and len(self.__instances) == 1 :
|
||||
if self.__config.initconf(self.__instances) :
|
||||
utils.log("[*] initial config succeeded")
|
||||
utils.log("[*] Initial config succeeded")
|
||||
else :
|
||||
utils.log("[!] initial config failed")
|
||||
utils.log("[!] Initial config failed")
|
||||
utils.log("[*] bunkerized-nginx instance created : " + name + " / " + id)
|
||||
elif event == "start" :
|
||||
self.__instances[id].reload()
|
||||
@@ -82,10 +87,12 @@ class AutoConf :
|
||||
def __process_server(self, instance, event, id, name, labels) :
|
||||
vars = { k.replace("bunkerized-nginx.", "", 1) : v for k, v in labels.items() if k.startswith("bunkerized-nginx.")}
|
||||
if event == "create" :
|
||||
utils.log("[*] Generating config for " + vars["SERVER_NAME"] + " ...")
|
||||
if self.__config.generate(self.__instances, vars) :
|
||||
utils.log("[*] Generated config for " + vars["SERVER_NAME"])
|
||||
self.__servers[id] = instance
|
||||
if self.__swarm :
|
||||
utils.log("[*] Activating config for " + vars["SERVER_NAME"] + " ...")
|
||||
if self.__config.activate(self.__instances, vars) :
|
||||
utils.log("[*] Activated config for " + vars["SERVER_NAME"])
|
||||
else :
|
||||
@@ -95,6 +102,7 @@ class AutoConf :
|
||||
elif event == "start" :
|
||||
if id in self.__servers :
|
||||
self.__servers[id].reload()
|
||||
utils.log("[*] Activating config for " + vars["SERVER_NAME"] + " ...")
|
||||
if self.__config.activate(self.__instances, vars) :
|
||||
utils.log("[*] Activated config for " + vars["SERVER_NAME"])
|
||||
else :
|
||||
@@ -102,6 +110,7 @@ class AutoConf :
|
||||
elif event == "die" :
|
||||
if id in self.__servers :
|
||||
self.__servers[id].reload()
|
||||
utils.log("[*] Deactivating config for " + vars["SERVER_NAME"])
|
||||
if self.__config.deactivate(self.__instances, vars) :
|
||||
utils.log("[*] Deactivated config for " + vars["SERVER_NAME"])
|
||||
else :
|
||||
@@ -109,11 +118,13 @@ class AutoConf :
|
||||
elif event == "destroy" or event == "remove" :
|
||||
if id in self.__servers :
|
||||
if self.__swarm :
|
||||
utils.log("[*] Deactivating config for " + vars["SERVER_NAME"])
|
||||
if self.__config.deactivate(self.__instances, vars) :
|
||||
utils.log("[*] Deactivated config for " + vars["SERVER_NAME"])
|
||||
else :
|
||||
utils.log("[!] Can't deactivate config for " + vars["SERVER_NAME"])
|
||||
del self.__servers[id]
|
||||
utils.log("[*] Removing config for " + vars["SERVER_NAME"])
|
||||
if self.__config.remove(vars) :
|
||||
utils.log("[*] Removed config for " + vars["SERVER_NAME"])
|
||||
else :
|
||||
|
||||
@@ -20,25 +20,42 @@ class Config :
|
||||
var = var_value.split("=")[0]
|
||||
value = var_value.replace(var + "=", "", 1)
|
||||
vars[var] = value
|
||||
if self.globalconf(instances) :
|
||||
i = 0
|
||||
started = False
|
||||
while i < 10 :
|
||||
if self.__ping(instances) :
|
||||
started = True
|
||||
break
|
||||
i = i + 1
|
||||
utils.log("[!] Waiting " + str(i) + " seconds before retrying to contact nginx instances")
|
||||
time.sleep(i)
|
||||
if started :
|
||||
proc = subprocess.run(["/bin/su", "-s", "/opt/entrypoint/jobs.sh", "nginx"], env=vars, capture_output=True)
|
||||
return proc.returncode == 0
|
||||
else :
|
||||
utils.log("[!] bunkerized-nginx instances are not started")
|
||||
|
||||
utils.log("[*] Generating global config ...")
|
||||
if not self.globalconf(instances) :
|
||||
utils.log("[!] Can't generate global config")
|
||||
return False
|
||||
utils.log("[*] Generated global config")
|
||||
|
||||
if "SERVER_NAME" in vars and vars["SERVER_NAME"] != "" :
|
||||
for server in vars["SERVER_NAME"].split(" ") :
|
||||
vars_site = vars.copy()
|
||||
vars_site["SERVER_NAME"] = server
|
||||
utils.log("[*] Generating config for " + vars["SERVER_NAME"] + " ...")
|
||||
if not self.generate(instances, vars_site) or not self.activate(instances, vars_site, reload=False) :
|
||||
utils.log("[!] Can't generate/activate site config for " + server)
|
||||
return False
|
||||
utils.log("[*] Generated config for " + vars["SERVER_NAME"])
|
||||
with open("/etc/nginx/autoconf", "w") as f :
|
||||
f.write("ok")
|
||||
|
||||
utils.log("[*] Waiting for bunkerized-nginx tasks ...")
|
||||
i = 1
|
||||
started = False
|
||||
while i <= 10 :
|
||||
time.sleep(i)
|
||||
if self.__ping(instances) :
|
||||
started = True
|
||||
break
|
||||
i = i + 1
|
||||
utils.log("[!] Waiting " + str(i) + " seconds before retrying to contact bunkerized-nginx tasks")
|
||||
if started :
|
||||
utils.log("[*] bunkerized-nginx tasks started")
|
||||
proc = subprocess.run(["/bin/su", "-s", "/opt/entrypoint/jobs.sh", "nginx"], env=vars, capture_output=True)
|
||||
return proc.returncode == 0
|
||||
else :
|
||||
utils.log("[!] Can't generate global conf")
|
||||
utils.log("[!] bunkerized-nginx tasks are not started")
|
||||
except Exception as e :
|
||||
traceback.print_exc()
|
||||
utils.log("[!] Error while initializing config : " + str(e))
|
||||
return False
|
||||
|
||||
@@ -54,12 +71,11 @@ class Config :
|
||||
vars[var] = value
|
||||
proc = subprocess.run(["/bin/su", "-s", "/opt/entrypoint/global-config.sh", "nginx"], env=vars, capture_output=True)
|
||||
if proc.returncode == 0 :
|
||||
with open("/etc/nginx/autoconf", "w") as f :
|
||||
f.write("ok")
|
||||
return True
|
||||
else :
|
||||
utils.log("[*] Error while generating global config : return code = " + str(proc.returncode))
|
||||
except Exception as e :
|
||||
traceback.print_exc()
|
||||
utils.log("[!] Error while generating global config : " + str(e))
|
||||
utils.log("[!] Exception while generating global config : " + str(e))
|
||||
return False
|
||||
|
||||
def generate(self, instances, vars) :
|
||||
@@ -79,61 +95,76 @@ class Config :
|
||||
vars_defaults.update(vars_instances)
|
||||
vars_defaults.update(vars)
|
||||
# Call site-config.sh to generate the config
|
||||
proc = subprocess.run(["/bin/su", "-s", "/bin/sh", "-c", "/opt/entrypoint/site-config.sh" + " " + vars["SERVER_NAME"], "nginx"], env=vars_defaults, capture_output=True)
|
||||
proc = subprocess.run(["/bin/su", "-s", "/bin/sh", "-c", "/opt/entrypoint/site-config.sh" + " \"" + vars["SERVER_NAME"] + "\"", "nginx"], env=vars_defaults, capture_output=True)
|
||||
if proc.returncode == 0 and vars_defaults["MULTISITE"] == "yes" and self.__swarm :
|
||||
proc = subprocess.run(["/bin/su", "-s", "/opt/entrypoint/multisite-config.sh", "nginx"], env=vars_defaults, capture_output=True)
|
||||
return proc.returncode == 0
|
||||
return proc.returncode == 0
|
||||
if proc.returncode == 0 :
|
||||
return True
|
||||
utils.log("[!] Error while generating site config for " + vars["SERVER_NAME"] + " : return code = " + str(proc.returncode))
|
||||
except Exception as e :
|
||||
traceback.print_exc()
|
||||
utils.log("[!] Error while generating site config : " + str(e))
|
||||
utils.log("[!] Exception while generating site config : " + str(e))
|
||||
return False
|
||||
|
||||
def activate(self, instances, vars) :
|
||||
def activate(self, instances, vars, reload=True) :
|
||||
try :
|
||||
# Get first server name
|
||||
first_server_name = vars["SERVER_NAME"].split(" ")[0]
|
||||
|
||||
# Check if file exists
|
||||
if not os.path.isfile("/etc/nginx/" + vars["SERVER_NAME"] + "/server.conf") :
|
||||
utils.log("[!] /etc/nginx/" + vars["SERVER_NAME"] + "/server.conf doesn't exist")
|
||||
if not os.path.isfile("/etc/nginx/" + first_server_name + "/server.conf") :
|
||||
utils.log("[!] /etc/nginx/" + first_server_name + "/server.conf doesn't exist")
|
||||
return False
|
||||
|
||||
# Include the server conf
|
||||
utils.replace_in_file("/etc/nginx/nginx.conf", "}", "include /etc/nginx/" + vars["SERVER_NAME"] + "/server.conf;\n}")
|
||||
utils.replace_in_file("/etc/nginx/nginx.conf", "}", "include /etc/nginx/" + first_server_name + "/server.conf;\n}")
|
||||
|
||||
# Reload
|
||||
if not reload or self.reload(instances) :
|
||||
return True
|
||||
|
||||
return self.reload(instances)
|
||||
except Exception as e :
|
||||
traceback.print_exc()
|
||||
utils.log("[!] Error while activating config : " + str(e))
|
||||
utils.log("[!] Exception while activating config : " + str(e))
|
||||
|
||||
return False
|
||||
|
||||
def deactivate(self, instances, vars) :
|
||||
try :
|
||||
# Get first server name
|
||||
first_server_name = vars["SERVER_NAME"].split(" ")[0]
|
||||
|
||||
# Check if file exists
|
||||
if not os.path.isfile("/etc/nginx/" + vars["SERVER_NAME"] + "/server.conf") :
|
||||
utils.log("[!] /etc/nginx/" + vars["SERVER_NAME"] + "/server.conf doesn't exist")
|
||||
if not os.path.isfile("/etc/nginx/" + first_server_name + "/server.conf") :
|
||||
utils.log("[!] /etc/nginx/" + first_server_name + "/server.conf doesn't exist")
|
||||
return False
|
||||
|
||||
# Remove the include
|
||||
utils.replace_in_file("/etc/nginx/nginx.conf", "include /etc/nginx/" + vars["SERVER_NAME"] + "/server.conf;\n", "")
|
||||
utils.replace_in_file("/etc/nginx/nginx.conf", "include /etc/nginx/" + first_server_name + "/server.conf;\n", "")
|
||||
|
||||
return self.reload(instances)
|
||||
# Reload
|
||||
if self.reload(instances) :
|
||||
return True
|
||||
|
||||
except Exception as e :
|
||||
traceback.print_exc()
|
||||
utils.log("[!] Error while deactivating config : " + str(e))
|
||||
utils.log("[!] Exception while deactivating config : " + str(e))
|
||||
|
||||
return False
|
||||
|
||||
def remove(self, instances, vars) :
|
||||
def remove(self, vars) :
|
||||
try :
|
||||
# Get first server name
|
||||
first_server_name = vars["SERVER_NAME"].split(" ")[0]
|
||||
|
||||
# Check if file exists
|
||||
if not os.path.isfile("/etc/nginx/" + vars["SERVER_NAME"] + "/server.conf") :
|
||||
utils.log("[!] /etc/nginx/" + vars["SERVER_NAME"] + "/server.conf doesn't exist")
|
||||
if not os.path.isfile("/etc/nginx/" + first_server_name + "/server.conf") :
|
||||
utils.log("[!] /etc/nginx/" + first_server_name + "/server.conf doesn't exist")
|
||||
return False
|
||||
|
||||
# Remove the folder
|
||||
shutil.rmtree("/etc/nginx/" + vars["SERVER_NAME"])
|
||||
shutil.rmtree("/etc/nginx/" + first_server_name)
|
||||
return True
|
||||
except Exception as e :
|
||||
utils.log("[!] Error while deactivating config : " + str(e))
|
||||
|
||||
return False
|
||||
|
||||
def reload(self, instances) :
|
||||
@@ -149,7 +180,7 @@ class Config :
|
||||
instance.reload()
|
||||
# Reload via API
|
||||
if self.__swarm :
|
||||
# Send POST request on http://serviceName.NodeID.TaskID:8000/reload
|
||||
# Send POST request on http://serviceName.NodeID.TaskID:8000/action
|
||||
name = instance.name
|
||||
for task in instance.tasks() :
|
||||
if task["Status"]["State"] != "running" :
|
||||
|
||||
@@ -12,6 +12,9 @@ RUN apk add py3-pip apache2-utils bash certbot curl logrotate openssl && \
|
||||
mkdir /opt/scripts && \
|
||||
addgroup -g 101 nginx && \
|
||||
adduser -h /var/cache/nginx -g nginx -s /sbin/nologin -G nginx -D -H -u 101 nginx && \
|
||||
mkdir /etc/letsencrypt && \
|
||||
chown root:nginx /etc/letsencrypt && \
|
||||
chmod 770 /etc/letsencrypt && \
|
||||
mkdir /var/log/letsencrypt && \
|
||||
chown root:nginx /var/log/letsencrypt && \
|
||||
chmod 770 /var/log/letsencrypt && \
|
||||
@@ -25,7 +28,11 @@ RUN apk add py3-pip apache2-utils bash certbot curl logrotate openssl && \
|
||||
chown root:nginx /var/log/jobs.log && \
|
||||
chmod 770 /var/log/jobs.log && \
|
||||
chown -R root:nginx /opt/confs/nginx && \
|
||||
chmod -R 770 /opt/confs/nginx
|
||||
chmod -R 770 /opt/confs/nginx && \
|
||||
mkdir /acme-challenge && \
|
||||
chown root:nginx /acme-challenge && \
|
||||
chmod 770 /acme-challenge
|
||||
|
||||
|
||||
COPY autoconf/misc/logrotate.conf /etc/logrotate.conf
|
||||
COPY scripts/* /opt/scripts/
|
||||
|
||||
@@ -7,10 +7,14 @@ COPY --from=builder /etc/nginx/ /opt/confs/nginx
|
||||
RUN apk add py3-pip apache2-utils bash certbot curl logrotate openssl && \
|
||||
pip3 install docker requests && \
|
||||
mkdir /opt/entrypoint && \
|
||||
mkdir -p /opt/confs/site && \
|
||||
mkdir -p /opt/confs/site && \
|
||||
mkdir -p /opt/confs/global && \
|
||||
mkdir /opt/scripts && \
|
||||
addgroup -g 101 nginx && \
|
||||
adduser -h /var/cache/nginx -g nginx -s /sbin/nologin -G nginx -D -H -u 101 nginx && \
|
||||
mkdir /etc/letsencrypt && \
|
||||
chown root:nginx /etc/letsencrypt && \
|
||||
chmod 770 /etc/letsencrypt && \
|
||||
mkdir /var/log/letsencrypt && \
|
||||
chown root:nginx /var/log/letsencrypt && \
|
||||
chmod 770 /var/log/letsencrypt && \
|
||||
@@ -24,7 +28,10 @@ RUN apk add py3-pip apache2-utils bash certbot curl logrotate openssl && \
|
||||
chown root:nginx /var/log/jobs.log && \
|
||||
chmod 770 /var/log/jobs.log && \
|
||||
chown -R root:nginx /opt/confs/nginx && \
|
||||
chmod -R 770 /opt/confs/nginx
|
||||
chmod -R 770 /opt/confs/nginx && \
|
||||
mkdir /acme-challenge && \
|
||||
chown root:nginx /acme-challenge && \
|
||||
chmod 770 /acme-challenge
|
||||
|
||||
COPY autoconf/misc/logrotate.conf /etc/logrotate.conf
|
||||
COPY scripts/* /opt/scripts/
|
||||
|
||||
@@ -15,8 +15,12 @@ RUN apk add py3-pip apache2-utils bash certbot curl logrotate openssl && \
|
||||
mkdir /opt/entrypoint && \
|
||||
mkdir -p /opt/confs/site && \
|
||||
mkdir -p /opt/confs/global && \
|
||||
mkdir /opt/scripts && \
|
||||
addgroup -g 101 nginx && \
|
||||
adduser -h /var/cache/nginx -g nginx -s /sbin/nologin -G nginx -D -H -u 101 nginx && \
|
||||
mkdir /etc/letsencrypt && \
|
||||
chown root:nginx /etc/letsencrypt && \
|
||||
chmod 770 /etc/letsencrypt && \
|
||||
mkdir /var/log/letsencrypt && \
|
||||
chown root:nginx /var/log/letsencrypt && \
|
||||
chmod 770 /var/log/letsencrypt && \
|
||||
@@ -30,7 +34,10 @@ RUN apk add py3-pip apache2-utils bash certbot curl logrotate openssl && \
|
||||
chown root:nginx /var/log/jobs.log && \
|
||||
chmod 770 /var/log/jobs.log && \
|
||||
chown -R root:nginx /opt/confs/nginx && \
|
||||
chmod -R 770 /opt/confs/nginx
|
||||
chmod -R 770 /opt/confs/nginx && \
|
||||
mkdir /acme-challenge && \
|
||||
chown root:nginx /acme-challenge && \
|
||||
chmod 770 /acme-challenge
|
||||
|
||||
COPY autoconf/misc/logrotate.conf /etc/logrotate.conf
|
||||
COPY scripts/* /opt/scripts/
|
||||
|
||||
@@ -15,8 +15,12 @@ RUN apk add py3-pip apache2-utils bash certbot curl logrotate openssl && \
|
||||
mkdir /opt/entrypoint && \
|
||||
mkdir -p /opt/confs/site && \
|
||||
mkdir -p /opt/confs/global && \
|
||||
mkdir /opt/scripts && \
|
||||
addgroup -g 101 nginx && \
|
||||
adduser -h /var/cache/nginx -g nginx -s /sbin/nologin -G nginx -D -H -u 101 nginx && \
|
||||
mkdir /etc/letsencrypt && \
|
||||
chown root:nginx /etc/letsencrypt && \
|
||||
chmod 770 /etc/letsencrypt && \
|
||||
mkdir /var/log/letsencrypt && \
|
||||
chown root:nginx /var/log/letsencrypt && \
|
||||
chmod 770 /var/log/letsencrypt && \
|
||||
@@ -30,7 +34,10 @@ RUN apk add py3-pip apache2-utils bash certbot curl logrotate openssl && \
|
||||
chown root:nginx /var/log/jobs.log && \
|
||||
chmod 770 /var/log/jobs.log && \
|
||||
chown -R root:nginx /opt/confs/nginx && \
|
||||
chmod -R 770 /opt/confs/nginx
|
||||
chmod -R 770 /opt/confs/nginx && \
|
||||
mkdir /acme-challenge && \
|
||||
chown root:nginx /acme-challenge && \
|
||||
chmod 770 /acme-challenge
|
||||
|
||||
COPY autoconf/misc/logrotate.conf /etc/logrotate.conf
|
||||
COPY scripts/* /opt/scripts/
|
||||
|
||||
@@ -9,8 +9,12 @@ RUN apk add py3-pip apache2-utils bash certbot curl logrotate openssl && \
|
||||
mkdir /opt/entrypoint && \
|
||||
mkdir -p /opt/confs/site && \
|
||||
mkdir -p /opt/confs/global && \
|
||||
mkdir /opt/scripts && \
|
||||
addgroup -g 101 nginx && \
|
||||
adduser -h /var/cache/nginx -g nginx -s /sbin/nologin -G nginx -D -H -u 101 nginx && \
|
||||
mkdir /etc/letsencrypt && \
|
||||
chown root:nginx /etc/letsencrypt && \
|
||||
chmod 770 /etc/letsencrypt && \
|
||||
mkdir /var/log/letsencrypt && \
|
||||
chown root:nginx /var/log/letsencrypt && \
|
||||
chmod 770 /var/log/letsencrypt && \
|
||||
@@ -24,7 +28,10 @@ RUN apk add py3-pip apache2-utils bash certbot curl logrotate openssl && \
|
||||
chown root:nginx /var/log/jobs.log && \
|
||||
chmod 770 /var/log/jobs.log && \
|
||||
chown -R root:nginx /opt/confs/nginx && \
|
||||
chmod -R 770 /opt/confs/nginx
|
||||
chmod -R 770 /opt/confs/nginx && \
|
||||
mkdir /acme-challenge && \
|
||||
chown root:nginx /acme-challenge && \
|
||||
chmod 770 /acme-challenge
|
||||
|
||||
COPY autoconf/misc/logrotate.conf /etc/logrotate.conf
|
||||
COPY scripts/* /opt/scripts/
|
||||
|
||||
@@ -44,6 +44,7 @@ with lock :
|
||||
|
||||
# Process events received from Docker
|
||||
try :
|
||||
utils.log("[*] Listening for Docker events ...")
|
||||
for event in client.events(decode=True) :
|
||||
|
||||
# Process only container/service events
|
||||
@@ -53,11 +54,15 @@ try :
|
||||
# Get Container/Service object
|
||||
try :
|
||||
if swarm :
|
||||
server = client.services.get(service_id=event["Actor"]["ID"])
|
||||
id = service_id=event["Actor"]["ID"]
|
||||
server = client.services.get(service_id=id)
|
||||
else :
|
||||
server = client.containers.get(event["id"])
|
||||
id = event["id"]
|
||||
server = client.containers.get(id)
|
||||
except docker.errors.NotFound as e :
|
||||
continue
|
||||
server = autoconf.get_server(id)
|
||||
if not server :
|
||||
continue
|
||||
|
||||
# Process the event
|
||||
with lock :
|
||||
|
||||
@@ -2,6 +2,12 @@
|
||||
|
||||
echo "[*] Starting autoconf ..."
|
||||
|
||||
# check permissions
|
||||
su -s "/opt/entrypoint/permissions.sh" nginx
|
||||
if [ "$?" -ne 0 ] ; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$SWARM_MODE" = "yes" ] ; then
|
||||
cp -r /opt/confs/nginx/* /etc/nginx
|
||||
chown -R root:nginx /etc/nginx
|
||||
|
||||
@@ -136,6 +136,10 @@ sed -i 's/^API_KEY=.*/API_KEY=%CROWDSEC_KEY%/' /usr/local/lib/lua/crowdsec/crowd
|
||||
sed -i 's/require "lrucache"/require "resty.lrucache"/' /usr/local/lib/lua/crowdsec/CrowdSec.lua
|
||||
sed -i 's/require "config"/require "crowdsec.config"/' /usr/local/lib/lua/crowdsec/CrowdSec.lua
|
||||
cd /tmp
|
||||
git_secure_clone https://github.com/hamishforbes/lua-resty-iputils.git 3151d6485e830421266eee5c0f386c32c835dba4
|
||||
cd lua-resty-iputils
|
||||
make LUA_LIB_DIR=/usr/local/lib/lua install
|
||||
cd /tmp
|
||||
git_secure_clone https://github.com/openresty/lua-nginx-module.git 2d23bc4f0a29ed79aaaa754c11bffb1080aa44ba
|
||||
export LUAJIT_LIB=/usr/local/lib
|
||||
export LUAJIT_INC=/usr/local/include/luajit-2.1
|
||||
@@ -153,7 +157,7 @@ fi
|
||||
tar -xvzf nginx-${NGINX_VERSION}.tar.gz
|
||||
cd nginx-$NGINX_VERSION
|
||||
CONFARGS=$(nginx -V 2>&1 | sed -n -e 's/^.*arguments: //p')
|
||||
CONFARGS=${CONFARGS/-Os -fomit-frame-pointer/-Os}
|
||||
CONFARGS=${CONFARGS/-Os -fomit-frame-pointer -g/-Os}
|
||||
./configure $CONFARGS --add-dynamic-module=/tmp/ModSecurity-nginx --add-dynamic-module=/tmp/headers-more-nginx-module --add-dynamic-module=/tmp/ngx_http_geoip2_module --add-dynamic-module=/tmp/nginx_cookie_flag_module --add-dynamic-module=/tmp/lua-nginx-module --add-dynamic-module=/tmp/ngx_brotli
|
||||
make -j $NTASK modules
|
||||
cp ./objs/*.so /usr/lib/nginx/modules
|
||||
|
||||
@@ -13,7 +13,7 @@ rewrite_by_lua_block {
|
||||
if api.is_api_call(api_uri) then
|
||||
ngx.header.content_type = 'text/plain'
|
||||
if api.do_api_call(api_uri) then
|
||||
ngx.log(ngx.WARN, "[API] API call " .. ngx.var.request_uri .. " successfull from " .. ngx.var.remote_addr)
|
||||
ngx.log(ngx.NOTICE, "[API] API call " .. ngx.var.request_uri .. " successfull from " .. ngx.var.remote_addr)
|
||||
ngx.say("ok")
|
||||
else
|
||||
ngx.log(ngx.WARN, "[API] API call " .. ngx.var.request_uri .. " failed from " .. ngx.var.remote_addr)
|
||||
|
||||
@@ -6,7 +6,7 @@ rewrite_by_lua_block {
|
||||
if api.is_api_call(api_uri) then
|
||||
ngx.header.content_type = 'text/plain'
|
||||
if api.do_api_call(api_uri) then
|
||||
ngx.log(ngx.WARN, "[API] API call " .. ngx.var.request_uri .. " successfull from " .. ngx.var.remote_addr)
|
||||
ngx.log(ngx.NOTICE, "[API] API call " .. ngx.var.request_uri .. " successfull from " .. ngx.var.remote_addr)
|
||||
ngx.say("ok")
|
||||
else
|
||||
ngx.log(ngx.WARN, "[API] API call " .. ngx.var.request_uri .. " failed from " .. ngx.var.remote_addr)
|
||||
|
||||
@@ -5,5 +5,5 @@ init_by_lua_block {
|
||||
ngx.log(ngx.ERR, "[Crowdsec] " .. err)
|
||||
error()
|
||||
end
|
||||
ngx.log(ngx.ERR, "[Crowdsec] Initialisation done")
|
||||
ngx.log(ngx.NOTICE, "[Crowdsec] Initialisation done")
|
||||
}
|
||||
|
||||
31
confs/global/init-lua.conf
Normal file
31
confs/global/init-lua.conf
Normal file
@@ -0,0 +1,31 @@
|
||||
init_by_lua_block {
|
||||
|
||||
local dataloader = require "dataloader"
|
||||
|
||||
local use_proxies = %USE_PROXIES%
|
||||
local use_abusers = %USE_ABUSERS%
|
||||
local use_tor_exit_nodes = %USE_TOR_EXIT_NODES%
|
||||
local use_user_agents = %USE_USER_AGENTS%
|
||||
local use_referrers = %USE_REFERRERS%
|
||||
|
||||
if use_proxies then
|
||||
dataloader.load_ip("/etc/nginx/proxies.list", ngx.shared.proxies_data)
|
||||
end
|
||||
|
||||
if use_abusers then
|
||||
dataloader.load_ip("/etc/nginx/abusers.list", ngx.shared.abusers_data)
|
||||
end
|
||||
|
||||
if use_tor_exit_nodes then
|
||||
dataloader.load_ip("/etc/nginx/tor-exit-nodes.list", ngx.shared.tor_exit_nodes_data)
|
||||
end
|
||||
|
||||
if use_user_agents then
|
||||
dataloader.load_raw("/etc/nginx/user-agents.list", ngx.shared.user_agents_data)
|
||||
end
|
||||
|
||||
if use_referrers then
|
||||
dataloader.load_raw("/etc/nginx/referrers.list", ngx.shared.referrers_data)
|
||||
end
|
||||
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
map $http_referer $bad_referrer { hostnames; default no; }
|
||||
@@ -1 +0,0 @@
|
||||
map $http_user_agent $bad_user_agent { default no; }
|
||||
@@ -10,8 +10,8 @@ load_module /usr/lib/nginx/modules/ngx_stream_geoip2_module.so;
|
||||
load_module /usr/lib/nginx/modules/ngx_http_brotli_filter_module.so;
|
||||
load_module /usr/lib/nginx/modules/ngx_http_brotli_static_module.so;
|
||||
|
||||
# run as daemon
|
||||
daemon on;
|
||||
# run in foreground
|
||||
daemon off;
|
||||
|
||||
# PID file
|
||||
pid /tmp/nginx.pid;
|
||||
@@ -25,9 +25,12 @@ pcre_jit on;
|
||||
# config files for dynamic modules
|
||||
include /etc/nginx/modules/*.conf;
|
||||
|
||||
# max open files for each worker
|
||||
worker_rlimit_nofile %WORKER_RLIMIT_NOFILE%;
|
||||
|
||||
events {
|
||||
# max connections per worker
|
||||
worker_connections 1024;
|
||||
worker_connections %WORKER_CONNECTIONS%;
|
||||
|
||||
# epoll seems to be the best on Linux
|
||||
use epoll;
|
||||
@@ -49,8 +52,8 @@ http {
|
||||
|
||||
# write logs to local syslog
|
||||
log_format logf '%LOG_FORMAT%';
|
||||
access_log syslog:server=unix:/tmp/log,nohostname,facility=local0,severity=notice logf;
|
||||
error_log syslog:server=unix:/tmp/log,nohostname,facility=local0 warn;
|
||||
access_log /var/log/access.log logf;
|
||||
error_log /var/log/error.log info;
|
||||
|
||||
# temp paths
|
||||
proxy_temp_path /tmp/proxy_temp;
|
||||
@@ -63,8 +66,8 @@ http {
|
||||
reset_timedout_connection on;
|
||||
|
||||
# timeouts
|
||||
client_body_timeout 12;
|
||||
client_header_timeout 12;
|
||||
client_body_timeout 10;
|
||||
client_header_timeout 10;
|
||||
keepalive_timeout 15;
|
||||
send_timeout 10;
|
||||
|
||||
@@ -81,6 +84,12 @@ http {
|
||||
%BLACKLIST_IP_CACHE%
|
||||
%BLACKLIST_REVERSE_CACHE%
|
||||
%DNSBL_CACHE%
|
||||
%BLOCK_PROXIES%
|
||||
%BLOCK_ABUSERS%
|
||||
%BLOCK_TOR_EXIT_NODES%
|
||||
%BLOCK_USER_AGENTS%
|
||||
%BLOCK_REFERRERS%
|
||||
%BAD_BEHAVIOR%
|
||||
|
||||
# crowdsec init
|
||||
%USE_CROWDSEC%
|
||||
@@ -94,18 +103,15 @@ http {
|
||||
# whitelist or blacklist country
|
||||
%USE_COUNTRY%
|
||||
|
||||
# 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;
|
||||
|
||||
# LUA init block
|
||||
include /etc/nginx/init-lua.conf;
|
||||
|
||||
# default server when MULTISITE=yes
|
||||
%MULTISITE_DEFAULT_SERVER%
|
||||
|
||||
|
||||
0
confs/global/tor-exit-nodes.list
Normal file
0
confs/global/tor-exit-nodes.list
Normal file
0
confs/global/user-agents.list
Normal file
0
confs/global/user-agents.list
Normal file
@@ -7,7 +7,7 @@ location = %ANTIBOT_URI% {
|
||||
local cookie = require "cookie"
|
||||
local captcha = require "captcha"
|
||||
if not cookie.is_set("uri") then
|
||||
ngx.log(ngx.WARN, "[ANTIBOT] captcha fail (1) for " .. ngx.var.remote_addr)
|
||||
ngx.log(ngx.NOTICE, "[ANTIBOT] captcha fail (1) for " .. ngx.var.remote_addr)
|
||||
return ngx.exit(ngx.HTTP_FORBIDDEN)
|
||||
end
|
||||
local img, res = captcha.get_challenge()
|
||||
@@ -22,19 +22,19 @@ location = %ANTIBOT_URI% {
|
||||
local cookie = require "cookie"
|
||||
local captcha = require "captcha"
|
||||
if not cookie.is_set("captchares") then
|
||||
ngx.log(ngx.WARN, "[ANTIBOT] captcha fail (2) for " .. ngx.var.remote_addr)
|
||||
ngx.log(ngx.NOTICE, "[ANTIBOT] captcha fail (2) for " .. ngx.var.remote_addr)
|
||||
return ngx.exit(ngx.HTTP_FORBIDDEN)
|
||||
end
|
||||
ngx.req.read_body()
|
||||
local args, err = ngx.req.get_post_args(1)
|
||||
if err == "truncated" or not args or not args["captcha"] then
|
||||
ngx.log(ngx.WARN, "[ANTIBOT] captcha fail (3) for " .. ngx.var.remote_addr)
|
||||
ngx.log(ngx.NOTICE, "[ANTIBOT] captcha fail (3) for " .. ngx.var.remote_addr)
|
||||
return ngx.exit(ngx.HTTP_FORBIDDEN)
|
||||
end
|
||||
local captcha_user = args["captcha"]
|
||||
local check = captcha.check(captcha_user, cookie.get("captchares"))
|
||||
if not check then
|
||||
ngx.log(ngx.WARN, "[ANTIBOT] captcha fail (4) for " .. ngx.var.remote_addr)
|
||||
ngx.log(ngx.NOTICE, "[ANTIBOT] captcha fail (4) for " .. ngx.var.remote_addr)
|
||||
return ngx.redirect("%ANTIBOT_URI%")
|
||||
end
|
||||
cookie.set({captcha = "ok"})
|
||||
|
||||
@@ -7,7 +7,7 @@ location = %ANTIBOT_URI% {
|
||||
local cookie = require "cookie"
|
||||
local recaptcha = require "recaptcha"
|
||||
if not cookie.is_set("uri") then
|
||||
ngx.log(ngx.WARN, "[ANTIBOT] recaptcha fail (1) for " .. ngx.var.remote_addr)
|
||||
ngx.log(ngx.NOTICE, "[ANTIBOT] recaptcha fail (1) for " .. ngx.var.remote_addr)
|
||||
return ngx.exit(ngx.HTTP_FORBIDDEN)
|
||||
end
|
||||
local code = recaptcha.get_code("%ANTIBOT_URI%", "%ANTIBOT_RECAPTCHA_SITEKEY%")
|
||||
@@ -20,19 +20,19 @@ location = %ANTIBOT_URI% {
|
||||
local cookie = require "cookie"
|
||||
local recaptcha = require "recaptcha"
|
||||
if not cookie.is_set("uri") then
|
||||
ngx.log(ngx.WARN, "[ANTIBOT] recaptcha fail (2) for " .. ngx.var.remote_addr)
|
||||
ngx.log(ngx.NOTICE, "[ANTIBOT] recaptcha fail (2) for " .. ngx.var.remote_addr)
|
||||
return ngx.exit(ngx.HTTP_FORBIDDEN)
|
||||
end
|
||||
ngx.req.read_body()
|
||||
local args, err = ngx.req.get_post_args(1)
|
||||
if err == "truncated" or not args or not args["token"] then
|
||||
ngx.log(ngx.WARN, "[ANTIBOT] recaptcha fail (3) for " .. ngx.var.remote_addr)
|
||||
ngx.log(ngx.NOTICE, "[ANTIBOT] recaptcha fail (3) for " .. ngx.var.remote_addr)
|
||||
return ngx.exit(ngx.HTTP_FORBIDDEN)
|
||||
end
|
||||
local token = args["token"]
|
||||
local check = recaptcha.check(token, "%ANTIBOT_RECAPTCHA_SECRET%")
|
||||
if check < %ANTIBOT_RECAPTCHA_SCORE% then
|
||||
ngx.log(ngx.WARN, "[ANTIBOT] recaptcha fail (4) for " .. ngx.var.remote_addr .. " (score = " .. tostring(check) .. ")")
|
||||
ngx.log(ngx.NOTICE, "[ANTIBOT] recaptcha fail (4) for " .. ngx.var.remote_addr .. " (score = " .. tostring(check) .. ")")
|
||||
return ngx.exit(ngx.HTTP_FORBIDDEN)
|
||||
end
|
||||
cookie.set({recaptcha = "ok"})
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
location ~* \.(%CLIENT_CACHE_EXTENSIONS%)$ {
|
||||
etag %CLIENT_CACHE_ETAG%;
|
||||
add_header Cache-Control "%CLIENT_CACHE_CONTROL%";
|
||||
etag %CLIENT_CACHE_ETAG%;
|
||||
set $cache "";
|
||||
if ($uri ~* \.(%CLIENT_CACHE_EXTENSIONS%)$) {
|
||||
set $cache "%CLIENT_CACHE_CONTROL%";
|
||||
}
|
||||
add_header Cache-Control $cache;
|
||||
|
||||
11
confs/site/log-lua.conf
Normal file
11
confs/site/log-lua.conf
Normal file
@@ -0,0 +1,11 @@
|
||||
log_by_lua_block {
|
||||
|
||||
local use_bad_behavior = %USE_BAD_BEHAVIOR%
|
||||
|
||||
local behavior = require "behavior"
|
||||
|
||||
if use_bad_behavior then
|
||||
behavior.count()
|
||||
end
|
||||
|
||||
}
|
||||
@@ -6,8 +6,11 @@ access_by_lua_block {
|
||||
local use_lets_encrypt = %USE_LETS_ENCRYPT%
|
||||
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_user_agents = %USE_USER_AGENTS%
|
||||
local use_proxies = %USE_PROXIES%
|
||||
local use_abusers = %USE_ABUSERS%
|
||||
local use_tor_exit_nodes = %USE_TOR_EXIT_NODES%
|
||||
local use_referrers = %USE_REFERRERS%
|
||||
local use_country = %USE_COUNTRY%
|
||||
local use_blacklist_ip = %USE_BLACKLIST_IP%
|
||||
local use_blacklist_reverse = %USE_BLACKLIST_REVERSE%
|
||||
@@ -17,9 +20,9 @@ local use_antibot_cookie = %USE_ANTIBOT_COOKIE%
|
||||
local use_antibot_javascript = %USE_ANTIBOT_JAVASCRIPT%
|
||||
local use_antibot_captcha = %USE_ANTIBOT_CAPTCHA%
|
||||
local use_antibot_recaptcha = %USE_ANTIBOT_RECAPTCHA%
|
||||
local use_bad_behavior = %USE_BAD_BEHAVIOR%
|
||||
|
||||
-- include LUA code
|
||||
|
||||
local whitelist = require "whitelist"
|
||||
local blacklist = require "blacklist"
|
||||
local dnsbl = require "dnsbl"
|
||||
@@ -27,15 +30,13 @@ local cookie = require "cookie"
|
||||
local javascript = require "javascript"
|
||||
local captcha = require "captcha"
|
||||
local recaptcha = require "recaptcha"
|
||||
local iputils = require "resty.iputils"
|
||||
local behavior = require "behavior"
|
||||
|
||||
-- user variables
|
||||
local antibot_uri = "%ANTIBOT_URI%"
|
||||
local whitelist_user_agent = {%WHITELIST_USER_AGENT%}
|
||||
|
||||
-- check if it's let's encrypt bot
|
||||
if use_lets_encrypt and string.match(ngx.var.request_uri, "^/.well-known/acme-challenge/") then
|
||||
ngx.exit(ngx.OK)
|
||||
end
|
||||
local whitelist_uri = {%WHITELIST_URI%}
|
||||
|
||||
-- check if already in whitelist cache
|
||||
if use_whitelist_ip and whitelist.ip_cached_ok() then
|
||||
@@ -72,6 +73,19 @@ if use_whitelist_reverse and not whitelist.reverse_cached() then
|
||||
end
|
||||
end
|
||||
|
||||
-- check if URI is whitelisted
|
||||
for k, v in pairs(whitelist_uri) do
|
||||
if ngx.var.request_uri == v then
|
||||
ngx.log(ngx.NOTICE, "[WHITELIST] URI " .. v .. " is whitelisted")
|
||||
ngx.exit(ngx.OK)
|
||||
end
|
||||
end
|
||||
|
||||
-- check if it's certbot
|
||||
if use_lets_encrypt and string.match(ngx.var.request_uri, "^/.well-known/acme-challenge/") then
|
||||
ngx.exit(ngx.OK)
|
||||
end
|
||||
|
||||
-- check if IP is blacklisted (only if not in cache)
|
||||
if use_blacklist_ip and not blacklist.ip_cached() then
|
||||
if blacklist.check_ip() then
|
||||
@@ -86,34 +100,98 @@ 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
|
||||
local headers = ngx.req.get_headers()
|
||||
local ua = headers["User-Agent"]
|
||||
if not whitelist_user_agent ~= "" then
|
||||
local k_ua_white, v_ua_white = next(whitelist_user_agent, nil)
|
||||
while v_ua_white do
|
||||
local rst_whitelist = string.match(ua, v_ua_white)
|
||||
if rst_whitelist ~= nil and rst_whitelist ~= "" then
|
||||
ngx.log(ngx.WARN, "[ALLOW] User-Agent " .. ngx.var.http_user_agent .. " is whitelisted")
|
||||
ngx.exit(ngx.OK)
|
||||
end
|
||||
k_ua_white, v_ua_white = next(whitelist_user_agent, k_ua_white)
|
||||
end
|
||||
end
|
||||
ngx.log(ngx.WARN, "[BLOCK] User-Agent " .. ngx.var.http_user_agent .. " is blacklisted")
|
||||
-- check if IP is banned because of "bad behavior"
|
||||
if use_bad_behavior and behavior.is_banned() then
|
||||
ngx.log(ngx.NOTICE, "[BLOCK] IP " .. ngx.var.remote_addr .. " is banned because of bad behavior")
|
||||
ngx.exit(ngx.HTTP_FORBIDDEN)
|
||||
end
|
||||
|
||||
-- check if IP is in proxies list
|
||||
if use_proxies then
|
||||
local value, flags = ngx.shared.proxies_data:get(iputils.ip2bin(ngx.var.remote_addr))
|
||||
if value ~= nil then
|
||||
ngx.log(ngx.NOTICE, "[BLOCK] IP " .. ngx.var.remote_addr .. " is in proxies list")
|
||||
ngx.exit(ngx.HTTP_FORBIDDEN)
|
||||
end
|
||||
end
|
||||
|
||||
-- check if IP is in abusers list
|
||||
if use_abusers then
|
||||
local value, flags = ngx.shared.abusers_data:get(iputils.ip2bin(ngx.var.remote_addr))
|
||||
if value ~= nil then
|
||||
ngx.log(ngx.NOTICE, "[BLOCK] IP " .. ngx.var.remote_addr .. " is in abusers list")
|
||||
ngx.exit(ngx.HTTP_FORBIDDEN)
|
||||
end
|
||||
end
|
||||
|
||||
-- check if IP is in TOR exit nodes list
|
||||
if use_tor_exit_nodes then
|
||||
local value, flags = ngx.shared.tor_exit_nodes_data:get(iputils.ip2bin(ngx.var.remote_addr))
|
||||
if value ~= nil then
|
||||
ngx.log(ngx.NOTICE, "[BLOCK] IP " .. ngx.var.remote_addr .. " is in TOR exit nodes list")
|
||||
ngx.exit(ngx.HTTP_FORBIDDEN)
|
||||
end
|
||||
end
|
||||
|
||||
-- check if user-agent is allowed
|
||||
if use_user_agents and ngx.var.http_user_agent ~= nil then
|
||||
local whitelisted = false
|
||||
for k, v in pairs(whitelist_user_agent) do
|
||||
if string.match(ngx.var.http_user_agent, v) then
|
||||
ngx.log(ngx.NOTICE, "[ALLOW] User-Agent " .. ngx.var.http_user_agent .. " is whitelisted")
|
||||
whitelisted = true
|
||||
break
|
||||
end
|
||||
end
|
||||
if not whitelisted then
|
||||
local value, flags = ngx.shared.user_agents_cache:get(ngx.var.http_user_agent)
|
||||
if value == nil then
|
||||
local patterns = ngx.shared.user_agents_data:get_keys(0)
|
||||
for i, pattern in ipairs(patterns) do
|
||||
if string.match(ngx.var.http_user_agent, pattern) then
|
||||
value = "ko"
|
||||
ngx.shared.user_agents_cache:set(ngx.var.http_user_agent, "ko", 86400)
|
||||
break
|
||||
end
|
||||
end
|
||||
if value == nil then
|
||||
value = "ok"
|
||||
ngx.shared.user_agents_cache:set(ngx.var.http_user_agent, "ok", 86400)
|
||||
end
|
||||
end
|
||||
if value == "ko" then
|
||||
ngx.log(ngx.NOTICE, "[BLOCK] User-Agent " .. ngx.var.http_user_agent .. " is blacklisted")
|
||||
ngx.exit(ngx.HTTP_FORBIDDEN)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- check if referrer is allowed
|
||||
if use_referrer and ngx.var.bad_referrer == "yes" then
|
||||
ngx.log(ngx.WARN, "[BLOCK] Referrer " .. ngx.var.http_referer .. " is blacklisted")
|
||||
ngx.exit(ngx.HTTP_FORBIDDEN)
|
||||
if use_referrer and ngx.var.http_referer ~= nil then
|
||||
local value, flags = ngx.shared.referrers_cache:get(ngx.var.http_referer)
|
||||
if value == nil then
|
||||
local patterns = ngx.shared.referrers_data:get_keys(0)
|
||||
for i, pattern in ipairs(patterns) do
|
||||
if string.match(ngx.var.http_referer, pattern) then
|
||||
value = "ko"
|
||||
ngx.shared.referrers_cache:set(ngx.var.http_referer, "ko", 86400)
|
||||
break
|
||||
end
|
||||
end
|
||||
if value == nil then
|
||||
value = "ok"
|
||||
ngx.shared.referrers_cache:set(ngx.var.http_referer, "ok", 86400)
|
||||
end
|
||||
end
|
||||
if value == "ko" then
|
||||
ngx.log(ngx.NOTICE, "[BLOCK] Referrer " .. ngx.var.http_referer .. " is blacklisted")
|
||||
ngx.exit(ngx.HTTP_FORBIDDEN)
|
||||
end
|
||||
end
|
||||
|
||||
-- check if country is allowed
|
||||
if use_country and ngx.var.allowed_country == "no" then
|
||||
ngx.log(ngx.WARN, "[BLOCK] Country of " .. ngx.var.remote_addr .. " is blacklisted")
|
||||
ngx.log(ngx.NOTICE, "[BLOCK] Country of " .. ngx.var.remote_addr .. " is blacklisted")
|
||||
ngx.exit(ngx.HTTP_FORBIDDEN)
|
||||
end
|
||||
|
||||
@@ -131,7 +209,7 @@ if use_crowdsec then
|
||||
ngx.log(ngx.ERR, "[Crowdsec] " .. err)
|
||||
end
|
||||
if not ok then
|
||||
ngx.log(ngx.ERR, "[Crowdsec] denied '" .. ngx.var.remote_addr .. "'")
|
||||
ngx.log(ngx.NOTICE, "[Crowdsec] denied '" .. ngx.var.remote_addr .. "'")
|
||||
ngx.exit(ngx.HTTP_FORBIDDEN)
|
||||
end
|
||||
end
|
||||
@@ -143,7 +221,7 @@ if use_antibot_cookie then
|
||||
cookie.set({uri = ngx.var.request_uri})
|
||||
return ngx.redirect(antibot_uri)
|
||||
end
|
||||
ngx.log(ngx.WARN, "[ANTIBOT] cookie fail for " .. ngx.var.remote_addr)
|
||||
ngx.log(ngx.NOTICE, "[ANTIBOT] cookie fail for " .. ngx.var.remote_addr)
|
||||
return ngx.exit(ngx.HTTP_FORBIDDEN)
|
||||
else
|
||||
if ngx.var.request_uri == antibot_uri then
|
||||
|
||||
@@ -49,8 +49,7 @@ SecResponseBodyLimit 524288
|
||||
SecResponseBodyLimitAction ProcessPartial
|
||||
|
||||
# log usefull stuff
|
||||
SecAuditEngine RelevantOnly
|
||||
SecAuditLogRelevantStatus "^(?:5|4(?!04))"
|
||||
SecAuditEngine %MODSECURITY_SEC_AUDIT_ENGINE%
|
||||
SecAuditLogType Serial
|
||||
SecAuditLog /var/log/nginx/modsec_audit.log
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
location %REVERSE_PROXY_URL% {
|
||||
etag off;
|
||||
proxy_pass %REVERSE_PROXY_HOST%;
|
||||
%REVERSE_PROXY_HEADERS%
|
||||
%REVERSE_PROXY_WS%
|
||||
|
||||
@@ -4,7 +4,7 @@ server {
|
||||
%FASTCGI_PATH%
|
||||
%SERVER_CONF%
|
||||
%PROXY_REAL_IP%
|
||||
%MAIN_LUA%
|
||||
%INCLUDE_LUA%
|
||||
%USE_MODSECURITY%
|
||||
%LISTEN_HTTP%
|
||||
%USE_HTTPS%
|
||||
@@ -27,12 +27,8 @@ server {
|
||||
%REFERRER_POLICY%
|
||||
%FEATURE_POLICY%
|
||||
%PERMISSIONS_POLICY%
|
||||
%BLOCK_TOR_EXIT_NODE%
|
||||
%BLOCK_PROXIES%
|
||||
%BLOCK_ABUSERS%
|
||||
%COOKIE_FLAGS%
|
||||
%ERRORS%
|
||||
%USE_FAIL2BAN%
|
||||
%USE_CLIENT_CACHE%
|
||||
%USE_GZIP%
|
||||
%USE_BROTLI%
|
||||
|
||||
4
dependencies.sh
Normal file
4
dependencies.sh
Normal file
@@ -0,0 +1,4 @@
|
||||
#!/bin/sh
|
||||
|
||||
# install dependencies
|
||||
apk --no-cache add certbot libstdc++ libmaxminddb geoip pcre yajl clamav apache2-utils openssl lua libgd go jq mariadb-connector-c bash brotli
|
||||
20
docs/Makefile
Normal file
20
docs/Makefile
Normal file
@@ -0,0 +1,20 @@
|
||||
# Minimal makefile for Sphinx documentation
|
||||
#
|
||||
|
||||
# You can set these variables from the command line, and also
|
||||
# from the environment for the first two.
|
||||
SPHINXOPTS ?=
|
||||
SPHINXBUILD ?= sphinx-build
|
||||
SOURCEDIR = .
|
||||
BUILDDIR = _build
|
||||
|
||||
# Put it first so that "make" without argument is like "make help".
|
||||
help:
|
||||
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||
|
||||
.PHONY: help Makefile
|
||||
|
||||
# Catch-all target: route all unknown targets to Sphinx using the new
|
||||
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
|
||||
%: Makefile
|
||||
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||
55
docs/conf.py
Normal file
55
docs/conf.py
Normal file
@@ -0,0 +1,55 @@
|
||||
# Configuration file for the Sphinx documentation builder.
|
||||
#
|
||||
# This file only contains a selection of the most common options. For a full
|
||||
# list see the documentation:
|
||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html
|
||||
|
||||
# -- Path setup --------------------------------------------------------------
|
||||
|
||||
# If extensions (or modules to document with autodoc) are in another directory,
|
||||
# add these directories to sys.path here. If the directory is relative to the
|
||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
#
|
||||
# import os
|
||||
# import sys
|
||||
# sys.path.insert(0, os.path.abspath('.'))
|
||||
|
||||
|
||||
# -- Project information -----------------------------------------------------
|
||||
|
||||
project = 'bunkerized-nginx'
|
||||
copyright = '2021, bunkerity'
|
||||
author = 'bunkerity'
|
||||
|
||||
# The full version, including alpha/beta/rc tags
|
||||
release = 'v1.2.5'
|
||||
|
||||
|
||||
# -- General configuration ---------------------------------------------------
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be
|
||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||
# ones.
|
||||
extensions = ['myst_parser']
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['_templates']
|
||||
|
||||
# List of patterns, relative to source directory, that match files and
|
||||
# directories to ignore when looking for source files.
|
||||
# This pattern also affects html_static_path and html_extra_path.
|
||||
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
|
||||
|
||||
# -- Options for HTML output -------------------------------------------------
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||
# a list of builtin themes.
|
||||
#
|
||||
import sphinx_rtd_theme
|
||||
html_theme = "sphinx_rtd_theme"
|
||||
html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
|
||||
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
html_static_path = ['_static']
|
||||
956
docs/environment_variables.md
Normal file
956
docs/environment_variables.md
Normal file
@@ -0,0 +1,956 @@
|
||||
# List of environment variables
|
||||
|
||||
## nginx
|
||||
|
||||
### Misc
|
||||
|
||||
`MULTISITE`
|
||||
Values : *yes* | *no*
|
||||
Default value : *no*
|
||||
Context : *global*
|
||||
When set to *no*, only one server block will be generated. Otherwise one server per host defined in the `SERVER_NAME` environment variable will be generated.
|
||||
Any environment variable tagged as *multisite* context can be used for a specific server block with the following format : *host_VARIABLE=value*. If the variable is used without the host prefix it will be applied to all the server blocks (but still can be overriden).
|
||||
|
||||
`SERVER_NAME`
|
||||
Values : *<first name> <second name> ...*
|
||||
Default value : *www.bunkerity.com*
|
||||
Context : *global*
|
||||
Sets the host names of the webserver separated with spaces. This must match the Host header sent by clients.
|
||||
Useful when used with `MULTISITE=yes` and/or `AUTO_LETSENCRYPT=yes` and/or `DISABLE_DEFAULT_SERVER=yes`.
|
||||
|
||||
`MAX_CLIENT_SIZE`
|
||||
Values : *0* | *Xm*
|
||||
Default value : *10m*
|
||||
Context : *global*, *multisite*
|
||||
Sets the maximum body size before nginx returns a 413 error code.
|
||||
Setting to 0 means "infinite" body size.
|
||||
|
||||
`ALLOWED_METHODS`
|
||||
Values : *allowed HTTP methods separated with | char*
|
||||
Default value : *GET|POST|HEAD*
|
||||
Context : *global*, *multisite*
|
||||
Only the HTTP methods listed here will be accepted by nginx. If not listed, nginx will close the connection.
|
||||
|
||||
`DISABLE_DEFAULT_SERVER`
|
||||
Values : *yes* | *no*
|
||||
Default value : *no*
|
||||
Context : *global*
|
||||
If set to yes, nginx will only respond to HTTP request when the Host header match a FQDN specified in the `SERVER_NAME` environment variable.
|
||||
For example, it will close the connection if a bot access the site with direct ip.
|
||||
|
||||
`SERVE_FILES`
|
||||
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.
|
||||
|
||||
`DNS_RESOLVERS`
|
||||
Values : *\<two IP addresses separated with a space\>*
|
||||
Default value : *127.0.0.11*
|
||||
Context : *global*
|
||||
The IP addresses of the DNS resolvers to use when performing DNS lookups.
|
||||
|
||||
`ROOT_FOLDER`
|
||||
Values : *\<any valid path to web files\>*
|
||||
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.
|
||||
|
||||
`ROOT_SITE_SUBFOLDER`
|
||||
Values : *\<any valid directory name\>*
|
||||
Default value :
|
||||
Context : *global*, *multisite*
|
||||
The subfolder where nginx will search for site web files.
|
||||
|
||||
`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*
|
||||
Context : *global*
|
||||
The HTTP port number used by nginx inside the container.
|
||||
|
||||
`HTTPS_PORT`
|
||||
Values : *\<any valid port greater than 1024\>*
|
||||
Default value : *8443*
|
||||
Context : *global*
|
||||
The HTTPS port number used by nginx inside the container.
|
||||
|
||||
### Information leak
|
||||
|
||||
`SERVER_TOKENS`
|
||||
Values : *on* | *off*
|
||||
Default value : *off*
|
||||
Context : *global*
|
||||
If set to on, nginx will display server version in Server header and default error pages.
|
||||
|
||||
`REMOVE_HEADERS`
|
||||
Values : \<*list of headers separated with space*\>
|
||||
Default value : *Server X-Powered-By X-AspNet-Version X-AspNetMvc-Version*
|
||||
Context : *global*, *multisite*
|
||||
List of header to remove when sending responses to clients.
|
||||
|
||||
### Custom error pages
|
||||
|
||||
`ERROR_XXX`
|
||||
Values : *\<relative path to the error page\>*
|
||||
Default value :
|
||||
Context : *global*, *multisite*
|
||||
Use this kind of environment variable to define custom error page depending on the HTTP error code. Replace XXX with HTTP code.
|
||||
For example : `ERROR_404=/404.html` means the /404.html page will be displayed when 404 code is generated. The path is relative to the root web folder.
|
||||
|
||||
### HTTP basic authentication
|
||||
|
||||
`USE_AUTH_BASIC`
|
||||
Values : *yes* | *no*
|
||||
Default value : *no*
|
||||
Context : *global*, *multisite*
|
||||
If set to yes, enables HTTP basic authentication at the location `AUTH_BASIC_LOCATION` with user `AUTH_BASIC_USER` and password `AUTH_BASIC_PASSWORD`.
|
||||
|
||||
`AUTH_BASIC_LOCATION`
|
||||
Values : *sitewide* | */somedir* | *\<any valid location\>*
|
||||
Default value : *sitewide*
|
||||
Context : *global*, *multisite*
|
||||
The location to restrict when `USE_AUTH_BASIC` is set to *yes*. If the special value *sitewide* is used then auth basic will be set at server level outside any location context.
|
||||
|
||||
`AUTH_BASIC_USER`
|
||||
Values : *\<any valid username\>*
|
||||
Default value : *changeme*
|
||||
Context : *global*, *multisite*
|
||||
The username allowed to access `AUTH_BASIC_LOCATION` when `USE_AUTH_BASIC` is set to yes.
|
||||
|
||||
`AUTH_BASIC_PASSWORD`
|
||||
Values : *\<any valid password\>*
|
||||
Default value : *changeme*
|
||||
Context : *global*, *multisite*
|
||||
The password of `AUTH_BASIC_USER` when `USE_AUTH_BASIC` is set to yes.
|
||||
|
||||
`AUTH_BASIC_TEXT`
|
||||
Values : *\<any valid text\>*
|
||||
Default value : *Restricted area*
|
||||
Context : *global*, *multisite*
|
||||
The text displayed inside the login prompt when `USE_AUTH_BASIC` is set to yes.
|
||||
|
||||
### Reverse proxy
|
||||
|
||||
`USE_REVERSE_PROXY`
|
||||
Values : *yes* | *no*
|
||||
Default value : *no*
|
||||
Context : *global*, *multisite*
|
||||
Set this environment variable to *yes* if you want to use bunkerized-nginx as a reverse proxy.
|
||||
|
||||
`REVERSE_PROXY_URL`
|
||||
Values : \<*any valid location path*\>
|
||||
Default value :
|
||||
Context : *global*, *multisite*
|
||||
Only valid when `USE_REVERSE_PROXY` is set to *yes*. Let's you define the location path to match 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_URL_1`, `REVERSE_PROXY_URL_2`, `REVERSE_PROXY_URL_3`, ...
|
||||
|
||||
`REVERSE_PROXY_HOST`
|
||||
Values : \<*any valid proxy_pass value*\>
|
||||
Default value :
|
||||
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`, ...
|
||||
|
||||
`REVERSE_PROXY_HEADERS`
|
||||
Values : *\<list of custom headers separated with a semicolon like this : header1 value1;header2 value2...\>*
|
||||
Default value :
|
||||
Context : *global*, *multisite*
|
||||
Only valid when `USE_REVERSE_PROXY` is set to *yes*.
|
||||
You can set multiple url/host by adding a suffix number to the variable name like this : `REVERSE_PROXY_HEADERS_1`, `REVERSE_PROXY_HEADERS_2`, `REVERSE_PROXY_HEADERS_3`, ...
|
||||
|
||||
`PROXY_REAL_IP`
|
||||
Values : *yes* | *no*
|
||||
Default value : *no*
|
||||
Context : *global*, *multisite*
|
||||
Set this environment variable to *yes* if you're using bunkerized-nginx behind a reverse proxy. This means you will see the real client address instead of the proxy one inside your logs. Modsecurity, fail2ban and others security tools will also then work correctly.
|
||||
|
||||
`PROXY_REAL_IP_FROM`
|
||||
Values : *\<list of trusted IP addresses and/or networks separated with spaces\>*
|
||||
Default value : *192.168.0.0/16 172.16.0.0/12 10.0.0.0/8*
|
||||
Context : *global*, *multisite*
|
||||
When `PROXY_REAL_IP` is set to *yes*, lets you define the trusted IPs/networks allowed to send the correct client address.
|
||||
|
||||
`PROXY_REAL_IP_HEADER`
|
||||
Values : *X-Forwarded-For* | *X-Real-IP* | *custom header*
|
||||
Default value : *X-Forwarded-For*
|
||||
Context : *global*, *multisite*
|
||||
When `PROXY_REAL_IP` is set to *yes*, lets you define the header that contains the real client IP address.
|
||||
|
||||
`PROXY_REAL_IP_RECURSIVE`
|
||||
Values : *on* | *off*
|
||||
Default value : *on*
|
||||
Context : *global*, *multisite*
|
||||
When `PROXY_REAL_IP` is set to *yes*, setting this to *on* avoid spoofing attacks using the header defined in `PROXY_REAL_IP_HEADER`.
|
||||
|
||||
### Compression
|
||||
|
||||
`USE_GZIP`
|
||||
Values : *yes* | *no*
|
||||
Default value : *no*
|
||||
Context : *global*, *multisite*
|
||||
When set to *yes*, nginx will use the gzip algorithm to compress responses sent to clients.
|
||||
|
||||
`GZIP_COMP_LEVEL`
|
||||
Values : \<*any integer between 1 and 9*\>
|
||||
Default value : *5*
|
||||
Context : *global*, *multisite*
|
||||
The gzip compression level to use when `USE_GZIP` is set to *yes*.
|
||||
|
||||
`GZIP_MIN_LENGTH`
|
||||
Values : \<*any positive integer*\>
|
||||
Default value : *1000*
|
||||
Context : *global*, *multisite*
|
||||
The minimum size (in bytes) of a response required to compress when `USE_GZIP` is set to *yes*.
|
||||
|
||||
`GZIP_TYPES`
|
||||
Values : \<*list of mime types separated with space*\>
|
||||
Default value : *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*
|
||||
Context : *global*, *multisite*
|
||||
List of response MIME type required to compress when `USE_GZIP` is set to *yes*.
|
||||
|
||||
`USE_BROTLI`
|
||||
Values : *yes* | *no*
|
||||
Default value : *no*
|
||||
Context : *global*, *multisite*
|
||||
When set to *yes*, nginx will use the brotli algorithm to compress responses sent to clients.
|
||||
|
||||
`BROTLI_COMP_LEVEL`
|
||||
Values : \<*any integer between 1 and 9*\>
|
||||
Default value : *5*
|
||||
Context : *global*, *multisite*
|
||||
The brotli compression level to use when `USE_BROTLI` is set to *yes*.
|
||||
|
||||
`BROTLI_MIN_LENGTH`
|
||||
Values : \<*any positive integer*\>
|
||||
Default value : *1000*
|
||||
Context : *global*, *multisite*
|
||||
The minimum size (in bytes) of a response required to compress when `USE_BROTLI` is set to *yes*.
|
||||
|
||||
`BROTLI_TYPES`
|
||||
Values : \<*list of mime types separated with space*\>
|
||||
Default value : *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*
|
||||
Context : *global*, *multisite*
|
||||
List of response MIME type required to compress when `USE_BROTLI` is set to *yes*.
|
||||
|
||||
### Cache
|
||||
|
||||
`USE_CLIENT_CACHE`
|
||||
Values : *yes* | *no*
|
||||
Default value : *no*
|
||||
Context : *global*, *multisite*
|
||||
When set to *yes*, clients will be told to cache some files locally.
|
||||
|
||||
`CLIENT_CACHE_EXTENSIONS`
|
||||
Values : \<*list of extensions separated with |*\>
|
||||
Default value : *jpg|jpeg|png|bmp|ico|svg|tif|css|js|otf|ttf|eot|woff|woff2*
|
||||
Context : *global*, *multisite*
|
||||
List of file extensions that clients should cache when `USE_CLIENT_CACHE` is set to *yes*.
|
||||
|
||||
`CLIENT_CACHE_CONTROL`
|
||||
Values : \<*Cache-Control header value*\>
|
||||
Default value : *public, max-age=15552000*
|
||||
Context : *global*, *multisite*
|
||||
Content of the [Cache-Control](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control) header to send when `USE_CLIENT_CACHE` is set to *yes*.
|
||||
|
||||
`CLIENT_CACHE_ETAG`
|
||||
Values : *on* | *off*
|
||||
Default value : *on*
|
||||
Context : *global*, *multisite*
|
||||
Whether or not nginx will send the [ETag](https://en.wikipedia.org/wiki/HTTP_ETag) header when `USE_CLIENT_CACHE` is set to *yes*.
|
||||
|
||||
`USE_OPEN_FILE_CACHE`
|
||||
Values : *yes* | *no*
|
||||
Default value : *no*
|
||||
Context : *global*, *multisite*
|
||||
When set to *yes*, nginx will cache open fd, existence of directories, ... See [open_file_cache](http://nginx.org/en/docs/http/ngx_http_core_module.html#open_file_cache).
|
||||
|
||||
`OPEN_FILE_CACHE`
|
||||
Values : \<*any valid open_file_cache parameters*\>
|
||||
Default value : *max=1000 inactive=20s*
|
||||
Context : *global*, *multisite*
|
||||
Parameters to use with open_file_cache when `USE_OPEN_FILE_CACHE` is set to *yes*.
|
||||
|
||||
`OPEN_FILE_CACHE_ERRORS`
|
||||
Values : *on* | *off*
|
||||
Default value : *on*
|
||||
Context : *global*, *multisite*
|
||||
Whether or not nginx should cache file lookup errors when `USE_OPEN_FILE_CACHE` is set to *yes*.
|
||||
|
||||
`OPEN_FILE_CACHE_MIN_USES`
|
||||
Values : \<*any valid integer *\>
|
||||
Default value : *2*
|
||||
Context : *global*, *multisite*
|
||||
The minimum number of file accesses required to cache the fd when `USE_OPEN_FILE_CACHE` is set to *yes*.
|
||||
|
||||
`OPEN_FILE_CACHE_VALID`
|
||||
Values : \<*any time value like Xs, Xm, Xh, ...*\>
|
||||
Default value : *30s*
|
||||
Context : *global*, *multisite*
|
||||
The time after which cached elements should be validated when `USE_OPEN_FILE_CACHE` is set to *yes*.
|
||||
|
||||
`USE_PROXY_CACHE`
|
||||
Values : *yes* | *no*
|
||||
Default value : *no*
|
||||
Context : *global*, *multisite*
|
||||
When set to *yes*, nginx will cache responses from proxied applications. See [proxy_cache](http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_cache).
|
||||
|
||||
`PROXY_CACHE_PATH_ZONE_SIZE`
|
||||
Values : \<*any valid size like Xk, Xm, Xg, ...*\>
|
||||
Default value : *10m*
|
||||
Context : *global*, *multisite*
|
||||
Maximum size of cached metadata when `USE_PROXY_CACHE` is set to *yes*.
|
||||
|
||||
`PROXY_CACHE_PATH_PARAMS`
|
||||
Values : \<*any valid parameters to proxy_cache_path directive*\>
|
||||
Default value : *max_size=100m*
|
||||
Context : *global*, *multisite*
|
||||
Parameters to use for [proxy_cache_path](http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_cache_path) directive when `USE_PROXY_CACHE` is set to *yes*.
|
||||
|
||||
`PROXY_CACHE_METHODS`
|
||||
Values : \<*list of HTTP methods separated with space*\>
|
||||
Default value : *GET HEAD*
|
||||
Context : *global*, *multisite*
|
||||
The HTTP methods that should trigger a cache operation when `USE_PROXY_CACHE` is set to *yes*.
|
||||
|
||||
`PROXY_CACHE_MIN_USES`
|
||||
Values : \<*any positive integer*\>
|
||||
Default value : *2*
|
||||
Context : *global*, *multisite*
|
||||
The minimum number of requests before the response is cached when `USE_PROXY_CACHE` is set to *yes*.
|
||||
|
||||
`PROXY_CACHE_KEY`
|
||||
Values : \<*list of variables*\>
|
||||
Default value : *$scheme$host$request_uri*
|
||||
Context : *global*, *multisite*
|
||||
The key used to uniquely identify a cached response when `USE_PROXY_CACHE` is set to *yes*.
|
||||
|
||||
`PROXY_CACHE_VALID`
|
||||
Values : \<*status=time list separated with space*\>
|
||||
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*.
|
||||
|
||||
`PROXY_NO_CACHE`
|
||||
Values : \<*list of variables*\>
|
||||
Default value : *$http_authorization*
|
||||
Context : *global*, *multisite*
|
||||
Conditions that must be met to disable caching of the response when `USE_PROXY_CACHE` is set to *yes*.
|
||||
|
||||
`PROXY_CACHE_BYPASS`
|
||||
Values : \<*list of variables*\>
|
||||
Default value : *$http_authorization*
|
||||
Context : *global*, *multisite*
|
||||
Conditions that must be met to bypass the cache when `USE_PROXY_CACHE` is set to *yes*.
|
||||
|
||||
## HTTPS
|
||||
|
||||
### Let's Encrypt
|
||||
|
||||
`AUTO_LETS_ENCRYPT`
|
||||
Values : *yes* | *no*
|
||||
Default value : *no*
|
||||
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@first-domain-in-server-name*
|
||||
Context : *global*, *multisite*
|
||||
Define the contact email address declare in the certificate.
|
||||
|
||||
### HTTP
|
||||
|
||||
`LISTEN_HTTP`
|
||||
Values : *yes* | *no*
|
||||
Default value : *yes*
|
||||
Context : *global*, *multisite*
|
||||
If set to no, nginx will not in listen on HTTP (port 80).
|
||||
Useful if you only want HTTPS access to your website.
|
||||
|
||||
`REDIRECT_HTTP_TO_HTTPS`
|
||||
Values : *yes* | *no*
|
||||
Default value : *no*
|
||||
Context : *global*, *multisite*
|
||||
If set to yes, nginx will redirect all HTTP requests to HTTPS.
|
||||
|
||||
### Custom certificate
|
||||
|
||||
`USE_CUSTOM_HTTPS`
|
||||
Values : *yes* | *no*
|
||||
Default value : *no*
|
||||
Context : *global*, *multisite*
|
||||
If set to yes, HTTPS will be enabled with certificate/key of your choice.
|
||||
|
||||
`CUSTOM_HTTPS_CERT`
|
||||
Values : *\<any valid path inside the container\>*
|
||||
Default value :
|
||||
Context : *global*, *multisite*
|
||||
Full path of the certificate or bundle file to use when `USE_CUSTOM_HTTPS` is set to yes. If your chain of trust contains one or more intermediate certificate(s), you will need to bundle them into a single file (more info [here](https://nginx.org/en/docs/http/configuring_https_servers.html#chains)).
|
||||
|
||||
`CUSTOM_HTTPS_KEY`
|
||||
Values : *\<any valid path inside the container\>*
|
||||
Default value :
|
||||
Context : *global*, *multisite*
|
||||
Full path of the key file to use when `USE_CUSTOM_HTTPS` is set to yes.
|
||||
|
||||
### Self-signed certificate
|
||||
|
||||
`GENERATE_SELF_SIGNED_SSL`
|
||||
Values : *yes* | *no*
|
||||
Default value : *no*
|
||||
Context : *global*
|
||||
If set to yes, HTTPS will be enabled with a container generated self-signed certificate.
|
||||
|
||||
`SELF_SIGNED_SSL_EXPIRY`
|
||||
Values : *integer*
|
||||
Default value : *365* (1 year)
|
||||
Context : *global*
|
||||
Needs `GENERATE_SELF_SIGNED_SSL` to work.
|
||||
Sets the expiry date for the self generated certificate.
|
||||
|
||||
`SELF_SIGNED_SSL_COUNTRY`
|
||||
Values : *text*
|
||||
Default value : *Switzerland*
|
||||
Context : *global*
|
||||
Needs `GENERATE_SELF_SIGNED_SSL` to work.
|
||||
Sets the country for the self generated certificate.
|
||||
|
||||
`SELF_SIGNED_SSL_STATE`
|
||||
Values : *text*
|
||||
Default value : *Switzerland*
|
||||
Context : *global*
|
||||
Needs `GENERATE_SELF_SIGNED_SSL` to work.
|
||||
Sets the state for the self generated certificate.
|
||||
|
||||
`SELF_SIGNED_SSL_CITY`
|
||||
Values : *text*
|
||||
Default value : *Bern*
|
||||
Context : *global*
|
||||
Needs `GENERATE_SELF_SIGNED_SSL` to work.
|
||||
Sets the city for the self generated certificate.
|
||||
|
||||
`SELF_SIGNED_SSL_ORG`
|
||||
Values : *text*
|
||||
Default value : *AcmeInc*
|
||||
Context : *global*
|
||||
Needs `GENERATE_SELF_SIGNED_SSL` to work.
|
||||
Sets the organisation name for the self generated certificate.
|
||||
|
||||
`SELF_SIGNED_SSL_OU`
|
||||
Values : *text*
|
||||
Default value : *IT*
|
||||
Context : *global*
|
||||
Needs `GENERATE_SELF_SIGNED_SSL` to work.
|
||||
Sets the organisitional unit for the self generated certificate.
|
||||
|
||||
`SELF_SIGNED_SSL_CN`
|
||||
Values : *text*
|
||||
Default value : *bunkerity-nginx*
|
||||
Context : *global*
|
||||
Needs `GENERATE_SELF_SIGNED_SSL` to work.
|
||||
Sets the CN server name for the self generated certificate.
|
||||
|
||||
### Misc
|
||||
|
||||
`HTTP2`
|
||||
Values : *yes* | *no*
|
||||
Default value : *yes*
|
||||
Context : *global*, *multisite*
|
||||
If set to yes, nginx will use HTTP2 protocol when HTTPS is enabled.
|
||||
|
||||
`HTTPS_PROTOCOLS`
|
||||
Values : *TLSv1.2* | *TLSv1.3* | *TLSv1.2 TLSv1.3*
|
||||
Default value : *TLSv1.2 TLSv1.3*
|
||||
Context : *global*, *multisite*
|
||||
The supported version of TLS. We recommend the default value *TLSv1.2 TLSv1.3* for compatibility reasons.
|
||||
|
||||
## ModSecurity
|
||||
|
||||
`USE_MODSECURITY`
|
||||
Values : *yes* | *no*
|
||||
Default value : *yes*
|
||||
Context : *global*, *multisite*
|
||||
If set to yes, the ModSecurity WAF will be enabled.
|
||||
You can include custom rules by adding .conf files into the /modsec-confs/ directory inside the container (i.e : through a volume).
|
||||
|
||||
`USE_MODSECURITY_CRS`
|
||||
Values : *yes* | *no*
|
||||
Default value : *yes*
|
||||
Context : *global*, *multisite*
|
||||
If set to yes, the [OWASP ModSecurity Core Rule Set](https://coreruleset.org/) will be used. It provides generic rules to detect common web attacks.
|
||||
You can customize the CRS (i.e. : add WordPress exclusions) by adding custom .conf files into the /modsec-crs-confs/ directory inside the container (i.e : through a volume). Files inside this directory are included before the CRS rules. If you need to tweak (i.e. : SecRuleUpdateTargetById) put .conf files inside the /modsec-confs/ which is included after the CRS rules.
|
||||
|
||||
## Security headers
|
||||
|
||||
`X_FRAME_OPTIONS`
|
||||
Values : *DENY* | *SAMEORIGIN* | *ALLOW-FROM https://www.website.net*
|
||||
Default value : *DENY*
|
||||
Context : *global*, *multisite*
|
||||
Policy to be used when the site is displayed through iframe. Can be used to mitigate clickjacking attacks.
|
||||
More info [here](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options).
|
||||
|
||||
`X_XSS_PROTECTION`
|
||||
Values : *0* | *1* | *1; mode=block*
|
||||
Default value : *1; mode=block*
|
||||
Context : *global*, *multisite*
|
||||
Policy to be used when XSS is detected by the browser. Only works with Internet Explorer.
|
||||
More info [here](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-XSS-Protection).
|
||||
|
||||
`X_CONTENT_TYPE_OPTIONS`
|
||||
Values : *nosniff*
|
||||
Default value : *nosniff*
|
||||
Context : *global*, *multisite*
|
||||
Tells the browser to be strict about MIME type.
|
||||
More info [here](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options).
|
||||
|
||||
`REFERRER_POLICY`
|
||||
Values : *no-referrer* | *no-referrer-when-downgrade* | *origin* | *origin-when-cross-origin* | *same-origin* | *strict-origin* | *strict-origin-when-cross-origin* | *unsafe-url*
|
||||
Default value : *no-referrer*
|
||||
Context : *global*, *multisite*
|
||||
Policy to be used for the Referer header.
|
||||
More info [here](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy).
|
||||
|
||||
`FEATURE_POLICY`
|
||||
Values : *<directive> <allow list>*
|
||||
Default value : *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'*
|
||||
Context : *global*, *multisite*
|
||||
Tells the browser which features can be used on the website.
|
||||
More info [here](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Feature-Policy).
|
||||
|
||||
`PERMISSIONS_POLICY`
|
||||
Values : *feature=(allow list)*
|
||||
Default value : 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=()
|
||||
Context : *global*, *multisite*
|
||||
Tells the browser which features can be used on the website.
|
||||
More info [here](https://www.w3.org/TR/permissions-policy-1/).
|
||||
|
||||
`COOKIE_FLAGS`
|
||||
Values : *\* HttpOnly* | *MyCookie secure SameSite=Lax* | *...*
|
||||
Default value : *\* HttpOnly SameSite=Lax*
|
||||
Context : *global*, *multisite*
|
||||
Adds some security to the cookies set by the server.
|
||||
Accepted value can be found [here](https://github.com/AirisX/nginx_cookie_flag_module).
|
||||
|
||||
`COOKIE_AUTO_SECURE_FLAG`
|
||||
Values : *yes* | *no*
|
||||
Default value : *yes*
|
||||
Context : *global*, *multisite*
|
||||
When set to *yes*, the *secure* will be automatically added to cookies when using HTTPS.
|
||||
|
||||
`STRICT_TRANSPORT_SECURITY`
|
||||
Values : *max-age=expireTime [; includeSubDomains] [; preload]*
|
||||
Default value : *max-age=31536000*
|
||||
Context : *global*, *multisite*
|
||||
Tells the browser to use exclusively HTTPS instead of HTTP when communicating with the server.
|
||||
More info [here](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security).
|
||||
|
||||
`CONTENT_SECURITY_POLICY`
|
||||
Values : *\<directive 1\>; \<directive 2\>; ...*
|
||||
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 allow-downloads; 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).
|
||||
|
||||
## Blocking
|
||||
|
||||
### Antibot
|
||||
|
||||
`USE_ANTIBOT`
|
||||
Values : *no* | *cookie* | *javascript* | *captcha* | *recaptcha*
|
||||
Default value : *no*
|
||||
Context : *global*, *multisite*
|
||||
If set to another allowed value than *no*, users must complete a "challenge" before accessing the pages on your website :
|
||||
- *cookie* : asks the users to set a cookie
|
||||
- *javascript* : users must execute a javascript code
|
||||
- *captcha* : a text captcha must be resolved by the users
|
||||
- *recaptcha* : use [Google reCAPTCHA v3](https://developers.google.com/recaptcha/intro) score to allow/deny users
|
||||
|
||||
`ANTIBOT_URI`
|
||||
Values : *\<any valid uri\>*
|
||||
Default value : */challenge*
|
||||
Context : *global*, *multisite*
|
||||
A valid and unused URI to redirect users when `USE_ANTIBOT` is used. Be sure that it doesn't exist on your website.
|
||||
|
||||
`ANTIBOT_SESSION_SECRET`
|
||||
Values : *random* | *\<32 chars of your choice\>*
|
||||
Default value : *random*
|
||||
Context : *global*, *multisite*
|
||||
A secret used to generate sessions when `USE_ANTIBOT` is set. Using the special *random* value will generate a random one. Be sure to use the same value when you are in a multi-server environment (so sessions are valid in all the servers).
|
||||
|
||||
`ANTIBOT_RECAPTCHA_SCORE`
|
||||
Values : *\<0.0 to 1.0\>*
|
||||
Default value : *0.7*
|
||||
Context : *global*, *multisite*
|
||||
The minimum score required when `USE_ANTIBOT` is set to *recaptcha*.
|
||||
|
||||
`ANTIBOT_RECAPTCHA_SITEKEY`
|
||||
Values : *\<public key given by Google\>*
|
||||
Default value :
|
||||
Context : *global*
|
||||
The sitekey given by Google when `USE_ANTIBOT` is set to *recaptcha*.
|
||||
|
||||
`ANTIBOT_RECAPTCHA_SECRET`
|
||||
Values : *\<private key given by Google\>*
|
||||
Default value :
|
||||
Context : *global*
|
||||
The secret given by Google when `USE_ANTIBOT` is set to *recaptcha*.
|
||||
|
||||
### External blacklists
|
||||
|
||||
`BLOCK_USER_AGENT`
|
||||
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) and [here](https://raw.githubusercontent.com/JayBizzle/Crawler-Detect/master/raw/Crawlers.txt).
|
||||
|
||||
`BLOCK_TOR_EXIT_NODE`
|
||||
Values : *yes* | *no*
|
||||
Default value : *yes*
|
||||
Context : *global*, *multisite*
|
||||
Is set to yes, will block known TOR exit nodes.
|
||||
Blacklist can be found [here](https://iplists.firehol.org/?ipset=tor_exits).
|
||||
|
||||
`BLOCK_PROXIES`
|
||||
Values : *yes* | *no*
|
||||
Default value : *yes*
|
||||
Context : *global*, *multisite*
|
||||
Is set to yes, will block known proxies.
|
||||
Blacklist can be found [here](https://iplists.firehol.org/?ipset=firehol_proxies).
|
||||
|
||||
`BLOCK_ABUSERS`
|
||||
Values : *yes* | *no*
|
||||
Default value : *yes*
|
||||
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`
|
||||
Values : *yes* | *no*
|
||||
Default value : *yes*
|
||||
Context : *global*, *multisite*
|
||||
If set to *yes*, DNSBL checks will be performed to the servers specified in the `DNSBL_LIST` environment variable.
|
||||
|
||||
`DNSBL_LIST`
|
||||
Values : *\<list of DNS zones separated with spaces\>*
|
||||
Default value : *bl.blocklist.de problems.dnsbl.sorbs.net sbl.spamhaus.org xbl.spamhaus.org*
|
||||
Context : *global*
|
||||
The list of DNSBL zones to query when `USE_DNSBL` is set to *yes*.
|
||||
|
||||
### CrowdSec
|
||||
|
||||
`USE_CROWDSEC`
|
||||
Values : *yes* | *no*
|
||||
Default value : *no*
|
||||
Context : *global*, *multisite*
|
||||
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
|
||||
|
||||
`USE_WHITELIST_IP`
|
||||
Values : *yes* | *no*
|
||||
Default value : *yes*
|
||||
Context : *global*, *multisite*
|
||||
If set to *yes*, lets you define custom IP addresses to be whitelisted through the `WHITELIST_IP_LIST` environment variable.
|
||||
|
||||
`WHITELIST_IP_LIST`
|
||||
Values : *\<list of IP addresses and/or network CIDR blocks separated with spaces\>*
|
||||
Default value : *23.21.227.69 40.88.21.235 50.16.241.113 50.16.241.114 50.16.241.117 50.16.247.234 52.204.97.54 52.5.190.19 54.197.234.188 54.208.100.253 54.208.102.37 107.21.1.8*
|
||||
Context : *global*
|
||||
The list of IP addresses and/or network CIDR blocks to whitelist when `USE_WHITELIST_IP` is set to *yes*. The default list contains IP addresses of the [DuckDuckGo crawler](https://help.duckduckgo.com/duckduckgo-help-pages/results/duckduckbot/).
|
||||
|
||||
`USE_WHITELIST_REVERSE`
|
||||
Values : *yes* | *no*
|
||||
Default value : *yes*
|
||||
Context : *global*, *multisite*
|
||||
If set to *yes*, lets you define custom reverse DNS suffixes to be whitelisted through the `WHITELIST_REVERSE_LIST` environment variable.
|
||||
|
||||
`WHITELIST_REVERSE_LIST`
|
||||
Values : *\<list of reverse DNS suffixes separated with spaces\>*
|
||||
Default value : *.googlebot.com .google.com .search.msn.com .crawl.yahoot.net .crawl.baidu.jp .crawl.baidu.com .yandex.com .yandex.ru .yandex.net*
|
||||
Context : *global*
|
||||
The list of reverse DNS suffixes to whitelist when `USE_WHITELIST_REVERSE` is set to *yes*. The default list contains suffixes of major search engines.
|
||||
|
||||
`WHITELIST_USER_AGENT`
|
||||
Values : *\<list of regexes separated with spaces\>*
|
||||
Default value :
|
||||
Context : *global*, *multisite*
|
||||
Whitelist user agent from being blocked by `BLOCK_USER_AGENT`.
|
||||
|
||||
`WHITELIST_URI`
|
||||
Values : *\<list of URI separated with spaces\>*
|
||||
Default value :
|
||||
Context : *global*, *multisite*
|
||||
URI listed here have security checks like bad user-agents, bad IP, ... disabled. Useful when using callbacks for example.
|
||||
|
||||
### Custom blacklisting
|
||||
|
||||
`USE_BLACKLIST_IP`
|
||||
Values : *yes* | *no*
|
||||
Default value : *yes*
|
||||
Context : *global*, *multisite*
|
||||
If set to *yes*, lets you define custom IP addresses to be blacklisted through the `BLACKLIST_IP_LIST` environment variable.
|
||||
|
||||
`BLACKLIST_IP_LIST`
|
||||
Values : *\<list of IP addresses and/or network CIDR blocks separated with spaces\>*
|
||||
Default value :
|
||||
Context : *global*
|
||||
The list of IP addresses and/or network CIDR blocks to blacklist when `USE_BLACKLIST_IP` is set to *yes*.
|
||||
|
||||
`USE_BLACKLIST_REVERSE`
|
||||
Values : *yes* | *no*
|
||||
Default value : *yes*
|
||||
Context : *global*, *multisite*
|
||||
If set to *yes*, lets you define custom reverse DNS suffixes to be blacklisted through the `BLACKLIST_REVERSE_LIST` environment variable.
|
||||
|
||||
`BLACKLIST_REVERSE_LIST`
|
||||
Values : *\<list of reverse DNS suffixes separated with spaces\>*
|
||||
Default value : *.shodan.io*
|
||||
Context : *global*
|
||||
The list of reverse DNS suffixes to blacklist when `USE_BLACKLIST_REVERSE` is set to *yes*.
|
||||
|
||||
### Requests limiting
|
||||
|
||||
`USE_LIMIT_REQ`
|
||||
Values : *yes* | *no*
|
||||
Default value : *yes*
|
||||
Context : *global*, *multisite*
|
||||
If set to yes, the amount of HTTP requests made by a user for a given resource will be limited during a period of time.
|
||||
More info rate limiting [here](https://www.nginx.com/blog/rate-limiting-nginx/) (the key used is $binary_remote_addr$uri).
|
||||
|
||||
`LIMIT_REQ_RATE`
|
||||
Values : *Xr/s* | *Xr/m*
|
||||
Default value : *1r/s*
|
||||
Context : *global*, *multisite*
|
||||
The rate limit to apply when `USE_LIMIT_REQ` is set to *yes*. Default is 1 request to the same URI and from the same IP per second.
|
||||
|
||||
`LIMIT_REQ_BURST`
|
||||
Values : *<any valid integer\>*
|
||||
Default value : *2*
|
||||
Context : *global*, *multisite*
|
||||
The number of requests to put in queue before rejecting requests.
|
||||
|
||||
`LIMIT_REQ_CACHE`
|
||||
Values : *Xm* | *Xk*
|
||||
Default value : *10m*
|
||||
Context : *global*
|
||||
The size of the cache to store information about request limiting.
|
||||
|
||||
### Connections limiting
|
||||
|
||||
`USE_LIMIT_CONN`
|
||||
Values : *yes* | *no*
|
||||
Default value : *yes*
|
||||
Context : *global*, *multisite*
|
||||
If set to yes, the number of connections made by an ip will be limited during a period of time. (ie. very small/weak ddos protection)
|
||||
More info connections limiting [here](http://nginx.org/en/docs/http/ngx_http_limit_conn_module.html).
|
||||
|
||||
`LIMIT_CONN_MAX`
|
||||
Values : *<any valid integer\>*
|
||||
Default value : *50*
|
||||
Context : *global*, *multisite*
|
||||
The maximum number of connections per ip to put in queue before rejecting requests.
|
||||
|
||||
`LIMIT_CONN_CACHE`
|
||||
Values : *Xm* | *Xk*
|
||||
Default value : *10m*
|
||||
Context : *global*
|
||||
The size of the cache to store information about connection limiting.
|
||||
|
||||
### Countries
|
||||
|
||||
`BLACKLIST_COUNTRY`
|
||||
Values : *\<country code 1\> \<country code 2\> ...*
|
||||
Default value :
|
||||
Context : *global*, *multisite*
|
||||
Block some countries from accessing your website. Use 2 letters country code separated with space.
|
||||
|
||||
`WHITELIST_COUNTRY`
|
||||
Values : *\<country code 1\> \<country code 2\> ...*
|
||||
Default value :
|
||||
Context : *global*, *multisite*
|
||||
Only allow specific countries accessing your website. Use 2 letters country code separated with space.
|
||||
|
||||
## PHP
|
||||
|
||||
`REMOTE_PHP`
|
||||
Values : *\<any valid IP/hostname\>*
|
||||
Default value :
|
||||
Context : *global*, *multisite*
|
||||
Set the IP/hostname address of a remote PHP-FPM to execute .php files.
|
||||
|
||||
`REMOTE_PHP_PATH`
|
||||
Values : *\<any valid absolute path\>*
|
||||
Default value : */app*
|
||||
Context : *global*, *multisite*
|
||||
The path where the PHP files are located inside the server specified in `REMOTE_PHP`.
|
||||
|
||||
## Bad behavior
|
||||
|
||||
`USE_BAD_BEHAVIOR`
|
||||
Values : *yes* | *no*
|
||||
Default value : *yes*
|
||||
Context : *global*, *multisite*
|
||||
If set to yes, bunkerized-nginx will block users getting too much "suspicious" HTTP codes in a period of time.
|
||||
|
||||
`BAD_BEHAVIOR_STATUS_CODES`
|
||||
Values : *\<HTTP status codes separated with space\>*
|
||||
Default value : *400 401 403 404 405 429 444*
|
||||
Context : *global*
|
||||
List of HTTP status codes considered as "suspicious".
|
||||
|
||||
`BAD_BEHAVIOR_THRESHOLD`
|
||||
Values : *<any positive integer>*
|
||||
Default value : *10*
|
||||
Context : *global*
|
||||
The number of "suspicious" HTTP status code before the corresponding IP is banned.
|
||||
|
||||
`BAD_BEHAVIOR_BAN_TIME`
|
||||
Values : *<any positive integer>*
|
||||
Default value : *86400*
|
||||
Context : *global*
|
||||
The duration time (in seconds) of a ban when the corresponding IP has reached the `BAD_BEHAVIOR_THRESHOLD`.
|
||||
|
||||
`BAD_BEHAVIOR_COUNT_TIME`
|
||||
Values : *<any positive integer>*
|
||||
Default value : *60*
|
||||
Context : *global*
|
||||
The duration time (in seconds) before the counter of "suspicious" HTTP is reset.
|
||||
|
||||
## ClamAV
|
||||
|
||||
`USE_CLAMAV_UPLOAD`
|
||||
Values : *yes* | *no*
|
||||
Default value : *yes*
|
||||
Context : *global*, *multisite*
|
||||
If set to yes, ClamAV will scan every file uploads and block the upload if the file is detected.
|
||||
|
||||
`USE_CLAMAV_SCAN`
|
||||
Values : *yes* | *no*
|
||||
Default value : *yes*
|
||||
Context : *global*
|
||||
If set to yes, ClamAV will scan all the files inside the container every day.
|
||||
|
||||
`CLAMAV_SCAN_REMOVE`
|
||||
Values : *yes* | *no*
|
||||
Default value : *yes*
|
||||
Context : *global*
|
||||
If set to yes, ClamAV will automatically remove the detected files.
|
||||
|
||||
## Cron jobs
|
||||
|
||||
`AUTO_LETS_ENCRYPT_CRON`
|
||||
Values : *\<cron expression\>*
|
||||
Default value : *15 0 \* \* \**
|
||||
Context : *global*
|
||||
Cron expression of how often certbot will try to renew the certificates.
|
||||
|
||||
`BLOCK_USER_AGENT_CRON`
|
||||
Values : *\<cron expression\>*
|
||||
Default value : *30 0 \* \* \* \**
|
||||
Context : *global*
|
||||
Cron expression of how often the blacklist of user agent is updated.
|
||||
|
||||
`BLOCK_TOR_EXIT_NODE_CRON`
|
||||
Values : *\<cron expression\>*
|
||||
Default value : *0 \*/1 \* \* \* \**
|
||||
Context : *global*
|
||||
Cron expression of how often the blacklist of tor exit node is updated.
|
||||
|
||||
`BLOCK_PROXIES_CRON`
|
||||
Values : *\<cron expression\>*
|
||||
Default value : *0 3 \* \* \* \**
|
||||
Context : *global*
|
||||
Cron expression of how often the blacklist of proxies is updated.
|
||||
|
||||
`BLOCK_ABUSERS_CRON`
|
||||
Values : *\<cron expression\>*
|
||||
Default value : *0 2 \* \* \* \**
|
||||
Context : *global*
|
||||
Cron expression of how often the blacklist of abusers is updated.
|
||||
|
||||
`BLOCK_REFERRER_CRON`
|
||||
Values : *\<cron expression\>*
|
||||
Default value : *45 0 \* \* \* \**
|
||||
Context : *global*
|
||||
Cron expression of how often the blacklist of referrer is updated.
|
||||
|
||||
`GEOIP_CRON`
|
||||
Values : *\<cron expression\>*
|
||||
Default value : *0 4 2 \* \**
|
||||
Context : *global*
|
||||
Cron expression of how often the GeoIP database is updated.
|
||||
|
||||
`USE_CLAMAV_SCAN_CRON`
|
||||
Values : *\<cron expression\>*
|
||||
Default value : *30 1 \* \* \**
|
||||
Context : *global*
|
||||
Cron expression of how often ClamAV will scan all the files inside the container.
|
||||
|
||||
`CLAMAV_UPDATE_CRON`
|
||||
Values : *\<cron expression\>*
|
||||
Default value : *0 1 \* \* \**
|
||||
Context : *global*
|
||||
Cron expression of how often ClamAV will update its database.
|
||||
|
||||
## misc
|
||||
|
||||
`SWARM_MODE`
|
||||
Values : *yes* | *no*
|
||||
Default value : *no*
|
||||
Context : *global*
|
||||
Only set to *yes* when you use *bunkerized-nginx* with *autoconf* feature in swarm mode. More info [here](#swarm-mode).
|
||||
|
||||
`USE_API`
|
||||
Values : *yes* | *no*
|
||||
Default value : *no*
|
||||
Context : *global*
|
||||
Only set to *yes* when you use *bunkerized-nginx* with *autoconf* feature in swarm mode. More info [here](#swarm-mode).
|
||||
|
||||
`API_URI`
|
||||
Values : *random* | *\<any valid URI path\>*
|
||||
Default value : *random*
|
||||
Context : *global*
|
||||
Set it to a random path when you use *bunkerized-nginx* with *autoconf* feature in swarm mode. More info [here](#swarm-mode).
|
||||
|
||||
`API_WHITELIST_IP`
|
||||
Values : *\<list of IP/CIDR separated with space\>*
|
||||
Default value : *192.168.0.0/16 172.16.0.0/12 10.0.0.0/8*
|
||||
Context : *global*
|
||||
List of IP/CIDR block allowed to send API order using the `API_URI` uri.
|
||||
12
docs/index.md
Normal file
12
docs/index.md
Normal file
@@ -0,0 +1,12 @@
|
||||
# bunkerized-nginx official documentation
|
||||
|
||||
```{toctree}
|
||||
:maxdepth: 2
|
||||
:caption: Contents
|
||||
introduction
|
||||
quickstart_guide
|
||||
security_tuning
|
||||
troubleshooting
|
||||
volumes
|
||||
environment_variables
|
||||
```
|
||||
29
docs/introduction.md
Normal file
29
docs/introduction.md
Normal file
@@ -0,0 +1,29 @@
|
||||
# Introduction
|
||||
|
||||
<p align="center">
|
||||
<img src="https://github.com/bunkerity/bunkerized-nginx/blob/master/logo.png?raw=true" width="425" />
|
||||
</p>
|
||||
|
||||
nginx Docker image secure by default.
|
||||
|
||||
Avoid the hassle of following security best practices "by hand" each time you need a web server or reverse proxy. Bunkerized-nginx provides generic security configs, settings and tools so you don't need to do it yourself.
|
||||
|
||||
Non-exhaustive list of features :
|
||||
- HTTPS support with transparent Let's Encrypt automation
|
||||
- State-of-the-art web security : HTTP security headers, prevent leaks, TLS hardening, ...
|
||||
- Integrated ModSecurity WAF with the OWASP Core Rule Set
|
||||
- Automatic ban of strange behaviors
|
||||
- Antibot challenge through cookie, javascript, captcha or recaptcha v3
|
||||
- Block TOR, proxies, bad user-agents, countries, ...
|
||||
- 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 or web UI
|
||||
- Automatic configuration with container labels
|
||||
- Docker Swarm support
|
||||
|
||||
Fooling automated tools/scanners :
|
||||
|
||||
<img src="https://github.com/bunkerity/bunkerized-nginx/blob/master/demo.gif?raw=true" />
|
||||
|
||||
You can find a live demo at <a href="https://demo-nginx.bunkerity.com" target="_blank">https://demo-nginx.bunkerity.com</a>, feel free to do some security tests.
|
||||
35
docs/make.bat
Normal file
35
docs/make.bat
Normal file
@@ -0,0 +1,35 @@
|
||||
@ECHO OFF
|
||||
|
||||
pushd %~dp0
|
||||
|
||||
REM Command file for Sphinx documentation
|
||||
|
||||
if "%SPHINXBUILD%" == "" (
|
||||
set SPHINXBUILD=sphinx-build
|
||||
)
|
||||
set SOURCEDIR=.
|
||||
set BUILDDIR=_build
|
||||
|
||||
if "%1" == "" goto help
|
||||
|
||||
%SPHINXBUILD% >NUL 2>NUL
|
||||
if errorlevel 9009 (
|
||||
echo.
|
||||
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
|
||||
echo.installed, then set the SPHINXBUILD environment variable to point
|
||||
echo.to the full path of the 'sphinx-build' executable. Alternatively you
|
||||
echo.may add the Sphinx directory to PATH.
|
||||
echo.
|
||||
echo.If you don't have Sphinx installed, grab it from
|
||||
echo.http://sphinx-doc.org/
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
|
||||
goto end
|
||||
|
||||
:help
|
||||
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
|
||||
|
||||
:end
|
||||
popd
|
||||
346
docs/quickstart_guide.md
Normal file
346
docs/quickstart_guide.md
Normal file
@@ -0,0 +1,346 @@
|
||||
# Quickstart guide
|
||||
|
||||
## Run HTTP server with default settings
|
||||
|
||||
```shell
|
||||
docker run -p 80:8080 -v /path/to/web/files:/www:ro bunkerity/bunkerized-nginx
|
||||
```
|
||||
|
||||
Web files are stored in the /www directory, the container will serve files from there. Please note that *bunkerized-nginx* doesn't run as root but as an unprivileged user with UID/GID 101 therefore you should set the rights of */path/to/web/files* accordingly.
|
||||
|
||||
## In combination with PHP
|
||||
|
||||
```shell
|
||||
docker network create mynet
|
||||
```
|
||||
|
||||
```shell
|
||||
docker run --network mynet \
|
||||
-p 80:8080 \
|
||||
-v /path/to/web/files:/www:ro \
|
||||
-e REMOTE_PHP=myphp \
|
||||
-e REMOTE_PHP_PATH=/app \
|
||||
bunkerity/bunkerized-nginx
|
||||
```
|
||||
|
||||
```shell
|
||||
docker run --network mynet \
|
||||
--name myphp \
|
||||
-v /path/to/web/files:/app \
|
||||
php:fpm
|
||||
```
|
||||
|
||||
The `REMOTE_PHP` environment variable lets you define the address of a remote PHP-FPM instance that will execute the .php files. `REMOTE_PHP_PATH` must be set to the directory where the PHP container will find the files.
|
||||
|
||||
## Run HTTPS server with automated Let's Encrypt
|
||||
|
||||
```shell
|
||||
docker run -p 80:8080 \
|
||||
-p 443:8443 \
|
||||
-v /path/to/web/files:/www:ro \
|
||||
-v /where/to/save/certificates:/etc/letsencrypt \
|
||||
-e SERVER_NAME=www.yourdomain.com \
|
||||
-e AUTO_LETS_ENCRYPT=yes \
|
||||
-e REDIRECT_HTTP_TO_HTTPS=yes \
|
||||
bunkerity/bunkerized-nginx
|
||||
```
|
||||
|
||||
Certificates are stored in the /etc/letsencrypt directory, you should save it on your local drive. Please note that *bunkerized-nginx* doesn't run as root but as an unprivileged user with UID/GID 101 therefore you should set the rights of */where/to/save/certificates* accordingly.
|
||||
|
||||
If you don't want your webserver to listen on HTTP add the environment variable `LISTEN_HTTP` with a *no* value (e.g. HTTPS only). But Let's Encrypt needs the port 80 to be opened so redirecting the port is mandatory.
|
||||
|
||||
Here you have three environment variables :
|
||||
- `SERVER_NAME` : define the FQDN of your webserver, this is mandatory for Let's Encrypt (www.yourdomain.com should point to your IP address)
|
||||
- `AUTO_LETS_ENCRYPT` : enable automatic Let's Encrypt creation and renewal of certificates
|
||||
- `REDIRECT_HTTP_TO_HTTPS` : enable HTTP to HTTPS redirection
|
||||
|
||||
## As a reverse proxy
|
||||
|
||||
```shell
|
||||
docker run -p 80:8080 \
|
||||
-e USE_REVERSE_PROXY=yes \
|
||||
-e REVERSE_PROXY_URL=/ \
|
||||
-e REVERSE_PROXY_HOST=http://myserver:8080 \
|
||||
bunkerity/bunkerized-nginx
|
||||
```
|
||||
|
||||
This is a simple reverse proxy to a unique application. If you have more than one application you can add more REVERSE_PROXY_URL/REVERSE_PROXY_HOST by appending a suffix number like this :
|
||||
|
||||
```shell
|
||||
docker run -p 80:8080 \
|
||||
-e USE_REVERSE_PROXY=yes \
|
||||
-e REVERSE_PROXY_URL_1=/app1/ \
|
||||
-e REVERSE_PROXY_HOST_1=http://myapp1:3000/ \
|
||||
-e REVERSE_PROXY_URL_2=/app2/ \
|
||||
-e REVERSE_PROXY_HOST_2=http://myapp2:3000/ \
|
||||
bunkerity/bunkerized-nginx
|
||||
```
|
||||
|
||||
## Behind a reverse proxy
|
||||
|
||||
```shell
|
||||
docker run -p 80:8080 \
|
||||
-v /path/to/web/files:/www \
|
||||
-e PROXY_REAL_IP=yes \
|
||||
bunkerity/bunkerized-nginx
|
||||
```
|
||||
|
||||
The `PROXY_REAL_IP` environment variable, when set to *yes*, activates the [ngx_http_realip_module](https://nginx.org/en/docs/http/ngx_http_realip_module.html) to get the real client IP from the reverse proxy.
|
||||
|
||||
See [this section](https://bunkerized-nginx.readthedocs.io/en/latest/environment_variables.html#reverse-proxy) if you need to tweak some values (trusted ip/network, header, ...).
|
||||
|
||||
## Multisite
|
||||
|
||||
By default, bunkerized-nginx will only create one server block. When setting the `MULTISITE` environment variable to *yes*, one server block will be created for each host defined in the `SERVER_NAME` environment variable.
|
||||
You can set/override values for a specific server by prefixing the environment variable with one of the server name previously defined.
|
||||
|
||||
```shell
|
||||
docker run -p 80:8080 \
|
||||
-p 443:8443 \
|
||||
-v /where/to/save/certificates:/etc/letsencrypt \
|
||||
-e SERVER_NAME=app1.domain.com app2.domain.com \
|
||||
-e MULTISITE=yes \
|
||||
-e AUTO_LETS_ENCRYPT=yes \
|
||||
-e REDIRECT_HTTP_TO_HTTPS=yes \
|
||||
-e USE_REVERSE_PROXY=yes \
|
||||
-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
|
||||
```
|
||||
|
||||
The `USE_REVERSE_PROXY` is a *global* variable that will be applied to each server block. Whereas the `app1.domain.com_*` and `app2.domain.com_*` will only be applied to the app1.domain.com and app2.domain.com server block respectively.
|
||||
|
||||
When serving files, the web root directory should contains subdirectories named as the servers defined in the `SERVER_NAME` environment variable. Here is an example :
|
||||
|
||||
```shell
|
||||
|
||||
docker run -p 80:8080 \
|
||||
-p 443:8443 \
|
||||
-v /where/to/save/certificates:/etc/letsencrypt \
|
||||
-v /where/are/web/files:/www:ro \
|
||||
-e SERVER_NAME=app1.domain.com app2.domain.com \
|
||||
-e MULTISITE=yes \
|
||||
-e AUTO_LETS_ENCRYPT=yes \
|
||||
-e REDIRECT_HTTP_TO_HTTPS=yes \
|
||||
-e app1.domain.com_REMOTE_PHP=php1 \
|
||||
-e app1.domain.com_REMOTE_PHP_PATH=/app \
|
||||
-e app2.domain.com_REMOTE_PHP=php2 \
|
||||
-e app2.domain.com_REMOTE_PHP_PATH=/app \
|
||||
bunkerity/bunkerized-nginx
|
||||
```
|
||||
|
||||
The */where/are/web/files* directory should have a structure like this :
|
||||
```shell
|
||||
/where/are/web/files
|
||||
├── app1.domain.com
|
||||
│ └── index.php
|
||||
│ └── ...
|
||||
└── app2.domain.com
|
||||
└── index.php
|
||||
└── ...
|
||||
```
|
||||
|
||||
## 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
|
||||
```
|
||||
|
||||
## Swarm mode
|
||||
|
||||
Automatic configuration through labels is also supported in swarm mode. The *bunkerized-nginx-autoconf* is used to listen for Swarm events (e.g. service create/rm) and "automagically" edit configurations files and reload nginx.
|
||||
|
||||
As a use case we will assume the following :
|
||||
- Some managers are also workers (they will only run the *autoconf* container for obvious security reasons)
|
||||
- The bunkerized-nginx service will be deployed on all workers (global mode) so clients can connect to each of them (e.g. load balancing, CDN, edge proxy, ...)
|
||||
- There is a shared folder mounted on managers and workers (e.g. NFS, GlusterFS, CephFS, ...)
|
||||
|
||||
Let's start by creating the network to allow communications between our services :
|
||||
```shell
|
||||
docker network create -d overlay mynet
|
||||
```
|
||||
|
||||
We can now create the *autoconf* service that will listen to swarm events :
|
||||
```shell
|
||||
docker service create --name autoconf \
|
||||
--network mynet \
|
||||
--mount type=bind,source=/var/run/docker.sock,destination=/var/run/docker.sock,ro \
|
||||
--mount type=bind,source=/shared/confs,destination=/etc/nginx \
|
||||
--mount type=bind,source=/shared/letsencrypt,destination=/etc/letsencrypt \
|
||||
--mount type=bind,source=/shared/acme-challenge,destination=/acme-challenge \
|
||||
-e SWARM_MODE=yes \
|
||||
-e API_URI=/ChangeMeToSomethingHardToGuess \
|
||||
--replicas 1 \
|
||||
--constraint node.role==manager \
|
||||
bunkerity/bunkerized-nginx-autoconf
|
||||
```
|
||||
|
||||
**You need to change `API_URI` to something hard to guess since there is no other security mechanism to protect the API at the moment.**
|
||||
|
||||
When *autoconf* is created, it's time for the *bunkerized-nginx* service to be up :
|
||||
|
||||
```shell
|
||||
docker service create --name nginx \
|
||||
--network mynet \
|
||||
-p published=80,target=8080,mode=host \
|
||||
-p published=443,target=8443,mode=host \
|
||||
--mount type=bind,source=/shared/confs,destination=/etc/nginx \
|
||||
--mount type=bind,source=/shared/letsencrypt,destination=/etc/letsencrypt,ro \
|
||||
--mount type=bind,source=/shared/acme-challenge,destination=/acme-challenge,ro \
|
||||
--mount type=bind,source=/shared/www,destination=/www,ro \
|
||||
-e SWARM_MODE=yes \
|
||||
-e USE_API=yes \
|
||||
-e API_URI=/ChangeMeToSomethingHardToGuess \
|
||||
-e MULTISITE=yes \
|
||||
-e SERVER_NAME= \
|
||||
-e AUTO_LETS_ENCRYPT=yes \
|
||||
-e REDIRECT_HTTP_TO_HTTPS=yes \
|
||||
-l bunkerized-nginx.AUTOCONF \
|
||||
--mode global \
|
||||
--constraint node.role==worker \
|
||||
bunkerity/bunkerized-nginx
|
||||
```
|
||||
|
||||
The `API_URI` value must be the same as the one specified for the *autoconf* service.
|
||||
|
||||
We can now create a new service 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 service create --name myapp \
|
||||
--network mynet \
|
||||
--mount type=bind,source=/shared/www/app.domain.com,destination=/app \
|
||||
-l bunkerized-nginx.SERVER_NAME=app.domain.com \
|
||||
-l bunkerized-nginx.REMOTE_PHP=myapp \
|
||||
-l bunkerized-nginx.REMOTE_PHP_PATH=/app \
|
||||
--constraint node.role==worker \
|
||||
php:fpm
|
||||
```
|
||||
|
||||
And a reverse proxy example :
|
||||
|
||||
```shell
|
||||
docker service create --name anotherapp \
|
||||
--network mynet \
|
||||
-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 \
|
||||
--constraint node.role==worker \
|
||||
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/.
|
||||
3
docs/requirements.txt
Normal file
3
docs/requirements.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
sphinx
|
||||
sphinx-rtd-theme
|
||||
myst-parser
|
||||
235
docs/security_tuning.md
Normal file
235
docs/security_tuning.md
Normal file
@@ -0,0 +1,235 @@
|
||||
# Security tuning
|
||||
|
||||
bunkerized-nginx comes with a set of predefined security settings that you can (and you should) tune to meet your own use case.
|
||||
|
||||
## Miscellaneous
|
||||
|
||||
Here is a list of miscellaneous environment variables related more or less to security :
|
||||
- `MAX_CLIENT_SIZE=10m` : maximum size of client body
|
||||
- `ALLOWED_METHODS=GET|POST|HEAD` : list of HTTP methos that clients are allowed to use
|
||||
- `DISABLE_DEFAULT_SERVER=no` : enable/disable the default server (i.e. : should your server respond to unknown Host header ?)
|
||||
- `SERVER_TOKENS=off` : enable/disable sending the version number of nginx
|
||||
|
||||
## HTTPS
|
||||
|
||||
### Settings
|
||||
|
||||
Here is a list of environment variables and the corresponding default value related to HTTPS :
|
||||
- `LISTEN_HTTP=yes` : you can set it to `no` if you want to disable HTTP access
|
||||
- `REDIRECT_HTTP_TO_HTTPS=no` : enable/disable HTTP to HTTPS redirection
|
||||
- `HTTPS_PROTOCOLS=TLSv1.2 TLSv1.3` : list of TLS versions to use
|
||||
- `HTTP2=yes` : enable/disable HTTP2 when HTTPS is enabled
|
||||
- `COOKIE_AUTO_SECURE_FLAG=yes` : enable/disable adding Secure flag when HTTPS is enabled
|
||||
- `STRICT_TRANSPORT_SECURITY=max-age=31536000` : force users to visit the website in HTTPS (more info [here](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy))
|
||||
|
||||
### Let's Encrypt
|
||||
|
||||
Using Let's Encrypt with the `AUTO_LETS_ENCRYPT=yes` environment variable is the easiest way to add HTTPS supports to your web services if they are connected to internet and you have public DNS A record(s).
|
||||
|
||||
You can also set the `EMAIL_LETS_ENCRYPT` environment variable if you want to receive notifications from Let's Encrypt (e.g. : expiration).
|
||||
|
||||
### Custom certificate(s)
|
||||
|
||||
If you have security constraints (e.g : local network, custom PKI, ...) you can use custom certificates of your choice and tell bunkerized-nginx to use them with the following environment variables :
|
||||
- `USE_CUSTOM_HTTPS=yes`
|
||||
- `CUSTOM_HTTPS_CERT=/path/inside/container/to/cert.pem`
|
||||
- `CUSTOM_HTTPS_KEY=/path/inside/container/to/key.pem`
|
||||
|
||||
Here is a an example on how to use custom certificates :
|
||||
|
||||
```shell
|
||||
$ ls /etc/ssl/my-web-app
|
||||
cert.pem key.pem
|
||||
|
||||
$ docker run -p 80:8080 \
|
||||
-p 443:8443 \
|
||||
-v /etc/ssl/my-web-app:/certs:ro \
|
||||
-e USE_CUSTOM_HTTPS=yes \
|
||||
-e CUSTOM_HTTPS_CERT=/certs/cert.pem \
|
||||
-e CUSTOM_HTTPS_KEY=/certs/key.pem \
|
||||
...
|
||||
bunkerity/bunkerized-nginx
|
||||
```
|
||||
|
||||
### Self-signed certificate
|
||||
|
||||
This method is not recommended in production but can be used to quickly deploy HTTPS for testing purposes. Just use the `GENERATE_SELF_SIGNED_SSL=yes` environment variable and bunkerized-nginx will generate a self-signed certificate for you :
|
||||
```shell
|
||||
$ docker run -p 80:8080 \
|
||||
-p 443:8443 \
|
||||
-e GENERATE_SELF_SIGNED_SSL=yes \
|
||||
...
|
||||
bunkerity/bunkerized-nginx
|
||||
```
|
||||
|
||||
## Headers
|
||||
|
||||
Some important HTTP headers related to client security are sent with a default value. Sometimes it can break a web application or can be tuned to provide even more security. The complete list is available [here](https://bunkerized-nginx.readthedocs.io/en/latest/environment_variables.html#security-headers).
|
||||
|
||||
You can also remove headers (e.g. : too verbose ones) by using the `REMOVE_HEADERS` environment variable which takes a list of header name separated with space (default value = `Server X-Powered-By X-AspNet-Version X-AspNetMvc-Version`).
|
||||
|
||||
## ModSecurity
|
||||
|
||||
ModSecurity is integrated and enabled by default alongside the OWASP Core Rule Set within bunkerized-nginx. To change this behaviour you can use the `USE_MODSECURITY=no` or `USE_MODSECURITY_CRS=no` environment variables.
|
||||
|
||||
We strongly recommend to keep both ModSecurity and the OWASP Core Rule Set enabled. The only downsides are the false positives that may occur. But they can be fixed easily and the CRS team maintains a list of exclusions for common application (e.g : wordpress, nextcloud, drupal, cpanel, ...).
|
||||
|
||||
Tuning the CRS with bunkerized-nginx is pretty simple : you can add configuration before (i.e. : exclusions) and after (i.e. : exceptions/tuning) the rules are loaded. You just need to mount your .conf files into the /modsec-crs-confs (before CRS is loaded) and /modsec-confs (after CRS is loaded).
|
||||
|
||||
Here is an example to illustrate it :
|
||||
|
||||
```shell
|
||||
$ cat /data/exclusions-crs/wordpress.conf
|
||||
SecAction \
|
||||
"id:900130,\
|
||||
phase:1,\
|
||||
nolog,\
|
||||
pass,\
|
||||
t:none,\
|
||||
setvar:tx.crs_exclusions_wordpress=1"
|
||||
|
||||
$ cat /data/tuning-crs/remove-false-positives.conf
|
||||
SecRule REQUEST_FILENAME "/wp-admin/admin-ajax.php" "id:1,ctl:ruleRemoveByTag=attack-xss,ctl:ruleRemoveByTag=attack-rce"
|
||||
SecRule REQUEST_FILENAME "/wp-admin/options.php" "id:2,ctl:ruleRemoveByTag=attack-xss"
|
||||
SecRule REQUEST_FILENAME "^/wp-json/yoast" "id:3,ctl:ruleRemoveById=930120"
|
||||
|
||||
$ docker run -p 80:8080 \
|
||||
-p 443:8443 \
|
||||
-v /data/exclusions-crs:/modsec-crs-confs:ro \
|
||||
-v /data/tuning-crs:/modsec-confs:ro \
|
||||
...
|
||||
bunkerity/bunkerized-nginx
|
||||
```
|
||||
|
||||
## Bad behaviors detection
|
||||
|
||||
When attackers search for and/or exploit vulnerabilities they might generate some suspicious HTTP status codes that a "regular" user won't generate within a period of time. If we detect that kind of behavior we can ban the offending IP address and force the attacker to come with a new one.
|
||||
|
||||
That kind of security measure is implemented and enabled by default in bunkerized-nginx. Here is the list of the related environment variables and their default value :
|
||||
- `USE_BAD_BEHAVIOR=yes` : enable/disable "bad behavior" detection and automatic ban of IP
|
||||
- `BAD_BEHAVIOR_STATUS_CODES=400 401 403 404 405 429 444` : the list of HTTP status codes considered as "suspicious"
|
||||
- `BAD_BEHAVIOR_THRESHOLD=10` : the number of "suspicious" HTTP status codes required before we ban the corresponding IP address
|
||||
- `BAD_BEHAVIOR_BAN_TIME=86400` : the duration time (in seconds) of the ban
|
||||
- `BAD_BEHAVIOR_COUNT_TIME=60` : the duration time (in seconds) to wait before resetting the counter of "suspicious" HTTP status codes for a given IP
|
||||
|
||||
## Antibot challenge
|
||||
|
||||
Attackers will certainly use automated tools to exploit/find some vulnerabilities on your web service. One countermeasure is to challenge the users to detect if it looks like a bot. It might be effective against script kiddies or "lazy" attackers.
|
||||
|
||||
You can use the `USE_ANTIBOT` environment variable to add that kind of checks whenever a new client is connecting. The available challenges are : `cookie`, `javascript`, `captcha` and `recaptcha`. More info [here](https://bunkerized-nginx.readthedocs.io/en/latest/environment_variables.html#antibot).
|
||||
|
||||
## External blacklists
|
||||
|
||||
### DNSBL
|
||||
|
||||
Automatic checks on external DNS BlackLists are enabled by default with the `USE_DNSBL=yes` environment variable. The list of DNSBL zones is also configurable, you just need to edit the `DNSBL_LIST` environment variable which contains the following value by default `bl.blocklist.de problems.dnsbl.sorbs.net sbl.spamhaus.org xbl.spamhaus.org`.
|
||||
|
||||
### CrowdSec
|
||||
|
||||
CrowdSec is not enabled by default because it's more than an external blacklists and needs some extra work to get it working. But bunkerized-nginx is fully working with CrowdSec, here are the related environment variables :
|
||||
- `USE_CROWDSEC=no` : enable/disable CrowdSec checks before we authorize a client
|
||||
- `CROWDSEC_HOST=` : full URL to your CrowdSec instance API
|
||||
- `CROWDSEC_KEY=` : bouncer key given from **cscli bouncer add MyBouncer**
|
||||
|
||||
If you want to give it a try, you have a concrete example on how to use CrowdSec with bunkerized-nginx [here](https://github.com/bunkerity/bunkerized-nginx/tree/master/examples/crowdsec).
|
||||
|
||||
### User-Agents
|
||||
|
||||
Sometimes script kiddies or lazy attackers don't put a "legitimate" value inside the **User-Agent** HTTP header so we can block them. This is controlled with the `BLOCK_USER_AGENT=yes` environment variable. The blacklist is composed of two files from [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).
|
||||
|
||||
If a legitimate User-Agent is blacklisted, you can use the `WHITELIST_USER_AGENT` while still keeping the `BLOCK_USER_AGENT=yes` (more info [here](https://bunkerized-nginx.readthedocs.io/en/latest/environment_variables.html#custom-whitelisting)).
|
||||
|
||||
### TOR exit nodes
|
||||
|
||||
Blocking TOR exit nodes might not be a good decision depending on your use case. We decided to enable it by default with the `BLOCK_TOR_EXIT_NODE=yes` environment variable. If privacy is a concern for you and/or your clients, you can override the default value (i.e : `BLOCK_TOR_EXIT_NODE=no`).
|
||||
|
||||
Please note that you have a concrete example on how to use bunkerized-nginx with a .onion hidden service [here](https://github.com/bunkerity/bunkerized-nginx/tree/master/examples/tor-hidden-service).
|
||||
|
||||
### Proxies
|
||||
|
||||
This list contains IP addresses and networks known to be open proxies (downloaded from [here](https://iplists.firehol.org/files/firehol_proxies.netset)). Unless privacy is important for you and/or your clients, you should keep the default environment variable `BLOCK_PROXIES=yes`.
|
||||
|
||||
### Abusers
|
||||
|
||||
This list contains IP addresses and networks known to be abusing (downloaded from [here](https://iplists.firehol.org/files/firehol_abusers_30d.netset)). You can control this feature with the `BLOCK_ABUSERS` environment variable (default : `yes`).
|
||||
|
||||
### Referrers
|
||||
|
||||
This list contains bad referrers domains known for spamming (downloaded from [here](https://raw.githubusercontent.com/mitchellkrogza/nginx-ultimate-bad-bot-blocker/master/_generator_lists/bad-referrers.list)). If one value is found inside the **Referer** HTTP header, request will be blocked. You can control this feature with the `BLOCK_REFERRER` environment variable (default = `yes`).
|
||||
|
||||
## Limiting
|
||||
|
||||
### Requests
|
||||
|
||||
To limit bruteforce attacks we decided to use the [rate limiting feature in nginx](https://www.nginx.com/blog/rate-limiting-nginx/) so attackers will be limited to X request(s)/s for the same resource. That kind of protection might be useful against other attacks too (e.g. : blind SQL injection).
|
||||
|
||||
Here is the list of related environment variables and their default value :
|
||||
- `USE_LIMIT_REQ=yes` : enable/disable request limiting
|
||||
- `LIMIT_REQ_RATE=1r/s` : the rate to apply for the same resource
|
||||
- `LIMIT_REQ_BURST=2` : the number of request tu put in a queue before effectively rejecting requests
|
||||
|
||||
### Connections
|
||||
|
||||
Opening too many connections from the same IP address might be considered as suspicious (unless it's a shared IP and everyone is sending requests to your web service). It can be a dos/ddos attempt too. Bunkerized-nginx levarages the [ngx_http_conn_module](http://nginx.org/en/docs/http/ngx_http_limit_conn_module.html) from nginx to prevent users opening too many connections.
|
||||
|
||||
Here is the list of related environment variables and their default value :
|
||||
- `USE_LIMIT_CONN=yes` : enable disable connection limiting
|
||||
- `LIMIT_CONN_MAX=50` : maximum number of connections per IP
|
||||
|
||||
## Country
|
||||
|
||||
If the location of your clients is known, you may want to add another security layer by whitelisting or blacklisting some countries. You can use the `BLACKLIST_COUNTRY` or `WHITELIST_COUNTRY` environment variables depending on your approach. They both take a list of 2 letters country code separated with space.
|
||||
|
||||
## Authentication
|
||||
|
||||
You can quickly protect sensitive resources (e.g. : admin panels) by requiring HTTP authentication. Here is the list of related environment variables and their default value :
|
||||
- `USE_AUTH_BASIC=no` : enable/disable auth basic
|
||||
- `AUTH_BASIC_LOCATION=sitewide` : location of the sensitive resource (e.g. `/admin`) or `sitewide` to force authentication on the whole service
|
||||
- `AUTH_BASIC_USER=changeme` : the username required
|
||||
- `AUTH_BASIC_PASSWORD=changeme` : the password required
|
||||
- `AUTH_BASIC_TEXT=Restricted area` : the text that will be displayed to the user
|
||||
|
||||
## Whitelisting
|
||||
|
||||
Adding extra security can sometimes trigger false positives. Also, it might be not useful to do the security checks for specific clients because we decided to trust them. Bunkerized-nginx supports two types of whitelist : by IP address and by reverse DNS.
|
||||
|
||||
Here is the list of related environment variables and their default value :
|
||||
- `USE_WHITELIST_IP=yes` : enable/disable whitelisting by IP address
|
||||
- `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` : list of IP addresses and/or network CIDR blocks to whitelist (default contains the IP addresses of the [DuckDuckGo crawler](https://help.duckduckgo.com/duckduckgo-help-pages/results/duckduckbot/))
|
||||
- `USE_WHITELIST_REVERSE=yes` : enable/disable whitelisting by reverse DNS
|
||||
- `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` : the list of reverse DNS suffixes to trust (default contains the list of major search engines crawlers)
|
||||
|
||||
## Blacklisting
|
||||
|
||||
Sometimes it isn't necessary to spend some resources for a particular client because we know for sure that he is malicious. Bunkerized-nginx nginx supports two types of blacklisting : by IP address and by reverse DNS.
|
||||
|
||||
Here is the list of related environment variables and their default value :
|
||||
- `USE_BLACKLIST_IP=yes` : enable/disable blacklisting by IP address
|
||||
- `BLACKLIST_IP_LIST=` : list of IP addresses and/or network CIDR blocks to blacklist
|
||||
- `USE_BLACKLIST_REVERSE=yes` : enable/disable blacklisting by reverse DNS
|
||||
- `BLACKLIST_REVERSE_LIST=.shodan.io` : the list of reverse DNS suffixes to never trust
|
||||
|
||||
## Container hardening
|
||||
|
||||
### Drop capabilities
|
||||
By default, *bunkerized-nginx* runs as non-root user inside the container and should not use any of the default [capabilities](https://docs.docker.com/engine/security/#linux-kernel-capabilities) allowed by Docker. You can safely remove all capabilities to harden the container :
|
||||
|
||||
```shell
|
||||
docker run ... --drop-cap=all ... bunkerity/bunkerized-nginx
|
||||
```
|
||||
|
||||
### User namespace remap
|
||||
Another hardening trick is [user namespace remapping](https://docs.docker.com/engine/security/userns-remap/) : it allows you to map the UID/GID of users inside a container to another UID/GID on the host. For example, you can map the user nginx with UID/GID 101 inside the container to a non-existent user with UID/GID 100101 on the host.
|
||||
|
||||
Let's assume you have the /etc/subuid and /etc/subgid files like this :
|
||||
```
|
||||
user:100000:65536
|
||||
```
|
||||
It means that everything done inside the container will be remapped to UID/GID 100101 (100000 + 101) on the host.
|
||||
|
||||
Please note that you must set the rights on the volumes (e.g. : /etc/letsencrypt, /www, ...) according to the remapped UID/GID :
|
||||
```shell
|
||||
$ chown root:100101 /path/to/letsencrypt
|
||||
$ chmod 770 /path/to/letsencrypt
|
||||
$ docker run ... -v /path/to/letsencrypt:/etc/letsencrypt ... bunkerity/bunkerized-nginx
|
||||
```
|
||||
33
docs/troubleshooting.md
Normal file
33
docs/troubleshooting.md
Normal file
@@ -0,0 +1,33 @@
|
||||
# Troubleshooting
|
||||
|
||||
## Logs
|
||||
|
||||
When troubleshooting, the logs are your best friends. We try our best to provide user-friendly logs to help you understand what happened. Please note that we don't store the logs inside the container, they are all displayed on stdout/stderr so Docker can capture them. They can be displayed using the [docker logs](https://docs.docker.com/engine/reference/commandline/logs/) command.
|
||||
|
||||
## Permissions
|
||||
|
||||
Don't forget that bunkerized-nginx runs as an unprivileged user with UID/GID 101. Double check the permissions of files and folders for each volumes (see the [volumes list](https://bunkerized-nginx.readthedocs.io/en/latest/volumes.html)).
|
||||
|
||||
## ModSecurity
|
||||
|
||||
The OWASP Core Rule Set can sometimes leads to false positives. Here is what you can do :
|
||||
- Check if your application has exclusions rules (e.g : wordpress, nextcloud, drupal, ...)
|
||||
- Edit the matched rules to exclude some parameters, URIs, ...
|
||||
- Remove the matched rules if editing it is too much a hassle
|
||||
|
||||
Some additional resources :
|
||||
- [Wordpress example](https://github.com/bunkerity/bunkerized-nginx/tree/master/examples/wordpress)
|
||||
- [Handling false positive](https://www.netnea.com/cms/apache-tutorial-8_handling-false-positives-modsecurity-core-rule-set/)
|
||||
- [Adding exceptions and tuning](https://coreruleset.org/docs/exceptions.html)
|
||||
|
||||
## Whitelisting
|
||||
|
||||
It's a common case that a bot gets flagged as suspicious and can't access your website. Instead of disabling the corresponding security feature(s) we recommend a whitelist approach. Here is a list of environment variables you can use :
|
||||
|
||||
- `WHITELIST_IP_LIST`
|
||||
- `WHITELIST_REVERSE_LIST`
|
||||
- `WHITELIST_URI`
|
||||
- `WHITELIST_USER_AGENT`
|
||||
|
||||
More information [here](https://bunkerized-nginx.readthedocs.io/en/latest/environment_variables.html#custom-whitelisting).
|
||||
|
||||
84
docs/volumes.md
Normal file
84
docs/volumes.md
Normal file
@@ -0,0 +1,84 @@
|
||||
# Volumes list
|
||||
|
||||
Please note that bunkerized-nginx run as an unprivileged user inside the container (UID/GID = 101) and you should set the rights on the host accordingly (e.g. : chmod 101:101 ...) to the files and folders on your host.
|
||||
|
||||
## Web files
|
||||
|
||||
Mountpoint : `/www`
|
||||
|
||||
Description :
|
||||
If `MULTISITE=no`, the web files are directly stored inside the `/www` folder. When `MULTISITE=yes`, you need to create subdirectories named as the servers defined in the `SERVER_NAME` environment variable.
|
||||
|
||||
Examples : [basic](https://github.com/bunkerity/bunkerized-nginx/tree/master/examples/basic-website-with-php) and [multisite](https://github.com/bunkerity/bunkerized-nginx/tree/master/examples/multisite-basic)
|
||||
|
||||
Read-only : yes
|
||||
|
||||
## Let's Encrypt
|
||||
|
||||
Mountpoint : `/etc/letsencrypt`
|
||||
|
||||
Description :
|
||||
When `AUTO_LETS_ENCRYPT=yes`, certbot will save configurations, certificates and keys inside the `/etc/letsencrypt` folder. It's a common practise to save it so you can remount it in case of a container restart and certbot won't generate new certificate(s).
|
||||
|
||||
Examples : [here](https://github.com/bunkerity/bunkerized-nginx/tree/master/examples/basic-website-with-php)
|
||||
|
||||
Read-only : no
|
||||
|
||||
## Custom nginx configurations
|
||||
|
||||
### http context
|
||||
|
||||
Mountpoint : `/http-confs`
|
||||
|
||||
Description :
|
||||
If you need to add custom configurations at http context, you can create **.conf** files and mount them to the `/http-confs` folder.
|
||||
|
||||
Examples : [load balancer](https://github.com/bunkerity/bunkerized-nginx/tree/master/examples/load-balancer)
|
||||
|
||||
Read-only : yes
|
||||
|
||||
### server context
|
||||
|
||||
Mountpoint : `/server-confs`
|
||||
|
||||
Description :
|
||||
If `MULTISITE=no`, you can create **.conf** files and mount them to the `/server-confs` folder. When `MULTISITE=yes`, you need to create subdirectories named as the servers defined in the `SERVER_NAME` environment variable.
|
||||
|
||||
Examples : [nextcloud](https://github.com/bunkerity/bunkerized-nginx/tree/master/examples/nextcloud) and [multisite](https://github.com/bunkerity/bunkerized-nginx/tree/master/examples/multisite-custom-server-confs)
|
||||
|
||||
Read-only : yes
|
||||
|
||||
## ModSecurity
|
||||
|
||||
### Rules and before CRS
|
||||
|
||||
Mountpoint : `/modsec-confs`
|
||||
|
||||
Description :
|
||||
Use this volume if you need to add custom ModSecurity rules and/or OWASP Core Rule Set configurations before the rules are loaded (e.g. : exclusions).
|
||||
If `MULTISITE=no` you can create **.conf** files and mount them to the `/modsec-confs` folder. When `MULTISITE=yes`, you need to create subdirectories named as the servers defined in the `SERVER_NAME` environment variable. You can also apply global configuration to all servers by putting **.conf** files directly on the root folder.
|
||||
|
||||
Examples : [wordpress](https://github.com/bunkerity/bunkerized-nginx/tree/master/examples/wordpress) and [multisite](https://github.com/bunkerity/bunkerized-nginx/tree/master/examples/multisite-custom-server-confs)
|
||||
|
||||
Read-only : yes
|
||||
|
||||
### After CRS
|
||||
|
||||
Mountpoint : `/modsec-crs-confs`
|
||||
|
||||
Description :
|
||||
Use this volume to tweak OWASP Core Rule Set (e.g. : tweak rules to avoid false positives). Your files are loaded after the rules.
|
||||
If `MULTISITE=no` you can create **.conf** files and mount them to the `/modsec-crs-confs` folder. When `MULTISITE=yes`, you need to create subdirectories named as the servers defined in the `SERVER_NAME` environment variable. You can also apply global configuration to all servers by putting **.conf** files directly on the root folder.
|
||||
|
||||
Examples : [wordpress](https://github.com/bunkerity/bunkerized-nginx/tree/master/examples/wordpress) and [multisite](https://github.com/bunkerity/bunkerized-nginx/tree/master/examples/multisite-custom-server-confs)
|
||||
|
||||
Read-only : yes
|
||||
|
||||
## Cache
|
||||
|
||||
Mountpoint : `/cache`
|
||||
|
||||
Description :
|
||||
Depending of the settings you use, bunkerized-nginx may download external content (e.g. : blacklists, GeoIP DB, ...). To avoid downloading it again in case of a container restart, you can save the data on the host.
|
||||
|
||||
Read-only : no
|
||||
@@ -6,6 +6,8 @@ HTTP_PORT="${HTTP_PORT-8080}"
|
||||
HTTPS_PORT="${HTTPS_PORT-8443}"
|
||||
MAX_CLIENT_SIZE="${MAX_CLIENT_SIZE-10m}"
|
||||
SERVER_TOKENS="${SERVER_TOKENS-off}"
|
||||
WORKER_CONNECTIONS="${WORKER_CONNECTIONS-1024}"
|
||||
WORKER_RLIMIT_NOFILE="${WORKER_RLIMIT_NOFILE-2048}"
|
||||
USE_CLIENT_CACHE="${USE_CLIENT_CACHE-no}"
|
||||
CLIENT_CACHE_EXTENSIONS="${CLIENT_CACHE_EXTENSIONS-jpg|jpeg|png|bmp|ico|svg|tif|css|js|otf|ttf|eot|woff|woff2}"
|
||||
CLIENT_CACHE_CONTROL="${CLIENT_CACHE_CONTROL-public, max-age=15552000}"
|
||||
@@ -62,19 +64,18 @@ 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 allow-popups; base-uri 'self';}"
|
||||
MODSECURITY_SEC_AUDIT_ENGINE="${MODSECURITY_SEC_AUDIT_ENGINE-RelevantOnly}"
|
||||
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 allow-downloads; base-uri 'self';}"
|
||||
COOKIE_FLAGS="${COOKIE_FLAGS-* HttpOnly SameSite=Lax}"
|
||||
COOKIE_AUTO_SECURE_FLAG="${COOKIE_AUTO_SECURE_FLAG-yes}"
|
||||
SERVE_FILES="${SERVE_FILES-yes}"
|
||||
WRITE_ACCESS="${WRITE_ACCESS-no}"
|
||||
REDIRECT_HTTP_TO_HTTPS="${REDIRECT_HTTP_TO_HTTPS-no}"
|
||||
LISTEN_HTTP="${LISTEN_HTTP-yes}"
|
||||
USE_FAIL2BAN="${USE_FAIL2BAN-yes}"
|
||||
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_BAD_BEHAVIOR="${USE_BAD_BEHAVIOR-yes}"
|
||||
BAD_BEHAVIOR_STATUS_CODES="${BAD_BEHAVIOR_STATUS_CODES-400 401 403 404 405 429 444}"
|
||||
BAD_BEHAVIOR_THRESHOLD="${BAD_BEHAVIOR_THRESHOLD-10}"
|
||||
BAD_BEHAVIOR_BAN_TIME="${BAD_BEHAVIOR_BAN_TIME-86400}"
|
||||
BAD_BEHAVIOR_COUNT_TIME="${BAD_BEHAVIOR_COUNT_TIME-60}"
|
||||
USE_CLAMAV_UPLOAD="${USE_CLAMAV_UPLOAD-yes}"
|
||||
USE_CLAMAV_SCAN="${USE_CLAMAV_SCAN-yes}"
|
||||
USE_CLAMAV_SCAN_CRON="${USE_CLAMAV_SCAN_CRON-30 1 * * *}"
|
||||
@@ -87,9 +88,7 @@ AUTH_BASIC_USER="${AUTH_BASIC_USER-changeme}"
|
||||
AUTH_BASIC_PASSWORD="${AUTH_BASIC_PASSWORD-changeme}"
|
||||
USE_CUSTOM_HTTPS="${USE_CUSTOM_HTTPS-no}"
|
||||
ROOT_FOLDER="${ROOT_FOLDER-/www}"
|
||||
LOGROTATE_MINSIZE="${LOGROTATE_MINSIZE-10M}"
|
||||
LOGROTATE_MAXAGE="${LOGROTATE_MAXAGE-7}"
|
||||
LOGROTATE_CRON="${LOGROTATE_CRON-0 0 * * *}"
|
||||
ROOT_SITE_SUBFOLDER="${ROOT_SITE_SUBFOLDER-}"
|
||||
DNS_RESOLVERS="${DNS_RESOLVERS-127.0.0.11}"
|
||||
USE_WHITELIST_IP="${USE_WHITELIST_IP-yes}"
|
||||
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}"
|
||||
@@ -102,11 +101,11 @@ BLACKLIST_REVERSE_LIST="${BLACKLIST_REVERSE_LIST-.shodan.io}"
|
||||
USE_DNSBL="${USE_DNSBL-yes}"
|
||||
DNSBL_LIST="${DNSBL_LIST-bl.blocklist.de problems.dnsbl.sorbs.net sbl.spamhaus.org xbl.spamhaus.org}"
|
||||
USE_LIMIT_REQ="${USE_LIMIT_REQ-yes}"
|
||||
LIMIT_REQ_RATE="${LIMIT_REQ_RATE-20r/s}"
|
||||
LIMIT_REQ_BURST="${LIMIT_REQ_BURST-40}"
|
||||
LIMIT_REQ_RATE="${LIMIT_REQ_RATE-1r/s}"
|
||||
LIMIT_REQ_BURST="${LIMIT_REQ_BURST-2}"
|
||||
LIMIT_REQ_CACHE="${LIMIT_REQ_CACHE-10m}"
|
||||
USE_LIMIT_CONN="${USE_LIMIT_CONN-yes}"
|
||||
LIMIT_CONN_MAX="${LIMIT_CONN_MAX-40}"
|
||||
LIMIT_CONN_MAX="${LIMIT_CONN_MAX-50}"
|
||||
LIMIT_CONN_CACHE="${LIMIT_CONN_CACHE-10m}"
|
||||
PROXY_REAL_IP="${PROXY_REAL_IP-no}"
|
||||
PROXY_REAL_IP_FROM="${PROXY_REAL_IP_FROM-192.168.0.0/16 172.16.0.0/12 10.0.0.0/8}"
|
||||
@@ -128,4 +127,6 @@ ANTIBOT_SESSION_SECRET="${ANTIBOT_SESSION_SECRET-random}"
|
||||
USE_CROWDSEC="${USE_CROWDSEC-no}"
|
||||
USE_API="${USE_API-no}"
|
||||
API_URI="${API_URI-random}"
|
||||
API_WHITELIST_IP="${API_WHITELIST_IP-192.168.0.0/16 172.16.0.0/12 10.0.0.0/8}"
|
||||
SWARM_MODE="${SWARM_MODE-no}"
|
||||
|
||||
|
||||
@@ -5,25 +5,13 @@
|
||||
|
||||
echo "[*] Starting bunkerized-nginx ..."
|
||||
|
||||
# execute custom scripts if it's a customized image
|
||||
for file in /entrypoint.d/* ; do
|
||||
[ -f "$file" ] && [ -x "$file" ] && "$file"
|
||||
done
|
||||
|
||||
# trap SIGTERM and SIGINT
|
||||
function trap_exit() {
|
||||
echo "[*] Catched stop operation"
|
||||
echo "[*] Stopping crond ..."
|
||||
pkill -TERM crond
|
||||
if [ "$USE_FAIL2BAN" = "yes" ] ; then
|
||||
echo "[*] Stopping fail2ban"
|
||||
fail2ban-client stop > /dev/null
|
||||
fi
|
||||
echo "[*] Stopping nginx ..."
|
||||
/usr/sbin/nginx -s stop
|
||||
echo "[*] Stopping rsyslogd ..."
|
||||
pkill -TERM rsyslogd
|
||||
pkill -TERM tail
|
||||
}
|
||||
trap "trap_exit" TERM INT QUIT
|
||||
|
||||
@@ -52,16 +40,20 @@ if [ ! -f "/opt/installed" ] ; then
|
||||
|
||||
echo "[*] Configuring bunkerized-nginx ..."
|
||||
|
||||
# logs config
|
||||
/opt/entrypoint/logs.sh
|
||||
# check permissions
|
||||
if [ "$SWARM_MODE" = "no" ] ; then
|
||||
/opt/entrypoint/permissions.sh
|
||||
else
|
||||
/opt/entrypoint/permissions-swarm.sh
|
||||
fi
|
||||
if [ "$?" -ne 0 ] ; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# lua config
|
||||
# TODO : move variables from /usr/local/lib/lua + multisite support ?
|
||||
/opt/entrypoint/lua.sh
|
||||
|
||||
# fail2ban config
|
||||
/opt/entrypoint/fail2ban.sh
|
||||
|
||||
# clamav config
|
||||
/opt/entrypoint/clamav.sh
|
||||
|
||||
@@ -93,9 +85,6 @@ else
|
||||
echo "[*] Skipping configuration process"
|
||||
fi
|
||||
|
||||
# start rsyslogd
|
||||
rsyslogd -i /tmp/rsyslogd.pid
|
||||
|
||||
# start crond
|
||||
crond
|
||||
|
||||
@@ -114,22 +103,8 @@ fi
|
||||
|
||||
# run nginx
|
||||
echo "[*] Running nginx ..."
|
||||
nginx
|
||||
if [ "$?" -eq 0 ] ; then
|
||||
echo "[*] nginx successfully started !"
|
||||
else
|
||||
echo "[!] nginx failed to start"
|
||||
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
|
||||
LOGS="$LOGS /var/log/fail2ban.log"
|
||||
fi
|
||||
nginx &
|
||||
pid="$!"
|
||||
|
||||
# autotest
|
||||
if [ "$1" == "test" ] ; then
|
||||
@@ -142,9 +117,8 @@ if [ "$1" == "test" ] ; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# display logs
|
||||
tail -F $LOGS &
|
||||
pid="$!"
|
||||
# wait for nginx
|
||||
wait "$pid"
|
||||
while [ -f "/tmp/nginx.pid" ] ; do
|
||||
wait "$pid"
|
||||
done
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# load default values
|
||||
. /opt/entrypoint/defaults.sh
|
||||
|
||||
# load some functions
|
||||
. /opt/entrypoint/utils.sh
|
||||
|
||||
# fail2ban setup
|
||||
if [ "$(has_value USE_FAIL2BAN yes)" != "" ] ; then
|
||||
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
|
||||
cp /opt/fail2ban/nginx-jail.local /etc/fail2ban/jail.d/nginx-jail.local
|
||||
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
|
||||
@@ -10,14 +10,18 @@
|
||||
cp /opt/confs/global/* /etc/nginx/
|
||||
|
||||
# include server block(s)
|
||||
if [ "$MULTISITE" = "yes" ] ; then
|
||||
includes=""
|
||||
for server in $SERVER_NAME ; do
|
||||
includes="${includes}include /etc/nginx/${server}/server.conf;\n"
|
||||
done
|
||||
replace_in_file "/etc/nginx/nginx.conf" "%INCLUDE_SERVER%" "$includes"
|
||||
if [ "$SWARM_MODE" = "no" ] ; then
|
||||
if [ "$MULTISITE" = "yes" ] ; then
|
||||
includes=""
|
||||
for server in $SERVER_NAME ; do
|
||||
includes="${includes}include /etc/nginx/${server}/server.conf;\n"
|
||||
done
|
||||
replace_in_file "/etc/nginx/nginx.conf" "%INCLUDE_SERVER%" "$includes"
|
||||
else
|
||||
replace_in_file "/etc/nginx/nginx.conf" "%INCLUDE_SERVER%" "include /etc/nginx/server.conf;"
|
||||
fi
|
||||
else
|
||||
replace_in_file "/etc/nginx/nginx.conf" "%INCLUDE_SERVER%" "include /etc/nginx/server.conf;"
|
||||
replace_in_file "/etc/nginx/nginx.conf" "%INCLUDE_SERVER%" ""
|
||||
fi
|
||||
|
||||
# setup default server block if multisite
|
||||
@@ -109,33 +113,52 @@ fi
|
||||
|
||||
# block bad UA
|
||||
if [ "$(has_value BLOCK_USER_AGENT yes)" != "" ] ; then
|
||||
replace_in_file "/etc/nginx/nginx.conf" "%BLOCK_USER_AGENT%" "include /etc/nginx/map-user-agent.conf;"
|
||||
echo "$BLOCK_USER_AGENT_CRON /opt/scripts/user-agents.sh" >> /etc/crontabs/nginx
|
||||
replace_in_file "/etc/nginx/nginx.conf" "%BLOCK_USER_AGENTS%" "lua_shared_dict user_agents_data 1m; lua_shared_dict user_agents_cache 10m;"
|
||||
replace_in_file "/etc/nginx/init-lua.conf" "%USE_USER_AGENTS%" "true"
|
||||
else
|
||||
replace_in_file "/etc/nginx/nginx.conf" "%BLOCK_USER_AGENT%" ""
|
||||
replace_in_file "/etc/nginx/nginx.conf" "%BLOCK_USER_AGENTS%" ""
|
||||
replace_in_file "/etc/nginx/init-lua.conf" "%USE_USER_AGENTS%" "false"
|
||||
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 "$BLOCK_REFERRER_CRON /opt/scripts/referrers.sh" >> /etc/crontabs/nginx
|
||||
replace_in_file "/etc/nginx/nginx.conf" "%BLOCK_REFERRERS%" "lua_shared_dict referrers_data 1m; lua_shared_dict referrers_cache 10m;"
|
||||
replace_in_file "/etc/nginx/init-lua.conf" "%USE_REFERRERS%" "true"
|
||||
else
|
||||
replace_in_file "/etc/nginx/nginx.conf" "%BLOCK_REFERRER%" ""
|
||||
replace_in_file "/etc/nginx/nginx.conf" "%BLOCK_REFERRERS%" ""
|
||||
replace_in_file "/etc/nginx/init-lua.conf" "%USE_REFERRERS%" "false"
|
||||
fi
|
||||
|
||||
# block TOR exit nodes
|
||||
if [ "$(has_value BLOCK_TOR_EXIT_NODE yes)" != "" ] ; then
|
||||
echo "$BLOCK_TOR_EXIT_NODE_CRON /opt/scripts/exit-nodes.sh" >> /etc/crontabs/nginx
|
||||
replace_in_file "/etc/nginx/nginx.conf" "%BLOCK_TOR_EXIT_NODES%" "lua_shared_dict tor_exit_nodes_data 1m;"
|
||||
replace_in_file "/etc/nginx/init-lua.conf" "%USE_TOR_EXIT_NODES%" "true"
|
||||
else
|
||||
replace_in_file "/etc/nginx/nginx.conf" "%BLOCK_TOR_EXIT_NODES%" ""
|
||||
replace_in_file "/etc/nginx/init-lua.conf" "%USE_TOR_EXIT_NODES%" "false"
|
||||
fi
|
||||
|
||||
# block proxies
|
||||
if [ "$(has_value BLOCK_PROXIES yes)" != "" ] ; then
|
||||
echo "$BLOCK_PROXIES_CRON /opt/scripts/proxies.sh" >> /etc/crontabs/nginx
|
||||
replace_in_file "/etc/nginx/nginx.conf" "%BLOCK_PROXIES%" "lua_shared_dict proxies_data 250m;"
|
||||
replace_in_file "/etc/nginx/init-lua.conf" "%USE_PROXIES%" "true"
|
||||
else
|
||||
replace_in_file "/etc/nginx/nginx.conf" "%BLOCK_PROXIES%" ""
|
||||
replace_in_file "/etc/nginx/init-lua.conf" "%USE_PROXIES%" "false"
|
||||
fi
|
||||
|
||||
# block abusers
|
||||
if [ "$(has_value BLOCK_ABUSERS yes)" != "" ] ; then
|
||||
echo "$BLOCK_ABUSERS_CRON /opt/scripts/abusers.sh" >> /etc/crontabs/nginx
|
||||
replace_in_file "/etc/nginx/nginx.conf" "%BLOCK_ABUSERS%" "lua_shared_dict abusers_data 50m;"
|
||||
replace_in_file "/etc/nginx/init-lua.conf" "%USE_ABUSERS%" "true"
|
||||
else
|
||||
replace_in_file "/etc/nginx/nginx.conf" "%BLOCK_ABUSERS%" ""
|
||||
replace_in_file "/etc/nginx/init-lua.conf" "%USE_ABUSERS%" "false"
|
||||
fi
|
||||
|
||||
# DNS resolvers
|
||||
@@ -171,7 +194,7 @@ fi
|
||||
|
||||
# request limiting
|
||||
if [ "$(has_value USE_LIMIT_REQ yes)" != "" ] ; then
|
||||
replace_in_file "/etc/nginx/nginx.conf" "%LIMIT_REQ_ZONE%" "limit_req_zone \$binary_remote_addr zone=limit:${LIMIT_REQ_CACHE} rate=${LIMIT_REQ_RATE};"
|
||||
replace_in_file "/etc/nginx/nginx.conf" "%LIMIT_REQ_ZONE%" "limit_req_zone \$binary_remote_addr\$uri zone=limit:${LIMIT_REQ_CACHE} rate=${LIMIT_REQ_RATE};"
|
||||
else
|
||||
replace_in_file "/etc/nginx/nginx.conf" "%LIMIT_REQ_ZONE%" ""
|
||||
fi
|
||||
@@ -197,9 +220,11 @@ 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
|
||||
# bad behavior
|
||||
if [ "$(has_value USE_BAD_BEHAVIOR yes)" != "" ] ; then
|
||||
replace_in_file "/etc/nginx/nginx.conf" "%BAD_BEHAVIOR%" "lua_shared_dict behavior_ban 10m;\nlua_shared_dict behavior_count 10m;"
|
||||
else
|
||||
replace_in_file "/etc/nginx/nginx.conf" "%BAD_BEHAVIOR%" ""
|
||||
fi
|
||||
|
||||
# CrowdSec setup
|
||||
@@ -220,3 +245,7 @@ if [ "$USE_API" = "yes" ] ; then
|
||||
else
|
||||
replace_in_file "/etc/nginx/nginx.conf" "%USE_API%" ""
|
||||
fi
|
||||
|
||||
# performance tuning
|
||||
replace_in_file "/etc/nginx/nginx.conf" "%WORKER_CONNECTIONS%" "$WORKER_CONNECTIONS"
|
||||
replace_in_file "/etc/nginx/nginx.conf" "%WORKER_RLIMIT_NOFILE%" "$WORKER_RLIMIT_NOFILE"
|
||||
|
||||
@@ -19,9 +19,9 @@ fi
|
||||
|
||||
# User-Agents
|
||||
if [ "$(has_value BLOCK_USER_AGENT yes)" != "" ] ; then
|
||||
if [ -f "/cache/map-user-agent.conf" ] ; then
|
||||
echo "[*] Copying cached map-user-agent.conf ..."
|
||||
cp /cache/map-user-agent.conf /etc/nginx/map-user-agent.conf
|
||||
if [ -f "/cache/user-agents.list" ] ; then
|
||||
echo "[*] Copying cached user-agents.list ..."
|
||||
cp /cache/user-agents.list /etc/nginx/user-agents.list
|
||||
else
|
||||
echo "[*] Downloading bad user-agent list (in background) ..."
|
||||
/opt/scripts/user-agents.sh > /dev/null 2>&1 &
|
||||
@@ -30,9 +30,9 @@ fi
|
||||
|
||||
# Referrers
|
||||
if [ "$(has_value BLOCK_REFERRER yes)" != "" ] ; then
|
||||
if [ -f "/cache/map-referrer.conf" ] ; then
|
||||
echo "[*] Copying cached map-referrer.conf ..."
|
||||
cp /cache/map-referrer.conf /etc/nginx/map-referrer.conf
|
||||
if [ -f "/cache/referrers.list" ] ; then
|
||||
echo "[*] Copying cached referrers.list ..."
|
||||
cp /cache/referrers.list /etc/nginx/referrers.list
|
||||
else
|
||||
echo "[*] Downloading bad referrer list (in background) ..."
|
||||
/opt/scripts/referrers.sh > /dev/null 2>&1 &
|
||||
@@ -41,9 +41,9 @@ fi
|
||||
|
||||
# exit nodes
|
||||
if [ "$(has_value BLOCK_TOR_EXIT_NODE yes)" != "" ] ; then
|
||||
if [ -f "/cache/block-tor-exit-node.conf" ] ; then
|
||||
echo "[*] Copying cached block-tor-exit-node.conf ..."
|
||||
cp /cache/block-tor-exit-node.conf /etc/nginx/block-tor-exit-node.conf
|
||||
if [ -f "/cache/tor-exit-nodes.list" ] ; then
|
||||
echo "[*] Copying cached tor-exit-nodes.list ..."
|
||||
cp /cache/tor-exit-nodes.list /etc/nginx/tor-exit-nodes.list
|
||||
else
|
||||
echo "[*] Downloading tor exit nodes list (in background) ..."
|
||||
/opt/scripts/exit-nodes.sh > /dev/null 2>&1 &
|
||||
@@ -52,9 +52,9 @@ fi
|
||||
|
||||
# proxies
|
||||
if [ "$(has_value BLOCK_PROXIES yes)" != "" ] ; then
|
||||
if [ -f "/cache/block-proxies.conf" ] ; then
|
||||
echo "[*] Copying cached block-proxies.conf ..."
|
||||
cp /cache/block-proxies.conf /etc/nginx/block-proxies.conf
|
||||
if [ -f "/cache/proxies.list" ] ; then
|
||||
echo "[*] Copying cached proxies.list ..."
|
||||
cp /cache/proxies.list /etc/nginx/proxies.list
|
||||
else
|
||||
echo "[*] Downloading proxies list (in background) ..."
|
||||
/opt/scripts/proxies.sh > /dev/null 2>&1 &
|
||||
@@ -63,9 +63,9 @@ fi
|
||||
|
||||
# abusers
|
||||
if [ "$(has_value BLOCK_ABUSERS yes)" != "" ] ; then
|
||||
if [ -f "/cache/block-abusers.conf" ] ; then
|
||||
echo "[*] Copying cached block-abusers.conf ..."
|
||||
cp /cache/block-abusers.conf /etc/nginx/block-abusers.conf
|
||||
if [ -f "/cache/abusers.list" ] ; then
|
||||
echo "[*] Copying cached abusers.list ..."
|
||||
cp /cache/abusers.list /etc/nginx/abusers.list
|
||||
else
|
||||
echo "[*] Downloading abusers list (in background) ..."
|
||||
/opt/scripts/abusers.sh > /dev/null 2>&1 &
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# load default values
|
||||
. /opt/entrypoint/defaults.sh
|
||||
|
||||
# load some functions
|
||||
. /opt/entrypoint/utils.sh
|
||||
|
||||
# copy stub confs
|
||||
cat /opt/logs/rsyslog.conf > /etc/rsyslog.conf
|
||||
cat /opt/logs/logrotate.conf > /etc/logrotate.conf
|
||||
|
||||
# create empty logs
|
||||
touch /var/log/access.log
|
||||
touch /var/log/error.log
|
||||
touch /var/log/jobs.log
|
||||
|
||||
# setup logrotate
|
||||
replace_in_file "/etc/logrotate.conf" "%LOGROTATE_MAXAGE%" "$LOGROTATE_MAXAGE"
|
||||
replace_in_file "/etc/logrotate.conf" "%LOGROTATE_MINSIZE%" "$LOGROTATE_MINSIZE"
|
||||
echo "$LOGROTATE_CRON /opt/scripts/logrotate.sh > /dev/null 2>&1" >> /etc/crontabs/nginx
|
||||
@@ -33,8 +33,21 @@ replace_in_file "/usr/local/lib/lua/blacklist.lua" "%BLACKLIST_REVERSE_LIST%" "$
|
||||
list=$(spaces_to_lua "$DNSBL_LIST")
|
||||
replace_in_file "/usr/local/lib/lua/dnsbl.lua" "%DNSBL_LIST%" "$list"
|
||||
|
||||
# bad behavior
|
||||
list=$(spaces_to_lua "$BAD_BEHAVIOR_STATUS_CODES")
|
||||
replace_in_file "/usr/local/lib/lua/behavior.lua" "%STATUS_CODES%" "$list"
|
||||
replace_in_file "/usr/local/lib/lua/behavior.lua" "%THRESHOLD%" "$BAD_BEHAVIOR_THRESHOLD"
|
||||
replace_in_file "/usr/local/lib/lua/behavior.lua" "%BAN_TIME%" "$BAD_BEHAVIOR_BAN_TIME"
|
||||
replace_in_file "/usr/local/lib/lua/behavior.lua" "%COUNT_TIME%" "$BAD_BEHAVIOR_COUNT_TIME"
|
||||
|
||||
# CrowdSec setup
|
||||
if [ "$(has_value USE_CROWDSEC yes)" != "" ] ; then
|
||||
replace_in_file "/usr/local/lib/lua/crowdsec/crowdsec.conf" "%CROWDSEC_HOST%" "$CROWDSEC_HOST"
|
||||
replace_in_file "/usr/local/lib/lua/crowdsec/crowdsec.conf" "%CROWDSEC_KEY%" "$CROWDSEC_KEY"
|
||||
fi
|
||||
|
||||
# Whitelist IP for API
|
||||
if [ "$USE_API" = "yes" ] ; then
|
||||
list=$(spaces_to_lua "$API_WHITELIST_IP")
|
||||
replace_in_file "/usr/local/lib/lua/api.lua" "%API_WHITELIST_IP%" "$list"
|
||||
fi
|
||||
|
||||
@@ -14,7 +14,8 @@ if [ "$MULTISITE" = "yes" ] ; then
|
||||
fi
|
||||
SERVER_PREFIX="/etc/nginx/${server}/"
|
||||
if grep "/etc/letsencrypt/live" ${SERVER_PREFIX}https.conf > /dev/null && [ ! -f /etc/letsencrypt/live/${server}/fullchain.pem ] ; then
|
||||
/opt/scripts/certbot-new.sh "$server" "$(cat ${SERVER_PREFIX}email-lets-encrypt.txt)"
|
||||
domains=$(cat ${SERVER_PREFIX}server.conf | sed -nE 's/^.*server_name (.*);$/\1/p' | sed "s/ /,/g")
|
||||
/opt/scripts/certbot-new.sh "$domains" "$(cat ${SERVER_PREFIX}email-lets-encrypt.txt)"
|
||||
fi
|
||||
if grep "modsecurity.conf" ${SERVER_PREFIX}server.conf > /dev/null ; then
|
||||
modsec_custom=""
|
||||
|
||||
25
entrypoint/permissions-swarm.sh
Normal file
25
entrypoint/permissions-swarm.sh
Normal file
@@ -0,0 +1,25 @@
|
||||
#!/bin/bash
|
||||
|
||||
# /etc/letsencrypt
|
||||
if [ ! -r "/etc/letsencrypt" ] || [ ! -x "/etc/letsencrypt" ] ; then
|
||||
echo "[!] WARNING - wrong permissions on /etc/letsencrypt"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# /www
|
||||
if [ ! -r "/www" ] || [ ! -x "/www" ] ; then
|
||||
echo "[!] ERROR - wrong permissions on /www"
|
||||
exit 2
|
||||
fi
|
||||
|
||||
# /etc/nginx
|
||||
if [ ! -r "/etc/nginx" ] || [ ! -x "/etc/nginx" ] ; then
|
||||
echo "[!] ERROR - wrong permissions on /etc/nginx"
|
||||
exit 3
|
||||
fi
|
||||
|
||||
# /acme-challenge
|
||||
if [ ! -r "/acme-challenge" ] || [ ! -x "/acme-challenge" ] ; then
|
||||
echo "[!] ERROR - wrong permissions on /acme-challenge"
|
||||
exit 4
|
||||
fi
|
||||
29
entrypoint/permissions.sh
Normal file
29
entrypoint/permissions.sh
Normal file
@@ -0,0 +1,29 @@
|
||||
#!/bin/bash
|
||||
|
||||
# /etc/letsencrypt
|
||||
if [ ! -w "/etc/letsencrypt" ] || [ ! -r "/etc/letsencrypt" ] || [ ! -x "/etc/letsencrypt" ] ; then
|
||||
echo "[!] WARNING - wrong permissions on /etc/letsencrypt"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -f "/usr/sbin/nginx" ] ; then
|
||||
# /www
|
||||
if [ ! -r "/www" ] || [ ! -x "/www" ] ; then
|
||||
echo "[!] ERROR - wrong permissions on /www"
|
||||
exit 2
|
||||
fi
|
||||
|
||||
fi
|
||||
|
||||
# /acme-challenge
|
||||
if [ ! -w "/acme-challenge" ] || [ ! -r "/acme-challenge" ] || [ ! -x "/acme-challenge" ] ; then
|
||||
echo "[!] ERROR - wrong permissions on /acme-challenge"
|
||||
exit 3
|
||||
fi
|
||||
|
||||
# /etc/nginx
|
||||
if [ ! -w "/etc/nginx" ] || [ ! -r "/etc/nginx" ] || [ ! -x "/etc/nginx" ] ; then
|
||||
echo "[!] ERROR - wrong permissions on /etc/nginx"
|
||||
exit 4
|
||||
fi
|
||||
|
||||
@@ -9,37 +9,39 @@
|
||||
# get nginx path and override multisite variables
|
||||
NGINX_PREFIX="/etc/nginx/"
|
||||
if [ "$MULTISITE" = "yes" ] ; then
|
||||
NGINX_PREFIX="${NGINX_PREFIX}${1}/"
|
||||
first_server=$(echo "$1" | cut -d ' ' -f 1)
|
||||
NGINX_PREFIX="${NGINX_PREFIX}${first_server}/"
|
||||
if [ ! -d "$NGINX_PREFIX" ] ; then
|
||||
mkdir "$NGINX_PREFIX"
|
||||
fi
|
||||
ROOT_FOLDER="${ROOT_FOLDER}/$1"
|
||||
fi
|
||||
|
||||
if [ "$MULTISITE" = "yes" ] ; then
|
||||
for var in $(env | cut -d '=' -f 1 | grep -E "^${1}_") ; do
|
||||
repl_name=$(echo "$var" | sed "s~${1}_~~")
|
||||
ROOT_FOLDER="${ROOT_FOLDER}/$first_server"
|
||||
for var in $(env | cut -d '=' -f 1 | grep -E "^${first_server}_") ; do
|
||||
repl_name=$(echo "$var" | sed "s~${first_server}_~~")
|
||||
repl_value=$(env | grep -E "^${var}=" | sed "s~^${var}=~~")
|
||||
read -r "$repl_name" <<< $repl_value
|
||||
done
|
||||
fi
|
||||
|
||||
if [ "$ROOT_SITE_SUBFOLDER" != "" ] ; then
|
||||
ROOT_FOLDER="${ROOT_FOLDER}/${ROOT_SITE_SUBFOLDER}"
|
||||
fi
|
||||
|
||||
set | grep -E -v "^(HOSTNAME|PWD|PKG_RELEASE|NJS_VERSION|SHLVL|PATH|_|NGINX_VERSION|HOME)=" > "${NGINX_PREFIX}nginx.env"
|
||||
if [ "$MULTISITE" = "yes" ] ; then
|
||||
for server in $SERVER_NAME ; do
|
||||
sed -i "~^${server}_.*=.*~d" "${NGINX_PREFIX}nginx.env"
|
||||
sed -i "/^${server}_.*=.*/d" "${NGINX_PREFIX}nginx.env"
|
||||
done
|
||||
sed -i "~^SERVER_NAME=.*~SERVER_NAME=${1}~" "${NGINX_PREFIX}nginx.env"
|
||||
sed -i "s~^SERVER_NAME=.*~SERVER_NAME=${1}~" "${NGINX_PREFIX}nginx.env"
|
||||
fi
|
||||
|
||||
# copy stub confs
|
||||
cp /opt/confs/site/* "$NGINX_PREFIX"
|
||||
|
||||
# replace paths
|
||||
replace_in_file "${NGINX_PREFIX}server.conf" "%MAIN_LUA%" "include ${NGINX_PREFIX}main-lua.conf;"
|
||||
replace_in_file "${NGINX_PREFIX}server.conf" "%INCLUDE_LUA%" "include ${NGINX_PREFIX}main-lua.conf;\ninclude ${NGINX_PREFIX}log-lua.conf;"
|
||||
if [ "$MULTISITE" = "yes" ] ; then
|
||||
replace_in_file "${NGINX_PREFIX}server.conf" "%SERVER_CONF%" "include /server-confs/*.conf;\ninclude /server-confs/${1}/*.conf;"
|
||||
replace_in_file "${NGINX_PREFIX}server.conf" "%PRE_SERVER_CONF%" "include /pre-server-confs/*.conf;\ninclude /pre-server-confs/${1}/*.conf;"
|
||||
replace_in_file "${NGINX_PREFIX}server.conf" "%SERVER_CONF%" "include /server-confs/*.conf;\ninclude /server-confs/${first_server}/*.conf;"
|
||||
replace_in_file "${NGINX_PREFIX}server.conf" "%PRE_SERVER_CONF%" "include /pre-server-confs/*.conf;\ninclude /pre-server-confs/${first_server}/*.conf;"
|
||||
else
|
||||
replace_in_file "${NGINX_PREFIX}server.conf" "%SERVER_CONF%" "include /server-confs/*.conf;"
|
||||
replace_in_file "${NGINX_PREFIX}server.conf" "%PRE_SERVER_CONF%" "include /pre-server-confs/*.conf;"
|
||||
@@ -67,10 +69,10 @@ if [ "$USE_REVERSE_PROXY" = "yes" ] ; then
|
||||
replace_in_file "${NGINX_PREFIX}reverse-proxy-${i}.conf" "%REVERSE_PROXY_URL%" "$url_value"
|
||||
replace_in_file "${NGINX_PREFIX}reverse-proxy-${i}.conf" "%REVERSE_PROXY_HOST%" "$host_value"
|
||||
if [ "$custom_headers_value" != "" ] ; then
|
||||
IFS_=$IFS
|
||||
IFS_=$IFS
|
||||
IFS=';'
|
||||
for header_value in $(echo "$custom_headers_value") ; do
|
||||
replace_in_file "${NGINX_PREFIX}reverse-proxy-${i}.conf" "%REVERSE_PROXY_CUSTOM_HEADERS%" "more_set_headers $header_value;\n%REVERSE_PROXY_CUSTOM_HEADERS%"
|
||||
for header_value in $(echo $custom_headers_value) ; do
|
||||
replace_in_file "${NGINX_PREFIX}reverse-proxy-${i}.conf" "%REVERSE_PROXY_CUSTOM_HEADERS%" "proxy_set_header $header_value;\n%REVERSE_PROXY_CUSTOM_HEADERS%"
|
||||
done
|
||||
IFS=$IFS_
|
||||
fi
|
||||
@@ -276,7 +278,7 @@ fi
|
||||
|
||||
# block bad UA
|
||||
if [ "$BLOCK_USER_AGENT" = "yes" ] ; then
|
||||
replace_in_file "${NGINX_PREFIX}main-lua.conf" "%USE_USER_AGENT%" "true"
|
||||
replace_in_file "${NGINX_PREFIX}main-lua.conf" "%USE_USER_AGENTS%" "true"
|
||||
if [ "$WHITELIST_USER_AGENT" != "" ] ; then
|
||||
list=$(spaces_to_lua "$WHITELIST_USER_AGENT")
|
||||
replace_in_file "${NGINX_PREFIX}main-lua.conf" "%WHITELIST_USER_AGENT%" "$list"
|
||||
@@ -284,36 +286,44 @@ if [ "$BLOCK_USER_AGENT" = "yes" ] ; then
|
||||
replace_in_file "${NGINX_PREFIX}main-lua.conf" "%WHITELIST_USER_AGENT%" ""
|
||||
fi
|
||||
else
|
||||
replace_in_file "${NGINX_PREFIX}main-lua.conf" "%USE_USER_AGENT%" "false"
|
||||
replace_in_file "${NGINX_PREFIX}main-lua.conf" "%USE_USER_AGENTS%" "false"
|
||||
replace_in_file "${NGINX_PREFIX}main-lua.conf" "%WHITELIST_USER_AGENT%" ""
|
||||
fi
|
||||
|
||||
# whitelist URI
|
||||
if [ "$WHITELIST_URI" != "" ] ; then
|
||||
list=$(spaces_to_lua "$WHITELIST_URI")
|
||||
replace_in_file "${NGINX_PREFIX}main-lua.conf" "%WHITELIST_URI%" "$list"
|
||||
else
|
||||
replace_in_file "${NGINX_PREFIX}main-lua.conf" "%WHITELIST_URI%" ""
|
||||
fi
|
||||
|
||||
# block bad referrer
|
||||
if [ "$BLOCK_REFERRER" = "yes" ] ; then
|
||||
replace_in_file "${NGINX_PREFIX}main-lua.conf" "%USE_REFERRER%" "true"
|
||||
replace_in_file "${NGINX_PREFIX}main-lua.conf" "%USE_REFERRERS%" "true"
|
||||
else
|
||||
replace_in_file "${NGINX_PREFIX}main-lua.conf" "%USE_REFERRER%" "false"
|
||||
fi
|
||||
|
||||
# block TOR exit nodes
|
||||
if [ "$BLOCK_TOR_EXIT_NODE" = "yes" ] ; then
|
||||
replace_in_file "${NGINX_PREFIX}server.conf" "%BLOCK_TOR_EXIT_NODE%" "include /etc/nginx/block-tor-exit-node.conf;"
|
||||
replace_in_file "${NGINX_PREFIX}main-lua.conf" "%USE_TOR_EXIT_NODES%" "true"
|
||||
else
|
||||
replace_in_file "${NGINX_PREFIX}server.conf" "%BLOCK_TOR_EXIT_NODE%" ""
|
||||
replace_in_file "${NGINX_PREFIX}main-lua.conf" "%USE_TOR_EXIT_NODES%" "false"
|
||||
fi
|
||||
|
||||
# block proxies
|
||||
if [ "$BLOCK_PROXIES" = "yes" ] ; then
|
||||
replace_in_file "${NGINX_PREFIX}server.conf" "%BLOCK_PROXIES%" "include /etc/nginx/block-proxies.conf;"
|
||||
replace_in_file "${NGINX_PREFIX}main-lua.conf" "%USE_PROXIES%" "true"
|
||||
else
|
||||
replace_in_file "${NGINX_PREFIX}server.conf" "%BLOCK_PROXIES%" ""
|
||||
replace_in_file "${NGINX_PREFIX}main-lua.conf" "%USE_PROXIES%" "false"
|
||||
fi
|
||||
|
||||
# block abusers
|
||||
if [ "$BLOCK_ABUSERS" = "yes" ] ; then
|
||||
replace_in_file "${NGINX_PREFIX}server.conf" "%BLOCK_ABUSERS%" "include /etc/nginx/block-abusers.conf;"
|
||||
replace_in_file "${NGINX_PREFIX}main-lua.conf" "%USE_ABUSERS%" "true"
|
||||
else
|
||||
replace_in_file "${NGINX_PREFIX}server.conf" "%BLOCK_ABUSERS%" ""
|
||||
replace_in_file "${NGINX_PREFIX}main-lua.conf" "%USE_ABUSERS%" "false"
|
||||
fi
|
||||
|
||||
# HTTPS config
|
||||
@@ -343,8 +353,8 @@ if [ "$AUTO_LETS_ENCRYPT" = "yes" ] || [ "$USE_CUSTOM_HTTPS" = "yes" ] || [ "$GE
|
||||
if [ "$MULTISITE" = "no" ] ; then
|
||||
FIRST_SERVER_NAME=$(echo "$SERVER_NAME" | cut -d " " -f 1)
|
||||
else
|
||||
FIRST_SERVER_NAME="$1"
|
||||
EMAIL_LETS_ENCRYPT="${EMAIL_LETS_ENCRYPT-contact@$1}"
|
||||
FIRST_SERVER_NAME="$first_server"
|
||||
EMAIL_LETS_ENCRYPT="${EMAIL_LETS_ENCRYPT-contact@$first_server}"
|
||||
echo -n "$EMAIL_LETS_ENCRYPT" > ${NGINX_PREFIX}email-lets-encrypt.txt
|
||||
fi
|
||||
replace_in_file "${NGINX_PREFIX}https.conf" "%HTTPS_CERT%" "/etc/letsencrypt/live/${FIRST_SERVER_NAME}/fullchain.pem"
|
||||
@@ -384,6 +394,7 @@ fi
|
||||
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}server.conf" "%USE_MODSECURITY%" "include ${NGINX_PREFIX}modsecurity.conf;"
|
||||
replace_in_file "${NGINX_PREFIX}modsecurity-rules.conf" "%MODSECURITY_SEC_AUDIT_ENGINE%" "$MODSECURITY_SEC_AUDIT_ENGINE"
|
||||
if [ "$MULTISITE" != "yes" ] ; then
|
||||
modsec_custom=""
|
||||
if ls /modsec-confs/*.conf > /dev/null 2>&1 ; then
|
||||
@@ -551,6 +562,15 @@ else
|
||||
replace_in_file "${NGINX_PREFIX}main-lua.conf" "%INCLUDE_ANTIBOT_RECAPTCHA%" ""
|
||||
fi
|
||||
|
||||
# bad behavior
|
||||
if [ "$USE_BAD_BEHAVIOR" = "yes" ] ; then
|
||||
replace_in_file "${NGINX_PREFIX}main-lua.conf" "%USE_BAD_BEHAVIOR%" "true"
|
||||
replace_in_file "${NGINX_PREFIX}log-lua.conf" "%USE_BAD_BEHAVIOR%" "true"
|
||||
else
|
||||
replace_in_file "${NGINX_PREFIX}main-lua.conf" "%USE_BAD_BEHAVIOR%" "false"
|
||||
replace_in_file "${NGINX_PREFIX}log-lua.conf" "%USE_BAD_BEHAVIOR%" "false"
|
||||
fi
|
||||
|
||||
# request limiting
|
||||
if [ "$USE_LIMIT_REQ" = "yes" ] ; then
|
||||
replace_in_file "${NGINX_PREFIX}server.conf" "%LIMIT_REQ%" "include ${NGINX_PREFIX}limit-req.conf;"
|
||||
@@ -567,13 +587,6 @@ else
|
||||
replace_in_file "${NGINX_PREFIX}server.conf" "%LIMIT_CONN%" ""
|
||||
fi
|
||||
|
||||
# fail2ban
|
||||
if [ "$USE_FAIL2BAN" = "yes" ] ; then
|
||||
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
|
||||
|
||||
# clamav scan uploaded files
|
||||
if [ "$USE_CLAMAV_UPLOAD" = "yes" ] ; then
|
||||
replace_in_file "${NGINX_PREFIX}modsecurity-rules.conf" "%USE_CLAMAV_UPLOAD%" "include ${NGINX_PREFIX}modsecurity-clamav.conf"
|
||||
|
||||
@@ -20,7 +20,6 @@ services:
|
||||
- DISABLE_DEFAULT_SERVER=yes
|
||||
- USE_CLIENT_CACHE=yes
|
||||
- USE_GZIP=yes
|
||||
- USE_BROTLI=yes
|
||||
labels:
|
||||
- "bunkerized-nginx.AUTOCONF"
|
||||
|
||||
@@ -32,6 +31,9 @@ services:
|
||||
- autoconf:/etc/nginx
|
||||
depends_on:
|
||||
- mywww
|
||||
- myapp1
|
||||
- myapp2
|
||||
- myapp3
|
||||
|
||||
myapp1:
|
||||
image: php:fpm
|
||||
|
||||
@@ -18,8 +18,8 @@ services:
|
||||
- REDIRECT_HTTP_TO_HTTPS=yes
|
||||
- DISABLE_DEFAULT_SERVER=yes
|
||||
- USE_CLIENT_CACHE=yes
|
||||
- USE_PROXY_CACHE=yes
|
||||
- USE_GZIP=yes
|
||||
- USE_BROTLI=yes
|
||||
- USE_REVERSE_PROXY=yes
|
||||
labels:
|
||||
- "bunkerized-nginx.AUTOCONF"
|
||||
@@ -32,6 +32,9 @@ services:
|
||||
- autoconf:/etc/nginx
|
||||
depends_on:
|
||||
- mywww
|
||||
- myapp1
|
||||
- myapp2
|
||||
- myapp3
|
||||
|
||||
myapp1:
|
||||
build: js-app
|
||||
|
||||
@@ -18,7 +18,6 @@ services:
|
||||
- DISABLE_DEFAULT_SERVER=yes
|
||||
- USE_CLIENT_CACHE=yes
|
||||
- USE_GZIP=yes
|
||||
- USE_BROTLI=yes
|
||||
- REMOTE_PHP=myphp
|
||||
- REMOTE_PHP_PATH=/app
|
||||
|
||||
|
||||
@@ -23,7 +23,6 @@ services:
|
||||
- PROXY_REAL_IP=yes
|
||||
- USE_CLIENT_CACHE=yes
|
||||
- USE_GZIP=yes
|
||||
- USE_BROTLI=yes
|
||||
- REMOTE_PHP=myphp1
|
||||
- REMOTE_PHP_PATH=/app
|
||||
labels:
|
||||
@@ -41,7 +40,6 @@ services:
|
||||
- PROXY_REAL_IP=yes
|
||||
- USE_CLIENT_CACHE=yes
|
||||
- USE_GZIP=yes
|
||||
- USE_BROTLI=yes
|
||||
- REMOTE_PHP=myphp2
|
||||
- REMOTE_PHP_PATH=/app
|
||||
labels:
|
||||
|
||||
26
examples/certbot-wildcard/certbot-wildcard.sh
Executable file
26
examples/certbot-wildcard/certbot-wildcard.sh
Executable file
@@ -0,0 +1,26 @@
|
||||
#!/bin/sh
|
||||
|
||||
# you need to run it before starting bunkerized-nginx
|
||||
# since it's manual there is no auto renew, you need to run it again before it expires
|
||||
|
||||
DOMAIN="*.website.com"
|
||||
SERVICE="mywww"
|
||||
|
||||
# ask for wildcard certificate
|
||||
# it's interactive and you will need to add a DNS entry
|
||||
docker run --rm -it -v "${PWD}/letsencrypt:/etc/letsencrypt" certbot/certbot certonly --manual -d $DOMAIN --agree-tos
|
||||
if [ $? -ne 0 ] ; then
|
||||
echo "error while getting certificate for $DOMAIN"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# fix permissions
|
||||
chown -R 101:101 "${PWD}/letsencrypt"
|
||||
|
||||
# reload nginx if it's already running (in case of a "renew")
|
||||
if [ -z `docker-compose ps -q $SERVICE` ] || [ -z `docker ps -q --no-trunc | grep $(docker-compose ps -q $SERVICE)` ]; then
|
||||
echo "bunkerized-nginx is not running, skipping nginx reload"
|
||||
else
|
||||
echo "bunkerized-nginx is running, sending reload order"
|
||||
docker-compose exec $SERVICE nginx -s reload
|
||||
fi
|
||||
39
examples/certbot-wildcard/docker-compose.yml
Normal file
39
examples/certbot-wildcard/docker-compose.yml
Normal file
@@ -0,0 +1,39 @@
|
||||
version: '3'
|
||||
|
||||
services:
|
||||
|
||||
mywww:
|
||||
image: bunkerity/bunkerized-nginx
|
||||
restart: always
|
||||
ports:
|
||||
- 80:8080
|
||||
- 443:8443
|
||||
volumes:
|
||||
- ./web-files:/www:ro
|
||||
- ./letsencrypt:/letsencrypt:ro
|
||||
environment:
|
||||
- SERVER_NAME=app1.website.com app2.website.com # replace with your domains
|
||||
- MULTISITE=yes
|
||||
- USE_CUSTOM_HTTPS=yes
|
||||
- CUSTOM_HTTPS_CERT=/letsencrypt/live/website.com/fullchain.pem
|
||||
- CUSTOM_HTTPS_KEY=/letsencrypt/live/website.com/privkey.pem
|
||||
- REDIRECT_HTTP_TO_HTTPS=yes
|
||||
- DISABLE_DEFAULT_SERVER=yes
|
||||
- USE_CLIENT_CACHE=yes
|
||||
- USE_GZIP=yes
|
||||
- app1.website.com_REMOTE_PHP=myapp1
|
||||
- app1.website.com_REMOTE_PHP_PATH=/app
|
||||
- app2.website.com_REMOTE_PHP=myapp2
|
||||
- app2.website.com_REMOTE_PHP_PATH=/app
|
||||
|
||||
myapp1:
|
||||
image: php:fpm
|
||||
restart: always
|
||||
volumes:
|
||||
- ./web-files/app1.website.com:/app
|
||||
|
||||
myapp2:
|
||||
image: php:fpm
|
||||
restart: always
|
||||
volumes:
|
||||
- ./web-files/app2.website.com:/app
|
||||
@@ -0,0 +1,5 @@
|
||||
<?php
|
||||
|
||||
echo "hello from app1 !";
|
||||
|
||||
?>
|
||||
@@ -0,0 +1,5 @@
|
||||
<?php
|
||||
|
||||
echo "hello from app2 !";
|
||||
|
||||
?>
|
||||
@@ -1,3 +1,23 @@
|
||||
#!/bin/sh
|
||||
|
||||
# first, you need to run the crowdsec service
|
||||
echo "running crowdsec service ..."
|
||||
docker-compose up -d mycrowdsec
|
||||
|
||||
# wait a little until it's up
|
||||
sleep 10
|
||||
|
||||
# get the bouncer key
|
||||
docker-compose exec mycrowdsec cscli bouncers add MyBouncer
|
||||
|
||||
# enter the key into the CROWDSEC_KEY environment variable
|
||||
read -p -s "edit CROWDSEC_KEY env var in docker-compose.yml file and press enter"
|
||||
|
||||
# start all services
|
||||
docker-compose up -d
|
||||
|
||||
# wait a little until it's up
|
||||
sleep 10
|
||||
|
||||
# restart crowdsec so it reads the log files
|
||||
docker-compose restart mycrowdsec
|
||||
|
||||
@@ -20,7 +20,6 @@ services:
|
||||
- DISABLE_DEFAULT_SERVER=yes
|
||||
- USE_CLIENT_CACHE=yes
|
||||
- USE_GZIP=yes
|
||||
- USE_BROTLI=yes
|
||||
- USE_CROWDSEC=yes
|
||||
- CROWDSEC_HOST=http://mycrowdsec:8080
|
||||
- CROWDSEC_KEY= # you need to generate it (see bouncer_key.sh)
|
||||
@@ -34,7 +33,7 @@ services:
|
||||
- net2
|
||||
|
||||
mycrowdsec:
|
||||
image: crowdsecurity/crowdsec:v1.0.2
|
||||
image: crowdsecurity/crowdsec:v1.0.13
|
||||
restart: always
|
||||
volumes:
|
||||
- ./acquis.yaml:/etc/crowdsec/acquis.yaml
|
||||
|
||||
30
examples/ghost/docker-compose.yml
Normal file
30
examples/ghost/docker-compose.yml
Normal file
@@ -0,0 +1,30 @@
|
||||
version: '3'
|
||||
|
||||
services:
|
||||
|
||||
myreverse:
|
||||
image: bunkerity/bunkerized-nginx
|
||||
restart: always
|
||||
ports:
|
||||
- 80:8080
|
||||
- 443:8443
|
||||
volumes:
|
||||
- ./letsencrypt:/etc/letsencrypt
|
||||
environment:
|
||||
- SERVER_NAME=www.website.com # replace with your domain
|
||||
- SERVE_FILES=no
|
||||
- DISABLE_DEFAULT_SERVER=yes
|
||||
- REDIRECT_HTTP_TO_HTTPS=yes
|
||||
- AUTO_LETS_ENCRYPT=yes
|
||||
- USE_PROXY_CACHE=yes
|
||||
- USE_GZIP=yes
|
||||
- USE_REVERSE_PROXY=yes
|
||||
- REVERSE_PROXY_URL=/
|
||||
- REVERSE_PROXY_HOST=http://myghost:2368/
|
||||
|
||||
myghost:
|
||||
image: ghost:alpine
|
||||
volumes:
|
||||
- ./data-ghost:/var/lib/ghost/content
|
||||
environment:
|
||||
- url=https://www.website.com # replace with your domain
|
||||
30
examples/gogs/docker-compose.yml
Normal file
30
examples/gogs/docker-compose.yml
Normal file
@@ -0,0 +1,30 @@
|
||||
version: '3'
|
||||
|
||||
services:
|
||||
|
||||
myreverse:
|
||||
image: bunkerity/bunkerized-nginx
|
||||
restart: always
|
||||
ports:
|
||||
- 80:8080
|
||||
- 443:8443
|
||||
volumes:
|
||||
- ./letsencrypt:/etc/letsencrypt
|
||||
- ./modsec-crs-confs:/modsec-crs-confs:ro # fix FP with CRS
|
||||
environment:
|
||||
- SERVER_NAME=www.website.com # replace with your domain
|
||||
- SERVE_FILES=no
|
||||
- DISABLE_DEFAULT_SERVER=yes
|
||||
- REDIRECT_HTTP_TO_HTTPS=yes
|
||||
- AUTO_LETS_ENCRYPT=yes
|
||||
- USE_PROXY_CACHE=yes
|
||||
- USE_CLIENT_CACHE=yes
|
||||
- USE_GZIP=yes
|
||||
- USE_REVERSE_PROXY=yes
|
||||
- REVERSE_PROXY_URL=/
|
||||
- REVERSE_PROXY_HOST=http://mygogs:3000/
|
||||
|
||||
mygogs:
|
||||
image: gogs/gogs
|
||||
volumes:
|
||||
- ./data-gogs:/data
|
||||
7
examples/gogs/modsec-crs-confs/gogs.conf
Normal file
7
examples/gogs/modsec-crs-confs/gogs.conf
Normal file
@@ -0,0 +1,7 @@
|
||||
SecAction \
|
||||
"id:900220,\
|
||||
phase:1,\
|
||||
nolog,\
|
||||
pass,\
|
||||
t:none,\
|
||||
setvar:'tx.allowed_request_content_type=|application/x-www-form-urlencoded| |multipart/form-data| |multipart/related| |text/xml| |application/xml| |application/soap+xml| |application/x-amf| |application/json| |application/cloudevents+json| |application/cloudevents-batch+json| |application/octet-stream| |application/csp-report| |application/xss-auditor-report| |text/plain| |application/x-git-upload-pack-request| |application/x-git-receive-pack-request|'"
|
||||
45
examples/joomla/docker-compose.yml
Normal file
45
examples/joomla/docker-compose.yml
Normal file
@@ -0,0 +1,45 @@
|
||||
version: '3'
|
||||
|
||||
services:
|
||||
|
||||
mywww:
|
||||
image: bunkerity/bunkerized-nginx
|
||||
restart: always
|
||||
ports:
|
||||
- 80:8080
|
||||
- 443:8443
|
||||
volumes:
|
||||
- ./joomla-files:/www:ro
|
||||
- ./letsencrypt:/etc/letsencrypt
|
||||
environment:
|
||||
- SERVER_NAME=www.website.com # replace with your domain
|
||||
- AUTO_LETS_ENCRYPT=yes
|
||||
- REDIRECT_HTTP_TO_HTTPS=yes
|
||||
- DISABLE_DEFAULT_SERVER=yes
|
||||
- MAX_CLIENT_SIZE=50m
|
||||
- USE_CLIENT_CACHE=yes
|
||||
- USE_GZIP=yes
|
||||
- REMOTE_PHP=myjoomla
|
||||
- REMOTE_PHP_PATH=/var/www/html
|
||||
|
||||
myjoomla:
|
||||
image: joomla:fpm-alpine
|
||||
restart: always
|
||||
volumes:
|
||||
- ./joomla-files:/var/www/html
|
||||
environment:
|
||||
- JOOMLA_DB_HOST=mydb
|
||||
- JOOMLA_DB_NAME=joomladb
|
||||
- JOOMLA_DB_USER=user
|
||||
- JOOMLA_DB_PASSWORD=db-user-pwd # replace with a stronger password (must match MYSQL_PASSWORD)
|
||||
|
||||
mydb:
|
||||
image: mariadb
|
||||
restart: always
|
||||
volumes:
|
||||
- ./db-data:/var/lib/mysql
|
||||
environment:
|
||||
- MYSQL_ROOT_PASSWORD=db-root-pwd # replace with a stronger password
|
||||
- MYSQL_DATABASE=joomladb
|
||||
- MYSQL_USER=user
|
||||
- MYSQL_PASSWORD=db-user-pwd # replace with a stronger password (must match JOOMLA_DB_PASSWORD)
|
||||
@@ -18,8 +18,8 @@ services:
|
||||
- REDIRECT_HTTP_TO_HTTPS=yes
|
||||
- AUTO_LETS_ENCRYPT=yes
|
||||
- USE_PROXY_CACHE=yes
|
||||
- USE_CLIENT_CACHE=yes
|
||||
- USE_GZIP=yes
|
||||
- USE_BROTLI=yes
|
||||
- USE_REVERSE_PROXY=yes
|
||||
- REVERSE_PROXY_URL=/
|
||||
- REVERSE_PROXY_HOST=http://app
|
||||
|
||||
@@ -18,8 +18,8 @@ services:
|
||||
- MAX_CLIENT_SIZE=50m
|
||||
- SERVE_FILES=no
|
||||
- USE_PROXY_CACHE=yes
|
||||
- USE_CLIENT_CACHE=yes
|
||||
- USE_GZIP=yes
|
||||
- USE_BROTLI=yes
|
||||
- USE_REVERSE_PROXY=yes
|
||||
- REVERSE_PROXY_URL=/
|
||||
- REVERSE_PROXY_HOST=https://mymoodle:8443
|
||||
|
||||
@@ -19,13 +19,11 @@ services:
|
||||
- DISABLE_DEFAULT_SERVER=yes
|
||||
- USE_CLIENT_CACHE=yes
|
||||
- USE_GZIP=yes
|
||||
- USE_BROTLI=yes
|
||||
- app1.website.com_REMOTE_PHP=myapp1
|
||||
- app1.website.com_REMOTE_PHP_PATH=/app
|
||||
- app2.website.com_REMOTE_PHP=myapp2
|
||||
- app2.website.com_REMOTE_PHP_PATH=/app
|
||||
- app3.website.com_SERVE_FILES=no
|
||||
- app3.website.com_USE_CLIENT_CACHE=no
|
||||
- app3.website.com_USE_PROXY_CACHE=yes
|
||||
- app3.website.com_USE_REVERSE_PROXY=yes
|
||||
- app3.website.com_REVERSE_PROXY_URL=/
|
||||
|
||||
@@ -22,16 +22,15 @@ services:
|
||||
- DISABLE_DEFAULT_SERVER=yes
|
||||
- USE_CLIENT_CACHE=yes
|
||||
- USE_GZIP=yes
|
||||
- USE_BROTLI=yes
|
||||
- wp.website.com_REMOTE_PHP=mywp
|
||||
- wp.website.com_REMOTE_PHP_PATH=/var/www/html
|
||||
- nc.website.com_REMOTE_PHP=mync
|
||||
- nc.website.com_REMOTE_PHP_PATH=/var/www/html
|
||||
- nc.website.com_LIMIT_REQ_RATE=40r/s
|
||||
- nc.website.com_LIMIT_REQ_BURST=60
|
||||
- nc.website.com_ALLOWED_METHODS=GET|POST|HEAD|PROPFIND|DELETE|PUT|MKCOL|MOVE|COPY|PROPPATCH|REPORT
|
||||
- nc.website.com_LIMIT_REQ_RATE=5r/s
|
||||
- nc.website.com_LIMIT_REQ_BURST=10
|
||||
- nc.website.com_ALLOWED_METHODS=GET|POST|HEAD|COPY|DELETE|LOCK|MKCOL|MOVE|PROPFIND|PROPPATCH|PUT|UNLOCK|OPTIONS
|
||||
- nc.website.com_X_FRAME_OPTIONS=SAMEORIGIN
|
||||
- nc.website.com_FAIL2BAN_STATUS_CODE=400|401|403|405|444
|
||||
- nc.website.com_BAD_BEHAVIOR_STATUS_CODE=400|401|403|405|444
|
||||
networks:
|
||||
- net1
|
||||
- net2
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
SecRuleRemoveById 921110
|
||||
SecRule REQUEST_FILENAME "@contains /remote.php/webdav" "id:1,ctl:ruleRemoveByTag=OWASP_CRS"
|
||||
|
||||
@@ -1 +1,4 @@
|
||||
SecRule REQUEST_FILENAME "/wp-admin/admin-ajax.php" "id:1,ctl:ruleRemoveByTag=attack-xss,ctl:ruleRemoveByTag=attack-rce"
|
||||
SecRule REQUEST_FILENAME "/wp-admin/options.php" "id:2,ctl:ruleRemoveByTag=attack-xss"
|
||||
SecRule REQUEST_FILENAME "^/wp-json/yoast" "id:3,ctl:ruleRemoveById=930120"
|
||||
SecRuleRemoveById 953120
|
||||
|
||||
@@ -12,4 +12,4 @@ SecAction \
|
||||
nolog,\
|
||||
pass,\
|
||||
t:none,\
|
||||
setvar:'tx.allowed_methods=GET HEAD POST PROPFIND DELETE PUT MKCOL MOVE COPY PROPPATCH REPORT'"
|
||||
setvar:'tx.allowed_methods=GET POST HEAD COPY DELETE LOCK MKCOL MOVE PROPFIND PROPPATCH PUT UNLOCK OPTIONS'"
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
<?php
|
||||
|
||||
echo "welcome to app1 !";
|
||||
|
||||
?>
|
||||
@@ -0,0 +1,5 @@
|
||||
<?php
|
||||
|
||||
echo "welcome to app2 !";
|
||||
|
||||
?>
|
||||
@@ -0,0 +1,9 @@
|
||||
FROM php:fpm
|
||||
|
||||
COPY docker-php-entrypoint /usr/local/bin/
|
||||
RUN chmod +x /usr/local/bin/docker-php-entrypoint
|
||||
|
||||
ENTRYPOINT ["/usr/local/bin/docker-php-entrypoint"]
|
||||
|
||||
EXPOSE 9000
|
||||
CMD ["php-fpm"]
|
||||
@@ -0,0 +1,5 @@
|
||||
<?php
|
||||
|
||||
echo "welcome to app3 !";
|
||||
|
||||
?>
|
||||
@@ -0,0 +1,4 @@
|
||||
#!/bin/sh
|
||||
|
||||
echo "[*] my-php started"
|
||||
exec "$@"
|
||||
60
examples/multisite-custom-subfolders/docker-compose.yml
Normal file
60
examples/multisite-custom-subfolders/docker-compose.yml
Normal file
@@ -0,0 +1,60 @@
|
||||
version: '3'
|
||||
|
||||
services:
|
||||
|
||||
myreverse:
|
||||
image: bunkerity/bunkerized-nginx
|
||||
restart: always
|
||||
ports:
|
||||
- 80:8080
|
||||
- 443:8443
|
||||
volumes:
|
||||
- ./apps:/www:ro
|
||||
- ./letsencrypt:/etc/letsencrypt
|
||||
environment:
|
||||
- MULTISITE=yes
|
||||
- SERVER_NAME=app1.website.com app2.website.com app3.website.com # replace with your domains
|
||||
- DISABLE_DEFAULT_SERVER=yes
|
||||
- REDIRECT_HTTP_TO_HTTPS=yes
|
||||
- AUTO_LETS_ENCRYPT=yes
|
||||
- USE_CLIENT_CACHE=yes
|
||||
- USE_GZIP=yes
|
||||
- ROOT_SITE_SUBFOLDER=web-files
|
||||
- app1.website.com_REMOTE_PHP=app1
|
||||
- app1.website.com_REMOTE_PHP_PATH=/var/www/html
|
||||
- app2.website.com_REMOTE_PHP=app2
|
||||
- app2.website.com_REMOTE_PHP_PATH=/var/www/html
|
||||
- app2.website.com_ROOT_SITE_SUBFOLDER= # no subfolder
|
||||
- app3.website.com_REMOTE_PHP=app3
|
||||
- app3.website.com_REMOTE_PHP_PATH=/var/www/html
|
||||
- app3.website.com_ROOT_SITE_SUBFOLDER=another-folder
|
||||
|
||||
app1:
|
||||
image: php:fpm
|
||||
restart: always
|
||||
volumes:
|
||||
- ./apps/app1.website.com/web-files:/var/www/html # app1 project web root
|
||||
|
||||
app1_db:
|
||||
image: mariadb
|
||||
restart: always
|
||||
volumes:
|
||||
- ./apps/app1.website.com/db-data:/var/lib/mysql # app1 project data
|
||||
environment:
|
||||
- MYSQL_ROOT_PASSWORD=db-root-pwd # replace with a stronger password
|
||||
- MYSQL_DATABASE=app
|
||||
- MYSQL_USER=user
|
||||
- MYSQL_PASSWORD=db-user-pwd # replace with a stronger password
|
||||
|
||||
app2:
|
||||
image: php:fpm
|
||||
restart: always
|
||||
volumes:
|
||||
- ./apps/app2.website.com:/var/www/html # app2 project with no subfolder
|
||||
|
||||
app3:
|
||||
build: apps/app3.website.com/. # project with custom built image
|
||||
image: my-php
|
||||
restart: always
|
||||
volumes:
|
||||
- ./apps/app3.website.com/another-folder:/var/www/html # app3 project web root
|
||||
@@ -23,16 +23,15 @@ services:
|
||||
- USE_CLIENT_CACHE=yes
|
||||
- REMOTE_PHP=mync
|
||||
- REMOTE_PHP_PATH=/var/www/html
|
||||
- LIMIT_REQ_RATE=40r/s
|
||||
- LIMIT_REQ_BURST=60
|
||||
- ALLOWED_METHODS=GET|POST|HEAD|PROPFIND|DELETE|PUT|MKCOL|MOVE|COPY|PROPPATCH|REPORT
|
||||
- LIMIT_REQ_RATE=5r/s
|
||||
- LIMIT_REQ_BURST=10
|
||||
- ALLOWED_METHODS=GET|POST|HEAD|COPY|DELETE|LOCK|MKCOL|MOVE|PROPFIND|PROPPATCH|PUT|UNLOCK|OPTIONS
|
||||
- X_FRAME_OPTIONS=SAMEORIGIN
|
||||
- USE_GZIP=yes
|
||||
- USE_BROTLI=yes
|
||||
- FAIL2BAN_STATUS_CODE=400|401|403|405|444
|
||||
- BAD_BEHAVIOR_STATUS_CODE=400|401|403|405|444
|
||||
|
||||
mync:
|
||||
image: nextcloud:20-fpm
|
||||
image: nextcloud:21-fpm
|
||||
restart: always
|
||||
volumes:
|
||||
- ./nc-files:/var/www/html
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
SecRuleRemoveById 921110
|
||||
SecRule REQUEST_FILENAME "@contains /remote.php/webdav" "id:1,ctl:ruleRemoveByTag=OWASP_CRS"
|
||||
|
||||
@@ -12,4 +12,4 @@ SecAction \
|
||||
nolog,\
|
||||
pass,\
|
||||
t:none,\
|
||||
setvar:'tx.allowed_methods=GET HEAD POST PROPFIND DELETE PUT MKCOL MOVE COPY PROPPATCH REPORT'"
|
||||
setvar:'tx.allowed_methods=GET POST HEAD COPY DELETE LOCK MKCOL MOVE PROPFIND PROPPATCH PUT UNLOCK OPTIONS'"
|
||||
|
||||
@@ -20,8 +20,8 @@ services:
|
||||
- ALLOWED_METHODS=GET|POST|HEAD|PUT|DELETE
|
||||
- SERVE_FILES=no
|
||||
- USE_PROXY_CACHE=yes
|
||||
- USE_CLIENT_CACHE=yes
|
||||
- USE_GZIP=yes
|
||||
- USE_BROTLI=yes
|
||||
- USE_REVERSE_PROXY=yes
|
||||
- REVERSE_PROXY_URL=/
|
||||
- REVERSE_PROXY_HOST=https://mypassbolt
|
||||
|
||||
@@ -20,9 +20,10 @@ services:
|
||||
- MAX_CLIENT_SIZE=50m
|
||||
- USE_CLIENT_CACHE=yes
|
||||
- USE_GZIP=yes
|
||||
- USE_BROTLI=yes
|
||||
- REMOTE_PHP=myprestashop
|
||||
- REMOTE_PHP_PATH=/var/www/html
|
||||
- LIMIT_REQ_RATE=5r/s
|
||||
- LIMIT_REQ_BURST=10
|
||||
|
||||
myprestashop:
|
||||
image: prestashop/prestashop:1.7-fpm
|
||||
|
||||
46
examples/redmine/docker-compose.yml
Normal file
46
examples/redmine/docker-compose.yml
Normal file
@@ -0,0 +1,46 @@
|
||||
version: '3'
|
||||
|
||||
services:
|
||||
|
||||
myreverse:
|
||||
image: bunkerity/bunkerized-nginx
|
||||
restart: always
|
||||
ports:
|
||||
- 80:8080
|
||||
- 443:8443
|
||||
volumes:
|
||||
- ./letsencrypt:/etc/letsencrypt
|
||||
environment:
|
||||
- SERVER_NAME=www.website.com # replace with your domain
|
||||
- SERVE_FILES=no
|
||||
- DISABLE_DEFAULT_SERVER=yes
|
||||
- REDIRECT_HTTP_TO_HTTPS=yes
|
||||
- AUTO_LETS_ENCRYPT=yes
|
||||
- USE_PROXY_CACHE=yes
|
||||
- USE_CLIENT_CACHE=yes
|
||||
- USE_GZIP=yes
|
||||
- USE_REVERSE_PROXY=yes
|
||||
- REVERSE_PROXY_URL=/
|
||||
- REVERSE_PROXY_HOST=http://myredmine:3000/
|
||||
|
||||
myredmine:
|
||||
image: redmine
|
||||
restart: always
|
||||
volumes:
|
||||
- ./redmine-data:/usr/src/redmine/files
|
||||
environment:
|
||||
- REDMINE_DB_MYSQL=mydb
|
||||
- REDMINE_DB_DATABASE=redminedb
|
||||
- REDMINE_DB_USERNAME=user
|
||||
- REDMINE_DB_PASSWORD=db-user-pwd # replace with a stronger password (must match MYSQL_PASSWORD)
|
||||
|
||||
mydb:
|
||||
image: mariadb
|
||||
restart: always
|
||||
volumes:
|
||||
- ./db-data:/var/lib/mysql
|
||||
environment:
|
||||
- MYSQL_ROOT_PASSWORD=db-root-pwd # replace with a stronger password
|
||||
- MYSQL_DATABASE=redminedb
|
||||
- MYSQL_USER=user
|
||||
- MYSQL_PASSWORD=db-user-pwd # replace with a stronger password (must match REDMINE_DB_PASSWORD)
|
||||
@@ -18,8 +18,8 @@ services:
|
||||
- REDIRECT_HTTP_TO_HTTPS=yes
|
||||
- AUTO_LETS_ENCRYPT=yes
|
||||
- USE_PROXY_CACHE=yes
|
||||
- USE_CLIENT_CACHE=yes
|
||||
- USE_GZIP=yes
|
||||
- USE_BROTLI=yes
|
||||
- USE_REVERSE_PROXY=yes
|
||||
- app1.website.com_REVERSE_PROXY_URL=/
|
||||
- app1.website.com_REVERSE_PROXY_HOST=http://app1:3000
|
||||
|
||||
@@ -18,8 +18,8 @@ services:
|
||||
- REDIRECT_HTTP_TO_HTTPS=yes
|
||||
- AUTO_LETS_ENCRYPT=yes
|
||||
- USE_PROXY_CACHE=yes
|
||||
- USE_CLIENT_CACHE=yes
|
||||
- USE_GZIP=yes
|
||||
- USE_BROTLI=yes
|
||||
- USE_REVERSE_PROXY=yes
|
||||
- REVERSE_PROXY_URL_1=/app1/
|
||||
- REVERSE_PROXY_HOST_1=http://app1:3000/
|
||||
|
||||
@@ -17,8 +17,8 @@ services:
|
||||
- REDIRECT_HTTP_TO_HTTPS=yes
|
||||
- AUTO_LETS_ENCRYPT=yes
|
||||
- USE_PROXY_CACHE=yes
|
||||
- USE_CLIENT_CACHE=yes
|
||||
- USE_GZIP=yes
|
||||
- USE_BROTLI=yes
|
||||
- USE_REVERSE_PROXY=yes
|
||||
- REVERSE_PROXY_URL=/ws/
|
||||
- REVERSE_PROXY_HOST=http://myws:8010/
|
||||
|
||||
@@ -32,7 +32,7 @@ services:
|
||||
mode: host
|
||||
protocol: tcp
|
||||
volumes:
|
||||
- /shared/confs:/etc/nginx:ro
|
||||
- /shared/confs:/etc/nginx
|
||||
- /shared/letsencrypt:/etc/letsencrypt:ro
|
||||
- /shared/acme-challenge:/acme-challenge:ro
|
||||
- /shared/www:/www:ro
|
||||
@@ -45,6 +45,7 @@ services:
|
||||
- AUTO_LETS_ENCRYPT=yes
|
||||
- REDIRECT_HTTP_TO_HTTPS=yes
|
||||
- DISABLE_DEFAULT_SERVER=yes
|
||||
- USE_CLIENT_CACHE=yes
|
||||
networks:
|
||||
- net_config
|
||||
- net_services
|
||||
@@ -86,6 +87,7 @@ services:
|
||||
- "node.role==worker"
|
||||
labels:
|
||||
- "bunkerized-nginx.SERVER_NAME=app2.website.com"
|
||||
- "bunkerized-nginx.USE_PROXY_CACHE=yes"
|
||||
- "bunkerized-nginx.USE_REVERSE_PROXY=yes"
|
||||
- "bunkerized-nginx.REVERSE_PROXY_URL=/"
|
||||
- "bunkerized-nginx.REVERSE_PROXY_HOST=http://app2"
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user