Posterous theme by Cory Watilo

LAMP through fcgid with suexec

Взглянем на Apache+mod_php. Плюсы:

  • настраивается максимально просто
  • интерпретатор стартует вместе с каждым форком апача

Минусы:

  • mpm_prefork далеко не самый быстрый
  • все работает под одним пользователем (да-да, можно накрутить mod_itk)

В попытках сделать секьюрно и по возможности быстро я решил скрутить apache (mpm_worker) + mod_fcgid + suexec. Сам по себе CGI очень небыстр за счет того, что при каждом запросе подымается интерпретатор. FastCGI быстрее, так как интерпретатор держится отдельным процессом. А mod_fcgid – модуль, бинарно совместимый с mod_fastcgi, с новой стратегией управления процессами.

Suexec в свою очередь позволяет выполнять CGI/FastCGI/SSI с указанными uid/gid. Да-да, в системе будут заводиться реальные пользователи.

Расстановкой прав можно добиться того, что даже взломав один сайт злоумышленник не сможет увидеть другие сайты.

Приступим.

apt-get install apache2-mpm-worker apache2-suexec ache2-threaded-dev libapache2-mod-fcgid php5-cgi

Теперь когда у нас все есть, создадим скелет для будущих площадок.

mkdir -p /root/web/skel
cd /root/web/skel
mkdir {cgi-bin,etc,logs,tmp,www}
chmod 770 tmp
chmod 751 {etc,logs,www}
cp /etc/php5/cgi/php.ini ./etc

Правим php.ini на предмет вывода ошибок и прочих мелких твиков.

Теперь сделаем враппер для самого обработчика (cgi-bin/php-cgi):

#!/bin/bash

cd $CGI_BIN_DIR
PHP_INI=../etc/php.ini

if [ ! -f $PHP_INI ]; then
    PHP_INI=/etc/php5/cgi/php.ini
fi

exec /usr/bin/php5-cgi -c $PHP_INI

И делаем

chown -R 755 cgi-bin
cd /root/web
touch {adduser,awstats,vhost}.skel
touch add_site.sh && chmod +x add_site.sh

adduser.skel:

DSHELL=/bin/false
DHOME=/var/www
GROUPHOMES=no
LETTERHOMES=no
SKEL=/root/web/skel
FIRST_SYSTEM_UID=2000
LAST_SYSTEM_UID=2999
FIRST_SYSTEM_GID=2000
LAST_SYSTEM_GID=2999
FIRST_UID=2000
LAST_UID=29999
FIRST_GID=2000
LAST_GID=2999
USERGROUPS=yes
USERS_GID=100
DIR_MODE=0751
SETGID_HOME=no
QUOTAUSER=""
SKEL_IGNORE_REGEX="dpkg-(old|new|dist)"

awstats.skel:

LogFile="/var/www/#USER/logs/#SITE-access.log"
SiteDomain="#SITE"
HostAliases="localhost 127.0.0.1 REGEX[#SITE$]"
Include "/etc/awstats/awstats.conf.local"

vhost.skel:

<virtualhost *>
    ServerName #SITE
    ServerAlias www.#SITE
    DocumentRoot /var/www/#USER/www/#SITE/public_html

    SuexecUserGroup #USER #USER

    ScriptAlias /cgi-bin/ /var/www/#USER/cgi-bin/
    <directory /var/www/#USER/www/#SITE/public_html>
    Options -Indexes +ExecCGI
    AllowOverride All
    AddHandler fcgid-script .php
    FCGIWrapper /var/www/#USER/cgi-bin/php-cgi .php
    Order allow,deny
    Allow from all
    </directory>

    ErrorLog /var/www/#USER/logs/#SITE-error.log
    CustomLog /var/www/#USER/logs/#SITE-access.log combined

    SetEnv AWSTATS_FORCE_CONFIG #SITE
    <location /cgi-bin/awstats.pl >
    AuthUserFile /var/www/#USER/etc/awstats.passwd
    AuthName "Website stats for #SITE"
    AuthType Basic
    require valid-user
    </location>
</virtualhost>

add_site.sh:

#!/bin/bash

if [ -z $1 ] || [ -z $2 ]; then
    echo "Oops. Some param not given."
    exit 1
fi

# If no such user exists - add one right now
if [ ! -d /var/www/$2 ]; then
    adduser --conf ./adduser.skel --disabled-login --gecos '' $2 || exit 1
fi

# Buld generic folders
mkdir -p /var/www/$2/www/$1/public_html
chown -R $2:$2 /var/www/$2/www/$1

# Build generic vhost for apache
cat vhost.skel | sed "s/#USER/${2}/g;s/#SITE/${1}/g" > ${2}_${1}

# Activate vhost
a2ensite ${2}_${1}

# Build generic awstats config
cat awstats.skel | sed "s/#USER/${2}/g;s/#SITE/${1}/g" >> /etc/awstats/awstats.${1}.conf

echo "Restart apache..."
apache2ctl configtest && apache2ctl restart

Вот таким нехитрым образом можно добавить сайт:

cd /root/web
./add_site example.org web_example

В результате будет создан пользователь web_example и у него сайт – example.org.

Tip

Иногда надо позволить скрипту выполяться боее 30 секунд. Думаете для этого достаточно подправить php.ini? Нет, в таком случае fcgid отстрелит скрипт по достижении 40 секунд. Для этих случаев в конфиге vhost’а требуется задать значение IPCCommTimeout в секундах (например, 300). Есть баг – глобальное значение отчего-то не хочет применяться к vhost’у, потому надо указать его непосредственно в vhost’е.

В общем и целом – конфиги показал, идею донес (надеюсь). Enjoy.

Exim mail forwarding

Стоял у меня MTA Exim4 для отправки почты. И вдруг на этот сервер перенесли MX-запись одного домена (например, example.org), и он вынужден был что-то делать с почтой. Решено было перенаправить всю почту для этого домена на другой ящик на время разбирательств кто прав, а кто виноват.

Для этого в Ubuntu пришлось сделать следующие правки.

В /etc/exim4/update-exim4.conf.conf в dc_other_hostnames и dc_relay_domains был добавлен example.org.

А сам форвард выглядел так (/etc/exim4/conf.d/router/099_exim4-config_redirects):

forward:
    driver = redirect
    domains = example.org
    data = tmp_example.org@somemail.com

В теории должна была сработать запись вида

sender_redirect:
  driver = redirect
  data = ${lookup{$sender_address}lsearch{/etc/exim4/sender_redirects}}
  domains = example.org

И в файле /etc/exim4/sender_redirects были бы

someone@example.org: where.to@forward.com

Но почему-то этот вариант не сработал как ожидалось.

Возможно я допустил какие-то ошибки в конфигурировании, но задача выполнена и почта не потеряна.