v.34.1
This commit is contained in:
1 parent
4afa88a65e
commit
2ab44c4c62
49 files changed
+1523
-176
No files matched your search
+35
-31
@@ -99,13 +99,9 @@ impl Settings {
|
||||
"metrics: token environment variable must use A-Z, 0-9, underscore"
|
||||
);
|
||||
}
|
||||
ensure!(
|
||||
!self.enabled
|
||||
|| self.listen.ip().is_loopback()
|
||||
|| self.bearer_token_env.is_some()
|
||||
|| self.allow_trusted_network_without_token,
|
||||
"metrics: a non-loopback bind requires a bearer token or explicit trusted-network token bypass; protect HTTP with a private network/TLS tunnel"
|
||||
);
|
||||
// Runtime checks a generated credential (stored in SQLite) or the
|
||||
// legacy environment variable. Without either, scrapes fail closed.
|
||||
// Non-loopback HTTP still needs a private network/TLS tunnel.
|
||||
Ok(())
|
||||
}
|
||||
pub fn restart_differs(&self, other: &Self) -> bool {
|
||||
@@ -166,32 +162,40 @@ async fn scrape(State(exporter): State<Arc<Exporter>>, request: Request) -> Resp
|
||||
if !allowed {
|
||||
return StatusCode::FORBIDDEN.into_response();
|
||||
}
|
||||
if let Some(expected) = &exporter.token {
|
||||
if settings.allow_trusted_network_without_token {
|
||||
// The actual transport peer already matched an administrator-configured
|
||||
// CIDR above. Forwarded headers never participate in this decision.
|
||||
if !settings.allow_trusted_network_without_token {
|
||||
let values: Vec<_> = request
|
||||
.headers()
|
||||
.get_all(http::header::AUTHORIZATION)
|
||||
.iter()
|
||||
.collect();
|
||||
let provided = if values.len() == 1 {
|
||||
values[0]
|
||||
.to_str()
|
||||
.ok()
|
||||
.and_then(|s| s.strip_prefix("Bearer "))
|
||||
} else {
|
||||
let values: Vec<_> = request
|
||||
.headers()
|
||||
.get_all(http::header::AUTHORIZATION)
|
||||
.iter()
|
||||
.collect();
|
||||
let valid = values.len() == 1
|
||||
&& values[0]
|
||||
.to_str()
|
||||
.ok()
|
||||
.and_then(|v| v.strip_prefix("Bearer "))
|
||||
.is_some_and(|value| crate::auth::equal_secret(value, expected));
|
||||
if !valid {
|
||||
return (
|
||||
StatusCode::UNAUTHORIZED,
|
||||
[
|
||||
("www-authenticate", "Bearer"),
|
||||
("cache-control", "no-store"),
|
||||
],
|
||||
)
|
||||
.into_response();
|
||||
None
|
||||
};
|
||||
let generated = exporter.app.prometheus_token_hash.load_full();
|
||||
let valid = provided.is_some_and(|value| {
|
||||
if let Some(hash) = generated.as_deref() {
|
||||
crate::auth::equal_secret(&crate::auth::secret_hash(value), hash)
|
||||
} else {
|
||||
exporter
|
||||
.token
|
||||
.as_ref()
|
||||
.is_some_and(|token| crate::auth::equal_secret(value, token))
|
||||
}
|
||||
});
|
||||
if !valid {
|
||||
return (
|
||||
StatusCode::UNAUTHORIZED,
|
||||
[
|
||||
("www-authenticate", "Bearer"),
|
||||
("cache-control", "no-store"),
|
||||
],
|
||||
)
|
||||
.into_response();
|
||||
}
|
||||
}
|
||||
let Ok(_permit) = exporter.slots.try_acquire() else {
|
||||
|
||||
@@ -8,7 +8,9 @@ mod tests {
|
||||
settings.validate().unwrap();
|
||||
settings.enabled = true;
|
||||
settings.listen = "0.0.0.0:9191".parse().unwrap();
|
||||
assert!(settings.validate().is_err());
|
||||
// With generated tokens validation permits remote binds; without a
|
||||
// configured credential the exporter itself returns 401, never 200.
|
||||
assert!(settings.validate().is_ok());
|
||||
settings.bearer_token_env = Some("RUST_PROXY_METRICS_TOKEN".into());
|
||||
settings.validate().unwrap();
|
||||
settings.bearer_token_env = None;
|
||||
|
||||
Reference in new issue
Block a user