jsch_ JSch uma biblioteca Java para facilitar o uso do SSH Executando comandos remotos e fazendo conexão SSH dentro de programas Java Durante a leitura do artigo iremos ver como a biblioteca JSch pode facilitar o uso do protocolo SSH. Várias tarefas relacionadas ao uso do protocolo podem ser executadas facilmente através de programas Java usando esta biblioteca. André Luís Fonseca | [email protected] é formado em Ciência da Computação pela Universidade Federal de São Carlos (UFSCar). Trabalha na empresa NTT Data Brasil. Com mais de 11 anos de experiência, já trabalhou em empresas de diversos setores como: Telecom, Bancos e Indústria utilizando Java, PHP e C. Possui as certificações SCJP, SCWCD, SCBCD e SCEA (I). O SSH é um protocolo de rede que suporta criptografia. Ele é utilizado para realizar a troca segura de dados entre servidores, execução remota de serviços ou comandos além de outros serviços entre dois computadores ligados em rede. O protocolo SSH cria um canal seguro por cima de uma rede insegura. O cliente SSH é um programa que usa o protocolo para se conectar a um computador remoto. O servidor SSH é um programa que usa o protocolo para aceitar as conexões de computadores remotos. O protocolo pode ser usado por muitas aplicações em vários sistemas operacionais diferentes, Unix, Linux, Windows etc. Em 2006 foi criada uma / 48 versão do protocolo chamada de SSH-2 que foi adotada como padrão e é usada até hoje. Mais detalhes podem ser encontrados na RFC relativa ao protocolo (consultar referências). Algumas das tarefas que podem ser executadas com esse protocolo incluem: » logar em uma shell de um computador remoto » executar comandos em um computador remoto » realizar uma cópia segura de arquivos entre computadores » juntamente com o programa rsync realizar operações de backup, cópia e espelhamento de arquivos de maneira eficiente e segura entre computadores » redirecionar pacotes de dados através da criação de um túnel A biblioteca JSch (Java Secure Shell) é uma implementação de um cliente SSH escrita em Java. Foi criada por uma empresa japonesa e disponibilizada através da licença BSD. A licença BSD permite que o software distribuído sobre essa licença seja incor- porado a produtos proprietários. Algumas aplicações que usam o JSch são: ANT, Eclipse, Netbeans, Maven, JiRA, entre outras. Além do JSch temos outras bibliotecas criadas pela mesma empresa que são: » WiredX e WeirdX – X Window Systems – um X Window System é um programa e um protocolo de rede que prove uma interface gráfica GUI para administração remota de servidores (o primeiro é comercial, o segundo usa licença GPL) » JZlib – implementação em Java da biblioteca Zlib de compressão de arquivos » JCTerm – um emulador de terminal escrito em Java para o protocolo SSH » JOrbis – implementação em Java para trabalhar com compressão de arquivos de áudio » JHttpTunnel – implementação em Java de um Túnel HTTP » JRexec – um cliente REXEC escrito em Java Este artigo está focado apenas na utilização da biblioteca JSch. Mais detalhes dos outros projetos podem ser encontrados no site da empresa (consultar referências). Iniciando com o JSch, criando o primeiro exemplo No momento da escrita deste artigo a última versão da biblioteca era a 0.1.4.9. No site do projeto pode ser feito o download do JAR apenas ou então do arquivo ZiP o qual contém vários exemplos que podem ser testados através do ANT. Após o download crie um novo projeto Java na sua iDE de preferência e adicione o JAR do JSch no CLASSPATH da aplicação, no meu caso estou usando o Eclipse. Para realizar uma conexão SSH com o host remoto (servidor) os seguintes passos precisam ser seguidos: criar um objeto JSch – onde ficam as configurações básicas como usuário e senha para a conexão » criar uma sessão – a sessão deve ser recuperada do objeto JSch » conectar a sessão no host remoto passando as credenciais do usuário » abrir um canal no host remoto de um determinado tipo usando a sessão aberta no passo anterior » executar os comandos desejados no canal aberto com o host remoto » fechar o canal » fechar a sessão com o host A seguir, temos a implementação destes passos através de código Java. » Listagem 1. Primeiro exemplo usando a biblioteca JSch. /* As configurações da sessão são feitas no objeto jSch */ JSch jSch = new JSch(); int port = 22; String host = “localhost”; String username = “andre.fonseca”; Session session = jSch.getSession(username, host, port); session.setPassword(“pass”); /* * Conecta ao host sem pedir confirmação caso contrário * você pode adicionar a lista de hosts conhecidos * usando o método setKnowHosts da classe JSch. Em * ambiente Linux recebe como parametro uma string * que aponta para o arquivo home/foo/.ssh/known_hosts */ Properties config = new Properties(); config.put(“StrictHostKeyChecking”, “no”); session.setConfig(config); /* Conecta a sessão usando um timeout */ session.connect(3000); /* Entrada e Saída em Java Entradas e Saídas de programas em Java são representadas por Streams ou Fluxos. Uma Stream pode representar um arquivo em disco, um dispositivo externo, um programa, um vetor em memória, entre outras coisas. São vários os tipos de dados suportados por uma Stream, como, por exemplo: bytes simples, tipos primitivos, caracteres, objetos etc. Uma Stream pode apenas transmitir os dados da origem para o destino, ou então manipular ou transformar os dados de entrada antes de enviá-los para a saída. Sempre representa o mesmo modelo de dados: uma Stream é uma sequência ordenada de bytes de tamanho indefinido. O Java possui três objetos representando Streams que são: System.in, System.out e System.err. Esses três objetos são inicializados automaticamente quando a JVM inicia. O primeiro representa a entrada padrão (normalmente o teclado ou o console dos programas), o segundo representa a saída padrão enquanto o último representa a saída padrão de erros. 49 \ * Abre um canal com a sessão do tipo shell * para alterar o tipo altere o parâmetro do método */ Channel channel = session.openChannel(“shell”); /* agora eu posso realizar as operações necessárias no servidor remoto */ /* Desconecta do canal */ channel.disconnect(); /* Desconecta da sessão */ session.disconnect(); Uma sessão representa uma conexão com o servidor SSH. Uma sessão pode ter um ou mais canais abertos com o servidor ao mesmo tempo. Os tipos existentes de canais são: » shell – a stream aberta com o servidor SSH possui tanto os comandos como os parâmetros de entrada dos comandos, funciona como se estivéssemos digitando comandos interativamente na shell remota » exec – os comandos são passados através do método setCommand antes do canal estar conectado com o servidor, funciona como se estivéssemos executando um shell script na máquina local » subsystem – neste caso as configurações do servidor SSH decidem o que deve ser feito, não a shell remota. Um uso comum é quando queremos fazer SFTP para o servidor SSH » direct-tcpip – este canal permite o redirecionamento de streams para e do servidor SSH » sftp – este canal se conecta a um servidor SFTP Figura 1. Selecione o pacote openssh na hora da instalação do Cygwin. Figura 2. Serviço sshd iniciado no Windows. Testando outros exemplos do JSch Conforme já mencionamos, o JSch vem com vários exemplos na pasta examples do arquivo ZiP. Vamos falar agora um pouco dos principais. Shell.java Este exemplo possibilita que o programa se conecte ao servidor sshd e retorne o prompt da shell. Ao rodar o exemplo no Eclipse você receberá um popup perguntando o usuário de entrada, preencha com o usuário que você usou para instalar o pacote Preparando o ambiente Precisamos de um servidor SSH para testar os openssh no Cygwin (usuário Windows). exemplos que vêm junto com o download do JSch. A fim de testar estes exemplos localmente sem a necessidade de um servidor remoto (em uma rede de computadores) iremos instalar o pacote openssh do Cygwin. O Cygwin é um emulador de sistemas Linux/ Unix para Windows. O openssh possui várias ferramentas de conexão SSH além de uma implementação Figura 3. Preencha com o usuário do Windows. de um servidor SSH (conhecido como sshd). Na sequência um novo popup irá perguntar a sePara instalar o Cygwin basta fazer o download do arquivo setup.exe e seguir os passos descritos no link nha do usuário. “instalando o Cygwin” nas referências do artigo (não se esqueça de selecionar o pacote openssh na hora da instalação). Após configurar o openssh no Cygwin, podemos verificar que um novo serviço foi instalado no Windows (consultar o link “Configurando um Servidor SSH Figura 4. Preencha com a senha do usuário Windows. no Cygwin” nas referências do artigo). / 50 Como não estamos armazenando a chave de autenticação no nosso computador toda vez que conectarmos no servidor “localhost” (serviço sshd rodando no Cygwin) iremos receber a mensagem abaixo. Em ambientes Linux esta chave é armazenada no arquivo ~/.ssh/known_hosts. Clique em “Yes” para continuar. // conecta no canal channel.connect(); // imprime no console do Eclipse o resultado do comando executado byte[] tmp=new byte[1024]; while(true){ while(in.available()>0){ int i=in.read(tmp, 0, 1024); if(i<0)break; } Figura 5. Mensagem de “Warning”. Listagem 2. Conectando no servidor sshd e recuperando o prompt de comando. // abre uma sessão do tipo shell Channel channel=session.openChannel(“shell”); // seta a Input Stream padrão (console do Eclipse) channel.setInputStream(System.in); // seta a Output Stream padrão (console do Eclipse) channel.setOutputStream(System.out); Exec.java } System.out.print(new String(tmp, 0, i)); if(channel.isClosed()){ System.out.println(“exit-status: “+channel.getExitStatus()); break; } ScpTo.java Este exemplo mostra como copiar um arquivo da máquina local para o servidor remoto. Antes de rodar o exemplo você deve editar as configurações do Eclipse (Run Configurations) para passar como argumento a string “file1 user@remotehost:file2” onde file1 é o caminho para o arquivo na máquina local e file2 é o caminho para o arquivo na máquina remota (Cygwin). Este exemplo abre um canal do tipo “exec” e pergunta ao usuário que comando deve ser executado no servidor remoto. Os comandos podem ser concatenados usando o pipe “|”. Exibe no console do Eclipse o resultado da execução do comando. Figura 7. Digite nos argumentos da JVM os caminhos para a cópia do arquivo. Figura 6. Digite o comando a ser executado no servidor remoto. Após rodar o exemplo você pode verificar no terminal do Cygwin que o arquivo foi copiado para a pasta home. Listagem 3. Executando comandos no servidor remoto. // usuário digita o comando a ser executado String command=JOptionPane.showInputDialog( “Enter command”, “set|grep SSH”); // abre uma sessão do tipo “exec” Channel channel=session.openChannel(“exec”); // atribuir para a sessão o comando digitado pelo usuário ((ChannelExec)channel).setCommand(command); // define a saída de erros para o console do Eclipse ((ChannelExec)channel).setErrStream(System.err); // recupera a stream de entrada do canal InputStream in=channel.getInputStream(); Figura 8. Arquivo copiado para a máquina remota. Listagem 4. Copiando um arquivo da máquina local para a máquina remota. // cria o comando e adiciona no canal do tipo “exec” String command = “scp -p -t” + rfile; 51 \ Channel channel = session.openChannel(“exec”); ((ChannelExec) channel).setCommand(command); // recupera as streams de entrada/saida do scp remoto OutputStream out = channel.getOutputStream(); InputStream in = channel.getInputStream(); // envia o comando “C0644 tamanho_arquivo nome_ arquivo”, onde o nome_arquivo // não deve possuir ‘/’ long filesize = _lfile.length(); command = “C0644 “ + filesize + “ “; if (lfile.lastIndexOf(‘/’) > 0) { command += lfile.substring(lfile.lastIndexOf(‘/’) + 1); } else { command += lfile; } command += “\n”; out.write(command.getBytes()); out.flush(); // envia o conteudo do arquivo para o servidor remoto fis = new FileInputStream(lfile); byte[] buf = new byte[1024]; while (true) { int len = fis.read(buf, 0, buf.length); if (len <= 0) break; out.write(buf, 0, len); // out.flush(); } fis.close(); fis = null; // send ‘\0’ buf[0] = 0; out.write(buf, 0, 1); out.flush(); out.close(); Logger.java Este exemplo mostra como criar um logger para recuperar as informações durante a conexão com o servidor. Listagem 5. Listando informações da conexão SSH com o servidor no console do Eclipse. NFO: Connecting to localhost port 22 INFO: Connection established INFO: Remote version string: SSH-2.0-OpenSSH_6.0 INFO: Local version string: SSH-2.0-JSCH-0.1.49 INFO: CheckCiphers: aes256-ctr,aes192-ctr,aes128ctr,aes256-cbc,aes192-cbc,aes128-cbc,3des-ctr,arcfour,arcf our128,arcfour256 INFO: CheckKexes: diffie-hellman-group14-sha1 INFO: diffie-hellman-group14-sha1 is not available. INFO: SSH_MSG_NEWKEYS sent INFO: SSH_MSG_NEWKEYS received / 52 I INFO: SSH_MSG_SERVICE_REQUEST sent INFO: SSH_MSG_SERVICE_ACCEPT received INFO: Authentications that can continue: publickey,keyboardinteractive,password INFO: Next authentication method: publickey INFO: Authentications that can continue: keyboardinteractive,password INFO: Next authentication method: keyboard-interactive INFO: Authentications that can continue: password INFO: Next authentication method: password INFO: Authentication succeeded (password). Listagem 6. Criando um Logger para exibir as informações da conexão SSH com o servidor. // seta um logger personalizado JSch.setLogger(new MyLogger()); // define uma classe estatica para implementação de um logger // personalizado public static class MyLogger implements com.jcraft.jsch.Logger { static java.util.Hashtable name= new java.util.Hashtable(); static{ name.put(new Integer(DEBUG), “DEBUG: “); name.put(new Integer(INFO), “INFO: “); name.put(new Integer(WARN), “WARN: “); name.put(new Integer(ERROR), “ERROR: “); name.put(new Integer(FATAL), “FATAL: “); } public boolean isEnabled(int level){ return true; } public void log(int level, String message){ System.err.print(name.get(new Integer(level))); System.err.println(message); } } X11Forwarding.java Este exemplo mostra como fazer “X11 Forwarding”. Esta técnica permite que você consiga executar aplicações gráficas no servidor remoto sendo que a interface será executada na máquina local. Ou seja, eu vou executar a aplicação no servidor e vou conseguir ver a interface na minha máquina local. Para conseguir rodar este exemplo precisamos iniciar o Cygwin/Xserver. No menu iniciar, na opção Cygwin-X clique em “XWin Server”. Na barra de tarefas deve aparecer um ícone no formato de um “X” mostrando que o XServer foi iniciado. Rode o exemplo no Eclipse. Depois defina o display digitando o seguinte comando export DISPLAY=’127.0.0.1:0’ no Console do Eclipse. Agora você pode iniciar uma aplicação “GUi” diretamente pelo console do Eclipse, tente, por exemplo, rodando xlogo.exe & ou xclock.exe & no console. Figura 10. Listando as opções disponíveis ao fazer o SFTP no console do Eclipse. Figura 9. Iniciando o Cygwin/XServer. Listagem 7. Definindo as configurações para fazer o X11 Forwarding. String xhost=”127.0.0.1”; int xport=0; // define as propriedades xhost e xport // necessárias para fazer o X11 Forwarding session.setx11Host(xhost); session.setX11Port(xport+6000); Channel channel=session.openChannel(“shell”); channel.setXForwarding(true); // seta a Input Stream padrão (console do Eclipse) channel.setInputStream(System.in); // seta a Output Stream padrão (console do Eclipse) channel.setOutputStream(System.out); Sftp.java Este exemplo abre um canal do tipo “sftp” (Secure File Transfer Protocol) com o servidor remoto possibilitando que arquivos sejam acessados, transferidos e manuseados de forma segura entre a máquina local e o servidor. Ao executar o exemplo no Eclipse, o usuário irá receber um prompt para se conectar no servidor e realizar as operações desejadas. Digite help no prompt (console do Eclipse) para ver as opções disponíveis. Considerações Finais A biblioteca JSch (Java Secure Shell) é uma implementação de um cliente SSH escrita em Java. Podemos utilizar essa biblioteca dentro dos nossos programas Java para facilitar a comunicação com servidores remotos através do protocolo SSH. Várias tarefas podem ser executadas como: execução remota de comandos, SCP, SFTP, X11 Forwarding etc. Essas funcionalidades podem ser integradas facilmente com outros programas já existentes facilitando a automatização de tarefas repetitivas, como backup, transferência de arquivos, entre outras. /referências > Site do JCraft (contendo todos os projetos inclusive a biblioteca JSch): http://www.jcraft.com/ > Download do JAR do JSch : http://sourceforge.net/ projects/jsch/files/jsch.jar/0.1.49/jsch-0.1.49.jar/download > Download do ZIP do JSch: http://sourceforge.net/ projects/jsch/files/jsch/0.1.49/jsch-0.1.49.zip/download > Javadoc do projeto (não oficial): http://epaul.github. com/jsch-documentation/javadoc/com/jcraft/jsch/packagesummary.html > Wiki: http://sourceforge.net/apps/mediawiki/jsch/index. php?title=Main_Page > Instalando o Cygwin: http://cygwin.com/install.html Configurando um Servidor SSH no Cygwin: http:// lifehacker.com/205090/geek-to-live--set-up-a-personalhome-ssh-server > Arquitetura do Protocolo SSH: http://tools.ietf.org/html/ rfc4251 > I/O Streams: http://docs.oracle.com/javase/tutorial/ essential/io/streams.html 53 \