Własny serwer SSH i znak końca linii

Własny serwer SSH i znak końca linii
KK
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 135
0

Próbując rozwiązać problem opisany tu: http://4programmers.net/Forum/Java/213375-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:

Kopiuj
	serverSocket = new ServerSocket(3001, lenghtOfQuere, InetAddress.getByName("localhost"));
			while (listening) {
				new RemoteConsoleHandler(serverSocket.accept()).start();
			}

			serverSocket.close();

oraz:

Kopiuj
	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.

Kopiuj
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

Kopiuj
sshd.setShellFactory(new ProcessShellFactory(new String[] { "telnet", "localhost", 3001 }));

wpisać

Kopiuj
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źć?

KK
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 135
0

OK, działa.
Chodziło o ustawienie zmiennych definiujących zachowanie konsoli -> drukowanie komunikatów i postępowanie z końcem linii dla wejścia i wyjścia. Zamieniłem też telnet na nc, ale to już nie miało znaczenia. Konkretniej ustawiłem:

Kopiuj
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));

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.