EndGame/site.conf

338 lines
11 KiB
Plaintext
Raw Permalink Normal View History

2024-07-08 20:09:12 +00:00
#right now there is a lot of logging to error_log so during an attack those logs will fill the disk eventually.
#a good idea would be to use a syslog server and log to a socket instead of a file for IO optimization
#logging could also be disabled in production
#depending on cluster setup some things can be changed here.
#keepalive 128; or proxy_bind on multiple local ips can be used to mitigate local port exhaustion
#most likely with this setup it's not the case
#if this runs on the same machine as the application server UNIX sockets should be used instead of TCP
upstream tor {
server unix:/run/tor_pass1.sock weight=10 fail_timeout=30s;
server unix:/run/tor_pass2.sock weight=10 fail_timeout=30s;
}
access_by_lua_no_postpone on;
lua_package_path "/etc/nginx/resty/?.lua;;";
init_by_lua_block {
allowed_hosts = {
--torconfig "mainonion",
--torconfig "masterbalanceonion",
--i2pconfig "i2paddress"
}
function in_array(tab, val)
for index, value in ipairs(tab) do
if value == val then
return true
end
end
return nil
end
function split(str, sep)
local result = {}
local regex = ("([^%s]+)"):format(sep)
for each in str:gmatch(regex) do
table.insert(result, each)
end
return result
end
local function calc_circuit(proxyheaderip)
if not proxyheaderip then
return
end
local cg = split(proxyheaderip, ":")
local g1 = cg[5]
local g2 = cg[6]
local glen = string.len(g1)
if (glen < 4) then
for i = (4 - glen),1,-1 do
g1 = "0" .. g1
::loop_label_1::
end
end
glen = string.len(g2)
if (glen < 4) then
for i = (4 - glen),1,-1 do
g2 = "0" .. g2
::loop_label_2::
end
end
local d1 = (string.sub(g1,1,1) .. string.sub(g1,2,2))
local d2 = (string.sub(g1,3,3) .. string.sub(g1,4,4))
local d3 = (string.sub(g2,1,1) .. string.sub(g2,2,2))
local d4 = (string.sub(g2,3,3) .. string.sub(g2,4,4))
local circuit_id = ((((bit.lshift(tonumber(d1, 16), 24)) + (bit.lshift(tonumber(d2, 16), 16))) + (bit.lshift(tonumber(d3, 16), 8))) + tonumber(d4, 16))
return circuit_id
end
function kill_circuit(premature, clientip, headerip)
local circuitid = calc_circuit(headerip)
if not circuitid then
return
end
local sockfile = "unix:/etc/tor/c1"
local response = "Closing circuit " .. circuitid .. " "
local sock = ngx.socket.tcp()
sock:settimeout(1000)
local ok, err = sock:connect(sockfile)
if not ok then
ngx.log(ngx.ERR, "failed to connect to tor: " .. err)
return
end
ngx.log(ngx.ERR, "connected to tor")
local bytes, err = sock:send("authenticate \"torauthpassword\"\n")
if not bytes then
ngx.log(ngx.ERR, "failed authenticate to tor: " .. err)
return
end
local data, err, partial = sock:receive()
if not data then
ngx.log(ngx.ERR, "failed receive data from tor: " .. err)
return
end
local response = response .. " " .. data
local bytes, err = sock:send("closecircuit " .. circuitid .. "\n")
if not bytes then
ngx.log(ngx.ERR, "failed send data to tor: " .. err)
return
end
local data, err, partial = sock:receive()
if not data then
ngx.log(ngx.ERR, "failed receive data from tor: " .. err)
return
end
local response = response .. " " .. data
ngx.log(ngx.ERR, response)
sock:close()
return
end
}
#rate limits should be set to the maximum number of resources (css/images/iframes) a page will load. Those should be kept to a minimum for performance reasons
#limiting by proxy_protocol_addr works only if tor is properly passing HiddenServiceExportCircuitID in haproxy form.
#limiting by cookie_<name> works regardless and must be used, otherwise an attacker can solve a captcha by hand and add it to a script/bot to spam
#limiting by X-I2P-DestHash works when using i2p and passing the request to nginx
#torconfiglimit_req_zone $proxy_protocol_addr zone=circuits:50m rate=requestratelimitvaluer/s;
limit_req_zone $cookie_dcap zone=capcookie:50m rate=requestratelimitvaluer/s;
#i2pconfiglimit_req_zone $http_x_i2p_desthash zone=i2pdesthash:50m rate=requestratelimitvaluer/s;
#caching of dynamic static elements (admin controlled only!)
proxy_cache_path /etc/nginx/cache/ levels=1:2 keys_zone=static:60m use_temp_path=off max_size=500m;
#proxy_protocol only makes sense with V3 onions (exportcircuitid) otherwise it will break things.
#kill_circuit can't be used without it
server {
#torconfig listen unix:/var/run/nginx1 proxy_protocol bind;
#i2pconfig listen 127.0.0.1:6969 backlog=65536 reuseport;
#i2pconfig allow 127.0.0.1;
#torconfig allow unix:;
deny all;
proxy_cache_key "$host$request_uri$is_args$args";
proxy_cache_valid 200 1d;
proxy_cache_min_uses 1;
proxy_cache_use_stale error timeout invalid_header http_500 http_502 http_503 http_504;
proxy_ignore_headers X-Accel-Expires Expires Cache-Control;
proxy_set_header Host $host;
proxy_cache_lock on;
proxy_cache_background_update on;
proxy_cache_revalidate on;
proxy_cache_methods GET;
more_clear_headers 'Server:*';
more_clear_headers 'Vary*';
more_clear_headers 'kill*';
#the following is an example of how to cache static content on the front
#this reduces the amount of requests and makes your site appear faster.
# location /favicon.ico {
# limit_except GET {
# deny all;
# }
# proxy_cache static;
# proxy_pass http://tor;
# }
#
# location ~* ^/((images|fonts|css)/)?.*\.(ico|css|jpeg|jpg|png|ttf|webp|pdf)$ {
# limit_except GET {
# deny all;
# }
# proxy_cache static;
# proxy_pass http://tor;
# }
#what do do when rate limit is triggered, blacklist the cookie (if exists) and kill circuit
location @ratelimit {
error_log /var/log/nginx/ratelimit.log;
access_by_lua_block {
local pa = "no_proxy"
if ngx.var.proxy_protocol_addr ~= nil then
pa = ngx.var.proxy_protocol_addr
end
local cook = require "resty.cookie"
local cookie, err = cook:new()
if not cookie then
ngx.log(ngx.ERR, err)
return
end
local field, err = cookie:get("dcap")
if field then
local blocked_cookies = ngx.shared.blocked_cookies
blocked_cookies:set(field, 1, sessionconfigvalue)
end
ngx.log(ngx.ERR, "Rate limited " .. ngx.var.remote_addr .. "|" .. pa)
if pa ~= "no_proxy" then
local ok, err = ngx.timer.at(0, kill_circuit, ngx.var.remote_addr, ngx.var.proxy_protocol_addr)
if not ok then
ngx.log(ngx.ERR, "failed to create timer: ", err)
return
end
end
ngx.exit(444)
}
}
#what do do when waf is triggered, just show the error page and kill circuit for now.
#naxsi seems to kick in before everything else except rate limiter but if it does trash traffic won't make it to the application servers anyway
#doesn't make sense to blacklist cookie as it will annoy users
location /waf {
error_log /var/log/nginx/error.log;
default_type text/html;
content_by_lua_block {
ngx.say("<head><title>Error</title></head>")
ngx.say("<body bgcolor=\"white\">")
ngx.say("<center><h1>Error</h1></center>")
ngx.say("<hr><center><p>Your browser sent a request that this server could not understand.</p></center>")
ngx.say("<center><p>Most likely your input contains invalid characters (\" , `, etc.) that except for passwords should not be used.</p></center>")
ngx.say("<center><p>This may also happen if you are trying to send contact information or external links.</p></center>")
ngx.say("<center><p>Please go back, check your input and try again.</p></center></body>")
proxyip = "no_proxy"
torip = ngx.var.remote_addr
if ngx.var.proxy_protocol_addr ~= nil then
proxyip = ngx.var.proxy_protocol_addr
end
ngx.log(ngx.ERR, "WAF triggered " .. torip .. "|" .. proxyip)
if proxyip ~= "no_proxy" then
local ok, err = ngx.timer.at(0, kill_circuit, torip, proxyip)
if not ok then
ngx.log(ngx.ERR, "failed to create timer: ", err)
return
end
end
}
}
location @502 {
default_type text/html;
content_by_lua_block {
ngx.say("<head><title>502 Timeout</title></head>")
ngx.say("<body bgcolor=\"white\">")
ngx.say("<center><h1>502 Timeout</h1></center>")
ngx.say("<hr><center><p>It seems this endgame front doesn't have a stable connection to the backend right now.</p></center>")
ngx.say("<center><p>To fix it you can try to reload the page. If that doesn't work, and you end back here, try the following:</p></center>")
ngx.say("<center><p>On Tor, if getting a new circuit doesn't work, Try to get a brand new Tor identity. If that doesn't work come back later.</p></center>")
ngx.say("<center><p>On I2P, just try and refresh again and again. If that doesn't work restart I2P and wait a couple minutes before trying again.</p></center></body>")
}
}
location /kill {
access_by_lua_block {
proxyip = "no_proxy"
torip = ngx.var.remote_addr
if ngx.var.proxy_protocol_addr ~= nil then
proxyip = ngx.var.proxy_protocol_addr
end
ngx.log(ngx.ERR, "Kill area visited" .. torip .. "|" .. proxyip)
local cook = require "resty.cookie"
local cookie, err = cook:new()
if not cookie then
ngx.log(ngx.ERR, err)
return
end
local field, err = cookie:get("dcap")
if field then
local blocked_cookies = ngx.shared.blocked_cookies
blocked_cookies:set(field, 1, sessionconfigvalue)
end
if proxyip ~= "no_proxy" then
local ok, err = ngx.timer.at(0, kill_circuit, torip, proxyip)
if not ok then
ngx.log(ngx.ERR, "failed to create timer: ", err)
return
end
end
ngx.exit(444)
}
}
location / {
aio threads;
aio_write on;
#access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
#rate limits per circuit ID (prevents many requests on a single tor circuit)
#torconfig limit_req zone=circuits burst=streamratelimitvalue nodelay;
#torconfig error_page 503 =503 @ratelimit;
#rate limits based on captcha cookie. if an attacker or bot solves the capcha by hand and inputs the cookie in a script
#the cookie will be blacklisted by all fronts (eventually) and subsequent requests dropped.
limit_req zone=capcookie burst=streamratelimitvalue nodelay;
error_page 503 =503 @ratelimit;
#rate limits based on the i2p destination hash (prevents many requests on a single i2p client connection) *DOES NOT KILL CIRCUITS*
#i2pconfig limit_req zone=i2pdesthash burst=streamratelimitvalue nodelay;
#i2pconfig error_page 503 =503 @ratelimit;
error_page 502 =502 @502;
#check if access captcha is solved and other things
access_by_lua_file lua/cap.lua;
SecRulesEnabled;
#LearningMode;
DeniedUrl /waf;
CheckRule "$SQL >= 8" BLOCK;
CheckRule "$RFI >= 8" BLOCK;
CheckRule "$TRAVERSAL >= 4" BLOCK;
CheckRule "$EVADE >= 4" BLOCK;
CheckRule "$XSS >= 8" BLOCK;
include "/etc/nginx/naxsi_whitelist.rules";
proxy_set_header Host $host;
proxy_pass http://backendurl;
header_filter_by_lua_block {
local cookie, err = cook:new()
if not cookie then
ngx.log(ngx.ERR, err)
return
end
if ngx.resp.get_headers()['kill'] ~= nil then
local field, err = cookie:get("dcap")
if field then
local blocked_cookies = ngx.shared.blocked_cookies
blocked_cookies:set(field, 1, sessionconfigvalue)
end
end
}
}
}