Refactor fetching header for difference between applet and transaction mode.

Improve locale_strings map by re json.encode.
Add method to put translation jsons into txn var and read with a json_query fetch inside template files not served by a lua view e.g. maintenance page
This commit is contained in:
Thomas Lynch
2023-05-21 15:18:32 +10:00
parent c93ca7f16c
commit 14922d7e2f
11 changed files with 127 additions and 91 deletions

View File

@ -76,14 +76,14 @@ frontend http-in
acl ddos_mode_enabled base,map(/etc/haproxy/map/ddos.map) -m bool acl ddos_mode_enabled base,map(/etc/haproxy/map/ddos.map) -m bool
# serve challenge page scripts directly from haproxy # serve challenge page scripts directly from haproxy
http-request return file /etc/haproxy/js/auto.min.js status 200 content-type "application/javascript; charset=utf-8" hdr "cache-control" "public, max-age=86400" if { path /.basedflare/js/auto.min.js } http-request return file /etc/haproxy/js/auto.min.js status 200 content-type "application/javascript; charset=utf-8" hdr "Cache-Control" "public, max-age=86400" if { path /.basedflare/js/auto.min.js }
http-request return file /etc/haproxy/js/argon2.min.js status 200 content-type "application/javascript; charset=utf-8" hdr "cache-control" "public, max-age=86400" if { path /.basedflare/js/argon2.min.js } http-request return file /etc/haproxy/js/argon2.min.js status 200 content-type "application/javascript; charset=utf-8" hdr "Cache-Control" "public, max-age=86400" if { path /.basedflare/js/argon2.min.js }
http-request return file /etc/haproxy/js/challenge.js status 200 content-type "application/javascript; charset=utf-8" hdr "cache-control" "public, max-age=86400" if { path /.basedflare/js/challenge.min.js } http-request return file /etc/haproxy/js/challenge.js status 200 content-type "application/javascript; charset=utf-8" hdr "Cache-Control" "public, max-age=86400" if { path /.basedflare/js/challenge.min.js }
http-request return file /etc/haproxy/js/worker.min.js status 200 content-type "application/javascript; charset=utf-8" hdr "cache-control" "public, max-age=86400" if { path /.basedflare/js/worker.min.js } http-request return file /etc/haproxy/js/worker.min.js status 200 content-type "application/javascript; charset=utf-8" hdr "Cache-Control" "public, max-age=86400" if { path /.basedflare/js/worker.min.js }
# acl for domains in maintenance mode to return maintenance page (after challenge page htp-request return rules, for the footerlogo) # acl for domains in maintenance mode to return maintenance page (after challenge page htp-request return rules, for the footerlogo)
acl maintenance_mode hdr(host),lower,map_str(/etc/haproxy/map/maintenance.map) -m found acl maintenance_mode hdr(host),lower,map_str(/etc/haproxy/map/maintenance.map) -m found
http-request return lf-file /etc/haproxy/template/maintenance.html status 200 content-type "text/html; charset=utf-8" hdr "cache-control" "private, max-age=30" if maintenance_mode use_backend maintenance if maintenance_mode
# rewrite specific domain+path to domain or domain+path # rewrite specific domain+path to domain or domain+path
http-request redirect location https://%[base,map(/etc/haproxy/map/rewrite.map)] code 302 if { base,map(/etc/haproxy/map/rewrite.map) -i -m found } http-request redirect location https://%[base,map(/etc/haproxy/map/rewrite.map)] code 302 if { base,map(/etc/haproxy/map/rewrite.map) -i -m found }
@ -114,18 +114,39 @@ frontend http-in
# simple example cache for files # simple example cache for files
http-request set-var(txn.path) path http-request set-var(txn.path) path
acl can_cache var(txn.path) -i -m end .png .jpg .jpeg .jpe .ico .webmanifest .xml .apng .bmp .webp .pjpeg .jfif .gif .mp4 .webm .mov .mkv .svg .m4a .aac .flac .mp3 .ogg .wav .opus .txt .pdf .sid acl can_cache var(txn.path) -i -m end .png .jpg .jpeg .jpe .ico .webmanifest .xml .apng .bmp .webp .pjpeg .jfif .gif .mp4 .webm .mov .mkv .svg .m4a .aac .flac .mp3 .ogg .wav .opus .txt .pdf .sid
http-request cache-use basic_cache if can_cache
http-response cache-store basic_cache if can_cache
# optional alt-svc header (done after cache so not set in cached responses # optional alt-svc header (done after cache so not set in cached responses
http-response set-header Alt-Svc %[var(txn.xcn),map(/etc/haproxy/map/alt-svc.map)] http-response set-header Alt-Svc %[var(txn.xcn),map(/etc/haproxy/map/alt-svc.map)]
acl c0 res.hdr(Cache-Control,0) -m sub max-age=0
acl c0 res.hdr(Cache-Control,1) -m sub max-age=0
acl c0 res.hdr(Cache-Control,2) -m sub max-age=0
acl cf0 res.fhdr(Cache-Control,0) -m sub max-age=0
acl cf0 res.fhdr(Cache-Control,1) -m sub max-age=0
acl cf0 res.fhdr(Cache-Control,2) -m sub max-age=0
http-response set-header X-c0 true if c0
http-response set-header X-cf0 true if cf0
http-response set-header X-res-hdr0-Cache-Control %[res.hdr(Cache-Control,0)]
http-response set-header X-res-hdr1-Cache-Control %[res.hdr(Cache-Control,1)]
http-response set-header X-res-hdr2-Cache-Control %[res.hdr(Cache-Control,2)]
http-response set-header X-res-fhdr0-Cache-Control %[res.fhdr(Cache-Control,0)]
http-response set-header X-res-fhdr1-Cache-Control %[res.fhdr(Cache-Control,1)]
http-response set-header X-res-fhdr2-Cache-Control %[res.fhdr(Cache-Control,2)]
http-request cache-use basic_cache
http-response cache-store basic_cache
default_backend servers default_backend servers
cache basic_cache cache basic_cache
total-max-size 2500 total-max-size 250
max-object-size 31457280 max-object-size 31457280
max-age 86400 max-age 86400
process-vary on
backend maintenance
http-request lua.set-lang-json
http-request return lf-file /etc/haproxy/template/maintenance.html status 200 content-type "text/html; charset=utf-8" hdr "Cache-Control" "private, max-age=30"
backend servers backend servers
balance leastconn balance leastconn

View File

@ -1,2 +1,2 @@
127.0.0.1 127.0.0.1:81 127.0.0.1 127.0.0.1:81
localhost 127.0.0.1:81 localhost 127.0.0.1:8200

View File

@ -0,0 +1 @@
localhost admin

View File

@ -2,7 +2,7 @@
<html> <html>
<head> <head>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<title>Maintenance Mode</title> <title>%[var(txn.lang_json),json_query($.Maintenance Mode)]</title>
<style type="text/css"> <style type="text/css">
:root{--text-color:#c5c8c6;--bg-color:#1d1f21} :root{--text-color:#c5c8c6;--bg-color:#1d1f21}
@media (prefers-color-scheme:light){:root{--text-color:#333;--bg-color:#fff}} @media (prefers-color-scheme:light){:root{--text-color:#333;--bg-color:#fff}}
@ -18,11 +18,11 @@ footer{font-size:x-small;margin-top:auto;padding:10px;text-align:center;border-t
<body> <body>
<h3 class="pt"> <h3 class="pt">
<img src="/.basedflare/pow-icon" width="64" height="64" alt=" "> <img src="/.basedflare/pow-icon" width="64" height="64" alt=" ">
Under maintenance. Please try again soon! %[var(txn.lang_json),json_query($.Under maintenance\. Please try again soon!)]
</h3> </h3>
<footer> <footer>
<p>Node: <code>%[env(RAY_ID)]</code></p> <p>Node: <code>%[env(RAY_ID)]</code></p>
<p>Performance & security by <a href="https://basedflare.com" rel="noreferrer noopener" target="_blank">BasedFlare</a></p> <p>%[var(txn.lang_json),json_query($.Performance & security by BasedFlare)]</p>
</footer> </footer>
</body> </body>
</html> </html>

View File

@ -1,23 +1,23 @@
{ {
"Maintenance Mode": "Maintenance Mode", "Maintenance Mode": "Maintenance Mode",
"Under maintenance. Please try again soon!": "Under maintenance. Please try again soon!", "Under maintenance. Please try again soon!": "Under maintenance. Please try again soon!",
"Hold on...": "Hold on...", "Hold on...": "Hold on...",
"Browser does not support Web Workers.": "Browser does not support Web Workers.", "Browser does not support Web Workers.": "Browser does not support Web Workers.",
"Browser does not support WebAssembly.": "Browser does not support WebAssembly.", "Browser does not support WebAssembly.": "Browser does not support WebAssembly.",
"Server rejected your submission.": "Server rejected your submission.", "Server rejected your submission.": "Server rejected your submission.",
"Server encountered an error.": "Server encountered an error.", "Server encountered an error.": "Server encountered an error.",
"Failed to send request to server.": "Failed to send request to server.", "Failed to send request to server.": "Failed to send request to server.",
"Working, ≈%ss remaining": "Working, ≈%ss remaining", "Working, ≈%ss remaining": "Working, ≈%ss remaining",
"Waiting for captcha.": "Waiting for captcha.", "Waiting for captcha.": "Waiting for captcha.",
"Submitting...": "Submitting...", "Submitting...": "Submitting...",
"Performance & security by BasedFlare": "Performance & security by <a href=\"https://basedflare.com\" rel=\"noreferrer noopener\" target=\"_blank\">BasedFlare</a>", "Performance & security by BasedFlare": "Performance & security by <a href=\"https://basedflare.com\" rel=\"noreferrer noopener\" target=\"_blank\">BasedFlare</a>",
"Verifying your connection to %s": "Verifying your connection to %s", "Verifying your connection to %s": "Verifying your connection to %s",
"This process is automatic, please wait a moment...": "This process is automatic, please wait a moment...", "This process is automatic, please wait a moment...": "This process is automatic, please wait a moment...",
"Please solve the captcha to continue.": "Please solve the captcha to continue.", "Please solve the captcha to continue.": "Please solve the captcha to continue.",
"JavaScript is required on this page.": "JavaScript is required on this page.", "JavaScript is required on this page.": "JavaScript is required on this page.",
"No JavaScript?": "No JavaScript?", "No JavaScript?": "No JavaScript?",
"Run this in a linux terminal (requires <code>argon2</code> package installed):": "Run this in a linux terminal (requires <code>argon2</code> package installed):", "Run this in a linux terminal (requires <code>argon2</code> package installed):": "Run this in a linux terminal (requires <code>argon2</code> package installed):",
"Run this in a linux terminal (requires <code>perl</code>):": "Run this in a linux terminal (requires <code>perl</code>):", "Run this in a linux terminal (requires <code>perl</code>):": "Run this in a linux terminal (requires <code>perl</code>):",
"Paste the script output into the box and submit:": "Paste the script output into the box and submit:", "Paste the script output into the box and submit:": "Paste the script output into the box and submit:",
"submit": "submit" "submit": "submit"
} }

View File

@ -1,23 +1,23 @@
{ {
"Maintenance Mode": "Modo Manutenção", "Maintenance Mode": "Modo Manutenção",
"Under maintenance. Please try again soon!": "Em manutenção. Tenta outra vez em breve!", "Under maintenance. Please try again soon!": "Em manutenção. Tenta outra vez em breve!",
"Hold on...": "Aguarda...", "Hold on...": "Aguarda...",
"Browser does not support Web Workers.": "Navegador não suporta Web Workers.", "Browser does not support Web Workers.": "Navegador não suporta Web Workers.",
"Browser does not support WebAssembly.": "Navegador não suporta WebAssembly.", "Browser does not support WebAssembly.": "Navegador não suporta WebAssembly.",
"Server rejected your submission.": "Servidor rejeitou o teu pedido.", "Server rejected your submission.": "Servidor rejeitou o teu pedido.",
"Server encountered an error.": "Servidor encontrou um erro.", "Server encountered an error.": "Servidor encontrou um erro.",
"Failed to send request to server.": "Envio de pedido ao servidor falhou.", "Failed to send request to server.": "Envio de pedido ao servidor falhou.",
"Working, ≈%ss remaining": "A trabalhar, ≈%ss para terminar", "Working, ≈%ss remaining": "A trabalhar, ≈%ss para terminar",
"Waiting for captcha.": "À espera do captcha.", "Waiting for captcha.": "À espera do captcha.",
"Submitting...": "A enviar...", "Submitting...": "A enviar...",
"Performance & security by BasedFlare": "Performance & segurança por <a href=\"https://basedflare.com\" rel=\"noreferrer noopener\" target=\"_blank\">BasedFlare</a>", "Performance & security by BasedFlare": "Performance & segurança por <a href=\"https://basedflare.com\" rel=\"noreferrer noopener\" target=\"_blank\">BasedFlare</a>",
"Verifying your connection to %s": "A verificar a tua ligação a %s", "Verifying your connection to %s": "A verificar a tua ligação a %s",
"This process is automatic, please wait a moment...": "Este processo é automático, por favor espera um momento...", "This process is automatic, please wait a moment...": "Este processo é automático, por favor espera um momento...",
"Please solve the captcha to continue.": "Por favor resolve a captcha para continuar.", "Please solve the captcha to continue.": "Por favor resolve a captcha para continuar.",
"JavaScript is required on this page.": "Javascript é necessário nesta página.", "JavaScript is required on this page.": "Javascript é necessário nesta página.",
"No JavaScript?": "Sem Javascript?", "No JavaScript?": "Sem Javascript?",
"Run this in a linux terminal (requires <code>argon2</code> package installed):": "Corre isto num terminal linux (requer package <code>argon2</code> instalada):", "Run this in a linux terminal (requires <code>argon2</code> package installed):": "Corre isto num terminal linux (requer package <code>argon2</code> instalada):",
"Run this in a linux terminal (requires <code>perl</code>):": "Corre isto num terminal linux (requer <code>perl</code>):", "Run this in a linux terminal (requires <code>perl</code>):": "Corre isto num terminal linux (requer <code>perl</code>):",
"Paste the script output into the box and submit:": "Cola o output do script na caixa e envia:", "Paste the script output into the box and submit:": "Cola o output do script na caixa e envia:",
"submit": "enviar" "submit": "enviar"
} }

View File

@ -1,23 +1,23 @@
{ {
"Maintenance Mode": "Modo Manutenção", "Maintenance Mode": "Modo Manutenção",
"Under maintenance. Please try again soon!": "Em manutenção. Tenta outra vez em breve!", "Under maintenance. Please try again soon!": "Em manutenção. Tenta outra vez em breve!",
"Hold on...": "Aguarda...", "Hold on...": "Aguarda...",
"Browser does not support Web Workers.": "Navegador não suporta Web Workers.", "Browser does not support Web Workers.": "Navegador não suporta Web Workers.",
"Browser does not support WebAssembly.": "Navegador não suporta WebAssembly.", "Browser does not support WebAssembly.": "Navegador não suporta WebAssembly.",
"Server rejected your submission.": "Servidor rejeitou o teu pedido.", "Server rejected your submission.": "Servidor rejeitou o teu pedido.",
"Server encountered an error.": "Servidor encontrou um erro.", "Server encountered an error.": "Servidor encontrou um erro.",
"Failed to send request to server.": "Envio de pedido ao servidor falhou.", "Failed to send request to server.": "Envio de pedido ao servidor falhou.",
"Working, ≈%ss remaining": "A trabalhar, ≈%ss para terminar", "Working, ≈%ss remaining": "A trabalhar, ≈%ss para terminar",
"Waiting for captcha.": "À espera do captcha.", "Waiting for captcha.": "À espera do captcha.",
"Submitting...": "A enviar...", "Submitting...": "A enviar...",
"Performance & security by BasedFlare": "Performance & segurança por <a href=\"https://basedflare.com\" rel=\"noreferrer noopener\" target=\"_blank\">BasedFlare</a>", "Performance & security by BasedFlare": "Performance & segurança por <a href=\"https://basedflare.com\" rel=\"noreferrer noopener\" target=\"_blank\">BasedFlare</a>",
"Verifying your connection to %s": "A verificar a tua ligação a %s", "Verifying your connection to %s": "A verificar a tua ligação a %s",
"This process is automatic, please wait a moment...": "Este processo é automático, por favor espera um momento...", "This process is automatic, please wait a moment...": "Este processo é automático, por favor espera um momento...",
"Please solve the captcha to continue.": "Por favor resolve a captcha para continuar.", "Please solve the captcha to continue.": "Por favor resolve a captcha para continuar.",
"JavaScript is required on this page.": "Javascript é necessário nesta página.", "JavaScript is required on this page.": "Javascript é necessário nesta página.",
"No JavaScript?": "Sem Javascript?", "No JavaScript?": "Sem Javascript?",
"Run this in a linux terminal (requires <code>argon2</code> package installed):": "Corre isto num terminal linux (requer package <code>argon2</code> instalada):", "Run this in a linux terminal (requires <code>argon2</code> package installed):": "Corre isto num terminal linux (requer package <code>argon2</code> instalada):",
"Run this in a linux terminal (requires <code>perl</code>):": "Corre isto num terminal linux (requer <code>perl</code>):", "Run this in a linux terminal (requires <code>perl</code>):": "Corre isto num terminal linux (requer <code>perl</code>):",
"Paste the script output into the box and submit:": "Cola o output do script na caixa e envia:", "Paste the script output into the box and submit:": "Cola o output do script na caixa e envia:",
"submit": "enviar" "submit": "enviar"
} }

View File

@ -5,6 +5,18 @@ local challenge_expiry = tonumber(os.getenv("CHALLENGE_EXPIRY"))
local challenge_includes_ip = os.getenv("CHALLENGE_INCLUDES_IP") local challenge_includes_ip = os.getenv("CHALLENGE_INCLUDES_IP")
local tor_control_port_password = os.getenv("TOR_CONTROL_PORT_PASSWORD") local tor_control_port_password = os.getenv("TOR_CONTROL_PORT_PASSWORD")
-- get header from different place depending on action vs view
function _M.get_header_from_context(context, header_name, is_applet)
local header_content = ""
if is_applet == true then
header_content = context.headers[header_name] or {}
header_content = header_content[0] or ""
else
header_content = context.sf:req_fhdr(header_name) or ""
end
return header_content
end
-- generate the challenge hash/user hash -- generate the challenge hash/user hash
function _M.generate_challenge(context, salt, user_key, is_applet) function _M.generate_challenge(context, salt, user_key, is_applet)
@ -15,14 +27,7 @@ function _M.generate_challenge(context, salt, user_key, is_applet)
end end
-- user agent to counter very dumb spammers -- user agent to counter very dumb spammers
local user_agent = "" local user_agent = _M.get_header_from_context(context, 'user-agent', is_applet)
if is_applet == true then
user_agent = context.headers['user-agent'] or {}
user_agent = user_agent[0] or ""
else
--note req_fhdr not req_hdr otherwise commas in useragent become a delimiter
user_agent = context.sf:req_fhdr('user-agent') or ""
end
local challenge_hash = sha.sha3_256(salt .. ip .. user_key .. user_agent) local challenge_hash = sha.sha3_256(salt .. ip .. user_key .. user_agent)

View File

@ -22,7 +22,7 @@ for file_name in io.popen('ls "'..locales_path..'"*.json'):lines() do
local json_object = json.decode(json_contents) local json_object = json.decode(json_contents)
file:close() file:close()
locales_table[file_name_without_ext] = json_object locales_table[file_name_without_ext] = json_object
locales_strings[file_name_without_ext] = json_contents locales_strings[file_name_without_ext] = json.encode(json_object)
end end
-- POW -- POW
@ -87,11 +87,10 @@ function _M.kill_tor_circuit(txn)
utils.send_tor_control_port(circuit_identifier) utils.send_tor_control_port(circuit_identifier)
end end
-- read first language from accept-language in applet -- read first language from accept-language in applet (note: does not consider q values)
local default_lang = "en-US" local default_lang = "en-US"
function _M.get_first_language(applet) function _M.get_first_language(context, is_applet)
local accept_language = applet.headers["accept-language"] or {} local accept_language = utils.get_header_from_context(context, 'accept-language', is_applet)
accept_language = accept_language[0] or ""
if #accept_language > 0 and #accept_language < 100 then -- length limit preventing abuse if #accept_language > 0 and #accept_language < 100 then -- length limit preventing abuse
for lang in accept_language:gmatch("[^,%s]+") do for lang in accept_language:gmatch("[^,%s]+") do
if not lang:find(";") then if not lang:find(";") then
@ -101,11 +100,10 @@ function _M.get_first_language(applet)
end end
end end
function _M.view(applet) function _M.view(applet)
-- set the ll and ls language var based off header or default to en-US -- set the ll and ls language var based off header or default to en-US
local lang = _M.get_first_language(applet) local lang = _M.get_first_language(applet, true)
local ll = locales_table[lang] local ll = locales_table[lang]
if ll == nil then if ll == nil then
ll = locales_table[default_lang] ll = locales_table[default_lang]
@ -379,6 +377,16 @@ function _M.view(applet)
end end
-- set lang json in var for use with json_query sf for using translations in template files without a lua view
function _M.set_lang_json(txn)
local lang = _M.get_first_language(txn, false)
local ls = locales_strings[lang]
if ls == nil then
ls = locales_strings[default_lang]
end
txn:set_var("txn.lang_json", ls)
end
-- check if captcha is enabled, path+domain priority, then just domain, and 0 otherwise -- check if captcha is enabled, path+domain priority, then just domain, and 0 otherwise
function _M.decide_checks_necessary(txn) function _M.decide_checks_necessary(txn)
local host = txn.sf:hdr("Host") local host = txn.sf:hdr("Host")

View File

@ -7,6 +7,7 @@ core.register_action("captcha-check", { 'http-req', }, bot_check.check_captcha_s
core.register_action("pow-check", { 'http-req', }, bot_check.check_pow_status) core.register_action("pow-check", { 'http-req', }, bot_check.check_pow_status)
core.register_action("decide-checks-necessary", { 'http-req', }, bot_check.decide_checks_necessary) core.register_action("decide-checks-necessary", { 'http-req', }, bot_check.decide_checks_necessary)
core.register_action("kill-tor-circuit", { 'http-req', }, bot_check.kill_tor_circuit) core.register_action("kill-tor-circuit", { 'http-req', }, bot_check.kill_tor_circuit)
core.register_action("set-lang-json", { 'http-req', }, bot_check.set_lang_json)
local backends_map = Map.new('/etc/haproxy/map/backends.map', Map._str) local backends_map = Map.new('/etc/haproxy/map/backends.map', Map._str)
function get_server_names(txn) function get_server_names(txn)

View File

@ -38,7 +38,7 @@ function setup_servers()
if verify_backend_ssl ~= nil then if verify_backend_ssl ~= nil then
tcp:send(string.format("add server %s %s check ssl verify required ca-file ca-certificates.crt sni req.hdr(Host)\n", server_name, backend_host)) tcp:send(string.format("add server %s %s check ssl verify required ca-file ca-certificates.crt sni req.hdr(Host)\n", server_name, backend_host))
else else
tcp:send(string.format("add server %s %s check ssl verify none\n", server_name, backend_host)) tcp:send(string.format("add server %s %s\n", server_name, backend_host))
end; end;
tcp:send(string.format("enable server %s\n", server_name)) tcp:send(string.format("enable server %s\n", server_name))
line = handle:read("*line") line = handle:read("*line")