this.is.rootnode

this.is.rootnode

Kiedy słyszysz tą nazwę, cichutko lub głośno. Kiedy przypominasz sobie te wszystkie piękne chwile spędzone w czarnej konsoli. To magiczne miejsce będące twoim wirtualnym domem, gdzie samotność nie istnieje. To jest Rootnode, a ja jestem jego administratorem.

Jun 7 / 4:30pm

O ograniczaniu SSH, chrootowalnym SFTP i problemach z szyfrowanym FTP.

SSH jest dobre. Każdy kto miał styczność z tym protokołem lub przewalał swoje pliki za pomocą SFTP wie, że nie ma nic lepszego na świecie. Po SSH można puszczać rsynca, wyświetlać zdalne okna, tunelować się przez Sri Lankę, czy też wypuścić na świat swojego domowego pożeracza prądu schowanego za dziesięcioma NATami.

SSH to władza, a władzę trzeba ograniczać. Szczególnie, gdy mamy do czynienia z użytkownikami, którzy sami mogą sobie zrobić krzywdę lub im po prostu nie ufamy. A z reguły nie ufamy nikomu, kto ma quotę większą niż 4KB.

Jail

Co i po co ograniczać?

Wyobraźmy sobie, że mamy serwer backupowy. Większość śmiertelników łączy się do swojego serwera backupowego jednym z protokołów, którym można wysłać dane i przesyła swoje pornuchy w odosobnione miejsce, tak aby w razie fuckupu systemu nie pozostać na lodzie. Akcję powtarzamy cyklicznie tworząc, czy to backup różnicowy, czy też przyrostowy, czy jakikolwiek inny.

Schemat wydaje się oczywisty i prawidłowy. Jedna radość naszego użytkownika przeminie, kiedy okaże się, że jego tajne hasło dupa.8 wycieknie gdzieś na zewnątrz i konto zostanie w brzydki sposób przejęte przez tajwańskich hakierów. Oczywiście jak na prawdziwych włamywaczy przystało oprócz zrobienia rm -rf, podglądną sobie także skrypty backupowe i przy okazji zanihilują wszystkie przyrosty, różnice i odrosty wykonane przez naszego rsynca, radośnie odpalanego z crontaba o północy.

I mówiąc bardzo rasistowsko, wtedy jesteśmy w czarnej dupie. Jak to powiedział bachus niedawno: nie mam nic do murzynów, każdy powinien mieć jednego.

Po wielkiej tragedii jednak nabieramy sprytu i wpadamy na pomysł, że może byłoby lepiej, gdyby to nasz serwer backupowy łączył się do naszego komputera i pobierał backupy.

Oczywiście w takiej sytuacji też istnieje szansa zaliczenia fuckupu i wycieku naszych danych, bo co by się stało gdyby ktoś przejął serwer backupowy. Zakładam jednak, że maszyna backupowa jest cytadelą naszej infrastruktury (a nawet jest poza nią na wypadek pożaru), a dostęp ma jedynie administrator logujący się z jednego IP, po kluczu 4096 bitowym chronionym hasłem i odciskiem palca oraz skanem siatkówki oka. Użytkownicy natomiast mają dostęp do swoich backupów albo na żądanie, albo za pomocą zasobu NFS wyeksportowanego w trybie read-only. Zupełnie tak samo jak to ma miejsce na Rootnode.

Ograniczenie dostępu przy tworzeniu backupu.

Ale wracając do tematu. Naszym zadaniem jest udostępnienie dostępu do plików z poziomu serwera backupowego. Ale nie chcemy dawać pełnego dostępu SSH, ponieważ ktoś z poziomu serwera backupowego mógłby nam zrobić krzywdę. Chcemy, aby tylko rsync mógł się połączyć i ściągnąć co potrzebuje.

Aby była pełna automatyzacja, czyli serwera backupowy mógł się bezproblemowo łączyć do maszyny naszego użytkownika tworzymy sobie zestaw kluczy RSA i klucz publiczny dodajemy do maszyny użytkownika (np. za pomocą fajnej komendy ssh-copy-id).

Kiedy już mamy klucze sprawdzamy, czy możemy się połączyć za pomocą ssh do maszyny użytkownika. Jeśli zobaczymy prompt shella to znaczy, że wszystko jest OK. Teraz naszym zadaniem jest ograniczenie, aby użytkownik nie dostawał shella, a nadal było możliwe kopiowanie danych. Jak już wspomniałem wcześniej korzystamy z rsync, więc trzeba użytkownikowi dać możliwość uruchomienia jedynie polecenia rsync --server

Przy okazji ograniczymy też kilka innych rzeczy, które możnaby zrobić z poziomu serwera backupowego. Dodajemy do pliku ~/.ssh/authorized_keys na maszynie użytkownika przed kluczem RSA taki ciąg znaków:

 
no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty,
command="/usr/local/bin/backup.sh",from="89.11.22.33" ssh-rsa
AAABB$TUTAJ_RESZTA_KLUCZA_RSA 
Zakazujemy tworzenie ptysiów, forwardowanie X11 i inne takie. Co najważniejsze to ograniczenie skąd może przyjść połączenie korzystające z tego klucza (from=) oraz polecenie jakie może zostać uruchomione (command=). Nie możemy w command wrzucić jedynie command="rsync --server", ponieważ to po prostu nie zabangla. Możemy natomiast wrzucić informację, że przez to połączenie można uruchomić nasz skrypt shellowy /usr/local/bin/backup.sh.

Naszym zadaniem jest doprowadzenie do wymiany plików za pomocą programu rsync. Jednocześnie chcemy zabezpieczyć się przed brudasami, którzy będą próbować nam w polecenie wcisnąć znaki specjalne typu ` lub & próbując przepchnąć przez połączenie jakieś dodatkowe rzeczy, np. rm -rf ;). Wiemy też, że polecenie jakie zostanie wykonane przez SSH umieszczane jest w zmiennej $SSH_ORIGINAL_COMMAND. Wykorzystujemy to w naszym skrypcie backup.sh:



#!/bin/sh
case "$SSH_ORIGINAL_COMMAND" in
        *\&*)
                 echo "Rejected"
                 ;;
        *\(*)
                 echo "Rejected"
                 ;;
        *\{*)
                 echo "Rejected"
                 ;;
        *\;*)
                 echo "Rejected"
                 ;;
        *\<*)
                 echo "Rejected"
                 ;;
        *\`*)
                 echo "Rejected"
                 ;;
        rsync\ --server*)
                 $SSH_ORIGINAL_COMMAND
                 ;;
        *)
                 echo "Rejected"
                 ;;
esac




Powyższe znaczy tyle co:
Jeśli w poleceniu SSH znajdziesz jakiś znak specjalny to ujeb połączenie komunikatem Rejected. Jeśli natomiast spotkasz coś zaczynającego się odd 'rsync --server' to uruchom.

Dzięki naszej specyficznej konfiguracji pliku ~/.ssh/authorized_keys
mamy pewność, że:
* połączenie przyjdzie z hosta, z którego powinno
* będzie to polecenie rsyncowe

Ograniczenie dostępu dla SFTP

Wyobraźmy sobie sytuację, że chcemy udostępnić naszej księgowej jeden katalog do pobierania faktur wygenerowanych na serwerze WWW. Księgowa nie powinna dodawać własnych faktur, ani tym bardziej zmieniać istniejących, dlatego konieczny jest dostęp read-only.

Pamiętam jeszcze czasy, jak kombinowaliśmy z obsługą SCPonly tworząc chrootowane środowiska dla użytkownika, który ma dostęp do danych szyfrowanym kanałem, ale nie ma dostępu do shella. Dozzie w końcu bazując na sftp-server wyklikał znakomitą binarkę sftponly, które umożliwiało dostęp bez shella, bez konieczności tworzenia całego środowiska wraz z /devem, bibliotekami i innymi niepotrzebnymi nikomu rzeczami.

Na szczęście w SSH od wersji 5.1 dostępny jest mechanizm Match, który jest chwalebny, ale czasami potrafi wkurwić, ze względu na konieczność specyficznych uprawnień. Ale o tym za chwilę.

Przykładowy mechanizm Match definiowany w pliku /etc/ssh/sshd_config wygląda następująco:

 
Match Group sftponly 
 ChrootDirectory %h 
 ForceCommand internal-sftp 
 AllowTcpForwarding no 

Robi on nic innego jak:

  • dla użytkowników z grupy pasującej do sftponly 
  • chrootnij do katalogu $HOME - znacznik %h to $HOME 
  • wymuś uruchomienie internal-sftp 
  • nie pozwalaj na forwardowanie portów 

Oczywiście możemy tam dorzucić jeszcze kilka innych opcji. Bardzo ważną rzeczą jest, aby w konfigu zamienić linię:

 
Subsystem sftp /usr/lib/openssh/sftp-server 

na linię:

 
Subsystem sftp internal-sftp 

Ponieważ przy mechanizmie Match korzystamy z wewnętrznego sftp. Po zmianach trzeba zrestartować sshd.

Teraz dodajemy naszego użytkownika do grupy sftponly i nadajemy specyficzne uprawnienia na katalog domowy:

  • właścicielem katalogu domowego musi być użytkownik root. 
  • grupa nie może mieć praw zapisu do katalogu 

Powyższe oznacza tyle, że katalog /home/ksiegowosc musi mięć właściciela i grupę ustawioną na root:sftponly i prawa dostępu na 750.

W konsekwencji przekłada się to na to, że po zalogowaniu przez SFTP użytkownik nie może utworzyć żadnego własnego pliku i katalogu, ale za to będzie chrootowany.

Aby ominąć to ograniczenie wystarczy wewnątrz katalogu domowego użytkownika utworzyć katalog public i nadać mu właściciela ksiegowosc:sftponly oraz prawa do zapisu (700 wystarczy).

Użytkownikowi księgowść można zmienić shella na /bin/false. Należy pamiętać, aby shell ten był wyszczególniony w pliku /etc/shells.

Bezpieczne FTP, czy aby na pewno?

Taka konfiguracja może rodzić poważne ograniczenia. Np. w sytuacji gdzie serwer www musi zapisywać tam swoje pliki i wymagane są jakieś specyficzne uprawnienia na pliki lub katalogi, które zaburzają wymagania chrootowanego katalogu.

W takiej sytuacji pozostaje nam jedynie postawienie serwera ftp. Polecam vsftpd. Konfiguracja nie jest skomplikowana. W pliku konfiguracyjnym ustawiamy takie parametry jak:

 
anonymous_enable=NO 
local_enable=YES 
chroot_local_user=YES 
ssl_enable=YES 
rsa_cert_file=/etc/ssl/certs/vsftpd.pem 
force_anon_data_ssl=YES 
force_anon_logins_ssl=YES 
force_local_data_ssl=YES 
force_local_logins_ssl=YES 
log_ftp_protocol=YES 
ssl_tlsv1=YES 
ssl_sslv2=NO 
ssl_sslv3=YES 
max_clients=100 
max_login_fails=3 
max_per_ip=15 

Powyższe opcje same się opisują. To co robimy to tworzymy chrootowane środowiska dla lokalnych użytkowników (czyli tych zapisanych w /etc/passwd) oraz uruchamiamy obsługę SSL i TLS. Jeśli nie mamy dedykowanego certyfikatu dla naszej domeny pozostaje nam utworzyć własny za pomocą polecenia:

 
$ openssl req -x509 -nodes -days 3650 -newkey rsa:1024 \
 -keyout /etc/vsftpd/vsftpd.pem -out /etc/vsftpd/vsftpd.pem 
Ustawiamy ważność certyfikatu na 10 lat. Zostanie nam zadanych kilka pytań takich jak kod kraju, common name (tu wpisujemy nazwę domeny) i podobne.

Restartujemy serwer i jeśli wszystko poszło zgodnie z planem nasi użytkownicy mają chrootowany dostęp w trybie read-only. Tutaj także możemy użytkownikowi spokojnie ustawić shella na /bin/false.

Przy połączeniu wybieramy opcję Explicit SSL lub Explicit TLS i jeśli łączymy się z komputera znajdującego się za NATem musimy wybrać opcję Passive Mode - w WinSCP jest w zakładce Connection.

Przy serwerze FTP pojawia się jeszcze jeden problem. Co jeśli nasz wspaniałomyślny użytkownik zapomni, że powinien łączyć się po SSL/TLS. Serwer odrzuci połączenie, ale nie to jest problemem. Wraz z inicjalizacją połączenia użytkownik wysyła w poleceniu USER i PASS swój login i hasło. Tak więc, jeśli mamy wrednego sniffera w sieci zgarnie on nasze dane dostępowe - pomimo tego, że serwer działa jedynie w trybie szyfrowanym. Sytuacja niezbyt fajna.

Dzieje się tak dlatego, że vsftpd nie rozłącza się automatycznie, gdy użytkownik zaczyna do niego gadać kanałem nieszyfrowanym.

Wystarczy odrobinę zmodyfikować kod vsftpd, aby serwer działał prawidłowo. Dla wersji 2.0.7 poniżej prosty patch:

 
diff -ur vsftpd-2.0.7/prelogin.c vsftpd.src/prelogin.c 
--- vsftpd-2.0.7/prelogin.c 2008-02-12 04:57:07.000000000 +0100 
+++ vsftpd.src/prelogin.c 2009-12-01 00:34:46.000000000 +0100 
@@ -175,8 +175,9 @@ 
 tunable_force_local_logins_ssl) 
 { 
 vsf_cmdio_write( 
- p_sess, FTP_LOGINERR, "Non-anonymous sessions must use encryption."); 
- str_empty(&p_sess->user_str); 
+ p_sess, FTP_LOGINERR, "HOAX Non-anonymous sessions must use encryption."); 
+ str_empty(&p_sess->user_str); 
+ vsf_sysutil_exit(0); 
 return; 
 } 
 if (tunable_ssl_enable && is_anon && !p_sess->control_use_ssl && 

Przy okazji do zwracanego komunikatu dodaliśmy słówko HOAX, aby mieć pewność (np. oglądając sesję w FileZilli), że serwer wykonuje poprawnie akcje rozłączania.

Straszliwie się rozpisałem. Właściwie na dzisiaj to tyle.

2 comments

Jan 08, 2011
heathenreel said...
Może więcej takich wpisów "Rootnode od kuchni"?
Sep 03, 2011
ziroux said...
Czołem,

> (...) w sytuacji gdzie serwer www musi zapisywać tam swoje pliki i wymagane są jakieś specyficzne uprawnienia na pliki lub katalogi, które zaburzają wymagania chrootowanego katalogu (...)

Zamiast specjalnie dorzucać ftp polecam małą przeróbkę sshd http://sftpfilecontrol.sourceforge.net - jest obecnie do wersji od 5.0 do 5.4
Jako że od wersji właśnie 5.4 sshd ma parametr "Subsystem sftp internal-sftp -u UMASK" to na własne potrzeby (u mnie np. 5.5) bardzo łatwo przerobić sobie patcha i wyciąć z sftpfilecontrol całą zabawę z umask coby przypadkiem nie kolidowało ale zostawić permity na chmod i chown - bardzo fajne w świecie, gdzie co klient sftp albo user to inna filozofia na uprawnienia. Inna sprawa, że jest jakiś problem ze wskoczeniem umask powyżej 0022, więc zamiast tego dobrodziejstwa używam starego, dobrego "session optional pam_umask.so umask=0027" w "/etc/pam.d/sshd" i bangla jak admin przykazał. A jak się ktoś akurat może zalogować normalnie przez ssh to mu wskakuje umask odpowiedni z /etc/profile (co podobno ma problem przy uzyciu "ssh host polecenie", ale to inna historia).

Zdrówko,
ziroux

Leave a comment...