SSH tunneling via JSch

18,570

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.

Share:
18,570
Unni Kris
Author by

Unni Kris

Updated on June 27, 2022

Comments

  • Unni Kris
    Unni Kris almost 2 years

    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
    
  • Unni Kris
    Unni Kris about 9 years
    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?
  • Anya Shenanigans
    Anya Shenanigans about 9 years
    These are listed in the openChannel documentation.
  • Arya
    Arya over 6 years
    @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?
  • Anya Shenanigans
    Anya Shenanigans over 6 years
    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
    Anya Shenanigans almost 6 years
    @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.
  • Dwarrior
    Dwarrior about 5 years
    @Petesh I am able to understand your code after referring these links:1 and 2. 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?
  • Anya Shenanigans
    Anya Shenanigans about 5 years
    @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.
  • Dwarrior
    Dwarrior about 5 years
    @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?