Czy istnieje odwrotny serwer proxy SSH hosta wirtualnego oparty na nazwie?

36

Polubiłem odwrotne proxy HTTP w naszym środowisku programistycznym i uznałem odwrotne proxy wirtualnych hostów oparte na DNS za całkiem przydatne. Otwarcie tylko jednego portu (i standardowego) w zaporze znacznie ułatwia zarządzanie.

Chciałbym znaleźć coś podobnego do połączeń SSH, ale nie miałem dużo szczęścia. Wolałbym nie używać tunelowania SSH, ponieważ wymaga to otwarcia zakresów portów innych niż standardowy. Czy jest coś, co może to zrobić?

Czy HAProxy może to zrobić?

Ahanson
źródło
Czy planowany transfer plików aplikacji lub faktyczny dostęp SSH do hostów?
Kyle Hodgson
Celem jest bezpośredni dostęp SSH do hosta. Ta potrzeba wynika z chęci wewnętrznego uruchomienia serwera Mercurial, ale nasz serwer znajduje się za zaporą ogniową. Obecnie pracuję nad konfiguracją wersji HTTP, ale chciałem, aby zatwierdzenia korzystały z SSH zamiast HTTP. Bezpośredni dostęp SSH do innych serwerów byłby bonusem, gdyby było to możliwe.
ahanson
To taki irytujący problem.
stronniczość
Rozumiem, że HAProxy obsługuje teraz to poprzez SNI. Chociaż nie byłem jeszcze w stanie tego uruchomić.
Carel

Odpowiedzi:

24

Nie sądzę, aby SSH oparte na nazwach było możliwe, biorąc pod uwagę sposób działania protokołu.

Oto kilka alternatyw.

  • Możesz skonfigurować hosta, który odpowiada na port 22, aby działał jako brama. Następnie możesz skonfigurować serwer ssh, aby przekazywał żądania do wewnątrz na podstawie klucza. Przykład bramy SSH z kluczami

  • Możesz dostosować klienta do używania tego hosta jako serwera proxy. Oznacza to, że będzie ssh do hosta bramy, a następnie użyje tego hosta do nawiązania połączenia z hostem wewnętrznym. Serwer proxy SSH z konfiguracją klienta .

  • Możesz także skonfigurować prosty serwer proxy HTTP na brzegu. Następnie użyj tego, aby zezwolić na połączenia przychodzące. SSH przez serwer proxy HTTP .

Oczywiście w przypadku wszystkich powyższych elementów upewnienie się, że poprawnie skonfigurujesz i zablokujesz bramę, jest bardzo ważne.

Zoredache
źródło
18

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_IGNOREwiadomość 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.1a 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ć Hostnagłó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.
kasperd
źródło
Czy potrafisz wyjaśnić, co masz na myśli the gateway can use the destination IP address to find out which server to send the packet to, w rozwiązaniu IPv6?
Jacob Ford
@JacobFord Kluczem do zrozumienia tego zdania jest to, że używam słowa brama, a nie proxy . Dzięki IPv6 otrzymujesz wystarczającą liczbę adresów IP, aby przypisać jeden do każdego hosta i nadal masz adresy do oszczędzenia. Wszystkie komputery, które umieścisz za serwerem proxy, będą miały swój własny adres IP, na który nazwy powinny być rozpoznawane. Dzięki temu nie potrzebujesz już serwera proxy, klienci wysyłają ruch na adres IP żądanego hosta i możesz kierować ruch bezpośrednio tam.
kasperd
w części dotyczącej obejścia znajduje się stosunkowo nowy przełącznik poleceń dla ssh -J, więc możesz użyć: ssh -J użytkownik1 @ front użytkownik2 @ cel
PMN
3

Nie wierzę, że jest to coś, co byłoby możliwe, przynajmniej tak, jak to opisałeś, chociaż chciałbym, aby udowodniono mi, że się mylę. Nie wydaje się, że klient wysyła nazwę hosta, z którym chce się połączyć (przynajmniej w sposób jawny). Pierwszym krokiem połączenia SSH wydaje się być ustawienie szyfrowania.

Ponadto wystąpiłyby problemy z weryfikacją klucza hosta. Klienci SSH zweryfikują klucze na podstawie adresu IP oraz nazwy hosta. Miałbyś wiele nazw hostów z różnymi kluczami, ale z tym samym adresem IP, z którym się łączysz.

Możliwym rozwiązaniem byłoby posiadanie hosta „bastionowego”, w którym klienci mogliby ssh do tej maszyny, uzyskać normalną (lub w razie potrzeby ograniczoną) powłokę, a następnie stamtąd mogliby ssh do wewnętrznych hostów.

znak
źródło
Koncepcja bastionu jest tym, co obecnie konfigurujemy, ale jest problematyczna dla mojej kontroli wersji (bez dodatkowego wysiłku).
ahanson
To bardzo denerwujące, że ssh nie wysyła fqdn i obsługuje DNS po stronie klienta. Wydaje mi się, że ludzie nie martwili się publicznym rozproszeniem IP i NATem, kiedy stworzono większość protokołów poziomu aplikacji TCP / IP. Ponieważ, na poważnie, powinieneś być w stanie wykonać NAT oparty na FQDN za pomocą iptables (tj. Filtrów jądra).
stronniczość
Klucz hosta może być kluczem odwrotnego proxy. Jest to korzyść, jeśli ufasz bezpieczeństwu backendu, ponieważ masz kontrolę nad siecią wewnętrzną i hostami.
rox0r
@bias Nie można wykonywać translacji NAT na podstawie nazwy hosta, nawet jeśli protokół wyższego poziomu wysyła nazwę hosta. Powodem, dla którego nie można tego zrobić, jest wybór zaplecza po odebraniu pakietu SYN, ale nazwa hosta jest wysyłana dopiero po przetworzeniu SYN i zwróceniu SYN-ACK. Możesz jednak użyć bardzo cienkiego serwera proxy warstwy aplikacji dla protokołów wysyłających nazwę hosta, takich jak http i https.
kasperd
3

Na bloku jest nowy dzieciak. Piper SSH przekieruje twoje połączenie na podstawie wstępnie zdefiniowanych nazw użytkowników, które powinny być mapowane na hosty wewnętrzne. To najlepsze rozwiązanie w kontekście odwrotnego proxy.

SSh Piper na github

Moje pierwsze testy potwierdziły, że SSH i SFTP oba działają.

Király István
źródło
To skutecznie atak MITM. Oczekuję, że zepsuje się w każdej konfiguracji chronionej przed atakami MITM.
kasperd
1
W rzeczywistości gospodarzem jest zarówno gospodarz, jak i mężczyzna pośrodku, dlatego tylko ci dwaj są zaangażowani.
Király István
@ KirályIstván to niesamowity projekt i dowodzi, że inne odpowiedzi nie są w 100% poprawne. Stworzyłem podobne narzędzie oparte na github.com/gliderlabs/sshfront i trochę bash / python (nie mogę go otworzyć, ale to nie jest tak interesujące - bash uruchamia klienta ssh, a Python służy jako moduł obsługi auth). Ale mam jeden problem z obecnym rozwiązaniem. To nie obsługuje sftp (scp działa). Wisi próba uruchomienia podsystemu debug1: Sending subsystem: sftp. Okazuje się, że przód koszuli nie obsługuje podsystemów. Czy wspierasz rąbek w pipecie SSh?
Maciek Sawicki
1
@MaciekSawicki Nie jestem autorem. Po prostu używam tego w moim projekcie. .)
Király István
2

Zastanawiam się, czy nie można zmodyfikować Honeytrap (honeypot o niskiej interakcji), który ma tryb proxy, aby to osiągnąć.

Ten plaster miodu jest w stanie przekazać dowolny protokół do innego komputera. Dodanie systemu vhost opartego na nazwie (zaimplementowanego przez Apache) może uczynić go idealnym odwrotnym proxy dla dowolnego protokołu nie?

Nie mam umiejętności, aby to osiągnąć, ale może to być świetny projekt.

Kafeine
źródło
świetny pomysł!
stronniczość
1
Funkcja vhost w Apache polega na tym, że klient wysyła nazwę hosta, zanim jakiekolwiek dane zostaną wysłane z serwera. Protokół SSH nie wymaga takiej nazwy hosta, więc nie widzę, jak byś zaczął wdrażać taką funkcję. Ale jeśli możesz rozwinąć swoje pomysły, chętnie przedstawię dowód koncepcji lub powiem, dlaczego to nie zadziała.
kasperd
1

Wraz ze wzrostem liczby portów / hostów, do których chcesz uzyskać dostęp za zaporą, zwiększa się wygoda VPN.

Jednak nie lubię VPN.

alex
źródło
1

z powodu tego, jak działa ssh, myślę, że to niemożliwe. Podobnie jak w przypadku protokołu https trzeba mieć różne (zewnętrzne) adresy IP dla różnych hostów, ponieważ brama nie wie, gdzie chcesz się połączyć, ponieważ wszystko w ssh jest zakodowane

Jure1873
źródło