Użyj Expect w skrypcie Bash, aby podać hasło do polecenia SSH

135

Próbuję użyć Expect w skrypcie Bash, aby podać hasło SSH. Podanie hasła działa, ale nie kończę sesji SSH tak, jak powinienem. Wraca cieśniną do Basha.

Mój skrypt:

#!/bin/bash

read -s PWD

/usr/bin/expect <<EOD
spawn ssh -oStrictHostKeyChecking=no -oCheckHostIP=no usr@$myhost.example.com'
expect "password"
send "$PWD\n"
EOD
echo "you're out"

Wynik mojego skryptu:

spawn ssh -oStrictHostKeyChecking=no -oCheckHostIP=no usr@$myhost.example.com
usr@$myhost.example.com's password: you're out

Chciałbym mieć sesję SSH i dopiero po jej zakończeniu wrócić do mojego skryptu Bash.

Powodem, dla którego używam Bash przed Expect, jest to, że muszę używać menu. Mogę wybrać, z którym urządzeniem / urządzeniem się połączyć.

Tym, którzy chcą odpowiedzieć, że powinienem używać kluczy SSH, wstrzymajcie się.

Maks
źródło
49
proszę zobaczyć pierwszą linijkę: Do tych, którzy chcą odpowiedzieć, że powinienem używać kluczy SSH, wstrzymaj się
maks.
54
Zmodyfikowałbym twoją pierwszą linię, aby była trochę bardziej przyjazna. Możesz rozważyć coś w rodzaju: „Z powodu ograniczeń po prostu nie mogę używać kluczy SSH, muszę znaleźć sposób, aby to działało zgodnie z oczekiwaniami”. Powinieneś spodziewać się, że ludzie mogą być naturalnie ciekawi, dlaczego nie używasz kluczy i po prostu starają się być pomocni :) @Ignacio nie sugerował , że ich używasz, po prostu potwierdzał to jako ograniczenie, a nie przeoczenie.
Tim Post
W tym przypadku spróbuję użyć kermitu. Ma bardzo rozbudowany język skryptowy columbia.edu/kermit/skermit.html#scripts
f3xy

Odpowiedzi:

89

Mieszanie Bash i Expect nie jest dobrym sposobem na osiągnięcie zamierzonego efektu. Spróbowałbym użyć tylko Oczekuj:

#!/usr/bin/expect
eval spawn ssh -oStrictHostKeyChecking=no -oCheckHostIP=no usr@$myhost.example.com

# Use the correct prompt
set prompt ":|#|\\\$"
interact -o -nobuffer -re $prompt return
send "my_password\r"
interact -o -nobuffer -re $prompt return
send "my_command1\r"
interact -o -nobuffer -re $prompt return
send "my_command2\r"
interact

Przykładowe rozwiązanie dla basha to:

#!/bin/bash
/usr/bin/expect -c 'expect "\n" { eval spawn ssh -oStrictHostKeyChecking=no -oCheckHostIP=no usr@$myhost.example.com; interact }'

To zaczeka na Entersesję interaktywną, a następnie powróci (na chwilę).

Piotr Król
źródło
1
działa świetnie, dzięki. Co mam zrobić, jeśli chcę wpisać polecenie po zalogowaniu się przez SSH?
Max
Ten skrypt powinien zwrócić nieaktywną powłokę z zalogowanym użytkownikiem. Nie rozumiem pytania. Jeśli absolutnie chcesz użyć tego samego skryptu w bash, spójrz na edytowany wpis.
Piotr Król
W ten sam sposób, w jaki wysyłam hasło po wyświetleniu monitu, chciałbym wysyłać polecenia systemowe po zalogowaniu.
Max.
Kod Samlple został dodany do powyższego postu. Oczywiście będzie to działać, dopóki my_commandX nie zmieni zwróconego znaku zachęty, jeśli tak się stanie, zmienna zachęty powinna zostać zmieniona.
Piotr Król
@pietrushnic Czy możesz trochę wyjaśnić, dlaczego używasz „interact -o -nobuffer -re $ prompt return” zamiast „oczekuj $ zachęty”? ten drugi wygląda na bardziej powszechnie używany ...
Richard,
54

Najłatwiej jest użyć sshpass . Jest to dostępne w repozytoriach Ubuntu / Debian i nie musisz zajmować się integracją z Bash.

Przykład:

sshpass -p<password> ssh <arguments>
sshpass -ptest1324 ssh [email protected] ls -l /tmp

Powyższe polecenie można łatwo zintegrować ze skryptem Bash.

Uwaga: zapoznaj się z sekcją „ Uwagi dotyczące bezpieczeństwa ”, man sshpassaby w pełni zrozumieć wpływ na bezpieczeństwo.

dotnix
źródło
Nie wiem, czy to dobre rozwiązanie, ale na pewno wiele upraszcza. Dzięki
erikbwork
9
Bardzo niebezpieczne z punktu widzenia bezpieczeństwa - argumenty wiersza poleceń mogą zostać odczytane przez każdy inny proces w systemie. Można je nadpisać i mam nadzieję sshpass, że to zrobi, ale nawet wtedy jest okres, w którym nadal się uruchamia, zanim będzie w stanie to zrobić, gdy hasło jest dostępne dla każdego / każdego procesu.
Charles Duffy
1
@CharlesDuffy Oczywiście masz rację. sshpassjest używany w scenariuszu, w którym masz proste skrypty testowe, które są wykonywane w środowisku sieci lokalnej, w którym bezpieczeństwo nie jest głównym problemem. W rzeczywistości jest sekcja, w man sshpassktórej wyjaśniono całą sekcję dotyczącą zagadnień bezpieczeństwa. Dodano to, aby odpowiedzieć, dzięki.
dotnix
@ erikb85 Zwykle pakiet wykonuje wszystkie brudne rzeczy za Ciebie, ale we wszystkich przypadkach te skrypty są budowane tylko do tego celu, byłoby to LEPSZE niż dodawanie własnych rzeczy. Ten komentarz dotyczy tego, aby nie odkrywać koła na nowo. Zajmuj się trudnymi rzeczami tylko wtedy, gdy nikt jeszcze się z tym nie uporał. sshpass to dobra funkcja.
m3nda
2
Dla kompletności wspomnę, że "man sshpass" dostarcza odpowiednie ostrzeżenia bezpieczeństwa potencjalnemu użytkownikowi, wskazuje, że "-p" jest najmniej bezpiecznym sposobem jego użycia i oferuje opcję "-e" do pobierania hasła przez zmienną środowiskową , co przynajmniej utrzymuje go poza wierszem poleceń.
Ron Burk,
25

Dodaj polecenie „interakcja” Oczekuj tuż przed EOD:

#!/bin/bash

read -s PWD

/usr/bin/expect <<EOD
spawn ssh -oStrictHostKeyChecking=no -oCheckHostIP=no usr@$myhost.example.com
expect "password"
send "$PWD\n"
interact
EOD
echo "you're out"

Powinno to umożliwić interakcję ze zdalną maszyną do czasu wylogowania. Wtedy wrócisz do Bash.

dr-jan
źródło
Wchodzi i natychmiast wychodzi. Drukowane jest „nie ma Cię”.
Emmanuel
8
Zastąpiłem „interakcję” i „EOD” na „oczekiwać eof” i to zadziałało. To jest na komputerze Mac.
Emmanuel
3
Żadna z odpowiedzi na tej stronie nie zadziałała dla mnie, ale sugestia @ Emmanuela, aby użyć, expect eofrozwiązała problem.
moertel
Usuń 'z usr@$myhost.example.com'i powinno działać. A może trzeba wymienić \nz \r, ale YMMV
Tino
21

Po miesiącach szukania odpowiedzi na to pytanie w końcu znajduję naprawdę najlepsze rozwiązanie: napisanie prostego scenariusza.

#!/usr/bin/expect

set timeout 20

set cmd [lrange $argv 1 end]
set password [lindex $argv 0]

eval spawn $cmd
expect "Password:"
send "$password\r";
interact

Umieść to /usr/bin/exp, a następnie możesz użyć:

  • exp <password> ssh <anything>
  • exp <password> scp <anysrc> <anydst>

Gotowe!

damn_c
źródło
expect "assword:"pan na myśli expect "password:"?
użytkownik
2
@user "assword"dopasuje zarówno Passwordi password.
Ferdinand.kraft
Świetne jest również dla scp z dwoma argumentami. Oczywiście ssh również z 2 argumentami.
Timo
9

Upewnij się również, że używasz

send -- "$PWD\r"

zamiast tego hasła zaczynające się od myślnika (-) zawiodą w przeciwnym razie.

Powyższe nie zinterpretuje ciągu zaczynającego się od myślnika jako opcji polecenia wysyłania.

Timmah
źródło
9

Prosty skrypt Expect:

Plik Remotelogin.exp

    #!/usr/bin/expect
    set user [lindex $argv 1]
    set ip [lindex $argv 0]
    set password [lindex $argv 2]
    spawn ssh $user@$ip
    expect "password"
    send "$password\r"
    interact

Przykład:

./Remotelogin.exp <ip> <user name> <password>
Vijay SB
źródło
7

Użyj narzędzia pomocniczego fd0ssh(z hxtools, a nie pmt). Działa bez konieczności oczekiwania konkretnego monitu od sshprogramu.

user562374
źródło
To jest świetne! Trochę trudno jest go uruchomić (przynajmniej na serwerze Ubuntu), ale działa płynnie! Konfiguracja, którą stworzyliśmy: echo "yoursshpass" | fd0ssh ssh -c -L$port:$ip:$remote_port [email protected] & DZIĘKI!
JP Illanes
1
O wiele bezpieczniejsze niż podanie hasła w wierszu poleceń sshpass.
Charles Duffy
5

Innym sposobem, który uznałem za przydatny w użyciu małego skryptu Expect ze skryptu Bash, jest następujący.

...
Bash script start
Bash commands
...
expect - <<EOF
spawn your-command-here
expect "some-pattern"
send "some-command"
...
...
EOF
...
More Bash commands
...

To działa, ponieważ ...If the string "-" is supplied as a filename, standard input is read instead...

Jimbo
źródło
Nie potrzebujesz -tutaj, ponieważ expectczyta stdingo domyślnie, jeśli wywołasz go bez argumentów. Jest to jednak przydatne, jeśli chcesz uruchomić go interaktywnie bez wiersza poleceń ( expectvs. expect -) lub w przypadku, gdy robisz coś takiego, w expect -f "$@"którym pierwszym argumentem powinien być plik, nawet jeśli wygląda jak opcja (zaczyna się od -). W takim przypadku, jeśli $1(podany plik) -to czyta z stdin.
Tino
1

sshpassjest zepsuty, jeśli spróbujesz użyć go wewnątrz celu kompilacji Sublime Text , wewnątrz pliku Makefile. Zamiast tego sshpassmożesz użyć passh: https://github.com/clarkwang/passh

Z sshpasstobą zrobiłbyś:

sshpass -p pa$$word ssh user@host

Z passhtobą zrobiłbyś:

passh -p pa$$word ssh user@host

Uwaga: nie zapomnij użyć -o StrictHostKeyChecking=no. W przeciwnym razie połączenie zawiesi się przy pierwszym użyciu. Na przykład:

passh -p pa$$word ssh -o StrictHostKeyChecking=no user@host

Bibliografia:

  1. Polecenie wysyłania hasła nie działa przy użyciu skryptu Expect w połączeniu SSH
  2. Jak mogę wyłączyć ścisłe sprawdzanie klucza hosta w ssh?
  3. Jak wyłączyć sprawdzanie klucza hosta SSH
  4. scp bez sprawdzenia znanych_hostów
  5. pam_mount i sshfs z uwierzytelnianiem hasłem
użytkownik
źródło