mirror of
https://gitgud.io/fatchan/haproxy-protection.git
synced 2025-05-09 02:05:37 +00:00
Start on localisation ref #22
This commit is contained in:
@ -24,6 +24,7 @@ Originally inspired by a proof of concept from https://github.com/mora9715/hapro
|
|||||||
- Maintenance mode page for selected domains.
|
- Maintenance mode page for selected domains.
|
||||||
- Geoip mapping support for alt-svc headers.
|
- Geoip mapping support for alt-svc headers.
|
||||||
- Support simple load balancing to multiple backends per domain dynamically.
|
- Support simple load balancing to multiple backends per domain dynamically.
|
||||||
|
- Multiple language support with locales files (currently en-US and pt-PT).
|
||||||
- Fix multiple security issues.
|
- Fix multiple security issues.
|
||||||
- Many bugfixes.
|
- Many bugfixes.
|
||||||
|
|
||||||
|
@ -17,6 +17,7 @@ services:
|
|||||||
- ./src/lua/scripts/:/etc/haproxy/scripts/
|
- ./src/lua/scripts/:/etc/haproxy/scripts/
|
||||||
- ./src/lua/libs/:/etc/haproxy/libs/
|
- ./src/lua/libs/:/etc/haproxy/libs/
|
||||||
- ./src/js/:/etc/haproxy/js/
|
- ./src/js/:/etc/haproxy/js/
|
||||||
|
- ./src/locales/:/etc/haproxy/locales/
|
||||||
environment:
|
environment:
|
||||||
# These are the hcaptcha and recaptcha test keys, not leaking any dont worry :^)
|
# These are the hcaptcha and recaptcha test keys, not leaking any dont worry :^)
|
||||||
- HCAPTCHA_SITEKEY=20000000-ffff-ffff-ffff-000000000002
|
- HCAPTCHA_SITEKEY=20000000-ffff-ffff-ffff-000000000002
|
||||||
|
23
src/locales/en-US.json
Normal file
23
src/locales/en-US.json
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"Maintenance Mode": "Maintenance Mode",
|
||||||
|
"Under maintenance. Please try again soon!": "Under maintenance. Please try again soon!",
|
||||||
|
"Hold on...": "Hold on...",
|
||||||
|
"Browser does not support Web Workers.": "Browser does not support Web Workers.",
|
||||||
|
"Browser does not support WebAssembly.": "Browser does not support WebAssembly.",
|
||||||
|
"Server rejected your submission.": "Server rejected your submission.",
|
||||||
|
"Server encountered an error.": "Server encountered an error.",
|
||||||
|
"Failed to send request to server.": "Failed to send request to server.",
|
||||||
|
"Working, ≈%ss remaining": "Working, ≈%ss remaining",
|
||||||
|
"Waiting for captcha.": "Waiting for captcha.",
|
||||||
|
"Submitting...": "Submitting...",
|
||||||
|
"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",
|
||||||
|
"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.",
|
||||||
|
"JavaScript is required on this page.": "JavaScript is required on this page.",
|
||||||
|
"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>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:",
|
||||||
|
"submit": "submit"
|
||||||
|
}
|
23
src/locales/pt-PT.json
Normal file
23
src/locales/pt-PT.json
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"Maintenance Mode": "Modo Manutenção",
|
||||||
|
"Under maintenance. Please try again soon!": "Em manutenção. Tenta outra vez em breve!",
|
||||||
|
"Hold on...": "Aguarda...",
|
||||||
|
"Browser does not support Web Workers.": "Navegador não suporta Web Workers.",
|
||||||
|
"Browser does not support WebAssembly.": "Navegador não suporta WebAssembly.",
|
||||||
|
"Server rejected your submission.": "Servidor rejeitou o teu pedido.",
|
||||||
|
"Server encountered an error.": "Servidor encontrou um erro.",
|
||||||
|
"Failed to send request to server.": "Envio de pedido ao servidor falhou.",
|
||||||
|
"Working, ≈%ss remaining": "A trabalhar, ≈%ss para terminar",
|
||||||
|
"Waiting for captcha.": "À espera do captcha.",
|
||||||
|
"Submitting...": "A enviar...",
|
||||||
|
"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",
|
||||||
|
"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.",
|
||||||
|
"JavaScript is required on this page.": "Javascript é necessário nesta página.",
|
||||||
|
"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>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:",
|
||||||
|
"submit": "enviar"
|
||||||
|
}
|
@ -11,6 +11,19 @@ local cookie = require("cookie")
|
|||||||
local json = require("json")
|
local json = require("json")
|
||||||
local randbytes = require("randbytes")
|
local randbytes = require("randbytes")
|
||||||
local templates = require("templates")
|
local templates = require("templates")
|
||||||
|
local locales_path = "/etc/haproxy/locales/"
|
||||||
|
local locales_table = {}
|
||||||
|
-- local locales_strings = {}
|
||||||
|
for file_name in io.popen('ls "'..locales_path..'"*.json'):lines() do
|
||||||
|
local file_name_with_path = utils.split(file_name, "/")
|
||||||
|
local file_name_without_ext = utils.split(file_name_with_path[#file_name_with_path], ".")[1]
|
||||||
|
local file = io.open(file_name, "r")
|
||||||
|
local json_contents = file:read("*all")
|
||||||
|
local json_object = json.decode(json_contents)
|
||||||
|
file:close()
|
||||||
|
locales_table[file_name_without_ext] = json_object
|
||||||
|
-- locales_strings[file_name_without_ext] = json_contents
|
||||||
|
end
|
||||||
|
|
||||||
-- POW
|
-- POW
|
||||||
local pow_type = os.getenv("POW_TYPE") or "argon2"
|
local pow_type = os.getenv("POW_TYPE") or "argon2"
|
||||||
@ -74,8 +87,31 @@ 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
|
||||||
|
local default_lang = "en-US"
|
||||||
|
function _M.get_first_language(applet)
|
||||||
|
local accept_language = applet.headers["accept-language"] or {}
|
||||||
|
accept_language = accept_language[0] or ""
|
||||||
|
if #accept_language > 0 and #accept_language < 100 then -- length limit preventing abuse
|
||||||
|
for lang in accept_language:gmatch("[^,%s]+") do
|
||||||
|
if not lang:find(";") then
|
||||||
|
return lang
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
function _M.view(applet)
|
function _M.view(applet)
|
||||||
|
|
||||||
|
-- set the ll language var based off header or default to en-US
|
||||||
|
local lang = _M.get_first_language(applet)
|
||||||
|
local ll = locales_table[lang]
|
||||||
|
if ll == nil then
|
||||||
|
ll = locales_table[default_lang]
|
||||||
|
lang = default_lang
|
||||||
|
end
|
||||||
|
|
||||||
-- set response body and declare status code
|
-- set response body and declare status code
|
||||||
local response_body = ""
|
local response_body = ""
|
||||||
local response_status_code
|
local response_status_code
|
||||||
@ -120,27 +156,62 @@ function _M.view(applet)
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- pow at least is always enabled when reaching bot-check page
|
-- pow at least is always enabled when reaching bot-check page
|
||||||
site_name_body = string.format(templates.site_name_section, host)
|
site_name_body = string.format(
|
||||||
|
templates.site_name_section,
|
||||||
|
string.format(ll["Verifying your connection to %s"], host)
|
||||||
|
)
|
||||||
if captcha_enabled then
|
if captcha_enabled then
|
||||||
captcha_body = string.format(templates.captcha_section, captcha_classname,
|
captcha_body = string.format(
|
||||||
captcha_sitekey, captcha_script_src)
|
templates.captcha_section,
|
||||||
|
ll["Please solve the captcha to continue."],
|
||||||
|
captcha_classname,
|
||||||
|
captcha_sitekey,
|
||||||
|
captcha_script_src
|
||||||
|
)
|
||||||
else
|
else
|
||||||
pow_body = templates.pow_section
|
pow_body = string.format(
|
||||||
|
templates.pow_section,
|
||||||
|
ll["This process is automatic, please wait a moment..."]
|
||||||
|
)
|
||||||
local noscript_extra
|
local noscript_extra
|
||||||
if pow_type == "argon2" then
|
if pow_type == "argon2" then
|
||||||
noscript_extra = templates.noscript_extra_argon2
|
noscript_extra = templates.noscript_extra_argon2
|
||||||
else
|
else
|
||||||
noscript_extra = templates.noscript_extra_sha256
|
noscript_extra = templates.noscript_extra_sha256
|
||||||
end
|
end
|
||||||
noscript_extra_body = string.format(noscript_extra, user_key,
|
noscript_extra_body = string.format(
|
||||||
challenge_hash, expiry, signature, math.ceil(pow_difficulty/8),
|
noscript_extra,
|
||||||
argon_time, argon_kb)
|
ll["No JavaScript?"],
|
||||||
|
ll["Run this in a linux terminal (requires <code>perl</code>):"],
|
||||||
|
user_key,
|
||||||
|
challenge_hash,
|
||||||
|
expiry,
|
||||||
|
signature,
|
||||||
|
math.ceil(pow_difficulty/8),
|
||||||
|
argon_time,
|
||||||
|
argon_kb,
|
||||||
|
ll["Paste the script output into the box and submit:"]
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- sub in the body sections
|
-- sub in the body sections
|
||||||
response_body = string.format(templates.body, combined_challenge,
|
response_body = string.format(
|
||||||
pow_difficulty, argon_time, argon_kb, pow_type,
|
templates.body,
|
||||||
site_name_body, pow_body, captcha_body, noscript_extra_body, ray_id)
|
lang,
|
||||||
|
ll["Hold on..."],
|
||||||
|
combined_challenge,
|
||||||
|
pow_difficulty,
|
||||||
|
argon_time,
|
||||||
|
argon_kb,
|
||||||
|
pow_type,
|
||||||
|
site_name_body,
|
||||||
|
pow_body,
|
||||||
|
captcha_body,
|
||||||
|
ll["JavaScript is required on this page."],
|
||||||
|
noscript_extra_body,
|
||||||
|
ray_id,
|
||||||
|
ll["Performance & security by BasedFlare"]
|
||||||
|
)
|
||||||
response_status_code = 403
|
response_status_code = 403
|
||||||
|
|
||||||
-- if request is POST, check the answer to the pow/cookie
|
-- if request is POST, check the answer to the pow/cookie
|
||||||
|
@ -4,9 +4,9 @@ local _M = {}
|
|||||||
_M.body = [[
|
_M.body = [[
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head lang="%s">
|
||||||
<meta name='viewport' content='width=device-width initial-scale=1'>
|
<meta name='viewport' content='width=device-width initial-scale=1'>
|
||||||
<title>Hold on...</title>
|
<title>%s</title>
|
||||||
<style>
|
<style>
|
||||||
: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}}
|
||||||
@ -46,13 +46,13 @@ details[open]{border-left-color: #1400ff}
|
|||||||
%s
|
%s
|
||||||
<noscript>
|
<noscript>
|
||||||
<br>
|
<br>
|
||||||
<p class="red left">JavaScript is required on this page.</p>
|
<p class="red left">%s</p>
|
||||||
%s
|
%s
|
||||||
</noscript>
|
</noscript>
|
||||||
<div class="powstatus"></div>
|
<div class="powstatus"></div>
|
||||||
<footer>
|
<footer>
|
||||||
<p>Node: <code>%s</code></p>
|
<p>Node: <code>%s</code></p>
|
||||||
<p>Performance & security by <a href="https://basedflare.com" rel="noreferrer noopener" target="_blank">BasedFlare</a></p>
|
<p>%s</p>
|
||||||
</footer>
|
</footer>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
@ -60,16 +60,16 @@ details[open]{border-left-color: #1400ff}
|
|||||||
|
|
||||||
_M.noscript_extra_argon2 = [[
|
_M.noscript_extra_argon2 = [[
|
||||||
<details>
|
<details>
|
||||||
<summary>No JavaScript?</summary>
|
<summary>%s</summary>
|
||||||
<ol>
|
<ol>
|
||||||
<li>
|
<li>
|
||||||
<p>Run this in a linux terminal (requires <code>argon2</code> package installed):</p>
|
<p>%s</p>
|
||||||
<code style="word-break: break-all;">
|
<code style="word-break: break-all;">
|
||||||
echo "Q0g9IiQyIjtCPSQocHJpbnRmIDAlLjBzICQoc2VxIDEgJDUpKTtlY2hvICJXb3JraW5nLi4uIjtJPTA7d2hpbGUgdHJ1ZTsgZG8gSD0kKGVjaG8gLW4gJENIJEkgfCBhcmdvbjIgJDEgLWlkIC10ICQ2IC1rICQ3IC1wIDEgLWwgMzIgLXIpO0U9JHtIOjA6JDV9O1tbICRFID09ICRCIF1dICYmIGVjaG8gIk91dHB1dDoiICYmIGVjaG8gJDEjJDIjJDMjJDQjJEkgJiYgZXhpdCAwOygoSSsrKSk7ZG9uZTsK" | base64 -d | bash -s %s %s %s %s %s %s %s
|
echo "Q0g9IiQyIjtCPSQocHJpbnRmIDAlLjBzICQoc2VxIDEgJDUpKTtlY2hvICJXb3JraW5nLi4uIjtJPTA7d2hpbGUgdHJ1ZTsgZG8gSD0kKGVjaG8gLW4gJENIJEkgfCBhcmdvbjIgJDEgLWlkIC10ICQ2IC1rICQ3IC1wIDEgLWwgMzIgLXIpO0U9JHtIOjA6JDV9O1tbICRFID09ICRCIF1dICYmIGVjaG8gIk91dHB1dDoiICYmIGVjaG8gJDEjJDIjJDMjJDQjJEkgJiYgZXhpdCAwOygoSSsrKSk7ZG9uZTsK" | base64 -d | bash -s %s %s %s %s %s %s %s
|
||||||
</code>
|
</code>
|
||||||
<li>Paste the script output into the box and submit:
|
<li>%s
|
||||||
<form method="post">
|
<form method="post">
|
||||||
<textarea name="pow_response" placeholder="script output" required></textarea>
|
<textarea name="pow_response" required></textarea>
|
||||||
<div><input type="submit" value="submit" /></div>
|
<div><input type="submit" value="submit" /></div>
|
||||||
</form>
|
</form>
|
||||||
</ol>
|
</ol>
|
||||||
@ -78,16 +78,16 @@ _M.noscript_extra_argon2 = [[
|
|||||||
|
|
||||||
_M.noscript_extra_sha256 = [[
|
_M.noscript_extra_sha256 = [[
|
||||||
<details>
|
<details>
|
||||||
<summary>No JavaScript?</summary>
|
<summary>%s</summary>
|
||||||
<ol>
|
<ol>
|
||||||
<li>
|
<li>
|
||||||
<p>Run this in a linux terminal (requires <code>perl</code>):</p>
|
<p>%s</p>
|
||||||
<code style="word-break: break-all;">
|
<code style="word-break: break-all;">
|
||||||
echo "dXNlIHN0cmljdDt1c2UgRGlnZXN0OjpTSEEgcXcoc2hhMjU2X2hleCk7cHJpbnQgIldvcmtpbmcuLi4iO215JGM9IiRBUkdWWzBdIi4iJEFSR1ZbMV0iO215JGlkPSRBUkdWWzRdKzA7bXkkZD0iMCJ4JGlkO215JGk9MDt3aGlsZSgxKXtsYXN0IGlmICRkIGVxIHN1YnN0ciBzaGEyNTZfaGV4KCRjLCRpKSwwLCRpZDskaSsrfXByaW50IlxuT3V0cHV0OlxuJEFSR1ZbMF0jJEFSR1ZbMV0jJEFSR1ZbMl0jJEFSR1ZbM10jJGlcbiI=" | base64 -d | perl -w - %s %s %s %s %s %s %s
|
echo "dXNlIHN0cmljdDt1c2UgRGlnZXN0OjpTSEEgcXcoc2hhMjU2X2hleCk7cHJpbnQgIldvcmtpbmcuLi4iO215JGM9IiRBUkdWWzBdIi4iJEFSR1ZbMV0iO215JGlkPSRBUkdWWzRdKzA7bXkkZD0iMCJ4JGlkO215JGk9MDt3aGlsZSgxKXtsYXN0IGlmICRkIGVxIHN1YnN0ciBzaGEyNTZfaGV4KCRjLCRpKSwwLCRpZDskaSsrfXByaW50IlxuT3V0cHV0OlxuJEFSR1ZbMF0jJEFSR1ZbMV0jJEFSR1ZbMl0jJEFSR1ZbM10jJGlcbiI=" | base64 -d | perl -w - %s %s %s %s %s %s %s
|
||||||
</code>
|
</code>
|
||||||
<li>Paste the script output into the box and submit:
|
<li>%s
|
||||||
<form method="post">
|
<form method="post">
|
||||||
<textarea name="pow_response" placeholder="script output" required></textarea>
|
<textarea name="pow_response" required></textarea>
|
||||||
<div><input type="submit" value="submit" /></div>
|
<div><input type="submit" value="submit" /></div>
|
||||||
</form>
|
</form>
|
||||||
</ol>
|
</ol>
|
||||||
@ -98,14 +98,14 @@ _M.noscript_extra_sha256 = [[
|
|||||||
_M.site_name_section = [[
|
_M.site_name_section = [[
|
||||||
<h3 class="pt">
|
<h3 class="pt">
|
||||||
<img src="/favicon.ico" width="64" height="64" alt=" ">
|
<img src="/favicon.ico" width="64" height="64" alt=" ">
|
||||||
Verifying your connection to %s
|
%s
|
||||||
</h3>
|
</h3>
|
||||||
]]
|
]]
|
||||||
|
|
||||||
-- animation while waiting
|
-- animation while waiting
|
||||||
_M.pow_section = [[
|
_M.pow_section = [[
|
||||||
<span>
|
<span>
|
||||||
This process is automatic, please wait a moment...
|
%s
|
||||||
</span>
|
</span>
|
||||||
<div class="jsonly">
|
<div class="jsonly">
|
||||||
<div id="loader"><div class="b"></div><div class="b"></div><div class="b"></div></div>
|
<div id="loader"><div class="b"></div><div class="b"></div><div class="b"></div></div>
|
||||||
@ -125,7 +125,7 @@ _M.pow_section = [[
|
|||||||
-- message, captcha form and submit button
|
-- message, captcha form and submit button
|
||||||
_M.captcha_section = [[
|
_M.captcha_section = [[
|
||||||
<p>
|
<p>
|
||||||
Please solve the captcha to continue.
|
%s
|
||||||
</p>
|
</p>
|
||||||
<div id="captcha" class="jsonly">
|
<div id="captcha" class="jsonly">
|
||||||
<div class="%s" data-sitekey="%s" data-callback="onCaptchaSubmit"></div>
|
<div class="%s" data-sitekey="%s" data-callback="onCaptchaSubmit"></div>
|
||||||
|
Reference in New Issue
Block a user