Запуск сайтов от разных пользователей в связке nginx + php-fpm

nginx_php_process_flowПо умолчанию все сайты будут запускаться от пользователя, указанного в настройках php-fpm. Чтобы запускать сайты от разных пользователей, необходимо создать отдельные конфигурационные файлы в директории /etc/php7/fpm/pool.d, убедившись при этом, что в файле /etc/php7/fpm/php-fpm.conf указана строчка:

include=/etc/php7/fpm/pool.d/*.conf

Теперь создаем файл конфигурации для нашего сайта (покажу на примере 891rpm.arthead.ru) /etc/php7/fpm/pool.d/891rpm.arthead.ru.conf:

[891rpm.arthead.ru]
listen = /run/php7-891rpm.sock
listen.mode = 0660
user = 891rpm_com    # user
group = 891rpm_com   # group
chdir = /var/www/891rpm.arthead.ru

php_admin_value[upload_tmp_dir] = /var/www/891rpm.arthead.ru/tmp
php_admin_value[soap.wsdl_cache_dir] = /var/www/891rpm.arthead.ru/tmp
php_admin_value[date.timezone] = Europe/Moscow
php_admin_value[upload_max_filesize] = 100M
php_admin_value[post_max_size] = 100M
php_admin_value[open_basedir] = "/var/www/891rpm.arthead.ru/"
php_admin_value[session.save_path] = /var/www/891rpm.arthead.ru/tmp
php_admin_value[disable_functions] = dl,exec,passthru,shell_exec,system,proc_open,popen,curl_exec,parse_ini_file,show_source
php_admin_value[cgi.fix_pathinfo] = 0
php_admin_value[apc.cache_by_default] = 0

# В зависимости от нагрузки меняем параметры
pm = dynamic
pm.max_children = 10
pm.start_servers = 2
pm.min_spare_servers = 2
pm.max_spare_servers = 4

В файле настроек /etc/nginx/sites-available/891rpm.arthead.ru.conf указываем сокет:

upstream 891rpm-sock {
    server unix:/var/run/php7-891rpm.sock;
}

server {
        listen 80;
        server_name 891rpm.arthead.ru www.891rpm.arthead.ru;
...
location ~ \.php$
        {
                include fastcgi.conf;
                fastcgi_intercept_errors on;
                fastcgi_pass 891rpm-sock;
        }
...
}

/etc/group

www-data:x:33:
891rpm_com:x:1001:www-data

 

Перезапускаем php-fpm и nginx:

sudo /etc/init.d/php7-fpm restart
sudo /etc/init.d/nginx restart

Теперь сайт будет запускаться от указанного пользователя.

Оперативная реакция на DDoS-атаки

DDoS-network-mapОдин из ресурсов, за которым я присматриваю, вдруг стал неожиданно популярным как у хороших пользователей, так и у плохих. Мощное, в общем-то, железо перестало справляться с нагрузкой. Софт на сервере самый обычный — Linux, Nginx, PHP-FPM (+APC), MySQL, версии — самые последние. На сайтах крутится Drupal и phpBB. Оптимизация на уровне софта (memcached, индексы в базе, где их не хватало) чуть помогла, но кардинально проблему не решила. А проблема — большое количество запросов, к статике, динамике и особенно базе. Поставил следующие лимиты в Nginx:

на соединения

limit_conn_zone $binary_remote_addr zone=perip:10m;
limit_conn perip 100;

и скорость запросов на динамику (fastcgi_pass на php-fpm)

limit_req_zone $binary_remote_addr zone=dynamic:10m rate=2r/s;
limit_req zone=dynamic burst=10 nodelay;

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

Но плохиши продолжали долбить, и захотелось их отбрасывать раньше — на уровне фаервола, и надолго.

Сначала сам парсил логи, и особо настырных добавлял через iptables в баню. Потом парсил уже по крону каждые 5 минут. Пробовал fail2ban. Когда понял, что плохишей стало очень много, перенёс их в ipset ip hash.

Почти всё хорошо стало, но есть неприятные моменты:
— парсинг/сортировка логов тоже приличное (процессорное) время отнимает
— сервер тупит, если началась новая волна между соседними разборками (логов)

Нужно было придумать как быстро добавлять нарушителей в черный список. Сначала была идея написать/дописать модуль к Nginx + демон, который будет ipset-ы обновлять. Можно и без демона, но тогда придётся запускать Nginx от рута, что не есть красиво. Написать это реально, но понял, что нет столько времени. Ничего похожего не нашёл (может плохо искал?), и придумал вот такой алгоритм.

При привышении лимита, Nginx выбрасывает 503-юю ошибку Service Temporarily Unavailable. Вот я решил на неё и прицепиться!

Для каждого location создаём свою страничку с ошибкой

error_page 503 =429 @blacklist;

И соответствующий именованный location

location @blacklist {
    fastcgi_pass    localhost:1234;
    fastcgi_param   SCRIPT_FILENAME    /data/web/cgi/blacklist.sh;
    include         fastcgi_params;
}

Дальше интересней.

Нам нужна поддержка CGI-скриптов. Ставим, настраиваем, запускаем spawn-fcgi и fcgiwrap. У меня уже было готовое для collectd.

Сам CGI-скрипт fc

#!/bin/bash

BAN_TIME=5
DB_NAME="web_black_list"
SQLITE_DB="/data/web/cgi/${DB_NAME}.sqlite3"
CREATE_TABLE_SQL="\
CREATE TABLE $DB_NAME (\
    ip varchar(16) NOT NULL PRIMARY KEY,\
    added DATETIME NOT NULL DEFAULT (DATETIME()),\
    updated DATETIME NOT NULL DEFAULT (DATETIME()),\
    counter INTEGER NOT NULL DEFAULT 0
)"
ADD_ENTRY_SQL="INSERT OR IGNORE INTO $DB_NAME (ip) VALUES (\"$REMOTE_ADDR\")"
UPD_ENTRY_SQL="UPDATE $DB_NAME SET updated=DATETIME(), counter=(counter+1) WHERE ip=\"$REMOTE_ADDR\""
SQLITE_CMD="/usr/bin/sqlite3 $SQLITE_DB"
IPSET_CMD="/usr/sbin/ipset"

$IPSET_CMD add $DB_NAME $REMOTE_ADDR > /dev/null 2>&1

if [ ! -f $SQLITE_DB ]; then
     $SQLITE_CMD "$CREATE_TABLE_SQL"
fi

$SQLITE_CMD "$ADD_ENTRY_SQL"
$SQLITE_CMD "$UPD_ENTRY_SQL"

echo "Content-type: text/html"
echo ""

echo "<html>"
echo "<head><title>429 Too Many Requests</title></head>"
echo "<body bgcolor=\"white\">"
echo "<center><h1>429 Too Many Requests</h1></center>"
echo "<center><small><p>Your address ($REMOTE_ADDR) is blacklisted for $BAN_TIME minutes</p></small></center>"
echo "<hr><center>$SERVER_SOFTWARE</center>"
echo "</body>"
echo "</html>"

Собственно всё очевидно, кроме, разве что, SQLite. Я его добавил пока просто для статистики, но в принципе можно использовать и для удаления устаревших плохишей из черного списка. Время 5 минут пока тоже не используется.

Черный список создавался вот так

ipset create web_black_list hash:ip

Правило в iptables у каждого может быть своё, в зависимости от конфигурации и фантазии.

З.Ы.: Улыбнуло сообщение одного «хакера» на форуме, как быстро он положил сервер. Он и не догадывался, что это сервер положил на него.

Источник: http://habrahabr.ru/post/176119/