Próbując rozwiązać problem opisany tu: Bezpieczna zdalna konsola CLI
Napisałem sobie własną konsolę CLI, do której można podłączyć się choćby telnetem, a teraz chciałem stworzyć własny serwer SSH, który po połączeniu będzie przenosił użytkownika do tej konsoli.
Poniżej najważniejsze rzeczy:
Konsola CLI po socketach:
serverSocket = new ServerSocket(3001, lenghtOfQuere, InetAddress.getByName("localhost"));
while (listening) {
new RemoteConsoleHandler(serverSocket.accept()).start();
}
serverSocket.close();
oraz:
public RemoteConsoleHandler(Socket s) {
this.connection = s;
setPrompt("geodet");
commandStore = new CommandStore();
}
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("Naciśnij ENTER, żeby rozpocząć"); // 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);
}
}
Pomijam resztę, bo ta część działa pięknie. Gorzej z SSHD.
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;
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(3000));
sshd.setKeyPairProvider(new SimpleGeneratorHostKeyProvider("plik_z_kluczami"));
sshd.setShellFactory(new ProcessShellFactory(new String[] { "telnet", "localhost", "3001"}));
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.checkUser(userName, password);
}
Samo w sobie też działa. Próbowałem zamiast
sshd.setShellFactory(new ProcessShellFactory(new String[] { "telnet", "localhost", 3001 }));
wpisać
sshd.setShellFactory(new ProcessShellFactory(new String[] { "/bin/bash", "-i", "-l"}));
i wtedy mam dostęp do zwykłego basha.
Nie pisałbym tego wszystkiego, gdyby nie pewne "ale". Po podłączeniu poprzez SSH i uwierzytelnieniu dostaję albo basha, albo od razu połączenie ze swoją konsolą, jednak gubią mi się znaki powrotu karetki. Wpisuję polecenie i wszystko się przesuwa, tzn. kursor drukuje linijkę niżej, ale nie wraca do początku.
Oprócz tego w przypadku wywołania telneta po połączeniu (z basha lub zamiast basha) nie widzę w dodatku tego, co piszę, a moja konsola dostaje wpisany tekst z jakimiś białymi znakami, których nie potrafi czytać, ani nawet wyświetlić. Moje logowanie wyświetla wpisaną komendę i normalnie powinna być ona obsłużona, a jednak nie może jej porównać ze wzorcem (zwykły equalsIgnoreCase). Domyślam się, że telnet przeszedł w tryb character, a chyba nie powinien (http://fooworks.com/apache-mina-user/2011-09/msg00032.html), bo jak łączę się bezpośrednio z socketem na którym mam konsolę (bez SSH w roli pośrednika) i sam ustawiam w telnecie "mode character", to też nie widzę, co piszę i też mam białe znaki (choć nie wpływa to na powrót karetki).
Walczę z tym już od pewnego czasu, ale oficjalna dokumentacja od SSHD jest... hmmm... uboga, więc niezbyt mi się to udaje. Może ktoś miał taki problem lub ma pomysł, jak to ugryźć?