groovy-ssh icon indicating copy to clipboard operation
groovy-ssh copied to clipboard

Port forwarding support?

Open silasdavis opened this issue 10 years ago • 5 comments

Is there a way to set up SSH port forwarding. Do you expose the jsch Session anywhere?

The method I am interested in calling is something like session.setPortForwardingL(8000, "localhost", 8000)

It seems like it could be added fairly simply to https://github.com/int128/groovy-ssh/blob/198fb091b9d005d5a39ad978adfc6a84fb1056cd/src/main/groovy/org/hidetake/groovy/ssh/connection/ConnectionManager.groovy#L62 if you could provide local/remote forwards in the remotes closure. I might have a go at it, but I don't have clear idea of how it fits together at the moment.

silasdavis avatar Jan 28 '15 18:01 silasdavis

Currently there is no way to achieve this. I am planning to provide DSL for port forwarding in future release. Just an idea:

ssh.run {
  session(ssh.remotes.some) {
    portForward localPort: 8000, remoteHost: 'localhost', remotePort: 8000
  }
}

int128 avatar Jan 29 '15 01:01 int128

Something like this would be really useful. Also thank you for groovy-ssh (and the gradle plugin), I really like how they are done. A few suggestions:

  • Supporting both local and remote forwards would be useful, as well as multiple forwards:
ssh.run {
  session(ssh.remotes.some) {
    portForwardLocal localPort: 443, remoteHost: 'localhost', remotePort: 8080
    portForwardLocal localPort: 8000, remoteHost: 'localhost', remotePort: 8000
    portForwardRemote remotePort: 8000, localHost: 'localhost', localPort: 8000
  }
}

However perhaps it would be better, as the SSH man page does, to drop the 'local' and 'remote' prefixes; the connection itself is established locally or remotely, and the remote or local host can just be any host accessible from either the local or remote side of the tunnel. So that would give us a DSL like:

ssh.run {
  session(ssh.remotes.some) {
    portForwardLocal port: 443, host: 'localhost', hostPort: 8080
    portForwardRemote port: 8000, host: 'localhost', hostPort: 8000
  }
}

It also keeps the arguments symmetrical between the two types of forward.

  • Would it be possible to support something like the -N ssh option: "Do not execute a remote command. This is useful for just forwarding ports ..."? Perhaps as a method in the session closure. We would like to hold the tunnel open without having to execute something on the remote side.
  • Currently your implementation of gateways (very useful) sets up a series of tunnels to forward connections throught a chain of gateways. There are a few issues with this in the presence of user-defined port forwards:
  1. You are tying up a randomly allocated port on the local side of each machine in the tunnel network with session.setPortForwardingL(0, 'localhost', 22) (no big deal).
  2. Because of the use of the forward, and hence connecting to localhost on a gateway, I've needed to add an entry to my known_hosts labelled as 127.0.0.1 not the real IP of a machine being forwarded (I don't know how this works for more than one hop).
  3. If you want to forward a port from a remote machine after a series of gateway hops you need to set up a chain of forwards on every machine (every remote in groovy-ssh).

I think these issues would be alleviated with a solution in the style of ssh ProxyCommand. For example I use in my ~/.ssh/config:

Host remote-machine
  HostName 100.10.0.1
  User silas
  ProxyCommand ssh -W %h:%p gateway # sometimes this is done with netcat, but I prefer this
  LocalForward 8000 localhost:8000

This has the advantage that the entire contents of the tunnel is proxied (stdin->stdout) through my gateway and I do not need intermediate port forwards. I can curl localhost:8000 and get direct access to port 8000 on remote-machine without a LocalForward 8000 localhost:8000 on gateway. Furthermore I haven't used up any unnecessary ports.

We could emulate this in Java with something like the snippet in the answer of: http://stackoverflow.com/questions/21567031/how-to-use-jsch-with-proxycommands-for-portforwarding. So we would use something along those lines instead of your current port-forwarding-based implementation of establishViaGateway.

Let me know what you think, if I could help (perhaps providing a proxy command class?)

silasdavis avatar Jan 29 '15 10:01 silasdavis

I created the pull request of port forwarding. Please let me know any improvement.

It has been pending problem for a long time that known_host does not work in the gateway access senario. Using ProxyCommand seems very good idea but a bit difficult with JSch. So would you help the proxy implementation? Any pull request is welcome.

Thanks for your suggestion.

int128 avatar Jan 29 '15 16:01 int128

Would it be possible to support something like the -N ssh option: "Do not execute a remote command. This is useful for just forwarding ports ..."? Perhaps as a method in the session closure. We would like to hold the tunnel open without having to execute something on the remote side.

We do not have to execute something in the session in order to keep the tunnel, because ssh.run closes the connection after all sessions are done. Following script will work fine on my pull request.

def response = ssh.run {
  session(ssh.remotes.some) {
    portForwardLocal port: 8080, host: 'localhost', hostPort: 8080

    // do something on the tunnel.
    // e.g. get a resource from the web server over the tunnel
    new RESTClient('http://localhost:8080').get(path: '/')

    // the tunnel will be kept until the session closure is done
  }
}

int128 avatar Jan 30 '15 04:01 int128

I think there are two issues on this topic,

  1. Providing the port forwarding in the session (New feature)
    • Adding DSL like portForwardLocal.
    • This may be useful for access over the tunnel such as a remote web server.
    • I wrote the pull request #31 for this one.
  2. Fix host key checking issue on the gateway access (Improvement)
    • It may be resolved by apply ProxyCommand on the JSch session.
    • It needs to try the stackoverflow solution you wrote.

int128 avatar Jan 30 '15 04:01 int128