fix: v1.5.5 — 「打开 Gitea」远程地址推断三重保险

根因:gitea_ui() 有个判断 bug——当请求 Host 是 127.0.0.1:8899
(CGI 内部连接面板的本地地址)时,面板把它当作"远程主机名"
去替换 gitea_host 中的 127.0.0.1,结果还是 127.0.0.1:3000。

修复:
1. 修判断 bug:只有推断出的真实主机名不是 loopback 时才替换
   gitea_host 中的 127.0.0.1(本地直连保持原样,不再被错误"替换")
2. gitea_ui 新增 Referer 兜底:即使 CGI 没转发 Host/X-Forwarded-Host
   (fnOS CGI 环境里 $HTTP_HOST 可能为空),也能从浏览器自带的
   Referer 头里推断出真实主机名
3. CGI 代理兜底转发:从 X-Forwarded-Host / HTTP_HOST / Referer
   多源提取真实主机,作为 Host 和 X-Forwarded-Host 转发
4. 调用方传入 X-Forwarded-Host / Host / Referer 三个头

实测 6 种场景(远程 CGI / 远程直连 / 只有 Referer / 本地直连 /
全部为空 / 远程 IP)全部生成正确地址
manifest 1.5.4 → 1.5.5
This commit is contained in:
cbb
2026-10-15 01:23:57 +08:00
parent b4673d05a7
commit 58197fd35b
3 changed files with 39 additions and 13 deletions
+25 -9
View File
@@ -97,17 +97,24 @@ def gitea_host():
return cfg.get("gitea_host") or DEFAULT_GITEA_HOST
def gitea_ui(host_header=""):
"""「打开 Gitea」按钮地址:优先环境变量;否则用设置里的地址,
但把 127.0.0.1/localhost 替换成访问面板的主机名(浏览器打不开本机回环)"""
def gitea_ui(host_header="", referer="", xfwd_host=""):
"""「打开 Gitea」按钮地址:把 127.0.0.1/localhost 替换成真实远程主机名。
推断顺序:X-Forwarded-Host(CGI 转发) > Host > Referer(浏览器自带)。
注意:仅当推断出的真实主机名不是 loopback 时才替换(本地直连保持原样)。"""
if GITEA_URL_ENV:
return GITEA_URL_ENV
base = gitea_host()
real = (xfwd_host or host_header or "").split(":")[0].strip()
if not real and referer:
m = re.match(r'^https?://([^/:/?#]+)', referer)
if m:
real = m.group(1)
if not real or real in ("127.0.0.1", "localhost", "0.0.0.0", "::1"):
return base # 本地/无法推断,保留 gitea_host 原值
try:
p = urlsplit(base)
host = (host_header or "").split(":")[0].strip()
if host and p.hostname in ("127.0.0.1", "localhost", "0.0.0.0", "::1", ""):
return f"{p.scheme}://{host}" + (f":{p.port}" if p.port else "")
if p.hostname in ("127.0.0.1", "localhost", "0.0.0.0", "::1", ""):
return f"{p.scheme}://{real}" + (f":{p.port}" if p.port else "")
except Exception:
pass
return base
@@ -1661,7 +1668,10 @@ class Handler(BaseHTTPRequestHandler):
path = self.path.split("?")[0].rstrip("/") or "/"
if path == "/":
acc = active_account()
body = (PAGE_HOME.replace("__GITEA__", gitea_ui(self.headers.get("Host")))
body = (PAGE_HOME.replace("__GITEA__", gitea_ui(
self.headers.get("Host"),
self.headers.get("Referer"),
self.headers.get("X-Forwarded-Host")))
.replace("__GH__", f"https://github.com/{acc['gh_user']}?tab=repositories" if acc else "")
.replace("__LOGPATH__", str(LOG_FILE)))
self._html(page("控制面板", body, "home"))
@@ -1685,7 +1695,10 @@ class Handler(BaseHTTPRequestHandler):
"account": account_public(acc),
"last_real": last_real_sync()})
elif path == "/api/status":
self._json(global_status())
self._json(global_status(gitea_ui(
self.headers.get("Host"),
self.headers.get("Referer"),
self.headers.get("X-Forwarded-Host"))))
elif path == "/api/settings":
self._json({"gitea_user": cfg["gitea_user"],
"gitea_pass": mask_token(cfg.get("gitea_pass")),
@@ -1700,7 +1713,10 @@ class Handler(BaseHTTPRequestHandler):
"max_retries": max_retries(),
"retry_interval": RETRY_INTERVAL,
"gitea_host": gitea_host(),
"gitea_url": gitea_ui(self.headers.get("Host")),
"gitea_url": gitea_ui(
self.headers.get("Host"),
self.headers.get("Referer"),
self.headers.get("X-Forwarded-Host")),
"active": active_id,
"accounts": [account_public(a) for a in accounts]})
elif path == "/api/log":
+13 -3
View File
@@ -19,9 +19,19 @@ CTYPE="${CONTENT_TYPE:-application/json}"
HDR="$(mktemp)"; BODY="$(mktemp)"; INBODY=""
CURL_ARGS=(-s -m 120 -X "$METHOD" -H "Content-Type: $CTYPE")
# 把真实访问 Host / 协议转发给面板:面板据此把「打开 Gitea」等远程可访问链接
# 的主机名从 127.0.0.1/localhost 替换成用户实际访问的主机名
[ -n "$HTTP_HOST" ] && CURL_ARGS+=(-H "Host: $HTTP_HOST")
# 把真实访问 Host / 协议转发给面板,面板据此把「打开 Gitea」等远程可访问
# 链接的主机名从 127.0.0.1/localhost 替换成用户实际访问的主机名。
# 真实主机从 X-Forwarded-Host / HTTP_HOST / Referer 中兜底提取(fnOS CGI
# 环境里 HTTP_HOST 可能为空)。
REAL_HOST=""
[ -n "$HTTP_X_FORWARDED_HOST" ] && REAL_HOST="$HTTP_X_FORWARDED_HOST"
[ -z "$REAL_HOST" ] && [ -n "$HTTP_HOST" ] && REAL_HOST="$HTTP_HOST"
if [ -z "$REAL_HOST" ] && [ -n "$HTTP_REFERER" ]; then
REAL_HOST=$(printf '%s' "$HTTP_REFERER" | sed -nE 's|^https?://([^/:/?#]+).*|\1|p' | head -1)
fi
if [ -n "$REAL_HOST" ]; then
CURL_ARGS+=(-H "Host: $REAL_HOST" -H "X-Forwarded-Host: $REAL_HOST")
fi
if [ "${HTTPS:-off}" = "on" ]; then
CURL_ARGS+=(-H "X-Forwarded-Proto: https")
else
+1 -1
View File
@@ -1,5 +1,5 @@
appname = GiteaHubSync
version = 1.5.4
version = 1.5.5
display_name = Gitea→GitHub 同步助手
desc = 每次推送到飞牛 Gitea 的代码,10 秒内自动镜像到 GitHub 的 7 个私有仓库。内置控制面板:自动同步开关、立即同步、仓库状态、同步日志,一键打开 Gitea。数据全部保存在 NAS 本地。
platform = all