Java and SSH: maintaining a connection - java

Goal: Remote control ssh server with one connection and multiple sessions or one persistent session.
Issue 1: I currently use sshj to do some remote control through SSH and it works well but I cant seem to get it to handle prompts correctly. (the host doesnt provide true root, just sudo -i so I need to log in first).
Issue 2: I downloaded ExpectJ to handle the prompt but I can't for the life of me figure out how to maintain a session once I have logged in and authenticated as root.
The current Hack solution requires that I re-log in every time:
public class Expect {
Spawn shell;
ExpectJ exp;
String host;
int port;
String username;
String passwd;
boolean sudo = false;
public Expect(String host,int port,String username,String passwd) throws IOException, TimeoutException, ExpectJException{
exp = new ExpectJ(5);
this.host = host;
this.port = port;
this.username = username;
this.passwd = passwd;
shell = exp.spawn(host, port, username, passwd);
shell.send("sudo netstat -natvp | grep Xtightvnc\n");
System.out.println(shell.getCurrentStandardOutContents());
try{
shell.expect("[sudo] password for #######:");
shell.send(passwd+"\n");
}
catch (IOException ex){
String err = ex.toString();
if(!err.equals("java.io.IOException: End of stream reached, no match found")){
throw new IOException(ex);
}
}
}
Question 1: can sshj be used to "expect" password prompts? I couldnt find any documentation alluding to that type of control.
Quetsion 2: How can I modify the above Expect code to maintain a persistent connection that I can make multiple calls to? I want to be able to continue to interact once I have reached the state of authenticating as root but the Spawn always closes once the initial command has been sent.

Question 1: can sshj be used to "expect" password prompts? I couldnt find any documentation alluding to that type of control.
Mainly what sshj provides is a handle on the shell or command's I/O streams, and methods to get stuff like the exit status.
Quetsion 2: How can I modify the above Expect code to maintain a persistent connection that I can make multiple calls to? I want to be able to continue to interact once I have reached the state of authenticating as root but the Spawn always closes once the initial command has been sent.
Multiplexing sessions, even concurrent session over a single SSH connection is supported. But note that you can only have one session for one shell/command/subsystem.
Which kind of Session are you using, i.e. Session.Shell (via session.startShell) or Session.Command (via session.exec)? In the latter case, once you're done executing a command, the session is meant to close and this is the expected behavior.
Normally wouldn't recommend Shell over Command but since you are equipped with ExpectJ which I guess can deal with prompts and such, you might want to go that route. If there are problems with echoing of characters or such, play with the PTY options (instead of session.allocateDefaultPTY call session.allocatePTY which takes a whole bunch of args, have a look at the source for allocateDefaultPTY).
Code examples: Shell, Command
Also, if you narrow the problem down to a bug with sshj, do report it on the issue tracker :)

Related

SSHJ How change current user with command su <user>?

I use sshj library for communication with linux from my Java app.
And I need change user with command: su
SSHClient ssh = new SSHClient();
ssh.connect(host);
ssh.authPassword(login, password);
Session s = ssh.startSession();
Command cmd = s.exec("su my_user");
List<String> resultLines = IOUtils.readLines(cmd.getInputStream(), Charset.defaultCharset());
But, in IOUtils.readLines(cmd.getInputStream(), ... app is waits and does not go next.
Thanks for any help.
So, a couple of things for you to do to check this. Given what you're describing, the IOUtils.readLines() method is never returning due to the InputStream never reaching the end of stream.
The only way I've ever seen this happen is if the command you've run is stuck awaiting input. My bet would be that it's prompting for a password and is sat waiting for a response that will never come.
The following steps should help you debug:
1) Add the following line before the exec command to allocate a pseudo-terminal and ensure that any prompts will definitely be written to the InputStream:
s.allocateDefaultPTY();
2) Change your output handling to print the output character by character to the console, instead of waiting for the end of stream to be reached. Something like the following would do the trick:
InputStream in = cmd.getInputStream();
System.out.println("Starting SSH output.");
int cInt;
while ((cInt = in.read()) >= 0) {
char c = (char) cInt;
System.out.print(c);
}
This will allow you to see in your console exactly what the prompt is that is causing your command to never finish executing.
If there is a prompt there, the best ways I've found to respond to them are either to:
1) use an expect script to look for the prompt and respond to it.
2) If you'd prefer to keep it within your java code, use the session.startShell() method instead of session.exec() in order to allow you to open a full shell session where you can use Input and Output streams to send your commands and monitor the output for prompts then handle them by writing your response to the provided OutputStream. This is definitely the longer and more involved approach however!

Socket connection runs fine in stand alone "main(..)" class, but times out in servlet enviornment

Let me explain the problem by a scenario :
1) User visit my page "www.proxy4html.com" , fills up the form:
web-address: |www.google.co.in |
2) click submit.
3) Gets google home page with "www.proxy4html.com" in browser address bar.
Now to fetch the contents from web I am using java.net.HttpURLConnection (although to resolve this I have tried several other options too). The code works as expected if it runs stand alone
(i.e while running through public static void main(String..).. thread, it fetched whatever the html is on given web address).
But the same when runs under the Servlet environment it throws
java.net.ConnectException: Connection timed out: connect
Here is the code:
public void write(String urlString, PrintWriter writer) {
URL url;
try {
url = new URL(urlString);
HttpURLConnection huc = (HttpURLConnection) url.openConnection();
HttpURLConnection.setFollowRedirects(false);
huc.setConnectTimeout(15 * 1000 * 60);
huc.setReadTimeout(15 * 1000 * 60);
huc.setRequestMethod("GET");
huc.connect();
InputStream input = url.openStream();
logger.info("got input stream");//I never reach here in servlet env :(
int i = 0;
while((i = input.read()) != -1) {
writer.write(i);
System.out.print((char)i);
}
input.close();
} catch (ConnectException e) {
logger.log(Level.SEVERE, "", e);
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
The code example given may not appear clean but point to be taken is that when the above method is invoked from Servlet passing a proper URL (and whatever outputstream), the connection is never made.
I am running all this on my tomcat which is on my local machine, which is in some corporate network (you may not want get any conclusion concerning firewall because same code is running under a p.s.v.main).
please help....
Thanks in advance.
Edit: I tried somewhat same at home and it worked fine as expected. I guess this question falls more into operation system domain rather than java. So I will like to rephrase my question again:
Is operating system (Window 7) is somehow preventing tomcat to make Http requests?
Is their any configuration under Window 7 that doesn't allow application to make Http Request other than browser?
I have seen in times that Mozilla Firefox (which I am running from a directory which is copied instead of being installed i.e may not have admin privileges and doesn't have a window registry) never able to do regular update to itself.
I understand that you prefer to insist that this is not a firewall issue (as the same code runs well as a standalone Java program), but I'm willing to take a risk here.
If you're absolutely sure that the very same code is being run in both cases (standalone case, and Tomcat case), there still is a possibility that it's a firewall issue.
The built-in firewall in Windows 7 assigns outbound rules per process per port. Is it possible that your standalone program runs with a JVM that has been allowed outbound access, whereas your Tomcat server runs with a JVM that hasn't been allowed outbound access?
In other words, are you absolutely confident that your standalone program, and your Tomcat server, are being run by the very same JVM? Very same java.exe? Under Windows, it's possible that your standalone program is actually being run by javaw.exe.
(You didn't mention whether you tried to completely disable the firewall. If you can disable the firewall completely, I'd suggest you do so, for the purpose of troubleshooting)

How to simulate server down/available in java?

Our application has server/client side. The client supports both offline and online work mode.
So I need to test the client when server down, regain connective.
Question comes. How to simulate server down. Use codes to switch from down to ready, or from ready to down state.
Thanks in advance.
Joseph
update:
Actually, I could not extend the server interface to response the incorrect status. In my test scenario, the server is transparent. So incorrect url + port is a solution to do this.
But I could not modify the url when the session is valid. Another method is modify the hosts file to do this. I have to face the privilege issue in Windows.
Depends on what you mean by "server down". Possible options are:
Write a fake/dummy server that can return error messages corresponding to being down for test purposes.
Change the IP address of the server that your client looks for to a non-existing one so that it will think that the server is entirely down.
The basic idea is to mock the behavior of your server somehow. You could use mocking frameworks to do so.
You could also create manual mocks for testing purposes. Let the "proxy" of the server on the client implement this interface:
public interface IServer
{
bool foo();
}
You could create a "fake" implementation of that server and return whatever you'd like
public class FakeOfflineServer implements IServer
{
public bool foo()
{
// throw some exception here.
}
}
This approach allows you to fake different scenarios (no network connectivity, invalid credentials, etc.)
You could also use composition to switch from up to down in your tests:
public bool FakeServer implements IServer
{
private IServer offline = new FakeOfflineServer();
private IServer online = new Server();
public bool isUp = false;
private IServer getServer()
{
return isUp ? online : offline;
}
public bool foo()
{
return getServer().foo();
}
}
While testing server down, give any incorrect URL OR Port (Prefered). For recovery give the correct URL/Port.
This depends where you are testing. If you're unit testing, the best option is the mocking suggested by Bryan Menard.
If you're testing in an integration or production environment, You can actually cut the connection between you and the server.
Depending upon your operating system, you can do this in a number of ways.
For Windows based systems, Fiddler is fantastic. You can simulate almost anything, including delays on the requests and indeed just throwing requests away. It does not require admin access for Windows.
For linux based systems, one technique I've used in the past is to use a proxy server, or cut the port at operating system level. You can do this using iptables for instance:
To deny access to a particular port (25 in this case)
/sbin/iptables -I OUTPUT -p tcp --dest 127.0.0.1 --dport 25 -j DROP
and to allow it again:
/sbin/iptables --delete OUTPUT 1
You'll need root acces for this to work, but it does have the advantage that you don't need to touch your server or client configuration.
To emulate the server down case, you could write a ServerAlwaysDown class extending your actual server, but throwing ServerException (HTTP 500) for every connection.
If you want to be thorough use always the closest you have to a production environment for the tests, put client and servers in different machines and cut the connection, then restore it.

Java Serversocket and Bash Connection

I was wondering if there was a way to open a pipe between a java serversocket and a bash tcp pipe. If so how would you do this. I know how to setup the java server side but how do you open the socket connection via bash or even a windows cmd prompt. Basically i want to pipe standard input into the tcp connection and receive standard output from it.
Any examples apprechiated!
-TJ
A ServerSocket can't do anything expect accept incoming connections, and create new Sockets which you use as endpoints (you don't use the ServerSocket for any send or recv operations). This is fine, but be aware of what side of the connection needs to accept connections. I'd assume it would be Bash since this seems to 'own' the pipe, so you'd use a Socket instead and connect that.
Use any of the SSH tools (there are a bunch opensources, e.g., ganymade) Ref:
http://java-source.net/open-source/network-clients/ganymed-ssh-2-for-java
I have not used this particular one, but other ssh clients to do what you want.
PS: You really do not need a ServerSocket for this, but use of threads is helpful.
Netcat is usually used to open TCP connections from a Unix shell. Bash (or any other shell I'm aware of) doesn't support it directly. There seem to exist a Windows port of Netcat too, but I haven't used it.
But it's a much better idea to just use SSH, as no special setup is required on the Java side in this case - you'll just get your standard I/O streams redirected to the SSH client automatically, with free encryption and optional compression (ssh -C) as bonuses. The disadvantage is that it would require installing an SSH server on the Java side and an SSH client on the connecting side.
Here is an example - the classes Connection, Session, etc. are defined in the imports.
At the time we were using the trilead SSH, and I dont think it is available publicly any more, but you can find a lot of other libraries on the web, e.g., http://linuxmafia.com/ssh/java.html
The point is, we did not have to open a serversocket to (1) establish an SSH session, (2) do a password authentication, (3) run some command at the user's login shell, and (4) grab its stdout. I believe most SSH libraries will give you this functionality. Hope this help, - M.S.
public void runCommand (String uName, String pWord, String cmd) throws Exception {
Connection conn = new Connection ("localhost");
conn.connect();
boolean authenticated = conn.authenticateWithPassword (uName, pWord);
if (!authenticated)
throw new Exception ("Authentication failure");
Session sess = connection.openSession();
sess.execCommand (cmd);
BufferedReader br = new BufferedReader (new InputStreamReader (
new StreamGobbler (sess.getStdout())));
for (String s = br.readLine() ; s != null ; s = br.readLine()) {
// do stuff with s
}
br.close();
sess.close();
conn.close();
}

Can anyone recommend a java FOSS server that does all the typical protocols (ssh etc)

I would like to add the ability for users to telnet into my app where by they can type in commands etc. I would like a simple Java Telnet server whereby i can provide the authentication strategy.
By "telnet" i am not referring what lib provides telnet connectivity but something a little more. I want the boring protocol stuff done along with a shell that has hooks that i can use to process and execute command lines. Where or what these commands are, is of course my implementation.
This q is not about how to start a socket server or other protocol plumbing type.
In my experience, telnet is telnet, ssh is ssh... if you want the latter then you might look at something like: http://mina.apache.org/sshd/
I've never used it, though.
You could also use the native operating system SSH (if it has it) to tunnel and then run regular telnet on the server. Creating your own telnet access isn't at all complicated if it's just a custom command shell. And as the other poster mentions, there are libraries that make it easy.
...but ssh would be more secure and more straight-forward.
Telnet servers only do telnet.
You might find http://telnetd.sourceforge.net/ suitable.
"Target Audience:
Developers that want to integrate reasonable telnet access into their application."
What exactly do you mean by "telnet into my app where by they can type in commands etc."?
Since both telnet and ssh requires some application to run on the remote machine (most often an instance of a command shell), they really only provide the transport mechanism for the commands. Telnet in particular can be (and has been) abused to send general text-commands over a tcp connection. You can browse the web by using the command telnet www.target-domain.com 80 and manually enter all the http protocol stuff, but i wouldn't recommend it. ssh is just the same, although it adds ssl/tls security to the channel.
Therefore, what I guess you want is something like this:
import java.io.*;
import java.net.*;
public class telnettest {
public static void main(String[] args) throws IOException {
Socket echoSocket = null;
PrintWriter out = null;
BufferedReader in = null;
try {
//Open a listening socket on port 8022 and wait for a connection
echoSocket = new ServerSocket(8022).accept();
System.out.println("connection established");
//Get input and output streams
out = new PrintWriter(echoSocket.getOutputStream(), true);
in = new BufferedReader(new InputStreamReader(
echoSocket.getInputStream()));
} catch (IOException e) {
System.err.println("Couldn't get I/O for "
+ "the connection");
System.exit(1);
}
//Read lines from the input stream (corresponding to user commands)
String userInput;
while ((userInput = in.readLine()) != null) {
//For each line entered, just output it to both remote and local terminal
out.println("echo: " + userInput);
System.out.println("echo: " + userInput);
}
//Clean up connections.
out.close();
in.close();
echoSocket.close();
}
}
which is a shameless edit of this tutorial example.
I'm not sure about ssh login, but I suspect you could get there using javax.net.ssl.SSLServerSocket instead of ServerSocket.
What remains is to do something sensible with the user input, instead of just throwing it back in their faces.
Depeding on the amount of commands and their parameters, you can either do the command parsing yourself, or find a library that handles it for you.

Categories

Resources