Laboratorium 9 i 10: chroot, ssl

strict warning: Only variables should be passed by reference in /usr/share/drupal6/modules/book/book.module on line 559.

1. Bezpieczne środowisko uruchamiania aplikacji

Jeśli aplikacja (np. serwer http) jest uruchamiana pod systemem operacyjnym, w szczególności np. z uprawnieniami roota, w przypadku umiejętnego wykorzystania błędu atakujący może uzyskać bardzo szerokie uprawnienia, m.in. nieograniczony dostęp do systemu plików.
Aby uniknąć takich zagrożeń:

  • należy uruchamiać aplikację z minimalnymi niezbędnymi uprawnieniami, w szczególności jako użytkownik inny niż root. Takie podejście jest szeroko stosowane, np. serwer http apache może być uruchomiony jako użytkownik inny niż root (zwykle http lub www);
  • jeśli to możliwe, trzeba zadbać, aby proces miał dostęp do ograniczonych zasobów systemowych.

1.1. Ograniczenie przestrzeni aktywności procesu - chroot

W systemie Linux można uruchomić dowolny proces (i jego procesy potomne) ze zmienionym katalogiem głównym za pomocą polecenia chroot (oraz wywołanie systemowego chroot()). Uruchomiony w ten sposób proces nie może powrócić do rzeczywistego katalogu głównego w systemie plików. Można więc przygotować inny niż główny katalog zawierający pliki niezbędne dla aplikacji np. bibliotki, czy pliki konfiguracyjne. Zwykle trzeba odwzorować część struktury katalogów i plików konfiguracyjnych, np. /etc, /usr/lib/, /var, /proc. Mechanizm chroot może być też przydatny dla celów testowania oprogramowania lub np. zrealizowania środowiska programistycznego ze specyficznym zestawem bibliotek i narzędzi. Do zalet należy także zaliczyć prostotę użycia i brak konieczności modyfikacji jądra systemu operacyjnego.

1.1.1. Przykład użycia polecenia chroot

Jeśli docelowy katalog został przygotowany, można uruchomić proces, np.:

chroot /przygotowany_katalog /usr/sbin/apache2 -k start

1.1.2. Wady chroot

Sam w sobie chroot nie posiada mechanizmów limitowania zasobów używanych przez proces, użycie go nie zabezpiecza więc przed atakami DoS.

Uruchomienie polecenia chroot wymaga uprawnień roota.

Chroot nie zmienia UID i GID procesu. Jeśli aplikacja sama nie zmieni UID, będzie wykonywana z prawami roota.

Istnieją sposoby, które pozwalają procesowi uruchomionemu z prawami roota, wyjście poza określony poleceniem chroot katalog -
opisano je w lekturze uzupełniajacej http://linux-vserver.org/Secure_chroot_Barrier.

Można przy okazji zacytować fragment listu Alana Coxa: "(...) chroot is not and never has been a security tool." :)

1.1.3 Przydatne triki

Jeżeli chcemy mimo wszystko przygotować katalog chroot, możemy ułatwić sobie życie na dwa sposoby. Po pierwsze można przygotować minimalną instalację jakiejś dystrybucji aby uniknąć zgadywania które pliki są potrzebne do pracy naszego programu; dotyczy to zwłaszcza bibliotek korzystających z wielu plików pomocniczych. Trzeba jednak pamiętać, aby nie umieszczać w systemie zbędnych programów z bitem SUID które mogłyby być wykorzystane do "ucieczki" z chroota. Warto też pamiętać o poleceniu

mount --bind /proc /przygotowany_katalog/proc
mount --rbind /dev /przygotowany_katalog/dev

pierwsza wersja powoduje, że pliki z systemu plików (lub jakiegoś katalogu z systemu plików) widocznego jako /proc będą też widoczne w katalogu /przygotowany_katalog/proc. Nie dotyczy to jednak katalogów podmonotwanych wewnątrz proc, np. /proc/sys/fs/binfmt_misc. Natomiast druga wersja obejmuje także katalogi podmontowane wewnątrz bindowanego katalogu.

1.2. Kontentery

Naturalnym roszerzeniem chroota jest objęcie ograniczeniami także innych zasobów niż system plików. W idealnym świecie uruchomiony w takim ulepszonym chroocie - czyli kontenerze - program powinien zachowywać się tak, jakby był uruchomiony na maszynie wirtualnej na tym samym komputerze i z oddzielną kopią tego samego jądra. Jednak ponieważ naprawdę nie uruchamiamy nowego jądra, to nie występuje narzut spowodowany koniecznością obsługi wywołań syswtemowych przez dwa jądra, wirtualizacją urządzeń sprzętowych itp. Możemy także, jeżeli tego potrzebujemy, osłabić izolację w jakimś miejscu aby programy bardziej efektywnie współpracowały. Ograniczeniem jest konieczność używania takiego samego jądra we wszystkich używanych kontenerach i systemie bazowym.

W Linuksie implementacja kontenerów opiera się na uogólnieniu pojącia chroot do pojęcia namespace. Są one następujące i pozwalają izolować:

       Namespace   Constant          Isolates
       Cgroup      CLONE_NEWCGROUP   Cgroup root directory
       IPC         CLONE_NEWIPC      System V IPC, POSIX message queues
       Network     CLONE_NEWNET      Network devices, stacks, ports, etc.
       Mount       CLONE_NEWNS       Mount points
       PID         CLONE_NEWPID      Process IDs
       User        CLONE_NEWUSER     User and group IDs
       UTS         CLONE_NEWUTS      Hostname and NIS domain name

Tabelka pochodzi z man namespace(7). Tę stronę podręcznika systemowego należy przeczytać, a strony zależne przejrzeć. Należy także zapoznać się z możliwościami nakładania ograniczeń na procesy za pomocą grup cgroups(7).

Na podstawie tej infrastuktury zbudowanych jest kilka środowisk dających wygodne narzędzia do wirtualizacji, są to. np. Linux Containers (LXC), OpenVZ, Docker.

1.3. Docker

Docker składa się z procesu serwera, który odpowiada za faktyczne tworzenie kontenerów, ich modyfikacje, itp. oraz klienta, którego funkcją jest zapewniania możliwości korzystania z serwera. Aby skonfigurować nowy kontener tworzymy w pustym katalogu plik o nazwie Dockerfile z przykładową zawartością:

FROM ubuntu
 
MAINTAINER Kto To Wie
 
RUN apt-get update && apt-get install -y apache2 
 
EXPOSE 80
 
CMD ["/usr/sbin/apache2", "-D", "FOREGROUND"]

pierwsza linia oznacza kontener, z którego korzystamy jako bazowego. Docker ma centralny rejestr kontenerów, z których można korzystać przy tworzeniu swoich, są w nim w szczególności podstawowe instalacje różnych dystrybucji. Aby uniezależnić się od twórców Dockera można też uruchomić własny taki serwer.

Linia z wpisem MAINTAINER jest wymagana, ale ma charakter informacyjny. Następnie mamy (być może kilka) linii z poleceniami RUN, opisujące polecenia potrzebne do przygotowania kontenera. Aby nie wykonywać zbyt często tych samych poleceń Docker zapamiętuje stan kontenera po każdej linijce z pliku Dockerfile w sposób analogiczny do snapshotów zwykłych maszyn wirtualnych.

Polecenie EXPOSE 80 udostępnia światu port 80. Polecenie CMD określa komendę, która będzie służyła do faktycznego uruchomienia kontenera - w tym przypadku jest to serwer apache. Gdy podany proces zakończy działanie docker zatrzyma wszystkie procesy w kontenerze

Aby uruchomić nasz kontener wchodzimy do odpowiedniego katalogu i piszemy

docker build .
docker run

Jeśli chcemy szybko uruchomić polecenie w kontenerze bez pisania dockerfile'a, możemy napisać

docker run -t -i ubuntu /bin/bash

1.3.1 Ciąg dalszy

Plik Dockerfile ma jeszcze wiele możliwości, których nie poznaliśmy. Dokumentacja Dockera jest dostepna pod adresem http://docker.io.

2. SSL

2.1. Wersje i nazewnictwo

SSL to standard ochrony warstwy aplikacji wykonany wraz z implementacją przez Netscape Communications Corporation. Powstały wersje SSL v1, v2, v3. Od 1996 roku standard jest rozwijany przez grupe roboczą IETF na bazie SSL v3 jako TLS (Transport Layer Security), powstały wersje standardu TLS 1.0 - RFC 2246, TLS 1.1 - RFC 4346, TLS 1.2 - RFC 5246 i trwają prace nad standardem TLS 1.3.
Wersje SSL v1 i SSL v2 są uznawane za niebezpieczne. Po ataku poodle (http://www.cert.gov.pl/cer/wiadomosci/zagrozenia-i-podatnosc/724,POODLE-...) SSL v3 również przestał być uważany za bezpieczny.

2.2. Implementacje

Najbardziej popularna to OpenSSL (http://www.openssl.org/). Zawiera implementację standardów SSL v2, v3, TLS 1.0-1,2. OpenSSL to biblioteka wraz z zestawem narzędzi (przede wszystkim program openssl). Alternatywna implementacja: GnuTLS - TLS 1.1-1.2 i SSL v3 (http://www.gnu.org/software/gnutls/).

2.3. Koncepcja działania

SSL/TLS zapewnia zestawienie szyfrowanego i uwierzytelnionego kanału dla warstwy aplikacji. W modelu ISO/OSI SSL/TLS znajduje się w
warstwie prezentacji, zatem ponad warstwą transportu (TCP/UDP), ale poniżej warstwy aplikacji.

2.3.1. Przebieg nawiązywania połączenia

  • Serwer uwierzytelniania się przed klientem.
  • Klient i serwer wymieniają między sobą informacje pozwalające uzgodnić im najsilniejsze mechanizmy kryptograficzne jakie obie
    strony wspierają np. algorytm funkcji skrótu kryptograficznego, algorytm służący do szyfrowania.
  • Jeżeli zachodzi taka potrzeba klient uwierzytelnia się przed serwerem.
  • Klient i serwer używając mechanizmów kryptografii klucza publicznego uzgadniają między sobą tajny klucz sesji (shared secret).
  • Zostaje ustanowiony bezpieczny kanał wymiany danych między obiema stronami, szyfrowany kluczem sesji.

Koncepcja SSL/TLS wykorzystuje idee kryptografi symetrycznej i niesymetrycznej. Kryptografia niesymetryczna służy do uwierzytelnienia i ustalenia w bezpieczny sposób klucza sesji. Następnie wykorzystywana jest kryptografia symetryczna (i ustalony klucz sesji), co zwiększa wydajność.

2.3.2. Przebieg sprawdzania autentyczności

To, że uda się zestawić kanał szyfrowany nie oznacza jeszcze, że wiadomo z jakim serwerem http mamy połączenie... Co się stanie, jeśli ktoś podszyje się pod serwer http (co jest popularnym sposobem wyłudzania haseł i innych poufnych informacji)? Przeglądarka, łącząc się z serwerem http, dostaje od niego certyfikat, w którym znajduje się klucz publiczny serwera. Oprócz klucza publicznego
certyfikat zawiera także inne dane:

  • numer wersji standardu X.509 (jest to standard budowy certyfikatu),
  • numer wystawionego certyfikatu,
  • identyfikator algorytmu podpisu certyfikatu,
  • nazwa funkcji skrótu kryptograficznego użyta przez wystawcę,
  • nazwa wystawcy certyfikatu,
  • daty ważności certyfikatu,
  • nazwa podmiotu dla którego wystawiany jest certyfikat,
  • parametry klucza publicznego podmiotu,
  • klucz publiczny podmiotu,
  • opcjonalne rozszerzenia,
  • podpis certyfikatu składany na nim przez wystawcę.

Dzieki tym danym, certyfikat poświadcza związek osoby z kluczami, którymi się posługuje. Ponieważ certyfikat jest podpisany, przeglądarka może sprawdzić autentyczność podpisu. Dokonuje tego za pomocą certyfikatów zawierających klucze publiczne Centrów Certyfikacji (Certification Authorities, CAs), jednostek wystawiających certyfikaty. CAs spełniają rolę zaufanej trzeciej strony (trusted third party) i odpowiedzialne są za poświadczanie tożsamości klientów. Zanim CA podpisze certyfikat podmiotu, sprawdza jego tożsamość (np. przez sprawdzenie dowodu osobistego, czy uprawnień do posługiwania się nazwą instytucji).

Przeglądarka weryfikuje certyfikat za pomocą zbioru certyfikatów zaufanych CA. Jeśli weryfikacja podpisu nie powiedzie się, generowane jest ostrzeżenie. Ostrzeżenia są tworzone także, jeśli nie zgadzają się inne elementy certyfikatu, np: data ważności, czy nazwa domeny (gdy serwer wyśle certyfikat zawierający inna nazwę domenową, niż ta która znajduje się w URL).

2.3.3. Własne CA

Na potrzeby własnej instytucji można utworzyć własne CA i z jego pomocą generować certyfikaty. Może to ograniczyć koszty utrzymywania certyfikatów podpisywanych przez zaufane CA np. Verisign. Tak utworzone certyfikaty spowodują, że np. przeglądarka internetowa lub inne aplikacje będą generować ostrzeżenia związane z brakiem możliwości weryfikacji podpisu (brakiem zaufania do CA podpisującej wystawiony certyfikat). By uniknąć generowania takich ostrzeżeń, certyfikat własnego CA należy zaimportować do przeglądarki.

2.3.4. PKI

Wyżej opisana koncepcja obiegu informacji związanych z wystawianiem certyfikatów dla podmiotów, wraz z istnieniem organizacji CA poświadczających związek certyfikatu z konkretną instytucją lub osobą nosi nazwę Infrastruktury Klucza Publicznego (ang. Public Key Infrastructure), w skrócie PKI.

Struktura PKI podlega standaryzacji i opiera się na dwóch elementach. Jednym jest standard X.509 opisujący strukturę certyfikatów oraz drugi określany mianem PKCS (ang. Public Key Cryptography Standards).

3. Ćwiczenia

3.1. Docker

# dpkg -i docker.io_1.6.2~dfsg1-1ubuntu4~14.04.1_i386.deb

W dalszej części scenariusza trzeba pamiętać o tym, że Docker Hub nie odróżnia systemów gościa 32- i 64-bitowych, gdyż zwykle nie ma to znaczenia. Na szczęście mamy do dyspozycji obrazy z 32-bitowym Ubuntu:

docker pull ioft/i386-ubuntu:16.04
docker run -i -t ioft/i386-ubuntu:16.04 bash -l
  • Wykonaj polecenia z treści powyższego modułu.

  • Napisz

docker images
  • Skasuj wszyskie obrazy.
  • Utwórz w katalogu z plikiem Dokerfile podkatalog a z plikami a1 i a2 i podkatalog b z plikiem b1.
  • Zmodyfikuj Dockerfile tak, aby kopia podkatalogu a była widoczna w kontenerze (polcenie ADD)
  • Zmodyfikuj Dockerfile tak, aby podkatalog b był widoczy w kontenerze (nie jako kopia).

3.2. SSL

Będziemy posługiwać się skryptami CA.sh, by uniknąć złożoności bezpośredniego wywołania programu openssl. Zachęcamy jednak do przejrzenia zawartości CA.sh i używanych w nim parametrów wywołania programu openssl.

3.2.1. Tworzenie nowego CA

/usr/lib/ssl/misc/CA.sh -newca

następnie:

enter to create

Wymagane hasło służy do ochrony klucza prywatnego, należy je podać, następnie:

Country Name (2 letter code) [AU]:PL
State or Province Name (full name) [Some-State]:Mazowieckie
Locality Name (eg, city) []:Warszawa
Organization Name (eg, company) [Internet Widgits Pty Ltd]:MIM UW
Organizational Unit Name (eg, section) []:SO LAB
Common Name (eg, YOUR name) []:CA SO LAB
Email Address []:ca@mimuw.edu.pl
Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []: pomijamy
An optional company name []: pomijamy

W tym miejscu pojawi się pytanie o hasło chroniące nasz klucz prywatny (ustawione w poprzednim kroku). Hasło to jest potrzebne, ponieważ certyfikat tworzonego właśnie CA (root certificate, self-signed certificate) będzie podpisywany kluczem prywatnym tego CA.

W efekcie powstał certyfikat naszego CA w katalogu DemoCA/cacert.pem oraz klucz prywatny CA w DemoCA/private/cakey.pem.

3.2.2. Tworzenie prośby o certyfikację

Tworzymy parę kluczy i prośbę do CA o certyfikowanie wygenerowanego klucza publicznego.

/usr/lib/ssl/misc/CA.sh -newreq

Podajemy hasło do ochrony nowo tworzonego klucza prywatnego, następnie:

Country Name (2 letter code) [AU]:PL
State or Province Name (full name) [Some-State]:Mazowieckie
Locality Name (eg, city) []:Warszawa
Organization Name (eg, company) [Internet Widgits Pty Ltd]:MIM UW SO LAB
Organizational Unit Name (eg, section) []:SO LAB
Common Name (eg, YOUR name) []:solabXX.mimuw.edu.pl 
       (zastąp XX numerem twojej stacji roboczej)
Email Address []: solab13@mimuw.edu.pl
 
A challenge password []: pomijamy
An optional company name []: pomijamy
Request is in newreq.pem, private key is in newkey.pem.

Ponieważ klucz publiczny używać będziemy do zestawiania połączeń https, Common Name musi być nazwą hosta, dla którego wystawiamy certyfikat (inaczej przeglądarka internetowa będzie wyświetlać komunikat o niezgodności).

Usuwanie hasła z klucza prywatnego powstałego przy okazji wystawiania prośby:

openssl rsa -in newkey.pem -out newkey.pem

(wczytujemy i wypisujemy klucz prywatny, wczytanie wymaga podania hasła, bo zostało wcześniej ustawione).

Powyższy krok jest potrzebny, gdyż serwer http nie mógłby skorzystać z klucza prywatnego zabezpieczonego hasłem.

3.2.3. Wystawianie certyfikatu (podpisywanie utworzonej prośby kluczem prywatnym CA)

/usr/lib/ssl/misc/CA.sh -sign
 
Enter pass phrase for ./demoCA/private/cakey.pem:

Należy podać hasło ustawione przy okazji tworzenia CA. Hasło to chroni klucz prywatny CA, którym będzie wykonany podpis.

Certificate is to be certified until Nov 24 22:19:47 2010 GMT (365 days)
Sign the certificate? [y/n]:y

Standardowo certyfikat jest wystawiany na rok. By zmienić okres ważności certyfikatu, należy zmienić liczbę dni w pliku /usr/lib/ssl/misc/CA.sh. Zadziała to przy wystawianiu prośby, ale niestety zmienna DAYS w CA.sh nie jest używana przy podpisywaniu, więc przed podpisaniem należy zmienić liczbę dni w konfiguracji domyślnej w pliku: /etc/ssl/openssl.cnf albo zmodyfikować odpowiednio skrypt CA.sh.

W końcu gotowy certyfikat:

Signed certificate is in newcert.pem

3.2.4. Konfiguracja obsługi protokołu https na przykładzie serwera apache

Do katalogu /etc/ssl/certs/ skopiuj podpisany certyfikat wystawiony dla hosta (newcert.pem). Do katalogu /etc/ssl/private skopiuj odpowiadający certyfikatowi klucz prywatny (newkey.pem).

Należy teraz włączyć mod-ssl poleceniem:

a2enmod

po ukazaniu się listy dostępnych modułów, należy wybrać ssl.
Następnie trzeba włączyć obsługę konfiguracji ssl:

a2ensite

i wybrać default-ssl.

W pliku konfiguracyjnym /etc/apache2/sites-available/default-ssl ustawić odpowiednie nazwy zainstalowanego certyfikatu i klucza prywatnego:

    SSLCertificateFile    /etc/ssl/certs/newcert.pem
    SSLCertificateKeyFile /etc/ssl/private/newkey.pem

i zrestartować serwer http:

/etc/init.d/apache2 restart

Teraz już można się cieszyć dostępem po https. Można to sprawdzić przeglądarką:

https://solab13.mimuw.edu.pl/

Przeglądarka wyświetli ostrzeżenie mówiące o braku zaufania do CA, które podpisało certyfikat dla hosta solab13 (czyli CA, które stworzyliśmy na początku tych ćwiczeń). Jeśli ufasz swojemu lokalnemu CA, możesz zaimportować do przeglądarki certyfikat lokalnej CA z pliku DemoCA/cacert.pem. Po zaimportowaniu certyfikatu CA, przeładuj stronę - nie powinna już generować ostrzeżeń.

Połącz się ze stroną https://localhost.
Dlaczego generowane jest ostrzeżenie?

Uwaga!
W ogólności, choć istnieją przypadki szczególne, jeśli chcemy utrzymywać na naszej maszynie kolejną
treść (np. stronę) dostępną po https pod inną nazwą domenową, istnieje konieczność posiadania
kolejnego adresu IP lub użycia niestandardowego portu ponieważ najpierw uzgadniane jest szyfrowane połączenia, a potem przesyła się dane HTTP, w szczególności oczekiwaną nazwę hosta. Jeżeli ktoś wierzy, że problem da się rozwiązać, może zajrzeć na stronę https://en.wikipedia.org/wiki/Server_Name_Indication. Jeżeli kto wierzy, że rozwiązanie skomplikowanego problemu nie może być proste, może zajrzeć na stronę https://www.digicert.com/ssl-support/apache-secure-multiple-sites-sni.htm.

4. Literatura

Obowiązkowa: ten dokument.
Obowiązkowa: dokument dostępny na Ważniaku 2:
http://wazniak.mimuw.edu.pl/index.php?title=Bezpiecze%C5%84stwo_system%C...

Nieobowiązkowa:
http://linux-vserver.org/
http://www.openssl.org/
http://www.gnu.org/software/gnutls/