Przekazywanie wielu plików przez stdin (over ssh)


15

Mam program na zdalnym hoście, którego wykonanie muszę zautomatyzować. Polecenie wykonania tego programu na tym samym komputerze wygląda mniej więcej tak:

/path/to/program -a file1.txt -b file2.txt

W tym przypadku file1.txti file2.txtsą używane do zupełnie różnych rzeczy w programie, więc nie mogę catich po prostu razem. Jednak w moim przypadku file1.txti file2.txtto, co chcę przekazać do programu, istnieje tylko na moim urządzeniu, a nie na hoście, na którym muszę uruchomić program. Wiem, że mogę przesłać co najmniej jeden plik przez SSH, przekazując go przez stdin:

cat file1.txt | ssh host.name /path/to/program -a /dev/stdin -b file2.txt

ale ponieważ nie wolno mi przechowywać plików na hoście, potrzebuję sposobu, aby się file2.txttam dostać . Myślę, że może to być możliwe poprzez nadużywanie zmiennych środowiskowych oraz kreatywne wykorzystanie cati sedrazem, ale nie znam narzędzi wystarczająco dobrze, aby zrozumieć, w jaki sposób wykorzystam je do osiągnięcia tego celu. Czy to jest wykonalne i jak?


2
cati sednie są tutaj rozwiązaniem.
Slyx

Prawdopodobnie byłbym w stanie zamontować, ale biorąc pod uwagę proxy i ograniczenia bezpieczeństwa, nie jestem pewien, czy uda mi się uciec.
Guy Green Cloak

Czy masz uprawnienia na zdalnym komputerze do zamontowania folderu ssh?
Slyx

1
jeśli możesz otworzyć sesję ssh ze zdalnego komputera na komputer lokalny, więc na poziomie sieci nie ma problemu z zamontowaniem folderu SSH.
Slyx

Czy możesz robić spedycje? Jakie masz systemy i powłoki na lokalnym i zdalnym końcu?
mosvy

Odpowiedzi:


18

Jeśli pliki podane jako argumenty twojego programu są plikami tekstowymi i możesz kontrolować ich zawartość (znasz linię, która nie występuje w nich), możesz użyć wielu dokumentów tutaj:

{
    echo "cat /dev/fd/3 3<<'EOT' /dev/fd/4 4<<'EOT' /dev/fd/5 5<<'EOT'"
    cat file1
    echo EOT
    cat file2
    echo EOT
    cat file3
    echo EOT
} | ssh user@host sh

Oto catprzykładowe polecenie, które przyjmuje nazwy plików jako argumenty. Może być zamiast tego:

echo "/path/to/prog -a /dev/fd/3 3<<'EOT' -b /dev/fd/4 4<<'EOT'

Zamień każdy na EOTcoś, co nie występuje odpowiednio w każdym z plików.


1
To podejście jest dość sprytne! Jeśli pliki nie są tekstem, możesz je kodować / dekodować za pomocą czegoś takiego jak base64 lub nawet dobry stary kod ... Bardzo dobrze!
filbranden

3
Zauważ, że kilka (najbardziej) implementacji sh implementuje tutaj dokumenty przy użyciu plików tymczasowych, więc może nie działać dla OP, jeśli nie wolno im przechowywać plików na zdalnym hoście.
Stéphane Chazelas

2
dash(the /bin/shw Debianie, Ubuntu itp), busybox shThe /bin/shz FreeBSD i yashnie używać plików tymczasowych dla tu-dokumentów. Właściwie przetestowałem to z chrootem tylko do odczytu. Oczywiście /dev/fd/pliki muszą być dostępne - tylko na standardowym systemie FreeBSD /dev/fd/0-2, więc fdescfsnależy je zamontować /dev/fd.
mosvy

1
Jestem fanem separatorów informacji ASCII , które mają na celu wyznaczanie pól bez przeszkód. U+001Cto „Information Separator Four” (separator plików, FS) i jest idealny do tego przypadku, chociaż pliki binarne mogą z niego korzystać przypadkowo. Dlatego sugerowałbym EOT="$(printf $'\x1c')"w zaawansowanych powłokach lub EOT="$(awk 'BEGIN{printf"%c",28}')"dla kompatybilności. Musisz go zacytować, gdy go umieścisz, na przykład echo "$EOT$EOT$EOT"(trzykrotnie, aby zmniejszyć prawdopodobieństwo dopasowania pliku binarnego).
Adam Katz

Jeśli zamierzasz użyć tty, dlaczego nie użyć ttypolecenia? Chyba że coś mi umknie - co, jak sądzę, jest możliwe?
Pryftan

14

Być może nie dokładnie to, czego chcesz ... Ale może rozważ wysłanie tarballa przez rurę otwartą przez ssh?

Ty to powiedziałeś:

Nie wolno mi przechowywać plików na hoście.

Możliwe, że nie masz zapisywalnego katalogu domowego lub innej dogodnej lokalizacji do przechowywania plików przez długi czas, ale powiedziałbym, że jest mało prawdopodobne, że nie masz żadnej zapisywalnej lokalizacji, nawet jeśli jest to tymczasowe tmpfs będzie dostępna tylko dla twojego szczególne połączenie.

Wiele programów (a nawet procedur libc) wymaga zapisu /tmp , więc jest bardzo prawdopodobne, że jeden będzie dla ciebie dostępny.

Następnie możesz użyć skryptu, który rozpakuje archiwum tar do katalogu tymczasowego, uruchom program i wyczyść połączenie ssh.

Coś jak:

$ tar cf - file1.txt file2.txt |
  ssh host.name '
      set -e
      tmpdir=$(mktemp -d -t tmp.XXXXXXXXXX)
      cleanup () { rm -rf "$tmpdir"; }
      trap cleanup EXIT
      cd "$tmpdir"
      tar xf -
      /path/to/program -a file1.txt -b file2.txt
  '

Może to wymagać dodatkowej uwagi ze ścieżkami plików i należy rozważyć kilka przypadków narożnych (przetestuj je), ale ogólne podejście powinno działać.

Jeśli nie jest dostępny katalog z możliwością zapisu, możliwe byłoby zmodyfikowanie go tak, programaby przyjmował tarball jako pojedyncze wejście i rozpakowywał jego zawartość do pamięci. Na przykład, jeśli programjest to skrypt w Pythonie, to użycie wbudowanego tarfilemodułu z łatwością osiągnęłoby coś takiego.


3
Nawet bez kłopotania się z tarballowaniem go - zdecydowałem mniej więcej po prostu napisać plik /tmp/i użyć go bezpośrednio.
Facet z Zielonym Płaszczem,

2
Podejście tarball ma pewne zalety, ponieważ używasz pojedynczego połączenia ssh (nie trzeba testować pod kątem sukcesu / niepowodzenia), a wiele równoległych uruchomień prawdopodobnie nie będzie ze sobą współpracować (użyj i / lub usuń pliki w / tmp przeznaczone do osobnych uruchomień .) Ale myślę, że to najlepsze, co możesz uzyskać od ssh, ponieważ istnieje teraz. Powodzenia!
filbranden

3
@GreenCloakGuy W końcu możesz dobrze przechowywać pliki na serwerze. Proszę odpowiednio zmodyfikować pytanie.
mosvy

1
@mosvy może tak, ale nie chce . Pytanie brzmi: „czy można to zrobić bez przechowywania plików”, odpowiedź wydaje się być „być może, ale nie w moim przypadku”
facet Green Cloak

5

Jeśli możesz ustawić detektor TCP (może to być wyższy port), możesz użyć drugiej sesji SSH, aby ustanowić drugie źródło wejściowe za pomocą nc .

Przykład:

Na serwerze znajduje się następujący skrypt ~/script.bash:

#!/usr/bin/env bash
cat "$1" | tr a 1
nc localhost "$2" | tr '[:lower:]' '[:upper:]'

I są te dwa pliki lokalnie:

$ cat a 
aaa
aaa
aaa
$ cat b
bbb
bbb
bbb

Teraz najpierw uruchom drugie źródło ( $servserwer):

ssh "$serv" nc -l -p 2222 -q1 <b &

I uruchom właściwe polecenie:

$ ssh "$serv" ./script.bash - 2222 <a
111
111
111
BBB
BBB
BBB
[1]+  Done                    ssh "$serv" nc -l -p 2222 -q1 < b

2

Zostało ustalone w komentarzu, który /tmpmożna zapisać, więc po prostu skopiuj wcześniej jeden z plików:

scp -p file2.txt host.name:/tmp/
ssh host.name "/path/to/program -a /dev/stdin -b /tmp/file2.txt && rm /tmp/file2.txt" < file1.txt

Spowoduje to również wyczyszczenie skopiowanego pliku po udanym uruchomieniu (zmień na &&a, ;jeśli chcesz go usunąć niezależnie od powodzenia, ale pamiętaj, że utracisz wartość wyjścia).


Jeśli nie jest to do przyjęcia, zaproponowałbym majstrowanie przy nim /path/to/programlub opakowanie, które może oddzielić dwa pliki od jednego strumienia wejściowego, takiego jak:

awk 'FNR == 1 && NR > 1 { printf "%c%c%c", 28, 28, 28 } 1' file1.txt file2.txt \
  | ssh host.name /path/to/tweaked_program

Wykorzystuje separator informacji ASCII czwarty (separator plików, FS) i potroił go, aby zminimalizować prawdopodobieństwo przypadkowego pliku binarnego zawierającego ten ciąg. Twójtweaked_programNastępnie podzielisz dane wejściowe za pomocą separatora, a następnie działał na dwóch zapisanych plikach jako zmienne.

Oczywiście, jeśli używasz języka, który ma biblioteki do radzenia sobie z archiwami, to bezpieczniejszym i czystszym podejściem byłoby po prostu wtopienie tarw taki kod ssh:

tar -zpc file1.txt file2.txt |ssh host.name /path/to/tweaked_program

A twoja tweaked_programby rozpakować i otworzyć archiwum, zapisać każdy plik do innej zmiennej, a następnie uruchomić oryginalną program„s logiki na zmiennych.


1

Jeśli możesz przekierowywać porty sshi masz dostęp do wgetkomputera zdalnego i busyboxkomputera lokalnego, możesz zrobić coś takiego:

mkdir /tmp/test; cd /tmp/test
echo 1st_file > 1st_file
echo 2nd_file > 2nd_file

busybox httpd -f -p 127.0.0.1:11080 &
ssh USER@HOST -R 10080:127.0.0.1:11080 '
        cat <(wget -q -O- http://localhost:10080/1st_file) \
            <(wget -q -O- http://localhost:10080/2nd_file)
'
kill $!

(za pomocą cat jako przykładowego programu, który pobiera dwa argumenty pliku).

-RNiezbędna jest tylko możliwość przekierowania portów przez - zamiast http można użyć innych metod, np. jeśli netcatobsługuje -di -Nopcje:

nc -Nl localhost 11001 < 1st_file &
nc -Nl localhost 11002 < 2nd_file &
ssh USER@HOST -R 10001:localhost:11001 -R 10002:localhost:11002 '
        cat <(nc -d localhost 10001) <(nc -d localhost 10002)

Mogą istnieć sposoby na zastąpienie <(...) podstawień procesów, jeśli powłoka logowania na zdalnym komputerze nie jest podobna do ksh lub bash.

Podsumowując, nie jest to zbyt świetne - lepsza wiedza na temat dokładnych uprawnień systemowych / powłok / konfiguracji / uprawnień (których nie dostarczyłeś) może pozwolić na inteligentniejsze rozwiązania.


-1

AKTUALIZACJA: To tak naprawdę nie działa, ssh ma bardzo ustalone pojęcie o tym, co oznaczają stdin i stdout / stderr i tak naprawdę nie pozwala uzurpować stderrowi z niego czytać. Usunę tę odpowiedź za kilka dni, ponieważ nie działa. Dzięki za bardzo interesującą dyskusję !!!


Niestety nie jest to świetny sposób, aby to zrobić bezpośrednio, ponieważ sshklient przejdzie tylko trzy deskryptory ( stdin, stdoutistderr ) do serwera i nie ma przepisów, aby zdać dodatkowy deskryptory plików (co byłoby pomocne dla tego konkretnego przypadku użycia .)

(Należy również pamiętać, że protokół SSH zawiera przepisy dotyczące przekazywania dodatkowych deskryptorów plików, tylko sshklient nie implementuje sposobu korzystania z tej funkcji. Teoretycznie rozszerzenie klienta o łatę wystarczyłoby, aby ujawnić tę funkcję).

Jednym z hackerskich sposobów na osiągnięcie tego, czego szukasz, jest użycie deskryptora pliku 2 ( stderrdeskryptora pliku) do przekazania drugiego pliku. Coś jak:

$ ssh remote.name \
      /path/to/program -a /dev/stdin -b /dev/stderr \
      <file1.txt 2<file2.txt

Powinno to działać, może powodować problem, jeśli programspróbuje napisać do stderr. Można obejść ten problem, ponownie żonglując deskryptorami plików na odległym końcu przed uruchomieniem programu. Możesz przejść file2.txtdo deskryptora pliku 3 i ponownie otworzyć stderr, aby dublować standardowe wyjście:

$ ssh remote.name \
      /path/to/program -a /dev/stdin -b /dev/fd/3 \
      '3<&2' '2>&1' \
      <file1.txt 2<file2.txt

Tak naprawdę nie testowałem tego (pisanie na telefonie), ale przynajmniej teoretycznie spodziewałbym się, że zadziała. Daj mi znać, jeśli z jakiegoś powodu tak nie jest. Twoje zdrowie!
filbranden

To w ogóle nie zadziała. Nawet z zhakowanym ssh. Stderr AFAIK nie używa osobnego kanału, ale jakiś specjalny rodzaj wiadomości. Ale tak, możliwość dostępu do kanałów ssh jako potoków lub nienazwanych gniazd domeny unix (zamiast konieczności przekazywania gniazd) byłaby świetna, po prostu nie wydaje się to możliwe w żadnej formie ani kształcie.
mosvy

@mosvy, fd 0, 1 i 2 na zdalnym poleceniu będą 3 różnymi potokami. Dane przesyłane są przez 3 różne kanały multipleksowane w połączeniu ssh, ale są one jednokierunkowe (od klienta do serwera dla fd 0 i od serwera do klienta dla 1 i 2), więc nawet w systemach, w których potoki są dwukierunkowe, wygrał t praca. W Linuksie otwieranie / dev / stderr w trybie tylko do odczytu daje na drugim końcu potoku, więc też nigdy by nie działało.
Stéphane Chazelas,

Zauważ, że ostatni zakłada powłokę logowania użytkownika na remote.name jest podobny 3<&2do Bourne'a ( jest operatorem powłoki Bourne'a, a sshd uruchamia powłokę logowania użytkownika, aby zinterpretować polecenie wysłane przez klienta)
Stéphane Chazelas

2
@ StéphaneChazelas Nie, jeden kanał ssh jest używany dla stdin / stdout / stderr, a dane stderr są przesyłane za pośrednictwem SSH_MSG_CHANNEL_EXTENDED_DATAwiadomości o typie ustawionym na SSH_EXTENDED_DATA_STDERR. Możesz przeczytać całość tutaj
mosvy
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.