feat(server): 通过响应头主动暴露版本与 git 描述信息
新增 version_headers_middleware,为所有响应附加三个头:
- Server: yggdrasil/<version>(遵循 product/version 习惯)
- X-Yggdrasil-Version: Cargo 版本号
- X-Yggdrasil-Git: git describe(版本+提交数+短hash+脏标记)
数据源复用 build_info::BUILD_INFO,与启动日志 log_build_info() 同源,
零新依赖。
关键设计:中间件挂在最终合并 router 的最外层(Ok(router) 前),因此
/healthz、/uploads/*、被 CSRF 拒(403)/超时/admin_guard 重定向的响应
都会带头,探测价值最大。此前 app_routes 的中间件栈只覆盖 Dioxus 路由,
无法覆盖这些端点。
受 EXPOSE_VERSION_HEADERS 控制,默认 true;设 0/false/no 关闭。
bool 解析沿用 COOKIE_SECURE 的 matches!("1"/"true"/"yes") 约定,
此处取反为"非 false 值即开"使默认行为对应「暴露」。启动时记录一条
info 日志便于确认生效值。
This commit is contained in:
parent
a3ca08f3f6
commit
71f3351c99
@ -107,6 +107,7 @@ Session / security tuning:
|
||||
COOKIE_SECURE=false # set true/1/yes to add Secure flag to session cookie
|
||||
TRUSTED_PROXY_COUNT=0 # number of reverse proxies in front of the app; used to extract real client IP from X-Forwarded-For
|
||||
APP_BASE_URL= # e.g. https://your-domain.example — trusted origin for CSRF checks on write requests; unset falls back to Host header + X-Forwarded-Proto
|
||||
EXPOSE_VERSION_HEADERS=true # 附加 Server / X-Yggdrasil-Version / X-Yggdrasil-Git 响应头;设 0/false/no 关闭(不向外部暴露版本/commit 时)
|
||||
```
|
||||
|
||||
## Architecture: Conditional Compilation
|
||||
|
||||
54
src/main.rs
54
src/main.rs
@ -417,6 +417,18 @@ fn main() {
|
||||
.invalidate_after(std::time::Duration::from_secs(ssr_cache_secs)),
|
||||
);
|
||||
|
||||
// 版本响应头开关:默认开启。设 0/false/no 关闭(注重安全、不想对外暴露版本/commit 时)。
|
||||
// bool 解析约定与 COOKIE_SECURE 一致(matches "1"/"true"/"yes");这里取反为
|
||||
// "非 false 值即开",使默认行为(unwrap_or(true))对应「暴露」。
|
||||
let expose_version_headers = std::env::var("EXPOSE_VERSION_HEADERS")
|
||||
.ok()
|
||||
.map(|v| !matches!(v.as_str(), "0" | "false" | "no"))
|
||||
.unwrap_or(true);
|
||||
tracing::info!(
|
||||
expose_version_headers,
|
||||
"版本响应头开关(Server / X-Yggdrasil-Version / X-Yggdrasil-Git)"
|
||||
);
|
||||
|
||||
// SSR 世代号中间件:把当前全局世代号注入请求扩展,并对 GET 请求的
|
||||
// 响应附加 `X-SSR-Generation` 头。这是为未来 Dioxus 支持自定义 SSR 缓存键
|
||||
// 预留的钩子;目前主要提供可观测性,不会实际失效 SSR 缓存。
|
||||
@ -441,6 +453,40 @@ fn main() {
|
||||
response
|
||||
}
|
||||
|
||||
// 版本头中间件:为所有响应附加 Server / X-Yggdrasil-Version / X-Yggdrasil-Git,
|
||||
// 数据源与启动日志 log_build_info() 同源(crate::build_info::BUILD_INFO)。
|
||||
// 挂在最终合并 router 的最外层(见下方 Ok(router) 前),因此连 /healthz、
|
||||
// /uploads/*、被 CSRF 拒(403)/超时/admin_guard 重定向的响应都会带头,
|
||||
// 探测价值最大。受 EXPOSE_VERSION_HEADERS 控制(默认 true)。
|
||||
async fn version_headers_middleware(
|
||||
req: axum::http::Request<axum::body::Body>,
|
||||
next: axum::middleware::Next,
|
||||
) -> axum::response::Response {
|
||||
let mut response = next.run(req).await;
|
||||
let h = response.headers_mut();
|
||||
// Server 头:产品名/版本,遵循 "Server: product/version" 习惯。
|
||||
h.insert(
|
||||
axum::http::header::SERVER,
|
||||
axum::http::HeaderValue::from_str(&format!(
|
||||
"yggdrasil/{}",
|
||||
crate::build_info::BUILD_INFO.version
|
||||
))
|
||||
.unwrap_or_else(|_| axum::http::HeaderValue::from_static("yggdrasil")),
|
||||
);
|
||||
// X-Yggdrasil-Version:Cargo.toml 版本号。
|
||||
h.insert(
|
||||
axum::http::header::HeaderName::from_static("x-yggdrasil-version"),
|
||||
axum::http::HeaderValue::from_static(crate::build_info::BUILD_INFO.version),
|
||||
);
|
||||
// X-Yggdrasil-Git:git describe(版本+提交数+短hash+脏标记)。
|
||||
h.insert(
|
||||
axum::http::header::HeaderName::from_static("x-yggdrasil-git"),
|
||||
axum::http::HeaderValue::from_str(crate::build_info::BUILD_INFO.git_describe)
|
||||
.unwrap_or_else(|_| axum::http::HeaderValue::from_static("unknown")),
|
||||
);
|
||||
response
|
||||
}
|
||||
|
||||
// 自定义 API 路由:图片上传(大文件,需要更长超时)
|
||||
// CSRF 校验置于最外层,先拦截非法来源再做超时/限体。
|
||||
let upload_route = axum::Router::new()
|
||||
@ -533,6 +579,14 @@ fn main() {
|
||||
.merge(app_routes)
|
||||
.merge(static_routes);
|
||||
|
||||
// 版本头中间件置于最终合并 router 的最外层:所有端点(含 /healthz、/uploads/*、
|
||||
// 被 CSRF 拒/超时/admin_guard 重定向的响应)都会带上版本头。受 EXPOSE_VERSION_HEADERS 控制。
|
||||
let router = if expose_version_headers {
|
||||
router.layer(axum::middleware::from_fn(version_headers_middleware))
|
||||
} else {
|
||||
router
|
||||
};
|
||||
|
||||
Ok(router)
|
||||
});
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user