Parse probe paths as URI (#10934)

Fixes #10877

Linkerd reads the list of container readiness and liveness probes for a pod in order to generate authorizations which allow probes by default.  However, when reading the `path` field of a probe, we interpret this field literally rather than parsing it as a URI.  This means that any non-path parts of the URI (such as URI params) will attempt to match against the path of a probe request, causing these authorizations to fail.

Instead, we parse this field as a URI and only use the path part for path matching.  Invalid URIs are skipped and a warning is logged.

Signed-off-by: Alex Leong <alex@buoyant.io>
This commit is contained in:
Alex Leong 2023-05-23 13:10:00 -07:00 committed by GitHub
parent cc6c0fc4bb
commit 5309789f35
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 33 additions and 2 deletions

View File

@ -1334,6 +1334,7 @@ dependencies = [
"anyhow",
"chrono",
"futures",
"http",
"k8s-gateway-api",
"kubert",
"linkerd-policy-controller-core",

View File

@ -9,6 +9,7 @@ publish = false
ahash = "0.8"
anyhow = "1"
futures = { version = "0.3", default-features = false }
http = "0.2"
k8s-gateway-api = "0.11"
kubert = { version = "0.16", default-features = false, features = ["index"] }
linkerd-policy-controller-core = { path = "../../core" }

View File

@ -74,8 +74,14 @@ fn container_http_probe_paths(
.filter_map(|p| {
let probe = p.http_get.as_ref()?;
let port = get_port(&probe.port, container)?;
let path = probe.path.clone().unwrap_or_else(|| "/".to_string());
Some((port, path))
let path = probe.path.as_deref().unwrap_or("/");
match http::Uri::try_from(path) {
Ok(uri) => Some((port, uri.path().to_string())),
Err(error) => {
tracing::warn!(%error, path, "invalid probe path");
None
}
}
})
}
@ -313,4 +319,27 @@ mod tests {
assert_eq!(paths.len(), 1);
assert_eq!(paths.iter().next().unwrap(), "/");
}
#[test]
fn probe_with_params() {
let probes = pod_http_probes(&k8s::PodSpec {
containers: vec![k8s::Container {
liveness_probe: Some(k8s::Probe {
http_get: Some(k8s::HTTPGetAction {
path: Some("/liveness-container-1?foo=bar".to_string()),
port: k8s::IntOrString::Int(5432),
..Default::default()
}),
..Default::default()
}),
..Default::default()
}],
..Default::default()
});
assert_eq!(probes.len(), 1);
let paths = probes.get(&5432.try_into().unwrap()).unwrap();
assert_eq!(paths.len(), 1);
assert_eq!(paths.iter().next().unwrap(), "/liveness-container-1");
}
}