Java Jsch Changing User on Remote MAChine and Execute Command

Java JSch changing user on remote machine and execute command

You have several problems here.

First, each channel in a SSH connection is independent of the other ones, and the command in each exec channel is executed in its own shell (command line interpreter). So any changes you are doing in one channel have no effect at all to the other channels. You also can't do stuff like this:

runSshCommand("cd Documents", session);
runSshCommand("ls -l", session);

(Actually you can do this, but it will not show the contents of the Documents directory, but of the home directory.)

For cd, you can work around by passing both commands as one "command", to be used in the same exec channel:

runSshCommand("cd Documents; ls -l");

(Instead of the ; you can also use a line break \n to separate the commands, or whatever else your shell accepts.)

For su this will not work, where we come to the second problem.

su is not a command which changes the state of the current shell (like cd), but a command which opens a new shell inside the existing one. It will only return to the outer shell when you leave the shell started by su (e.g. by exit, logout or end-of-file), and then you are again the same user as before.

To pass commands to the "inner shell", you'll have to pass them to the shells input. Or use the -c (--command) argument of su:

runSshCommand("su -c 'tail -1 ~/mylog.log' - john ",session);

You might then run in the third problem: su will ask for john's password, and might refuse to read it from the standard input, but try to read it from the terminal. And your channel has no pseudo-terminal. You can try to use cannel.setPty(true) and then actually write your password to the output stream, though I'm not sure that this will work.

Alternatives: Instead of su -c you can use sudo, which can be configured not to ask for a password for certain commands and users (otherwise you'll have the same terminal problem again). Or you could directly log in as john, or make the logfile readable for tom. (Also, I hope your real password is better than the one in your source code.)

Execute a local script on a remote machine through Java JSch library

You cannot execute a local script on a server.

You have two options (that you are obviously aware of):

  • Upload the script and execute it (and delete afterwards, if needed).
  • Execute the contents of the script. As you already know, just concatenate all lines with ;.

    Though be careful about the for and if. There should be no ; after the do and then.

    cd /log ; searchString='<Error ErrorCode="java.sql.SQLException"' ; files=`find . -mmin -60 -type f -exec ls {} +` ; echo `pwd` ; echo `hostname` ; echo ${files} ; for file in ${files[@]} ; do if grep -Fq "$searchString" $file ; then echo "String found in $file" ; fi ; done
  • Similarly to the previous approach, you can execute the bash -s and write the contents of the script (the commands) to its input.

    ChannelExec channelExec = (ChannelExec)session.openChannel("exec");
    channelExec.setCommand("bash -s");
    channelExec.connect();
    OutputStream out = channel.getOutputStream();
    FileInputStream fis = new FileInputStream("testScript.sh");
    byte[] buf = new byte[1024];
    while (true)
    {
    int len = fis.read(buf, 0, buf.length);
    if (len <= 0) break;
    out.write(buf, 0, len);
    }

    The above is basically copied from seemingly unrelated SCP upload example - That example actually feeds a local file to remote scp process - So it's actually pretty related.


Ntb, "echo `pwd`" and "echo `hostname`" do not make sense. Use "pwd" and "hostname" directly.

Run a command over SSH with JSch

The following code example written in Java will allow you to execute any command on a foreign computer through SSH from within a java program. You will need to include the com.jcraft.jsch jar file.

  /* 
* SSHManager
*
* @author cabbott
* @version 1.0
*/
package cabbott.net;

import com.jcraft.jsch.*;
import java.io.IOException;
import java.io.InputStream;
import java.util.logging.Level;
import java.util.logging.Logger;

public class SSHManager
{
private static final Logger LOGGER =
Logger.getLogger(SSHManager.class.getName());
private JSch jschSSHChannel;
private String strUserName;
private String strConnectionIP;
private int intConnectionPort;
private String strPassword;
private Session sesConnection;
private int intTimeOut;

private void doCommonConstructorActions(String userName,
String password, String connectionIP, String knownHostsFileName)
{
jschSSHChannel = new JSch();

try
{
jschSSHChannel.setKnownHosts(knownHostsFileName);
}
catch(JSchException jschX)
{
logError(jschX.getMessage());
}

strUserName = userName;
strPassword = password;
strConnectionIP = connectionIP;
}

public SSHManager(String userName, String password,
String connectionIP, String knownHostsFileName)
{
doCommonConstructorActions(userName, password,
connectionIP, knownHostsFileName);
intConnectionPort = 22;
intTimeOut = 60000;
}

public SSHManager(String userName, String password, String connectionIP,
String knownHostsFileName, int connectionPort)
{
doCommonConstructorActions(userName, password, connectionIP,
knownHostsFileName);
intConnectionPort = connectionPort;
intTimeOut = 60000;
}

public SSHManager(String userName, String password, String connectionIP,
String knownHostsFileName, int connectionPort, int timeOutMilliseconds)
{
doCommonConstructorActions(userName, password, connectionIP,
knownHostsFileName);
intConnectionPort = connectionPort;
intTimeOut = timeOutMilliseconds;
}

public String connect()
{
String errorMessage = null;

try
{
sesConnection = jschSSHChannel.getSession(strUserName,
strConnectionIP, intConnectionPort);
sesConnection.setPassword(strPassword);
// UNCOMMENT THIS FOR TESTING PURPOSES, BUT DO NOT USE IN PRODUCTION
// sesConnection.setConfig("StrictHostKeyChecking", "no");
sesConnection.connect(intTimeOut);
}
catch(JSchException jschX)
{
errorMessage = jschX.getMessage();
}

return errorMessage;
}

private String logError(String errorMessage)
{
if(errorMessage != null)
{
LOGGER.log(Level.SEVERE, "{0}:{1} - {2}",
new Object[]{strConnectionIP, intConnectionPort, errorMessage});
}

return errorMessage;
}

private String logWarning(String warnMessage)
{
if(warnMessage != null)
{
LOGGER.log(Level.WARNING, "{0}:{1} - {2}",
new Object[]{strConnectionIP, intConnectionPort, warnMessage});
}

return warnMessage;
}

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();
}

public void close()
{
sesConnection.disconnect();
}

}

For testing.

  /**
* Test of sendCommand method, of class SSHManager.
*/
@Test
public void testSendCommand()
{
System.out.println("sendCommand");

/**
* YOU MUST CHANGE THE FOLLOWING
* FILE_NAME: A FILE IN THE DIRECTORY
* USER: LOGIN USER NAME
* PASSWORD: PASSWORD FOR THAT USER
* HOST: IP ADDRESS OF THE SSH SERVER
**/
String command = "ls FILE_NAME";
String userName = "USER";
String password = "PASSWORD";
String connectionIP = "HOST";
SSHManager instance = new SSHManager(userName, password, connectionIP, "");
String errorMessage = instance.connect();

if(errorMessage != null)
{
System.out.println(errorMessage);
fail();
}

String expResult = "FILE_NAME\n";
// call sendCommand for each command and the output
//(without prompts) is returned
String result = instance.sendCommand(command);
// close only after all commands are sent
instance.close();
assertEquals(expResult, result);
}

Executing ssh on remote server to jump to another server and execute further commands there using Java and JSch

Try executing pwd;ssh -tt user@ip;pwd in a normal SSH terminal client connected to the RM1. It won't work, So it won't work in Java either.

To do the "jump", you should use port forwarding (See JSch JumpHosts example), instead of executing ssh on the jump server.

JSch - How to issue commands as the user I have switched to

Extending @Martin's answer, the other solution would be to open the channel in "shell" mode which would maintain the session. Like this...

Channel channel = session.openChannel("shell")

Ssh with JSch to a remote machine that asks twice for username and password

Your ssh commands starts a shell session. The prompts for the credentials are just regular I/O prompts, as any other. Nothing credentials-specific. So you should provide the input the way, you would provide any other input – by writing it to the shell input stream.



Related Topics



Leave a reply



Submit