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

Reverse tunnel listen interface #587

Closed
lostystyg opened this issue Jul 20, 2023 · 9 comments · Fixed by #600
Closed

Reverse tunnel listen interface #587

lostystyg opened this issue Jul 20, 2023 · 9 comments · Fixed by #600

Comments

@lostystyg
Copy link
Contributor

According to this comment anything that goes to localhost:4321 on the server will be received by 2224 on the local machine but it definitely binds the socket to 0.0.0.0 instead of localhost and thus every outside connection can reach my local lemonade clipboard service while i definitely want it to be accessible only by the server i am connecting to.
E.x.

# on the client
-> et -r 58909:58909 $ip
# on the server
-> lsof -i -P -n | grep 58909
etserver  195783 root   14u  IPv4 2227673      0t0  TCP *:58909 (LISTEN)

while using plain ssh:

# on the client
-> ssh -R 58909:127.0.0.1:58909
# on the server
-> lsof -i -P -n | grep 58909
sshd      198448 boommy    8u  IPv6 3361053      0t0  TCP [::1]:58909 (LISTEN)
sshd      198448 boommy    9u  IPv4 3361054      0t0  TCP 127.0.0.1:58909 (LISTEN)

Notice that sshd listens for 127.0.0.0 and et listens for *.
Is it a bug, feature or requires an option to control the behavior?

@MisterTea
Copy link
Owner

Interesting, thanks for sharing! This is a bug and we should fix it. Can you submit a PR to fix? It should be pretty simple to search for the places we call bind() and figure out which one is for tunneling.

@lostystyg
Copy link
Contributor Author

lostystyg commented Jul 22, 2023

SocketEndpoint::name is being used to determine the ip to bind the socket on (here)
I think the proper place to set the name is here, so e.x. the solution would be:

      } else {
        PortForwardSourceRequest pfsr;
+++     pfsr.mutable_source()->set_name("localhost");
        pfsr.mutable_source()->set_port(stoi(sourceDestination[0]));
        pfsr.mutable_destination()->set_port(stoi(sourceDestination[1]));
        pfsrs.push_back(pfsr);
      }

at this point the ip is being controlled by the client and etserver just binds the socket to a requested ip (localhost in this case), also it may be later converted to an option or smth for the client.

On the other hand it can be hardcoded on the etserver side, ex. here:

  ForwardSourceHandler::ForwardSourceHandler(
      shared_ptr<SocketHandler> _socketHandler, const SocketEndpoint& _source,
      const SocketEndpoint& _destination)
      : socketHandler(_socketHandler),
        source(_source),
        destination(_destination) {
+++ source->set_name("localhost");
    socketHandler->listen(source);
  }

What solution is preferable?

@MisterTea
Copy link
Owner

Yep, agreed that the first way makes the most sense.

@lostystyg
Copy link
Contributor Author

lostystyg commented Jul 22, 2023

Ofc not that easy) Fails because of this. Btw it doesn't make sense for me to perform this check and fail while the source.name is being used further in the networkSocketHandler

@MisterTea
Copy link
Owner

That check was to prevent a different issue. If the name is "localhost" we can skip that check (i.e. let's change the check to allow localhost through)

@lostystyg
Copy link
Contributor Author

lostystyg commented Jul 24, 2023

At this point it will be identical to just hardcoding localhost on the server. I think that theoretically server can listen for any host/ip that is specified by the client.
Furthermore, it seems for me that there only 2 types of tunneling: via pipe and via tcp. The check is performed for both pipe and tcp but it seems that it is required only to check that pipe filename is not specified on the client and forced to be generated on the server. Also, if the type of tunnel is pipe and there is a source without name, the code will skip temporarily filename creation without error and try to create a pipe socket without a filename. This is definitely not what should happen, so i suppose that the logic of this function should be a bit rewritten, so it could allow any source.name for tcp tunnels (the goal of this issue), and do not allow any source at all for pipe tunnels.
Basically:

if (!pfsr.has_source()) {
  // pipe stuff
} else if (pfsr.source().has_port()) {
  // tcp stuff
} else {
  // error
}

@lostystyg
Copy link
Contributor Author

Hmm, i misunderstand this a bit, if pfsr.has_source() it is always a tcp tunnel even now. Why not then allow to specify a name? What was the purpose to do this check and fail? If client specify a rubbish name (means wrong ip or wrong hostname or literally rubbish) then it will fail inside networkSocketHandler with Error getting address info for ... or Error binding ... that i assume is the correct and obvious behavior, isn't it?

@lostystyg
Copy link
Contributor Author

lostystyg commented Aug 1, 2023

At the end of the day i see this as a right solution lostystyg#1
Using it for a week and it feels good for me
If it's ok for you i will rebase the PR to the upstream

@MisterTea
Copy link
Owner

That makes sense to me. Please submit a PR, thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants