13

My aim is to connect to a server (host) which is behind a firewall. I am able to access this server by connecting to another server (tunnel) in the network and then SSH to this server. However I am not able to implement the same scenario via JSch.

I am not able to have the below code work, which I have written for this purpose. Please let me know if I am doing anything silly here.

public class JschExecutor {

    public static void main(String[] args){
        JschExecutor t=new JschExecutor();
        try{
            t.go();
        } catch(Exception ex){
            ex.printStackTrace();
        }
    }
    public void go() throws Exception{

        StringBuilder outputBuffer = new StringBuilder();

        String host="xxx.xxx.xxx.xxx"; // The host to be connected finally
        String user="user";
        String password="passwrd";
        int port=22;

        String tunnelRemoteHost="xx.xx.xx.xx"; // The host from where the tunnel is created

        JSch jsch=new JSch();
        Session session=jsch.getSession(user, host, port);
        session.setPassword(password);
        localUserInfo lui=new localUserInfo();
        session.setUserInfo(lui);
        session.setConfig("StrictHostKeyChecking", "no");

        ProxySOCKS5 proxyTunnel = new ProxySOCKS5(tunnelRemoteHost, 22);
        proxyTunnel.setUserPasswd(user, password);
        session.setProxy(proxyTunnel);

        session.connect(30000);

        Channel channel=session.openChannel("exec");
        ((ChannelExec)channel).setCommand("hostname");

        channel.setInputStream(null);
        ((ChannelExec)channel).setErrStream(System.err);

        InputStream in=channel.getInputStream();
        BufferedReader ebr = new BufferedReader(new InputStreamReader(in));

        channel.connect();

        while (true) {
            byte[] tmpArray=new byte[1024];
            while(in.available()>0){
                int i=in.read(tmpArray, 0, 1024);
                if(i<0)break;
                outputBuffer.append(new String(tmpArray, 0, i)).append("\n");
             }
            if(channel.isClosed()){
                System.out.println("exit-status: "+channel.getExitStatus());
                break;
             }
        }
        ebr.close();

        channel.disconnect();

        session.disconnect();

        System.out.println(outputBuffer.toString());
    }

  class localUserInfo implements UserInfo{
    String passwd;
    public String getPassword(){ return passwd; }
    public boolean promptYesNo(String str){return true;}
    public String getPassphrase(){ return null; }
    public boolean promptPassphrase(String message){return true; }
    public boolean promptPassword(String message){return true;}
    public void showMessage(String message){}
  }     

} 

The above code gives the below exception on the session.connect(30000); line.

com.jcraft.jsch.JSchException: ProxySOCKS5: com.jcraft.jsch.JSchException: fail in SOCKS5 proxy
    at com.jcraft.jsch.ProxySOCKS5.connect(ProxySOCKS5.java:317)
    at com.jcraft.jsch.Session.connect(Session.java:231)
    at com.ukris.main.JschExecutor.go(JschExecutor.java:50)
    at com.ukris.main.JschExecutor.main(JschExecutor.java:19)
Caused by: com.jcraft.jsch.JSchException: fail in SOCKS5 proxy
    at com.jcraft.jsch.ProxySOCKS5.connect(ProxySOCKS5.java:200)
    ... 3 more
Martin Prikryl
  • 188,800
  • 56
  • 490
  • 992
Unni Kris
  • 3,081
  • 4
  • 35
  • 57

2 Answers2

12

a SOCKS proxy setting on jsch allows you to connect to a running proxy server on the remote side. An sshd on the remote side would not be considered a SOCKS proxy. What you will have to do is establish a local port forward to the ssh port on the machine you're tunneling to, then establish a secondary ssh connection to this system using the api.

I've taken your example and slightly rewritten it to accomplish this:

import com.jcraft.jsch.*;
import java.io.*;

public class JschExecutor2 {

    public static void main(String[] args){
        JschExecutor2 t=new JschExecutor2();
        try{
            t.go();
        } catch(Exception ex){
            ex.printStackTrace();
        }
    }

    public void go() throws Exception{

        StringBuilder outputBuffer = new StringBuilder();

        String host="firstsystem"; // First level target
        String user="username";
        String password="firstlevelpassword";
        String tunnelRemoteHost="secondlevelhost"; // The host of the second target
        String secondPassword="targetsystempassword";
        int port=22;


        JSch jsch=new JSch();
        Session session=jsch.getSession(user, host, port);
        session.setPassword(password);
        localUserInfo lui=new localUserInfo();
        session.setUserInfo(lui);
        session.setConfig("StrictHostKeyChecking", "no");
        // create port from 2233 on local system to port 22 on tunnelRemoteHost
        session.setPortForwardingL(2233, tunnelRemoteHost, 22);
        session.connect();
        session.openChannel("direct-tcpip");

        // create a session connected to port 2233 on the local host.
        Session secondSession = jsch.getSession(user, "localhost", 2233);
        secondSession.setPassword(secondPassword);
        secondSession.setUserInfo(lui);
        secondSession.setConfig("StrictHostKeyChecking", "no");

        secondSession.connect(); // now we're connected to the secondary system
        Channel channel=secondSession.openChannel("exec");
        ((ChannelExec)channel).setCommand("hostname");

        channel.setInputStream(null);

        InputStream stdout=channel.getInputStream();

        channel.connect();

        while (true) {
            byte[] tmpArray=new byte[1024];
            while(stdout.available() > 0){
                int i=stdout.read(tmpArray, 0, 1024);
                if(i<0)break;
                outputBuffer.append(new String(tmpArray, 0, i));
             }
            if(channel.isClosed()){
                System.out.println("exit-status: "+channel.getExitStatus());
                break;
             }
        }
        stdout.close();

        channel.disconnect();

        secondSession.disconnect();
        session.disconnect();

        System.out.print(outputBuffer.toString());
    }

  class localUserInfo implements UserInfo{
    String passwd;
    public String getPassword(){ return passwd; }
    public boolean promptYesNo(String str){return true;}
    public String getPassphrase(){ return null; }
    public boolean promptPassphrase(String message){return true; }
    public boolean promptPassword(String message){return true;}
    public void showMessage(String message){}
  }

} 

What this code does is create a local port forwarding to the ssh port on the target system, then connects through it. The running of the hostname command illustrates that it is, indeed, running on the forwarded-to system.

Anya Shenanigans
  • 91,618
  • 3
  • 107
  • 122
  • Thanks Petesh.. This works like a charm. And thanks for clarifying on the `SOCKS` part. One query though, `session.openChannel("direct-tcpip")`, do u have the list of all available channels for a JSch session? – Unni Kris Mar 04 '15 at 11:56
  • 1
    These are listed in the [openChannel](http://epaul.github.io/jsch-documentation/javadoc/com/jcraft/jsch/Session.html#openChannel%28java.lang.String%29) documentation. – Anya Shenanigans Mar 04 '15 at 12:04
  • @Petesh if I understand correctly this pretty much does the thing thing the tunneling setting in Putty does? While it's running I can point Firefox's SOCKS proxy to port 2233 and it will use it as a SOCKS proxy? – Arya Oct 12 '17 at 03:58
  • Yes, this does the same as the 'tunneling' setting in putty. In this case it produces a local port which mirrors a port on the remote end, allowing you to connect to it. If the remote port you are connecting to is a SOCKS proxy server's port, then you can point firefox's settings to this local port as a SOCKS proxy. It does not 'SOCKSify' the port in any way, it simply adds a local mirror to the remote port. – Anya Shenanigans Oct 12 '17 at 15:02
  • @FedericoTaschin I don't know where you get the exception to try to debug this. The first connection establishes the tunnel from a local port to the remote system. If this 'went away' as soon as connected to, then this would cause the closed connection from the foreign host exception on the second call; but I'm just positing an approach. You could test this from the command line using command line ssh `ssh -L 2233:tunnelRemoteHost:22 host`, and then `ssh -p 2233 localhost` and see where it falls over. – Anya Shenanigans May 30 '18 at 13:42
  • @Petesh I am able to understand your code after referring these links:[1](https://unix.stackexchange.com/questions/115897/whats-ssh-port-forwarding-and-whats-the-difference-between-ssh-local-and-remot) and [2](http://unixwiz.net/techtips/ssh-agent-forwarding.html#sec). 2 things to clarify: (1) 2nd URL says "though the agent uses a socket, it's a UNIX domain socket, which is accessible only from the local filesystem. The agents do not listen on any TCP/IP-based socket." How it relates to "direct-tcpip"that you have used in open channel.(2) any security implications of using this code? – Dwarrior Feb 20 '19 at 11:53
  • @Dwarrior agent forwarding is something completely different and unrelated to the feature of tcp port forwarding, which is what this question is asking about. Security implications of this code? It uses embedded passwords, it creates port forwardings which can be taken advantage of on the relay box because TCP ports have no inherent access security. This code is simple, *sample* code and should not be used in a production environment. – Anya Shenanigans Feb 20 '19 at 12:07
  • @Petesh - Thank you for clarifying. That's exactly what I was trying to understand. Assume your passwords are stored securely but are decrypted to actual password while getting session and one wants to use port forwarding in production, how should that be handled then? – Dwarrior Feb 20 '19 at 20:03
0

This is tested and working fine. this works like secure pipes and best for tunneling

        String strSshUser = "ssh_user_name"; // SSH loging username
        String strSshPassword = "abcd1234"; // SSH login password
        String strSshHost = "your.ssh.hostname.com"; // hostname or ip or
                                                        // SSH server
        int nSshPort = 22; // remote SSH host port number
        String strRemoteHost = "your.database.hostname.com"; // hostname or
                                                                // ip of
                                                                // your
                                                                // database
                                                                // server
        int nLocalPort = 3366; // local port number use to bind SSH tunnel
        int nRemotePort = 3306; // remote port number of your database
        String strDbUser = "db_user_name"; // database loging username
        String strDbPassword = "4321dcba"; // database login password

    final JSch jsch = new JSch();
    Session session = jsch.getSession(strSshUser, strSshHost, 22);
    session.setPassword(strSshPassword);

    final Properties config = new Properties();
    config.put("StrictHostKeyChecking", "no");
    session.setConfig(config);

    session.connect();
    session.setPortForwardingL(nLocalPort, strRemoteHost, nRemotePort);