SSH port fowarding with SSHj

后端 未结 1 1796
鱼传尺愫
鱼传尺愫 2021-01-07 00:13

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

相关标签:
1条回答
  • 2021-01-07 01:17

    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()
    
    0 讨论(0)
提交回复
热议问题