bunkerweb 1.4.0
This commit is contained in:
38
confs/api.conf
Normal file
38
confs/api.conf
Normal file
@@ -0,0 +1,38 @@
|
||||
server {
|
||||
server_name {{ API_SERVER_NAME }};
|
||||
|
||||
# HTTP listen
|
||||
listen 0.0.0.0:{{ API_HTTP_PORT }};
|
||||
listen 127.0.0.1:{{ API_HTTP_PORT }};
|
||||
|
||||
# maximum body size for API
|
||||
client_max_body_size 1G;
|
||||
|
||||
# default mime type is JSON
|
||||
default_type 'application/json';
|
||||
|
||||
# check IP and do the API call
|
||||
access_by_lua_block {
|
||||
local api = require "api"
|
||||
local logger = require "logger"
|
||||
if not ngx.var.http_host or ngx.var.http_host ~= "{{ API_SERVER_NAME }}" then
|
||||
logger.log(ngx.WARN, "API", "Wrong Host header from IP " .. ngx.var.remote_addr)
|
||||
return ngx.exit(ngx.HTTP_CLOSE)
|
||||
end
|
||||
local ok, err = api:is_allowed_ip()
|
||||
if not ok then
|
||||
logger.log(ngx.WARN, "API", "Can't validate access from IP " .. ngx.var.remote_addr .. " : " .. err)
|
||||
return ngx.exit(ngx.HTTP_CLOSE)
|
||||
end
|
||||
logger.log(ngx.NOTICE, "API", "Validated access from IP " .. ngx.var.remote_addr)
|
||||
local ok, err, status, resp = api:do_api_call()
|
||||
if not ok then
|
||||
logger.log(ngx.WARN, "API", "Call from " .. ngx.var.remote_addr .. " on " .. ngx.var.uri .. " failed : " .. err)
|
||||
else
|
||||
logger.log(ngx.NOTICE, "API", "Successful call from " .. ngx.var.remote_addr .. " on " .. ngx.var.uri .. " : " .. err)
|
||||
end
|
||||
ngx.status = status
|
||||
ngx.say(resp)
|
||||
return ngx.exit(status)
|
||||
}
|
||||
}
|
||||
15
confs/default-server-http.conf
Normal file
15
confs/default-server-http.conf
Normal file
@@ -0,0 +1,15 @@
|
||||
server {
|
||||
|
||||
server_name _;
|
||||
|
||||
# HTTP listen
|
||||
{% if LISTEN_HTTP == "yes" +%}
|
||||
listen 0.0.0.0:{{ HTTP_PORT }} default_server {% if USE_PROXY_PROTOCOL == "yes" %}proxy_protocol{% endif %};
|
||||
{% endif %}
|
||||
|
||||
# include core and plugins default-server configurations
|
||||
include /etc/nginx/default-server-http/*.conf;
|
||||
|
||||
# include custom default-server configurations
|
||||
include /opt/bunkerweb/configs/default-server-http/*.conf;
|
||||
}
|
||||
@@ -1,10 +1,4 @@
|
||||
{% if REMOTE_PHP != "" +%}
|
||||
fastcgi_param SCRIPT_FILENAME {{ REMOTE_PHP_PATH }}/$fastcgi_script_name;
|
||||
{% elif LOCAL_PHP != "" +%}
|
||||
fastcgi_param SCRIPT_FILENAME {{ LOCAL_PHP_PATH }}/$fastcgi_script_name;
|
||||
{% else +%}
|
||||
fastcgi_param SCRIPT_FILENAME $fastcgi_script_name;
|
||||
{% endif %}
|
||||
|
||||
fastcgi_param QUERY_STRING $query_string;
|
||||
fastcgi_param REQUEST_METHOD $request_method;
|
||||
fastcgi_param CONTENT_TYPE $content_type;
|
||||
@@ -19,7 +13,7 @@ fastcgi_param REQUEST_SCHEME $scheme;
|
||||
fastcgi_param HTTPS $https if_not_empty;
|
||||
|
||||
fastcgi_param GATEWAY_INTERFACE CGI/1.1;
|
||||
fastcgi_param SERVER_SOFTWARE nginx;
|
||||
fastcgi_param SERVER_SOFTWARE nginx/$nginx_version;
|
||||
|
||||
fastcgi_param REMOTE_ADDR $remote_addr;
|
||||
fastcgi_param REMOTE_PORT $remote_port;
|
||||
@@ -1,30 +0,0 @@
|
||||
client_max_body_size 1G;
|
||||
|
||||
location ~ %API_URI% {
|
||||
|
||||
rewrite_by_lua_block {
|
||||
|
||||
local api = require "api"
|
||||
local api_whitelist_ip = { %API_WHITELIST_IP% }
|
||||
local api_uri = "%API_URI%"
|
||||
local logger = require "logger"
|
||||
|
||||
if api.is_api_call(api_uri, api_whitelist_ip) then
|
||||
ngx.header.content_type = 'text/plain'
|
||||
if api.do_api_call(api_uri) then
|
||||
logger.log(ngx.NOTICE, "API", "API call " .. ngx.var.request_uri .. " successfull from " .. ngx.var.remote_addr)
|
||||
ngx.print("ok")
|
||||
else
|
||||
logger.log(ngx.WARN, "API", "API call " .. ngx.var.request_uri .. " failed from " .. ngx.var.remote_addr)
|
||||
ngx.print("ko")
|
||||
end
|
||||
|
||||
ngx.exit(ngx.HTTP_OK)
|
||||
|
||||
end
|
||||
|
||||
ngx.exit(ngx.OK)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
# todo : if api_uri == "random"
|
||||
client_max_body_size 1G;
|
||||
rewrite_by_lua_block {
|
||||
|
||||
local api = require "api"
|
||||
local api_whitelist_ip = {% raw %}{{% endraw %}{% if API_WHITELIST_IP != ""%}{% set elements = API_WHITELIST_IP.split(" ") %}{% for i in range(0, elements|length) %}"{{ elements[i] }}"{% if i < elements|length-1 %},{% endif %}{% endfor %}{% endif %}{% raw %}}{% endraw +%}
|
||||
local api_uri = "{{ API_URI }}"
|
||||
local logger = require "logger"
|
||||
|
||||
if api.is_api_call(api_uri, api_whitelist_ip) then
|
||||
ngx.header.content_type = 'text/plain'
|
||||
if api.do_api_call(api_uri) then
|
||||
logger.log(ngx.NOTICE, "API", "API call " .. ngx.var.request_uri .. " successfull from " .. ngx.var.remote_addr)
|
||||
ngx.print("ok")
|
||||
else
|
||||
logger.log(ngx.WARN, "API", "API call " .. ngx.var.request_uri .. " failed from " .. ngx.var.remote_addr)
|
||||
ngx.print("ko")
|
||||
end
|
||||
|
||||
ngx.exit(ngx.HTTP_OK)
|
||||
|
||||
end
|
||||
|
||||
ngx.exit(ngx.OK)
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
API_URL={{ CROWDSEC_HOST }}
|
||||
API_KEY={{ CROWDSEC_KEY }}
|
||||
LOG_FILE=/tmp/lua_mod.log
|
||||
CACHE_EXPIRATION=1
|
||||
CACHE_SIZE=1000
|
||||
@@ -1,19 +0,0 @@
|
||||
geoip2 /etc/nginx/geoip.mmdb {
|
||||
auto_reload 5m;
|
||||
$geoip2_metadata_country_build metadata build_epoch;
|
||||
$geoip2_data_country_code country iso_code;
|
||||
}
|
||||
|
||||
map $geoip2_data_country_code $allowed_country {
|
||||
default {% if WHITELIST_COUNTRY != "" %}no{% else %}yes{% endif +%};
|
||||
{% if WHITELIST_COUNTRY != "" %}
|
||||
{% for country in WHITELIST_COUNTRY.split(" ") +%}
|
||||
{{ country }} yes;
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% if BLACKLIST_COUNTRY != "" %}
|
||||
{% for country in BLACKLIST_COUNTRY.split(" ") +%}
|
||||
{{ country }} no;
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
}
|
||||
@@ -1,147 +0,0 @@
|
||||
init_by_lua_block {
|
||||
|
||||
local dataloader = require "dataloader"
|
||||
local logger = require "logger"
|
||||
local cjson = require "cjson"
|
||||
local remoteapi = require "remoteapi"
|
||||
local iputils = require "resty.iputils"
|
||||
|
||||
local use_redis = {% if USE_REDIS == "yes" %}true{% else %}false{% endif +%}
|
||||
|
||||
local use_proxies = {% if has_value("BLOCK_PROXIES", "yes") %}true{% else %}false{% endif +%}
|
||||
local use_abusers = {% if has_value("BLOCK_ABUSERS", "yes") %}true{% else %}false{% endif +%}
|
||||
local use_tor_exit_nodes = {% if has_value("BLOCK_TOR_EXIT_NODE", "yes") %}true{% else %}false{% endif +%}
|
||||
local use_user_agents = {% if has_value("BLOCK_USER_AGENT", "yes") %}true{% else %}false{% endif +%}
|
||||
local use_referrers = {% if has_value("BLOCK_REFERRER", "yes") %}true{% else %}false{% endif +%}
|
||||
|
||||
local use_remote_api = {% if has_value("USE_REMOTE_API", "yes") %}true{% else %}false{% endif +%}
|
||||
|
||||
|
||||
-- Load reserved IPs
|
||||
local reserved_ips = {
|
||||
"0.0.0.0/8",
|
||||
"10.0.0.0/8",
|
||||
"100.64.0.0/10",
|
||||
"127.0.0.0/8",
|
||||
"169.254.0.0/16",
|
||||
"172.16.0.0/12",
|
||||
"192.0.0.0/24",
|
||||
"192.0.2.0/24",
|
||||
"192.88.99.0/24",
|
||||
"192.168.0.0/16",
|
||||
"198.18.0.0/15",
|
||||
"198.51.100.0/24",
|
||||
"203.0.113.0/24",
|
||||
"224.0.0.0/4",
|
||||
"233.252.0.0/24",
|
||||
"240.0.0.0/4",
|
||||
"255.255.255.255/32"
|
||||
}
|
||||
local success, err, forcible = ngx.shared.reserved_ips:set("data", cjson.encode(iputils.parse_cidrs(reserved_ips)), 0)
|
||||
if not success then
|
||||
logger.log(ngx.ERR, "INIT", "Can't load reserved IPs : " .. err)
|
||||
end
|
||||
|
||||
-- Load blacklists
|
||||
if not use_redis then
|
||||
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
|
||||
end
|
||||
|
||||
-- Load plugins
|
||||
ngx.shared.plugins_data:safe_set("plugins", nil, 0)
|
||||
local p = io.popen("find /opt/bunkerized-nginx/plugins -maxdepth 1 -type d ! -path /opt/bunkerized-nginx/plugins")
|
||||
for dir in p:lines() do
|
||||
-- read JSON
|
||||
local file = io.open(dir .. "/plugin.json")
|
||||
if file then
|
||||
-- store settings
|
||||
local data = cjson.decode(file:read("*a"))
|
||||
for k, v in pairs(data.settings) do
|
||||
ngx.shared.plugins_data:safe_set(data.id .. "_" .. k, v, 0)
|
||||
end
|
||||
file:close()
|
||||
-- call init
|
||||
local plugin = require(data.id .. "/" .. data.id)
|
||||
local init = true
|
||||
if plugin["init"] ~= nil then
|
||||
init = plugin.init()
|
||||
end
|
||||
-- store plugin
|
||||
if init then
|
||||
local plugins, flags = ngx.shared.plugins_data:get("plugins")
|
||||
if plugins == nil then
|
||||
ngx.shared.plugins_data:safe_set("plugins", data.id, 0)
|
||||
else
|
||||
ngx.shared.plugins_data:safe_set("plugins", plugins .. " " .. data.id, 0)
|
||||
end
|
||||
logger.log(ngx.ERR, "PLUGINS", "*NOT AN ERROR* plugin " .. data.name .. "/" .. data.version .. " has been loaded")
|
||||
else
|
||||
logger.log(ngx.ERR, "PLUGINS", "init failed for plugin " .. data.name .. "/" .. data.version)
|
||||
end
|
||||
else
|
||||
logger.log(ngx.ERR, "PLUGINS", "Can't load " .. dir .. "/plugin.json")
|
||||
end
|
||||
|
||||
end
|
||||
p:close()
|
||||
|
||||
-- Remote API
|
||||
if use_remote_api then
|
||||
|
||||
-- Save server
|
||||
ngx.shared.remote_api:set("server", "{{ REMOTE_API_SERVER }}", 0)
|
||||
|
||||
-- Save version
|
||||
local f = io.open("/opt/bunkerized-nginx/VERSION", "r")
|
||||
ngx.shared.remote_api:set("version", f:read("*all"):gsub("[\r\n]", ""), 0)
|
||||
f:close()
|
||||
|
||||
-- Save machine ID
|
||||
local id = "empty"
|
||||
local f = io.open("/etc/nginx/machine.id", "r")
|
||||
if f == nil then
|
||||
logger.log(ngx.ERR, "REMOTE API", "USE_REMOTE_API is set to yes but machine ID is not generated - communication with {{ REMOTE_API_SERVER }} won't work")
|
||||
else
|
||||
id = f:read("*all"):gsub("[\r\n]", "")
|
||||
logger.log(ngx.ERR, "REMOTE API", "*NOT AN ERROR* Using existing machine ID (" .. id .. ")")
|
||||
f:close()
|
||||
end
|
||||
ngx.shared.remote_api:set("id", id, 0)
|
||||
|
||||
-- Ping the remote API
|
||||
local ping = "ko"
|
||||
if id ~= "empty" then
|
||||
if remoteapi.ping2() then
|
||||
ping = "ok"
|
||||
logger.log(ngx.ERR, "REMOTE API", "*NOT AN ERROR* Successfully requested the remote API")
|
||||
else
|
||||
logger.log(ngx.ERR, "REMOTE API", "Can't contact the remote API, feature will be disabled")
|
||||
end
|
||||
end
|
||||
ngx.shared.remote_api:set("ping", ping, 0)
|
||||
|
||||
-- Load the database
|
||||
if ping ~= "ko" then
|
||||
dataloader.load_ip("/etc/nginx/remote-api.db", ngx.shared.remote_api_db)
|
||||
end
|
||||
end
|
||||
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
listen 0.0.0.0:{{ HTTPS_PORT }} default_server ssl {% if USE_HTTP2 == "yes" %}http2{% endif %};
|
||||
ssl_certificate /etc/nginx/default-cert.pem;
|
||||
ssl_certificate_key /etc/nginx/default-key.pem;
|
||||
ssl_protocols {{ HTTPS_PROTOCOLS }};
|
||||
ssl_prefer_server_ciphers off;
|
||||
ssl_session_tickets off;
|
||||
ssl_session_timeout 1d;
|
||||
ssl_session_cache shared:MozSSL:10m;
|
||||
{% if "TLSv1.2" in HTTPS_PROTOCOLS +%}
|
||||
ssl_dhparam /etc/nginx/dhparam;
|
||||
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
|
||||
{% endif +%}
|
||||
@@ -1,3 +0,0 @@
|
||||
location ~ ^/.well-known/acme-challenge/ {
|
||||
root /opt/bunkerized-nginx/acme-challenge;
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
server {
|
||||
{% if LISTEN_HTTP == "yes" %}listen 0.0.0.0:{{ HTTP_PORT }} default_server;{% endif +%}
|
||||
server_name _;
|
||||
{% if has_value("AUTO_LETS_ENCRYPT", "yes") %}include /etc/nginx/multisite-default-server-https.conf;{% endif +%}
|
||||
include /etc/nginx/multisite-default-server-lets-encrypt-webroot.conf;
|
||||
{% if USE_API == "yes" %}
|
||||
location ^~ {{ API_URI }} {
|
||||
include /etc/nginx/api.conf;
|
||||
}
|
||||
{% endif %}
|
||||
{% if DISABLE_DEFAULT_SERVER == "yes" %}include /etc/nginx/multisite-disable-default-server.conf;{% endif +%}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
location / {
|
||||
return 444;
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
load_module /usr/lib/nginx/modules/ngx_http_lua_module.so;
|
||||
|
||||
#daemon on;
|
||||
|
||||
pid /tmp/nginx-temp.pid;
|
||||
|
||||
events {
|
||||
worker_connections 1024;
|
||||
use epoll;
|
||||
}
|
||||
|
||||
http {
|
||||
proxy_temp_path /tmp/proxy_temp;
|
||||
client_body_temp_path /tmp/client_temp;
|
||||
fastcgi_temp_path /tmp/fastcgi_temp;
|
||||
uwsgi_temp_path /tmp/uwsgi_temp;
|
||||
scgi_temp_path /tmp/scgi_temp;
|
||||
lua_package_path "/opt/bunkerized-nginx/lua/?.lua;/opt/bunkerized-nginx/plugins/?.lua;/opt/bunkerized-nginx/deps/lib/lua/?.lua;;";
|
||||
lua_package_cpath "/opt/bunkerized-nginx/deps/lib/?.so;/opt/bunkerized-nginx/deps/lib/lua/?.so;;";
|
||||
server {
|
||||
listen 0.0.0.0:%HTTP_PORT% default_server;
|
||||
server_name _;
|
||||
location ~ ^/.well-known/acme-challenge/ {
|
||||
root /opt/bunkerized-nginx/acme-challenge;
|
||||
}
|
||||
%USE_API%
|
||||
location / {
|
||||
return 444;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,152 +0,0 @@
|
||||
# /etc/nginx/nginx.conf
|
||||
|
||||
# load dynamic modules
|
||||
load_module /usr/lib/nginx/modules/ngx_http_cookie_flag_filter_module.so;
|
||||
load_module /usr/lib/nginx/modules/ngx_http_geoip2_module.so;
|
||||
load_module /usr/lib/nginx/modules/ngx_http_headers_more_filter_module.so;
|
||||
load_module /usr/lib/nginx/modules/ngx_http_lua_module.so;
|
||||
load_module /usr/lib/nginx/modules/ngx_http_modsecurity_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;
|
||||
|
||||
# PID file
|
||||
pid /tmp/nginx.pid;
|
||||
|
||||
# worker number (default = auto)
|
||||
worker_processes {{ WORKER_PROCESSES }};
|
||||
|
||||
# faster regexp
|
||||
pcre_jit on;
|
||||
|
||||
# config files for dynamic modules
|
||||
include /etc/nginx/modules/*.conf;
|
||||
|
||||
# max open files for each worker
|
||||
worker_rlimit_nofile {{ WORKER_RLIMIT_NOFILE }};
|
||||
|
||||
events {
|
||||
# max connections per worker
|
||||
worker_connections {{ WORKER_CONNECTIONS }};
|
||||
|
||||
# epoll seems to be the best on Linux
|
||||
use epoll;
|
||||
}
|
||||
|
||||
http {
|
||||
# zero copy within the kernel
|
||||
sendfile on;
|
||||
|
||||
# send packets only if filled
|
||||
tcp_nopush on;
|
||||
|
||||
# remove 200ms delay
|
||||
tcp_nodelay on;
|
||||
|
||||
# load mime types and set default one
|
||||
include /etc/nginx/mime-types.conf;
|
||||
default_type application/octet-stream;
|
||||
|
||||
# write logs to local syslog
|
||||
log_format logf '{{ LOG_FORMAT }}';
|
||||
access_log /var/log/nginx/access.log logf;
|
||||
error_log /var/log/nginx/error.log {{ LOG_LEVEL }};
|
||||
|
||||
# temp paths
|
||||
proxy_temp_path /tmp/proxy_temp;
|
||||
client_body_temp_path /tmp/client_temp;
|
||||
fastcgi_temp_path /tmp/fastcgi_temp;
|
||||
uwsgi_temp_path /tmp/uwsgi_temp;
|
||||
scgi_temp_path /tmp/scgi_temp;
|
||||
|
||||
# close connections in FIN_WAIT1 state
|
||||
reset_timedout_connection on;
|
||||
|
||||
# timeouts
|
||||
client_body_timeout 10;
|
||||
client_header_timeout 10;
|
||||
keepalive_timeout 15;
|
||||
send_timeout 10;
|
||||
|
||||
# resolvers to use
|
||||
resolver {{ DNS_RESOLVERS }} ipv6=off;
|
||||
|
||||
# remove ports when sending redirects
|
||||
port_in_redirect off;
|
||||
|
||||
# lua path and dicts
|
||||
lua_package_path "/opt/bunkerized-nginx/lua/?.lua;/opt/bunkerized-nginx/plugins/?.lua;/opt/bunkerized-nginx/deps/lib/lua/?.lua;;";
|
||||
lua_package_cpath "/opt/bunkerized-nginx/deps/lib/?.so;/opt/bunkerized-nginx/deps/lib/lua/?.so;;";
|
||||
lua_ssl_trusted_certificate "/opt/bunkerized-nginx/lua/misc/root-ca.pem";
|
||||
lua_ssl_verify_depth 2;
|
||||
{% if has_value("USE_WHITELIST_IP", "yes") %}lua_shared_dict whitelist_ip_cache 10m;{% endif +%}
|
||||
{% if has_value("USE_WHITELIST_REVERSE", "yes") %}lua_shared_dict whitelist_reverse_cache 10m;{% endif +%}
|
||||
{% if has_value("USE_BLACKLIST_IP", "yes") %}lua_shared_dict blacklist_ip_cache 10m;{% endif +%}
|
||||
{% if has_value("USE_BLACKLIST_REVERSE", "yes") %}lua_shared_dict blacklist_reverse_cache 10m;{% endif +%}
|
||||
{% if has_value("USE_DNSBL", "yes") %}lua_shared_dict dnsbl_cache 10m;{% endif +%}
|
||||
{% if has_value("BLOCK_PROXIES", "yes") %}lua_shared_dict proxies_data 250m;{% endif +%}
|
||||
{% if has_value("BLOCK_ABUSERS", "yes") %}lua_shared_dict abusers_data 50m;{% endif +%}
|
||||
{% if has_value("BLOCK_TOR_EXIT_NODE", "yes") %}lua_shared_dict tor_exit_nodes_data 1m;{% endif +%}
|
||||
{% if has_value("BLOCK_USER_AGENT", "yes") %}lua_shared_dict user_agents_data 1m;{% endif +%}
|
||||
{% if has_value("BLOCK_USER_AGENT", "yes") %}lua_shared_dict user_agents_cache 10m;{% endif +%}
|
||||
{% if has_value("BLOCK_REFERRER", "yes") %}lua_shared_dict referrers_data 1m;{% endif +%}
|
||||
{% if has_value("BLOCK_REFERRER", "yes") %}lua_shared_dict referrers_cache 10m;{% endif +%}
|
||||
{% if has_value("USE_BAD_BEHAVIOR", "yes") %}lua_shared_dict behavior_ban 10m;{% endif +%}
|
||||
{% if has_value("USE_BAD_BEHAVIOR", "yes") %}lua_shared_dict behavior_count 10m;{% endif +%}
|
||||
{% if has_value("USE_LIMIT_REQ", "yes") %}lua_shared_dict limit_req {{ LIMIT_REQ_CACHE }};{% endif +%}
|
||||
lua_shared_dict plugins_data 10m;
|
||||
lua_shared_dict reserved_ips 1m;
|
||||
{% if has_value("USE_REMOTE_API", "yes") %}lua_shared_dict remote_api 1m;{% endif +%}
|
||||
{% if has_value("USE_REMOTE_API", "yes") %}lua_shared_dict remote_api_db 10m;{% endif +%}
|
||||
|
||||
# shared memory zone for limit_req
|
||||
#{% if has_value("USE_LIMIT_REQ", "yes") %}limit_req_zone $binary_remote_addr$uri zone=limit:{{ LIMIT_REQ_CACHE }} rate={{ LIMIT_REQ_RATE }};{% endif +%}
|
||||
|
||||
# shared memory zone for limit_conn
|
||||
{% if has_value("USE_LIMIT_CONN", "yes") %}limit_conn_zone $binary_remote_addr zone=ddos:{{ LIMIT_CONN_CACHE }};{% endif +%}
|
||||
|
||||
# whitelist or blacklist country
|
||||
{% if BLACKLIST_COUNTRY != "" or WHITELIST_COUNTRY != "" %}include /etc/nginx/geoip.conf;{% endif +%}
|
||||
|
||||
# zone for proxy_cache
|
||||
{% if has_value("USE_PROXY_CACHE", "yes") %}proxy_cache_path /tmp/proxy_cache keys_zone=proxycache:{{ PROXY_CACHE_PATH_ZONE_SIZE }} {{ PROXY_CACHE_PATH_PARAMS }};{% endif +%}
|
||||
|
||||
# custom http confs
|
||||
include /opt/bunkerized-nginx/http-confs/*.conf;
|
||||
|
||||
# LUA init block
|
||||
include /etc/nginx/init-lua.conf;
|
||||
|
||||
# default server when MULTISITE=yes
|
||||
{% if MULTISITE == "yes" %}include /etc/nginx/multisite-default-server.conf;{% endif +%}
|
||||
|
||||
# server config(s)
|
||||
{% if MULTISITE == "yes" and SERVER_NAME != "" %}
|
||||
{% set map_servers = {} %}
|
||||
{% for server_name in SERVER_NAME.split(" ") %}
|
||||
{% if server_name + "_SERVER_NAME" in all %}
|
||||
{% set x = map_servers.update({server_name : all[server_name + "_SERVER_NAME"].split(" ")}) %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% for server_name in SERVER_NAME.split(" ") %}
|
||||
{% if not server_name in map_servers %}
|
||||
{% set found = {"res": false} %}
|
||||
{% for first_server, servers in map_servers.items() %}
|
||||
{% if server_name in servers %}
|
||||
{% set x = found.update({"res" : true}) %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% if not found["res"] %}
|
||||
{% set x = map_servers.update({server_name : [server_name]}) %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% for first_server in map_servers +%}
|
||||
include /etc/nginx/{{ first_server }}/server.conf;
|
||||
{% endfor %}
|
||||
{% elif MULTISITE == "no" +%}
|
||||
include /etc/nginx/server.conf;
|
||||
{% endif %}
|
||||
|
||||
# API
|
||||
{% if USE_API == "yes" %}include /etc/nginx/api.conf;{% endif +%}
|
||||
}
|
||||
25
confs/healthcheck.conf
Normal file
25
confs/healthcheck.conf
Normal file
@@ -0,0 +1,25 @@
|
||||
server {
|
||||
|
||||
# healthcheck service for docker, swarm and k8s
|
||||
server_name healthcheck.bunkerweb.io;
|
||||
|
||||
# only listen on localhost
|
||||
listen 127.0.0.1:6000;
|
||||
|
||||
# healthcheck endpoint
|
||||
location ~ ^/healthz$ {
|
||||
keepalive_timeout 0;
|
||||
default_type "text/plain";
|
||||
content_by_lua_block {
|
||||
ngx.say("ok")
|
||||
}
|
||||
}
|
||||
|
||||
# disable logging
|
||||
access_log off;
|
||||
|
||||
# don't respond to other requests
|
||||
location / {
|
||||
return 444;
|
||||
}
|
||||
}
|
||||
94
confs/http.conf
Normal file
94
confs/http.conf
Normal file
@@ -0,0 +1,94 @@
|
||||
# /etc/nginx/base_http.conf
|
||||
|
||||
# zero copy within the kernel
|
||||
sendfile on;
|
||||
|
||||
# send packets only if filled
|
||||
tcp_nopush on;
|
||||
|
||||
# remove 200ms delay
|
||||
tcp_nodelay on;
|
||||
|
||||
# load mime types and set default one
|
||||
include /etc/nginx/mime.types;
|
||||
default_type application/octet-stream;
|
||||
|
||||
# access log format
|
||||
log_format logf '{{ LOG_FORMAT }}';
|
||||
access_log /var/log/nginx/access.log logf;
|
||||
|
||||
# temp paths
|
||||
proxy_temp_path /opt/bunkerweb/tmp/proxy_temp;
|
||||
client_body_temp_path /opt/bunkerweb/tmp/client_temp;
|
||||
fastcgi_temp_path /opt/bunkerweb/tmp/fastcgi_temp;
|
||||
uwsgi_temp_path /opt/bunkerweb/tmp/uwsgi_temp;
|
||||
scgi_temp_path /opt/bunkerweb/tmp/scgi_temp;
|
||||
|
||||
# close connections in FIN_WAIT1 state
|
||||
reset_timedout_connection on;
|
||||
|
||||
# timeouts
|
||||
client_body_timeout 10;
|
||||
client_header_timeout 10;
|
||||
keepalive_timeout 15;
|
||||
send_timeout 10;
|
||||
|
||||
# resolvers to use
|
||||
resolver {{ DNS_RESOLVERS }} ipv6=off;
|
||||
|
||||
# remove ports when sending redirects
|
||||
port_in_redirect off;
|
||||
|
||||
# lua path and dicts
|
||||
lua_package_path "/opt/bunkerweb/lua/?.lua;/opt/bunkerweb/core/?.lua;/opt/bunkerweb/plugins/?.lua;/opt/bunkerweb/deps/lib/lua/?.lua;;";
|
||||
lua_package_cpath "/opt/bunkerweb/deps/lib/?.so;/opt/bunkerweb/deps/lib/lua/?.so;;";
|
||||
lua_ssl_trusted_certificate "/opt/bunkerweb/misc/root-ca.pem";
|
||||
lua_ssl_verify_depth 2;
|
||||
lua_shared_dict datastore {{ DATASTORE_MEMORY_SIZE }};
|
||||
|
||||
# LUA init block
|
||||
include /etc/nginx/init-lua.conf;
|
||||
|
||||
# API server
|
||||
{% if USE_API == "yes" %}include /etc/nginx/api.conf;{% endif +%}
|
||||
|
||||
# healthcheck server
|
||||
include /etc/nginx/healthcheck.conf;
|
||||
|
||||
# default server
|
||||
{% if MULTISITE == "yes" or DISABLE_DEFAULT_SERVER == "yes" +%}
|
||||
include /etc/nginx/default-server-http.conf;
|
||||
{% endif +%}
|
||||
|
||||
# disable sending nginx version globally
|
||||
server_tokens off;
|
||||
|
||||
# server config(s)
|
||||
{% if TEMP_NGINX != "yes" +%}
|
||||
{% if MULTISITE == "yes" and SERVER_NAME != "" %}
|
||||
{% set map_servers = {} %}
|
||||
{% for server_name in SERVER_NAME.split(" ") %}
|
||||
{% if server_name + "_SERVER_NAME" in all %}
|
||||
{% set x = map_servers.update({server_name : all[server_name + "_SERVER_NAME"].split(" ")}) %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% for server_name in SERVER_NAME.split(" ") %}
|
||||
{% if not server_name in map_servers %}
|
||||
{% set found = {"res": false} %}
|
||||
{% for first_server, servers in map_servers.items() %}
|
||||
{% if server_name in servers %}
|
||||
{% set x = found.update({"res" : true}) %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% if not found["res"] %}
|
||||
{% set x = map_servers.update({server_name : [server_name]}) %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% for first_server in map_servers +%}
|
||||
include /etc/nginx/{{ first_server }}/server.conf;
|
||||
{% endfor %}
|
||||
{% elif MULTISITE == "no" and SERVER_NAME != "" +%}
|
||||
include /etc/nginx/server.conf;
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
118
confs/init-lua.conf
Normal file
118
confs/init-lua.conf
Normal file
@@ -0,0 +1,118 @@
|
||||
init_by_lua_block {
|
||||
|
||||
local logger = require "logger"
|
||||
local datastore = require "datastore"
|
||||
local plugins = require "plugins"
|
||||
local utils = require "utils"
|
||||
local cjson = require "cjson"
|
||||
|
||||
logger.log(ngx.NOTICE, "INIT", "Init phase started")
|
||||
|
||||
-- Remove previous data from the datastore
|
||||
local data_keys = {"^plugin_", "^variable_", "^plugins$", "^api_", "^misc_"}
|
||||
for i, key in pairs(data_keys) do
|
||||
local ok, err = datastore:delete_all(key)
|
||||
if not ok then
|
||||
logger.log(ngx.ERR, "INIT", "Can't delete " .. key .. " from datastore : " .. err)
|
||||
return false
|
||||
end
|
||||
logger.log(ngx.INFO, "INIT", "Deleted " .. key .. " from datastore")
|
||||
end
|
||||
|
||||
-- Load variables into the datastore
|
||||
local file = io.open("/etc/nginx/variables.env")
|
||||
if not file then
|
||||
logger.log(ngx.ERR, "INIT", "Can't open /etc/nginx/variables.env file")
|
||||
return false
|
||||
end
|
||||
file:close()
|
||||
for line in io.lines("/etc/nginx/variables.env") do
|
||||
local variable, value = line:match("(.+)=(.*)")
|
||||
ok, err = datastore:set("variable_" .. variable, value)
|
||||
if not ok then
|
||||
logger.log(ngx.ERR, "INIT", "Can't save variable " .. variable .. " into datastore")
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
-- Set default values into the datastore
|
||||
ok, err = datastore:set("plugins", cjson.encode({}))
|
||||
if not ok then
|
||||
logger.log(ngx.ERR, "INIT", "Can't set default value for plugins into the datastore : " .. err)
|
||||
return false
|
||||
end
|
||||
ok, err = utils.set_values()
|
||||
if not ok then
|
||||
logger.log(ngx.ERR, "INIT", "Error while setting default values : " .. err)
|
||||
return false
|
||||
end
|
||||
|
||||
-- API setup
|
||||
local value, err = datastore:get("variable_USE_API")
|
||||
if not value then
|
||||
logger.log(ngx.ERR, "INIT", "Can't get variable USE_API from the datastore")
|
||||
return false
|
||||
end
|
||||
if value == "yes" then
|
||||
value, err = datastore:get("variable_API_WHITELIST_IP")
|
||||
if not value then
|
||||
logger.log(ngx.ERR, "INIT", "Can't get variable API_WHITELIST_IP from the datastore")
|
||||
return false
|
||||
end
|
||||
local whitelists = { data = {}}
|
||||
for whitelist in value:gmatch("%S+") do
|
||||
table.insert(whitelists.data, whitelist)
|
||||
end
|
||||
ok, err = datastore:set("api_whitelist_ip", cjson.encode(whitelists))
|
||||
if not ok then
|
||||
logger.log(ngx.ERR, "INIT", "Can't save api_whitelist_ip to datastore : " .. err)
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
-- Load plugins into the datastore
|
||||
local plugin_paths = {"/opt/bunkerweb/core", "/opt/bunkerweb/plugins"}
|
||||
for i, plugin_path in ipairs(plugin_paths) do
|
||||
local paths = io.popen("find -L " .. plugin_path .. " -maxdepth 1 -type d ! -path " .. plugin_path)
|
||||
for path in paths:lines() do
|
||||
plugin, err = plugins:load(path)
|
||||
if not plugin then
|
||||
logger.log(ngx.ERR, "INIT", "Error while loading plugin from " .. path .. " : " .. err)
|
||||
return false
|
||||
end
|
||||
logger.log(ngx.NOTICE, "INIT", "Loaded plugin " .. plugin.id .. " v" .. plugin.version)
|
||||
end
|
||||
end
|
||||
|
||||
-- Call init method of plugins
|
||||
local list, err = plugins:list()
|
||||
if not list then
|
||||
logger.log(ngx.ERR, "INIT", "Can't list loaded plugins : " .. err)
|
||||
list = {}
|
||||
end
|
||||
for i, plugin in ipairs(list) do
|
||||
local ret, plugin_lua = pcall(require, plugin.id .. "/" .. plugin.id)
|
||||
if ret then
|
||||
local plugin_obj = plugin_lua.new()
|
||||
if plugin_obj.init ~= nil then
|
||||
ok, err = plugin_obj:init()
|
||||
if not ok then
|
||||
logger.log(ngx.ERR, "INIT", "Plugin " .. plugin.id .. " failed on init() : " .. err)
|
||||
else
|
||||
logger.log(ngx.INFO, "INIT", "Successfull init() call for plugin " .. plugin.id .. " : " .. err)
|
||||
end
|
||||
else
|
||||
logger.log(ngx.INFO, "INIT", "init() method not found in " .. plugin.id .. ", skipped execution")
|
||||
end
|
||||
else
|
||||
if plugin_lua:match("not found") then
|
||||
logger.log(ngx.INFO, "INIT", "can't require " .. plugin.id .. " : not found")
|
||||
else
|
||||
logger.log(ngx.ERR, "INIT", "can't require " .. plugin.id .. " : " .. plugin_lua)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
logger.log(ngx.NOTICE, "INIT", "Init phase ended")
|
||||
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
|
||||
types {
|
||||
text/html html htm shtml;
|
||||
text/css css;
|
||||
@@ -14,6 +15,7 @@ types {
|
||||
text/vnd.wap.wml wml;
|
||||
text/x-component htc;
|
||||
|
||||
image/avif avif;
|
||||
image/png png;
|
||||
image/svg+xml svg svgz;
|
||||
image/tiff tif tiff;
|
||||
@@ -50,6 +52,7 @@ types {
|
||||
application/vnd.openxmlformats-officedocument.wordprocessingml.document
|
||||
docx;
|
||||
application/vnd.wap.wmlc wmlc;
|
||||
application/wasm wasm;
|
||||
application/x-7z-compressed 7z;
|
||||
application/x-cocoa cco;
|
||||
application/x-java-archive-diff jardiff;
|
||||
64
confs/nginx.conf
Normal file
64
confs/nginx.conf
Normal file
@@ -0,0 +1,64 @@
|
||||
# /etc/nginx/nginx.conf
|
||||
|
||||
# load dynamic modules
|
||||
load_module /opt/bunkerweb/modules/ngx_http_cookie_flag_filter_module.so;
|
||||
#load_module /opt/bunkerweb/modules/ngx_http_geoip2_module.so;
|
||||
load_module /opt/bunkerweb/modules/ngx_http_headers_more_filter_module.so;
|
||||
load_module /opt/bunkerweb/modules/ngx_http_lua_module.so;
|
||||
load_module /opt/bunkerweb/modules/ngx_http_modsecurity_module.so;
|
||||
load_module /opt/bunkerweb/modules/ngx_http_brotli_filter_module.so;
|
||||
load_module /opt/bunkerweb/modules/ngx_http_brotli_static_module.so;
|
||||
#load_module /opt/bunkerweb/modules/ngx_stream_geoip2_module.so;
|
||||
#load_module /opt/bunkerweb/modules/ngx_stream_lua_module.so;
|
||||
|
||||
# PID file
|
||||
{% if TEMP_NGINX != "yes" +%}
|
||||
pid /opt/bunkerweb/tmp/nginx.pid;
|
||||
{% else +%}
|
||||
pid /opt/bunkerweb/tmp/nginx-temp.pid;
|
||||
{% endif %}
|
||||
|
||||
# worker number (default = auto)
|
||||
worker_processes {{ WORKER_PROCESSES }};
|
||||
|
||||
# faster regexp
|
||||
pcre_jit on;
|
||||
|
||||
# max open files for each worker
|
||||
worker_rlimit_nofile {{ WORKER_RLIMIT_NOFILE }};
|
||||
|
||||
# error log level
|
||||
error_log /var/log/nginx/error.log {{ LOG_LEVEL }};
|
||||
|
||||
# reason env var
|
||||
env REASON;
|
||||
|
||||
events {
|
||||
# max connections per worker
|
||||
worker_connections {{ WORKER_CONNECTIONS }};
|
||||
|
||||
# epoll seems to be the best on Linux
|
||||
use epoll;
|
||||
}
|
||||
|
||||
http {
|
||||
# include base http configuration
|
||||
include /etc/nginx/http.conf;
|
||||
|
||||
# include core and plugins http configurations
|
||||
include /etc/nginx/http/*.conf;
|
||||
|
||||
# include custom http configurations
|
||||
include /opt/bunkerweb/configs/http/*.conf;
|
||||
}
|
||||
|
||||
#stream {
|
||||
# include base stream configuration
|
||||
# include /etc/nginx/stream.conf;
|
||||
|
||||
# include core and plugins stream configurations
|
||||
# include /etc/nginx/stream/*.conf;
|
||||
|
||||
# include custom stream configurations
|
||||
# include /opt/bunkerweb/configs/stream/*.conf;
|
||||
#}
|
||||
17
confs/scgi_params
Normal file
17
confs/scgi_params
Normal file
@@ -0,0 +1,17 @@
|
||||
|
||||
scgi_param REQUEST_METHOD $request_method;
|
||||
scgi_param REQUEST_URI $request_uri;
|
||||
scgi_param QUERY_STRING $query_string;
|
||||
scgi_param CONTENT_TYPE $content_type;
|
||||
|
||||
scgi_param DOCUMENT_URI $document_uri;
|
||||
scgi_param DOCUMENT_ROOT $document_root;
|
||||
scgi_param SCGI 1;
|
||||
scgi_param SERVER_PROTOCOL $server_protocol;
|
||||
scgi_param REQUEST_SCHEME $scheme;
|
||||
scgi_param HTTPS $https if_not_empty;
|
||||
|
||||
scgi_param REMOTE_ADDR $remote_addr;
|
||||
scgi_param REMOTE_PORT $remote_port;
|
||||
scgi_param SERVER_PORT $server_port;
|
||||
scgi_param SERVER_NAME $server_name;
|
||||
63
confs/server-http/access-lua.conf
Normal file
63
confs/server-http/access-lua.conf
Normal file
@@ -0,0 +1,63 @@
|
||||
access_by_lua_block {
|
||||
|
||||
local logger = require "logger"
|
||||
local datastore = require "datastore"
|
||||
local plugins = require "plugins"
|
||||
|
||||
-- Don't process internal requests
|
||||
if ngx.req.is_internal() then
|
||||
logger.log(ngx.INFO, "ACCESS", "Skipped access phase because request is internal")
|
||||
return
|
||||
end
|
||||
|
||||
logger.log(ngx.INFO, "ACCESS", "Access phase started")
|
||||
|
||||
-- Process bans as soon as possible
|
||||
local banned, err = datastore:get("bans_ip_" .. ngx.var.remote_addr)
|
||||
if banned then
|
||||
logger.log(ngx.WARN, "ACCESS", "IP " .. ngx.var.remote_addr .. " is banned with reason : " .. banned)
|
||||
ngx.exit(ngx.HTTP_FORBIDDEN)
|
||||
end
|
||||
|
||||
-- List all plugins
|
||||
local list, err = plugins:list()
|
||||
if not list then
|
||||
logger.log(ngx.ERR, "ACCESS", "Can't list loaded plugins : " .. err)
|
||||
list = {}
|
||||
end
|
||||
|
||||
-- Call access method of plugins
|
||||
for i, plugin in ipairs(list) do
|
||||
local ret, plugin_lua = pcall(require, plugin.id .. "/" .. plugin.id)
|
||||
if ret then
|
||||
local plugin_obj = plugin_lua.new()
|
||||
if plugin_obj.access ~= nil then
|
||||
logger.log(ngx.INFO, "ACCESS", "Executing access() of " .. plugin.id)
|
||||
local ok, err, ret, value = plugin_obj:access()
|
||||
if not ok then
|
||||
logger.log(ngx.ERR, "ACCESS", "Error while calling access() on plugin " .. plugin.id .. " : " .. err)
|
||||
else
|
||||
logger.log(ngx.INFO, "ACCESS", "Return value from " .. plugin.id .. ".access() is : " .. err)
|
||||
end
|
||||
if ret then
|
||||
if type(value) == "number" then
|
||||
if value == ngx.HTTP_FORBIDDEN then
|
||||
logger.log(ngx.WARN, "ACCESS", "Denied access from " .. plugin.id .. " : " .. err)
|
||||
ngx.var.reason = plugin.id
|
||||
else
|
||||
logger.log(ngx.NOTICE, "ACCESS", plugin.id .. " returned status " .. tostring(value) .. " : " .. err)
|
||||
end
|
||||
return ngx.exit(value)
|
||||
else
|
||||
return value
|
||||
end
|
||||
end
|
||||
else
|
||||
logger.log(ngx.INFO, "ACCESS", "access() method not found in " .. plugin.id .. ", skipped execution")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
logger.log(ngx.INFO, "ACCESS", "Access phase ended")
|
||||
|
||||
}
|
||||
44
confs/server-http/log-lua.conf
Normal file
44
confs/server-http/log-lua.conf
Normal file
@@ -0,0 +1,44 @@
|
||||
log_by_lua_block {
|
||||
|
||||
local utils = require "utils"
|
||||
local logger = require "logger"
|
||||
local datastore = require "datastore"
|
||||
local plugins = require "plugins"
|
||||
|
||||
logger.log(ngx.INFO, "LOG", "Log phase started")
|
||||
|
||||
-- List all plugins
|
||||
local list, err = plugins:list()
|
||||
if not list then
|
||||
logger.log(ngx.ERR, "LOG", "Can't list loaded plugins : " .. err)
|
||||
list = {}
|
||||
end
|
||||
|
||||
-- Call log method of plugins
|
||||
for i, plugin in ipairs(list) do
|
||||
local ret, plugin_lua = pcall(require, plugin.id .. "/" .. plugin.id)
|
||||
if ret then
|
||||
local plugin_obj = plugin_lua.new()
|
||||
if plugin_obj.log ~= nil then
|
||||
logger.log(ngx.INFO, "LOG", "Executing log() of " .. plugin.id)
|
||||
local ok, err = plugin_obj:log()
|
||||
if not ok then
|
||||
logger.log(ngx.ERR, "LOG", "Error while calling log() on plugin " .. plugin.id .. " : " .. err)
|
||||
else
|
||||
logger.log(ngx.INFO, "LOG", "Return value from " .. plugin.id .. ".log() is : " .. err)
|
||||
end
|
||||
else
|
||||
logger.log(ngx.INFO, "LOG", "log() method not found in " .. plugin.id .. ", skipped execution")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Display reason at info level
|
||||
local reason = utils.get_reason()
|
||||
if reason then
|
||||
logger.log(ngx.INFO, "LOG", "Client was denied with reason : " .. reason)
|
||||
end
|
||||
|
||||
logger.log(ngx.INFO, "LOG", "Log phase ended")
|
||||
|
||||
}
|
||||
27
confs/server-http/server.conf
Normal file
27
confs/server-http/server.conf
Normal file
@@ -0,0 +1,27 @@
|
||||
server {
|
||||
# server name (vhost)
|
||||
server_name {{ SERVER_NAME }};
|
||||
|
||||
# HTTP listen
|
||||
{% if LISTEN_HTTP == "yes" +%}
|
||||
listen 0.0.0.0:{{ HTTP_PORT }}{% if MULTISITE == "no" and DISABLE_DEFAULT_SERVER == "no" %} default_server{% endif %}{% if USE_PROXY_PROTOCOL == "yes" %} proxy_protocol{% endif %};
|
||||
{% endif %}
|
||||
|
||||
index index.php index.html index.htm;
|
||||
|
||||
# custom config
|
||||
include /opt/bunkerweb/configs/server-http/*.conf;
|
||||
{% if MULTISITE == "yes" +%}
|
||||
include /opt/bunkerweb/configs/server-http/{{ SERVER_NAME.split(" ")[0] }}/*.conf;
|
||||
{% endif %}
|
||||
|
||||
# reason variable
|
||||
set $reason '';
|
||||
|
||||
# include LUA files
|
||||
include {{ NGINX_PREFIX }}access-lua.conf;
|
||||
include {{ NGINX_PREFIX }}log-lua.conf;
|
||||
|
||||
# include config files
|
||||
include {{ NGINX_PREFIX }}server-http/*.conf;
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
location = {{ ANTIBOT_URI }} {
|
||||
|
||||
default_type 'text/html';
|
||||
|
||||
if ($request_method = GET) {
|
||||
content_by_lua_block {
|
||||
local cookie = require "cookie"
|
||||
local captcha = require "captcha"
|
||||
local logger = require "logger"
|
||||
if not cookie.is_set("uri") then
|
||||
logger.log(ngx.WARN, "ANTIBOT", "captcha fail (1) for " .. ngx.var.remote_addr)
|
||||
return ngx.exit(ngx.HTTP_FORBIDDEN)
|
||||
end
|
||||
local img, res = captcha.get_challenge()
|
||||
cookie.set({captchares = res})
|
||||
local code = captcha.get_code(img, "{{ ANTIBOT_URI }}")
|
||||
ngx.say(code)
|
||||
}
|
||||
}
|
||||
|
||||
if ($request_method = POST) {
|
||||
access_by_lua_block {
|
||||
local cookie = require "cookie"
|
||||
local captcha = require "captcha"
|
||||
local logger = require "logger"
|
||||
if not cookie.is_set("captchares") then
|
||||
logger.log(ngx.WARN, "ANTIBOT", "captcha fail (2) for " .. ngx.var.remote_addr)
|
||||
return ngx.exit(ngx.HTTP_FORBIDDEN)
|
||||
end
|
||||
ngx.req.read_body()
|
||||
local args, err = ngx.req.get_post_args(1)
|
||||
if err == "truncated" or not args or not args["captcha"] then
|
||||
logger.log(ngx.WARN, "ANTIBOT", "captcha fail (3) for " .. ngx.var.remote_addr)
|
||||
return ngx.exit(ngx.HTTP_FORBIDDEN)
|
||||
end
|
||||
local captcha_user = args["captcha"]
|
||||
local check = captcha.check(captcha_user, cookie.get("captchares"))
|
||||
if not check then
|
||||
logger.log(ngx.WARN, "ANTIBOT", "captcha fail (4) for " .. ngx.var.remote_addr)
|
||||
return ngx.redirect("{{ ANTIBOT_URI }}")
|
||||
end
|
||||
cookie.set({captcha = "ok"})
|
||||
return ngx.redirect(cookie.get("uri"))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
location = {{ ANTIBOT_URI }} {
|
||||
|
||||
default_type 'text/html';
|
||||
|
||||
if ($request_method = GET) {
|
||||
content_by_lua_block {
|
||||
local cookie = require "cookie"
|
||||
local javascript = require "javascript"
|
||||
local logger = require "logger"
|
||||
if not cookie.is_set("challenge") then
|
||||
logger.log(ngx.WARN, "ANTIBOT", "javascript fail (1) for " .. ngx.var.remote_addr)
|
||||
return ngx.exit(ngx.HTTP_FORBIDDEN)
|
||||
end
|
||||
local challenge = cookie.get("challenge")
|
||||
local code = javascript.get_code(challenge, "{{ ANTIBOT_URI }}", cookie.get("uri"))
|
||||
ngx.say(code)
|
||||
}
|
||||
}
|
||||
|
||||
if ($request_method = POST) {
|
||||
content_by_lua_block {
|
||||
local cookie = require "cookie"
|
||||
local javascript = require "javascript"
|
||||
local logger = require "logger"
|
||||
if not cookie.is_set("challenge") then
|
||||
logger.log(ngx.WARN, "ANTIBOT", "javascript fail (2) for " .. ngx.var.remote_addr)
|
||||
return ngx.exit(ngx.HTTP_FORBIDDEN)
|
||||
end
|
||||
ngx.req.read_body()
|
||||
local args, err = ngx.req.get_post_args(1)
|
||||
if err == "truncated" or not args or not args["challenge"] then
|
||||
logger.log(ngx.WARN, "ANTIBOT", "javascript fail (3) for " .. ngx.var.remote_addr)
|
||||
return ngx.exit(ngx.HTTP_FORBIDDEN)
|
||||
end
|
||||
local challenge = args["challenge"]
|
||||
local check = javascript.check(cookie.get("challenge"), challenge)
|
||||
if not check then
|
||||
logger.log(ngx.WARN, "ANTIBOT", "javascript fail (4) for " .. ngx.var.remote_addr)
|
||||
return ngx.exit(ngx.HTTP_FORBIDDEN)
|
||||
end
|
||||
cookie.set({javascript = "ok"})
|
||||
return ngx.exit(ngx.OK)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
location = {{ ANTIBOT_URI }} {
|
||||
|
||||
default_type 'text/html';
|
||||
|
||||
if ($request_method = GET) {
|
||||
content_by_lua_block {
|
||||
local cookie = require "cookie"
|
||||
local recaptcha = require "recaptcha"
|
||||
local logger = require "logger"
|
||||
if not cookie.is_set("uri") then
|
||||
logger.log(ngx.WARN, "ANTIBOT", "recaptcha fail (1) for " .. ngx.var.remote_addr)
|
||||
return ngx.exit(ngx.HTTP_FORBIDDEN)
|
||||
end
|
||||
local code = recaptcha.get_code("{{ ANTIBOT_URI }}", "{{ ANTIBOT_RECAPTCHA_SITEKEY }}")
|
||||
ngx.say(code)
|
||||
}
|
||||
}
|
||||
|
||||
if ($request_method = POST) {
|
||||
access_by_lua_block {
|
||||
local cookie = require "cookie"
|
||||
local recaptcha = require "recaptcha"
|
||||
local logger = require "logger"
|
||||
if not cookie.is_set("uri") then
|
||||
logger.log(ngx.WARN, "ANTIBOT", "recaptcha fail (2) for " .. ngx.var.remote_addr)
|
||||
return ngx.exit(ngx.HTTP_FORBIDDEN)
|
||||
end
|
||||
ngx.req.read_body()
|
||||
local args, err = ngx.req.get_post_args(1)
|
||||
if err == "truncated" or not args or not args["token"] then
|
||||
logger.log(ngx.WARN, "ANTIBOT", "recaptcha fail (3) for " .. ngx.var.remote_addr)
|
||||
return ngx.exit(ngx.HTTP_FORBIDDEN)
|
||||
end
|
||||
local token = args["token"]
|
||||
local check = recaptcha.check(token, "{{ ANTIBOT_RECAPTCHA_SECRET }}")
|
||||
if check < {{ ANTIBOT_RECAPTCHA_SCORE }} then
|
||||
logger.log(ngx.WARN, "ANTIBOT", "recaptcha fail (4) for " .. ngx.var.remote_addr .. " (score = " .. tostring(check) .. ")")
|
||||
return ngx.exit(ngx.HTTP_FORBIDDEN)
|
||||
end
|
||||
cookie.set({recaptcha = "ok"})
|
||||
return ngx.redirect(cookie.get("uri"))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
auth_basic "{{ AUTH_BASIC_TEXT }}";
|
||||
auth_basic_user_file {{ NGINX_PREFIX }}htpasswd;
|
||||
@@ -1,4 +0,0 @@
|
||||
location {{ AUTH_BASIC_LOCATION }} {
|
||||
auth_basic "{{ AUTH_BASIC_TEXT }}";
|
||||
auth_basic_user_file {{ NGINX_PREFIX }}htpasswd;
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
# Basic Authelia Config
|
||||
# Send a subsequent request to Authelia to verify if the user is authenticated
|
||||
# and has the right permissions to access the resource.
|
||||
auth_request /authelia;
|
||||
# Set the `target_url` variable based on the request. It will be used to build the portal
|
||||
# URL with the correct redirection parameter.
|
||||
auth_request_set $target_url $scheme://$http_host$request_uri;
|
||||
# Set the X-Forwarded-User and X-Forwarded-Groups with the headers
|
||||
# returned by Authelia for the backends which can consume them.
|
||||
# This is not safe, as the backend must make sure that they come from the
|
||||
# proxy. In the future, it's gonna be safe to just use OAuth.
|
||||
auth_request_set $user $upstream_http_remote_user;
|
||||
auth_request_set $groups $upstream_http_remote_groups;
|
||||
auth_request_set $name $upstream_http_remote_name;
|
||||
auth_request_set $email $upstream_http_remote_email;
|
||||
fastcgi_param REMOTE_USER $user;
|
||||
fastcgi_param REMOTE_GROUPS $groups;
|
||||
fastcgi_param REMOTE_NAME $name;
|
||||
fastcgi_param REMOTE_EMAIL $email;
|
||||
proxy_set_header Remote-User $user;
|
||||
proxy_set_header Remote-Groups $groups;
|
||||
proxy_set_header Remote-Name $name;
|
||||
proxy_set_header Remote-Email $email;
|
||||
{% if AUTHELIA_MODE == "portal" +%}
|
||||
error_page 401 =302 {{ AUTHELIA_BACKEND }}/?rd=$target_url;
|
||||
{% endif %}
|
||||
@@ -1,40 +0,0 @@
|
||||
set $upstream_authelia {{ AUTHELIA_UPSTREAM }}/api/verify;
|
||||
|
||||
# Virtual endpoint created by nginx to forward auth requests.
|
||||
location /authelia {
|
||||
internal;
|
||||
proxy_pass_request_body off;
|
||||
proxy_pass $upstream_authelia;
|
||||
proxy_set_header Content-Length "";
|
||||
|
||||
# Timeout if the real server is dead
|
||||
proxy_next_upstream error timeout invalid_header http_500 http_502 http_503;
|
||||
|
||||
# [REQUIRED] Needed by Authelia to check authorizations of the resource.
|
||||
# Provide either X-Original-URL and X-Forwarded-Proto or
|
||||
# X-Forwarded-Proto, X-Forwarded-Host and X-Forwarded-Uri or both.
|
||||
# Those headers will be used by Authelia to deduce the target url of the user.
|
||||
# Basic Proxy Config
|
||||
client_body_buffer_size 128k;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Original-URL $scheme://$http_host$request_uri;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-Method $request_method;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Host $http_host;
|
||||
proxy_set_header X-Forwarded-Uri $request_uri;
|
||||
proxy_set_header X-Forwarded-For $remote_addr;
|
||||
proxy_set_header X-Forwarded-Ssl on;
|
||||
proxy_redirect http:// $scheme://;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Connection "";
|
||||
proxy_cache_bypass $cookie_session;
|
||||
proxy_no_cache $cookie_session;
|
||||
proxy_buffers 4 32k;
|
||||
|
||||
# Advanced Proxy Config
|
||||
send_timeout 5m;
|
||||
proxy_read_timeout 240;
|
||||
proxy_send_timeout 240;
|
||||
proxy_connect_timeout 240;
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
brotli on;
|
||||
brotli_types {{ BROTLI_TYPES }};
|
||||
brotli_comp_level {{ BROTLI_COMP_LEVEL }};
|
||||
brotli_min_length {{ BROTLI_MIN_LENGTH }};
|
||||
@@ -1,6 +0,0 @@
|
||||
etag {{ CLIENT_CACHE_ETAG }};
|
||||
set $cache "";
|
||||
if ($uri ~* \.({{ CLIENT_CACHE_EXTENSIONS }})$) {
|
||||
set $cache "{{ CLIENT_CACHE_CONTROL }}";
|
||||
}
|
||||
add_header Cache-Control $cache;
|
||||
@@ -1 +0,0 @@
|
||||
more_set_headers "Content-Security-Policy: {{ CONTENT_SECURITY_POLICY }}";
|
||||
@@ -1 +0,0 @@
|
||||
set_cookie_flag {{ COOKIE_FLAGS }}{% if COOKIE_AUTO_SECURE_FLAG == "yes" and (AUTO_LETS_ENCRYPT == "yes" or USE_CUSTOM_HTTPS == "yes" or GENERATE_SELF_SIGNED_SSL == "yes") %} Secure{% endif %};
|
||||
@@ -1,5 +0,0 @@
|
||||
{% for k, v in all.items() +%}
|
||||
{% if k.startswith("CUSTOM_HEADER") and v != "" +%}
|
||||
more_set_header "{{ v }}";
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
@@ -1,9 +0,0 @@
|
||||
listen 0.0.0.0:443 ssl {% if HTTP2 == "yes" %}http2{% endif %};
|
||||
ssl_certificate {{ HTTPS_CUSTOM_CERT }};
|
||||
ssl_certificate_key {{ HTTPS_CUSTOM_KEY }};
|
||||
ssl_protocols TLSv1.3;
|
||||
ssl_prefer_server_ciphers off;
|
||||
ssl_session_tickets off;
|
||||
{% if STRICT_TRANSPORT_SECURITY != "" +%}
|
||||
more_set_headers 'Strict-Transport-Security: {{ STRICT_TRANSPORT_SECURITY }}';
|
||||
{% endif %}
|
||||
@@ -1,3 +0,0 @@
|
||||
if ($host !~ ^({{ SERVER_NAME.replace(" ", "|") }})$) {
|
||||
return 444;
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
{% if ERRORS != "" %}
|
||||
{% for element in ERRORS.split(" ") %}
|
||||
{% set code = element.split("=")[0] %}
|
||||
{% set page = element.split("=")[1] %}
|
||||
error_page {{ code }} {{ page }};
|
||||
|
||||
location = {{ page }} {
|
||||
root {{ ROOT_FOLDER }};
|
||||
modsecurity off;
|
||||
internal;
|
||||
}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
{% set default_errors = ["400", "401", "403", "404", "429", "500", "501", "502", "503", "504"] %}
|
||||
{% for default_error in default_errors %}
|
||||
{% if not default_error + "=" in ERRORS +%}
|
||||
error_page {{ default_error }} /errors/{{ default_error }}.html;
|
||||
|
||||
location = /errors/{{ default_error }}.html {
|
||||
root /opt/bunkerized-nginx/defaults;
|
||||
modsecurity off;
|
||||
internal;
|
||||
}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
@@ -1 +0,0 @@
|
||||
more_set_headers "Feature-Policy: {{ FEATURE_POLICY }}";
|
||||
@@ -1,4 +0,0 @@
|
||||
gzip on;
|
||||
gzip_comp_level {{ GZIP_COMP_LEVEL }};
|
||||
gzip_min_length {{ GZIP_MIN_LENGTH }};
|
||||
gzip_types {{ GZIP_TYPES }};
|
||||
@@ -1,3 +0,0 @@
|
||||
{% if USE_AUTH_BASIC == "yes" %}
|
||||
{{ AUTH_BASIC_USER }}:{{ sha512_crypt(AUTH_BASIC_PASSWORD) }}
|
||||
{% endif %}
|
||||
@@ -1,34 +0,0 @@
|
||||
listen 0.0.0.0:{{ HTTPS_PORT }} ssl {% if HTTP2 == "yes" %}http2{% endif %};
|
||||
{% set paths = {"cert": "", "key": ""} %}
|
||||
{% if AUTO_LETS_ENCRYPT == "yes" %}
|
||||
{% set x = paths.update({"cert": "/etc/letsencrypt/live/" + FIRST_SERVER + "/fullchain.pem"}) %}
|
||||
{% set x = paths.update({"key": "/etc/letsencrypt/live/" + FIRST_SERVER + "/privkey.pem"}) %}
|
||||
{% elif USE_CUSTOM_HTTPS == "yes" %}
|
||||
{% set x = paths.update({"cert": CUSTOM_HTTPS_CERT}) %}
|
||||
{% set x = paths.update({"key": CUSTOM_HTTPS_KEY}) %}
|
||||
{% elif GENERATE_SELF_SIGNED_SSL == "yes" %}
|
||||
{% if MULTISITE == "yes" %}
|
||||
{% set x = paths.update({"cert": "/etc/nginx/" + FIRST_SERVER + "/self-cert.pem"}) %}
|
||||
{% set x = paths.update({"key": "/etc/nginx/" + FIRST_SERVER + "/self-key.pem"}) %}
|
||||
{% else %}
|
||||
{% set x = paths.update({"cert": "/etc/nginx/self-cert.pem"}) %}
|
||||
{% set x = paths.update({"key": "/etc/nginx/self-key.pem"}) %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
ssl_certificate {{ paths["cert"] }};
|
||||
ssl_certificate_key {{ paths["key"] }};
|
||||
ssl_protocols {{ HTTPS_PROTOCOLS }};
|
||||
ssl_prefer_server_ciphers on;
|
||||
ssl_session_tickets off;
|
||||
ssl_session_timeout 1d;
|
||||
ssl_session_cache shared:MozSSL:10m;
|
||||
{% if STRICT_TRANSPORT_SECURITY != "" +%}
|
||||
more_set_headers 'Strict-Transport-Security: {{ STRICT_TRANSPORT_SECURITY }}';
|
||||
{% endif %}
|
||||
{% if "TLSv1.2" in HTTPS_PROTOCOLS +%}
|
||||
ssl_dhparam /etc/nginx/dhparam;
|
||||
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
|
||||
{% endif %}
|
||||
{% if AUTO_LETS_ENCRYPT +%}
|
||||
include {{ NGINX_PREFIX }}lets-encrypt-webroot.conf;
|
||||
{% endif %}
|
||||
@@ -1 +0,0 @@
|
||||
sub_filter '</body>' '{{ INJECT_BODY }}</body>';
|
||||
@@ -1,3 +0,0 @@
|
||||
location ~ ^/.well-known/acme-challenge/ {
|
||||
root /opt/bunkerized-nginx/acme-challenge;
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
limit_conn ddos {{ LIMIT_CONN_MAX }};
|
||||
@@ -1,3 +0,0 @@
|
||||
limit_req_status 429;
|
||||
limit_req zone=limit burst={{ LIMIT_REQ_BURST }} nodelay;
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
log_by_lua_block {
|
||||
|
||||
local logger = require "logger"
|
||||
local cjson = require "cjson"
|
||||
|
||||
-- bad behavior
|
||||
local use_bad_behavior = {% if USE_BAD_BEHAVIOR == "yes" %}true{% else %}false{% endif +%}
|
||||
local behavior = require "behavior"
|
||||
local bad_behavior_status_codes = {% raw %}{{% endraw %}{% if BAD_BEHAVIOR_STATUS_CODES != "" %}{% set elements = BAD_BEHAVIOR_STATUS_CODES.split(" ") %}{% for i in range(0, elements|length) %}"{{ elements[i] }}"{% if i < elements|length-1 %},{% endif %}{% endfor %}{% endif %}{% raw %}}{% endraw +%}
|
||||
local bad_behavior_threshold = {{ BAD_BEHAVIOR_THRESHOLD }}
|
||||
local bad_behavior_count_time = {{ BAD_BEHAVIOR_COUNT_TIME }}
|
||||
local bad_behavior_ban_time = {{ BAD_BEHAVIOR_BAN_TIME }}
|
||||
|
||||
if use_bad_behavior then
|
||||
local new_bad_behavior_ban = false
|
||||
if not behavior.is_banned() then
|
||||
new_bad_behavior_ban = behavior.count(bad_behavior_status_codes, bad_behavior_threshold, bad_behavior_count_time, bad_behavior_ban_time)
|
||||
end
|
||||
end
|
||||
|
||||
-- remote API
|
||||
local use_remote_api = {% if USE_REMOTE_API == "yes" %}true{% else %}false{% endif +%}
|
||||
local remoteapi = require "remoteapi"
|
||||
local iputils = require "resty.iputils"
|
||||
|
||||
if use_remote_api and ngx.status == ngx.HTTP_FORBIDDEN and not iputils.ip_in_cidrs(ngx.var.remote_addr, cjson.decode(ngx.shared.reserved_ips:get("data"))) and ngx.shared.remote_api:get("id") ~= "empty" then
|
||||
if ngx.shared.remote_api:get("ping") == "ko" then
|
||||
if remoteapi.ping2() then
|
||||
ngx.shared.remote_api:set("ping", "ok", 0)
|
||||
logger.log(ngx.NOTICE, "REMOTE API", "Successfully requested the remote API again")
|
||||
else
|
||||
logger.log(ngx.ERR, "REMOTE API", "Can't contact the remote API, feature will be disabled")
|
||||
end
|
||||
end
|
||||
if ngx.shared.remote_api:get("ping") ~= "ko" then
|
||||
local reason = "other"
|
||||
if use_bad_behavior and new_bad_behavior_ban then
|
||||
reason = "behavior"
|
||||
end
|
||||
local report_ip = function (premature, ip, reason)
|
||||
if premature then
|
||||
return
|
||||
end
|
||||
local remoteapi = require "remoteapi"
|
||||
local logger = require "logger"
|
||||
local res, data = remoteapi.ip(ip, reason)
|
||||
-- TODO : find a way to log
|
||||
end
|
||||
local ok, err = ngx.timer.at(0, report_ip, ngx.var.remote_addr, reason)
|
||||
if not ok then
|
||||
logger.log(ngx.ERR, "REMOTE API", "Error while creating report timer " .. err)
|
||||
else
|
||||
logger.log(ngx.NOTICE, "REMOTE API", "Reporting " .. ngx.var.remote_addr .. "(reason: " .. reason .. ") to the remote API")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
}
|
||||
@@ -1,333 +0,0 @@
|
||||
{% if ANTIBOT_SESSION_SECRET == "random" +%}
|
||||
set $session_secret {{ random(32) }};
|
||||
{% else +%}
|
||||
set $session_secret {{ ANTIBOT_SESSION_SECRET }};
|
||||
{% endif %}
|
||||
set $session_check_addr on;
|
||||
|
||||
access_by_lua_block {
|
||||
|
||||
-- disable checks for internal requests
|
||||
if ngx.req.is_internal() then
|
||||
return
|
||||
end
|
||||
|
||||
-- let's encrypt
|
||||
local use_lets_encrypt = {% if AUTO_LETS_ENCRYPT == "yes" %}true{% else %}false{% endif +%}
|
||||
|
||||
-- redis
|
||||
local use_redis = {% if USE_REDIS == "yes" %}true{% else %}false{% endif +%}
|
||||
local redis_host = "{{ REDIS_HOST }}"
|
||||
|
||||
-- external blacklists
|
||||
local use_user_agents = {% if BLOCK_USER_AGENT == "yes" %}true{% else %}false{% endif +%}
|
||||
local use_proxies = {% if BLOCK_PROXIES == "yes" %}true{% else %}false{% endif +%}
|
||||
local use_abusers = {% if BLOCK_ABUSERS == "yes" %}true{% else %}false{% endif +%}
|
||||
local use_tor_exit_nodes = {% if BLOCK_TOR_EXIT_NODE == "yes" %}true{% else %}false{% endif +%}
|
||||
local use_referrers = {% if BLOCK_REFERRER == "yes" %}true{% else %}false{% endif +%}
|
||||
|
||||
-- countries
|
||||
local use_country = {% if WHITELIST_COUNTRY != "" or BLACKLIST_COUNTRY != "" %}true{% else %}false{% endif +%}
|
||||
|
||||
-- antibot
|
||||
local use_antibot_cookie = {% if USE_ANTIBOT == "cookie" %}true{% else %}false{% endif +%}
|
||||
local use_antibot_javascript = {% if USE_ANTIBOT == "javascript" %}true{% else %}false{% endif +%}
|
||||
local use_antibot_captcha = {% if USE_ANTIBOT == "captcha" %}true{% else %}false{% endif +%}
|
||||
local use_antibot_recaptcha = {% if USE_ANTIBOT == "recaptcha" %}true{% else %}false{% endif +%}
|
||||
|
||||
-- resolvers
|
||||
local dns_resolvers = {% raw %}{{% endraw %}{% if DNS_RESOLVERS != "" %}{% set elements = DNS_RESOLVERS.split(" ") %}{% for i in range(0, elements|length) %}"{{ elements[i] }}"{% if i < elements|length-1 %},{% endif %}{% endfor %}{% endif %}{% raw %}}{% endraw +%}
|
||||
|
||||
-- whitelist
|
||||
local use_whitelist_ip = {% if USE_WHITELIST_IP == "yes" %}true{% else %}false{% endif +%}
|
||||
local use_whitelist_reverse = {% if USE_WHITELIST_REVERSE == "yes" %}true{% else %}false{% endif +%}
|
||||
local whitelist_ip_list = {% raw %}{{% endraw %}{% if WHITELIST_IP_LIST != "" %}{% set elements = WHITELIST_IP_LIST.split(" ") %}{% for i in range(0, elements|length) %}"{{ elements[i] }}"{% if i < elements|length-1 %},{% endif %}{% endfor %}{% endif %}{% raw %}}{% endraw +%}
|
||||
local whitelist_reverse_list = {% raw %}{{% endraw %}{% if WHITELIST_REVERSE_LIST != "" %}{% set elements = WHITELIST_REVERSE_LIST.split(" ") %}{% for i in range(0, elements|length) %}"{{ elements[i] }}"{% if i < elements|length-1 %},{% endif %}{% endfor %}{% endif %}{% raw %}}{% endraw +%}
|
||||
|
||||
-- blacklist
|
||||
local use_blacklist_ip = {% if USE_BLACKLIST_IP == "yes" %}true{% else %}false{% endif +%}
|
||||
local use_blacklist_reverse = {% if USE_BLACKLIST_REVERSE == "yes" %}true{% else %}false{% endif +%}
|
||||
local blacklist_ip_list = {% raw %}{{% endraw %}{% if BLACKLIST_IP_LIST != "" %}{% set elements = BLACKLIST_IP_LIST.split(" ") %}{% for i in range(0, elements|length) %}"{{ elements[i] }}"{% if i < elements|length-1 %},{% endif %}{% endfor %}{% endif %}{% raw %}}{% endraw +%}
|
||||
local blacklist_reverse_list = {% raw %}{{% endraw %}{% if BLACKLIST_REVERSE_LIST != "" %}{% set elements = BLACKLIST_REVERSE_LIST.split(" ") %}{% for i in range(0, elements|length) %}"{{ elements[i] }}"{% if i < elements|length-1 %},{% endif %}{% endfor %}{% endif %}{% raw %}}{% endraw +%}
|
||||
|
||||
-- dnsbl
|
||||
local use_dnsbl = {% if USE_DNSBL == "yes" %}true{% else %}false{% endif +%}
|
||||
local dnsbl_list = {% raw %}{{% endraw %}{% if DNSBL_LIST != "" %}{% set elements = DNSBL_LIST.split(" ") %}{% for i in range(0, elements|length) %}"{{ elements[i] }}"{% if i < elements|length-1 %},{% endif %}{% endfor %}{% endif %}{% raw %}}{% endraw +%}
|
||||
|
||||
-- bad behavior
|
||||
local use_bad_behavior = {% if USE_BAD_BEHAVIOR == "yes" %}true{% else %}false{% endif +%}
|
||||
|
||||
-- limit req
|
||||
local use_limit_req = {% if USE_LIMIT_REQ == "yes" %}true{% else %}false{% endif +%}
|
||||
|
||||
-- remote API
|
||||
local use_remote_api = {% if USE_REMOTE_API == "yes" %}true{% else %}false{% endif +%}
|
||||
|
||||
-- include LUA code
|
||||
local whitelist = require "whitelist"
|
||||
local blacklist = require "blacklist"
|
||||
local dnsbl = require "dnsbl"
|
||||
local cookie = require "cookie"
|
||||
local cjson = require "cjson"
|
||||
local javascript = require "javascript"
|
||||
local captcha = require "captcha"
|
||||
local recaptcha = require "recaptcha"
|
||||
local iputils = require "resty.iputils"
|
||||
local behavior = require "behavior"
|
||||
local logger = require "logger"
|
||||
local redis = require "resty.redis"
|
||||
local checker = require "checker"
|
||||
local limitreq = require "limitreq"
|
||||
|
||||
-- user variables
|
||||
local antibot_uri = "{{ ANTIBOT_URI }}"
|
||||
local whitelist_user_agent = {% raw %}{{% endraw %}{% if WHITELIST_USER_AGENT != "" %}{% set elements = WHITELIST_USER_AGENT.split(" ") %}{% for i in range(0, elements|length) %}"{{ elements[i] }}"{% if i < elements|length-1 %},{% endif %}{% endfor %}{% endif %}{% raw %}}{% endraw +%}
|
||||
local whitelist_uri = {% raw %}{{% endraw %}{% if WHITELIST_URI != "" %}{% set elements = WHITELIST_URI.split(" ") %}{% for i in range(0, elements|length) %}"{{ elements[i] }}"{% if i < elements|length-1 %},{% endif %}{% endfor %}{% endif %}{% raw %}}{% endraw +%}
|
||||
|
||||
-- check if already in whitelist cache
|
||||
if use_whitelist_ip and whitelist.ip_cached_ok() then
|
||||
ngx.exit(ngx.OK)
|
||||
end
|
||||
if use_whitelist_reverse and whitelist.reverse_cached_ok() then
|
||||
ngx.exit(ngx.OK)
|
||||
end
|
||||
|
||||
-- check if already in blacklist cache
|
||||
if use_blacklist_ip and blacklist.ip_cached_ko() then
|
||||
ngx.exit(ngx.HTTP_FORBIDDEN)
|
||||
end
|
||||
if use_blacklist_reverse and blacklist.reverse_cached_ko() then
|
||||
ngx.exit(ngx.HTTP_FORBIDDEN)
|
||||
end
|
||||
|
||||
-- check if already in dnsbl cache
|
||||
if use_dnsbl and dnsbl.cached_ko() then
|
||||
ngx.exit(ngx.HTTP_FORBIDDEN)
|
||||
end
|
||||
|
||||
-- check if IP is whitelisted (only if not in cache)
|
||||
if use_whitelist_ip and not whitelist.ip_cached() then
|
||||
if whitelist.check_ip(whitelist_ip_list) then
|
||||
ngx.exit(ngx.OK)
|
||||
end
|
||||
end
|
||||
|
||||
-- check if reverse is whitelisted (only if not in cache)
|
||||
if use_whitelist_reverse and not whitelist.reverse_cached() then
|
||||
if whitelist.check_reverse(whitelist_reverse_list) then
|
||||
ngx.exit(ngx.OK)
|
||||
end
|
||||
end
|
||||
|
||||
-- check if URI is whitelisted
|
||||
for k, v in pairs(whitelist_uri) do
|
||||
if ngx.var.request_uri == v then
|
||||
logger.log(ngx.NOTICE, "WHITELIST", "URI " .. v .. " is whitelisted")
|
||||
ngx.exit(ngx.OK)
|
||||
end
|
||||
end
|
||||
|
||||
-- check if it's certbot
|
||||
if use_lets_encrypt and string.match(ngx.var.request_uri, "^/%.well%-known/acme%-challenge/[A-Za-z0-9%-%_]+$") then
|
||||
logger.log(ngx.INFO, "LETSENCRYPT", "got a visit from Let's Encrypt")
|
||||
ngx.exit(ngx.OK)
|
||||
end
|
||||
|
||||
-- check if IP is blacklisted (only if not in cache)
|
||||
if use_blacklist_ip and not blacklist.ip_cached() then
|
||||
if blacklist.check_ip(blacklist_ip_list) then
|
||||
ngx.exit(ngx.HTTP_FORBIDDEN)
|
||||
end
|
||||
end
|
||||
|
||||
-- check if reverse is blacklisted (only if not in cache)
|
||||
if use_blacklist_reverse and not blacklist.reverse_cached() then
|
||||
if blacklist.check_reverse(blacklist_reverse_list, dns_resolvers) then
|
||||
ngx.exit(ngx.HTTP_FORBIDDEN)
|
||||
end
|
||||
end
|
||||
|
||||
-- check if IP is banned because of "bad behavior"
|
||||
if use_bad_behavior and behavior.is_banned() then
|
||||
logger.log(ngx.WARN, "BEHAVIOR", "IP " .. ngx.var.remote_addr .. " is banned because of bad behavior")
|
||||
ngx.exit(ngx.HTTP_FORBIDDEN)
|
||||
end
|
||||
|
||||
-- check if IP is banned because of "request limit"
|
||||
if use_limit_req then
|
||||
{% if USE_LIMIT_REQ == "yes" %}
|
||||
{% for k, v in all.items() %}
|
||||
{% if k.startswith("LIMIT_REQ_URL") and v != "" +%}
|
||||
{% set url = v %}
|
||||
{% set rate = all[k.replace("URL", "RATE")] if k.replace("URL", "RATE") in all else "1r/s" %}
|
||||
{% set burst = all[k.replace("URL", "BURST")] if k.replace("URL", "BURST") in all else "5" %}
|
||||
{% set delay = all[k.replace("URL", "DELAY")] if k.replace("URL", "DELAY") in all else "1" %}
|
||||
{% if url == "/" %}
|
||||
if limitreq.check("{{ rate }}", {{ burst }}, {{ delay }}) then
|
||||
ngx.exit(ngx.HTTP_TOO_MANY_REQUESTS)
|
||||
end
|
||||
{% else %}
|
||||
if ngx.var.uri == "{{ url }}" and limitreq.check("{{ rate }}", {{ burst }}, {{ delay }}) then
|
||||
ngx.exit(ngx.HTTP_TOO_MANY_REQUESTS)
|
||||
end
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
end
|
||||
|
||||
-- our redis client
|
||||
local redis_client = nil
|
||||
if use_redis then
|
||||
redis_client = redis:new()
|
||||
local ok, err = redis_client:connect(redis_host, 6379)
|
||||
if not ok then
|
||||
redis_client = nil
|
||||
logger.log(ngx.ERR, "REDIS", "Can't connect to the Redis service " .. redis_host)
|
||||
end
|
||||
end
|
||||
|
||||
-- check if IP is in proxies list
|
||||
if use_proxies then
|
||||
local checker = checker:new("proxies", ngx.shared.proxies_data, redis_client, "simple")
|
||||
if checker:check(iputils.ip2bin(ngx.var.remote_addr)) then
|
||||
logger.log(ngx.WARN, "PROXIES", "IP " .. ngx.var.remote_addr .. " is in proxies list")
|
||||
ngx.exit(ngx.HTTP_FORBIDDEN)
|
||||
end
|
||||
end
|
||||
|
||||
-- check if IP is in abusers list
|
||||
if use_abusers then
|
||||
local checker = checker:new("abusers", ngx.shared.abusers_data, redis_client, "simple")
|
||||
if checker:check(iputils.ip2bin(ngx.var.remote_addr)) then
|
||||
logger.log(ngx.WARN, "ABUSERS", "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 checker = checker:new("exit-nodes", ngx.shared.tor_exit_nodes_data, redis_client, "simple")
|
||||
if checker:check(iputils.ip2bin(ngx.var.remote_addr)) then
|
||||
logger.log(ngx.WARN, "TOR", "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
|
||||
logger.log(ngx.NOTICE, "WHITELIST", "User-Agent " .. ngx.var.http_user_agent .. " is whitelisted")
|
||||
whitelisted = true
|
||||
break
|
||||
end
|
||||
end
|
||||
if not whitelisted then
|
||||
local checker = checker:new("user-agents", ngx.shared.user_agents_data, redis_client, "match")
|
||||
if checker:check(ngx.var.http_user_agent) then
|
||||
logger.log(ngx.WARN, "USER-AGENTS", "User-Agent " .. ngx.var.http_user_agent .. " is blacklisted")
|
||||
ngx.exit(ngx.HTTP_FORBIDDEN)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- check if referrer is allowed
|
||||
if use_referrer and ngx.var.http_referer ~= nil then
|
||||
local checker = checker:new("referrers", ngx.shared.referrers_data, redis_client, "match")
|
||||
if checker:check(ngx.var.http_referer) then
|
||||
logger.log(ngx.WARN, "REFERRERS", "Referrer " .. ngx.var.http_referer .. " is blacklisted")
|
||||
ngx.exit(ngx.HTTP_FORBIDDEN)
|
||||
end
|
||||
end
|
||||
|
||||
-- check if country is allowed
|
||||
if use_country and ngx.var.allowed_country == "no" and not iputils.ip_in_cidrs(ngx.var.remote_addr, cjson.decode(ngx.shared.reserved_ips:get("data"))) then
|
||||
logger.log(ngx.WARN, "COUNTRY", "Country of " .. ngx.var.remote_addr .. " is blacklisted")
|
||||
ngx.exit(ngx.HTTP_FORBIDDEN)
|
||||
end
|
||||
|
||||
-- check if IP is in DNSBLs (only if not in cache)
|
||||
if use_dnsbl and not dnsbl.cached() then
|
||||
if dnsbl.check(dnsbl_list, dns_resolvers) then
|
||||
ngx.exit(ngx.HTTP_FORBIDDEN)
|
||||
end
|
||||
end
|
||||
|
||||
-- check if IP is in distributed DB
|
||||
if use_remote_api then
|
||||
local checker = checker:new("remote-api-db", ngx.shared.remote_api_db, redis_client, "simple")
|
||||
if checker:check(iputils.ip2bin(ngx.var.remote_addr)) then
|
||||
logger.log(ngx.WARN, "REMOTE API", "IP " .. ngx.var.remote_addr .. " is in the distributed DB")
|
||||
ngx.exit(ngx.HTTP_FORBIDDEN)
|
||||
end
|
||||
end
|
||||
|
||||
-- cookie check
|
||||
if use_antibot_cookie and ngx.var.uri ~= "/favicon.ico" then
|
||||
if not cookie.is_set("uri") then
|
||||
if ngx.var.request_uri ~= antibot_uri then
|
||||
cookie.set({uri = ngx.var.request_uri})
|
||||
return ngx.redirect(antibot_uri)
|
||||
end
|
||||
logger.log(ngx.WARN, "ANTIBOT", "cookie fail for " .. ngx.var.remote_addr)
|
||||
return ngx.exit(ngx.HTTP_FORBIDDEN)
|
||||
else
|
||||
if ngx.var.request_uri == antibot_uri then
|
||||
return ngx.redirect(cookie.get("uri"))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- javascript check
|
||||
if use_antibot_javascript and ngx.var.uri ~= "/favicon.ico" then
|
||||
if not cookie.is_set("javascript") then
|
||||
if ngx.var.request_uri ~= antibot_uri then
|
||||
cookie.set({uri = ngx.var.request_uri, challenge = javascript.get_challenge()})
|
||||
return ngx.redirect(antibot_uri)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- captcha check
|
||||
if use_antibot_captcha and ngx.var.uri ~= "/favicon.ico" then
|
||||
if not cookie.is_set("captcha") then
|
||||
if ngx.var.request_uri ~= antibot_uri then
|
||||
cookie.set({uri = ngx.var.request_uri})
|
||||
return ngx.redirect(antibot_uri)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- recaptcha check
|
||||
if use_antibot_recaptcha and ngx.var.uri ~= "/favicon.ico" then
|
||||
if not cookie.is_set("recaptcha") then
|
||||
if ngx.var.request_uri ~= antibot_uri then
|
||||
cookie.set({uri = ngx.var.request_uri})
|
||||
return ngx.redirect(antibot_uri)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- plugins check
|
||||
local plugins, flags = ngx.shared.plugins_data:get("plugins")
|
||||
if plugins ~= nil then
|
||||
for plugin_id in string.gmatch(plugins, "%w+") do
|
||||
local plugin = require(plugin_id .. "/" .. plugin_id)
|
||||
plugin.check()
|
||||
end
|
||||
end
|
||||
|
||||
ngx.exit(ngx.OK)
|
||||
|
||||
}
|
||||
|
||||
{% if USE_ANTIBOT == "javascript" +%}
|
||||
include {{ NGINX_PREFIX }}antibot-javascript.conf;
|
||||
{% elif USE_ANTIBOT == "captcha" +%}
|
||||
include {{ NGINX_PREFIX }}antibot-captcha.conf;
|
||||
{% elif USE_ANTIBOT == "recaptcha" +%}
|
||||
include {{ NGINX_PREFIX }}antibot-recaptcha.conf;
|
||||
{% endif %}
|
||||
@@ -1,78 +0,0 @@
|
||||
# process rules with disruptive actions
|
||||
SecRuleEngine On
|
||||
|
||||
# allow body checks
|
||||
SecRequestBodyAccess On
|
||||
|
||||
# enable XML parsing
|
||||
SecRule REQUEST_HEADERS:Content-Type "(?:application(?:/soap\+|/)|text/)xml" \
|
||||
"id:'200000',phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=XML"
|
||||
|
||||
# enable JSON parsing
|
||||
SecRule REQUEST_HEADERS:Content-Type "application/json" \
|
||||
"id:'200001',phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=JSON"
|
||||
|
||||
# maximum data size
|
||||
SecRequestBodyLimit 13107200
|
||||
SecRequestBodyNoFilesLimit 131072
|
||||
|
||||
# reject requests if bigger than max data size
|
||||
SecRequestBodyLimitAction Reject
|
||||
|
||||
# reject if we can't process the body
|
||||
SecRule REQBODY_ERROR "!@eq 0" \
|
||||
"id:'200002', phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.',logdata:'%{reqbody_error_msg}',severity:2"
|
||||
|
||||
# be strict with multipart/form-data body
|
||||
SecRule MULTIPART_STRICT_ERROR "!@eq 0" \
|
||||
"id:'200003',phase:2,t:none,log,deny,status:400, \
|
||||
msg:'Multipart request body failed strict validation: \
|
||||
PE %{REQBODY_PROCESSOR_ERROR}, \
|
||||
BQ %{MULTIPART_BOUNDARY_QUOTED}, \
|
||||
BW %{MULTIPART_BOUNDARY_WHITESPACE}, \
|
||||
DB %{MULTIPART_DATA_BEFORE}, \
|
||||
DA %{MULTIPART_DATA_AFTER}, \
|
||||
HF %{MULTIPART_HEADER_FOLDING}, \
|
||||
LF %{MULTIPART_LF_LINE}, \
|
||||
SM %{MULTIPART_MISSING_SEMICOLON}, \
|
||||
IQ %{MULTIPART_INVALID_QUOTING}, \
|
||||
IP %{MULTIPART_INVALID_PART}, \
|
||||
IH %{MULTIPART_INVALID_HEADER_FOLDING}, \
|
||||
FL %{MULTIPART_FILE_LIMIT_EXCEEDED}'"
|
||||
SecRule MULTIPART_UNMATCHED_BOUNDARY "@eq 1" \
|
||||
"id:'200004',phase:2,t:none,log,deny,msg:'Multipart parser detected a possible unmatched boundary.'"
|
||||
|
||||
# enable response body checks
|
||||
SecResponseBodyAccess On
|
||||
SecResponseBodyMimeType text/plain text/html text/xml application/json
|
||||
SecResponseBodyLimit 524288
|
||||
SecResponseBodyLimitAction ProcessPartial
|
||||
|
||||
# log usefull stuff
|
||||
SecAuditEngine {{ MODSECURITY_SEC_AUDIT_ENGINE }}
|
||||
SecAuditLogType Serial
|
||||
SecAuditLog /var/log/nginx/modsec_audit.log
|
||||
|
||||
# include OWASP CRS configuration
|
||||
{% if USE_MODSECURITY_CRS == "yes" %}
|
||||
include /opt/bunkerized-nginx/crs-setup.conf
|
||||
|
||||
# custom CRS configurations before loading rules (exclusions)
|
||||
{% if is_custom_conf("/opt/bunkerized-nginx/modsec-crs-confs") +%}
|
||||
include /opt/bunkerized-nginx/modsec-crs-confs/*.conf
|
||||
{% endif %}
|
||||
{% if MULTISITE == "yes" and is_custom_conf("/opt/bunkerized-nginx/modsec-crs-confs/" + FIRST_SERVER) +%}
|
||||
include /opt/bunkerized-nginx/modsec-crs-confs/{{ FIRST_SERVER }}/*.conf
|
||||
{% endif %}
|
||||
|
||||
# include OWASP CRS rules
|
||||
include /opt/bunkerized-nginx/crs/*.conf
|
||||
{% endif %}
|
||||
|
||||
# custom rules after loading the CRS
|
||||
{% if is_custom_conf("/opt/bunkerized-nginx/modsec-confs") +%}
|
||||
include /opt/bunkerized-nginx/modsec-confs/*.conf
|
||||
{% endif %}
|
||||
{% if MULTISITE == "yes" and is_custom_conf("/opt/bunkerized-nginx/modsec-confs/" + FIRST_SERVER) +%}
|
||||
include /opt/bunkerized-nginx/modsec-confs/{{ FIRST_SERVER }}/*.conf
|
||||
{% endif %}
|
||||
@@ -1,2 +0,0 @@
|
||||
modsecurity on;
|
||||
modsecurity_rules_file {{ NGINX_PREFIX }}modsecurity-rules.conf;
|
||||
@@ -1,4 +0,0 @@
|
||||
open_file_cache {{ OPEN_FILE_CACHE }};
|
||||
open_file_cache_errors {{ OPEN_FILE_CACHE_ERRORS }};
|
||||
open_file_cache_min_uses {{ OPEN_FILE_CACHE_MIN_USES }};
|
||||
open_file_cache_valid {{ OPEN_FILE_CACHE_VALID }};
|
||||
@@ -1 +0,0 @@
|
||||
more_set_headers "Permissions-Policy: {{ PERMISSIONS_POLICY }}";
|
||||
@@ -1,9 +0,0 @@
|
||||
location ~ \.php$ {
|
||||
{% if REMOTE_PHP != "" +%}
|
||||
set $backend "{{ REMOTE_PHP }}:9000";
|
||||
fastcgi_pass $backend;
|
||||
{% elif LOCAL_PHP != "" +%}
|
||||
fastcgi_pass unix:{{ LOCAL_PHP }};
|
||||
{% endif %}
|
||||
fastcgi_index index.php;
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
proxy_cache proxycache;
|
||||
proxy_cache_methods {{ PROXY_CACHE_METHODS }};
|
||||
proxy_cache_min_uses {{ PROXY_CACHE_MIN_USES }};
|
||||
proxy_cache_key {{ PROXY_CACHE_KEY }};
|
||||
proxy_no_cache {{ PROXY_NO_CACHE }};
|
||||
proxy_cache_bypass {{ PROXY_CACHE_BYPASS }};
|
||||
{% if PROXY_CACHE_VALID != "" %}
|
||||
{% for element in PROXY_CACHE_VALID.split(" ") +%}
|
||||
proxy_cache_valid {{ element.split("=")[0] }} {{ element.split("=")[1] }};
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
@@ -1,8 +0,0 @@
|
||||
{% if PROXY_REAL_IP_FROM != "" %}
|
||||
{% for element in PROXY_REAL_IP_FROM.split(" ") +%}
|
||||
set_real_ip_from {{ element }};
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
real_ip_header {{ PROXY_REAL_IP_HEADER }};
|
||||
real_ip_recursive {{ PROXY_REAL_IP_RECURSIVE }};
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
if ($scheme = http) {
|
||||
return 301 https://$host$request_uri;
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
{% if REDIRECT_TO_REQUEST_URI == "yes" %}
|
||||
return 301 {{ REDIRECT_TO }}$request_uri;
|
||||
{% else %}
|
||||
return 301 {{ REDIRECT_TO }};
|
||||
{% endif %}
|
||||
@@ -1 +0,0 @@
|
||||
more_set_headers "Referrer-Policy: {{ REFERRER_POLICY }}";
|
||||
@@ -1,6 +0,0 @@
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Protocol $scheme;
|
||||
proxy_set_header X-Forwarded-Host $http_host;
|
||||
@@ -1,39 +0,0 @@
|
||||
{% if USE_REVERSE_PROXY == "yes" %}
|
||||
{% for k, v in all.items() %}
|
||||
{% if k.startswith("REVERSE_PROXY_URL") and v != "" +%}
|
||||
{% set url = v %}
|
||||
{% set host = all[k.replace("URL", "HOST")] if k.replace("URL", "HOST") in all else "" %}
|
||||
{% set ws = all[k.replace("URL", "WS")] if k.replace("URL", "WS") in all else "" %}
|
||||
{% set headers = all[k.replace("URL", "HEADERS")] if k.replace("URL", "HEADERS") in all else "" %}
|
||||
{% set buffering = all[k.replace("URL", "BUFFERING")] if k.replace("URL", "BUFFERING") in all else "yes" %}
|
||||
{% set keepalive = all[k.replace("URL", "KEEPALIVE")] if k.replace("URL", "KEEPALIVE") in all else "yes" %}
|
||||
location {{ url }} {% raw %}{{% endraw +%}
|
||||
etag off;
|
||||
set $backend "{{ host }}";
|
||||
proxy_pass $backend;
|
||||
{% if buffering == "yes" +%}
|
||||
proxy_buffering on;
|
||||
{% else +%}
|
||||
proxy_buffering off;
|
||||
{% endif %}
|
||||
{% if USE_AUTHELIA == "yes" +%}
|
||||
include {{ NGINX_PREFIX }}authelia-auth-request.conf;
|
||||
{% endif %}
|
||||
include {{ NGINX_PREFIX }}reverse-proxy-headers.conf;
|
||||
{% if ws == "yes" +%}
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "Upgrade";
|
||||
{% elif keepalive == "yes" +%}
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Connection "";
|
||||
{% endif %}
|
||||
{% if headers != "" %}
|
||||
{% for header in headers.split(";") +%}
|
||||
proxy_set_header {{ header }};
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% raw %}}{% endraw %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
@@ -1,3 +0,0 @@
|
||||
root {{ ROOT_FOLDER }};
|
||||
index index.html index.php;
|
||||
try_files $uri $uri/ =404;
|
||||
@@ -1,196 +0,0 @@
|
||||
# custom config before server block
|
||||
include /opt/bunkerized-nginx/pre-server-confs/*.conf;
|
||||
{% if MULTISITE == "yes" +%}
|
||||
include /opt/bunkerized-nginx/pre-server-confs/{{ FIRST_SERVER }}/*.conf;
|
||||
{% endif %}
|
||||
|
||||
server {
|
||||
|
||||
# FastCGI variables
|
||||
{% if REMOTE_PHP != "" or LOCAL_PHP != "" +%}
|
||||
include {{ NGINX_PREFIX }}fastcgi.conf;
|
||||
{% endif %}
|
||||
|
||||
# custom config
|
||||
include /opt/bunkerized-nginx/server-confs/*.conf;
|
||||
{% if MULTISITE == "yes" +%}
|
||||
include /opt/bunkerized-nginx/server-confs/{{ FIRST_SERVER }}/*.conf;
|
||||
{% endif %}
|
||||
|
||||
# proxy real IP
|
||||
{% if PROXY_REAL_IP == "yes" +%}
|
||||
include {{ NGINX_PREFIX }}proxy-real-ip.conf;
|
||||
{% endif %}
|
||||
|
||||
# include LUA files
|
||||
include {{ NGINX_PREFIX }}main-lua.conf;
|
||||
include {{ NGINX_PREFIX }}log-lua.conf;
|
||||
|
||||
# ModSecurity
|
||||
{% if USE_MODSECURITY == "yes" +%}
|
||||
include {{ NGINX_PREFIX }}modsecurity.conf;
|
||||
{% endif %}
|
||||
|
||||
# HTTP listen
|
||||
{% if LISTEN_HTTP == "yes" +%}
|
||||
listen 0.0.0.0:{{ HTTP_PORT }};
|
||||
{% endif %}
|
||||
|
||||
# HTTPS listen + config
|
||||
{% if AUTO_LETS_ENCRYPT == "yes" or USE_CUSTOM_HTTPS == "yes" or GENERATE_SELF_SIGNED_SSL == "yes" +%}
|
||||
include {{ NGINX_PREFIX }}https.conf;
|
||||
{% endif %}
|
||||
|
||||
# HTTP to HTTPS
|
||||
{% if REDIRECT_HTTP_TO_HTTPS == "yes" +%}
|
||||
include {{ NGINX_PREFIX }}redirect-http-to-https.conf;
|
||||
{% endif %}
|
||||
|
||||
# server name (vhost)
|
||||
server_name {{ SERVER_NAME }};
|
||||
|
||||
# disable default server
|
||||
{% if DISABLE_DEFAULT_SERVER == "yes" and MULTISITE != "yes" +%}
|
||||
include {{ NGINX_PREFIX }}disable-default-server.conf;
|
||||
{% endif %}
|
||||
|
||||
# serve local files
|
||||
{% if SERVE_FILES == "yes" +%}
|
||||
include {{ NGINX_PREFIX }}serve-files.conf;
|
||||
{% endif %}
|
||||
|
||||
# allowed HTTP methods
|
||||
if ($request_method !~ ^({{ ALLOWED_METHODS }})$) {
|
||||
return 405;
|
||||
}
|
||||
|
||||
# requests limiting
|
||||
#{% if USE_LIMIT_REQ == "yes" +%}
|
||||
# include {{ NGINX_PREFIX }}limit-req.conf;
|
||||
#{% endif %}
|
||||
|
||||
# connections limiting
|
||||
{% if USE_LIMIT_CONN == "yes" +%}
|
||||
include {{ NGINX_PREFIX }}limit-conn.conf;
|
||||
{% endif %}
|
||||
|
||||
# auth basic
|
||||
{% if USE_AUTH_BASIC == "yes" +%}
|
||||
{% if AUTH_BASIC_LOCATION == "sitewide" %}
|
||||
include {{ NGINX_PREFIX }}auth-basic-sitewide.conf;
|
||||
{% else %}
|
||||
include {{ NGINX_PREFIX }}auth-basic.conf;
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
# remove headers
|
||||
{% if REMOVE_HEADERS != "" %}
|
||||
{% for header in REMOVE_HEADERS.split(" ") +%}
|
||||
more_clear_headers '{{ header }}';
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
# X-Frame-Option header
|
||||
{% if X_FRAME_OPTIONS != "" +%}
|
||||
include {{ NGINX_PREFIX }}x-frame-options.conf;
|
||||
{% endif %}
|
||||
|
||||
# X-XSS-Protection header
|
||||
{% if X_XSS_PROTECTION != "" +%}
|
||||
include {{ NGINX_PREFIX }}x-xss-protection.conf;
|
||||
{% endif %}
|
||||
|
||||
# X-Content-Type header
|
||||
{% if X_CONTENT_TYPE_OPTIONS != "" +%}
|
||||
include {{ NGINX_PREFIX }}x-content-type-options.conf;
|
||||
{% endif %}
|
||||
|
||||
# Content-Security-Policy header
|
||||
{% if CONTENT_SECURITY_POLICY != "" +%}
|
||||
include {{ NGINX_PREFIX }}content-security-policy.conf;
|
||||
{% endif %}
|
||||
|
||||
# Referrer-Policy header
|
||||
{% if REFERRER_POLICY != "" +%}
|
||||
include {{ NGINX_PREFIX }}referrer-policy.conf;
|
||||
{% endif %}
|
||||
|
||||
# Feature-Policy header
|
||||
{% if FEATURE_POLICY != "" +%}
|
||||
include {{ NGINX_PREFIX }}feature-policy.conf;
|
||||
{% endif %}
|
||||
|
||||
# Permissions-Policy header
|
||||
{% if PERMISSIONS_POLICY != "" +%}
|
||||
include {{ NGINX_PREFIX }}permissions-policy.conf;
|
||||
{% endif %}
|
||||
|
||||
# cookie flags
|
||||
{% if COOKIE_FLAGS != "" +%}
|
||||
include {{ NGINX_PREFIX }}cookie-flags.conf;
|
||||
{% endif %}
|
||||
|
||||
# custom errors
|
||||
include {{ NGINX_PREFIX }}error.conf;
|
||||
|
||||
# client caching
|
||||
{% if USE_CLIENT_CACHE == "yes" +%}
|
||||
include {{ NGINX_PREFIX }}client-cache.conf;
|
||||
{% endif %}
|
||||
|
||||
# gzip compression
|
||||
{% if USE_GZIP == "yes" +%}
|
||||
include {{ NGINX_PREFIX }}gzip.conf;
|
||||
{% endif %}
|
||||
|
||||
# brotli compression
|
||||
{% if USE_BROTLI == "yes" +%}
|
||||
include {{ NGINX_PREFIX }}brotli.conf;
|
||||
{% endif %}
|
||||
|
||||
# maximum body size
|
||||
client_max_body_size {{ MAX_CLIENT_SIZE }};
|
||||
|
||||
# enable/disable showing version
|
||||
server_tokens {{ SERVER_TOKENS }};
|
||||
|
||||
# open file caching
|
||||
{% if USE_OPEN_FILE_CACHE == "yes" +%}
|
||||
include {{ NGINX_PREFIX }}open-file-cache.conf;
|
||||
{% endif %}
|
||||
|
||||
# proxy caching
|
||||
{% if USE_PROXY_CACHE == "yes" +%}
|
||||
include {{ NGINX_PREFIX }}proxy-cache.conf;
|
||||
{% endif %}
|
||||
|
||||
# authelia
|
||||
{% if USE_AUTHELIA == "yes" +%}
|
||||
include {{ NGINX_PREFIX }}authelia-upstream.conf;
|
||||
include {{ NGINX_PREFIX }}authelia-auth-request.conf;
|
||||
{% endif %}
|
||||
|
||||
# inject into body
|
||||
{% if INJECT_BODY != "" +%}
|
||||
include {{ NGINX_PREFIX }}inject-body.conf;
|
||||
{% endif %}
|
||||
|
||||
# redirect everything to another host
|
||||
{% if REDIRECT_TO != "" +%}
|
||||
include {{ NGINX_PREFIX }}redirect-to.conf;
|
||||
{% endif %}
|
||||
|
||||
# custom headers
|
||||
include {{ NGINX_PREFIX }}custom-headers.conf;
|
||||
|
||||
# reverse proxy
|
||||
{% if USE_REVERSE_PROXY == "yes" +%}
|
||||
include {{ NGINX_PREFIX }}reverse-proxy.conf;
|
||||
{% endif %}
|
||||
|
||||
# remote PHP
|
||||
{% if REMOTE_PHP != "" or LOCAL_PHP != "" +%}
|
||||
include {{ NGINX_PREFIX }}php.conf;
|
||||
{% endif %}
|
||||
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
more_set_headers "X-Content-Type-Options: {{ X_CONTENT_TYPE_OPTIONS }}";
|
||||
@@ -1 +0,0 @@
|
||||
more_set_headers "X-Frame-Options: {{ X_FRAME_OPTIONS }}";
|
||||
@@ -1 +0,0 @@
|
||||
more_set_headers "X-XSS-Protection: {{ X_XSS_PROTECTION }}";
|
||||
71
confs/stream.conf
Normal file
71
confs/stream.conf
Normal file
@@ -0,0 +1,71 @@
|
||||
# /etc/nginx/stream.conf
|
||||
|
||||
# size of the preread buffer
|
||||
preread_buffer_size 16k;
|
||||
|
||||
# timeout of the preread phase
|
||||
preread_timeout 30s;
|
||||
|
||||
# proxy protocol timeout
|
||||
proxy_protocol_timeout 30s;
|
||||
|
||||
# resolvers to use
|
||||
resolver {{ DNS_RESOLVERS }} ipv6=off;
|
||||
|
||||
# resolver timeout
|
||||
resolver_timeout 30s;
|
||||
|
||||
# remove 200ms delay
|
||||
tcp_nodelay on;
|
||||
|
||||
# bucket hash size
|
||||
variables_hash_bucket_size 64;
|
||||
variables_hash_max_size 1024;
|
||||
|
||||
# log format and level
|
||||
log_format proxy '$remote_addr [$time_local] '
|
||||
'$protocol $status $bytes_sent $bytes_received '
|
||||
'$session_time "$upstream_addr" '
|
||||
'"$upstream_bytes_sent" "$upstream_bytes_received" "$upstream_connect_time"';
|
||||
access_log /var/log/nginx/access.log proxy;
|
||||
|
||||
# lua path and dicts
|
||||
lua_package_path "/opt/bunkerweb/lua/?.lua;/opt/bunkerweb/core/?.lua;/opt/bunkerweb/plugins/?.lua;/opt/bunkerweb/deps/lib/lua/?.lua;;";
|
||||
lua_package_cpath "/opt/bunkerweb/deps/lib/?.so;/opt/bunkerweb/deps/lib/lua/?.so;;";
|
||||
lua_ssl_trusted_certificate "/opt/bunkerweb/misc/root-ca.pem";
|
||||
lua_ssl_verify_depth 2;
|
||||
lua_shared_dict datastore 256m;
|
||||
|
||||
# LUA init block
|
||||
include /etc/nginx/init-lua.conf;
|
||||
|
||||
# default server when MULTISITE=yes
|
||||
{% if MULTISITE == "yes" %}include /etc/nginx/multisite-default-server.conf;{% endif +%}
|
||||
|
||||
# server config(s)
|
||||
{% if MULTISITE == "yes" and SERVER_NAME != "" %}
|
||||
{% set map_servers = {} %}
|
||||
{% for server_name in SERVER_NAME.split(" ") %}
|
||||
{% if server_name + "_SERVER_NAME" in all %}
|
||||
{% set x = map_servers.update({server_name : all[server_name + "_SERVER_NAME"].split(" ")}) %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% for server_name in SERVER_NAME.split(" ") %}
|
||||
{% if not server_name in map_servers %}
|
||||
{% set found = {"res": false} %}
|
||||
{% for first_server, servers in map_servers.items() %}
|
||||
{% if server_name in servers %}
|
||||
{% set x = found.update({"res" : true}) %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% if not found["res"] %}
|
||||
{% set x = map_servers.update({server_name : [server_name]}) %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% for first_server in map_servers +%}
|
||||
include /etc/nginx/{{ first_server }}/server.conf;
|
||||
{% endfor %}
|
||||
{% elif MULTISITE == "no" +%}
|
||||
include /etc/nginx/server.conf;
|
||||
{% endif %}
|
||||
17
confs/uwsgi_params
Normal file
17
confs/uwsgi_params
Normal file
@@ -0,0 +1,17 @@
|
||||
|
||||
uwsgi_param QUERY_STRING $query_string;
|
||||
uwsgi_param REQUEST_METHOD $request_method;
|
||||
uwsgi_param CONTENT_TYPE $content_type;
|
||||
uwsgi_param CONTENT_LENGTH $content_length;
|
||||
|
||||
uwsgi_param REQUEST_URI $request_uri;
|
||||
uwsgi_param PATH_INFO $document_uri;
|
||||
uwsgi_param DOCUMENT_ROOT $document_root;
|
||||
uwsgi_param SERVER_PROTOCOL $server_protocol;
|
||||
uwsgi_param REQUEST_SCHEME $scheme;
|
||||
uwsgi_param HTTPS $https if_not_empty;
|
||||
|
||||
uwsgi_param REMOTE_ADDR $remote_addr;
|
||||
uwsgi_param REMOTE_PORT $remote_port;
|
||||
uwsgi_param SERVER_PORT $server_port;
|
||||
uwsgi_param SERVER_NAME $server_name;
|
||||
Reference in New Issue
Block a user