java 通过 JSch 的 SSH 隧道

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/28850188/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-02 14:16:48  来源:igfitidea点击:

SSH tunneling via JSch

javasshjschssh-tunnel

提问by Unni Kris

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.

我的目标是连接到防火墙后面的服务器(主机)。我可以通过连接到网络中的另一台服务器(隧道)然后通过 SSH 连接到该服务器来访问该服务器。但是我无法通过 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.

上面的代码在线上给出了以下异常session.connect(30000);

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

采纳答案by Petesh

a SOCKSproxy setting on jsch allows you to connect to a runningproxy server on the remote side. An sshdon the remote side would notbe considered a SOCKSproxy. 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.

SOCKSjsch 上的代理设置允许您连接到远程端正在运行的代理服务器。sshd远程端的An不会被视为SOCKS代理。您需要做的是建立一个本地端口转发到您要通过隧道连接的机器上的 ssh 端口,然后使用 api 建立到该系统的辅助 ssh 连接。

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.

这段代码的作用是创建一个本地端口转发到目标系统上的 ssh 端口,然后通过它进行连接。hostname 命令的运行说明它确实在转发到的系统上运行。