A więc dla potomnych zdalne CLI nasłuchujące tylko lokalnie i zabezpieczony serwer SSH, który zajmuje się przekierowaniem połączenia na ten CLI:
Serwer nasłuchujący na połączenia od localhosta:
import java.io.IOException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.util.concurrent.atomic.AtomicBoolean;
import org.apache.log4j.Logger;
public class ConsoleServer extends Thread {
private final int port;
private final int lenghtOfQuere;
public AtomicBoolean listening;
private static final Logger log = Logger.getLogger("Konsola zdalna");
public boolean isListening() {
return listening.get();
}
public void setListening(boolean listening) {
this.listening.set(listening);
}
public ConsoleServer() {
port = Integer.parseInt(ServerProperties.getProperty("console_port"));
log.info("Uruchamiam serwer bez szyfrowania na porcie " + port);
lenghtOfQuere = 100;
listening = new AtomicBoolean(true);
}
@Override
public void run() {
ServerSocket serverSocket = null;
try {
serverSocket = new ServerSocket(port, lenghtOfQuere, InetAddress.getByName("localhost"));
while (listening.get()) {
new RemoteConsoleHandler(serverSocket.accept()).start();
}
serverSocket.close();
} catch (IOException e) {
log.error("Nie mogę uruchomić serwera z powodu błędu", e);
try {
serverSocket.close();
} catch (Exception ex) {
}
CriticalBehaviour.closeOnFatal(ConsoleServer.class, e);
}
}
}
Obsługa poleceń:
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.Socket;
import java.util.LinkedHashMap;
import org.apache.log4j.Logger;
public class RemoteConsoleHandler extends Thread {
private Socket connection = null;
private static final Logger log = Logger.getLogger("Obsługa konsoli zdalnej");
private PrintWriter out;
private BufferedReader in;
private String prompt;
private CommandStore commandStore;
public RemoteConsoleHandler(Socket s) {
this.connection = s;
setPrompt("serwer");
commandStore = new CommandStore();
}
private void setPrompt(String prompt) {
this.prompt = prompt.concat("# ");
}
private String getPrompt() {
return prompt;
}
@Override
public void run() {
try {
InetAddress adr = connection.getInetAddress();
int remotePort = connection.getPort();
log.debug("Rozpoczynam obsługę połączenia do konsoli zdalnej z adresu " + adr + " oraz portu " + remotePort);
out = new PrintWriter(connection.getOutputStream(), true);
in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
out.format("Nacisnij ENTER, aby rozpoczac"); // jeśli użytkownik łączy się telnetem, to pierwszy komunikat zaczyna się od śmieci, więc warto go ominąć
in.readLine();
out.format(getPrompt());
String command = in.readLine();
if (command != null) {
while (!command.equalsIgnoreCase("exit")) {
if (command.length() > 0) {
log.trace("Użytkownik " + adr.getHostName() + "/" + remotePort + " wydał polecenie: " + command);
String answer = handleCommand(command);
send(answer);
}
out.format(getPrompt());
command = in.readLine();
}
log.debug("Rozłączam użytkownika połączonego z adresu " + adr + " oraz portu " + remotePort);
}
out.close();
in.close();
connection.close();
} catch (NullPointerException e) {
log.warn("Użytkownik rozłączył się niespodziewanie");
} catch (Exception e) {
log.error("Błąd zdalnej konsoli użytkownika", e);
}
}
private String handleCommand(String command) {
String status = "Nie rozpoznalem polecenia :(";
if (command.equalsIgnoreCase("help")) {
commandHelp();
status = "";
} else {
String newStatus = commandStore.execute(command);
if (newStatus != null && newStatus.length() > 0) {
status = newStatus;
}
}
return status;
}
private void commandHelp() {
LinkedHashMap<String, String> helpMessage = commandStore.getHelp();
String value = "Dostepne sa nastepujace polecenia:\n\n";
for (String key : helpMessage.keySet()) {
value = value.concat(key + "\n");
value = value.concat(helpMessage.get(key));
value = value.concat("\n\n---------\n\n");
}
send(value);
}
void send(String msg) {
try {
if (msg != null && msg.length() > 0) {
msg = msg.replaceAll("\n", "\n\r");
log.trace("Wysyłam odpowiedź: \n" + msg);
out.println(msg);
}
} catch (Exception ioException) {
log.error("Nie mogę wysłać odpowiedzi z powodu błędu", ioException);
}
}
}
No i najciekawsze - tunel SSH:
import java.util.EnumSet;
import org.apache.log4j.Logger;
import org.apache.sshd.SshServer;
import org.apache.sshd.server.PasswordAuthenticator;
import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider;
import org.apache.sshd.server.session.ServerSession;
import org.apache.sshd.server.shell.ProcessShellFactory;
import org.apache.sshd.server.shell.ProcessShellFactory.TtyOptions;
public class TunnelSSH implements PasswordAuthenticator {
private static final Logger log = Logger.getLogger("Tunel SSH dla konsoli zdalnej");
public void openTunnel() {
try {
SshServer sshd = SshServer.setUpDefaultServer();
sshd.setPort(Integer.parseInt(ServerProperties.getProperty("secure_console_port")));
sshd.setKeyPairProvider(new SimpleGeneratorHostKeyProvider(ServerProperties.getProperty("local_ssh_keyset_filename")));
EnumSet<TtyOptions> enumSet = EnumSet.noneOf(TtyOptions.class);
enumSet.add(TtyOptions.Echo);
enumSet.add(TtyOptions.ONlCr);
enumSet.add(TtyOptions.ICrNl);
sshd.setShellFactory(new ProcessShellFactory(new String[] { "nc", "localhost", ServerProperties.getProperty("console_port") }, enumSet));
sshd.setPasswordAuthenticator(this);
sshd.start();
} catch (Exception e) {
log.error("Nie mogę uruchomić bezpiecznego przekierowania dla portu konsoli zdalnej", e);
}
}
public boolean authenticate(String userName, String password, ServerSession session) {
return UserCache.getInstance().checkUser(userName, password);
}
}
Widziałem podobne wątki bez odpowiedzi, więc liczę, że się przyda.