Bug Description
When configured to use path based routing, the traefik operator will send requests to the backend on the same path that came into the frontend. This requires the backend service being able to reconfigure itself based upon the path prefix that is chosen from by the traefik operator. This is generally possible, however not all backends will be able to necessarily reconfigure the root path without sticking a proxy in the way to rewrite the URL. Instead, the traefik operator should allow for the requires side of the relation to indicate that the path prefix should be stripped or left intact.
This is fairly simply to do, and requires that a middleware option be injected/included into the yaml configuration for the service (relevant documentation). In fact, this should maybe be the default option since backend services may not be aware that they need to handle a forwarded HTTP request to the original path determined by the traefik operator.
To Reproduce
I think it's fairly self-explanatory, but ...
- juju deploy --channel beta traefik
- juju deploy
- juju add-relation traefik
- curl URL provided on the relation
- Observe the traefik logs do not include the X-Forwarded-Prefix headers, which is used then the path prefix is stripped:
2022-04-18T23:04:38.084Z [traefik] time="2022-04-18T23:04:38Z" level=debug msg="vulcand/oxy/roundrobin/rr: Forwarding this request to URL" Request="{\"Method\":\"GET\",\"URL\":{\"Scheme\":\"\",\"Opaque\":\"\",\"User\":null,\"Host\":\"\",\"Path\":\"/openstack-nova/v2.1/os-hypervisors/detail\",\"RawPath\":\"\",\"ForceQuery\":false,\"RawQuery\":\"\",\"Fragment\":\"\",\"RawFragment\":\"\"},\"Proto\":\"HTTP/1.1\",\"ProtoMajor\":1,\"ProtoMinor\":1,\"Header\":{\"Accept\":[\"application/json\"],\"Accept-Encoding\":[\"gzip, deflate\"],\"Connection\":[\"keep-alive\"],\"User-Agent\":[\"python-novaclient\"],\"X-Auth-Token\":[\"gAAAAABiXe6FWsObpnFYyAUq26ZavlDHi5bvGG1I0PFWmL6OkKdHe1HmSsmqIIaYfvQnEOsrtplRVtpOLeQPT8uEobez5VLp5uWu8lxPlncTYTFYIelnuQFUo2H5mFrWgCfqGoIbwByT9QtLCOgcfJnn35QhUT2hbjVU-BRaJgc7flM1XuqzJ9Q\"],\"X-Forwarded-Host\":[\"192.168.8.2\"],\"X-Forwarded-Port\":[\"80\"],\"X-Forwarded-Proto\":[\"http\"],\"X-Forwarded-Server\":[\"traefik-0\"],\"X-Openstack-Nova-Api-Version\":[\"2.1\"],\"X-Real-Ip\":[\"10.1.38.1\"]},\"ContentLength\":0,\"TransferEncoding\":null,\"Host\":\"192.168.8.2\",\"Form\":null,\"PostForm\":null,\"MultipartForm\":null,\"Trailer\":null,\"RemoteAddr\":\"10.1.38.1:3050\",\"RequestURI\":\"/openstack-nova/v2.1/os-hypervisors/detail\",\"TLS\":null}" ForwardURL="http://nova.openstack.svc.cluster.local:8774"
2022-04-18T23:04:40.984Z [traefik] time="2022-04-18T23:04:40Z" level=debug msg="vulcand/oxy/roundrobin/rr: completed ServeHttp on request" Request="{\"Method\":\"GET\",\"URL\":{\"Scheme\":\"\",\"Opaque\":\"\",\"User\":null,\"Host\":\"\",\"Path\":\"/openstack-nova/v2.1/os-hypervisors/detail\",\"RawPath\":\"\",\"ForceQuery\":false,\"RawQuery\":\"\",\"Fragment\":\"\",\"RawFragment\":\"\"},\"Proto\":\"HTTP/1.1\",\"ProtoMajor\":1,\"ProtoMinor\":1,\"Header\":{\"Accept\":[\"application/json\"],\"Accept-Encoding\":[\"gzip, deflate\"],\"Connection\":[\"keep-alive\"],\"User-Agent\":[\"python-novaclient\"],\"X-Auth-Token\":[\"gAAAAABiXe6FWsObpnFYyAUq26ZavlDHi5bvGG1I0PFWmL6OkKdHe1HmSsmqIIaYfvQnEOsrtplRVtpOLeQPT8uEobez5VLp5uWu8lxPlncTYTFYIelnuQFUo2H5mFrWgCfqGoIbwByT9QtLCOgcfJnn35QhUT2hbjVU-BRaJgc7flM1XuqzJ9Q\"],\"X-Forwarded-Host\":[\"192.168.8.2\"],\"X-Forwarded-Port\":[\"80\"],\"X-Forwarded-Proto\":[\"http\"],\"X-Forwarded-Server\":[\"traefik-0\"],\"X-Openstack-Nova-Api-Version\":[\"2.1\"],\"X-Real-Ip\":[\"10.1.38.1\"]},\"ContentLength\":0,\"TransferEncoding\":null,\"Host\":\"192.168.8.2\",\"Form\":null,\"PostForm\":null,\"MultipartForm\":null,\"Trailer\":null,\"RemoteAddr\":\"10.1.38.1:3050\",\"RequestURI\":\"/openstack-nova/v2.1/os-hypervisors/detail\",\"TLS\":null}"
Reconfiguring the yaml to include the necessary stripPrefix middleware, results in the proper request and the backend knowing how to service the path without reconfiguring the backend application:
2022-04-18T23:09:01.542Z [traefik] time="2022-04-18T23:09:01Z" level=debug msg="vulcand/oxy/roundrobin/rr: Forwarding this request to URL" Request="{\"Method\":\"GET\",\"URL\":{\"Scheme\":\"\",\"Opaque\":\"\",\"User\":null,\"Host\":\"\",\"Path\":\"/v2.1/os-hypervisors/detail\",\"RawPath\":\"\",\"ForceQuery\":false,\"RawQuery\":\"\",\"Fragment\":\"\",\"RawFragment\":\"\"},\"Proto\":\"HTTP/1.1\",\"ProtoMajor\":1,\"ProtoMinor\":1,\"Header\":{\"Accept\":[\"application/json\"],\"Accept-Encoding\":[\"gzip, deflate\"],\"Connection\":[\"keep-alive\"],\"User-Agent\":[\"python-novaclient\"],\"X-Auth-Token\":[\"gAAAAABiXe-Nan9s3Mo6BygUvoiauekQFcnWbMBOHGU5aD5XInjpc6zlVCT2MeA6e0ucbTlWFUnXNlTHpo0OkHd3IfHrpwBtNr1-pwGotTi_5Sx0xW_DPPz1MdmC_rXZnOOYvNyVa3quCQar18pyOktTP42QJxP-cM6unim9omPvj3iE1MKIyDE\"],\"X-Forwarded-Host\":[\"192.168.8.2\"],\"X-Forwarded-Port\":[\"80\"],\"X-Forwarded-Prefix\":[\"/openstack-nova\"],\"X-Forwarded-Proto\":[\"http\"],\"X-Forwarded-Server\":[\"traefik-0\"],\"X-Openstack-Nova-Api-Version\":[\"2.1\"],\"X-Real-Ip\":[\"10.1.38.1\"]},\"ContentLength\":0,\"TransferEncoding\":null,\"Host\":\"192.168.8.2\",\"Form\":null,\"PostForm\":null,\"MultipartForm\":null,\"Trailer\":null,\"RemoteAddr\":\"10.1.38.1:7822\",\"RequestURI\":\"/v2.1/os-hypervisors/detail\",\"TLS\":null}" ForwardURL="http://nova.openstack.svc.cluster.local:8774"
2022-04-18T23:09:05.230Z [traefik] time="2022-04-18T23:09:05Z" level=debug msg="vulcand/oxy/roundrobin/rr: completed ServeHttp on request" Request="{\"Method\":\"GET\",\"URL\":{\"Scheme\":\"\",\"Opaque\":\"\",\"User\":null,\"Host\":\"\",\"Path\":\"/v2.1/os-hypervisors/detail\",\"RawPath\":\"\",\"ForceQuery\":false,\"RawQuery\":\"\",\"Fragment\":\"\",\"RawFragment\":\"\"},\"Proto\":\"HTTP/1.1\",\"ProtoMajor\":1,\"ProtoMinor\":1,\"Header\":{\"Accept\":[\"application/json\"],\"Accept-Encoding\":[\"gzip, deflate\"],\"Connection\":[\"keep-alive\"],\"User-Agent\":[\"python-novaclient\"],\"X-Auth-Token\":[\"gAAAAABiXe-Nan9s3Mo6BygUvoiauekQFcnWbMBOHGU5aD5XInjpc6zlVCT2MeA6e0ucbTlWFUnXNlTHpo0OkHd3IfHrpwBtNr1-pwGotTi_5Sx0xW_DPPz1MdmC_rXZnOOYvNyVa3quCQar18pyOktTP42QJxP-cM6unim9omPvj3iE1MKIyDE\"],\"X-Forwarded-Host\":[\"192.168.8.2\"],\"X-Forwarded-Port\":[\"80\"],\"X-Forwarded-Prefix\":[\"/openstack-nova\"],\"X-Forwarded-Proto\":[\"http\"],\"X-Forwarded-Server\":[\"traefik-0\"],\"X-Openstack-Nova-Api-Version\":[\"2.1\"],\"X-Real-Ip\":[\"10.1.38.1\"]},\"ContentLength\":0,\"TransferEncoding\":null,\"Host\":\"192.168.8.2\",\"Form\":null,\"PostForm\":null,\"MultipartForm\":null,\"Trailer\":null,\"RemoteAddr\":\"10.1.38.1:7822\",\"RequestURI\":\"/v2.1/os-hypervisors/detail\",\"TLS\":null}"
Environment
microk8s: v1.23.5
juju: 2.9.28
traefik: beta (rev 22)
Relevant log output
Relevant logs are included in the output from the reproducer. This is a design change request.
Additional context
I believe something along the lines of the following would suffice for the per-app ingress scenario:
Added to the _generate_per_app_config
method after the config dict has been created:
if request.strip_prefix and self._routing_mode == _RoutingMode.path:
traefik_middleware_name = f"juju-{prefix}-stripprefix"
config['http']['routers'][traefik_router_name]['middlewares'] = [traefik_middleware_name]
config['http']['middlewares'] = {
traefik_middleware_name: {
'stripPrefix': {
'prefixes': [f"/{prefix}"],
}
}
}