Przez ostatnie 16 miesięcy szukałem rozwiązania tego problemu. Ale za każdym razem, gdy patrzę, wydaje się to niemożliwe z protokołem SSH określonym w odpowiednich dokumentach RFC i wdrożonym przez główne implementacje.
Jeśli jednak chcesz użyć nieco zmodyfikowanego klienta SSH i chcesz wykorzystać protokoły w sposób, który nie był dokładnie zamierzony w momencie ich zaprojektowania, możesz to osiągnąć. Więcej na ten temat poniżej.
Dlaczego nie jest to możliwe
Klient nie wysyła nazwy hosta w ramach protokołu SSH.
Może wysyłać nazwę hosta jako część wyszukiwania DNS, ale może to być buforowane, a ścieżka od klienta przez resolwery do autorytatywnych serwerów może nie przechodzić przez serwer proxy, a nawet jeśli tak, nie ma solidnego sposobu na powiązanie określonych wyszukiwania DNS z konkretnych klientów SSH.
Z samym protokołem SSH nie ma też nic wymyślnego. Musisz wybrać serwer, nawet nie widząc baneru wersji SSH od klienta. Musisz wysłać baner do klienta, zanim wyśle cokolwiek do proxy. Banery z serwerów mogą być inne i nie masz szansy zgadnąć, który z nich jest właściwy.
Mimo że ten baner jest wysyłany niezaszyfrowany, nie można go modyfikować. Każdy fragment tego sztandaru zostanie zweryfikowany podczas konfiguracji połączenia, więc spowodowałbyś awarię połączenia nieco dalej.
Wniosek jest dla mnie dość jasny, trzeba zmienić coś po stronie klienta, aby ta łączność działała.
Większość obejść obejmuje hermetyzację ruchu SSH w innym protokole. Można sobie również wyobrazić dodatek do samego protokołu SSH, w którym baner wersji wysyłany przez klienta zawiera nazwę hosta. Może to pozostać kompatybilne z istniejącymi serwerami, ponieważ część banera jest obecnie określana jako pole identyfikacyjne w dowolnym formularzu i chociaż klienci zwykle czekają na wersję banera z serwera przed wysłaniem własnego, protokół pozwala klientowi wysłać baner pierwszy. Niektóre najnowsze wersje klienta ssh (na przykład ta w Ubuntu 14.04) wysyłają banner bez czekania na banner serwera.
Nie znam żadnego klienta, który podjąłby kroki w celu umieszczenia nazwy hosta serwera w tym banerze. Muszę wysłać poprawkę do listy OpenSSH dodać taką funkcję do. Zostało to jednak odrzucone z powodu chęci nieujawniania nazwy hosta nikomu węszącemu w ruchu SSH. Ponieważ tajna nazwa hosta jest zasadniczo niezgodna z działaniem serwera proxy opartego na nazwie, nie należy spodziewać się wkrótce oficjalnego rozszerzenia SNI dla protokołu SSH.
Prawdziwe rozwiązanie
Najlepszym rozwiązaniem dla mnie było użycie IPv6.
Dzięki IPv6 mogę mieć osobny adres IP przypisany do każdego serwera, dzięki czemu brama może użyć docelowego adresu IP, aby dowiedzieć się, do którego serwera wysłać pakiet. Klienci SSH mogą czasami działać w sieciach, w których jedynym sposobem na uzyskanie adresu IPv6 byłoby skorzystanie z Teredo. Wiadomo, że Teredo jest zawodne, ale tylko wtedy, gdy natywny koniec połączenia IPv6 korzysta z publicznego przekaźnika Teredo. Można po prostu umieścić przekaźnik Teredo na bramie, na której miałbyś uruchomić proxy. Miredo można zainstalować i skonfigurować jako przekaźnik w mniej niż pięć minut.
Obejście
Możesz użyć hosta skoku / hosta bastionu. To podejście jest przeznaczone dla przypadków, w których nie chcesz narażać portu SSH poszczególnych serwerów bezpośrednio na publiczny internet. Ma to tę dodatkową zaletę, że zmniejsza liczbę zewnętrznych adresów IP potrzebnych do SSH, dlatego jest przydatny w tym scenariuszu. Fakt, że jest to rozwiązanie mające na celu dodanie kolejnej warstwy ochrony ze względów bezpieczeństwa, nie uniemożliwia korzystania z niego, gdy nie jest potrzebne dodatkowe zabezpieczenie.
ssh -o ProxyCommand='ssh -W %h:%p user1@bastion' user2@target
Brudny do zhakowania, aby działał, jeśli prawdziwe rozwiązanie (IPv6) jest poza twoim zasięgiem
Hack, który zamierzam opisać, powinien być używany tylko jako ostateczność. Zanim nawet pomyślisz o użyciu tego hacka, zdecydowanie zalecamy uzyskanie adresu IPv6 dla każdego serwera, który ma być dostępny zewnętrznie przez SSH. Użyj IPv6 jako podstawowej metody dostępu do serwerów SSH i użyj tego hacka tylko wtedy, gdy potrzebujesz uruchomić klienta SSH z sieci opartej tylko na IPv4, gdzie nie masz żadnego wpływu na wdrożenie IPv6.
Chodzi o to, że ruch między klientem a serwerem musi być całkowicie prawidłowym ruchem SSH. Ale serwer proxy musi tylko zrozumieć wystarczająco dużo o strumieniu pakietów, aby zidentyfikować nazwę hosta. Ponieważ SSH nie definiuje sposobu wysyłania nazwy hosta, możesz zamiast tego rozważyć inne protokoły, które zapewniają taką możliwość.
Zarówno HTTP, jak i HTTPS pozwalają klientowi wysłać nazwę hosta, zanim serwer wyśle jakiekolwiek dane. Pytanie brzmi teraz, czy można zbudować strumień bajtów, który jest jednocześnie ważny jako ruch SSH oraz jako HTTP i HTTPS. HTTPs to w zasadzie nie starter, ale HTTP jest możliwy (dla wystarczająco liberalnych definicji HTTP).
SSH-2.0-OpenSSH_6.6.1 / HTTP/1.1
$:
Host: example.com
Czy to wygląda dla ciebie jak SSH lub HTTP? Jest zgodny z SSH i całkowicie zgodny z RFC (z wyjątkiem tego, że niektóre znaki binarne zostały nieco zniekształcone przez renderowanie SF).
Ciąg wersji SSH zawiera pole komentarza, które powyżej ma wartość / HTTP/1.1
. Po nowej linii SSH ma pewne dane pakietu binarnego. Pierwszy pakiet to MSG_SSH_IGNORE
wiadomość wysłana przez klienta i zignorowana przez serwer. Ładunek do zignorowania to:
:
Host: example.com
Jeśli proxy HTTP jest wystarczająco liberalne pod względem tego, co akceptuje, wówczas ta sama sekwencja bajtów byłaby interpretowana jako wywołana metoda HTTP, SSH-2.0-OpenSSH_6.6.1
a dane binarne na początku komunikatu o ignorowaniu byłyby interpretowane jako nazwa nagłówka HTTP.
Serwer proxy nie zrozumie ani metody HTTP, ani pierwszego nagłówka. Ale może zrozumieć Host
nagłówek, który jest wszystkim, czego potrzebuje, aby znaleźć backend.
Aby to zadziałało, proxy musiałoby być zaprojektowane na zasadzie, że musi tylko zrozumieć wystarczającą ilość HTTP, aby znaleźć backend, a po znalezieniu backendu proxy po prostu przekaże strumień surowych bajtów i opuści prawdziwe zakończenie połączenia HTTP do wykonania przez backend.
To może wydawać się nieco rozciągnięte, aby przyjąć tak wiele założeń dotyczących proxy HTTP. Ale jeśli chcesz zainstalować nowe oprogramowanie opracowane z myślą o obsłudze SSH, wymagania dotyczące serwera proxy HTTP nie brzmią tak źle.
W moim przypadku ta metoda działała na już zainstalowanym serwerze proxy bez żadnych zmian w kodzie, konfiguracji lub w inny sposób. Dotyczyło to serwera proxy napisanego wyłącznie z myślą o HTTP i bez SSH.
Dowód klienta koncepcyjnego i proxy . (Wyłączenie odpowiedzialności, że serwer proxy jest usługą obsługiwaną przeze mnie. Możesz zamienić link, gdy jakikolwiek inny serwer proxy zostanie potwierdzony w celu obsługi tego użycia).
Ostrzeżenia dotyczące tego włamania
- Nie używaj tego. Lepiej jest użyć prawdziwego rozwiązania, jakim jest IPv6.
- Jeśli serwer proxy spróbuje zrozumieć ruch HTTP, z pewnością się zepsuje.
- Poleganie na zmodyfikowanym kliencie SSH nie jest miłe.