Home    | Software    | Articoli    | Tips'n Tricks    | Contattaci    | Supportaci  
Home arrow Articoli arrow Java ssh tunneling con jsch

Java ssh tunneling con jsch

Introduzione
Alcune persone stanno utilizzando Tunnel4J, un semplice programma grafico che permette di connettersi ad un server ssh remoto e "mappare" porte locali e remote da usare come "tunnel". Tunnel4J è basato su Jsch, una eccellente libreria 100% java per ssh. Dato che recentemente ho ricevuto diverse richieste su come fare "ssh tunneling" in java, ho deciso di scrivere questo breve articolo.

I perchè
Il "tunneling ssh" è usato per accedere a risorse che si trovino dietro un "firewall", altrimenti impossibili da raggiungere.
Ciò che è spiegato qui può essere ottenuto semplicemente utilizzando openssh su sistemi unix o sistemi per i quali ne esista una versione, come spiegato qui e qui.
L'utilizzo di una libreria 100% java ci garantisce indipendenza dalla piattaforma. Sotto possiamo vedere una tipica situazione che richieda "tunneling ssh":

Typical situation requiring ssh tunneling

In questo caso abbiamo bisogno di accedere ad un server web (porta 80) ospitato su una macchina posizionata dietro ad un firewall e non direttamente accessibile. Avendo una utenza attiva sul firewall, possiamo connetterci ad esso e forzare un inoltro (forwarding) di pacchetti tcp verso il server web sulla rete locale. Vediamo come:

Il codice

Per prima cosa è necessario scaricare Jsch da Jcraft, ed assicurarsi di avere la libreria nel "classpath".
Di seguito è mostrato una classe di esempio, semplice ma funzionante, utile per spiegare le basi del "tunneling" mediante jsch.

   1:import com.jcraft.jsch.*;
2:
3:public class Tunnel {
4: public static void main(String[] args){
5: Tunnel t=new Tunnel();
6: try{
7: t.go();
8: } catch(Exception ex){
9: ex.printStackTrace();
10: }
11: }
12: public void go() throws Exception{
13: String host="XXX.XXX.XXX.XXX";
14: String user=&qmauot;username";
15: String password="password";
16: int port=22;
17:
18: int tunnelLocalPort=9080;
19: String tunnelRemoteHost="YYY.YYY.YYY.YYY";
20: int tunnelRemotePort=80;
21:
22: JSch jsch=new JSch();
23: Session session=jsch.getSession(user, host, port);
24: session.setPassword(password);
25: localUserInfo lui=new localUserInfo();
26: session.setUserInfo(lui);
27: session.connect();
28: session.setPortForwardingL(tunnelLocalPort,tunnelRemoteHost,tunnelRemotePort);
29: System.out.println("Connected");
30:
31: }
32:
33: class localUserInfo implements UserInfo{
34: String passwd;
35: public String getPassword(){ return passwd; }
36: public boolean promptYesNo(String str){return true;}
37: public String getPassphrase(){ return null; }
38: public boolean promptPassphrase(String message){return true; }
39: public boolean promptPassword(String message){return true;}
40: public void showMessage(String message){}
41: }
42:}

Concentriamoci sul metodo "go". Alla riga 22 istanziamo un oggetto Jsch, che è il cuore della libreria. Poi lo usiamo come "factory" per ottenere un oggetto "Session" (linea 23), al quale passiamo nome utente, indirizzo del server e porta. Nel nostro caso, l'"host" è il "firewall" e la porta è la 22 (ssh).
Alla linea 24 passiamo la password dell'utente del firewall. Ignoriamo per il momento le righe 25 e 26 (le rivedremo tra un minuto).
Alla riga 27 ci connettiamo all'host remoto(il firewall), ma la "magia" è alla riga 28. Con il metodo "setPortForwardingL" istruiamo la nostra sessione aperta a redirigere tutti i dati ricevuti sulla porta "tunnelLocalPort" del firewall verso la porta "tunnelRemotePort" della macchina con indirizzo "tunnelRemoteHost" posizionata dietro il firewall, e viceversa. In questo modo, con i valori utilizzati nel codice di cui sopra (ricordiamoci di sostituire gli indirizzi IP e le porte tcp con quelle reali), se sul "browser" scriviamo "http://127.0.0.1:9080", vedremo la pagina principale del web server attivo sulla porta 80 della macchina con indirizzo IP YYY.YYY.YYY.YYY che si trova dietro il firewall.

Torniamo alle righe 25 e 26. Queste si riferiscono alla "inner class" "localUserInfo",definita nel codice. Essa implementa UserInfo e si comporta come una classe di utilità per autenticarsi sulla macchina proxy(il firewall nel nostro caso). I metodi implementati di nostro interesse sono:
  1. promptPassword - qui mettiamo il codice necessario per la richiesta di password. Nel nostro caso abbiamo già inserito la password mediante "session.setPassword", altrimenti l'avremmo dovuta inserire qui. In applicativi grafici potremmo mostrare una "dialog box" con campo di testo, mentre in un applicativo a riga di comando avremmo richiesto l'inserimento appunto da riga di comando ( standard input).
  2. showMessage - Questo verrà chiamato da jsch al verificarsi di qualche evento durante la connessione/autenticazione. Sta allo sviluppatore mostrare il messaggio passato, attraverso un semplice System.out.println, una "dialog box" o altro.
  3. promptYesNo - Questo viene chiamato da jsch con un messaggio se è necessaria una conferma o altro (tipicamente l'accettazione della connessione ad un host sconosciuto). In questo caso restituiamo "true" (altrimenti la connessione viene respinta ma, di nuovo, è possibile implementare una "dialog box" si/no per richiedere conferma).

Detto ciò, la riga 25 istanzia la nostra "inner class", mentre la 26 lascia che la nostra "session" utilizzi l'istanza di "localUserInfo" per gestire l'autenticazione.

Abbiamo visto lo scenario più semplice possibile, ma utilizzando Jsch è possibile fare alcune cose piuttosto interessanti. Ad esempio la tecnica viene utilizzata per montare localmente dischi di macchine che si trovino dietro un firewall. Non è difficile:
  1. Fare un "forwarding" locale della propria porta 2022 (qualsiasi porta libera va bene) verso la porta 22 della risorsa protetta
  2. Fare un shfsmount usando come host 127.0.0.1 e porta 2022


Hasta la proxima.
 
< Prec.   Pros. >

  Articles RSS feed

Articoli pił recenti
Software pił recenti
   
designed by allmambo.com