New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
FR: Support stripping HTTP request paths from funnel proxy routes #6571
Comments
I did take down that |
I guess the alternative is to run a load balancer like Caddy, and have it strip the paths. |
This change trims the mountPoint from the request URL path beforing sending the request to the reverse proxy. Today if you mount a proxy at `/foo` and make a reuqest to `/foo/bar/baz`, we leak the `mountPoint` `/foo` as part of the request URL's path. This fix makes the removed the `mountPoint` prefix from the path so proxied services receive requests as if they were running at the root (`/`) path. This could be an issue if the app generates URLs (in HTML or otherwise) and assumes `/path`. In this case those URLs will 404. With that, I still think we should trim by default and not leak the `mountPoint` (specific to Tailscale) into whatever app is being hosted. If it causes an issue with URL generation I'd suggest look at configuring an app specific path prefix or running Caddy as a more advanced solution. Fixes: #6571 Signed-off-by: Shayne Sweeney <shayne@tailscale.com>
This change trims the mountPoint from the request URL path before sending the request to the reverse proxy. Today if you mount a proxy at `/foo` and request to `/foo/bar/baz`, we leak the `mountPoint` `/foo` as part of the request URL's path. This fix makes removed the `mountPoint` prefix from the path so proxied services receive requests as if they were running at the root (`/`) path. This could be an issue if the app generates URLs (in HTML or otherwise) and assumes `/path`. In this case, those URLs will 404. With that, I still think we should trim by default and not leak the `mountPoint` (specific to Tailscale) into whatever app is hosted. If it causes an issue with URL generation, I'd suggest looking at configuring an app-specific path prefix or running Caddy as a more advanced solution. Fixes: #6571 Signed-off-by: Shayne Sweeney <shayne@tailscale.com>
This change trims the mountPoint from the request URL path before sending the request to the reverse proxy. Today if you mount a proxy at `/foo` and request to `/foo/bar/baz`, we leak the `mountPoint` `/foo` as part of the request URL's path. This fix makes removed the `mountPoint` prefix from the path so proxied services receive requests as if they were running at the root (`/`) path. This could be an issue if the app generates URLs (in HTML or otherwise) and assumes `/path`. In this case, those URLs will 404. With that, I still think we should trim by default and not leak the `mountPoint` (specific to Tailscale) into whatever app is hosted. If it causes an issue with URL generation, I'd suggest looking at configuring an app-specific path prefix or running Caddy as a more advanced solution. Fixes: #6571 Signed-off-by: Shayne Sweeney <shayne@tailscale.com>
@DentonGentry I indeed can, but I assume that the main point behind HTTP support in Funnel is convenience, so this tweak would help there :) |
You'll run into problems on either side of this issue. However, I feel we should trim the |
This change trims the mountPoint from the request URL path before sending the request to the reverse proxy. Today if you mount a proxy at `/foo` and request to `/foo/bar/baz`, we leak the `mountPoint` `/foo` as part of the request URL's path. This fix makes removed the `mountPoint` prefix from the path so proxied services receive requests as if they were running at the root (`/`) path. This could be an issue if the app generates URLs (in HTML or otherwise) and assumes `/path`. In this case, those URLs will 404. With that, I still think we should trim by default and not leak the `mountPoint` (specific to Tailscale) into whatever app is hosted. If it causes an issue with URL generation, I'd suggest looking at configuring an app-specific path prefix or running Caddy as a more advanced solution. Fixes: #6571 Signed-off-by: Shayne Sweeney <shayne@tailscale.com>
This change trims the mountPoint from the request URL path before sending the request to the reverse proxy. Today if you mount a proxy at `/foo` and request to `/foo/bar/baz`, we leak the `mountPoint` `/foo` as part of the request URL's path. This fix makes removed the `mountPoint` prefix from the path so proxied services receive requests as if they were running at the root (`/`) path. This could be an issue if the app generates URLs (in HTML or otherwise) and assumes `/path`. In this case, those URLs will 404. With that, I still think we should trim by default and not leak the `mountPoint` (specific to Tailscale) into whatever app is hosted. If it causes an issue with URL generation, I'd suggest looking at configuring an app-specific path prefix or running Caddy as a more advanced solution. Fixes: #6571 Signed-off-by: Shayne Sweeney <shayne@tailscale.com>
Due out in v1.38.3 |
This change trims the mountPoint from the request URL path before sending the request to the reverse proxy. Today if you mount a proxy at `/foo` and request to `/foo/bar/baz`, we leak the `mountPoint` `/foo` as part of the request URL's path. This fix makes removed the `mountPoint` prefix from the path so proxied services receive requests as if they were running at the root (`/`) path. This could be an issue if the app generates URLs (in HTML or otherwise) and assumes `/path`. In this case, those URLs will 404. With that, I still think we should trim by default and not leak the `mountPoint` (specific to Tailscale) into whatever app is hosted. If it causes an issue with URL generation, I'd suggest looking at configuring an app-specific path prefix or running Caddy as a more advanced solution. Fixes: #6571 Signed-off-by: Shayne Sweeney <shayne@tailscale.com>
Thank you! |
woo! Thank you! |
This change trims the mountPoint from the request URL path before sending the request to the reverse proxy. Today if you mount a proxy at `/foo` and request to `/foo/bar/baz`, we leak the `mountPoint` `/foo` as part of the request URL's path. This fix makes removed the `mountPoint` prefix from the path so proxied services receive requests as if they were running at the root (`/`) path. This could be an issue if the app generates URLs (in HTML or otherwise) and assumes `/path`. In this case, those URLs will 404. With that, I still think we should trim by default and not leak the `mountPoint` (specific to Tailscale) into whatever app is hosted. If it causes an issue with URL generation, I'd suggest looking at configuring an app-specific path prefix or running Caddy as a more advanced solution. Fixes: #6571 Signed-off-by: Shayne Sweeney <shayne@tailscale.com>
Oh, interesting! We don't currently support additional paths for the proxy target, so If you want to make |
Thanks for the information! In my opinion, this would be extremely useful to have because it could then be even more useful. |
…le#7334) This change trims the mountPoint from the request URL path before sending the request to the reverse proxy. Today if you mount a proxy at `/foo` and request to `/foo/bar/baz`, we leak the `mountPoint` `/foo` as part of the request URL's path. This fix makes removed the `mountPoint` prefix from the path so proxied services receive requests as if they were running at the root (`/`) path. This could be an issue if the app generates URLs (in HTML or otherwise) and assumes `/path`. In this case, those URLs will 404. With that, I still think we should trim by default and not leak the `mountPoint` (specific to Tailscale) into whatever app is hosted. If it causes an issue with URL generation, I'd suggest looking at configuring an app-specific path prefix or running Caddy as a more advanced solution. Fixes: tailscale#6571 Signed-off-by: Shayne Sweeney <shayne@tailscale.com>
So, hm, this broke my existing tailscale serve/funnel configuration where I was funneling out two paths relevant to the synapse service on my machine: {
"TCP": {
"8443": {
"HTTPS": true
}
},
"Web": {
"gloria.red-acted.ts.net:8443": {
"Handlers": {
"/_matrix": {
"Proxy": "http://127.0.0.1:8448"
},
"/_synapse/client": {
"Proxy": "http://127.0.0.1:8448"
}
}
}
},
"AllowFunnel": {
"gloria.red-acted.ts.net:8443": true
}
} Now forwards all requests to Is there any way to get the old (non-path-stripping) behavior back? I tried the following, but it seems to still strip the paths when target paths are specified explicitly: $ tailscale serve https:8443 /_matrix http://127.0.0.1:8448/_matrix
$ tailscale serve https:8443 /_synapse/client http://127.0.0.1:8448/_synapse/client
$ tailscale serve status
# Funnel on:
# - https://gloria.red-acted.ts.net:8443
https://gloria.red-acted.ts.net:8443 (Funnel on)
|-- /_matrix proxy http://127.0.0.1:8448
|-- /_synapse/client proxy http://127.0.0.1:8448 |
The config you're looking for should work with the recently added support for proxy paths. Make sure you're on the $ sudo tailscale serve https /_matrix http://127.0.0.1:3000/_matrix
$ curl https://node.my-tcd.ts.net/_matrix/foo/bar
# receives:
GET /_matrix/foo/bar HTTP/1.1
Host: node.my-tcd.ts.net
Make sure you call Tip: Start from scratch and clear your serve / Funnel config with |
Re-opening to reconsider |
@antifuchs - did you manage to set up Matrix Federation trough Tailscale Funnel? I've managed to have working client of Matrix trough it, as you with @shayne described above, but unfortunatelly Funnel is limited to [443, 8443, 10000] ports, where Federation need to connect trough 8448. Do you have any idea how to overcome that? |
@gromoslaw-kroczka, we could permit 8448 too. @shayne, objections? |
You can specify that the federation lives on other ports, using the .well-known delegation method on the http server on your matrix hostname. I have the following on my own matrix domain: :; curl https://asf.computer/.well-known/matrix/server ; echo
{"m.server":"matrix.tiger-turtle.ts.net:443"}
:; curl https://asf.computer/.well-known/matrix/client ; echo
{"m.homeserver":{"base_url":"https://matrix.tiger-turtle.ts.net:443"},"m.identity_server":{"base_url":"https://vector.im"}} Note that the client service discovery file needs an HTTPS URL and the server URL must be only hostname:port (that caused a matrix outage on my machine for a few hours!); but that's it & you can listen on :443 with funnel. (I use tsnsrv to be the proxy for my homeserver & set up the funneling, but I believe this works just as well with plain |
Thank you @antifuchs ! I've missed that part of documentation (only checked somehow SRV DNS record delegation). |
What are you trying to do?
I'm using Funnel to replace ngrok with tailscale. So far it's been working well!
Right now, I have set it up as follows:
Right now I am using this to test HTTP webhooks from a GitHub App, where the local Go server simply listens on http at port 10080. GitHub sends the events to https://p14s.blue-toad.ts.net/. So far so good, and it works perfectly.
However, this particular GitHub app is only one of the pieces of software that I want to expose via Funnel for end-to-end testing. I could swap which program listens on port 10080 each time, but that's not great. For example, if I stop my Go backend for that GitHub App and work on something else serving HTTP over funnel, it will still receive the GitHub App webhook events from the previous app, because they're still wired up to the same place.
I could disable the webhook events from the GitHub App every time I stop testing it, but that's cumbersome. The problem here is that I'm sharing the same host (https://p14s.blue-toad.ts.net/) to test multiple apps at different times.
One decent option is to use different ports:
However, that's only three ports, so I could only test three different pieces of software, and then we're back to the same problem of sharing the same host and port. It would also be confusing for future me: I'll be sure to forget what I used each port for, even if I had unlimited ports. Potentially, attaching some form of label or name to each route or port, but that wouldn't be visible when looking at the HTTPS URLs alone.
I tried to solve this by using different paths, like so:
This way, the GitHub App was configured with the URL https://p14s.blue-toad.ts.net/cue-unity-github-app, which is not going to be shared with any other funnel route. The path also helps me remember what I exposed this particular route for. However, when doing a GET on that URL, an HTTP server listening on
10080
sees:That is, it receives a
GET /cue-unity-github-app
rather thanGET /
. Right now it expects webhooks to come in atPOST /foo
, not atPOST /cue-unity-github-app/foo
. I could teach each one of these pieces of software to strip a path prefix, like https://pkg.go.dev/net/http#StripPrefix, but it feels unfortunate. Some of the pieces of software I need to test aren't written in Go, so changing their behavior won't be as straightforward.How should we solve this?
Funnel already acts as an HTTP proxy, so I think it could also be taught to strip paths so that my app would see
GET /
in the example above. It doesn't need to be always on, or even by default - it could be an opt-in flag, like--strip-prefix
.Note that this is a problem I experience with the
proxy
subcommand, but not withpath
. If/home/mvdan/foo/bar.txt
exists as a plaintext file and I expose it viatailscale serve /foo path /home/mvdan/foo
, you will notice that https://p14s.blue-toad.ts.net/foo/bar.txt works perfectly - it reads/home/mvdan/foo/bar.txt
rather than/home/mvdan/foo/foo/bar.txt
.My experience with HTTP proxies is somewhat limited, so I'm not sure whether what I am asking is an anti-pattern of any sort. However, the stripping already appears to happen for
path
, and intuitively I would find theproxy
mode more useful if the stripping happened.Yet another alternative would be to allow exposing multiple subdomains. For example, instead of exposing this GitHub App backend for testing at https://p14s.blue-toad.ts.net/cue-unity-github-app, I could expose it at https://cue-unity-github-app.p14s.blue-toad.ts.net/. I don't have a strong preference; both are equally easy to identify and remember.
What is the impact of not solving this?
I am able to use Funnel, but it's awkward to use it for more than one HTTP server at the same time - even when those HTTP servers aren't all running at the same time.
Anything else?
No response
The text was updated successfully, but these errors were encountered: