SSH tunneling 2 gateways via JSch - java

I wonder if its possible to extend the answer in SSH tunneling via JSch to a case where I have two gateways (two points) before the final host.
What I tried was
import com.jcraft.jsch.*;
import java.io.InputStream;
public class Main {
public static void main(String[] args){
Main t=new Main();
try{
t.go();
} catch(Exception ex){
ex.printStackTrace();
}
}
public void go() throws Exception{
StringBuilder outputBuffer = new StringBuilder();
String Gateway1="192.168.0.101"; // First level target
String user1="root";
String password="12qwaszx";
String Gateway2="192.168.0.102"; // The host of the second target
String user2 = "root";
String secondPassword="12qwaszx";
String endpoint = "10.81.77.52";
String finaluser="admin";
String finaluserpass="admin";
JSch jsch=new JSch();
Session session=jsch.getSession(user1, Gateway1, 22);
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, Gateway2, 22);
session.connect();
session.openChannel("direct-tcpip");
// create a session connected to port 2233 on the local host.
Session secondSession = jsch.getSession(user2, "localhost", 2233);
secondSession.setPassword(secondPassword);
secondSession.setUserInfo(lui);
secondSession.setConfig("StrictHostKeyChecking", "no");
secondSession.setPortForwardingL(2233, endpoint, 22);
secondSession.connect(); // now we're connected to the secondary system
secondSession.openChannel("direct-tcpip");
Session finalSession = jsch.getSession(finaluser, "localhost", 2233);
finalSession.setPassword(finaluserpass);
finalSession.setUserInfo(lui);
finalSession.setConfig("StrictHostKeyChecking", "no");
finalSession.connect();
Channel channel=secondSession.openChannel("exec");
((ChannelExec)channel).setCommand("show system information | match \"System Name\"");
channel.setInputStream(null);
InputStream stdout=channel.getInputStream();
channel.connect();
if (channel.isConnected()){
System.out.println("connected");
}
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){}
}
}
But despite my efforts, I don't actually get to connect to the endpoint. Getting
com.jcraft.jsch.JSchException: PortForwardingL: local port 127.0.0.1:2233 cannot be bound.
at com.jcraft.jsch.PortWatcher.<init>(PortWatcher.java:158)
at com.jcraft.jsch.PortWatcher.addPort(PortWatcher.java:110)
at com.jcraft.jsch.Session.setPortForwardingL(Session.java:1847)
at com.jcraft.jsch.Session.setPortForwardingL(Session.java:1828)
at com.jcraft.jsch.Session.setPortForwardingL(Session.java:1809)
at com.jcraft.jsch.Session.setPortForwardingL(Session.java:1792)
at Main.go(Main.java:36)
at Main.main(Main.java:9)
Caused by: java.net.BindException: Address already in use: JVM_Bind
at java.net.DualStackPlainSocketImpl.bind0(Native Method)
at java.net.DualStackPlainSocketImpl.socketBind(DualStackPlainSocketImpl.java:106)
at java.net.AbstractPlainSocketImpl.bind(AbstractPlainSocketImpl.java:387)
at java.net.PlainSocketImpl.bind(PlainSocketImpl.java:190)
at java.net.ServerSocket.bind(ServerSocket.java:375)
at java.net.ServerSocket.<init>(ServerSocket.java:237)
at com.jcraft.jsch.PortWatcher.<init>(PortWatcher.java:150)
... 7 more
Process finished with exit code 0
Please, if anyone could help me. I need to Get to the endpoint by ssh tunneling two gateways.

I swear I didn't meant it,
but the problem was that the first lport was equal to the second.
it worked like bellow:
session.setPortForwardingL(2222, Gateway2, 22);
session.connect();
session.openChannel("direct-tcpip");
Session secondSession = jsch.getSession(user2, "localhost", 2222);

Related

Authentication using a key encrypted with a passphrase in JSch

I'd like to use JSch to open an SFTP connection to download some files.
String SFTPPRIVATEKEY = "/folder/privatekeyfile";
String SFTPUSER = "user";
String SFTPPASS = "";
String SFTPHOST = "server.tld";
String SFTPPORT = "22";
int usePrivateKey = 1;
public boolean connect() {
boolean isConnected = false;
try {
JSch jsch = new JSch();
if (usePrivateKey) {
jsch.addIdentity(SFTPPRIVATEKEY);
}
session = jsch.getSession(SFTPUSER,SFTPHOST,SFTPPORT);
if (!usePrivateKey) {
session.setPassword(SFTPPASS);
}
java.util.Properties config = new java.util.Properties();
config.put("StrictHostKeyChecking", "no");
session.setConfig(config);
session.connect();
if (session.isConnected() == true) {
log.println("Connection to Session server is successfully");
}
channel = session.openChannel("sftp");
channel.connect();
channelSftp = (ChannelSftp)channel;
isConnected = true;
} catch (JSchException e) {
log.println("SFTPClient Connect ERROR: "+e.getMessage());
e.printStackTrace();
}
return isConnected;
}
If i run my code i get:
com.jcraft.jsch.JSchException: USERAUTH fail
I tried to connect using sftp on the shell on the same client where my Java code runs. I run
sftp -i privatekeyfile user#server.tld
It prompts for a passphrase for the privatekeyfile. I entered the passphrase and the connection works great.
But JSch did not connect. I found no option to set the passphrase in JSch. Maybe this is the problem?
Can some of you help?
Thanks
There's JSch.addIdentity overload that takes the passphrase:
public void addIdentity(String prvkey, String passphrase)
Obligatory warning: Do not use StrictHostKeyChecking=no to blindly accept all host keys. That is a security flaw. You lose a protection against MITM attacks. For the correct (and secure) approach, see: How to resolve Java UnknownHostKey, while using JSch SFTP library?

Implementing SSH shell terminal in webapp using JSch

I am trying to implement a shell terminal in a webapp using websocket in spring. I am able to send a single command to the JSch "exec" channel and send the output back to the websocket.
The problem I am having is:
I am not able to retain the state of the shell like working directory, when I send a second command. How can I retain the previous state? I have tried using the same session but it does not work.
public String sendCommand(String command) {
StringBuilder outputBuffer = new StringBuilder();
try {
Channel channel = sesConnection.openChannel("exec");
((ChannelExec) channel).setCommand(command);
InputStream commandOutput = channel.getInputStream();
channel.connect();
int readByte = commandOutput.read();
while (readByte != 0xffffffff) {
outputBuffer.append((char) readByte);
readByte = commandOutput.read();
}
channel.disconnect();
} catch (IOException ioX) {
logWarning(ioX.getMessage());
return null;
} catch (JSchException jschX) {
logWarning(jschX.getMessage());
return null;
}
return outputBuffer.toString();
}
To send back to the websocket, in the controller I have :
private SSHManager getSSHInstance() {
String errorMessage = null;
if (sshInstance == null) {
sshInstance = new SSHManager(username, password, host, "", port);
errorMessage = sshInstance.connect();
System.out.println("Instance created");
if (errorMessage != null) {
throw new RuntimeException("Could not create an ssh connection");
}
}
System.out.println("Returning created instance");
return sshInstance;
}
#MessageMapping("/user")
#SendTo("/topic/user")
public UserResponse getResponse(String command) {
SSHManager currInstance = getSSHInstance();
String result = currInstance.sendCommand(command);
return new UserResponse(result);
}
I tried using the "shell" channel instead of "exec" which worked for getting the input and output through standard input and output stream but I could not get the real-time input and output from/back to the websocket and UI. I am not sure how to proceed from here. Any direction on where/what to look would be very helpful.
Here is my code for the SSH terminal through standard input/output stream:
import com.jcraft.jsch.*;
public class Terminal{
public static void main(String[] args){
try{
JSch jsch=new JSch();
String host = "127.0.0.1";
String user = "user";
String password = "pass";
Session session=jsch.getSession(user, host, 5679);
session.setPassword(password);
session.setConfig("StrictHostKeyChecking", "no");
session.connect(10000);
Channel channel=session.openChannel("shell");
channel.setInputStream(System.in);
channel.setOutputStream(System.out);
channel.connect(3*1000);
}
catch(Exception e){
System.out.println(e.getMessage());
}
}
}
To send the command from the UI, I have the following:
function sendCommand() {
if (stompClient != null) {
stompClient.send("/app/user", {}, JSON.stringify({'command': $("#command").val()}));
}
}
If you want to implement an interactive shell, you have to use the "shell" channel, not the "exec" channel. The "exec" channel is intended for automating individual commands.
Some references:
What is the difference between the 'shell' channel and the 'exec' channel in JSch
JSch Shell.java example

Unable to execute commands with Jsch shell channel

I have a test case which connects to my app server using Jsch shell channel. I am able to execute commands using Jsch if I run my test case from test vm. But there is some situation that the test case will run in the app server and do ssh to itself with same credentials. My problem is here. In this case the commands are not executed by Jsch.
Code:
import com.google.common.io.Files;
import com.jcraft.jsch.*;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.*;
import java.sql.Timestamp;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Properties;
/**
* Created by prasantabiswas on 17/03/17.
*/
public class SSHManager
{
private static Session session;
private static Channel channel;
private static ChannelSftp channelSftp;
private static Logger log= LogManager.getLogger(SSHManager.class);
private static HelperClass helperClass=HelperClass.getInstance();
enum ChannelType
{
SHELL,
SFTP
}
public static void openConnection(String remoteHost) throws JSchException,SecurityException
{
if(session == null || !session.isConnected())
{
String userName=helperClass.getSshUser();
log.debug("Remote host: "+remoteHost);
log.debug("SSH user: "+userName);
if(helperClass.getSshAuthMode().equalsIgnoreCase("key"))
{
String key=helperClass.getSshKey();
log.debug("SSH key: "+key);
session=connectWithKey(remoteHost,userName,key);
}
else if(helperClass.getSshAuthMode().equalsIgnoreCase("password"))
{
String password=helperClass.getSshPassword();
log.debug("SSH password: "+password);
session=connectWithPassword(remoteHost,userName,password);
}
else
{
String password=helperClass.getSshPassword();
log.warn("No SSH authentication mode provided. It will try with the default mode that is with password: "+password);
session=connectWithPassword(remoteHost,userName,password);
}
}
else
log.warn("Session already connected. Disconnect first for new session.");
}
private static Session connectWithPassword(String hostname,String username,String password) throws JSchException,SecurityException
{
JSch jSch = new JSch();
Session _session = jSch.getSession(username, hostname, 22);
Properties config = new Properties();
config.put("StrictHostKeyChecking", "no");
config.put("PreferredAuthentications","publickey,keyboard-interactive,password");
_session.setConfig(config);
_session.setPassword(password);
log.info("Connecting SSH to " + hostname + " - Please wait for few seconds... ");
_session.connect();
log.info("Connected!");
return _session;
}
private static Session connectWithKey(String hostname,String username,String key) throws JSchException,SecurityException
{
JSch jSch = new JSch();
log.debug("Key file path: "+key);
jSch.addIdentity(key);
Session _session = jSch.getSession(username, hostname, 22);
Properties config = new Properties();
config.put("StrictHostKeyChecking", "no");
config.put("PreferredAuthentications","publickey,keyboard-interactive,password");
_session.setConfig(config);
log.info("Connecting SSH to " + hostname + " - Please wait for few seconds... ");
_session.connect();
log.info("Connected!");
return _session;
}
private static void createChannel(ChannelType channelType)
{
if(channel == null || !channel.isConnected()){
try
{
if(channelType==ChannelType.SHELL)
{
channel = session.openChannel("shell");
log.info("Shell channel created!");
}
else if(channelType==ChannelType.SFTP)
{
channel = session.openChannel("sftp");
log.info("SFTP channel created!");
}
else
throw new JSchException("Invalid channel type");
channel.connect();
}
catch(JSchException e)
{
log.error("Error while opening channel: "+ e);
}
}
}
public static String executeCommand(List<String> commands,String remoteHost) throws JSchException,IOException
{
log.info("Opening SSH session...");
openConnection(remoteHost);
log.info("Creating shell channel...");
createChannel(ChannelType.SHELL);
log.info("Sending commands...");
PrintStream out = new PrintStream(channel.getOutputStream());
out.println("#!/bin/bash");
for(String command : commands){
out.println(command);
}
out.println("exit");
out.flush();
String output=getChannelOutput(channel);
log.info("Finished sending commands!");
log.info("Closing session...");
close();
log.info("Returning execution output...");
return output;
}
public static void copyFileToRemoteHost(String sourcePath,String destinationPath,String remoteHost) throws SftpException, IOException, JSchException
{
log.info("Opening SSH session...");
openConnection(remoteHost);
log.info("Creating SFTP channel...");
createChannel(ChannelType.SFTP);
log.info("Copying file to remote host: "+remoteHost+" from "+sourcePath+" to "+destinationPath);
channelSftp = (ChannelSftp) channel;
channelSftp.cd(destinationPath);
File sourceFile = new File(sourcePath);
channelSftp.put(new FileInputStream(sourceFile), sourceFile.getName(),channelSftp.OVERWRITE);
log.info("File transfer complete.");
log.info("Closing session...");
close();
}
public static void removeFileFromRemoteHost(String targetPath,String remoteHost) throws JSchException, SftpException, IOException
{
log.info("Opening SSH session...");
openConnection(remoteHost);
log.info("Creating SFTP channel...");
createChannel(ChannelType.SFTP);
log.info("Removing file from remote host: "+remoteHost+" at "+targetPath);
channelSftp = (ChannelSftp) channel;
channelSftp.rm(targetPath);
log.info("File removed");
log.info("Closing session...");
close();
}
private static String getChannelOutput(Channel channel) throws IOException
{
byte[] buffer = new byte[1024];
InputStream in = channel.getInputStream();
String line = "";
Calendar calendar= Calendar.getInstance();
calendar.setTime(new Date());
int minuteBefore=calendar.get(Calendar.MINUTE);
int minuteAfter=0;
while (true)
{
while (in.available() > 0)
{
int i = in.read(buffer, 0, 1024);
if (i < 0)
{
break;
}
line += "\n" + new String(buffer, 0, i);
}
if(line.contains("logout"))
{
break;
}
if (channel.isClosed())
{
break;
}
calendar.setTime(new Date());
minuteAfter=calendar.get(Calendar.MINUTE);
if(minuteAfter-minuteBefore>=3)
break;
try
{
Thread.sleep(1000);
} catch (Exception e)
{
}
}
return line;
}
public static void close()
{
channel.disconnect();
session.disconnect();
log.info("Disconnected channel and session");
}
}
I am using the above class to execute commands on remote host. This is working fine but when I am running it in my local machine with the remoteHost parameter being the localhost, the commands are not getting executed. However it successfully logs in which I can see in the log but cannot execute the commands which are sent throug print stream. It is stuck for sometime after "Sending commands..." and then return the log which only shows the command prompt not all the commands.
Suppose my ssh username is root and I am sending two commands : su - admin, txe restart and the remote host and the host I am running the test is same that is my localhost. I should get output like
#!/bin/bash
su - admin
txe restart
exit
Last login: Thu Apr 13 23:17:10 2017 from 10.10.4.46
[root#platform15 ~]# #!/bin/bash
[root#platform15 ~]# su - admin
id: cannot find name for group ID 491000000
admin#platform15(ERS):~> txe restart
Stopping txe...
.
.
OK
Starting txe...OK (PID: 13083)
admin#platform15(ERS):~> exit
logout
But I get this
Last login: Thu Apr 13 23:34:42 2017 from 10.10.4.27
[root#platform15 ~]#
This is only happening when I am running the test in localhost with remoteHost set to localhost. Otherwise it is working fine is remoteHost is set to some other host.
Any suggestion on this?

Exit status 4 received for JSch exec channel

I'm coding an app to manage some remote devices. I have trouble using JSch to restart dhcpdaemon. I do:
#Override
public void createSession(String hostname) throws JSchException {
this.session = jsch.getSession(username, hostname, 22);
this.session.setPassword(password);
java.util.Properties config = new java.util.Properties();
config.put("StrictHostKeyChecking", "no");
session.setConfig(config);
session.connect();
this.hostname = hostname;
}
private final String RESTART_COMMAND = "/etc/init.d/isc-dhcp-server restart";
public int reloadDHCPDaemon() throws IOException, JSchException, InterruptedException {
int exitStatus = -1;
String command = RESTART_COMMAND;
ChannelExec channel = (ChannelExec) session.openChannel("exec");
channel.setPty(true);
channel.setCommand(command);
channel.connect();
Thread.sleep(1000);
if (channel.isClosed() == true) {
exitStatus = channel.getExitStatus();
channel.disconnect();
}
return exitStatus;
}
I'm getting 4 as an exit status no matter if the command ended successfully or not. The same code but with other commands, for example cp works fine. Can somone help me?

Remote SSH is stop giving response after couple of iterations

Written java program to establish remote SSH connection and send commands to that SSH device with the help of JSch. And that code need to run infinite loop means code will establish connection with first device, sends commands to that and moves to second device. After completion of sending commands to tenth device code will start again from the first device. For two to three iterations it is working fine. But, next iteration onwards not getting any response from device but connection establishment was successful and code was stuck at that device. Please help to solve this, there is no issue from device side. If it is stuck also, code needs to wait some time and start establish connection with next device.
Code is:
public class SSHManager {
private JSch shell;
private Session session;
private Channel channel;
private static OutputStream out;
private static InputStream in;
public void connect(String username, String password, String host,int port)
throws JSchException, IOException, InterruptedException {
shell = new JSch();
session = shell.getSession(username, host, port);
session.setPassword(password);
session.setConfig("StrictHostKeyChecking", "no");
session.connect();
channel=session.openChannel("shell");
channel.setInputStream(null);
channel.setOutputStream(null);
in=channel.getInputStream();
out =channel.getOutputStream();
((ChannelShell)channel).setPtyType("vt102");
channel.connect();
}
public String send(String command) throws IOException, InterruptedException {
byte[] tmp=new byte[1024];
out.write((command+";echo \"z4a3ce4f3317Z\"").getBytes());
out.write(("\n").getBytes());
out.flush();
String result = "";
while(true){
while(in.available()>0){
int i=in.read(tmp, 0, 1024);
if(i<0)
break;
result = result + (new String(tmp, 0, i));
}
if(result.indexOf("z4a3ce4f3317Z") != -1){
break;
}
try{Thread.sleep(300);}catch(Exception ee){}
}
return result;
}
public boolean isConnected() {
return (channel != null && channel.isConnected());
}
public void disconnect() {
if (isConnected()) {
channel.disconnect();
session.disconnect();
}
}
}
class Test {
final static SSHManager client = new SSHManager();
public static void main(String[] args) throws JSchException, IOException, InterruptedException {
while(true) {
try
{
for (int i=1;i<=10;i++)
{
String ipaddr = "10.35.57."+i;
System.out.println(ipaddr);
client.connect("root", "root", ipaddr, 22);
client.send("cd /\n");
Thread.sleep(3500);
client.send("rm sett*");
// Send five more commands to that device
}
}
catch(Exception e)
{
System.out.println(e);
}
Thread.sleep(150*1000);
}
}
}

Categories