Skip to content
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

Valid upgrade path detection requires dual / #346

Closed
Ongy opened this issue Sep 6, 2024 · 5 comments
Closed

Valid upgrade path detection requires dual / #346

Ongy opened this issue Sep 6, 2024 · 5 comments
Labels

Comments

@Ongy
Copy link
Contributor

Ongy commented Sep 6, 2024

Describe the bug

When we setup a reverse proxy with a subpath and remove the prefix on the server side, the wstunnel server portion gets confused because it get's the path /events, which is not valid.

After looking at the code a bit, I found

.uri(format!("/{}/events", &client_cfg.http_upgrade_path_prefix))
which compositos the path //events when there is no prefix set.

This allowed the implementation, to rely on having at least 2 slashes in the path, even if they are only 1 path separator.

let Some((l, r)) = path[min_len..].split_once('/') else {
checks for a slash past the first byte.

To Reproduce

Run wstunnel server side behind a reverse proxy that strips the path.
E.g. we run different versions (a 9.x and a 10.x for different clients) and provide -P /wstunnel and -P /wstunnel-10 to the respective clients.
The result is //wstunnel/events on the server.
The reverse proxy rewrites this to /events by dropping /wstunnel, as path segment.

Expected behavior

The server component should allow clients that reach it with /events. Since it is equivalent to //events as path.

Your wstunnel setup

Paste your logs of wstunnel, started with --log-lvl=DEBUG, and with the command line used

  • client wss://... --http-upgrade-path-prefix /{{.Project}}/aqualine/ --local-to-remote http://0.0.0.0:1212 --connection-min-idle "10" --http-headers-file /shared/header
  • server ws:0.0.0.0:8080

Desktop (please complete the following information):

  • OS: [e.g. iOS]
  • Version [e.g. 22]

Additional context
Getting it started with the debug flags is a bit hard in our setup, but I hope pointing to the code helps here.
I can likely provide a PR to fix this myself soon™

I think the client should retain the doubling behaviour, to not break old systems, but the server should be a bit more permissive/not require an empty path segment, but allow path normalization along the way.

@Ongy Ongy added the bug label Sep 6, 2024
@erebe
Copy link
Owner

erebe commented Sep 6, 2024

Hello,

By default, if the user does not specify anything, the path prefix is v1 so /v1/events.

  -P, --http-upgrade-path-prefix <HTTP_UPGRADE_PATH_PREFIX>
          Use a specific prefix that will show up in the http path during the upgrade request.
          Useful if you need to route requests server side but don't have vhosts
          When using mTLS this option overrides the default behavior of using the common name of the
          client's certificate. This will likely result in the wstunnel server rejecting the connection.

          [env: WSTUNNEL_HTTP_UPGRADE_PATH_PREFIX=]
------->  [default: v1]     <----------

So you only need your reverse proxy to rewrite the url from //events to /whatever/events to solve your issue.
There just must be always a path before the /events.

Out of curiosity, what kind of service are you providing to your clients that you are in need of wstunnel ?

@Ongy
Copy link
Contributor Author

Ongy commented Sep 6, 2024

Ahh, I tried to find it in code and thought it was empty.
Ok, that explains part of it.

Yea, we currently have <identifier>/aqualine as prefix, and use that identifier as hint for authentication. Aqualine being an internal codename for the feature.
I.e. I consider the entire prefix in the "domain" of the reverse proxy.

This leads to a request on /project/aqualine/events that gets rewritten to /events.
The workaround is to set the prefix with this in mind, so it ends on a /, which leads to /project/aqualine//events => //events which is odd.

Your pointer to the default being v1 makes sense. But I prefer my mental model over yours :o

We are using it to tunnel grpc(http2) connections through middleboxes we don't control, which do not support anything newer than http1.

@Ongy
Copy link
Contributor Author

Ongy commented Sep 6, 2024

My mental model being, I think the entire specified prefix should be possible to strip.

Though judging from other features, it's partially intended to be used in tandem with the prefix filter on the frontend to use it as symmetric secret.

yea, if you don't like the proposed change, I can just do a different rewrite on the server.

@erebe
Copy link
Owner

erebe commented Sep 6, 2024

indeed it is supposed to work as a shared secret between client and server, so it is mandatory when restriction is on.

I will go with letting you change the rewrite rule of your reverse-proxy 😉

@erebe
Copy link
Owner

erebe commented Sep 6, 2024

In anycase, thank you for trying to debug your issue and understand the codebase 🙏

@erebe erebe closed this as completed Sep 12, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants