Mam problem, który mnie zaskoczył.PHP Telnet/SSH dynamiczny login
Próbuję zautomatyzować logowanie interfejsu CLI do routera i uruchomić niektóre polecenia uzyskane przez stronę sieci Web. Jednak nie wiem, czy router ma włączone telnet lub SSH (może być jeden, drugi lub oba) i mam listę możliwych kombinacji nazwy użytkownika/hasła, które muszę spróbować uzyskać dostęp.
Och, i nie mogę zmienić ani typu protokołu, ani referencji na urządzeniu, więc to naprawdę nie jest opcja.
Udało mi się dowiedzieć, jak zalogować się do routera za pomocą znanego protokołu i poświadczeń logowania i uruchomić niezbędne polecenia (zawarte poniżej), ale nie wiem, czy powinienem użyć bloku if/else do pracy poprzez decyzje telnet/ssh, lub czy zmiana switcha może być lepsza? Czy użycie Expect wewnątrz PHP byłoby prostszym sposobem na przejście?
function tunnelRun($commands,$user,$pass, $yubi){
$cpeIP = "1.2.3.4";
$commands_explode = explode("\n", $commands);
$screenOut = "";
$ssh = new Net_SSH2('router_jumphost');
if (!$ssh->login($user, $pass . $yubi)) {
exit('Login Failed');
}
$ssh->setTimeout(2);
$ssh->write("ssh -l username $cpeIP\n");
$ssh->read("assword:");
$ssh->write("password\n");
$ssh->read("#");
$ssh->write("\n");
$cpePrompt = $ssh->read('/.*[#|>]/', NET_SSH2_READ_REGEX);
$cpePrompt = str_replace("\n", '', trim($cpePrompt));
$ssh->write("config t\n");
foreach ($commands_explode as $i) {
$ssh->write("$i\n"); // note the "\n"
$ssh->setTimeout(2);
$screenOut .= $ssh->read();
}
$ssh->write("end\n");
$ssh->read($cpePrompt);
$ssh->write("exit\n");
echo "Router Update completed! Results below:<br><br>";
echo "<div id=\"text_out\"><textarea style=\" border:none; width: 700px;\" rows=\"20\">".$screenOut."</textarea></div>";
Update:
Roztwór I udał się z pętli, gdy przełącznik /. Poszedłbym na trasę Expect, ale ciągle napotykałem problemy związane z włączeniem modułu Expect do PHP na moim serwerze (Windows box). Gdybym używał serwera Unix/Linux Expect byłby najprostszym sposobem na osiągnięcie tego . Zrobiłem to na razie w działającym demo, więc nadal brakuje instrukcji dotyczących przypadków, a obsługa błędów wciąż musi zostać rozwiązana, ale podstawowy pomysł istnieje. Nadal chcę przesuwać instrukcje preg_match dookoła nieco więcej, aby dopasować je na górze pętli while (aby nie spamować całej sekcji case z różnymi liniami preg_match), ale to może okazać się więcej pracy niż ja chcę na razie. Mam nadzieję, że to pomoże komuś innemu próbować zrobić to samo!
<?php
include('Net/SSH2.php');
define('NET_SSH2_LOGGING', NET_SSH2_LOG_COMPLEX);
ini_set('display_errors', 1);
$conn = new Net_SSH2('somewhere.outthere.com');
if (!$conn->login($user, $pass . $yubi)) {
exit('Login Failed');
}
$prompt = "Testing#";
$conn->setTimeout(2);
$conn->write("PS1=\"$prompt\"");
$conn->read();
$conn->write("\n");
$screenOut = $conn->read();
//echo "$screenOut is set on the shell<br><br>";
echo $login_db[3][0]. " ". $login_db[3][1];
$logged_in = false;
$status = "SSH";
$status_prev = "";
$login_counter = 0;
while (!$logged_in && $login_counter <=3) {
switch ($status) {
case "Telnet":
break;
case "SSH":
$conn->write("\n");
$conn->write("ssh -l " . $login_db[$login_counter][0] . " $cpeIP\n");
$status_prev = $status;
$status = $conn->read('/\n([.*])$/', NET_SSH2_READ_REGEX);
break;
case (preg_match('/Permission denied.*/', $status) ? true : false):
$conn->write(chr(3)); //Sends Ctrl+C
$status = $conn->read();
if (strstr($status, "Testing#")) {
$status = "SSH";
$login_counter++;
break;
} else {
break 2;
}
case (preg_match('/[pP]assword:/', $status) ? true : false):
$conn->write($login_db[$login_counter][1] . "\n");
$status_prev = $status;
$status = $conn->read('/\n([.*])$/', NET_SSH2_READ_REGEX);
break;
case (preg_match('/yes\/no/', $status) ? true : false):
$conn->write("yes\n");
$status_prev = $status;
$status = $conn->read('/\n([.*])$/', NET_SSH2_READ_REGEX);
break;
case (preg_match('/(^[a-zA-Z0-9_]+[#]$)|(>)/', $status,$matches) ? true : false):
$conn->write("show version\n");
$status = $conn->read(">");
if(preg_match('/ADTRAN|Adtran|Cisco/', $status)? true:false){
$logged_in = true;
break;
}
default:
echo "<br>Something done messed up! Exiting";
break 2;
}
//echo "<pre>" . $conn->getLog() . "</pre>";
}
if ($logged_in === true) {
echo "<br> Made it out of the While loop cleanly";
} else {
echo "<br> Made it out of the While loop, but not cleanly";
}
echo "<pre>" . $conn->getLog() . "</pre>";
$conn->disconnect();
echo "disconnected cleanly";
}
?>
'' '' '' else' bloki i 'switch' -case" bloki są praktycznie takie same. –