Pytanie (TL; DR)
Jak dynamicznie przypisywać porty do zdalnego przekazywania (inaczej -R
opcja), w jaki sposób skrypt na zdalnej maszynie (na przykład pochodzącej z .bashrc
) może określić, które porty zostały wybrane przez OpenSSH?
tło
Używam OpenSSH (na obu końcach) do łączenia się z naszym centralnym serwerem, który udostępniam wielu innym użytkownikom. Do mojej zdalnej sesji (na razie) chciałbym przesłać X, puchary i pulseaudio.
Najbardziej trywialnym jest przekazywanie X za pomocą -X
opcji. Przydzielony adres X jest przechowywany w zmiennej środowiskowej DISPLAY
i na tej podstawie mogę określić odpowiedni port TCP, w większości przypadków i tak. Ale prawie nigdy nie muszę, bo Xlib honoruje DISPLAY
.
Potrzebuję podobnego mechanizmu do filiżanek i pulseaudio. Istnieją podstawy dla obu usług, w postaci odpowiednio zmiennych środowiskowych CUPS_SERVER
i PULSE_SERVER
. Oto przykłady użycia:
ssh -X -R12345:localhost:631 -R54321:localhost:4713 datserver
export CUPS_SERVER=localhost:12345
lowriter #and I can print using my local printer
lpr -P default -o Duplex=DuplexNoTumble minutes.pdf #printing through the tunnel
lpr -H localhost:631 -P default -o Duplex=DuplexNoTumble minutes.pdf #printing remotely
mpg123 mp3s/van_halen/jump.mp3 #annoy co-workers
PULSE_SERVER=localhost:54321 mpg123 mp3s/van_halen/jump.mp3 #listen to music through the tunnel
Problem jest ustawiony CUPS_SERVER
i PULSE_SERVER
poprawnie.
Często korzystamy z przekierowania portów, dlatego potrzebuję dynamicznych przydziałów portów. Statyczne przydziały portów nie są opcją.
OpenSSH ma mechanizm dynamicznej alokacji portów na zdalnym serwerze, określając 0
jako bind-port do zdalnego przekazywania ( -R
opcja). Korzystając z następującego polecenia, OpenSSH dynamicznie przydziela porty dla pucharów i przekazywania impulsów.
ssh -X -R0:localhost:631 -R0:localhost:4713 datserver
Gdy użyję tego polecenia, ssh
wydrukuję następujące informacje STDERR
:
Allocated port 55710 for remote forward to 127.0.0.1:4713
Allocated port 41273 for remote forward to 127.0.0.1:631
Potrzebuję informacji! Ostatecznie chcę wygenerować:
export CUPS_SERVER=localhost:41273
export PULSE_SERVER=localhost:55710
Jednak komunikaty „Przydzielony port ...” są tworzone na moim komputerze lokalnym i wysyłane do STDERR
, do którego nie mam dostępu na komputerze zdalnym. Co dziwne, OpenSSH wydaje się nie mieć środków do pobierania informacji o przekierowaniu portów.
Jak pobrać te informacje, aby umieścić je w skrypcie powłoki, aby odpowiednio ustawić CUPS_SERVER
i PULSE_SERVER
na zdalnym hoście?
Dead Ends
Jedyną łatwą rzeczą, jaką mogłem znaleźć, było zwiększenie szczegółowości, sshd
dopóki informacje te nie zostaną odczytane z dzienników. Nie jest to wykonalne, ponieważ informacje te ujawniają znacznie więcej informacji niż jest to rozsądne, aby udostępnić je użytkownikom innym niż root.
Zastanawiałem się nad załataniem OpenSSH do obsługi dodatkowej sekwencji ucieczki, która wypisuje ładną reprezentację wewnętrznej struktury permitted_opens
, ale nawet jeśli tego właśnie chcę, nadal nie mogę uzyskać skryptu uzyskującego dostęp do sekwencji ucieczki klienta po stronie serwera.
Musi być lepszy sposób
Poniższe podejście wydaje się bardzo niestabilne i jest ograniczone do jednej takiej sesji SSH na użytkownika. Potrzebuję jednak co najmniej dwóch równoczesnych sesji i jeszcze więcej użytkowników. Ale próbowałem ...
Gdy gwiazdy są odpowiednio wyrównane, po poświęceniu jednego lub dwóch kurczaków, mogę nadużywać faktu, że sshd
nie został uruchomiony jako mój użytkownik, ale po upuszczeniu upuszcza uprawnienia, aby to zrobić:
uzyskać listę numerów portów dla wszystkich gniazd nasłuchujących należących do mojego użytkownika
netstat -tlpen | grep ${UID} | sed -e 's/^.*:\([0-9]\+\) .*$/\1/'
uzyskać listę numerów portów dla wszystkich gniazd nasłuchujących należących do procesów uruchomionych przez mojego użytkownika
lsof -u ${UID} 2>/dev/null | grep LISTEN | sed -e 's/.*:\([0-9]\+\) (LISTEN).*$/\1/'
Wszystkie porty, które są w pierwszym secie, ale nie w drugim zestawie mają wysoką Likelyhood być moim porty spedycji, a nawet odjęcie wydajności zestawów
41273
,55710
a6010
; kubki, puls i X, odpowiednio.6010
jest identyfikowany jako port X za pomocąDISPLAY
.41273
jest portem kubków, ponieważlpstat -h localhost:41273 -a
zwraca0
.55710
jest portem impulsowym, ponieważpactl -s localhost:55710 stat
zwraca0
. (Drukuje nawet nazwę hosta mojego klienta!)
(Aby wykonać odejmowanie zestawu I sort -u
i zapisać dane wyjściowe z powyższych wierszy poleceń i użyć comm
do odejmowania.)
Pulseaudio pozwala mi zidentyfikować klienta i, dla wszystkich celów i celów, może to służyć jako kotwica do oddzielnych sesji SSH, które wymagają oddzielenia. Jednak nie znaleźli sposób, aby związać 41273
, 55710
i 6010
do tego samego sshd
procesu. netstat
nie ujawni tych informacji użytkownikom innym niż root. Dostaję tylko -
w PID/Program name
kolumnie, w której chciałbym przeczytać 2339/54
(w tym konkretnym przypadku). Tak blisko ...
źródło
netstat
nie pokaże PID dla procesów, których nie jesteś właścicielem lub które są przestrzenią jądra. Na przykładOdpowiedzi:
Weź dwa (zobacz historię wersji, która wykonuje scp ze strony serwera i jest nieco prostsza), to powinno to zrobić. Istota tego jest taka:
Po pierwsze, zainstaluj po stronie zdalnej, musisz włączyć wysyłanie zmiennej env w konfiguracji sshd :
Znajdź linię
AcceptEnv
i dodajMY_PORT_FILE
do niej (lub dodaj linię w prawejHost
części, jeśli jeszcze jej nie ma). Dla mnie linia stała się następująca:Pamiętaj również, aby ponownie uruchomić sshd, aby to zadziałało .
Dodatkowo, aby poniższe skrypty działały, zrób
mkdir ~/portfiles
po zdalnej stronie!Następnie po stronie lokalnej: fragment skryptu, który będzie
Fragment skryptu:
Następnie fragment dla strony zdalnej, odpowiedni dla .bashrc :
Uwaga : powyższy kod nie jest oczywiście dokładnie testowany i może zawierać wszelkiego rodzaju błędy, błędy kopiowania i wklejania itp. Każdy, kto go używa, również go rozumie, używa na własne ryzyko! Przetestowałem to za pomocą połączenia localhost i działało dla mnie w mojej testowej env. YMMV.
źródło
scp
od strony zdalnej do strony lokalnej, czego nie mogę. Miałem podobne podejście, alessh
po nawiązaniu połączenia owinąłem go w tło, a następnie wyślę ten plik z lokalnego do zdalnego za pośrednictwem,scp
a następnie pociągnęssh
klienta na pierwszy plan i uruchom skrypt po zdalnej stronie. Nie wymyśliłem, jak ładnie napisać skrypt do tworzenia tła i planowania lokalnych i zdalnych procesów. Zawijanie i integrowanie lokalnegossh
klienta z takimi zdalnymi skryptami nie wydaje się dobrym podejściem.(while [ ... ] ; do sleep 1 ; done ; scp ... )&
. Następnie poczekaj na pierwszym planie na serwerze.bashrc
(zakładając, że klient wyśle odpowiednią zmienną env) na pojawienie się pliku. Zaktualizuję odpowiedź później po kilku testach (prawdopodobnie nie będzie czasu do jutra).~/.ssh-${PID}-forwards
Fragment po stronie lokalnej, odpowiedni dla .bashrc:
źródło
Osiągnąłem to samo, tworząc potok na lokalnym kliencie, a następnie przekierowując stderr do potoku, który jest również przekierowywany do wejścia ssh. Nie wymaga wielu połączeń ssh, aby założyć wolny znany port, który mógłby zawieść. W ten sposób baner logowania i tekst „Przydzielony port ### ...” są przekierowywane do zdalnego hosta.
Mam prosty skrypt na hoście,
getsshport.sh
który jest uruchamiany na hoście zdalnym, który odczytuje przekierowane dane wejściowe i analizuje port. Dopóki ten skrypt nie zakończy się, zdalne przekazywanie ssh pozostaje otwarte.strona lokalna
3>&1 1>&2 2>&3
jest małą sztuczką, aby zamienić stderr i stdout, tak że stderr zostaje przesłany do cat, a wszystkie normalne dane wyjściowe z ssh są wyświetlane na stderr.strona zdalna ~ / getsshport.sh
Próbowałem
grep
najpierw wyświetlić komunikat „przydzielony port” po stronie lokalnej przed wysłaniem go przez ssh, ale wygląda na to, że ssh zablokuje oczekiwanie na otwarcie potoku na standardowym wejściu. grep nie otwiera potoku do pisania, dopóki czegoś nie otrzyma, więc w zasadzie jest to impas.cat
jednak wydaje się, że nie zachowuje się tak samo i otwiera potok do natychmiastowego zapisu, pozwalając ssh na otwarcie połączenia.jest to ten sam problem po drugiej stronie i dlaczego
read
wiersz po wierszu zamiast po prostu grep ze standardowego wejścia - w przeciwnym razie `/ tmp / przydzieloneports 'nie zostaną zapisane, dopóki tunel ssh nie zostanie zamknięty, co pokona cały cel~/getsshport.sh
Preferowane jest umieszczenie stderr ssh w poleceniu podobnym , ponieważ bez podawania polecenia, tekstu banera lub czegokolwiek innego, co znajduje się w potoku, jest ono wykonywane na zdalnej powłoce.źródło
renice +10 $$; exec cat
przed,done
aby zaoszczędzić zasoby.Jest to trudna, dodatkowa obsługa po stronie serwera zgodnie z wytycznymi
SSH_CONNECTION
lubDISPLAY
byłaby świetna, ale nie jest łatwa do dodania: część problemu polega na tym, że tylkossh
klient zna lokalne miejsce docelowe, pakiet żądań (na serwer) zawiera tylko zdalny adres i port.Inne odpowiedzi tutaj zawierają różne bezpretensjonalne rozwiązania do przechwytywania tej strony klienta i wysyłania jej na serwer. Oto alternatywne podejście, które wcale nie jest ładniejsze, ale przynajmniej ta brzydka impreza jest utrzymywana po stronie klienta ;-)
SendEnv
, abyśmy mogli przesyłać niektóre zmienne środowiskowe natywnie przez ssh (prawdopodobnie nie domyślnie)AcceptEnv
aby zaakceptować to samo (prawdopodobnie domyślnie wyłączone)ssh
wyjściowe stderr klienta za pomocą dynamicznie ładowanej biblioteki i aktualizuj środowisko klienta ssh podczas konfigurowania połączeniaDziała to (na szczęście i tak na razie), ponieważ zdalne przekazywanie jest konfigurowane i rejestrowane przed wymianą środowiska (potwierdź za pomocą
ssh -vv ...
). Dynamicznie ładowana biblioteka musi przechwycić funkcjęwrite()
libc (ssh_confirm_remote_forward()
→logit()
→do_log()
→write()
). Przekierowywanie lub zawijanie funkcji w pliku binarnym ELF (bez ponownej kompilacji) jest o rząd wielkości bardziej złożone niż robienie tego samego dla funkcji w bibliotece dynamicznej.W kliencie
.ssh/config
(lub wierszu poleceń-o SendEnv ...
)Na serwerze
sshd_config
(wymagana zmiana administratora / administratora)To podejście działa dla klientów Linux i nie wymaga niczego specjalnego na serwerze, powinno działać dla innych * nix z kilkoma drobnymi poprawkami. Działa od co najmniej OpenSSH 5.8p1 do 7.5p1.
Kompiluj za pomocą
gcc -Wall -shared -ldl -Wl,-soname,rfwd -o rfwd.so rfwd.c
Invoke za pomocą:Kod:
(Istnieje kilka pułapek na niedźwiedzie glibc związane z wersjonowaniem symboli przy takim podejściu, ale
write()
nie ma tego problemu).Jeśli czujesz się odważny, możesz wziąć
setenv()
odpowiedni kod i wstawić go dossh.c
ssh_confirm_remote_forward()
funkcji wywołania zwrotnego.Ustawia zmienne środowiskowe o nazwie
SSH_RFWD_nnn
, sprawdzaj je w swoim profilu, np. Wbash
Ostrzeżenia:
ssh
obecnie nie rejestruje w sposób wyraźny pełnego przekazywania formularza * local: port: remote: port * (w razie potrzeby wymagane byłoby dalsze parsowaniedebug1
wiadomościssh -v
), ale nie jest to potrzebne w przypadku użyciaMożesz (częściowo) to zrobić interaktywnie za pomocą ucieczki
~#
, co dziwne, implementacja przeskakuje nad kanałami, które nasłuchują, wyświetla tylko te otwarte (tj. TCP USTANOWIONE) i nie drukuje użytecznych pól w żadnym wypadku. Widziećchannels.c
channel_open_message()
Można załatać tę funkcję do drukowania szczegółów dla
SSH_CHANNEL_PORT_LISTENER
szczelin, ale to tylko dostaje lokalnych forwardings ( kanały nie są tym samym, co faktycznym forward ). Lub możesz go załatać, aby zrzucić dwie tabele przesyłania z globalnejoptions
struktury:Działa to dobrze, chociaż nie jest to rozwiązanie „programowe”, z zastrzeżeniem, że kod klienta nie (jeszcze oznaczony jako XXX w źródle) aktualizuje listę, gdy dodajesz / usuwasz przekazy w locie (
~C
)Jeśli serwer (y) to Linux, masz jeszcze jedną opcję, z której korzystam ogólnie, chociaż do lokalnego przekazywania zamiast zdalnej.
lo
to 127.0.0.1/8, w systemie Linux możesz transparentnie powiązać z dowolnym adresem w 127/8 , więc możesz użyć stałych portów, jeśli używasz unikalnych adresów 127.xyz, np .:Jest to uzależnione od wiązania uprzywilejowanych portów <1024, OpenSSH nie obsługuje możliwości Linuksa i ma zakodowane sprawdzenie UID na większości platform.
Mądrze wybrane oktety (w moim przypadku porządkowe mnemoniki ASCII) pomagają rozwikłać bałagan na koniec dnia.
źródło