Apache, vhosty za pomocą mod_rewrite i cgiwrap (czyli php wykonywane z prawami użytkownika) – HOWTO

A

Dzień dobry, smacznego, dobry wieczór i dobranoc (w zależności o jakiej porze czytasz te bzdury). Dawno nic nie pisałem, tym bardziej cieszę się zaprezentować coś choć w części interesującego. Zajmę się dość ciekawą kombinacją – Apache + mod_rewrite + cgiwrap.

Wymagania

Ponieważ używam standardowej konfiguracji PLD-Linux, jeśli chodzi o użytkowników, równie standardowa będzie konfiguracja Apache (co zarazem znaczy, że jest to bardzo specyficzna konfiguracja i wiem, że z inną konfiguracją poniższy opis nie będzie funkcjonować).

Wymagane są następujące komponenty:

  • Apache 2.2 + mod_rewrite
  • cgiwrap-4.1 + ten patch (lub jeśli używamy PLD-Linux – cgiwrap-4.1-4.*.rpm)
  • konta użytkowników w /home/users
  • strony użytkowników w /home/users/*/public_html
  • zdrowy, nie przepity, nie zaćpany rozum i chęć zrozumienia idei
  • dobra znajomość mod_rewrite (bo nie wszystko chce mi się tłumaczyć)

Apache + vhosty za pomocą mod_rewrite

Dlaczego nie po prostu mod_vhost_alias? W sumie tylko z dwóch powodów:
1. Po każdym wpisie nowego virtualhosta trzeba restartować Apache
2. Wpis zajmuje parę linijek, z lenistwa wolę pisać tylko 1 (mod_rewrite)

Reasumując plusy dodatnie i plusy ujemne tego przedsięwzięcia, mamy taką oto listę.

Plusy:

  • nie trzeba restartować Apache po dodaniu nowego vhosta
  • krótkie wpisy = mało zmarnowanego czasu
  • cachowanie vhostów – Apache mod_rewrite potrafi automatycznie cachować zmapowane rewrity by działać szybciej
  • użytkownik może sam sobie dodawać vhosty jeśli da mu się odpowiednie prawa/udostępni odpowiednią automatykę

Minusy:

  • brak wildcardów (niektórzy powiedzą, że to plus), czyli wpisów z gwiazdką ‘*’
  • niektóre niedogodności lub banalne ustawienia w mod_vhost_alias trzeba obchodzić kolejnymi zagmatwanymi rewritami
  • rewrite sam w sobie, można się pogubić :-)
  • wywołania http://domena.pl/~user nie działają (co dla niektórych też jest plusem)

Ulubionym edytorem tekstu edytujemy plik /etc/httpd/httpd.conf/00_mod_rewrite.conf i dodajemy następujące wpisy:

RewriteEngine On

RewriteMap      lowercase       int:tolower
RewriteMap      vhost txt:/etc/virtualhosts.map

RewriteCond     %{REQUEST_URI}  !^/icons/
RewriteCond     %{REQUEST_URI}  !^/error/
RewriteCond     %{REQUEST_URI}  !^/cgi-bin/
RewriteCond     ${lowercase:%{HTTP_HOST}}       ^domena.pl$
RewriteRule     ^/~(([a-z])[a-z0-9-]+)(.*)      http://$1.domena.pl$3 [R,L]

RewriteCond     %{REQUEST_URI}  !^/icons/
RewriteCond     %{REQUEST_URI}  !^/error/
RewriteCond     %{REQUEST_URI}  !^/cgi-bin/
RewriteCond     ${lowercase:%{HTTP_HOST}}       ^(.+)$
RewriteCond     ${vhost:%1}   ^(/.*)$
RewriteRule     ^/(.*)$ %1/$1   [E=VHOST:${lowercase:%{HTTP_HOST}}]

Krótkie wytłumaczenie. RewriteMap (/etc/virtualhosts.map) zawiera wpisy w formacie:

user.domena.pl		/home/users/user/public_html
domena2.com		/home/users/user150/public_html
itd...

Jaki typ RewriteMap wybierzemy jest obojętne. Ja wybrałem “txt” bo automat dodaje mi vhosty użytkowników. Dodatkowo nie pozwalamy nadpisywać aliasów dla ikon, stron z błędami oraz globalnego katalogu cgi-bin. Ponieważ URLe http://domena.pl/~user nie działają, a niektórzy na pewno będą próbowali się tak odwoływać do własnej strony, to przepisujemy te adresy na adresy typu http://user.domena.pl (pierwszy RewriteRule). Następnie już tylko mapujemy odpowiedni vhost do jemu przyporządkowanego (w RewriteMapie) katalogu.

Od tej pory mamy działające virtualhosty i możemy je używać bez problemu z mod_php.

CGIWrap – czyli php wykonywane z prawami użytkownika

Przypominam, że CGIWrap z patchem, którego udostępniam działa tylko z taką konfiguracją. Jest to spowodowane tym, że obliczam w CGIWrapie użytkownika na podstawie jego katalogu domowego i edytuje odpowiednio zmienne środowiskowe PATH_INFO oraz PATH_TRANSLATED.

Ulubionym edytorem tekstu edytujemy plik /etc/httpd/httpd.conf/01_mod_cgid.conf i dopisujemy następujące linie:

ScriptAlias /cgi-bin/ "/home/services/httpd/cgi-bin/"

<Directory /home/services/httpd/cgi-bin\>
        Options FollowSymLinks ExecCGI
</Directory\>

Action _phpwrap /cgi-bin/php-cgiwrap
AddHandler _phpwrap .php .php4 .php5

Jeśli ktoś nie chce odinstalować mod_php, to nie musi. Wystarczy, że odbierze mod_php przywiązanie do plików o typach zdefiniowanych dla _phpwrap.

Następnie dobieramy się po raz kolejny do pliku /etc/httpd/httpd.conf/00_mod_rewrite.conf i dodajemy:

RewriteMap      cgi     txt:/etc/cgi.map

<Files ~ "\.php$">
#CGIWrap
RewriteCond     %{REQUEST_URI}  ^/
RewriteCond     %{REQUEST_URI}  !^/icons/
RewriteCond     %{REQUEST_URI}  !^/error/
RewriteCond     %{REQUEST_URI}  !^/cgi-bin/
RewriteCond     ${lowercase:%{HTTP_HOST}}       ^(.+)$
RewriteCond     ${cgi:%1}       ^(/.*)$
RewriteRule     ^/(.*)$ %1/$1 [PT,E=REDIRECT_STATUS:200]
</Files>

Jeśli Apache wypluje nam coś o tym, że cgi nie działa ponieważ REDITECT_STATUS nie jest poprawnie ustawiony, to należy dodać go analogicznie do poprzedniego RewriteRule.
/etc/cgi.map zawiera wpisy w formacie:

user.domena.pl		/cgi-bin/php-cgiwrap/
domena2.pl			/cgi-bin/php-cgiwrap/
itd...

To chyba wszystko co należy skonfigurować. W razie problemów włączamy sobie:

RewriteLog      /var/log/httpd/rewrite_log
RewriteLogLevel 9

i oglądamy sobie co jest nie tak, jeśli problem jest w mod_rewrite. Jeśli problem jest w CGIWrapie, to jako handlera wybieramy np. php-cgiwrapd – wtedy dostaniemy output z debugiem.

Mam nadzieję, że o niczym nie zapomniałem, bo wpis ten traktuję głównie jako notkę do siebie ;-) Korzystając z okazji pragnę podziękować za pomoc zbyniowi i sparkiemu.

Bijcie!

About the author

Bartosz

iOS and OS X software engineer. Currently working in Berlin, Germany.

By Bartosz