Start on localisation ref #22

This commit is contained in:
Thomas Lynch
2023-04-19 23:07:40 +10:00
parent 93c01c05c5
commit 2af32627eb
6 changed files with 144 additions and 25 deletions

View File

@ -24,6 +24,7 @@ Originally inspired by a proof of concept from https://github.com/mora9715/hapro
- Maintenance mode page for selected domains.
- Geoip mapping support for alt-svc headers.
- 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.
- Many bugfixes.

View File

@ -17,6 +17,7 @@ services:
- ./src/lua/scripts/:/etc/haproxy/scripts/
- ./src/lua/libs/:/etc/haproxy/libs/
- ./src/js/:/etc/haproxy/js/
- ./src/locales/:/etc/haproxy/locales/
environment:
# These are the hcaptcha and recaptcha test keys, not leaking any dont worry :^)
- HCAPTCHA_SITEKEY=20000000-ffff-ffff-ffff-000000000002

23
src/locales/en-US.json Normal file
View 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
View 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"
}

View File

@ -11,6 +11,19 @@ local cookie = require("cookie")
local json = require("json")
local randbytes = require("randbytes")
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
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)
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)
-- 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
local response_body = ""
local response_status_code
@ -120,27 +156,62 @@ function _M.view(applet)
end
-- 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
captcha_body = string.format(templates.captcha_section, captcha_classname,
captcha_sitekey, captcha_script_src)
captcha_body = string.format(
templates.captcha_section,
ll["Please solve the captcha to continue."],
captcha_classname,
captcha_sitekey,
captcha_script_src
)
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
if pow_type == "argon2" then
noscript_extra = templates.noscript_extra_argon2
else
noscript_extra = templates.noscript_extra_sha256
end
noscript_extra_body = string.format(noscript_extra, user_key,
challenge_hash, expiry, signature, math.ceil(pow_difficulty/8),
argon_time, argon_kb)
noscript_extra_body = string.format(
noscript_extra,
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
-- sub in the body sections
response_body = string.format(templates.body, combined_challenge,
pow_difficulty, argon_time, argon_kb, pow_type,
site_name_body, pow_body, captcha_body, noscript_extra_body, ray_id)
response_body = string.format(
templates.body,
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
-- if request is POST, check the answer to the pow/cookie

View File

@ -4,9 +4,9 @@ local _M = {}
_M.body = [[
<!DOCTYPE html>
<html>
<head>
<head lang="%s">
<meta name='viewport' content='width=device-width initial-scale=1'>
<title>Hold on...</title>
<title>%s</title>
<style>
:root{--text-color:#c5c8c6;--bg-color:#1d1f21}
@media (prefers-color-scheme:light){:root{--text-color:#333;--bg-color:#fff}}
@ -46,13 +46,13 @@ details[open]{border-left-color: #1400ff}
%s
<noscript>
<br>
<p class="red left">JavaScript is required on this page.</p>
<p class="red left">%s</p>
%s
</noscript>
<div class="powstatus"></div>
<footer>
<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>
</body>
</html>
@ -60,16 +60,16 @@ details[open]{border-left-color: #1400ff}
_M.noscript_extra_argon2 = [[
<details>
<summary>No JavaScript?</summary>
<summary>%s</summary>
<ol>
<li>
<p>Run this in a linux terminal (requires <code>argon2</code> package installed):</p>
<p>%s</p>
<code style="word-break: break-all;">
echo "Q0g9IiQyIjtCPSQocHJpbnRmIDAlLjBzICQoc2VxIDEgJDUpKTtlY2hvICJXb3JraW5nLi4uIjtJPTA7d2hpbGUgdHJ1ZTsgZG8gSD0kKGVjaG8gLW4gJENIJEkgfCBhcmdvbjIgJDEgLWlkIC10ICQ2IC1rICQ3IC1wIDEgLWwgMzIgLXIpO0U9JHtIOjA6JDV9O1tbICRFID09ICRCIF1dICYmIGVjaG8gIk91dHB1dDoiICYmIGVjaG8gJDEjJDIjJDMjJDQjJEkgJiYgZXhpdCAwOygoSSsrKSk7ZG9uZTsK" | base64 -d | bash -s %s %s %s %s %s %s %s
</code>
<li>Paste the script output into the box and submit:
<li>%s
<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>
</form>
</ol>
@ -78,16 +78,16 @@ _M.noscript_extra_argon2 = [[
_M.noscript_extra_sha256 = [[
<details>
<summary>No JavaScript?</summary>
<summary>%s</summary>
<ol>
<li>
<p>Run this in a linux terminal (requires <code>perl</code>):</p>
<p>%s</p>
<code style="word-break: break-all;">
echo "dXNlIHN0cmljdDt1c2UgRGlnZXN0OjpTSEEgcXcoc2hhMjU2X2hleCk7cHJpbnQgIldvcmtpbmcuLi4iO215JGM9IiRBUkdWWzBdIi4iJEFSR1ZbMV0iO215JGlkPSRBUkdWWzRdKzA7bXkkZD0iMCJ4JGlkO215JGk9MDt3aGlsZSgxKXtsYXN0IGlmICRkIGVxIHN1YnN0ciBzaGEyNTZfaGV4KCRjLCRpKSwwLCRpZDskaSsrfXByaW50IlxuT3V0cHV0OlxuJEFSR1ZbMF0jJEFSR1ZbMV0jJEFSR1ZbMl0jJEFSR1ZbM10jJGlcbiI=" | base64 -d | perl -w - %s %s %s %s %s %s %s
</code>
<li>Paste the script output into the box and submit:
<li>%s
<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>
</form>
</ol>
@ -98,14 +98,14 @@ _M.noscript_extra_sha256 = [[
_M.site_name_section = [[
<h3 class="pt">
<img src="/favicon.ico" width="64" height="64" alt=" ">
Verifying your connection to %s
%s
</h3>
]]
-- animation while waiting
_M.pow_section = [[
<span>
This process is automatic, please wait a moment...
%s
</span>
<div class="jsonly">
<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
_M.captcha_section = [[
<p>
Please solve the captcha to continue.
%s
</p>
<div id="captcha" class="jsonly">
<div class="%s" data-sitekey="%s" data-callback="onCaptchaSubmit"></div>