hueta2
build-and-push / detect (push) Successful in 12s
build-and-push / build (${{ fromJSON(needs.detect.outputs.services) }}) (push) Successful in 25s

This commit is contained in:
frogolonso
2026-08-26 13:06:59 +03:00
parent 2ae70fc165
commit 526374b5a6
4 changed files with 45 additions and 8 deletions
+27 -6
View File
@@ -24,22 +24,40 @@ def safe_path(path: str):
return True
def fwd_headers():
"""PATCH V4 (defense in depth): не пропускаем клиентский X-Internal внутрь —
иначе клиент мог бы попытаться выдать себя за внутренний узел. Также режем
hop-by-hop заголовки, которые ломают проксирование."""
drop = {"x-internal", "host", "content-length", "connection",
"transfer-encoding", "keep-alive", "te", "upgrade"}
return {k: v for k, v in request.headers.items() if k.lower() not in drop}
@app.route('/', defaults={'path': 'index.html'})
@app.route('/<path:path>', methods=["GET", "POST"])
def catch_all(path: str):
if not safe_path(path):
abort(404)
if request.content_length and request.content_length > 1_000_000:
abort(413)
ext = path.split('.')[-1]
if ext == "html":
if path not in ALLOWED_HTML:
abort(404)
qs = request.full_path.split('?')[1] if '?' in request.full_path else ''
resp = r.get(f"http://{content_node}/{path}?{qs}", headers=request.headers,
timeout=10)
try:
resp = r.get(f"http://{content_node}/{path}?{qs}", headers=fwd_headers(),
timeout=10)
except r.RequestException:
abort(502)
return resp.content, resp.status_code, resp.headers.items()
if ext in STATIC_EXT:
resp = r.get(f"http://{static_node}/{path}", stream=True, timeout=5)
headers = [("Cache-Control", "max-age=36000;"), ("Content-Type", resp.headers["Content-Type"])]
try:
resp = r.get(f"http://{static_node}/{path}", stream=True, timeout=5)
except r.RequestException:
abort(502)
ct = resp.headers.get("Content-Type", "application/octet-stream")
headers = [("Cache-Control", "max-age=36000;"), ("Content-Type", ct)]
return resp.content, resp.status_code, headers
if path.startswith("api/"):
action = path[4:].split("/", 1)[0]
@@ -51,8 +69,11 @@ def catch_all(path: str):
# PATCH V3: allow_redirects=False. requests по умолчанию ХОДИТ по Location,
# который отдаёт logic, а Location раньше строился из заголовка Origin =>
# proxy делал запрос во внутреннюю сеть за атакующего (SSRF).
resp = passcall(f"http://{logic_node}/{path[4:]}", json=request.get_json(silent=True),
headers=request.headers, allow_redirects=False, timeout=8)
try:
resp = passcall(f"http://{logic_node}/{path[4:]}", json=request.get_json(silent=True),
headers=fwd_headers(), allow_redirects=False, timeout=8)
except r.RequestException:
abort(502)
return resp.content, resp.status_code, resp.headers.items()
return redirect("/", code=302)