mirror of
https://gitgud.io/fatchan/haproxy-protection.git
synced 2025-05-09 02:05:37 +00:00
Add new (optional) auto script to be optionally included in frontend sites, will auto solve POW when low time left
Allow bot-check to return json format to be compatible with that Update challenge script
This commit is contained in:
@ -70,9 +70,10 @@ 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/argon2.min.js status 200 content-type "application/javascript; charset=utf-8" hdr "cache-control" "public, max-age=300" if { path /.basedflare/js/argon2.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=3600" if { path /.basedflare/js/auto.min.js }
|
||||||
http-request return file /etc/haproxy/js/challenge.min.js status 200 content-type "application/javascript; charset=utf-8" hdr "cache-control" "public, max-age=300" if { path /.basedflare/js/challenge.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=3600" if { path /.basedflare/js/argon2.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=300" if { path /.basedflare/js/worker.min.js }
|
http-request return file /etc/haproxy/js/challenge.min.js status 200 content-type "application/javascript; charset=utf-8" hdr "cache-control" "public, max-age=3600" 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=3600" 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
|
||||||
|
98
src/js/auto.js
Normal file
98
src/js/auto.js
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
(() => {
|
||||||
|
const doBotCheck = async () => {
|
||||||
|
try {
|
||||||
|
const json = await fetch("/.basedflare/bot-check", { headers: { "accept": "application/json" }})
|
||||||
|
.then(res => res.json());
|
||||||
|
if (json && json.ch) {
|
||||||
|
if (json.ca) {
|
||||||
|
// TODO: captcha popup
|
||||||
|
} else {
|
||||||
|
const [ userkey, challenge, _expiry, _signature ] = json.ch.split("#");
|
||||||
|
const [ mode, diff, argon_time, argon_kb ] = json.pow.split("#");
|
||||||
|
if (mode === "argon2") {
|
||||||
|
if (!argon2) {
|
||||||
|
await new Promise((res) => {
|
||||||
|
const script = document.createElement("script");
|
||||||
|
script.onload = () => res();
|
||||||
|
script.src = something;
|
||||||
|
document.head.appendChild(script);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log(json)
|
||||||
|
const diffString = "0".repeat(diff);
|
||||||
|
const cpuThreads = window.navigator.hardwareConcurrency;
|
||||||
|
const isTor = location.hostname.endsWith(".onion");
|
||||||
|
const workerThreads = (isTor || cpuThreads === 2) ? cpuThreads : Math.max(Math.ceil(cpuThreads / 2), cpuThreads - 1);
|
||||||
|
const workers = [];
|
||||||
|
let finished = false;
|
||||||
|
const messageHandler = (e) => {
|
||||||
|
if (e.data.length === 1) {
|
||||||
|
return console.log(e.data[0]);
|
||||||
|
}
|
||||||
|
if (finished) return;
|
||||||
|
finished = true;
|
||||||
|
workers.forEach((w) => w.terminate());
|
||||||
|
const [_workerId, answer] = e.data;
|
||||||
|
fetch("/.basedflare/bot-check", {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/x-www-form-urlencoded",
|
||||||
|
},
|
||||||
|
body: new URLSearchParams({
|
||||||
|
"pow_response": `${json.ch}#${answer}`,
|
||||||
|
}),
|
||||||
|
redirect: "manual",
|
||||||
|
}).then((res) => {
|
||||||
|
if (res.status >= 400) {
|
||||||
|
console.error("basedflare post status >= 400", res);
|
||||||
|
}
|
||||||
|
}).catch((e) => {
|
||||||
|
console.error(e)
|
||||||
|
});
|
||||||
|
};
|
||||||
|
for (let i = 0; i < workerThreads; i++) {
|
||||||
|
const powWorker = new Worker("/.basedflare/js/worker.min.js");
|
||||||
|
powWorker.onmessage = messageHandler;
|
||||||
|
workers.push(powWorker);
|
||||||
|
powWorker.postMessage([
|
||||||
|
userkey,
|
||||||
|
challenge,
|
||||||
|
diff,
|
||||||
|
diffString,
|
||||||
|
{
|
||||||
|
time: argon_time,
|
||||||
|
mem: argon_kb,
|
||||||
|
hashLen: 32,
|
||||||
|
parallelism: 1,
|
||||||
|
type: argon2 ? argon2.ArgonType.Argon2id : null,
|
||||||
|
mode: mode,
|
||||||
|
},
|
||||||
|
i,
|
||||||
|
workerThreads,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch(e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const cookieMinLife = 600;
|
||||||
|
const checkCookie = () => {
|
||||||
|
const powCookie = document.cookie
|
||||||
|
.split("; ")
|
||||||
|
.find((row) => row.startsWith("_basedflare_pow="));
|
||||||
|
if (powCookie) {
|
||||||
|
powCookieValue = powCookie.split("=")[1];
|
||||||
|
const expiry = powCookieValue.split("#")[2];
|
||||||
|
const remainingSecs = ((expiry*1000) - Date.now()) / 1000;
|
||||||
|
console.log('Basedflare cookie check, valid for', remainingSecs, 'seconds');
|
||||||
|
if (remainingSecs < cookieMinLife) {
|
||||||
|
return doBotCheck();
|
||||||
|
}
|
||||||
|
setTimeout(checkCookie, Math.floor(((remainingSecs-cookieMinLife-(Math.random()*300))*1000)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
checkCookie();
|
||||||
|
})();
|
1
src/js/auto.min.js
vendored
Normal file
1
src/js/auto.min.js
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
(()=>{const doBotCheck=async()=>{try{const json=await fetch("/.basedflare/bot-check",{headers:{"accept":"application/json"}}).then(res=>res.json());if(json&&json.ch){if(json.ca){}else{const[userkey,challenge,_expiry,_signature]=json.ch.split("#");const[mode,diff,argon_time,argon_kb]=json.pow.split("#");if(mode==="argon2"){if(!argon2){await new Promise((res)=>{const script=document.createElement("script");script.onload=()=>res();script.src=something;document.head.appendChild(script)})}}console.log(json);const diffString="0".repeat(diff);const cpuThreads=window.navigator.hardwareConcurrency;const isTor=location.hostname.endsWith(".onion");const workerThreads=(isTor||cpuThreads===2)?cpuThreads:Math.max(Math.ceil(cpuThreads/2),cpuThreads-1);const workers=[];let finished=false;const messageHandler=(e)=>{if(e.data.length===1){return console.log(e.data[0])}if(finished){return}finished=true;workers.forEach((w)=>w.terminate());const[_workerId,answer]=e.data;fetch("/.basedflare/bot-check",{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({"pow_response":`${json.ch }#${ answer }`}),redirect:"manual"}).then((res)=>{if(res.status>=400){console.error("basedflare post status >= 400",res)}}).catch((e)=>{console.error(e)})};for(let i=0;i<workerThreads;i+=1){const powWorker=new Worker("/.basedflare/js/worker.min.js");powWorker.onmessage=messageHandler;workers.push(powWorker);powWorker.postMessage([userkey,challenge,diff,diffString,{time:argon_time,mem:argon_kb,hashLen:32,parallelism:1,type:argon2?argon2.ArgonType.Argon2id:null,mode:mode},i,workerThreads])}}}}catch(e){console.error(e)}};const cookieMinLife=600;const checkCookie=()=>{const powCookie=document.cookie.split("; ").find((row)=>row.startsWith("_basedflare_pow="));if(powCookie){powCookieValue=powCookie.split("=")[1];const expiry=powCookieValue.split("#")[2];const remainingSecs=((expiry*1000)-Date.now())/1000;console.log('Basedflare cookie check, valid for',remainingSecs,'seconds');if(remainingSecs<cookieMinLife){return doBotCheck()}setTimeout(checkCookie,Math.floor(((remainingSecs-cookieMinLife-(Math.random()*300))*1000)))}}checkCookie()})();
|
@ -11,7 +11,6 @@ function updateElem(selector, text, color) {
|
|||||||
function insertError(str) {
|
function insertError(str) {
|
||||||
const loader = document.querySelector("#loader");
|
const loader = document.querySelector("#loader");
|
||||||
const captcha = document.querySelector("#captcha");
|
const captcha = document.querySelector("#captcha");
|
||||||
console.log(loader, captcha);
|
|
||||||
(captcha || loader).insertAdjacentHTML(
|
(captcha || loader).insertAdjacentHTML(
|
||||||
"afterend",
|
"afterend",
|
||||||
`<p class="red">Error: ${str}</p>`,
|
`<p class="red">Error: ${str}</p>`,
|
||||||
@ -51,6 +50,25 @@ const wasmSupported = (() => {
|
|||||||
return false;
|
return false;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
// const registerServiceWorker = async () => {
|
||||||
|
// if ("serviceWorker" in navigator) {
|
||||||
|
// try {
|
||||||
|
// const registration = await navigator.serviceWorker.register("/.basedflare/js/serviceworker.min.js", {
|
||||||
|
// scope: "/",
|
||||||
|
// });
|
||||||
|
// if (registration.installing) {
|
||||||
|
// console.log("BasedFlare service worker installing");
|
||||||
|
// } else if (registration.waiting) {
|
||||||
|
// console.log("BasedFlare service worker installed");
|
||||||
|
// } else if (registration.active) {
|
||||||
|
// console.log("BasedFlare service worker active");
|
||||||
|
// }
|
||||||
|
// } catch (error) {
|
||||||
|
// console.error(`BasedFlare worker registration failed: ${error}`);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// };
|
||||||
|
|
||||||
function postResponse(powResponse, captchaResponse) {
|
function postResponse(powResponse, captchaResponse) {
|
||||||
const body = {
|
const body = {
|
||||||
"pow_response": powResponse,
|
"pow_response": powResponse,
|
||||||
@ -107,6 +125,7 @@ const powFinished = new Promise((resolve) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
window.addEventListener("DOMContentLoaded", async () => {
|
window.addEventListener("DOMContentLoaded", async () => {
|
||||||
|
// registerServiceWorker();
|
||||||
const {
|
const {
|
||||||
time,
|
time,
|
||||||
kb,
|
kb,
|
||||||
|
2
src/js/challenge.min.js
vendored
2
src/js/challenge.min.js
vendored
File diff suppressed because one or more lines are too long
@ -106,6 +106,19 @@ function _M.view(applet)
|
|||||||
captcha_enabled = true
|
captcha_enabled = true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- return simple json if they send accept: application/json header
|
||||||
|
local accept_header = applet.headers['accept']
|
||||||
|
if accept_header ~= nil and accept_header[0] == 'application/json' then
|
||||||
|
local_pow_combined = string.format('%s#%d#%s#%s', pow_type, math.ceil(pow_difficulty/8), argon_time, argon_kb)
|
||||||
|
response_body = "{\"ch\":\""..combined_challenge.."\",\"ca\":"..(captcha_enabled and "true" or "false")..",\"pow\":\""..local_pow_combined.."\"}"
|
||||||
|
applet:set_status(403)
|
||||||
|
applet:add_header("content-type", "application/json; charset=utf-8")
|
||||||
|
applet:add_header("content-length", string.len(response_body))
|
||||||
|
applet:start_response()
|
||||||
|
applet:send(response_body)
|
||||||
|
return
|
||||||
|
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, host)
|
||||||
if captcha_enabled then
|
if captcha_enabled then
|
||||||
@ -192,7 +205,8 @@ function _M.view(applet)
|
|||||||
applet:add_header(
|
applet:add_header(
|
||||||
"set-cookie",
|
"set-cookie",
|
||||||
string.format(
|
string.format(
|
||||||
"_basedflare_pow=%s; Expires=Thu, 31-Dec-37 23:55:55 GMT; Path=/; Domain=.%s; SameSite=Strict; HttpOnly;%s",
|
--"_basedflare_pow=%s; Expires=Thu, 31-Dec-37 23:55:55 GMT; Path=/; Domain=.%s; SameSite=Strict; HttpOnly;%s",
|
||||||
|
"_basedflare_pow=%s; Expires=Thu, 31-Dec-37 23:55:55 GMT; Path=/; Domain=.%s; SameSite=Strict; %s",
|
||||||
combined_cookie,
|
combined_cookie,
|
||||||
applet.headers['host'][0],
|
applet.headers['host'][0],
|
||||||
secure_cookie_flag
|
secure_cookie_flag
|
||||||
|
Reference in New Issue
Block a user