-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Description
What is the issue?
If you try to use an HTTPRoute RequestRedirect
filter with the ReplacePrefixMatch
operation, the HTTPRoute will be accepted, but any route defined by that HTTPRoute will fail with an HTTP 500 and l5d-proxy-error: unexpected error
. The culprit ultimately seems to be this bit in linkerd/http/route/src/http/filter/redirect.rs in the proxy:
api::path_modifier::Replace::Prefix(prefix) => {
if prefix.starts_with('/') {
return Err(InvalidRequestRedirect::RelativePath);
}
Ok(ModifyPath::ReplacePrefixMatch(prefix))
}
This seems to be saying that the ReplacePrefixMatch
option requires a relative path. However:
linkerd-policy-validator
will not allow a relative path there; it requires any replacement path to be absolute.- To further confuse things, if you use an absolute path, the error is reported as "paths must be absolute"... which it already is.
The right answer would seem to be to allow either a relative path or an absolute path for ReplacePrefixMatch
: they both make sense. At minimum, though, if the proxy will only accept a relative path, then the validator must allow a relative path!
How can it be reproduced?
Get an empty cluster (I was using k3d), then:
kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.3.0/standard-install.yaml
linkerd install --crds | kubectl apply -f -
linkerd install | kubectl apply -f -
linkerd check
kubectl create ns faces
kubectl annotate ns/faces linkerd.io/inject=enabled
helm install \
faces -n faces \
oci://ghcr.io/buoyantio/faces-chart --version 2.0.0 \
--set gui.serviceType=LoadBalancer \
--set smiley2.enabled=true \
--wait
kubectl apply -f - <<EOF
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: tools
namespace: faces
spec:
replicas: 1
selector:
matchLabels:
app: tools
template:
metadata:
labels:
app: tools
spec:
containers:
- name: tools
args:
- -c
- |
sleep 86400
command:
- /bin/sh
image: jonlabelle/network-tools
EOF
kubectl rollout status -n faces deploy
At this point we have a running Faces installation, fully meshed, and
kubectl exec -it -n faces deploy/tools -c tools -- curl -v http://smiley/
will work (it'll return a 200 and a JSON body).
Next up, apply an HTTPRoute with an unconditional redirect using full path replacement:
kubectl apply -f - <<EOF
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: mesh-redirect-path
namespace: faces
spec:
parentRefs:
- group: ""
kind: Service
name: smiley
port: 80
rules:
- filters:
- type: RequestRedirect
requestRedirect:
path:
type: ReplaceFullPath
replaceFullPath: /replace-full-path
statusCode: 301
EOF
Rerun the curl:
kubectl exec -it -n faces deploy/tools -c tools -- curl -v http://smiley/
and you'll get a 301 with location: http://smiley/replace-full-path
. So far so good.
Now update the Route to use ReplacePrefixMatch:
kubectl apply -f - <<EOF
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: mesh-redirect-path
namespace: faces
spec:
parentRefs:
- group: ""
kind: Service
name: smiley
port: 80
rules:
- filters:
- type: RequestRedirect
requestRedirect:
path:
type: ReplacePrefixMatch
replacePrefixMatch: /replace-prefix-match
statusCode: 301
EOF
Rerun the curl:
kubectl exec -it -n faces deploy/tools -c tools -- curl -v http://smiley/
and you'll get a 500 with l5d-proxy-error: unexpected error
... but the proxy logs for the outgoing proxy have more. Buried in the output of kubectl logs -n faces deploy/tools -c linkerd-proxy
you'll find this bit:
[ 424.629421s] WARN ThreadId(01) outbound:proxy{addr=10.43.239.204:80}: linkerd_app_outbound::policy::api: Client policy misconfigured error=invalid HTTP route: invalid filter: invalid HTTP redirect: redirect paths must be absolute
Finally, one last point. We can throw other rules
in there, too: this Route should redirect only when the incoming path starts with /redirect
, and allow other paths through unmodified.
kubectl apply -f - <<EOF
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: mesh-redirect-path
namespace: faces
spec:
parentRefs:
- group: ""
kind: Service
name: smiley
port: 80
rules:
- matches:
- path:
type: PathPrefix
value: /redirect
filters:
- type: RequestRedirect
requestRedirect:
path:
type: ReplacePrefixMatch
replacePrefixMatch: /replace-prefix-match
statusCode: 301
- backendRefs:
- group: ""
kind: Service
name: smiley
port: 80
EOF
but with that in place, everything still fails because of the one broken rule, so both of these will 500:
kubectl exec -it -n faces deploy/tools -c tools -- curl -v http://smiley/
kubectl exec -it -n faces deploy/tools -c tools -- curl -v http://smiley/redirect/
Logs, error output, etc
See above.
output of linkerd check -o short
~/e/gateway-api [☸ k3d-conf] on flynn/linkerd-conformance [$»!?]
:; linkerd check -o short
Status check results are √
Environment
K3d running v1.30.6+k3s1 on MacOS with OrbStack, Linkerd enterprise-2.18 and edge-25.6.1.
Possible solution
No response
Additional context
No response
Would you like to work on fixing this bug?
None