Apache: konfiguracja
Po tej lekcji: kursant zna układ plików konfiguracji Apache na Ubuntu i RHEL, stawia kilka stron na jednym serwerze przez VirtualHosty, włącza moduły, używa .htaccess i mod_rewrite, sprawdza konfigurację przed restartem i czyta logi.
Wstęp
Masz jeden serwer. Za pięć złotych miesięcznie albo pod biurkiem. A chcesz na nim postawić stronę kawiarni, stronę piekarni i swojego bloga. Trzy różne adresy, trzy różne strony. Kupujesz trzy serwery?
Nie. Jeden Apache obsłuży je wszystkie i jeszcze zostanie mu miejsce na czwartą. Tak działa większość tanich hostingów na świecie: setki stron na jednej maszynie. Dziś zrobisz to sam i przy okazji nauczysz się czytać konfigurację Apache, zamiast kopiować ją z forów i modlić się, żeby zadziałało.
Gdzie leży konfiguracja
Tu dwie rodziny rozjeżdżają się najbardziej. Ten sam Apache, dwie filozofie układania papierów.
[Ubuntu] Wszystko w /etc/apache2:
ls /etc/apache2apache2.conf
conf-available
conf-enabled
envvars
magic
mods-available
mods-enabled
ports.conf
sites-available
sites-enabledapache2.conf— główny plik, regulamin całego hotelu.ports.conf— na jakich portach słuchać (Listen 80, a przy SSLListen 443).sites-available— wszystkie strony, jakie masz w szufladzie.sites-enabled— strony, które są teraz włączone.mods-available/mods-enabled— to samo dla modułów.conf-available/conf-enabled— to samo dla dodatkowych kawałków konfiguracji.
Zobacz, co naprawdę leży w sites-enabled:
ls -l /etc/apache2/sites-enabled/lrwxrwxrwx 1 root root 35 Nov 3 16:28 000-default.conf -> ../sites-available/000-default.confTo nie kopie, tylko dowiązania symboliczne, strzałki do plików w sites-available. Szuflada available to magazyn szyldów, a enabled to ściana, na której wiszą te, które są aktualnie powieszone. Żeby powiesić albo zdjąć szyld, nie robisz dowiązań ręcznie. Debian dał Ci do tego narzędzia:
sudo a2ensite kawiarnia.test # włącz stronę
sudo a2dissite kawiarnia.test # wyłącz stronę
sudo a2enmod rewrite # włącz moduł
sudo a2dismod rewrite # wyłącz moduł
sudo a2enconf servername # włącz kawałek konfiguracjia2 to skrót od „apache2”, en od „enable”, dis od „disable”. Ktoś w Debianie bardzo nie lubił długich nazw.
[RHEL] Wszystko w /etc/httpd:
/etc/httpd/conf/httpd.conf— główny plik./etc/httpd/conf.d/— tu wrzucasz swoje pliki.conf, w tym strony. Każdy plik kończący się na.confjest wczytywany automatycznie./etc/httpd/conf.modules.d/— moduły.
Nie ma a2ensite. Chcesz włączyć stronę, kładziesz plik kawiarnia.conf w conf.d. Chcesz wyłączyć, zmieniasz nazwę na kawiarnia.conf.wylaczone albo przenosisz go gdzie indziej. Proste jak drzwi do stodoły. Ubuntu to szafa z szufladami i etykietami, RHEL to jedno pudło, do którego wrzucasz to, co ma działać.
VirtualHost, czyli wiele szyldów na jednym budynku
Pamiętasz z poprzedniej lekcji nagłówek Host: w rozmowie przeglądarki z serwerem? To jest cały sekret. Przeglądarka nie tylko łączy się z adresem IP serwera, ale też mówi: „Dzień dobry, przyszedłem do kawiarni”. Wszystkie trzy nazwy prowadzą do tego samego budynku, ale recepcjonista patrzy, do kogo gość przyszedł, i kieruje go do odpowiedniego skrzydła.
Każde takie skrzydło to VirtualHost, wirtualny serwer. Zrobimy dwa: kawiarnię i piekarnię.
Najpierw nazwy. Nie mamy prawdziwych domen, więc dopisujemy je do /etc/hosts na komputerze, z którego otwierasz strony. Na samym serwerze:
echo '127.0.0.1 kawiarnia.test piekarnia.test' | sudo tee -a /etc/hostsDomena .test jest zarezerwowana do testów i nigdy nie będzie prawdziwa w internecie. Bezpieczniej niż wymyślać kawiarnia.pl i potem się dziwić, czemu wchodzisz na cudzą stronę.
Folder na stronę:
sudo mkdir -p /var/www/kawiarnia.test/public
echo '<h1>Kawiarnia: dzis espresso za 5 zl</h1>' | sudo tee /var/www/kawiarnia.test/public/index.htmlI plik strony, [Ubuntu] /etc/apache2/sites-available/kawiarnia.test.conf, [RHEL] /etc/httpd/conf.d/kawiarnia.test.conf:
<VirtualHost *:80>
ServerName kawiarnia.test
ServerAlias www.kawiarnia.test
DocumentRoot /var/www/kawiarnia.test/public
ErrorLog ${APACHE_LOG_DIR}/kawiarnia_error.log
CustomLog ${APACHE_LOG_DIR}/kawiarnia_access.log combined
</VirtualHost>Linijka po linijce:
<VirtualHost *:80>— to skrzydło obsługuje gości na porcie 80, na każdym adresie.ServerName— główna nazwa, po której recepcja rozpoznaje gościa.ServerAlias— inne nazwy tej samej strony. Ludzie wpisują i zwww, i bez.DocumentRoot— folder, z którego bierzemy pliki.ErrorLogiCustomLog— osobne księgi skarg i gości dla tej strony. Gdy masz dziesięć stron, nie chcesz szukać błędu kawiarni wśród wizyt w piekarni.
[RHEL] ${APACHE_LOG_DIR} to zmienna z Ubuntu. Na RHEL piszesz ścieżki wprost: ErrorLog /var/log/httpd/kawiarnia_error.log.
Włączamy i sprawdzamy:
sudo a2ensite kawiarnia.test
sudo apachectl configtest
sudo systemctl reload apache2
curl http://kawiarnia.test/Enabling site kawiarnia.test.
To activate the new configuration, you need to run:
service apache2 reload
Syntax OK
<h1>Kawiarnia: dzis espresso za 5 zl</h1>apachectl configtest, czyli zmierz dwa razy, tnij raz
Zapamiętaj kolejność, bo to najważniejsza rzecz w tej lekcji: najpierw configtest, potem reload. Zawsze.
Dlaczego? Bo jak przeładujesz Apache z błędem w konfiguracji, możesz położyć nie tylko nową stronę, ale wszystkie strony na serwerze. Kawiarnia, piekarnia i blog lecą razem, bo pomyliłeś jedną literkę. configtest sprawdza całą konfigurację bez dotykania działającego serwera:
Syntax OKTo jest zdanie, które chcesz zobaczyć. A przy okazji pozbądźmy się ostrzeżenia z poprzedniej lekcji. Apache nie wie, jak się nazywa, więc mu powiemy:
echo 'ServerName localhost' | sudo tee /etc/apache2/conf-available/servername.conf
sudo a2enconf servername
sudo apachectl configtestSyntax OKBez AH00558. Cisza i spokój. [RHEL] Tam dopisujesz ServerName w /etc/httpd/conf/httpd.conf.
A teraz mapa wszystkich stron:
sudo apachectl -S*:80 is a NameVirtualHost
default server localhost (/etc/apache2/sites-enabled/000-default.conf:1)
port 80 namevhost localhost (/etc/apache2/sites-enabled/000-default.conf:1)
port 80 namevhost kawiarnia.test (/etc/apache2/sites-enabled/kawiarnia.test.conf:1)
alias www.kawiarnia.testZwróć uwagę na default server. Co się stanie, jak ktoś przyjdzie z nazwą, której nie znamy?
curl -s -H 'Host: nieznana.test' http://127.0.0.1/ | grep -o '<title>.*</title>'<title>Apache2 Ubuntu Default Page: It works</title>Trafia do pierwszego VirtualHosta na liście. Na Ubuntu to 000-default, i właśnie dlatego ma w nazwie trzy zera: żeby alfabetycznie był pierwszy. Jeśli go wyłączysz, domyślną stroną zostanie pierwsza alfabetycznie z Twoich. Gość, który pomylił drzwi, wyląduje w kawiarni. Czasem to dobrze, czasem bardzo źle.
Flaga -H w curlu ustawia nagłówek ręcznie. Super sztuczka do testów: sprawdzasz VirtualHosta, zanim jeszcze ustawisz DNS.
Directory, czyli kto ma klucz do których drzwi
Druga strona, piekarnia. Tym razem z plikami w /srv, bo pamiętasz z planu biurowca, /srv to dane udostępniane przez serwer:
<VirtualHost *:80>
ServerName piekarnia.test
DocumentRoot /srv/piekarnia
ErrorLog ${APACHE_LOG_DIR}/piekarnia_error.log
CustomLog ${APACHE_LOG_DIR}/piekarnia_access.log combined
</VirtualHost>sudo a2ensite piekarnia.test
sudo apachectl configtest && sudo systemctl reload apache2
curl -s -o /dev/null -w '%{http_code}\n' http://piekarnia.test/403Zakaz. Plik jest, prawa 644 są. Co jest grane? Log piekarni:
[authz_core:error] [client 127.0.0.1:55832] AH01630: client denied by server configuration: /srv/piekarnia/„Odmówiono przez konfigurację serwera”. W apache2.conf siedzi taki fragment:
<Directory />
Options FollowSymLinks
AllowOverride None
Require all denied
</Directory>Czyli: cały dysk, od korzenia, jest zamknięty dla gości. Potem jest wyjątek „ale /var/www jest otwarte”. A /srv? Wyjątek dla niego jest w pliku, tylko zakomentowany. Apache domyślnie nie pokazuje niczego spoza wyznaczonych miejsc. I bardzo dobrze, bo inaczej jedna literówka w DocumentRoot mogłaby wystawić w świat /etc.
Naprawa: dajemy klucz do tego jednego folderu, w pliku strony:
<Directory /srv/piekarnia>
Require all granted
</Directory><h1>Piekarnia: chleb od 6:00</h1><Directory> to zasady dla konkretnego folderu. Require all granted to „wpuszczaj wszystkich”, Require all denied to „nikogo”. Można też wpuszczać tylko wybrane adresy, na przykład Require ip 192.168.1.0/24 dla strony tylko dla biura.
[RHEL] Tu dochodzi SELinux: pliki w /srv nie mają domyślnie etykiety, którą Apache może czytać. O tym szczegółowo w następnej lekcji.
Moduły
Apache sam z siebie umie mało. Wszystko, co ciekawe, to moduły: szyfrowanie, przekierowania, kompresja, logowanie hasłem. Jak dodatki do odkurzacza: końcówka do szczelin, szczotka do mebli. Kupujesz jeden odkurzacz, a końcówki dokładasz.
apachectl -M # lista załadowanych modułów
sudo a2enmod rewrite # Ubuntu: włącz moduł
sudo systemctl restart apache2Po włączeniu lub wyłączeniu modułu dawaj restart, a nie reload. Przy zmianie stron wystarczy reload.
[RHEL] Moduły włącza się w plikach w /etc/httpd/conf.modules.d/, a wiele popularnych, w tym mod_rewrite, jest załadowanych od razu. Sprawdzisz przez httpd -M | grep rewrite.
.htaccess i mod_rewrite
.htaccess to karteczka z zasadami, którą przykleja się w konkretnym pokoju, zamiast zmieniać regulamin całego hotelu. Plik o tej nazwie w folderze strony może zmieniać ustawienia dla tego folderu. Kochają go hostingi współdzielone, bo klient nie ma dostępu do głównej konfiguracji, ale może wrzucić .htaccess do swojego folderu.
A mod_rewrite to moduł do przepisywania adresów. Gość prosi o /karta, a my po cichu dajemy mu menu.html. Albo gość prosi o stary adres, a my odsyłamy go pod nowy.
echo '<h1>Menu: espresso, latte, sernik</h1>' | sudo tee /var/www/kawiarnia.test/public/menu.html/var/www/kawiarnia.test/public/.htaccess:
RewriteEngine On
RewriteRule ^karta$ menu.html [L]
RewriteRule ^stare-menu$ /karta [R=301,L]Pierwsza reguła: adres karta po cichu pokazuje menu.html, gość nawet nie wie. [L] to „last”, czyli na tym koniec. Druga reguła: stary adres odsyła pod nowy z kodem 301, „przeprowadziliśmy się na stałe”. Przeglądarki i wyszukiwarki zapamiętują nowy adres.
Test:
curl -s -o /dev/null -w '%{http_code}\n' http://kawiarnia.test/karta404404? Reguła jest, plik jest. A Apache ignoruje .htaccess w całości. Dlaczego? Bo domyślnie AllowOverride None. Właściciel hotelu nie pozwala przyklejać karteczek. Trzeba mu to wyraźnie dopuścić w pliku strony:
<Directory /var/www/kawiarnia.test/public>
AllowOverride All
Require all granted
</Directory>sudo apachectl configtest && sudo systemctl reload apache2
curl -s -o /dev/null -w '%{http_code}\n' http://kawiarnia.test/karta500Gorzej! Teraz recepcjonista zemdlał. Ale log mówi dokładnie, czemu. O tym w typowym błędzie. Spoiler: brakuje modułu.
sudo a2enmod rewrite
sudo systemctl restart apache2
curl http://kawiarnia.test/karta
curl -sI http://kawiarnia.test/stare-menu | grep -E 'HTTP|Location'<h1>Menu: espresso, latte, sernik</h1>
HTTP/1.1 301 Moved Permanently
Location: http://kawiarnia.test/kartaDziała. A czy ktoś może podejrzeć naszą karteczkę?
curl -s -o /dev/null -w '%{http_code}\n' http://kawiarnia.test/.htaccess403Nie. Apache domyślnie nie pokazuje plików zaczynających się od .ht.
Logi
Każda strona ma teraz swoje księgi. Kilka sztuczek na co dzień:
sudo tail -f /var/log/apache2/kawiarnia_access.log # na żywo
sudo grep ' 404 ' /var/log/apache2/kawiarnia_access.log # czego brakuje
sudo awk '{print $9}' /var/log/apache2/kawiarnia_access.log | sort | uniq -c | sort -rnOstatnia komenda to taśma w fabryce z modułu o tekstach: dziewiąte pole w logu to kod odpowiedzi, liczymy, ile było których:
6 200
4 301
1 500
1 404
1 403Od razu widać, że ktoś dostał 500. A na produkcji wystarczy jedna linijka 500, żeby ktoś się zdenerwował.
Logi rosną bez końca, więc system tnie je codziennie: na Ubuntu /etc/logrotate.d/apache2 ma daily i rotate 14, czyli dwa tygodnie historii, starsze lecą do kosza.
Jak to działa pod spodem
Przy starcie Apache czyta główny plik, a w nim dyrektywy Include i IncludeOptional, które doklejają kolejne pliki: na Ubuntu z mods-enabled, conf-enabled, sites-enabled, na RHEL z conf.modules.d i conf.d. Z tego wszystkiego powstaje jedna wielka konfiguracja w pamięci. Dlatego literówka w dowolnym pliku psuje całość.
Kiedy przychodzi żądanie, Apache patrzy na adres i port, na którym przyszło połączenie, wybiera grupę VirtualHostów pasującą do tego adresu, a w niej szuka takiego, którego ServerName albo ServerAlias zgadza się z nagłówkiem Host. Nie znalazł? Bierze pierwszy z grupy. Potem skleja DocumentRoot z adresem, przechodzi przez zasady z <Directory>, i jeśli AllowOverride pozwala, czyta po drodze .htaccess.
reload (w Apache: graceful) każe procesom roboczym dokończyć obsługę bieżących gości i wczytuje nową konfigurację, nie zrywając połączeń. restart zatrzymuje wszystko i startuje od nowa.
Ściąga flag
| Komenda | Flaga | Co robi |
|---|---|---|
a2ensite / a2dissite | NAZWA | włącz/wyłącz stronę (Ubuntu) |
a2enmod / a2dismod | NAZWA | włącz/wyłącz moduł (Ubuntu) |
a2enconf / a2disconf | NAZWA | włącz/wyłącz fragment konfiguracji (Ubuntu) |
apachectl | configtest | sprawdź składnię przed przeładowaniem |
apachectl | -S | lista VirtualHostów i domyślny serwer |
apachectl | -M | lista modułów |
apachectl | graceful | przeładuj bez zrywania połączeń |
systemctl | reload apache2 / httpd | przeładuj konfigurację |
systemctl | restart apache2 / httpd | pełny restart (np. po zmianie modułów) |
curl | -H 'Host: nazwa' | test VirtualHosta bez DNS |
curl | -w '%{http_code}\n' | wypisz sam kod odpowiedzi |
tail | -f | śledź log na żywo |
⚠ Typowy błąd
Ten 500 z .htaccess. Log kawiarni:
sudo tail -1 /var/log/apache2/kawiarnia_error.log[core:alert] [client 127.0.0.1:43726] /var/www/kawiarnia.test/public/.htaccess: Invalid command 'RewriteEngine', perhaps misspelled or defined by a module not included in the server configuration„Nieznane polecenie RewriteEngine, może literówka albo moduł nie jest załadowany”. Apache nie zna słowa RewriteEngine, bo zna je dopiero mod_rewrite, a ten był wyłączony. Naprawa: sudo a2enmod rewrite i restart.
Ten sam komunikat dostaniesz przy prawdziwej literówce w głównej konfiguracji. Na przykład DocumentRot zamiast DocumentRoot:
sudo apachectl configtestAH00526: Syntax error on line 4 of /etc/apache2/sites-enabled/kawiarnia.test.conf:
Invalid command 'DocumentRot', perhaps misspelled or defined by a module not included in the server configurationNumer linii, nazwa pliku i błędne słowo. Lepszej podpowiedzi nie dostaniesz. I dlatego configtest przed reload: ten błąd zobaczył tylko Ty, a nie klienci kawiarni.
A jeszcze jedna pułapka: literówka w ścieżce DocumentRoot nie jest błędem składni:
AH00112: Warning: DocumentRoot [/var/www/kawiarnia.tset/public] does not exist
Syntax OKSyntax OK na dole, a strona i tak nie zadziała. Czytaj też ostrzeżenia nad tym zdaniem, nie tylko ostatnią linijkę.
✎ Zadanie
- Zrób VirtualHost
notatki.testz DocumentRoot w/var/www/notatki.test/publici osobnymi logami. Dopisz nazwę do/etc/hosts. - W folderze strony połóż
index.htmlio-mnie.html. Przez.htaccessimod_rewritezrób tak, żeby adres/kim-jestempokazywało-mnie.html. - Zrób celowo literówkę w pliku strony, uruchom
apachectl configtest, przeczytaj komunikat i napraw. - Sprawdź
apachectl -S, która strona jest domyślna, i otwórz serwer przezcurl -H 'Host: cokolwiek.test'.
Pokaż rozwiązanie
VirtualHost notatki.test, plik /etc/apache2/sites-available/notatki.test.conf ([RHEL] /etc/httpd/conf.d/notatki.test.conf, z logami w /var/log/httpd/):
<VirtualHost *:80>
ServerName notatki.test
DocumentRoot /var/www/notatki.test/public
<Directory /var/www/notatki.test/public>
AllowOverride All
Require all granted
</Directory>
ErrorLog ${APACHE_LOG_DIR}/notatki_error.log
CustomLog ${APACHE_LOG_DIR}/notatki_access.log combined
</VirtualHost>.htaccess w folderze strony:
RewriteEngine On
RewriteRule ^kim-jestem$ o-mnie.html [L]echo '127.0.0.1 notatki.test' | sudo tee -a /etc/hosts
sudo a2ensite notatki.test
sudo a2enmod rewrite
sudo apachectl configtest && sudo systemctl restart apache2
curl http://notatki.test/kim-jestemSyntax OK
<h1>O mnie: admin w budowie</h1>Pamiętaj o AllowOverride All, bo inaczej karteczka .htaccess wisi na drzwiach, a nikt jej nie czyta. Celowa literówka, ServerNme:
AH00526: Syntax error on line 2 of /etc/apache2/sites-enabled/notatki.test.conf:
Invalid command 'ServerNme', perhaps misspelled or defined by a module not included in the server configurationI domyślny serwer:
sudo apachectl -S | grep 'default server'
curl -s -H 'Host: cokolwiek.test' http://127.0.0.1/ | grep -o '<title>.*</title>' default server localhost (/etc/apache2/sites-enabled/000-default.conf:1)
<title>Apache2 Ubuntu Default Page: It works</title>Nieznany gość ląduje na stronie domyślnej. Czyli recepcja działa jak powinna: jak nie wie, do kogo przyszedłeś, wysyła Cię do holu.
Co dalej
Wszystkie nasze strony gadają otwartym tekstem, a każdy po drodze może podsłuchać. W następnej lekcji dokładamy kłódkę: HTTPS, certyfikat z Let's Encrypt przez certbota, przekierowanie z 80 na 443 i SELinux na Red Hacie, który potrafi zablokować stronę, mimo że prawa plików są idealne.