Konfiguracja Apache'a

Wprowadzenie

Apache jest jednym z najpopularniejszych serwerów WWW. Co prawda ostatnio pojawiła się konkurencja w postaci serwera nginx, niemniej jak na razie jego pozycja jest niezagrożona. W dalszej części będziemy się zajmować konfiguracją serwera Apache pod Linuksem, a dokładniej pod SuSE Linux, chociaż na większości dystrybucji wygląda to bardzo podobnie.

Instalacja

Minimalny zestaw pakietów, żeby zadziałał serwer:

  • apache2
  • apache2-prefork

Dodatkowo możemy też doinstalować przykładowe strony:

  • apache2-example-pages

Po instalacji pakietów oraz uruchomieniu serwera

# rcapache2 start

otwieramy przeglądarkę i wprowadzamy adres http://localhost/. Powinniśmy zobaczyć stronę startową, w przypadku SLES 10 jest to po prostu napis „It works!”. Dokładniej, pod tym adresem widoczna jest zawartość katalogu /srv/www/htdocs, przy czym nie wskazując żadnego pliku (żądanym zasobem w tym adresie to „”), serwer domyślnie pokaże zawartość pliku index.html (domyślnie, w tym katalogu są kopiowane strony z pakietu apache2-example-pages). Katalog /srv/www/htdocs jest nazywany DocumentRoot. Ponieważ mamy mapowanie katalogu /srv/www/htdocs na adres http://localhost/ , możemy więc tworzyć podkatalogi, czyli np. /srv/www/htdocs/admin/ będzie dostępny pod adresem http://localhost/admin/

Pliki konfiguracyjne

Pliki konfiguracyjne są w katalogu /etc/apache2. Kilka istotniejszych plików z tego katalogu:

httpd.conf – główny plik konfiguracyjny

default-server.conf - podstawowa konfiguracja serwera (np. określony tam jest DocumentRoot oraz podstawowy dostęp dla DocumentRoot)

vhost.d/ - pliki konfiguracyjne wirtualnych hostów

uid.conf – tożsamość, w kontekście której będzie działa proces serwera Apache (domyślnie użytownik wwwrun i grupa www)

listen.conf – tam określamy porty i interfejsy, na których serwer będzie nasłuchiwał

server-tuning.conf – parametry, które pozwalają na zwiększenie wydajności serwera w pewnych sytuacjach, np. przy bardzo dużym obciążeniu na bardzo wydajnym serwerze

error.conf – szereg wpisów określających zachowanie w przypadku, gdy żądanie nie może być zrealizowane poprawnie (np. są określone dokumentu dla poszczególnych kodów błędów, przykładowo 404 czy 500)

ssl-global.conf – globalna konfiguracja dla protokołu SSL.

Podstawy konfiguracji

Podstawowym elementem konfiguracji są dyrektywy (np. Include, Order, Allow), które można grupować i aplikować tylko do wybranych katalogów poprzez konstrukcję <Directory "/path/to/catalog">...</Directory>. Znak # służy do tworzenia komentarzy.

Kilka istotniejszych dyrektyw:

  • DocumentRoot – główny katalog udostępniany pod ścieżką „/”
  • <Directory "katalog">...</Directory> - zgrupowanie dyrektyw i zaaplikowanie do wybranego katalogu
  • AllowOverride - określa, czy dyrektywy mogą być nadpisane przez odpowiedniki z pliku .htaccess
  • Alias "/files" "c:/downloads" – udostępnia dowolny katalog pod wskazanym adresem, w tym przykładzie „c:\downloads” pod adresem „/files”
    http://www.thewebhostinghero.com/tutorials/apache-alias.html
  • ScriptAlias /cgi-bin/ /usr/local/apache2/cgi-bin/ – określa, że tworzony jest adres (/cgi-bin) wskazujący na wybrany katalog (/usr/local/apache2/cgi-bin), z którego każdy plik apache traktuje jak program do wykonania.
    http://httpd.apache.org/docs/2.0/howto/cgi.html

Po edycji plików konfiguracyjnych, serwer apache należy przeładować komendą

# rcapache2 reload

a czasami nawet zrestartować

# rcapache2 restart

Zawsze też możemy sprawdzić poprawność plików konfiguracyjnych poleceniem

# apache2ctl configtest

Hosty wirtualne

W klasycznym podejściu, któremu przyjrzeliśmy się do tej pory, serwer WWW może pod adresem http://localhost/ (lub poprzez FQDN, np. http://www.onet.pl/) prezentować zawartość dokładnie jednego katalogu (zwykle htdocs). To prowadzi do sytuacji, że każda domena jest obsługiwana przez inny komputer. Tak podejście byłoby oczywiście bardzo kłopotliwe, jednak aby zaradzić problemowi mamy mechanizm tzw. wirtualnych hostów. Dzięki niemu jeden serwer WWW może obsługiwać wiele domen. Działa to w skrócie następująco:

  1. klient wpisuje w przeglądarce adres www.domena.pl
  2. na podstawie wpisu w DNS, żądanie jest kierowane do określonego serwera WWW
  3. ponieważ w żądaniu HTTP dołączone jest pole nagłówka Host, serwer WWW może ustalić, z jakiego adresu domenowego klient chce otrzymać zasób
  4. na podstawie nazwy domeny, serwer odszukuje odpowiedni host wirtualny, który zajmuje się dalszą obsługą żądania.

Hosty wirtualne mają swoje pliki konfiguracji w katalogu /etc/apache2/vhosts.d/

Wybrane dyrektywy do konfiguracji hosta wirtualnego:

  • ServerAdmin – adres email administratora
  • ServerName – pełna nazwa hosta, na podstawie której virtualny host będzie wyszukiwany dla danego żądania (można też wprowadzić w definicji hosta, czyli w )
  • DocumentRoot – katalog z plikami dla danej domny
  • ErrorLog – plik dziennika dla błędów (wwwrun musi mieć prawa do zapisu)
  • CustomLog – ogól908ny plik dziennika (wwwrun musi mieć prawa do zapisu)
  • <Directory "document root"> - konfiguracja katalogu z plikami (nie wolno o tym zapomnieć).

Utwórzmy zatem przykładowy host wirtualny dla domeny www.example.com:

  1. W pliku /etc/hosts modyfikujemy wiersz opisujący localhost do postaci:
    127.0.0.1	localhost www.example.com
    
  2. Tworzymy katalog /srv/www/vhosts/www.example.com
    mkdir -p /srv/www/vhosts/www.example.com
    
  3. Tworzymy w katalogu pliku index.html
    echo "www.example.com" > /srv/www/vhosts/www.example.com
    
  4. Ponieważ z katalogu vhosts.d wczytywane są wszystkie pliki konfiguracyjne wirtualnych hostów z rozszerzeniem .conf (reguła w httpd.conf), tworzymy plik konfiguracyjny na bazie szablonu:
    cd /etc/apache/vhosts.d/
    cp vhost.template www.example.com.conf
    
  5. Teraz musimy dokonać kilku zmian w właśnie utworzonym pliku www.example.com.conf
    1. Poprawiamy wartość ServerAdmin na np. webmaster@example.com
    2. Poprawiamy wartość ServerName na www.example.com (WAŻNE)
    3. Poprawiamy ścieżki do plików ErrorLog i CustomLog na jakieś sensowne, np.
      ErrorLog /var/log/apache2/www.example.com-error_log
      CustomLog /var/log/apache2/www.example.com-access_log combined
      
    4. Ponieważ najpewniej nie potrzebujemy skryptów CGI, dodajemy znak # przed wierszami:
      1. zaczynającym się od ScriptAlias
      2. sekcji Directory, która opisuje katalog kończący się z cgi-bin
    5. W ostatniej sekcji Directory poprawiamy katalog na
      /srv/www/vhosts/www.example.com
      
      (pierwotnie jest /srv/www/vhosts/dummy-host.example.com)
    6. Zmiany zapisujemy
  6. To wszystko! Teraz restartujemy serwer apache poleceniem
    # rcapache2 restart
    
    a następnie uruchamiamy przeglądarkę i wpisujemy adres
    http://www.example.com/
    
    Powinien się w niej pojawić napis
    www.example.com
    

Sterowanie dostępem do zasobów serwera WWW

Przy domyślnej konfiguracji serwera WWW, dostęp do udostępnianych zasobów mają wszyscy użytkownicy. Piszę udostępnianych, ponieważ domyślnie dostęp do np. katalogu głównego serwera jest zablokowany, zresztą przy wykorzystywaniu ogólnych mechanizmów, które dalej zostaną omówione.

Na poziomie samego serwera Apache mamy dwa mechanizmy sterowania dostępem:

  • Na podstawie adresu IP,
  • Na podstawie nazwy użytkownika i hasła.

Sterowanie dostępem na podstawie adresów IP

Do uzyskania tej funkcjonalności wykorzystujemy dyrektywy Order, Allow i Deny, które osadzamy w sekcji Directory, określającej dla jakiego katalogu definiujemy prawa dostępu. Jak się łatwo domyślić, Allow określa kto ma dostęp do katalogu, a Deny określa kto dostępu nie ma. Z Order jest z kolei związana kwestia wyliczania, czy dany host ostatecznie ma dostęp do witryny, ponieważ Allow i Deny mogą występować razem. Wyliczanie działa zgodnie z zasadą „ostatnie na wierzchu” i najlepiej chyba będzie pokazać to na małym przykładzie:

<Directory "/srv/www/htdocs">
Order deny,allow
Deny from all
Allow from 192.168.0.0/24
</Directory>

Dyrektywa Order określa, że wyliczana jest najpierw dyrektywa Deny, a potem Allow. Po wyliczeniu Deny mamy sytuację, że nikt nie ma dostępu. Następnie wyliczamy Allow, który określa, że dostęp jest dla hostów z siecie 192.168.0.0/24. Więc ostatecznie dostęp jest zabroniony dla wszystkich hostów oprócz tych z sieci 192.168.0.0/24, ponieważ Allow tak jakby nadpisał wcześniej wyliczone prawa na obszarze hostów z 192.168.0.0/24, dając im dostęp.

Co możemy pisać po from:

  • all, co oznacza wszystkie hosty,
  • konkretne adresy IP,
  • podsieci w postaci adres/maska lub CIDR, czyli adres/NN (np. 192.168.0.0/24 lub 192.168.0.0/255.255.255.0)
  • fragment adresu IP, np. 192.168
  • domenę, co oznacza tą domenę (np. example.com) i wszystkie poddomeny (np. www.example.com)

Poza wyliczaniem uprawnień kolejność podana po Order ma jeszcze jedną konsekwencję:

  • Order Allow,Deny oznacza, że dla hostów nie ujętych na listach, domyślnie dostęp będzie zabroniony,
  • Order Deny,Allow oznacza, że dla hostów nie ujętych na listach, domyślnie dostęp będzie dozwolony.

Sterowanie dostępem na podstawie nazwy użytkownika i hasła

W tym scenariuszu korzystamy z mechanizmu zwanego Basic Authentication. Utworzenie tego typu zabezpieczenia składa się z dwóch kroków: utworzenie bazy użytkowników oraz zabezpieczenie wybranego katalogu.

Utworzenie użytkowników

Wykorzystujemy do tego polecenie htpasswd2. Podstawowe użycia:

  1. Dodanie użytkownika lub ustawienie mu hasła:
    # htpasswd2 /etc/apache2/htpasswd username
    
  2. Usunięcie użytkownika
    # htpasswd2 -D /etc/apache2/htpasswd username
    

Plik /etc/apache2/htpasswd występujący w powyższych poleceniach jest plikiem z użytkownikami i hasłami. Jeśli pliku nie ma, to przy tworzeniu pierwszego użytkownika dodajemy opcję -c; polecenie wtedy wygląda następująco:

# htpasswd2 -c /etc/apache2/htpasswd username 

Bardzo ważne jest, aby plik ten był dostępny dla użytkownika systemu wwwrun, ale nie był dostępny przez WWW.

Ustawienie dostępu dla katalogu

Do sekcji opisującej katalog dodajemy następujące wiersze:

AuthType Basic
AuthName "Opis obszaru chroniony"
AuthUserFile /etc/apache2/htpasswd
Require user pawel bea

Znaczenie wierszy jest raczej jasne, krótki komentarz tylko do ostatniego wiersza. Dyrektywa Require określa, kto będzie miał dostęp do zasobów. Oczywiście wprowadzeni użytkownicy muszą być wcześniej zdefiniowani w pliku z hasłami. Mamy dwa sposoby określania dostępu:

  • Require user pawel bea – określamy wprost, którzy użytkownicy otrzymają dostęp po zalogowaniu (kolejnych użytkowników rozdzielamy znakiem spacji)
  • Require valid-user – dowolny uwierzytelniony użytkownik otrzyma dostęp.

Na koniec jeszcze krótki komentarz jak wygląda protokół w przypadku uwierzytelnienia na podstawie login i hasła.

  • Klient wysyła żądanie do chronionego zasobu
  • Serwer odpowiada kodem 401 oraz podaje opis obszaru, z którego pochodzi zasób (tzw. Realm)
    HTTP/1.1 401 Authorization Required
    ...
    WWW-Authenticate: Basic realm="Secure Area"
    ...
    
  • W przeglądarce pojawia się w tym momencie najczęściej monit o podanie loginu i hasła. Po wprowadzeniu danych, klient ponawia żądanie dołączając pole nagłówka Authorization z wprowadzonym loginem i hasłem (postaci: login:hasło) zakodowanym Base64
    ...
    Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
    
  • Jeśli login i hasło jest poprawne oraz użytkownik ma dostęp, serwer odpowiada żądanym zasobem.

Ładnie jest to opisane w Wikipedii: http://en.wikipedia.org/wiki/Basic_access_authentication.

Uwaga: cała transmisja domyślnie nie jest w żaden sposób zabezpieczona, więc kluczowym elementem dla wykorzystania powyższego mechanizmu jest konfiguracja protokołu SSL (omówiona w dalszej części).

Zanim przejdziemy dalej warto odnotować, że możliwości sterowania dostępem są dużo bardziej rozbudowane od wyżej przedstawionych. Od prostej i naturalnej możliwości tworzenia grup użytkowników, do sprzężenia logowania z serwerem LDAP oraz ustawieniu wielu różnych opcji czy szczegółów. Zainteresowani bez problemu znajdą w tym temacie sporo artykułów w Sieci, niemało jest też na samych stronach Apache’a.

Apache + SSL

W wielu sytuacjach udostępnienie aplikacji za pomocą szyfrowanego kanału SSL jest kluczowe dla bezpiecznego funkcjonowania aplikacji. Okazuje się, że konfiguracja serwera Apache do pracy z SSL-em jest całkiem łatwe. Ponieważ żywię przekonanie, że przykłady potrafią często najlepiej przedstawić zagadnienie, dalej przedstawię kroki prowadzące doprowadzą do sytuacji, w której:

  • pod adresem https://localhost/ będzie dostępna witryna z zawartością katalogu htdocs, oraz
  • pod adresem https://www.example.com/ będzie dostępny utworzony wcześniej wirtualny host dla domeny www.example.com.

Pierwsze wspólne kroki dla obu punktów są następujące:

  1. Najpierw generujemy certyfikat wraz z kluczem prywatnym (najszybszy sposób):
    # openssl req -new -x509 -out apachecert.pem
    
    Podczas generowania zostaniemy zapytani o szereg danych, ważne, aby jako common name podać domenę, dla której certyfikat jest generowany (wpisujemy www.example.com, patrz uwaga poniżej).
    Ostatecznie w pliku apachecert.pem jest certyfikat, a w pliku privkey.pem – klucz prywatny. Warto też dla wygody ściągnąć hasło z klucza prywatnego
    # openssl rsa -in privkey.pem -out apachekey.pem
    
    Uwaga: żeby było bardzo ok, należałoby wygenerować dwa certyfikaty z odpowiednio wpisanym z common name localhost i www.example.com. Żeby kroki były jak najprostsze, ograniczymy się do utworzenia jednego certyfikatu dla domeny www.example.com.
  2. Kopiujemy
    apachecert.pem do /etc/apache2/ssl.crt/
    apachekey.pem do /etc/apache2/ssl.key/
  3. Na koniec robimy kilka zmian w pliku /etc/sysconfig/apache2:
    • APACHE_START_TIMEOUT="10"
      (żebyśmy mieli czas na wpisania hasła do klucza prywatnego – w naszym przypadku można pominąć, ponieważ zdjęliśmy hasło z klucza prywatnego)
    • APACHE_SERVER_FLAGS="SSL" (np. włącza słuchanie na porcie 443)

Dalej, najpierw uruchomimy adres https://localhost/. Aby to zrobić, najpierw tworzymy plik wirtualnego hosta (nazwa localhost-ssl.conf nie większego znaczenia, o ile ma rozszerzenie .conf):

# cp /etc/apache2/vhosts/vhost-ssl.template /etc/apache2/vhosts/localhost-ssl.conf

Następnie odnajdujemy w pliku localhost-ssl.conf linie z dyrektywami SSLCertificateFile oraz SSLCertificateKeyFile i ustawiamy je następująco:

SSLCertificateFile /etc/apache2/ssl.crt/apachecert.pem
SSLCertificateKeyFile /etc/apache2/ssl.key/apachekey.pem

Warto w tym miejscu odnotować, że istotne są jeszcze wiersze SSLEngine oraz SSLCipherSuite, ale są one w tym pliku już poprawnie ustawione. Ostatecznie restartujemy serwer

# rcapache2 restart

I localhost na SSL-u powinien już działać.

Teraz uruchomimy na SSL-u wirtualny host www.example.com. Jak poprzednio, najpierw tworzymy plik wirtualnego hosta:

# cp /etc/apache2/vhosts/vhost-ssl.template /etc/apache2/vhosts/www.exampel.com-ssl.conf

Następnie wykonujemy w pliku www.example.com-ssl.conf następujące zmiany:

  1. W deklaracji sekcji VirtualHost, zmieniamy _default_ na www.example.com
  2. Odkomentowujemy ServerName i ServerAdmin (dla domeny www.example.com są tam już poprawne wartości, dla innych domen musielibyśmy je odpowiednio poprawić)
  3. Jak poprzednio, poprawiamy wartości dla SSLCertificateFile i SSLCertificateKeyFile:
    SSLCertificateFile /etc/apache2/ssl.crt/apachecert.pem
    SSLCertificateKeyFile /etc/apache2/ssl.key/apachekey.pem
    
  4. Musimy dopisać sekcję ustawiającą uprawnienia dla katalogu /srv/www/vhosts/www.example.com (wersja chyba najkrótsza), ponieważ domyślnie takiej sekcji w pliku nie ma:
    <Directory /srv/www/vhosts/www.example.com>
    Order allow,denny
    Allow from all
    </Directory>
    

Ostatecznie restartujemy serwer apache

# rcapache2 restart

i sprawdzamy, że pod adresem https://www.example.com/ mamy dostępną stronę z oczekiwaną zawartością.

No koniec jedno spostrzeżenie: jeśli byśmy utworzyli plik wirtualnego hosta dla www.example.com:443 (czyli dla SSL), a nie utworzyli wirtualnego hosta www.example.com (czyli bez SSL), to po wpisaniu w przeglądarce http://www.example.com/ pojawiłaby się taka sama witryna, jak ta dostępna pod adresem http://localhost/.