34 Commits

Author SHA1 Message Date
bunkerity
b0ca85ff75 v1.2.5 - performance improvement 2021-05-14 16:42:08 +02:00
Bunkerity
2f115c444d Merge pull request #131 from bunkerity/issue-templates
Update issue templates
2021-05-14 16:37:37 +02:00
Bunkerity
7f15741ea2 Update issue templates 2021-05-14 16:33:01 +02:00
bunkerity
288b8eb851 docs improvement + road to v1.2.5 2021-05-14 15:41:15 +02:00
bunkerity
61c08fb97b docs - troubleshooting 2021-05-14 12:12:33 +02:00
bunkerity
01ef47a669 docs - security tuning improvement 2021-05-14 11:15:00 +02:00
florian
71515a9101 doc - volumes list 2021-05-13 20:34:41 +02:00
bunkerity
a33d0658c6 docs - road to a beautiful documentation 2021-05-13 17:46:31 +02:00
bunkerity
0b3ff6a9f4 bad behavior - move from fail2ban to pure lua 2021-05-13 16:21:51 +02:00
bunkerity
eb2d0d330d performance - rsyslog and fail2ban removing 2021-05-13 11:14:39 +02:00
bunkerity
5bcbb38638 doc - official document started 2021-05-12 17:35:32 +02:00
bunkerity
ca660b2501 init work on official doc 2021-05-12 12:28:01 +02:00
bunkerity
3a34436cd8 add AquaeAtrae example for ROOT_SITE_SUBFOLDER 2021-05-12 12:07:29 +02:00
bunkerity
b1d03cd11c performance - move bad user-agents and referrers checks from nginx to LUA with caching 2021-05-11 15:30:16 +02:00
bunkerity
42c3fb8740 add sandbox allow-downloads to the default value of CONTENT_SECURITY_POLICY 2021-05-11 08:57:23 +02:00
bunkerity
f1c043604a add missing backslash in the quickstart guide and update autoconf examples with the depends_on directive 2021-05-11 08:54:34 +02:00
bunkerity
fd61df205f performance - move external blacklists checks from nginx to LUA 2021-05-10 17:51:07 +02:00
bunkerity
009d6fb5ae choose connection and nofile numbers, increase error_log level to get modsecurity rules, add MODSECURITY_SEC_AUDIT_ENGINE var 2021-05-05 17:38:22 +02:00
bunkerity
ba4185a42e jobs - fix automatic reload 2021-05-03 14:18:10 +02:00
bunkerity
70976d0fbc fix user-agent not blocking and add documentation on bundle when USE_CUSTOM_HTTPS=yes 2021-05-03 13:59:55 +02:00
bunkerity
062a39c63a integrate AquaeAtrae work - add ROOT_SITE_SUBFOLDER 2021-05-03 10:31:37 +02:00
bunkerity
83841b290a jobs - edit adren work on external blacklists 2021-05-02 16:14:13 +02:00
Bunkerity
10dc58cb6d Merge pull request #126 from adren/patch-6
deduplicate list of user-agents
2021-05-02 15:14:10 +02:00
Bunkerity
668754686c Merge pull request #125 from adren/patch-5
more optimized way to generate map referrer file
2021-05-02 15:13:17 +02:00
Bunkerity
84b1933f63 Merge pull request #124 from adren/patch-4
improve the generation of blocking file (abusers)
2021-05-02 15:12:19 +02:00
Bunkerity
15f6d0a32a Merge pull request #123 from adren/patch-3
improve generation of block file (Tor exit nodes)
2021-05-02 15:11:27 +02:00
Bunkerity
e628361a89 Merge pull request #122 from adren/patch-1
huge improvement to generate blocking file
2021-05-02 15:10:52 +02:00
Cyril Chaboisseau
f8d71e067e improved way to generate user-agent file 2021-05-01 19:04:18 +02:00
Cyril Chaboisseau
02ae3b6bd3 change IFS before subshell
There needs to be a change in IFS before the 2 curl commands in order to keep line by line formatting
2021-05-01 15:48:33 +02:00
Cyril Chaboisseau
2fb0e7c473 deduplicate list of user-agents 2021-05-01 15:08:52 +02:00
Cyril Chaboisseau
9adcc2f1a7 more optimized way to generate map referrer file 2021-05-01 14:51:28 +02:00
Cyril Chaboisseau
7b98db4d14 improve the generation of blocking file (abusers) 2021-05-01 12:29:15 +02:00
Cyril Chaboisseau
ddb2b85916 improve generation of block file (Tor exit nodes) 2021-05-01 12:25:43 +02:00
Cyril Chaboisseau
da1a460a64 huge improvement to generate blocking file
process the file in 2 commands (grep + sed) instead of a loop running on each line
the time to generate the file takes 0.235 seconds instead of one hour, making it roughly 15,000 times quicker
the output file is exactly the same as with the former method
2021-05-01 11:42:07 +02:00
72 changed files with 2444 additions and 1551 deletions

17
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View 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 !**

View File

@@ -24,5 +24,5 @@ jobs:
exit-code: '1' exit-code: '1'
ignore-unfixed: true ignore-unfixed: true
severity: 'UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL' severity: 'UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL'
skip-dirs: '/usr/lib/go'

1
.gitignore vendored
View File

@@ -1 +1,2 @@
.idea/ .idea/
docs/_build/

View File

@@ -6,15 +6,20 @@ RUN chmod +x /tmp/compile.sh && \
/tmp/compile.sh && \ /tmp/compile.sh && \
rm -rf /tmp/* 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 entrypoint/ /opt/entrypoint
COPY confs/ /opt/confs COPY confs/ /opt/confs
COPY scripts/ /opt/scripts COPY scripts/ /opt/scripts
COPY fail2ban/ /opt/fail2ban
COPY logs/ /opt/logs
COPY lua/ /opt/lua COPY lua/ /opt/lua
COPY prepare.sh /tmp/prepare.sh 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 # fix CVE-2021-20205
RUN apk add "libjpeg-turbo>=2.1.0-r0" RUN apk add "libjpeg-turbo>=2.1.0-r0"

View File

@@ -6,15 +6,20 @@ RUN chmod +x /tmp/compile.sh && \
/tmp/compile.sh && \ /tmp/compile.sh && \
rm -rf /tmp/* 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 entrypoint/ /opt/entrypoint
COPY confs/ /opt/confs COPY confs/ /opt/confs
COPY scripts/ /opt/scripts COPY scripts/ /opt/scripts
COPY fail2ban/ /opt/fail2ban
COPY logs/ /opt/logs
COPY lua/ /opt/lua COPY lua/ /opt/lua
COPY prepare.sh /tmp/prepare.sh 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 # fix CVE-2021-20205
RUN apk add "libjpeg-turbo>=2.1.0-r0" RUN apk add "libjpeg-turbo>=2.1.0-r0"

View File

@@ -13,15 +13,20 @@ RUN chmod +x /tmp/compile.sh && \
/tmp/compile.sh && \ /tmp/compile.sh && \
rm -rf /tmp/* 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 entrypoint/ /opt/entrypoint
COPY confs/ /opt/confs COPY confs/ /opt/confs
COPY scripts/ /opt/scripts COPY scripts/ /opt/scripts
COPY fail2ban/ /opt/fail2ban
COPY logs/ /opt/logs
COPY lua/ /opt/lua COPY lua/ /opt/lua
COPY prepare.sh /tmp/prepare.sh 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 # fix CVE-2021-20205
RUN apk add "libjpeg-turbo>=2.1.0-r0" RUN apk add "libjpeg-turbo>=2.1.0-r0"

View File

@@ -13,15 +13,20 @@ RUN chmod +x /tmp/compile.sh && \
/tmp/compile.sh && \ /tmp/compile.sh && \
rm -rf /tmp/* 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 entrypoint/ /opt/entrypoint
COPY confs/ /opt/confs COPY confs/ /opt/confs
COPY scripts/ /opt/scripts COPY scripts/ /opt/scripts
COPY fail2ban/ /opt/fail2ban
COPY logs/ /opt/logs
COPY lua/ /opt/lua COPY lua/ /opt/lua
COPY prepare.sh /tmp/prepare.sh 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 # fix CVE-2021-20205
RUN apk add "libjpeg-turbo>=2.1.0-r0" RUN apk add "libjpeg-turbo>=2.1.0-r0"

View File

@@ -6,15 +6,20 @@ RUN chmod +x /tmp/compile.sh && \
/tmp/compile.sh && \ /tmp/compile.sh && \
rm -rf /tmp/* 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 entrypoint/ /opt/entrypoint
COPY confs/ /opt/confs COPY confs/ /opt/confs
COPY scripts/ /opt/scripts COPY scripts/ /opt/scripts
COPY fail2ban/ /opt/fail2ban
COPY logs/ /opt/logs
COPY lua/ /opt/lua COPY lua/ /opt/lua
COPY prepare.sh /tmp/prepare.sh 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 # fix CVE-2021-20205
RUN apk add "libjpeg-turbo>=2.1.0-r0" RUN apk add "libjpeg-turbo>=2.1.0-r0"

1128
README.md

File diff suppressed because it is too large Load Diff

View File

@@ -1 +1 @@
1.2.4 1.2.5

View 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
}

View File

@@ -1 +0,0 @@
map $http_referer $bad_referrer { hostnames; default no; }

View File

@@ -1 +0,0 @@
map $http_user_agent $bad_user_agent { default no; }

View File

@@ -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_filter_module.so;
load_module /usr/lib/nginx/modules/ngx_http_brotli_static_module.so; load_module /usr/lib/nginx/modules/ngx_http_brotli_static_module.so;
# run as daemon # run in foreground
daemon on; daemon off;
# PID file # PID file
pid /tmp/nginx.pid; pid /tmp/nginx.pid;
@@ -25,9 +25,12 @@ pcre_jit on;
# config files for dynamic modules # config files for dynamic modules
include /etc/nginx/modules/*.conf; include /etc/nginx/modules/*.conf;
# max open files for each worker
worker_rlimit_nofile %WORKER_RLIMIT_NOFILE%;
events { events {
# max connections per worker # max connections per worker
worker_connections 1024; worker_connections %WORKER_CONNECTIONS%;
# epoll seems to be the best on Linux # epoll seems to be the best on Linux
use epoll; use epoll;
@@ -49,8 +52,8 @@ http {
# write logs to local syslog # write logs to local syslog
log_format logf '%LOG_FORMAT%'; log_format logf '%LOG_FORMAT%';
access_log syslog:server=unix:/tmp/log,nohostname,facility=local0,severity=notice logf; access_log /var/log/access.log logf;
error_log syslog:server=unix:/tmp/log,nohostname,facility=local0 notice; error_log /var/log/error.log info;
# temp paths # temp paths
proxy_temp_path /tmp/proxy_temp; proxy_temp_path /tmp/proxy_temp;
@@ -63,8 +66,8 @@ http {
reset_timedout_connection on; reset_timedout_connection on;
# timeouts # timeouts
client_body_timeout 12; client_body_timeout 10;
client_header_timeout 12; client_header_timeout 10;
keepalive_timeout 15; keepalive_timeout 15;
send_timeout 10; send_timeout 10;
@@ -81,6 +84,12 @@ http {
%BLACKLIST_IP_CACHE% %BLACKLIST_IP_CACHE%
%BLACKLIST_REVERSE_CACHE% %BLACKLIST_REVERSE_CACHE%
%DNSBL_CACHE% %DNSBL_CACHE%
%BLOCK_PROXIES%
%BLOCK_ABUSERS%
%BLOCK_TOR_EXIT_NODES%
%BLOCK_USER_AGENTS%
%BLOCK_REFERRERS%
%BAD_BEHAVIOR%
# crowdsec init # crowdsec init
%USE_CROWDSEC% %USE_CROWDSEC%
@@ -94,18 +103,15 @@ http {
# whitelist or blacklist country # whitelist or blacklist country
%USE_COUNTRY% %USE_COUNTRY%
# list of blocked user agents
%BLOCK_USER_AGENT%
# list of blocked referrers
%BLOCK_REFERRER%
# zone for proxy_cache # zone for proxy_cache
%PROXY_CACHE_PATH% %PROXY_CACHE_PATH%
# custom http confs # custom http confs
include /http-confs/*.conf; include /http-confs/*.conf;
# LUA init block
include /etc/nginx/init-lua.conf;
# default server when MULTISITE=yes # default server when MULTISITE=yes
%MULTISITE_DEFAULT_SERVER% %MULTISITE_DEFAULT_SERVER%

View File

View File

11
confs/site/log-lua.conf Normal file
View 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
}

View File

@@ -6,8 +6,11 @@ access_by_lua_block {
local use_lets_encrypt = %USE_LETS_ENCRYPT% local use_lets_encrypt = %USE_LETS_ENCRYPT%
local use_whitelist_ip = %USE_WHITELIST_IP% local use_whitelist_ip = %USE_WHITELIST_IP%
local use_whitelist_reverse = %USE_WHITELIST_REVERSE% local use_whitelist_reverse = %USE_WHITELIST_REVERSE%
local use_user_agent = %USE_USER_AGENT% local use_user_agents = %USE_USER_AGENTS%
local use_referrer = %USE_REFERRER% 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_country = %USE_COUNTRY%
local use_blacklist_ip = %USE_BLACKLIST_IP% local use_blacklist_ip = %USE_BLACKLIST_IP%
local use_blacklist_reverse = %USE_BLACKLIST_REVERSE% local use_blacklist_reverse = %USE_BLACKLIST_REVERSE%
@@ -17,6 +20,7 @@ local use_antibot_cookie = %USE_ANTIBOT_COOKIE%
local use_antibot_javascript = %USE_ANTIBOT_JAVASCRIPT% local use_antibot_javascript = %USE_ANTIBOT_JAVASCRIPT%
local use_antibot_captcha = %USE_ANTIBOT_CAPTCHA% local use_antibot_captcha = %USE_ANTIBOT_CAPTCHA%
local use_antibot_recaptcha = %USE_ANTIBOT_RECAPTCHA% local use_antibot_recaptcha = %USE_ANTIBOT_RECAPTCHA%
local use_bad_behavior = %USE_BAD_BEHAVIOR%
-- include LUA code -- include LUA code
local whitelist = require "whitelist" local whitelist = require "whitelist"
@@ -26,6 +30,8 @@ local cookie = require "cookie"
local javascript = require "javascript" local javascript = require "javascript"
local captcha = require "captcha" local captcha = require "captcha"
local recaptcha = require "recaptcha" local recaptcha = require "recaptcha"
local iputils = require "resty.iputils"
local behavior = require "behavior"
-- user variables -- user variables
local antibot_uri = "%ANTIBOT_URI%" local antibot_uri = "%ANTIBOT_URI%"
@@ -94,26 +100,93 @@ if use_blacklist_reverse and not blacklist.reverse_cached() then
end end
end end
-- check if user-agent is allowed -- check if IP is banned because of "bad behavior"
if use_user_agent and ngx.var.bad_user_agent == "yes" then if use_bad_behavior and behavior.is_banned() then
local block = false ngx.log(ngx.NOTICE, "[BLOCK] IP " .. ngx.var.remote_addr .. " is banned because of bad behavior")
for k, v in pairs(whitelist_user_agent) do ngx.exit(ngx.HTTP_FORBIDDEN)
if string.match(ngx.var.http_user_agent, v) then end
ngx.log(ngx.NOTICE, "[ALLOW] User-Agent " .. ngx.var.http_user_agent .. " is whitelisted")
block = false -- check if IP is in proxies list
break if use_proxies then
end local value, flags = ngx.shared.proxies_data:get(iputils.ip2bin(ngx.var.remote_addr))
end if value ~= nil then
if block then ngx.log(ngx.NOTICE, "[BLOCK] IP " .. ngx.var.remote_addr .. " is in proxies list")
ngx.log(ngx.NOTICE, "[BLOCK] User-Agent " .. ngx.var.http_user_agent .. " is blacklisted")
ngx.exit(ngx.HTTP_FORBIDDEN) ngx.exit(ngx.HTTP_FORBIDDEN)
end end
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 -- check if referrer is allowed
if use_referrer and ngx.var.bad_referrer == "yes" then if use_referrer and ngx.var.http_referer ~= nil then
ngx.log(ngx.NOTICE, "[BLOCK] Referrer " .. ngx.var.http_referer .. " is blacklisted") local value, flags = ngx.shared.referrers_cache:get(ngx.var.http_referer)
ngx.exit(ngx.HTTP_FORBIDDEN) 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 end
-- check if country is allowed -- check if country is allowed

View File

@@ -49,7 +49,7 @@ SecResponseBodyLimit 524288
SecResponseBodyLimitAction ProcessPartial SecResponseBodyLimitAction ProcessPartial
# log usefull stuff # log usefull stuff
SecAuditEngine RelevantOnly SecAuditEngine %MODSECURITY_SEC_AUDIT_ENGINE%
SecAuditLogType Serial SecAuditLogType Serial
SecAuditLog /var/log/nginx/modsec_audit.log SecAuditLog /var/log/nginx/modsec_audit.log

View File

@@ -4,7 +4,7 @@ server {
%FASTCGI_PATH% %FASTCGI_PATH%
%SERVER_CONF% %SERVER_CONF%
%PROXY_REAL_IP% %PROXY_REAL_IP%
%MAIN_LUA% %INCLUDE_LUA%
%USE_MODSECURITY% %USE_MODSECURITY%
%LISTEN_HTTP% %LISTEN_HTTP%
%USE_HTTPS% %USE_HTTPS%
@@ -27,12 +27,8 @@ server {
%REFERRER_POLICY% %REFERRER_POLICY%
%FEATURE_POLICY% %FEATURE_POLICY%
%PERMISSIONS_POLICY% %PERMISSIONS_POLICY%
%BLOCK_TOR_EXIT_NODE%
%BLOCK_PROXIES%
%BLOCK_ABUSERS%
%COOKIE_FLAGS% %COOKIE_FLAGS%
%ERRORS% %ERRORS%
%USE_FAIL2BAN%
%USE_CLIENT_CACHE% %USE_CLIENT_CACHE%
%USE_GZIP% %USE_GZIP%
%USE_BROTLI% %USE_BROTLI%

4
dependencies.sh Normal file
View 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
View 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
View 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']

View 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 : *&lt;directive&gt; &lt;allow list&gt;*
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
View 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
View 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
View 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
View 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
View File

@@ -0,0 +1,3 @@
sphinx
sphinx-rtd-theme
myst-parser

235
docs/security_tuning.md Normal file
View 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
View 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
View 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

View File

@@ -6,6 +6,8 @@ HTTP_PORT="${HTTP_PORT-8080}"
HTTPS_PORT="${HTTPS_PORT-8443}" HTTPS_PORT="${HTTPS_PORT-8443}"
MAX_CLIENT_SIZE="${MAX_CLIENT_SIZE-10m}" MAX_CLIENT_SIZE="${MAX_CLIENT_SIZE-10m}"
SERVER_TOKENS="${SERVER_TOKENS-off}" SERVER_TOKENS="${SERVER_TOKENS-off}"
WORKER_CONNECTIONS="${WORKER_CONNECTIONS-1024}"
WORKER_RLIMIT_NOFILE="${WORKER_RLIMIT_NOFILE-2048}"
USE_CLIENT_CACHE="${USE_CLIENT_CACHE-no}" 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_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}" 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}" STRICT_TRANSPORT_SECURITY="${STRICT_TRANSPORT_SECURITY-max-age=31536000}"
USE_MODSECURITY="${USE_MODSECURITY-yes}" USE_MODSECURITY="${USE_MODSECURITY-yes}"
USE_MODSECURITY_CRS="${USE_MODSECURITY_CRS-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_FLAGS="${COOKIE_FLAGS-* HttpOnly SameSite=Lax}"
COOKIE_AUTO_SECURE_FLAG="${COOKIE_AUTO_SECURE_FLAG-yes}" COOKIE_AUTO_SECURE_FLAG="${COOKIE_AUTO_SECURE_FLAG-yes}"
SERVE_FILES="${SERVE_FILES-yes}" SERVE_FILES="${SERVE_FILES-yes}"
WRITE_ACCESS="${WRITE_ACCESS-no}"
REDIRECT_HTTP_TO_HTTPS="${REDIRECT_HTTP_TO_HTTPS-no}" REDIRECT_HTTP_TO_HTTPS="${REDIRECT_HTTP_TO_HTTPS-no}"
LISTEN_HTTP="${LISTEN_HTTP-yes}" LISTEN_HTTP="${LISTEN_HTTP-yes}"
USE_FAIL2BAN="${USE_FAIL2BAN-yes}" USE_BAD_BEHAVIOR="${USE_BAD_BEHAVIOR-yes}"
FAIL2BAN_STATUS_CODES="${FAIL2BAN_STATUS_CODES-400|401|403|404|405|444}" BAD_BEHAVIOR_STATUS_CODES="${BAD_BEHAVIOR_STATUS_CODES-400 401 403 404 405 429 444}"
FAIL2BAN_BANTIME="${FAIL2BAN_BANTIME-3600}" BAD_BEHAVIOR_THRESHOLD="${BAD_BEHAVIOR_THRESHOLD-10}"
FAIL2BAN_FINDTIME="${FAIL2BAN_FINDTIME-60}" BAD_BEHAVIOR_BAN_TIME="${BAD_BEHAVIOR_BAN_TIME-86400}"
FAIL2BAN_MAXRETRY="${FAIL2BAN_MAXRETRY-15}" BAD_BEHAVIOR_COUNT_TIME="${BAD_BEHAVIOR_COUNT_TIME-60}"
FAIL2BAN_IGNOREIP="${FAIL2BAN_IGNOREIP-127.0.0.1/8 192.168.0.0/16 172.16.0.0/12 10.0.0.0/8}"
USE_CLAMAV_UPLOAD="${USE_CLAMAV_UPLOAD-yes}" USE_CLAMAV_UPLOAD="${USE_CLAMAV_UPLOAD-yes}"
USE_CLAMAV_SCAN="${USE_CLAMAV_SCAN-yes}" USE_CLAMAV_SCAN="${USE_CLAMAV_SCAN-yes}"
USE_CLAMAV_SCAN_CRON="${USE_CLAMAV_SCAN_CRON-30 1 * * *}" 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}" AUTH_BASIC_PASSWORD="${AUTH_BASIC_PASSWORD-changeme}"
USE_CUSTOM_HTTPS="${USE_CUSTOM_HTTPS-no}" USE_CUSTOM_HTTPS="${USE_CUSTOM_HTTPS-no}"
ROOT_FOLDER="${ROOT_FOLDER-/www}" ROOT_FOLDER="${ROOT_FOLDER-/www}"
LOGROTATE_MINSIZE="${LOGROTATE_MINSIZE-10M}" ROOT_SITE_SUBFOLDER="${ROOT_SITE_SUBFOLDER-}"
LOGROTATE_MAXAGE="${LOGROTATE_MAXAGE-7}"
LOGROTATE_CRON="${LOGROTATE_CRON-0 0 * * *}"
DNS_RESOLVERS="${DNS_RESOLVERS-127.0.0.11}" DNS_RESOLVERS="${DNS_RESOLVERS-127.0.0.11}"
USE_WHITELIST_IP="${USE_WHITELIST_IP-yes}" 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}" 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}"
@@ -130,3 +129,4 @@ USE_API="${USE_API-no}"
API_URI="${API_URI-random}" 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}" 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}" SWARM_MODE="${SWARM_MODE-no}"

View File

@@ -5,25 +5,13 @@
echo "[*] Starting bunkerized-nginx ..." 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 # trap SIGTERM and SIGINT
function trap_exit() { function trap_exit() {
echo "[*] Catched stop operation" echo "[*] Catched stop operation"
echo "[*] Stopping crond ..." echo "[*] Stopping crond ..."
pkill -TERM crond pkill -TERM crond
if [ "$USE_FAIL2BAN" = "yes" ] ; then
echo "[*] Stopping fail2ban"
fail2ban-client stop > /dev/null
fi
echo "[*] Stopping nginx ..." echo "[*] Stopping nginx ..."
/usr/sbin/nginx -s stop /usr/sbin/nginx -s stop
echo "[*] Stopping rsyslogd ..."
pkill -TERM rsyslogd
pkill -TERM tail
} }
trap "trap_exit" TERM INT QUIT trap "trap_exit" TERM INT QUIT
@@ -62,16 +50,10 @@ if [ ! -f "/opt/installed" ] ; then
exit 1 exit 1
fi fi
# logs config
/opt/entrypoint/logs.sh
# lua config # lua config
# TODO : move variables from /usr/local/lib/lua + multisite support ? # TODO : move variables from /usr/local/lib/lua + multisite support ?
/opt/entrypoint/lua.sh /opt/entrypoint/lua.sh
# fail2ban config
/opt/entrypoint/fail2ban.sh
# clamav config # clamav config
/opt/entrypoint/clamav.sh /opt/entrypoint/clamav.sh
@@ -103,9 +85,6 @@ else
echo "[*] Skipping configuration process" echo "[*] Skipping configuration process"
fi fi
# start rsyslogd
rsyslogd -i /tmp/rsyslogd.pid
# start crond # start crond
crond crond
@@ -124,22 +103,8 @@ fi
# run nginx # run nginx
echo "[*] Running nginx ..." echo "[*] Running nginx ..."
nginx nginx &
if [ "$?" -eq 0 ] ; then pid="$!"
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 /var/log/nginx/modsec_audit.log"
# start fail2ban
if [ "$USE_FAIL2BAN" = "yes" ] ; then
echo "[*] Running fail2ban ..."
fail2ban-server > /dev/null
LOGS="$LOGS /var/log/fail2ban.log"
fi
# autotest # autotest
if [ "$1" == "test" ] ; then if [ "$1" == "test" ] ; then
@@ -152,9 +117,8 @@ if [ "$1" == "test" ] ; then
exit 1 exit 1
fi fi
# display logs # wait for nginx
tail -F $LOGS & wait "$pid"
pid="$!"
while [ -f "/tmp/nginx.pid" ] ; do while [ -f "/tmp/nginx.pid" ] ; do
wait "$pid" wait "$pid"
done done

View File

@@ -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

View File

@@ -113,33 +113,52 @@ fi
# block bad UA # block bad UA
if [ "$(has_value BLOCK_USER_AGENT yes)" != "" ] ; then 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 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 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 fi
# block bad refferer # block bad refferer
if [ "$(has_value BLOCK_REFERRER yes)" != "" ] ; then 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 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 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 fi
# block TOR exit nodes # block TOR exit nodes
if [ "$(has_value BLOCK_TOR_EXIT_NODE yes)" != "" ] ; then if [ "$(has_value BLOCK_TOR_EXIT_NODE yes)" != "" ] ; then
echo "$BLOCK_TOR_EXIT_NODE_CRON /opt/scripts/exit-nodes.sh" >> /etc/crontabs/nginx 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 fi
# block proxies # block proxies
if [ "$(has_value BLOCK_PROXIES yes)" != "" ] ; then if [ "$(has_value BLOCK_PROXIES yes)" != "" ] ; then
echo "$BLOCK_PROXIES_CRON /opt/scripts/proxies.sh" >> /etc/crontabs/nginx 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 fi
# block abusers # block abusers
if [ "$(has_value BLOCK_ABUSERS yes)" != "" ] ; then if [ "$(has_value BLOCK_ABUSERS yes)" != "" ] ; then
echo "$BLOCK_ABUSERS_CRON /opt/scripts/abusers.sh" >> /etc/crontabs/nginx 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 fi
# DNS resolvers # DNS resolvers
@@ -201,9 +220,11 @@ else
replace_in_file "/etc/nginx/multisite-default-server.conf" "%MULTISITE_DISABLE_DEFAULT_SERVER%" "" replace_in_file "/etc/nginx/multisite-default-server.conf" "%MULTISITE_DISABLE_DEFAULT_SERVER%" ""
fi fi
# fail2ban setup # bad behavior
if [ "$(has_value USE_FAIL2BAN yes)" != "" ] ; then if [ "$(has_value USE_BAD_BEHAVIOR yes)" != "" ] ; then
echo "" > /etc/nginx/fail2ban-ip.conf 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 fi
# CrowdSec setup # CrowdSec setup
@@ -224,3 +245,7 @@ if [ "$USE_API" = "yes" ] ; then
else else
replace_in_file "/etc/nginx/nginx.conf" "%USE_API%" "" replace_in_file "/etc/nginx/nginx.conf" "%USE_API%" ""
fi 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"

View File

@@ -19,9 +19,9 @@ fi
# User-Agents # User-Agents
if [ "$(has_value BLOCK_USER_AGENT yes)" != "" ] ; then if [ "$(has_value BLOCK_USER_AGENT yes)" != "" ] ; then
if [ -f "/cache/map-user-agent.conf" ] ; then if [ -f "/cache/user-agents.list" ] ; then
echo "[*] Copying cached map-user-agent.conf ..." echo "[*] Copying cached user-agents.list ..."
cp /cache/map-user-agent.conf /etc/nginx/map-user-agent.conf cp /cache/user-agents.list /etc/nginx/user-agents.list
else else
echo "[*] Downloading bad user-agent list (in background) ..." echo "[*] Downloading bad user-agent list (in background) ..."
/opt/scripts/user-agents.sh > /dev/null 2>&1 & /opt/scripts/user-agents.sh > /dev/null 2>&1 &
@@ -30,9 +30,9 @@ fi
# Referrers # Referrers
if [ "$(has_value BLOCK_REFERRER yes)" != "" ] ; then if [ "$(has_value BLOCK_REFERRER yes)" != "" ] ; then
if [ -f "/cache/map-referrer.conf" ] ; then if [ -f "/cache/referrers.list" ] ; then
echo "[*] Copying cached map-referrer.conf ..." echo "[*] Copying cached referrers.list ..."
cp /cache/map-referrer.conf /etc/nginx/map-referrer.conf cp /cache/referrers.list /etc/nginx/referrers.list
else else
echo "[*] Downloading bad referrer list (in background) ..." echo "[*] Downloading bad referrer list (in background) ..."
/opt/scripts/referrers.sh > /dev/null 2>&1 & /opt/scripts/referrers.sh > /dev/null 2>&1 &
@@ -41,9 +41,9 @@ fi
# exit nodes # exit nodes
if [ "$(has_value BLOCK_TOR_EXIT_NODE yes)" != "" ] ; then if [ "$(has_value BLOCK_TOR_EXIT_NODE yes)" != "" ] ; then
if [ -f "/cache/block-tor-exit-node.conf" ] ; then if [ -f "/cache/tor-exit-nodes.list" ] ; then
echo "[*] Copying cached block-tor-exit-node.conf ..." echo "[*] Copying cached tor-exit-nodes.list ..."
cp /cache/block-tor-exit-node.conf /etc/nginx/block-tor-exit-node.conf cp /cache/tor-exit-nodes.list /etc/nginx/tor-exit-nodes.list
else else
echo "[*] Downloading tor exit nodes list (in background) ..." echo "[*] Downloading tor exit nodes list (in background) ..."
/opt/scripts/exit-nodes.sh > /dev/null 2>&1 & /opt/scripts/exit-nodes.sh > /dev/null 2>&1 &
@@ -52,9 +52,9 @@ fi
# proxies # proxies
if [ "$(has_value BLOCK_PROXIES yes)" != "" ] ; then if [ "$(has_value BLOCK_PROXIES yes)" != "" ] ; then
if [ -f "/cache/block-proxies.conf" ] ; then if [ -f "/cache/proxies.list" ] ; then
echo "[*] Copying cached block-proxies.conf ..." echo "[*] Copying cached proxies.list ..."
cp /cache/block-proxies.conf /etc/nginx/block-proxies.conf cp /cache/proxies.list /etc/nginx/proxies.list
else else
echo "[*] Downloading proxies list (in background) ..." echo "[*] Downloading proxies list (in background) ..."
/opt/scripts/proxies.sh > /dev/null 2>&1 & /opt/scripts/proxies.sh > /dev/null 2>&1 &
@@ -63,9 +63,9 @@ fi
# abusers # abusers
if [ "$(has_value BLOCK_ABUSERS yes)" != "" ] ; then if [ "$(has_value BLOCK_ABUSERS yes)" != "" ] ; then
if [ -f "/cache/block-abusers.conf" ] ; then if [ -f "/cache/abusers.list" ] ; then
echo "[*] Copying cached block-abusers.conf ..." echo "[*] Copying cached abusers.list ..."
cp /cache/block-abusers.conf /etc/nginx/block-abusers.conf cp /cache/abusers.list /etc/nginx/abusers.list
else else
echo "[*] Downloading abusers list (in background) ..." echo "[*] Downloading abusers list (in background) ..."
/opt/scripts/abusers.sh > /dev/null 2>&1 & /opt/scripts/abusers.sh > /dev/null 2>&1 &

View File

@@ -1,28 +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
# setup rsyslog
if [ "$REMOTE_SYSLOG" != "" ] ; then
replace_in_file "/etc/rsyslog.conf" "%REMOTE_SYSLOG%" "local0.* @${REMOTE_SYSLOG};rawFormat"
else
replace_in_file "/etc/rsyslog.conf" "%REMOTE_SYSLOG%" ""
fi

View File

@@ -33,6 +33,13 @@ replace_in_file "/usr/local/lib/lua/blacklist.lua" "%BLACKLIST_REVERSE_LIST%" "$
list=$(spaces_to_lua "$DNSBL_LIST") list=$(spaces_to_lua "$DNSBL_LIST")
replace_in_file "/usr/local/lib/lua/dnsbl.lua" "%DNSBL_LIST%" "$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 # CrowdSec setup
if [ "$(has_value USE_CROWDSEC yes)" != "" ] ; then 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_HOST%" "$CROWDSEC_HOST"

View File

@@ -22,6 +22,10 @@ if [ "$MULTISITE" = "yes" ] ; then
done done
fi 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" set | grep -E -v "^(HOSTNAME|PWD|PKG_RELEASE|NJS_VERSION|SHLVL|PATH|_|NGINX_VERSION|HOME)=" > "${NGINX_PREFIX}nginx.env"
if [ "$MULTISITE" = "yes" ] ; then if [ "$MULTISITE" = "yes" ] ; then
for server in $SERVER_NAME ; do for server in $SERVER_NAME ; do
@@ -34,7 +38,7 @@ fi
cp /opt/confs/site/* "$NGINX_PREFIX" cp /opt/confs/site/* "$NGINX_PREFIX"
# replace paths # 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 if [ "$MULTISITE" = "yes" ] ; then
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" "%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;" replace_in_file "${NGINX_PREFIX}server.conf" "%PRE_SERVER_CONF%" "include /pre-server-confs/*.conf;\ninclude /pre-server-confs/${first_server}/*.conf;"
@@ -274,7 +278,7 @@ fi
# block bad UA # block bad UA
if [ "$BLOCK_USER_AGENT" = "yes" ] ; then 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 if [ "$WHITELIST_USER_AGENT" != "" ] ; then
list=$(spaces_to_lua "$WHITELIST_USER_AGENT") list=$(spaces_to_lua "$WHITELIST_USER_AGENT")
replace_in_file "${NGINX_PREFIX}main-lua.conf" "%WHITELIST_USER_AGENT%" "$list" replace_in_file "${NGINX_PREFIX}main-lua.conf" "%WHITELIST_USER_AGENT%" "$list"
@@ -282,7 +286,7 @@ if [ "$BLOCK_USER_AGENT" = "yes" ] ; then
replace_in_file "${NGINX_PREFIX}main-lua.conf" "%WHITELIST_USER_AGENT%" "" replace_in_file "${NGINX_PREFIX}main-lua.conf" "%WHITELIST_USER_AGENT%" ""
fi fi
else 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%" "" replace_in_file "${NGINX_PREFIX}main-lua.conf" "%WHITELIST_USER_AGENT%" ""
fi fi
@@ -296,30 +300,30 @@ fi
# block bad referrer # block bad referrer
if [ "$BLOCK_REFERRER" = "yes" ] ; then 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 else
replace_in_file "${NGINX_PREFIX}main-lua.conf" "%USE_REFERRER%" "false" replace_in_file "${NGINX_PREFIX}main-lua.conf" "%USE_REFERRER%" "false"
fi fi
# block TOR exit nodes # block TOR exit nodes
if [ "$BLOCK_TOR_EXIT_NODE" = "yes" ] ; then 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 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 fi
# block proxies # block proxies
if [ "$BLOCK_PROXIES" = "yes" ] ; then 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 else
replace_in_file "${NGINX_PREFIX}server.conf" "%BLOCK_PROXIES%" "" replace_in_file "${NGINX_PREFIX}main-lua.conf" "%USE_PROXIES%" "false"
fi fi
# block abusers # block abusers
if [ "$BLOCK_ABUSERS" = "yes" ] ; then 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 else
replace_in_file "${NGINX_PREFIX}server.conf" "%BLOCK_ABUSERS%" "" replace_in_file "${NGINX_PREFIX}main-lua.conf" "%USE_ABUSERS%" "false"
fi fi
# HTTPS config # HTTPS config
@@ -390,6 +394,7 @@ fi
if [ "$USE_MODSECURITY" = "yes" ] ; then if [ "$USE_MODSECURITY" = "yes" ] ; then
replace_in_file "${NGINX_PREFIX}modsecurity.conf" "%MODSEC_RULES_FILE%" "${NGINX_PREFIX}modsecurity-rules.conf" replace_in_file "${NGINX_PREFIX}modsecurity.conf" "%MODSEC_RULES_FILE%" "${NGINX_PREFIX}modsecurity-rules.conf"
replace_in_file "${NGINX_PREFIX}server.conf" "%USE_MODSECURITY%" "include ${NGINX_PREFIX}modsecurity.conf;" 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 if [ "$MULTISITE" != "yes" ] ; then
modsec_custom="" modsec_custom=""
if ls /modsec-confs/*.conf > /dev/null 2>&1 ; then if ls /modsec-confs/*.conf > /dev/null 2>&1 ; then
@@ -557,6 +562,15 @@ else
replace_in_file "${NGINX_PREFIX}main-lua.conf" "%INCLUDE_ANTIBOT_RECAPTCHA%" "" replace_in_file "${NGINX_PREFIX}main-lua.conf" "%INCLUDE_ANTIBOT_RECAPTCHA%" ""
fi 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 # request limiting
if [ "$USE_LIMIT_REQ" = "yes" ] ; then if [ "$USE_LIMIT_REQ" = "yes" ] ; then
replace_in_file "${NGINX_PREFIX}server.conf" "%LIMIT_REQ%" "include ${NGINX_PREFIX}limit-req.conf;" replace_in_file "${NGINX_PREFIX}server.conf" "%LIMIT_REQ%" "include ${NGINX_PREFIX}limit-req.conf;"
@@ -573,13 +587,6 @@ else
replace_in_file "${NGINX_PREFIX}server.conf" "%LIMIT_CONN%" "" replace_in_file "${NGINX_PREFIX}server.conf" "%LIMIT_CONN%" ""
fi 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 # clamav scan uploaded files
if [ "$USE_CLAMAV_UPLOAD" = "yes" ] ; then if [ "$USE_CLAMAV_UPLOAD" = "yes" ] ; then
replace_in_file "${NGINX_PREFIX}modsecurity-rules.conf" "%USE_CLAMAV_UPLOAD%" "include ${NGINX_PREFIX}modsecurity-clamav.conf" replace_in_file "${NGINX_PREFIX}modsecurity-rules.conf" "%USE_CLAMAV_UPLOAD%" "include ${NGINX_PREFIX}modsecurity-clamav.conf"

View File

@@ -31,6 +31,9 @@ services:
- autoconf:/etc/nginx - autoconf:/etc/nginx
depends_on: depends_on:
- mywww - mywww
- myapp1
- myapp2
- myapp3
myapp1: myapp1:
image: php:fpm image: php:fpm

View File

@@ -32,6 +32,9 @@ services:
- autoconf:/etc/nginx - autoconf:/etc/nginx
depends_on: depends_on:
- mywww - mywww
- myapp1
- myapp2
- myapp3
myapp1: myapp1:
build: js-app build: js-app

View File

@@ -30,7 +30,7 @@ services:
- nc.website.com_LIMIT_REQ_BURST=10 - 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_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_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: networks:
- net1 - net1
- net2 - net2

View File

@@ -0,0 +1,5 @@
<?php
echo "welcome to app1 !";
?>

View File

@@ -0,0 +1,5 @@
<?php
echo "welcome to app2 !";
?>

View File

@@ -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"]

View File

@@ -0,0 +1,5 @@
<?php
echo "welcome to app3 !";
?>

View File

@@ -0,0 +1,4 @@
#!/bin/sh
echo "[*] my-php started"
exec "$@"

View 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

View File

@@ -28,7 +28,7 @@ services:
- ALLOWED_METHODS=GET|POST|HEAD|COPY|DELETE|LOCK|MKCOL|MOVE|PROPFIND|PROPPATCH|PUT|UNLOCK|OPTIONS - ALLOWED_METHODS=GET|POST|HEAD|COPY|DELETE|LOCK|MKCOL|MOVE|PROPFIND|PROPPATCH|PUT|UNLOCK|OPTIONS
- X_FRAME_OPTIONS=SAMEORIGIN - X_FRAME_OPTIONS=SAMEORIGIN
- USE_GZIP=yes - USE_GZIP=yes
- FAIL2BAN_STATUS_CODE=400|401|403|405|444 - BAD_BEHAVIOR_STATUS_CODE=400|401|403|405|444
mync: mync:
image: nextcloud:21-fpm image: nextcloud:21-fpm

View File

@@ -20,7 +20,7 @@ services:
- BLOCK_TOR_EXIT_NODE=no - BLOCK_TOR_EXIT_NODE=no
- BLOCK_ABUSERS=no - BLOCK_ABUSERS=no
- BLOCK_PROXIES=no - BLOCK_PROXIES=no
- USE_FAIL2BAN=no - USE_BAD_BEHAVIOR=no
- USE_DNSBL=no - USE_DNSBL=no
- USE_WHITELIST_IP=no - USE_WHITELIST_IP=no
- USE_WHITELIST_REVERSE=no - USE_WHITELIST_REVERSE=no

View File

@@ -1,8 +0,0 @@
[Definition]
actionstart = echo "" > /etc/nginx/fail2ban-ip.conf && /usr/sbin/nginx -s reload
actionstop = echo "" > /etc/nginx/fail2ban-ip.conf && /usr/sbin/nginx -s reload
actioncheck =
actionflush = echo "" > /etc/nginx/fail2ban-ip.conf && /usr/sbin/nginx -s reload
actionban = echo -n "deny <ip>;" >> /etc/nginx/fail2ban-ip.conf && /usr/sbin/nginx -s reload
actionunban = sed -i "s/deny <ip>;//g" /etc/nginx/fail2ban-ip.conf && /usr/sbin/nginx -s reload

View File

@@ -1,7 +0,0 @@
[INCLUDES]
before = common.conf
[Definition]
failregex = <HOST> - .* \[.*\] ".*" (%FAIL2BAN_STATUS_CODES%) .* ".*" ".*"
ignoreregex =
datepattern = %%d/%%b/%%Y:%%H:%%M:%%S

View File

@@ -1,8 +0,0 @@
[nginx-filter]
bantime = %FAIL2BAN_BANTIME%
findtime = %FAIL2BAN_FINDTIME%
maxretry = %FAIL2BAN_MAXRETRY%
ignoreip = %FAIL2BAN_IGNOREIP%
enabled = true
action = nginx-action
logpath = /var/log/access.log

View File

@@ -1,23 +0,0 @@
/var/log/*.log /var/log/clamav/*.log /var/log/nginx/*.log /var/log/letsencrypt/*.log {
# compress old files using gzip
compress
# rotate everyday
daily
# remove old logs after X days
maxage %LOGROTATE_MAXAGE%
rotate %LOGROTATE_MAXAGE%
# no errors if a file is missing
missingok
# disable mailing
nomail
# mininum size of a logfile before rotating
minsize %LOGROTATE_MINSIZE%
# make a copy and truncate the files
copytruncate
}

View File

@@ -1,29 +0,0 @@
#### Global directives ####
# Sets the directory that rsyslog uses for work files.
$WorkDirectory /var/lib/rsyslog
# Sets default permissions for all log files.
$FileOwner nginx
$FileGroup nginx
$FileCreateMode 0660
$DirCreateMode 0770
$Umask 0007
# Include all config files in /etc/rsyslog.d/.
include(file="/etc/rsyslog.d/*.conf" mode="optional")
#### Modules ####
# Provides --MARK-- message capability.
#module(load="immark")
# Provides support for local system logging (e.g. via logger command).
module(load="imuxsock" SysSock.Name="/tmp/log")
# Nginx
$template rawFormat,"%msg:2:2048%\n"
local0.=notice /var/log/access.log;rawFormat
local0.*;local0.!=notice /var/log/error.log;rawFormat
%REMOTE_SYSLOG%

37
lua/behavior.lua Normal file
View File

@@ -0,0 +1,37 @@
local M = {}
local status_codes = {%STATUS_CODES%}
local threshold = %THRESHOLD%
local count_time = %COUNT_TIME%
local ban_time = %BAN_TIME%
function M.is_banned ()
return ngx.shared.behavior_ban:get(ngx.var.remote_addr) == true
end
function M.count ()
for k, v in ipairs(status_codes) do
if v == tostring(ngx.status) then
local count = ngx.shared.behavior_count:get(ngx.var.remote_addr)
if count == nil then
count = 0
end
count = count + 1
local ok, err = ngx.shared.behavior_count:set(ngx.var.remote_addr, count, count_time)
if not ok then
ngx.log(ngx.ERR, "[BEHAVIOR] not enough memory allocated to behavior_ip_count")
return
end
if count >= threshold then
ngx.log(ngx.NOTICE, "[BEHAVIOR] threshold reached for " .. ngx.var.remote_addr .. " (" .. count .. " / " .. threshold .. ") : IP is banned for " .. ban_time .. " seconds")
local ok, err = ngx.shared.behavior_ban:safe_set(ngx.var.remote_addr, true, ban_time)
if not ok then
ngx.log(ngx.ERR, "[BEHAVIOR] not enough memory allocated to behavior_ip_ban")
return
end
end
break
end
end
end
return M

69
lua/dataloader.lua Normal file
View File

@@ -0,0 +1,69 @@
local M = {}
local iputils = require "resty.iputils"
function M.flush_dict (dict)
local keys = dict:get_keys(0)
for i, key in ipairs(keys) do
dict:delete(key)
end
end
function M.load_ip (path, dict)
M.flush_dict(dict)
local file = io.open(path, "r")
if not file then
ngx.log(ngx.ERR, "[INIT] can't open " .. path)
else
io.input(file)
local i = 0
for line in io.lines() do
local continue = true
if string.match(line, "/") then
local lower, upper = iputils.parse_cidr(line)
local bin_ip = lower
while bin_ip <= upper do
local ok, err = dict:safe_set(bin_ip, true, 0)
if not ok then
ngx.log(ngx.ERR, "[INIT] not enough memory allocated to load data from " .. path)
continue = false
break
end
bin_ip = bin_ip + 1
i = i + 1
end
else
local bin_ip, bin_octets = iputils.ip2bin(line)
dict:set(bin_ip, true, 0)
i = i + 1
end
if not continue then
break
end
end
ngx.log(ngx.ERR, "[INIT] *NOT AN ERROR* loaded " .. tostring(i) .. " IPs from " .. path)
io.close(file)
end
end
function M.load_raw (path, dict)
M.flush_dict(dict)
local file = io.open(path, "r")
if not file then
ngx.log(ngx.ERR, "[INIT] can't open " .. path)
else
io.input(file)
local i = 0
for line in io.lines() do
local ok, err = dict:safe_set(line, true, 0)
if not ok then
ngx.log(ngx.ERR, "[INIT] not enough memory allocated to load data from " .. path)
break
end
i = i + 1
end
ngx.log(ngx.ERR, "[INIT] *NOT AN ERROR* loaded " .. tostring(i) .. " entries from " .. path)
io.close(file)
end
end
return M

View File

@@ -1,11 +1,5 @@
#!/bin/sh #!/bin/sh
# install dependencies
apk --no-cache add certbot libstdc++ libmaxminddb geoip pcre yajl fail2ban clamav apache2-utils rsyslog openssl lua libgd go jq mariadb-connector-c bash brotli
# temp fix ?
chmod 644 /usr/lib/python3.8/site-packages/fail2ban-*/*
# custom entrypoint # custom entrypoint
mkdir /opt/entrypoint.d mkdir /opt/entrypoint.d
@@ -28,19 +22,20 @@ chmod -R 770 /etc/nginx
# prepare /var/log # prepare /var/log
rm -f /var/log/nginx/* rm -f /var/log/nginx/*
chown root:nginx /var/log/nginx chown root:nginx /var/log/nginx
touch /var/log/nginx/error.log /var/log/nginx/modsec_audit.log /var/log/jobs.log
chown nginx:nginx /var/log/nginx/*
chmod -R 770 /var/log/nginx chmod -R 770 /var/log/nginx
touch /var/log/access.log /var/log/error.log /var/log/jobs.log /var/log/fail2ban.log ln -s /proc/1/fd/2 /var/log/nginx/error.log
chown nginx:nginx /var/log/*.log ln -s /proc/1/fd/2 /var/log/nginx/modsec_audit.log
chmod 770 /var/log/*.log ln -s /proc/1/fd/1 /var/log/access.log
ln -s /proc/1/fd/2 /var/log/error.log
ln -s /proc/1/fd/1 /var/log/jobs.log
ln -s /proc/1/fd/1 /var/log/clamav.log
mkdir /var/log/letsencrypt mkdir /var/log/letsencrypt
chown nginx:nginx /var/log/letsencrypt chown nginx:nginx /var/log/letsencrypt
chmod 770 /var/log/letsencrypt chmod 770 /var/log/letsencrypt
touch /var/log/clamav.log rm -rf /var/log/clamav/*
chown root:nginx /var/log/clamav.log chown root:nginx /var/log/clamav
chmod 770 /var/log/clamav.log chmod 770 /var/log/clamav
find /var/log -type f -exec chmod 0774 {} \; ln -s /proc/1/fd/1 /var/log/freshclam.log
# prepare /acme-challenge # prepare /acme-challenge
mkdir /acme-challenge mkdir /acme-challenge
@@ -57,16 +52,6 @@ mkdir /var/lib/letsencrypt
chown root:nginx /var/lib/letsencrypt chown root:nginx /var/lib/letsencrypt
chmod 770 /var/lib/letsencrypt chmod 770 /var/lib/letsencrypt
# prepare /etc/fail2ban
rm -rf /etc/fail2ban/jail.d/*.conf
chown -R root:nginx /etc/fail2ban
find /etc/fail2ban -type f -exec chmod 0760 {} \;
find /etc/fail2ban -type d -exec chmod 0770 {} \;
# prepare /var/run/fail2ban and /var/lib/fail2ban
chown -R root:nginx /var/run/fail2ban /var/lib/fail2ban
chmod -R 770 /var/run/fail2ban /var/lib/fail2ban
# prepare /usr/local/lib/lua # prepare /usr/local/lib/lua
chown -R root:nginx /usr/local/lib/lua chown -R root:nginx /usr/local/lib/lua
chmod 770 /usr/local/lib/lua chmod 770 /usr/local/lib/lua
@@ -79,20 +64,11 @@ mkdir /cache
chown root:nginx /cache chown root:nginx /cache
chmod 770 /cache chmod 770 /cache
# prepare misc files
chown root:nginx /etc/rsyslog.conf /etc/logrotate.conf
chmod 660 /etc/rsyslog.conf /etc/logrotate.conf
chown root:nginx /etc/rsyslog.conf
# prepare /etc/crontabs/nginx # prepare /etc/crontabs/nginx
touch /etc/crontabs/nginx touch /etc/crontabs/nginx
chown root:nginx /etc/crontabs/nginx chown root:nginx /etc/crontabs/nginx
chmod 660 /etc/crontabs/nginx chmod 660 /etc/crontabs/nginx
# prepare /var/log/clamav
chown root:nginx /var/log/clamav
chmod 770 /var/log/clamav
# prepare /var/lib/clamav # prepare /var/lib/clamav
chown root:nginx /var/lib/clamav chown root:nginx /var/lib/clamav
chmod 770 /var/lib/clamav chmod 770 /var/lib/clamav

View File

@@ -4,48 +4,43 @@
. /opt/entrypoint/utils.sh . /opt/entrypoint/utils.sh
# copy old conf to cache # copy old conf to cache
cp /etc/nginx/block-abusers.conf /cache cp /etc/nginx/abusers.list /cache
# generate the new conf
curl -s "https://iplists.firehol.org/files/firehol_abusers_30d.netset" | \
grep -E "^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}/?[0-9]*$" > /tmp/abusers.list
# if we are running nginx # if we are running nginx
if [ -f /tmp/nginx.pid ] ; then if [ -f /tmp/nginx.pid ] ; then
RELOAD="/usr/sbin/nginx -s reload > /dev/null 2>&1" RELOAD="/usr/sbin/nginx -s reload"
# if we are in autoconf # if we are in autoconf
elif [ -S /tmp/autoconf.sock ] ; then elif [ -S /tmp/autoconf.sock ] ; then
RELOAD="/opt/entrypoint/reload.py" RELOAD="/opt/entrypoint/reload.py"
fi fi
# generate the new conf
curl -s "https://iplists.firehol.org/files/firehol_abusers_30d.netset" | grep -v "^\#.*" |
while read entry ; do
check=$(echo $entry | grep -E "^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}/?[0-9]*$")
if [ "$check" != "" ] ; then
echo "deny ${entry};" >> /tmp/block-abusers.conf
fi
done
# check if we have at least 1 line # check if we have at least 1 line
lines="$(wc -l /tmp/block-abusers.conf | cut -d ' ' -f 1)" lines="$(wc -l /tmp/abusers.list | cut -d ' ' -f 1)"
if [ "$lines" -gt 1 ] ; then if [ "$lines" -gt 1 ] ; then
job_log "[BLACKLIST] abusers list updated ($lines entries)" job_log "[BLACKLIST] abusers list updated ($lines entries)"
# reload nginx with the new config # reload nginx with the new config
mv /tmp/block-abusers.conf /etc/nginx/block-abusers.conf mv /tmp/abusers.list /etc/nginx/abusers.list
if [ "$RELOAD" != "" ] ; then if [ "$RELOAD" != "" ] ; then
$RELOAD $RELOAD > /dev/null 2>&1
# new config is ok : save it in the cache # new config is ok : save it in the cache
if [ "$?" -eq 0 ] ; then if [ "$?" -eq 0 ] ; then
cp /etc/nginx/block-abusers.conf /cache cp /etc/nginx/abusers.list /cache
job_log "[NGINX] successfull nginx reload after abusers list update" job_log "[NGINX] successfull nginx reload after abusers list update"
else else
job_log "[NGINX] failed nginx reload after abusers list update fallback to old list" job_log "[NGINX] failed nginx reload after abusers list update fallback to old list"
cp /cache/block-abusers.conf /etc/nginx cp /cache/abusers.list /etc/nginx
$RELOAD $RELOAD > /dev/null 2>&1
fi fi
else else
cp /etc/nginx/block-abusers.conf /cache cp /etc/nginx/abusers.list /cache
fi fi
else else
job_log "[BLACKLIST] can't update abusers list" job_log "[BLACKLIST] can't update abusers list"
fi fi
rm -f /tmp/block-abusers.conf 2> /dev/null rm -f /tmp/abusers.list 2> /dev/null

View File

@@ -7,15 +7,15 @@ job_log "[CERTBOT] certificates have been renewed"
# if we are running nginx # if we are running nginx
if [ -f /tmp/nginx.pid ] ; then if [ -f /tmp/nginx.pid ] ; then
RELOAD="/usr/sbin/nginx -s reload > /dev/null 2>&1" RELOAD="/usr/sbin/nginx -s reload"
# if we are in autoconf # if we are in autoconf
elif [ -S /tmp/autoconf.sock ] ; then elif [ -S /tmp/autoconf.sock ] ; then
RELOAD="echo reload > /tmp/autoconf.sock" RELOAD="/opt/entrypoint/reload.py"
fi fi
# reload nginx # reload nginx
if [ "$RELOAD" != "" ] ; then if [ "$RELOAD" != "" ] ; then
$RELOAD $RELOAD > /dev/null 2>&1
if [ "$?" -eq 0 ] ; then if [ "$?" -eq 0 ] ; then
job_log "[NGINX] successfull nginx reload after certbot renew" job_log "[NGINX] successfull nginx reload after certbot renew"
else else

View File

@@ -4,48 +4,42 @@
. /opt/entrypoint/utils.sh . /opt/entrypoint/utils.sh
# copy old conf to cache # copy old conf to cache
cp /etc/nginx/block-tor-exit-node.conf /cache cp /etc/nginx/tor-exit-nodes.list /cache
# generate the new conf
curl -s "https://iplists.firehol.org/files/tor_exits.ipset" | \
grep -E "^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}/?[0-9]*$" > /tmp/tor-exit-nodes.list
# if we are running nginx # if we are running nginx
if [ -f /tmp/nginx.pid ] ; then if [ -f /tmp/nginx.pid ] ; then
RELOAD="/usr/sbin/nginx -s reload > /dev/null 2>&1" RELOAD="/usr/sbin/nginx -s reload"
# if we are in autoconf # if we are in autoconf
elif [ -S /tmp/autoconf.sock ] ; then elif [ -S /tmp/autoconf.sock ] ; then
RELOAD="/opt/entrypoint/reload.py" RELOAD="/opt/entrypoint/reload.py"
fi fi
# generate the new conf
curl -s "https://iplists.firehol.org/files/tor_exits.ipset" | grep -v "^\#.*" |
while read entry ; do
check=$(echo $entry | grep -E "^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}/?[0-9]*$")
if [ "$check" != "" ] ; then
echo "deny ${entry};" >> /tmp/block-tor-exit-node.conf
fi
done
# check if we have at least 1 line # check if we have at least 1 line
lines="$(wc -l /tmp/block-tor-exit-node.conf | cut -d ' ' -f 1)" lines="$(wc -l /tmp/tor-exit-nodes.list | cut -d ' ' -f 1)"
if [ "$lines" -gt 1 ] ; then if [ "$lines" -gt 1 ] ; then
job_log "[BLACKLIST] TOR exit node list updated ($lines entries)" job_log "[BLACKLIST] TOR exit node list updated ($lines entries)"
# reload nginx with the new config # reload nginx with the new config
mv /tmp/block-tor-exit-node.conf /etc/nginx/block-tor-exit-node.conf mv /tmp/tor-exit-nodes.list /etc/nginx/tor-exit-nodes.list
if [ "$RELOAD" != "" ] ; then if [ "$RELOAD" != "" ] ; then
$RELOAD $RELOAD > /dev/null 2>&1
# new config is ok : save it in the cache # new config is ok : save it in the cache
if [ "$?" -eq 0 ] ; then if [ "$?" -eq 0 ] ; then
cp /etc/nginx/block-tor-exit-node.conf /cache cp /etc/nginx/tor-exit-nodes.list /cache
job_log "[NGINX] successfull nginx reload after TOR exit node list update" job_log "[NGINX] successfull nginx reload after TOR exit node list update"
else else
job_log "[NGINX] failed nginx reload after TOR exit node list update fallback to old list" job_log "[NGINX] failed nginx reload after TOR exit node list update fallback to old list"
cp /cache/block-tor-exit-node.conf /etc/nginx cp /cache/tor-exit-nodes.list /etc/nginx
$RELOAD $RELOAD > /dev/null 2>&1
fi fi
else else
cp /etc/nginx/block-tor-exit-node.conf /cache cp /etc/nginx/tor-exit-nodes.list /cache
fi fi
else else
job_log "[BLACKLIST] can't update TOR exit node list" job_log "[BLACKLIST] can't update TOR exit node list"
fi fi
rm -f /tmp/block-tor-exit-node.conf 2> /dev/null rm -f /tmp/tor-exit-nodes.list 2> /dev/null

View File

@@ -5,7 +5,7 @@
# if we are running nginx # if we are running nginx
if [ -f /tmp/nginx.pid ] ; then if [ -f /tmp/nginx.pid ] ; then
RELOAD="/usr/sbin/nginx -s reload > /dev/null 2>&1" RELOAD="/usr/sbin/nginx -s reload"
# if we are in autoconf # if we are in autoconf
elif [ -S /tmp/autoconf.sock ] ; then elif [ -S /tmp/autoconf.sock ] ; then
RELOAD="/opt/entrypoint/reload.py" RELOAD="/opt/entrypoint/reload.py"
@@ -22,7 +22,7 @@ if [ "$?" -eq 0 ] && [ -f /tmp/geoip.mmdb.gz ] ; then
fi fi
mv /tmp/geoip.mmdb /etc/nginx mv /tmp/geoip.mmdb /etc/nginx
if [ "$RELOAD" != "" ] ; then if [ "$RELOAD" != "" ] ; then
$RELOAD $RELOAD > /dev/null 2>&1
if [ "$?" -eq 0 ] ; then if [ "$?" -eq 0 ] ; then
cp /etc/nginx/geoip.mmdb /cache cp /etc/nginx/geoip.mmdb /cache
job_log "[NGINX] successfull nginx reload after GeoIP DB update" job_log "[NGINX] successfull nginx reload after GeoIP DB update"
@@ -30,7 +30,7 @@ if [ "$?" -eq 0 ] && [ -f /tmp/geoip.mmdb.gz ] ; then
job_log "[NGINX] failed nginx reload after GeoIP DB update" job_log "[NGINX] failed nginx reload after GeoIP DB update"
if [ -f /cache/geoip.mmdb ] ; then if [ -f /cache/geoip.mmdb ] ; then
cp /cache/geoip.mmdb /etc/nginx/geoip.mmdb cp /cache/geoip.mmdb /etc/nginx/geoip.mmdb
$RELOAD $RELOAD > /dev/null 2>&1
fi fi
fi fi
else else

View File

@@ -1,19 +0,0 @@
#!/bin/sh
# load some functions
. /opt/entrypoint/utils.sh
logrotate -f /etc/logrotate.conf > /dev/null 2>&1
pkill -HUP rsyslogd
fail2ban-client flushlogs
if [ -f /tmp/nginx.pid ] ; then
/usr/sbin/nginx -s reload > /dev/null 2>&1
if [ "$?" -eq 0 ] ; then
job_log "[NGINX] successfull nginx reload after logrotate"
else
job_log "[NGINX] failed nginx reload after logrotate"
fi
fi

View File

@@ -4,48 +4,43 @@
. /opt/entrypoint/utils.sh . /opt/entrypoint/utils.sh
# copy old conf to cache # copy old conf to cache
cp /etc/nginx/block-proxies.conf /cache cp /etc/nginx/proxies.list /cache
# generate the new conf
curl -s "https://iplists.firehol.org/files/firehol_proxies.netset" | \
grep -E "^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}/?[0-9]*$" > /tmp/proxies.list
# if we are running nginx # if we are running nginx
if [ -f /tmp/nginx.pid ] ; then if [ -f /tmp/nginx.pid ] ; then
RELOAD="/usr/sbin/nginx -s reload > /dev/null 2>&1" RELOAD="/usr/sbin/nginx -s reload"
# if we are in autoconf # if we are in autoconf
elif [ -S /tmp/autoconf.sock ] ; then elif [ -S /tmp/autoconf.sock ] ; then
RELOAD="/opt/entrypoint/reload.py" RELOAD="/opt/entrypoint/reload.py"
fi fi
# generate the new conf
curl -s "https://iplists.firehol.org/files/firehol_proxies.netset" | grep -v "^\#.*" |
while read entry ; do
check=$(echo $entry | grep -E "^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}/?[0-9]*$")
if [ "$check" != "" ] ; then
echo "deny ${entry};" >> /tmp/block-proxies.conf
fi
done
# check if we have at least 1 line # check if we have at least 1 line
lines="$(wc -l /tmp/block-proxies.conf | cut -d ' ' -f 1)" lines="$(wc -l /tmp/proxies.list | cut -d ' ' -f 1)"
if [ "$lines" -gt 1 ] ; then if [ "$lines" -gt 1 ] ; then
job_log "[BLACKLIST] proxies list updated ($lines entries)" job_log "[BLACKLIST] proxies list updated ($lines entries)"
# reload nginx with the new config # reload nginx with the new config
mv /tmp/block-proxies.conf /etc/nginx/block-proxies.conf mv /tmp/proxies.list /etc/nginx/proxies.list
if [ "$RELOAD" != "" ] ; then if [ "$RELOAD" != "" ] ; then
$RELOAD $RELOAD > /dev/null 2>&1
# new config is ok : save it in the cache # new config is ok : save it in the cache
if [ "$?" -eq 0 ] ; then if [ "$?" -eq 0 ] ; then
cp /etc/nginx/block-proxies.conf /cache cp /etc/nginx/proxies.list /cache
job_log "[NGINX] successfull nginx reload after proxies list update" job_log "[NGINX] successfull nginx reload after proxies list update"
else else
job_log "[NGINX] failed nginx reload after proxies list update fallback to old list" job_log "[NGINX] failed nginx reload after proxies list update fallback to old list"
cp /cache/block-proxies.conf /etc/nginx cp /cache/proxies.list /etc/nginx
$RELOAD $RELOAD > /dev/null 2>&1
fi fi
else else
cp /etc/nginx/block-proxies.conf /cache cp /etc/nginx/proxies.list /cache
fi fi
else else
job_log "[BLACKLIST] can't update proxies list" job_log "[BLACKLIST] can't update proxies list"
fi fi
rm -f /tmp/block-proxies.conf 2> /dev/null rm -f /tmp/proxies.list 2> /dev/null

View File

@@ -4,48 +4,45 @@
. /opt/entrypoint/utils.sh . /opt/entrypoint/utils.sh
# save old conf # save old conf
cp /etc/nginx/map-referrer.conf /cache cp /etc/nginx/referrers.list /cache
# generate new conf
BLACKLIST="$(curl -s https://raw.githubusercontent.com/mitchellkrogza/nginx-ultimate-bad-bot-blocker/master/_generator_lists/bad-referrers.list | sed 's:\.:%\.:g;s:\-:%\-:g')"
if [ "$?" -ne 0 ] ; then
job_log "[BLACKLIST] can't update referrers list"
echo -n "" > /tmp/referrers.list
else
echo -e "$BLACKLIST" > /tmp/referrers.list
fi
# if we are running nginx # if we are running nginx
if [ -f /tmp/nginx.pid ] ; then if [ -f /tmp/nginx.pid ] ; then
RELOAD="/usr/sbin/nginx -s reload > /dev/null 2>&1" RELOAD="/usr/sbin/nginx -s reload"
# if we are in autoconf # if we are in autoconf
elif [ -S /tmp/autoconf.sock ] ; then elif [ -S /tmp/autoconf.sock ] ; then
RELOAD="/opt/entrypoint/reload.py" RELOAD="/opt/entrypoint/reload.py"
fi fi
# generate new conf
BLACKLIST="$(curl -s https://raw.githubusercontent.com/mitchellkrogza/nginx-ultimate-bad-bot-blocker/master/_generator_lists/bad-referrers.list)"
if [ "$?" -ne 0 ] ; then
job_log "[BLACKLIST] can't update referrers list"
fi
DATA=""
IFS=$'\n'
for ref in $BLACKLIST ; do
DATA="${DATA}\"~${ref}\" yes;\n"
done
echo -e "map \$http_referer \$bad_referrer { hostnames; default no; $DATA }" > /tmp/map-referrer.conf
# check number of lines # check number of lines
lines="$(wc -l /tmp/map-referrer.conf | cut -d ' ' -f 1)" lines="$(wc -l /tmp/referrers.list | cut -d ' ' -f 1)"
if [ "$lines" -gt 1 ] ; then if [ "$lines" -gt 1 ] ; then
mv /tmp/map-referrer.conf /etc/nginx/map-referrer.conf mv /tmp/referrers.list /etc/nginx/referrers.list
job_log "[BLACKLIST] referrers list updated ($lines entries)" job_log "[BLACKLIST] referrers list updated ($lines entries)"
if [ "$RELOAD" != "" ] ; then if [ "$RELOAD" != "" ] ; then
$RELOAD $RELOAD > /dev/null 2>&1
if [ "$?" -eq 0 ] ; then if [ "$?" -eq 0 ] ; then
cp /etc/nginx/map-referrer.conf /cache cp /etc/nginx/referrers.list /cache
job_log "[NGINX] successfull nginx reload after referrers list update" job_log "[NGINX] successfull nginx reload after referrers list update"
else else
cp /cache/map-referrer.conf /etc/nginx cp /cache/referrers.list /etc/nginx
job_log "[NGINX] failed nginx reload after referrers list update fallback to old list" job_log "[NGINX] failed nginx reload after referrers list update fallback to old list"
$RELOAD $RELOAD > /dev/null 2>&1
fi fi
else else
cp /etc/nginx/map-referrer.conf /cache cp /etc/nginx/referrers.list /cache
fi fi
else else
job_log "[BLACKLIST] can't update referrers list" job_log "[BLACKLIST] can't update referrers list"
fi fi
rm -f /tmp/map-referrer.conf 2> /dev/null rm -f /tmp/referrers.list 2> /dev/null

View File

@@ -4,50 +4,45 @@
. /opt/entrypoint/utils.sh . /opt/entrypoint/utils.sh
# save old conf # save old conf
cp /etc/nginx/map-user-agent.conf /cache cp /etc/nginx/user-agents.list /cache
# generate new conf
BLACKLIST="$( (curl -s https://raw.githubusercontent.com/mitchellkrogza/nginx-ultimate-bad-bot-blocker/master/_generator_lists/bad-user-agents.list ; curl -s https://raw.githubusercontent.com/JayBizzle/Crawler-Detect/master/raw/Crawlers.txt) | sort -u | sed 's:\\ : :g;s:\\\.:%\.:g;s:\\\\:\\:g;s:\\/:/:g;s:\-:%\-:g')"
if [ "$?" -ne 0 ] ; then
job_log "[BLACKLIST] can't update user-agent list"
echo -n "" > /tmp/user-agents.list
else
echo -e "$BLACKLIST" > /tmp/user-agents.list
fi
# if we are running nginx # if we are running nginx
if [ -f /tmp/nginx.pid ] ; then if [ -f /tmp/nginx.pid ] ; then
RELOAD="/usr/sbin/nginx -s reload > /dev/null 2>&1" RELOAD="/usr/sbin/nginx -s reload"
# if we are in autoconf # if we are in autoconf
elif [ -S /tmp/autoconf.sock ] ; then elif [ -S /tmp/autoconf.sock ] ; then
RELOAD="/opt/entrypoint/reload.py" RELOAD="/opt/entrypoint/reload.py"
fi fi
# generate new conf
BLACKLIST="$(curl -s https://raw.githubusercontent.com/mitchellkrogza/nginx-ultimate-bad-bot-blocker/master/_generator_lists/bad-user-agents.list)
$(curl -s https://raw.githubusercontent.com/JayBizzle/Crawler-Detect/master/raw/Crawlers.txt)"
if [ "$?" -ne 0 ] ; then
job_log "[BLACKLIST] can't update user-agent list"
fi
DATA=""
IFS=$'\n'
for ua in $BLACKLIST ; do
DATA="${DATA}~*${ua} yes;\n"
done
DATA_ESCAPED=$(echo "$DATA" | sed 's: :\\\\ :g' | sed 's:\\\\ yes;: yes;:g' | sed 's:\\\\\\ :\\\\ :g')
echo -e "map \$http_user_agent \$bad_user_agent { default no; $DATA_ESCAPED }" > /tmp/map-user-agent.conf
# check number of lines # check number of lines
lines="$(wc -l /tmp/map-user-agent.conf | cut -d ' ' -f 1)" lines="$(wc -l /tmp/user-agents.list | cut -d ' ' -f 1)"
if [ "$lines" -gt 1 ] ; then if [ "$lines" -gt 1 ] ; then
mv /tmp/map-user-agent.conf /etc/nginx/map-user-agent.conf mv /tmp/user-agents.list /etc/nginx/user-agents.list
job_log "[BLACKLIST] user-agent list updated ($lines entries)" job_log "[BLACKLIST] user-agent list updated ($lines entries)"
if [ "$RELOAD" != "" ] ; then if [ "$RELOAD" != "" ] ; then
$RELOAD $RELOAD > /dev/null 2>&1
if [ "$?" -eq 0 ] ; then if [ "$?" -eq 0 ] ; then
cp /etc/nginx/map-user-agent.conf /cache cp /etc/nginx/user-agents.list /cache
job_log "[NGINX] successfull nginx reload after user-agent list update" job_log "[NGINX] successfull nginx reload after user-agent list update"
else else
cp /cache/map-user-agent.conf /etc/nginx cp /cache/user-agents.list /etc/nginx
job_log "[NGINX] failed nginx reload after user-agent list update fallback to old list" job_log "[NGINX] failed nginx reload after user-agent list update fallback to old list"
$RELOAD $RELOAD > /dev/null 2>&1
fi fi
else else
cp /etc/nginx/map-user-agent.conf /cache cp /etc/nginx/user-agents.list /cache
fi fi
else else
job_log "[BLACKLIST] can't update user-agent list" job_log "[BLACKLIST] can't update user-agent list"
fi fi
rm -f /tmp/map-user-agent.conf 2> /dev/null rm -f /tmp/user-agents.list 2> /dev/null

View File

@@ -804,19 +804,6 @@
} }
] ]
}, },
"Fail2ban":{
"id":"fail2ban",
"params":[
{
"type":"checkbox",
"label":"Use fail2ban",
"env":"USE_FAIL2BAN",
"regex":"^(yes|no)$",
"id":"use-fail2ban",
"default":"yes"
}
]
},
"ClamAV":{ "ClamAV":{
"id":"clamav", "id":"clamav",
"params":[ "params":[

View File

@@ -46,8 +46,8 @@
<div class="col col-6 text-center"><i class="fas fa-{{ env_to_summary_class([service["AUTO_LETS_ENCRYPT"], service["USE_CUSTOM_HTTPS"], service["GENERATE_SELF_SIGNED_SSL"]], ["yes", "yes", "yes"]) }}"></i></div> <div class="col col-6 text-center"><i class="fas fa-{{ env_to_summary_class([service["AUTO_LETS_ENCRYPT"], service["USE_CUSTOM_HTTPS"], service["GENERATE_SELF_SIGNED_SSL"]], ["yes", "yes", "yes"]) }}"></i></div>
<div class="col col-6">ModSecurity</div> <div class="col col-6">ModSecurity</div>
<div class="col col-6 text-center"><i class="fas fa-{{ env_to_summary_class(service["USE_MODSECURITY"], "yes") }}"></i></div> <div class="col col-6 text-center"><i class="fas fa-{{ env_to_summary_class(service["USE_MODSECURITY"], "yes") }}"></i></div>
<div class="col col-6">Fail2Ban</div> <div class="col col-6">Bad behavior</div>
<div class="col col-6 text-center"><i class="fas fa-{{ env_to_summary_class(service["USE_FAIL2BAN"], "yes") }}"></i></div> <div class="col col-6 text-center"><i class="fas fa-{{ env_to_summary_class(service["USE_BAD_BEHAVIOR"], "yes") }}"></i></div>
<div class="col col-6">Limit req</div> <div class="col col-6">Limit req</div>
<div class="col col-6 text-center"><i class="fas fa-{{ env_to_summary_class(service["USE_LIMIT_REQ"], "yes") }}"></i></div> <div class="col col-6 text-center"><i class="fas fa-{{ env_to_summary_class(service["USE_LIMIT_REQ"], "yes") }}"></i></div>
<div class="col col-6">DNSBL</div> <div class="col col-6">DNSBL</div>