使用SSHj进行SSH端口转发 [英] SSH port fowarding with SSHj

查看:235
本文介绍了使用SSHj进行SSH端口转发的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试创建一个隧道来使用支持SSH的防火墙后面的服务。我想要一个完整的java解决方案,但我似乎无法让它工作。我发现了这个 github snip 并基于此我创建了以下代码以保持后台线程为我提供隧道:

I am trying to create a tunnel to use a service behind a firewall, that supports SSH. I wanted a complete solution in java, but I cannot seem to get it to work. I found this github snip and based on that I created the following code to keep a background thread giving me the tunnel:

// property on surrounding class
// static final SSHClient sshclient = new SSHClient();

Thread thread = new Thread(new Runnable() {

    @Override
    public void run() {
        try {
            String host = "10.0.3.96";
            sshclient.useCompression();
            sshclient.addHostKeyVerifier("30:68:2a:20:21:9f:c8:e8:ac:b4:a7:fc:2d:a7:d0:26");
            sshclient.connect(host);
            sshclient.authPassword("messy", "messy");
            if (!sshclient.isAuthenticated()) {
                throw new RuntimeException(String.format("Unable to authenticate against '%s'", host));
            }
            Forward forward = new Forward(8111);
            InetSocketAddress addr = new InetSocketAddress("google.com", 80);
            SocketForwardingConnectListener listener = new SocketForwardingConnectListener(addr);

            sshclient.getRemotePortForwarder().bind(forward, listener);
            sshclient.getTransport().setHeartbeatInterval(30);

            HttpURLConnection con = (HttpURLConnection) new URL("http://localhost:8111").openConnection();
            con.setRequestMethod("GET");
            BufferedReader reader = new BufferedReader(new InputStreamReader(con.getInputStream()));
            String line = null;
            while( (line = reader.readLine()) != null) {
                System.out.println(line);
            }

            sshclient.getTransport().join();

        } catch (IOException e) {
            e.printStackTrace();
        }

        try {
            if (sshclient != null && sshclient.isConnected()) {
                sshclient.disconnect();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
});
thread.setDaemon(true);
thread.start();

问题是,如果我连接到像'10 .0.3.96'这样的远程SSH服务器,它就不会工作。如果我在本地主机上使用本地SSH服务器,它将起作用。我已经通过不同的配置运行并尝试调试,但我无法掌握SSHj包内的内容。

The problem is that if I connect to a remote SSH server like '10.0.3.96' it does not work. If I use the local SSH server on my localhost it will work. I have been going thru different configurations with any luck and tried debugging, but I cannot grasp what is going on inside the SSHj package.

现在它不一定是SSHj解决方案,但这将是首选,因为代码的其他部分完全实现并使用SSHj,我不想在一个项目中混合两个SSH包。

Now it does not have to be a SSHj solution, but that would be preferred since other parts of the code are fully implemented and using SSHj and I do not want to mix two SSH packages in one project.

谢谢任何帮助。

推荐答案

尝试这样的事情。它接收要连接的服务器列表。它会将每个中间连接隧道传输到最后一个服务器。我没有测试超过2台服务器,但它应该工作。这个答案是从改编项目改编而来的。你应该只需要导入才能让它在groovyconsole中运行。

Try something like this. It takes in a list of servers to connect to. It will tunnel each intermediate connection to the last server. I have not tested with more than 2 servers, but it should work. This answer was adapted from the overthere project and written in groovy. You should only need imports to get it working in groovyconsole.

@Grab(group='net.schmizz', module='sshj', version='0.8.1')
@Grab(group='org.bouncycastle', module='bcprov-jdk16', version='1.46')

//the sequence of hosts that the connections will be made through
def hosts = ["server1", "server2"]
//the starting port for local port forwarding
def startPort = 2222
//username for connecting to all servers
def username = 'user'
def pw = 'pass'

//--------------------------------------------------------------------------//

final TunnelPortManager PORT_MANAGER = new TunnelPortManager()

//list of all active port forwarders
List<PortForwarder> portForwarders = []

Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());

/**
 *  Established the actual port forwarder 
 */
class PortForwarder extends Thread implements Closeable {
    private final SSHClient sshClient;
    private final InetSocketAddress remoteAddress;
    private final ServerSocket localSocket;
    private CountDownLatch latch = new CountDownLatch(1);

    public PortForwarder(SSHClient sshClient, InetSocketAddress remoteAddress, ServerSocket localSocket) {
        this.sshClient = sshClient;
        this.remoteAddress = remoteAddress;
        this.localSocket = localSocket;
    }

    private static String buildName(InetSocketAddress remoteAddress, Integer localPort) {
        return "SSH local port forward thread [${localPort}:${remoteAddress.toString()}]"
    }

    @Override
    public void run() {
        LocalPortForwarder.Parameters params = new LocalPortForwarder.Parameters("127.0.0.1", localSocket.getLocalPort(),
                remoteAddress.getHostName(), remoteAddress.getPort());
        LocalPortForwarder forwarder = sshClient.newLocalPortForwarder(params, localSocket);
        try {
            latch.countDown();
            forwarder.listen();
        } catch (IOException ignore) {/* OK. */}
    }

    @Override
    public void close() throws IOException {
        localSocket.close();
        try {
            this.join();
        } catch (InterruptedException e) {/* OK.*/}
    }
}

/**
 *  Will hand out local ports available for port forwarding
 */
class TunnelPortManager {
    final int MAX_PORT = 65536

    Set<Integer> portsHandedOut = new HashSet()

    ServerSocket leaseNewPort(Integer startFrom) {
        for (int port = startFrom; port < MAX_PORT; port++) {
            if (isLeased(port)) {
                continue;
            }

            ServerSocket socket = tryBind(port);
            if (socket != null) {
                portsHandedOut.add(port);
                println "handing out port ${port} for local binding"
                return socket;
            }
        }
        throw new IllegalStateException("Could not find a single free port in the range [${startFrom}-${MAX_PORT}]...");
    }

    synchronized void returnPort(ServerSocket socket) {
        portsHandedOut.remove(socket.getLocalPort());
    }

    private boolean isLeased(int port) {
        return portsHandedOut.contains(port);
    }

    protected ServerSocket tryBind(int localPort) {
        try {
            ServerSocket ss = new ServerSocket();
            ss.setReuseAddress(true);
            ss.bind(new InetSocketAddress("localhost", localPort));
            return ss;
        } catch (IOException e) {
            return null;
        }
    }
}


PortForwarder startForwarder(PortForwarder forwarderThread) {
    forwarderThread.start();
    try {
        forwarderThread.latch.await();
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
    }
    return forwarderThread;
}


SSHClient getSSHClient(username, pw, String hostname, int port=22){
    SSHClient client = new SSHClient()
    client.addHostKeyVerifier(new PromiscuousVerifier())
    client.connect(hostname, port)
    client.authPassword(username, pw)
    return client
}

int hostCount = hosts.size()
String hostname = hosts[0]

SSHClient client = getSSHClient(username, pw, hostname)
println "making initial connection to ${hostname}"

Session session

//create port forwards up until the final
for (int i=1; i<hostCount; i++){
    hostname = hosts[i]
    println "creating connection to ${hostname}"
    ServerSocket ss = PORT_MANAGER.leaseNewPort(startPort)
    InetSocketAddress remoteAddress = new InetSocketAddress(hostname, 22)

    PortForwarder forwarderThread = new PortForwarder(client, remoteAddress, ss)
    forwarderThread = startForwarder(forwarderThread)
    session = client.startSession()

    println "adding port forward from local port ${ss.getLocalPort()} to ${remoteAddress.toString()}"
    portForwarders.add(forwarderThread)

    client = getSSHClient(username, pw, "127.0.0.1", ss.getLocalPort())
}

session = client.startSession()

//shut down the running jboss using the script
Command cmd = session.exec("hostname")
String response = IOUtils.readFully(cmd.getInputStream()).toString()
cmd.join(5, TimeUnit.SECONDS)
println "response -> ${response}"

portForwarders.each { pf ->
    pf.close()
}

session.close()
client.disconnect()

这篇关于使用SSHj进行SSH端口转发的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆