Как создать свой хостинг "с нуля"
ОГЛАВЛЕНИЕ
Задачи
- создать максимально производительный и в то же время безопасный вебхостинг
- позволить mod_php создавать файлы от имени пользователя, а не с правами вебсервера
- обезопасить пользователей друг от друга
- обезопасить систему от вторжения пользователей
- обезопасить систему от вторжения извне
Общая схема построения хостинга
В качестве вебсервера будем использовать Apache 1.3 с модулем mod_php и возможностью запуска cgi скриптов, как наиболее популярный среди вебмастеров. СУБД - MySQL 5.1.
Для "продвинутых" клиентов предоставим в пользование компилятор gcc. Не следует бояться давать пользователю доступ к компилятору - на правильно настроенной системе даже при наличии компилятора ничего сломать не получится. Мы строим именно правильно настроенную систему, поэтому в распоряжении пользователя будет весь комплекс программного обеспечения.
Чтобы снизить нагрузку на Apache, установим акселерирующий прокси-сервер. Из собственной многолетней практики, наиболее подходящим акселератором на данный момент является nginx - стабильный и качественный многофункциональный вебсервер/акселератор, разрабатываемый Игорем Сысоевым.
Таким образом, сначала запрос от пользователя поступает на акселератор nginx, который ожидает приема всех данных и проксирует данные на Apache только после полного его получения. Этим мы снижаем нагрузку на Apache, который каждый запрос обрабатывает в отдельном тяжеловесном процессе.
Для по-настоящему безопасной работы необходимо чтобы программы на площадке каждого клиента выполнялись от своего пользователя. Для CGI скриптов эта задача решается путем настройки suexec. Модуль PHP, который по определению является частью Apache, выполняется с правами пользователя, от которого запущен вебсервер. Есть альтернативный вариант с использованием suphp, однако это очень сильно грузит систему и потому такая схема для массового вебхостинга неприменима.
Мы выберем компромиссный вариант: PHP будет работать как модуль Apache, а безопасность обеспечим настройками файловой системы и модуля PHP.
Осталось решить последнюю проблему, а именно - создание файлов при работе mod_php с правами пользователя-владельца площадки, а не с правами вебсервера. Обратимся к руководству по команде mount(8) , из которого следует, что для наследования владельца при создании объектов внути каталога, необходимо замонтировать раздел с опцией suiddir.
Доступ по FTP следует предоставлять только через виртуальных пользователей. Это требование обусловлено тем, что пароли по протоколу FTP передаются в нешифрованном виде и их очень легко перехватить. Чтобы исключить возможность вторжения в систему по SSH, перехватив пароль для FTP и необходимы виртуальные пользователи - не существующие в системе, следовательно, бесполезные для потенциального взломщика.
Итак, полученная нами схема выглядит следующим образом:
+--------------+ http +-------------------+ http +--------+
| пользователь | ---> | Акселератор nginx | ---> | Apache |
+--------------+ +-------------------+ +--------+
|
| +---------+
|-> | mod_php | >-+--------------+
| +---------+ | suiddir |
| +---------+ | |
|-> | cgi | >-| |
| +---------+ | Каталог |
| +---------+ | пользователя |
|-> | SSI | >-| |
+---------+ | |
| |
+-----------------+ ssh +-----------+ Пользователь | |
| клиент хостинга | ----> | SSHD | --------------------------> | |
+-----------------+ +-----------+ | |
| |
+-----------------+ ftp +-----------+ Виртуальный пользователь | |
| клиент хостинга | ----> | Pure-FTPD | --------------------------> | |
+-----------------+ +-----------+ +--------------+
Помните, все сервисы, собранные в одном месте - не более чем тестовая машина. В реальной конфигурации, способной обслуживать тысячи запросов в секунду, сервер MySQL должен быть на отдельной машине. Это же касается и акселератора nginx. Попытка собрать все воедино на одном физическом сервере резко снизит скорость работы всего комплекса ПО из-за слишком высокой нагрузки на дисковую систему.
Первоначальная подготовка сервера
Конфигурация сервера: Intel(R) Xeon(TM) 3.0GHz / 2G RAM / Mirror SATA RAID (2 * 150G).
Операционная система: FreeBSD 6.2-STABLE SMP i386.
Диски:
/ - 400M
/tmp - 2G
/home - 85G
/var - 40G
/usr - 20G
К сожалению, стандартные GENERIC и SMP ядра не позволяет монтировать файловые системы с SUIDDIR, а потому требуется пересборка. Также неплохо было бы поменять в сторону увеличения параметры, заведующие выделением разделяемой памяти и оперативной памяти на процесс. Создаем файл /usr/src/sys/i386/conf/SMP-stellar со следующим содержимым
include GENERIC
ident SMP-GENERIC-STELLAR
options SMP
options SUIDDIR
options SYSVSHM
options SYSVSEM
options SYSVMSG
options SHMMAXPGS=65536
options SEMMNI=40
options SEMMNS=240
options SEMUME=40
options SEMMNU=120
options MAXDSIZ=(1024UL*1024*1024)
options MAXSSIZ=(128UL*1024*1024)
options DFLDSIZ=(1024UL*1024*1024)
пересобираем ядро и перегружаемся:
cd /usr/src
make buildkernel KERNCONF=SMP-stellar
make installkernel KERNCONF=SMP-stellar
shutdown -r now
После перезагрузки FreeBSD может монтировать файловые системы с опцией suiddir.
Изменяем файл /etc/fstab так, чтобы раздел /home монтировался с suiddir. Дополнительно отключаем запись времени последнего доступа к файлам; это дает некоторый выигрыш в производительности:
/dev/ar0s1g /home ufs rw,suiddir,noatime 2 2
Проверяем результат:
umount /home
mount /home
mount | grep /home
/dev/ar0s1g on /home (ufs, local, noatime, suiddir, soft-updates)
Сборка и настройка комплекса ПО
Перед установкой добавляем в /etc/make.conf
WITHOUT_X11=yes
Устанавливаем MySQL. Собственно, здесь нет ничего экстраординарного:
cd /usr/ports/databases/mysql51-server
export WITH_CHARSET=cp1251
export WITH_XCHARSET=all
export BUILD_OPTIMIZED=yes
make install clean
После установки сервера указываем в /etc/rc.conf
#
# MySQL for virtual hosts
#
mysql_enable="YES"
Записываем файл конфигурации MySQL /var/db/mysql/my.cnf:
[client]
port = 3306
socket = /tmp/mysql.sock
default-character-set=cp1251
[mysqld]
bind-address = 127.0.0.1
port = 3306
socket = /tmp/mysql.sock
skip-character-set-client-handshake
default-character-set=cp1251
skip-locking
key_buffer = 32M
max_allowed_packet = 16M
table_cache = 256
sort_buffer_size = 1M
read_buffer_size = 1M
read_rnd_buffer_size = 4M
myisam_sort_buffer_size = 64M
thread_cache_size = 8
query_cache_size = 16M
thread_concurrency = 8
max_connections = 128
max_user_connections = 126
log-slow-queries
log
binlog_ignore_db = mysql
expire_logs_days = 5
max_binlog_size = 400M
# Если нет необходимости в работе по сети, включаем опцию skip-networking
#skip-networking
log-bin = mysql-bin
server-id = 1
[mysqldump]
quick
max_allowed_packet = 16M
[mysql]
no-auto-rehash
[isamchk]
key_buffer = 128M
sort_buffer_size = 128M
read_buffer = 2M
write_buffer = 2M
[myisamchk]
key_buffer = 128M
sort_buffer_size = 128M
read_buffer = 2M
write_buffer = 2M
[mysqlhotcopy]
interactive-timeout
запускаем сервер
/usr/local/etc/rc.d/mysql-server start
и проверяем, все ли работает нормально
ps axw | grep mysql
780 con- I 0:00.01 /bin/sh /usr/local/bin/mysqld_safe
--defaults-extra-file=/var/db/mysql/my.cnf --user=mysql --datadir=/var/db/mysql --pid-file=/v
862 con- S 107:52.92 /usr/local/libexec/mysqld --defaults-extra-file=/var/db/mysql/my.cnf
--basedir=/usr/local --datadir=/var/db/mysql --pid-file=/va
7861 p0 S+ 0:00.00 grep mysql
Задаем пароль для MySQL root:
mysql -uroot
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 24356
Server version: 5.1.15-beta-log FreeBSD port: mysql-server-5.1.15
Type 'help;' or '\h' for help. Type '\c' to clear the buffer.
mysql> GRANT ALL ON *.* TO 'root'@'localhost' IDENTIFIED BY 'topsecret';
mysql> GRANT ALL ON *.* TO 'root'@'127.0.0.1' IDENTIFIED BY 'topsecret';
На этом установка MySQL окончена.
В качестве фронтэнда у нас будет работать вебсервер nginx, задача которого - проксировать HTTP запросы на сервер Apache. Это дает резкое снижение нагрузки из-за того, что nginx - мультиплексирующий сервер, способный обслуживать десятки тысяч одновременных подключений без порождения сотен дополнительных процессов или тредов. Схема выглядит следующим образом:
+------------+ +-------------------+ +-------------------+
| посетитель | ------> | Внешний IP: NGINX | ------> | 127.0.0.1: Apache |
+------------+ +-------------------+ +-------------------+
При такой схеме все запросы, приходящие на Apache, будут иметь один и тот же адрес - адрес, на котором запущен nginx. Для того чтобы Apache воспринимал запросы с IP адресов посетителей, а не прокси nginx, необходимо дополнительно установить модуль mod_realip.
Итак, устанавливаем nginx:
cd /usr/ports/www/nginx
make install clean
Устанавливаем Apache:
cd /usr/ports/www/apache13
export WITH_APACHE_SUEXEC=yes
export APACHE_SUEXEC_LOG=/var/log/apache/suexec_log
export APACHE_SUEXEC_USERDIR=www
export APACHE_SUEXEC_DOCROOT=/home/www
make install clean
Устанавливаем mod_realip:
cd /usr/ports/www/mod_realip
make install clean
На этом собственно установка вебсервера окончена. Следующий шаг - сборка php и mod_php. Из портов можно установить PHP4 или PHP5 - принципиальной разницы в технологии установки и конфигурирования нет, а потому будет описана только сборка mod_php5. Для нормальной работы с временными файлами (сессии, загрузка файлов и т.п.) требуется дополнительный патч, изменяющий права доступа с 0600 до 0660, чтобы вебсервер имел право записи в них. Патч доступен по адресу PHP4, patch-main::open_temporary_file.c.4 и PHP5, patch-main::open_temporary_file.c.5
cd /usr/ports/lang/php5
fetch http://reki.ru/products/samag/PHPPatchset.tar.gz
tar -xzf PHPPatchset.tar.gz
mv PHPPatchset/patch-main::open_temporary_file.c.5 files/
make install clean
В появившемся меню указываем требуемые опции:
+--------------------------------------------------------------------+
¦ Options for php5 5.2.1_3 ¦
¦ +----------------------------------------------------------------+ ¦
¦ ¦ [X] CLI Build CLI version ¦ ¦
¦ ¦ [X] CGI Build CGI version ¦ ¦
¦ ¦ [X] APACHE Build Apache module ¦ ¦
¦ ¦ [ ] DEBUG Enable debug ¦ ¦
¦ ¦ [X] SUHOSIN Enable Suhosin protection system ¦ ¦
¦ ¦ [X] MULTIBYTE Enable zend multibyte support ¦ ¦
¦ ¦ [ ] IPV6 Enable ipv6 support ¦ ¦
¦ ¦ [ ] MAILHEAD Enable mail header patch ¦ ¦
¦ ¦ [X] REDIRECT Enable force-cgi-redirect support (CGI only) ¦ ¦
¦ ¦ [X] DISCARD Enable discard-path support (CGI only) ¦ ¦
¦ ¦ [ ] FASTCGI Enable fastcgi support (CGI only) ¦ ¦
¦ ¦ [X] PATHINFO Enable path-info-check support (CGI only) ¦ ¦
¦ ¦ ¦ ¦
¦ ¦ ¦ ¦
¦ ¦ ¦ ¦
+-+----------------------------------------------------------------+-+
¦ [ OK ] Cancel ¦
+--------------------------------------------------------------------+
проверить работоспособность установленного PHP можно командой echo "<? phpinfo(); ?>" | php
В выводе команды полностью отсутствуют упоминания о mysql, gd, iconv и прочих функциях. Что неудивительно, ибо в системе FreeBSD все дополнительные модули ставятся отдельно.
Чтобы установить необходимые модули можно воспользоваться двумя тактиками: поставить все за один раз, подав команды
cd /usr/ports/lang/php5-extensions
make install clean
и выбрав необходимый набор модулей из меню.
Или - собрать каждый модуль по отдельности:
cd /usr/ports/www/php5-session
make install clean
cd /usr/ports/graphics/php5-gd
make install clean
...
и т.д.
В случае необходимости можно дополнительно установить Zend Optimizer. Он также есть в портах FreeBSD (/usr/ports/devel/ZendOptimizer). Установка Zend не представляет никакой сложности; а сам продукт практически не нуждается в настройке.
По окончании установки, прописываем в файл /etc/rc.conf параметры для запуска nginx и Apache:
#
# Nginx Accelerator
#
nginx_enable="YES"
#
# Apache Backend
#
apache_enable="YES"
Конфигурируем nginx (файл /usr/local/etc/nginx/nginx.conf):
user www;
worker_processes 2;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] $request '
'"$status" $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log off;
sendfile on;
keepalive_timeout 65;
gzip on;
server {
listen XXX.YYY.ZZZ.DDD:80;
server_name localhost;
location /
{
# Максимальный размер отдаваемого файла
client_max_body_size 20M;
# Адрес, на который будем проксировать
proxy_pass http://127.0.0.1:80;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_connect_timeout 90;
proxy_send_timeout 90;
proxy_read_timeout 90;
proxy_send_lowat 12000;
proxy_buffer_size 4k;
proxy_buffers 4 32k;
proxy_busy_buffers_size 64k;
proxy_temp_file_write_size 64k;
proxy_temp_path /var/tmp/nginx;
}
}
}
Конфигурируем Apache(файл /usr/local/etc/apache/httpd.conf):
LoadModule realip_module libexec/apache/mod_realip.so
ServerName 127.0.0.1
Listen 127.0.0.1:80
RealIP localhost on
<Directory /home/www/*/*/www>
AllowOverride All
Options +Indexes +ExecCGI FollowSymLinks
DirectoryIndex index.php index.html index.htm
</Directory>
<Directory /home/www/*/*/cgi>
AllowOverride All
Options -Indexes +ExecCGI FollowSymLinks
DirectoryIndex index.cgi index.html index.htm index.cgi
</Directory>
NameVirtualHost 127.0.0.1:80
Запускаем nginx и Apache:
/usr/local/etc/rc.d/nginx start
/usr/local/etc/rc.d/apache.sh start
Проверяем, все ли работает так, как надо:
ps axw | grep nginx
3803 ?? Is 0:00.00 nginx: master process /usr/local/sbin/nginx
3804 ?? S 0:22.79 nginx: worker process (nginx)
3805 ?? S 0:22.31 nginx: worker process (nginx)
8128 p0 D+ 0:00.00 grep nginx
ps axw | grep httpd
1363 ?? Ss 0:03.24 /usr/local/sbin/httpd
8124 ?? S 0:01.83 /usr/local/sbin/httpd
8125 ?? R 0:01.16 /usr/local/sbin/httpd
8126 ?? S 0:01.20 /usr/local/sbin/httpd
8129 ?? S 0:01.16 /usr/local/sbin/httpd
8130 ?? S 0:01.08 /usr/local/sbin/httpd
8131 ?? S 0:01.23 /usr/local/sbin/httpd
8132 ?? S 0:01.27 /usr/local/sbin/httpd
Для FTP доступа установим сервер pure-ftpd. Pure-ftpd - один из самых надежных и одновременно удобных FTP серверов, зарекомендовавший себя великолепной работой на протяжении многих лет.
cd /usr/ports/ftp/pure-ftpd
make install clean
Указываем, что собирать сервер следует с поддержкой СУБД PostgreSQL, в которой будут храниться виртуальные пользователи. Выбор базы данных зависит от используемой системы управления хостингом. В нашем случае она построена на основе собственной разработки, базирующейся на СУБД PostgreSQL.
+--------------------------------------------------------------------+
¦ Options for pure-ftpd 1.0.21_1 ¦
¦ +----------------------------------------------------------------+ ¦
¦ ¦[ ] LDAP Support for users in LDAP directories ¦ ¦
¦ ¦[ ] MYSQL Support for users in MySQL database ¦ ¦
¦ ¦[ ] PAM Support for PAM authentication ¦ ¦
¦ ¦[X] PGSQL Support for users in PostgreSQL database ¦ ¦
¦ ¦[X] PRIVSEP Enable privilege separation ¦ ¦
¦ ¦[X] PERUSERLIMITS Per-user concurrency limits ¦ ¦
¦ ¦[X] THROTTLING Bandwidth throttling ¦ ¦
¦ ¦[X] BANNER Show pure-ftpd welcome upon session start ¦ ¦
¦ ¦[X] UPLOADSCRIPT Support uploadscript daemon ¦ ¦
¦ ¦[ ] UTF8 Support for charset conversion (expreimental)¦ ¦
¦ ¦[X] SENDFILE Support for the sendfile syscall ¦ ¦
¦ ¦ ¦ ¦
¦ ¦ ¦ ¦
¦ ¦ ¦ ¦
¦ ¦ ¦ ¦
+-+----------------------------------------------------------------+-+
¦ [ OK ] Cancel ¦
+--------------------------------------------------------------------+
При выборе установки с PostgreSQL, автоматически поставятся порты databases/postgresql82-client и databases/postgresql82-server После установки добавляем в файл /etc/rc.conf строчки
#
# BILLING DATABASE
#
postgresql_enable="YES"
меняем домашний каталог для пользователя pgsql на /var/db/pgsql
mkdir /var/db/pgsql
chown pgsql:pgsql /var/db/pgsql
chmod 0700 /var/db/pgsql
pw usermod pgsql -d /var/db/pgsql
и инициализируем базу данных:
su - pgsql
initdb --encoding=UTF8 --locale=ru_RU.UTF-8 --pgdata=/var/db/pgsql/data
The files belonging to this database system will be owned by user "pgsql".
This user must also own the server process.
The database cluster will be initialized with locale ru_RU.UTF-8.
......
конфигурируем файл /var/db/pgsql/data/postgresql.conf:
max_connections = 150
shared_buffers = 40MB
max_fsm_pages = 76800
log_destination = 'syslog'
silent_mode = on
autovacuum = on
datestyle = 'iso, dmy'
lc_messages = 'ru_RU.UTF-8'
lc_monetary = 'ru_RU.UTF-8'
lc_numeric = 'ru_RU.UTF-8'
lc_time = 'ru_RU.UTF-8'
файл прав доступа к базе данных /var/db/pgsql/data/pg_hba.conf
# TYPE DATABASE USER CIDR-ADDRESS METHOD
# "local" is for Unix domain socket connections only
local all all md5
# IPv4 local connections:
host all all 127.0.0.1/32 md5
# IPv6 local connections:
host all all ::1/128 md5
и запускаем сервер PostgreSQL
/usr/local/etc/rc.d/postgresql start
Проверяем работу:
ps axw | grep postgres
749 ?? Ss 0:05.39 /usr/local/bin/postgres -D /var/db/pgsql/data
751 ?? Ss 0:08.97 postgres: writer process (postgres)
752 ?? Ss 0:00.64 postgres: stats collector process (postgres)
Конфигурируем Pure-FTPD (/usr/local/etc/pure-ftpd.conf)
ChrootEveryone yes
BrokenClientsCompatibility no
MaxClientsNumber 50
Daemonize yes
MaxClientsPerIP 8
VerboseLog no
DisplayDotFiles yes
AnonymousOnly no
NoAnonymous yes
SyslogFacility ftp
DontResolve yes
MaxIdleTime 15
PGSQLConfigFile /usr/local/etc/pureftpd-pgsql.conf
LimitRecursion 2000 8
AnonymousCanCreateDirs no
MaxLoad 4
AntiWarez yes
Umask 110:007
MinUID 100
AllowUserFXP no
AllowAnonymousFXP no
ProhibitDotFilesWrite no
ProhibitDotFilesRead no
AutoRename no
AnonymousCantUpload no
MaxDiskUsage 99
CustomerProof yes
Указываем, откуда брать информацию о виртуальных пользователях (/usr/local/etc/pureftpd-pgsql.conf).
PGSQLServer 127.0.0.1
PGSQLPort 5432
PGSQLUser pureftpd
PGSQLPassword topsecret
PGSQLDatabase billing
PGSQLCrypt cleartext
PGSQLGetPW SELECT passwd FROM ftp.ftpusers WHERE ftpuser='\L' AND enabled = 1;
PGSQLGetUID SELECT user_id FROM ftp.ftpusers WHERE ftpuser='\L' AND enabled = 1;
PGSQLGetGID SELECT group_id FROM ftp.ftpusers WHERE ftpuser='\L' AND enabled = 1;
PGSQLGetDir SELECT homedir FROM ftp.ftpusers WHERE ftpuser='\L' AND enabled = 1;
Создаем таблицу пользователей в СУБД PostgreSQL:
psql -U pgsqlМы разделяем типы пользователей. То есть, у нас есть отдельный пользователь c отдельным паролем для доступа к СУБД, FTP и для работы в шелле по SSH.
CREATE USER billing WITH PASSWORD 'supersecret' NOCREATEDB NOCREATEUSER;
CREATE DATABASE billing WITH ENCODING='UTF8' OWNER=billing;
CREATE SCHEMA ftp;
CREATE TABLE ftp.ftpusers
(
ftpuser VARCHAR(255) NOT NULL CHECK (LENGTH(ftpuser) > 0),
passwd VARCHAR(255) NOT NULL CHECK (LENGTH(passwd) > 0),
user_id INT NOT NULL DEFAULT '-1',
group_id INT NOT NULL DEFAULT '-1',
homedir VARCHAR(255) NOT NULL CHECK (LENGTH(homedir) > 0),
expires bigint not null default '-1',
enabled bigint not null default '0',
descr text,
CONSTRAINT ftpusers_pk PRIMARY KEY (ftpuser)
) WITHOUT OIDS;
CREATE USER pureftpd WITH PASSWORD 'topsecret' NOCREATEDB NOCREATEUSER;
GRANT USAGE ON SCHEMA ftp TO pureftpd;
GRANT SELECT ON ftp.ftpusers TO pureftpd;
Чтобы не возникало неразберихи, следует как-то систематизировать все создаваемые сущности. Хорошим и проверенным методом является следующий: все пользователи данной техплощадки имеют один и тот же префикс в виде имени площадки. Пример: u00000, где u - сокращение от "user", а 00000 - порядковый номер площадки по базе данных.
То есть, для площадки с номером 12345 у нас будут следующие реквизиты:
/home/www/u12345 - каталог с сайтами, например /home/www/u12345/somesite.ru
u12345 - имя пользователя для работы по SSH и FTP
u12345.ftp.ourhosting.ru - название хоста для доступа на техплощадку по FTP
u12345.ssh.ourhosting.ru - название хоста для доступа на техплощадку по SSH
u12345 - имя пользователя для работы c базой данных
u12345 - собственно база данных
и
u12345.mysql.ourhosting.ru - имя хоста, на котором расположена БД
Разумеется, у нашего хостинга должны быть настроены зоны DNS так, чтобы при запросе имен хостов отдавались правильные IP адреса.
Небольшое отступление. Очень плохая практика называть пользователей значимыми именами, например "alex" или "somesite_ru": это очень неудобно с точки зрения организации биллинга, системы резервного копирования и миграции данных между машинами, заставляет каждый раз придумывать новые имена, а самое главное - при наличии достаточного количества клиентов приводит к повторениям и дальнейшей путанице.
Профиль пользователя
На сегодняшний день реалии таковы, что большая часть сайтов имеют кодировку Windows-1251. Применительно к вопросу вебхостинга это означает что вся локализация консоли, вебсервера и сервера базы данных должна быть ru_RU.CP1251. Для локализации консоли необходимо создать дополнительный класс russianwww в файле /etc/login.conf:
#
# Russian Web Users Accounts. Setup proper environment variables.
#
russianwww|Russian Users Accounts:\
:charset=CP1251:\
:lang=ru_RU.CP1251:\
:umask=0007:\
:tc=default:
Если мы хотим ограничить пользователя в системных ресурсах, а возникают ситуациии когда это единственный способ одновременно уместить много требовательных к ресурсам сайтов на одном сервере без передёргивания всего "одеяла" на кого либо одного можно добавить в созданный класс строки вида:
:cputime=5m:\
:datasize=32M:\
:stacksize=4M:\
:memorylocked=64M:\
:memoryuse=96M:\
:filesize=unlimited:\
:coredumpsize=0:\
:openfiles=256:\
:maxproc=128:\
:sbsize=unlimited:\
:vmemoryuse=unlimited:\
:priority=20:\
:ignoretime@:\
Их необходимо вставить перед :tc=default: предложенные значения даны в иллюстративных целях и в Вашей системе могут быть другими и перед их установкой рекомендуется ознакомиться со страницей справочника man login.conf(5). После необходимых правок /etc/login.conf, пересоздаем базу данных:
cap_mkdb /etc/login.conf
Для локализации Apache придется изменить файл /usr/local/sbin/apachectl внеся в него следующие строки:
#!/bin/sh
# Localization
umask 0002
LC_ALL=ru_RU.CP1251; export LC_ALL
LANG=ru_RU.CP1251; export LANG
MM_CHARSET=CP1251; export MM_CHARSET
Чтобы локализация "подхватывалась" при автозапуске, следует заменить скрипт /usr/local/etc/rc.d/apache на предлагаемый:
#!/bin/sh
case "$1" in
start)
[ -x /usr/local/sbin/apachectl ] && /usr/local/sbin/apachectl start > /dev/null && echo -n ' apache'
;;
stop)
[ -r /var/run/httpd.pid ] && /usr/local/sbin/apachectl stop > /dev/null && echo -n ' apache'
;;
*)
echo "Usage: ${0##*/} { start | stop }" >&2
exit 64
;;
esac
exit 0
Для сервера MySQL достаточно будет создавать пользовательские базы данных с кодировкой cp1251 и cp1251_general_ci collation:
CREATE DATABASE `u00001` DEFAULT CHARACTER SET cp1251 COLLATE cp1251_general_ci;
Система каталогов пользователей
Как уже было описано в предыдущей статье, пользователи виртуального хостинга размещаются на отдельном разделе /home в каталоге www. Если с течением времени место в /home/www закончится, мы сможем установить в сервер дополнительный физический диск и смонтировать его в /home/www1. При этом общая схема работы хостинга не изменится и дополнительная настройка не понадобится.
Для того чтобы пользователи не имели доступа к "чужим" даныым необходимо правильно выставить права и владельцев каталогов. Такая настройка - очень важный момент, поэтому остановимся на ней подробнее.
Итак, пользователи не должны иметь доступ к каталогам друг друга, но при этом надо позаботиться о доступности этих данных вебсерверу. К счастью, механизм UNIX permissions позволяет решить эту проблему: мы создадим специальную группу в которую будут входить пользователи вебхостинга и пользователь, от которого запущен вебсервер Apache
pw groupadd virtwww -M www
Создаем каталоги:
mkdir -p /home/www
chown root:wheel /home/www
Права на каталог /home/www должны быть 0711, чтобы нельзя было получить список файлов/каталогов внутри. Пользователи могут войти в каталог, но не могут получить листинг.
Создаем пользователя нашего вебхостинга:
pw useradd u00001 -g virtwww -d /home/www/u00001 -s /usr/local/bin/bash -L russianwww -m
pw groupadd u00001 -M u00001
Таким образом, каждый пользователь имеет две группы: первичную - virtwww и дополнительную, совпадающую по имени с именем пользователя.
Устанавливаем права на домашний каталог пользователя:
chmod 04710 /home/www/u00001
chown u00001:www /home/www/u00001
При таких правах на каталог никто кроме пользователя u00001 и вебсервера не будет иметь доступа к данным пользователя, а созданные внутри файлы и каталоги будут наследовать ID и GID владельца.
Для хранения лог-файлов мы выберем каталог /var/log/apache.
Именно здесь нам потребуется введенная дополнительно группа у пользователя хостинга: при помощи нее мы выдадим права на просмотр лог-файлов.
Создаем каталог, в котором будут храниться лог-файлы:
mkdir -p /var/log/apache
chmod 0711 /var/log/apache
chown root:wheel /var/log/apache
mkdir /var/log/apache/u00001
chmod 0750 /var/log/apache/u00001
chown root:u00001 /var/log/apache/u00001
Теперь осталось расписать процедуру заведения виртуального хоста для пользователя. Сделаем это на примере сайта example.ru:
1) Создаем каталоги
mkdir -p /home/www/u00001/example.ru/{www,cgi,tmp}
chown -R u00001:virtwww /home/www/u00001/example.ru
chmod -R 04770 /home/www/u00001/example.ru
chmod 0710 /home/www/u00001/example.ru/cgi
mkdir /var/log/apache/u00001/example.ru
chmod 0750 /var/log/apache/u00001/example.ru
2) Прописываем конфигурацию для Apache
<VirtualHost 127.0.0.1:80>
ServerName example.ru
ServerAlias www.example.ru
DocumentRoot /home/www/u00001/example.ru/www
ScriptAlias /cgi /home/www/u00001/example.ru/cgi
ErrorLog /var/log/apache/u00001/example.ru/error_log
TransferLog /var/log/apache/u00001/example.ru/access_log
php_admin_value upload_tmp_dir /home/www/u00001/example.ru/tmp
php_admin_value doc_root /home/www/u00001/example.ru
php_admin_value user_dir www
php_admin_value open_basedir /home/www/u00001:/usr/local/share/smarty:/usr/local/share/pear
php_admin_value session.save_path "0;0660;/home/www/u00001/example.ru/tmp"
User u00001
Group virtwww
</VirtualHost>
Обратите внимание на значение переменной session.save_path. В ней требуется указать права 0660.
3) Проверяем конфигурацию Apache:
/usr/local/sbin/apachectl configtest
Syntax OK
4) Перезапускаем Apache и проверяем работу системы:
/usr/local/sbin/apachectl restart
echo "<? phpinfo(); ?>" > /home/www/u00001/example.ru/www/test.php
fetch http://example.ru/test.html
test.php 34 kB 13 MBps
rm /home/www/u00001/example.ru/www/test.php
5) Заводим FTP доступ для пользвателя Поскольку всех FTP пользователей мы храним в БД, вся процедура создания доступа по FTP сводится к созданию ровно одной записи:
INSERT INTO ftp.ftpusers
VALUES('u00001', 'topsecret', '1001', '1000', '/home/www/u00001', 0, 1, '');
где 1001 - UID пользователя, а 1000 - GID группы virtwww Проверяем доступ по FTP:
ftp localhost
Trying ::1...
Connected to localhost.
220---------- Welcome to Pure-FTPd [privsep] [TLS] ----------
220-You are user number 1 of 50 allowed.
220-Local time is now 01:58. Server port: 21.
220-This is a private system - No anonymous login
220 You will be disconnected after 15 minutes of inactivity.
Name (localhost:stellar): u00001
331 User u00001 OK. Password required
Password:
230-User u00001 has group access to: u00001 virtwww
230 OK. Current restricted directory is /
Remote system type is UNIX.
Using binary mode to transfer files.
ftp> quit
221-Goodbye. You uploaded 0 and downloaded 0 kbytes.
221 Logout.
Для автоматизации создания пользователей можно воспользоваться скриптом, доступным по адресу http://www.reki.ru/products/webhosting/addftpuser
Настройка параметров системы
Настройка параметров системы преследует две цели. Первая цель: повысить уровень безопасности путем изоляции пользователей друг от друга и от системы и вторая - достичь оптимальной производительности хостинга.
Для изоляции клиентов хостинга на уровне файловой системы мы воспользовались системой unix permissions. Теперь необходимо изолировать клиентов на уровне доступа к списку процессов, используемых ресурсов и т.п. Для этого в файл /etc/sysctl.conf вносим строчку
security.bsd.see_other_uids=0
и перечитываем конфигурацию /etc/rc.d/sysctl restart.
Для того чтобы при падении программ система писала core-файлы в один каталог, а не во всю файловую систему, также добавляем строчки
kern.sugid_coredump=0
kern.corefile=/var/tmp/%N.core
Для оптимизации работы системы пропишем следующие параметры:
kern.ipc.maxsockbuf=16777216
net.inet.tcp.rfc1323=1
net.inet.tcp.sendspace=1048576
net.inet.tcp.recvspace=1048576
net.inet.tcp.sack.enable=1
kern.maxfiles=204800
kern.maxfilesperproc=200000
net.inet.ip.portrange.first=1024
net.inet.ip.portrange.last=65535
net.inet.ip.portrange.randomized=0
net.inet.tcp.nolocaltimewait=1
kern.ipc.shmmax=268435456
Детальное их описание довольно сложно и выходит за рамки данного руководства; интересующимся следует обратиться к текстам докладов Первой конференции российскийх вебразрботчиков.
Выводы
Разумеется, это - всего лишь прототип реального хостинга и при количестве клиентов до нескольких сотен будет работать в предлагаемой конфигурации. Для развертывания системы в эксплуатацию с несколькими тысячами пользователей необходимо, как минимум, разнести вебсервер и сервер БД, а для биллинга выделить отдельную машину. Также следует рассмотреть вопросы организации резервных копий (backup), систему миграции пользователей между серверами хостнговой площадки, а также - систему миниторинга пользовательской активности. Возможно, об этом - в следующих статьях.