groovy-ssh
groovy-ssh copied to clipboard
Port forwarding support?
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.
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
}
}
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
-Nssh 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:
- 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). - 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).
- 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?)
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.
Would it be possible to support something like the
-Nssh 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
}
}
I think there are two issues on this topic,
- 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.
- Adding DSL like
- Fix host key checking issue on the gateway access (Improvement)
- It may be resolved by apply
ProxyCommandon the JSch session. - It needs to try the stackoverflow solution you wrote.
- It may be resolved by apply