Zdalna kontrola pulpitu w oparciu o darmowe i otwarte oprogramowanie

Wstęp

Na codzień staram się unikać korzystania z zamkniętego oprogramowania, jednak czasem jest ciężko. Do niedawna byłem zmuszony korzystać z Teamviewera. Jest to bardzo dobre narzędzie do pomocy zdalnej i do tego jest darmowe do niekomercyjnego użytku. Mimo wszystko, Teamviewer jest oprogramowaniem zamkniętym i ostatnio zastanawiałem się czy uda się znaleźć otwartą alternatywę.

Przeczesywałem Internet w poszukiwaniu podobnego narzędzia, ale nie znalazłem nic na takim poziomie funkcjonalnym. Chciałem narzędzie, w którym użytkownik będzie w stanie uruchomić serwer jednym kliknięciem, a asystent będzie w stanie podłączyć się do niego jednym kliknięciem. Wszystko to oczywiście powinno być zabezpieczone i dostępne niezależnie od miejsca przebywania użytkownika i asystenta (nawet za NATami).

Techniczne trzy słowa

Wszędzie dostawałem wyniki dotyczące VNC, a z tego co wiem większość programów nie wspiera szyfrowania transmisji. Do tego pozostawał problem NATów.

Po rekonesansie dotyczącym VNC dowiedziałem się, że istnieje możliwość połączeń odwrotnych w VNC. Innymi słowy jest możliwość uruchomienia serwera po stronie użytkownika i połączenia się do klienta po stronie asystenta. Po tym wpadlem na pomysł, żeby otworzyć jakiś niestandardowy port na swoim firewallu i przekierować na swój komputer. Jednak nie do końca mnie to satysfakcjonowało, bo korzystając z takiego rozwiązania mógłbym pomóc znajomym siedząc tylko w swoim mieszkaniu, a obraz i tak byłby przesyłany nieszyfrowaną transmisją. Co nie jest zbyt bezpieczne.

I w ten sposób natknąłem się na artykuł Michaela Franzla: http://michaelfranzl.com/2014/09/07/remote-desktop-via-ssh-teamviewer-alternative/

Gdzie opisuje on jak można rozwiązać mój problem korzystając z tunelowania SSH (opisanego pobieżnie przeze mnie w tym wpisie). Pisząc ten wpis będę się posiłkował wpisem Michaela (zwłaszcza grafikami, które uważam za intuicyjne).

Czego potrzebuję?

Po stronie użytkownika będziemy potrzebowali:

  • serwera VNC (ja będę używał x11vnc)
  • klienta SSH (u mnie OpenSSH)

Po stronie asystenta będzie potrzebny:

  • klient VNC (tutaj będzie przeglądarka od TightVNC vncviewer)
  • klient SSH (j.w.)

Niezależnie:

  • Serwer SSH (najlepiej swój, o czym zaraz)

Zasada działania

Wychodząc od początku:

  • asystent siedzi przy komputerze sittinghere
  • uzytkownik siedzi przy komputerze overthere
  • gdzieś niezależnie od nich znajduje się publicznie widoczny serwer SSH (na grafikach nazwany hopper) przez który będą “przeskakiwały” nasze dane.

Z założenia serwer nasłuchuje na porcie 22 (chociaż nie ma przeciwskazań żeby to był dowolny inny port). Będziemy się logować na dowolnego użytkownika który ma możliwość utworzenia socketa. Będziemy używali portów wysokich (~7000) żeby nie było problemów z uprawnieniami roota.

Sytuacja początkowa wygląda następująco:

vncfun1

Firewalle blokują dostęp z zewnątrz na maszyny sittinghere i overthere, a w serwerze hopper mamy otwarty port 22. sittinghere i overthere są za NATami, więc żeby otworzyć przejście trzeba zainicjować połączenie z wewnątrz sieci.

W tej sytuacji po stronie klienta musimy uruchomić serwer VNC. Przykładowym poleceniem jest:

x11vnc -nap -noxdamage -passwd 123456 -bg

Po tym poleceniu sytuacja wygląda następująco:

vncfun2Serwer VNC nasłuchuje domyślnie na porcie 5900. Trzymamy się tego, że nie było problemów, chociaż prawda jest taka, że może to być na innym porcie. Jeżeli połączenie będzie miało dotyczyć innego ekranu, wtedy dostaniemy numer portu + numer ekranu. Innymi słowy, dla DISPLAY=0 dostaniemy port 5900, ale dla DISPLAY=1 dostaniemy port 5901.

Teraz z komputera użytkownika musimy się połączyć do naszego serwera SSH.

ssh -R 7001:localhost:5900 uzytkownik@hopper

Korzystając z tego polecenia prosimy sshd na serwerze, żeby po spięciu połączenia otworzył port 7001  i przekierował wszystko na to połączenie którego własnie używamy.  Innymi słowy wszyscy którzy się połączą na tym serwerze na port 7001, wygląda to tak jakby szyfrowanym łączem połączyli się do komputera overthere na port 5900.

Na obecną chwilę sytuacja wygląda następująco:

vncfun3To jest wszystko co robi użytkownik ze swojego komputera.

Po stronie asystenta (sittinghere) trzeba teraz spiąć podobny tunel. Można do tego celu użyć polecenia:

ssh -L 7002:localhost:7001 uzytkownik@hopper

To polecenie sprawia, że na komputerze asystenta jest spinane połączenie pomiędzy portem 7001 na hopperze i portem 7002 na localhoście, czyli sittinghere. Innymi słowy jak ktoś połączy się do tego portu na tym komputerze to tak jakby się połączył do portu 7001 na serwerze hopper. Czyli idąc dalej, tak jakby się połączył na port 5900 na overthere.

Po wykonaniu tego polecenia całość wygląda następująco:

vncfun4Ostatnim krokiem jest uruchomienie przeglądarki vnc, która podłączy się do komputera lokalnego na port 7002.

Można do tego użyć polecenia:

vncviewer localhost:7002

W ten sposób dochodzimy do sytuacji końcowej:

vncfun5W tym momencie powinniśmy zobaczyć ekran użytkownika przy komputerze asystenta.

Dodatkowe zabezpieczenie

Dlatego wcześniej polecałem posiadanie swojego serwera SSH, że po pierwsze na tym serwerze nasze dane będą rozszyfrowywane (i dopiero szyfrowane z powrotem), a po drugie będziemy w stanie ograniczyć dostęp użytkownikom, którzy się łączą do serwera SSH jak poniżej.

Do połączenia pomiędzy dwoma znajomymi nie będzie problemu. Często jednak zdarza się, że z serwerem SSH łączą się osoby, którym nie do końca możemy ufać i wypadałoby dodatkowo ograniczyć możliwości jakie są w stanie wykonać użytkownicy i asystenci na naszym serwerze.

Można do tego celu skorzystać z paczki SleepShell. Po skonfigurowaniu tego shella, użytkownik nie będzie w stanie zrobić nic, poza otwarciem portu do tunelu SSH.

Chroot Directory w serwerze SSH

Podchodząc do sprawy bardziej paranoicznie, możemy zabezpieczyć się w razie wydostania się użytkownika ze sleepshella. Do tego celu możemy skorzystać z funkcji konfiguracyjnej serwera SSH ChrootDirectory.  Przygotujmy więc środowisko działające po chrootnięciu (ja będę pisał na przykładzie bash w FreeBSD, ale dla Linuksów to na pewno podobny proces):

Tworzymy sobie katalog do którego będziemy się chrootować i wszystkie potrzebne w nim podkatalogi (u mnie jest to katalog /remoteaccess).

# mkdir -p /remoteaccess/{dev, home, lib, libexec, usr}
# mkdir /remoteaccess/home/{asystent,uzytkownik}
# mkdir -p /remoteaccess/usr/local/bin

Kopiuję domyślny shell moich użytkowników do odpowiadającego im loginshella. Osobiście proponuję skopiować link do rbasha, żeby dostęp był jeszcze bardziej okrojony:

# cp -p /usr/local/bin/rbash /remoteaccess/usr/local/bin

Tworzę grupę użytkowników remoteaccess do której będą trafiali użytkownicy z ograniczonymi uprawnieniami po SSH.

# pw group add remoteaccess

Tworzę dwa konta użytkowników asystent i uzytkownik i dodaję ich do grupy remoteaccess. Do tego celu można użyć narzedzi adduser lub pw. Dla przykładu:

# pw user add asystent -g remoteaccess
# pw user add uzytkownik -g remoteaccess

Teraz próbuję się zalogować na dowolnego z nich korzystając z SSH. Dostaję errory, że brakuje pewnych bibliotek lub modułów, więc o wszystkie poproszone kopiuję do mojego chrootowanego środowiska:

# cp -p /lib/co_potrzeba /remoteaccess/lib/co_potrzeba (itd.)
lub
# cp -p /usr/local/lib/co_potrzeba /remoteaccess/lib/co_potrzeba (itd.)

I zmienić domyślnego shella dla użytkowników na odpowiedniego shella:

# chsh -s /usr/local/bin/rbash asystent
# chsh -s /usr/local/bin/rbash uzytkownik

Można jeszcze hardenować rbash:

# cd ~asystent
# for i in .bash_login .bash_profile .bash_logout .profile; do echo ". .bashrc" > $i; done
# echo "export PATH=/home/ruser/usr/bin" > .bashrc
# cd ~uzytkownik
# for i in .bash_login .bash_profile .bash_logout .profile; do echo ". .bashrc" > $i; done
# echo "export PATH=/usr/local/bin" > .bashrc

Po zrobieniu wszystkiego co było konieczne do zalogowania się należy dodać dwie linie do pliku konfiguracyjnego serwera ssh (sshd_config):

Match Group remoteaccess
ChrootDirectory /remoteaccess

Po restarcie serwera użytkownicy asystent i uzytkownik przez swoją grupę wpadną do puli chrootowanych użytkowników do folderu /remoteaccess. Przez to będą mogli korzystać tylko z narzędzi, które znajdują się w ich folderze rootowym. Innymi słowy w tym momencie będą mogli korzystać z kilku podstawowych elementów basha. I z niczego innego.

Podsumowanie

Cały proces da się oskryptować i przenieść na dowolną platformę wspierającą SSH oraz VNC. Myślę, że niedługo pojawi się jakieś repo na Githubie dotyczące takich skryptów.

Zgodnie z tym co pisał Michael połączenie jest lepsze niż w przypadku komercyjnych rozwiązań. Ja nie miałem jeszcze okazji przetestować tego w rzeczywistym świecie, ale w domowym laboratorium trzeba przyznać że efekt jest zadowalający.

Źródła

Edycje / notki:

Topu podrzucił mi pomysł żebym skorzystał z command w konfiguracji serwera ssh i w ten sposób ograniczył dostęp do wykonywania niektórych komend. Jeżeli ktoś testował takie rozwiązanie to proszę o informację.

Chcąc dodatkowo zabezpieczyć użytkownika przed wykonywaniem komend dotarłem do rbash (Restricted Bash). O ograniczeniach narzuconych na rbasha można sobie poczytać w Internetach i man bash -> RESTRICTED BASH.

Nawiązując do kolejnej uwagi Topu, z poziomu rbasha można było wywołać bash. Poprawka we wpisie już zrobiona.

Zgodnie z tym co napisał KaP, można było zaszkodzić serwerowi przez uruchomienie w nieskończonej pętli shella. Mogło się to skończyć skończeniem PIDów na serwerze. Zaproponował on użycie SleepShella, który idealnie pasuje do podanego problemu.

Mam zamiar jeszcze dodać szyfrowanie po kluczach tak żeby nie trzeba było podawać hasła do użytkownika remote za każdym razem. Jakieś propozycje?

Dodaj komentarz