Как мы поднимали сервер или создание веб-сервера для большой нагрузки (осторожно, с юмором)
Доброго времени суток :-)
Начну немного с истории… Предлогом для написания этой статьи послужили две вещи. Во-первых, умеренная DDoS атака на gibs0n.name/, из-за которой сервер был недоступен аж несколько дней, а одна из попыток побороть атаку завершилась нашей ошибкой – был заблокирован доступ к серверу со всех адресов. Пришлось ехать в датацентр и разбираться с этой адской машиной под названием сервер… Во-вторых, недавний сбой по питанию в датацентре, после которого мы однозначно решили: будем менять железо. С этого всё и началось… На этом этапе, хочется сказать огромное спасибо моему лучшему другу – gibson'у за предоставленное железо для сервера. Среди этого железа – материнская плата MSI P965 Neo. Было решено установить процессор Intel Core2Duo и 8ГБ памяти DDRII-800. Как потом оказалось, на этой материнке жутко нестандартные контроллеры дисков. Обнаружилось это, когда я подключил к ней 2 винчестера, чтобы перенести полностью настроенную и готовую к работе ОС с испытательного винчестера на действующий… Тут-то и начались танцы с бубном. Master-винчестер, подключенный к единственному IDE каналу, оказался /dev/ad6, вместо ожидаемого /dev/ad0, а Slave-винчестер — /dev/ad7. Находящийся рядом с ним «отделённый» SATA порт оказался /dev/ad4 (как, в общем, первый SATA порт практически на любых материнках). А сгруппированные сбоку 4 SATA порта имели названия /dev/ad8, /dev/ad10, /dev/ad12, /dev/ad14 и были расположены «змейкой».
Все винчестеры, порты на материнке и шлейфы, а так же, слоты на материнке и сетевые карты (их у меня две) были помечены наклейками.
Вот так выглядит сервер изнутри:
А так – снаружи:
А теперь – в датацентре:

(по понятным причинам, телефон, написанный на верхнем сервере, стёрт)
Наша широкая душа занимает аж три юнита в стойке… :)
Ну а теперь – хватит. Насмотрелись мы уже подобных картин:
Будем поднимать сервер самостоятельно и экономить на тушОнке :)
В качестве ОС была выбрана FreeBSD 6.4 amd64. Такой выбор связан с тем, что, во-первых, я большой любитель FreeBSD, во-вторых, нужна 64-битная система для нормальной работы с 8ГБ памяти, ну а в-третьих, на момент всех моих действий, более новые версии системы имели известные уязвимости.
Итак, начнём с установки системы. Процесс установки хорошо описан в одной из моих предыдущих статей. Но на том, что там описано, мы не остановимся.
Устанавливаем систему, доходим до выбора компонентов (а конкретнее – src) и выбираем там все исходники, кроме games. Почему так – а потому, что мы будем перестраивать абсолютно всё, чтобы добиться максимальной производительности. После установки – обновляем порты и устанавливаем deco и le, это наши рабочие инструменты. mc пока можно не ставить (хотя, кому он удобнее, может и поставить).
С chflags прийдётся повременить, т.к. он нам будет мешать перестроить «мир». Точно так же и с удалением ненужных файлов и папок, потому как перестройка «мира» восстановит всю файловую структуру. Так же, не вижу особого смысла настраивать rc.conf на этом этапе.
Теперь у нас две задачи – сначала перестроить ядро системы, потом перестроить «мир». Начнём с конфигурации /etc/make.conf перед всем этим… Приводим вышеуказанный файл к такому виду:
CPUTYPE?=core2 NO_PROFILE=yes NO_GAMES=yes DOC_LANG=en_US.ISO8859-1 ru_RU.KOI8-R WITHOUT_X11=yes NO_X11=yes WITH_IDEA=yes MAKE_IDEA=yes WITHOUT_GAMES=yes WITHOUT_INET6=yes WITHOUT_INET6_SUPPORT=yes WITHOUT_PROFILE=yes WITHOUT_IPV6=yes NO_BLUETOOTH=yes NO_AUDIT=yes NO_USB=yes NO_NIS=yes SENDMAIL_CFLAGS=-I/usr/local/include/sasl -DSASL SENDMAIL_LDFLAGS=-L/usr/local/lib SENDMAIL_LDADD=-lsasl2 PORTSDIR?=/ports DEFAULT_MYSQL_VER=51 .if ${.CURDIR} == ${PORTSDIR}/databases/mysql${DEFAULT_MYSQL_VER}-client WITH_CHARSET=utf8 WITH_COLLATION=utf8_general_ci BUILD_OPTIMIZED=yes BUILD_STATIC=yes .endif .if ${.CURDIR} == ${PORTSDIR}/databases/mysql${DEFAULT_MYSQL_VER}-server WITH_CHARSET=utf8 WITH_XCHARSET=all WITH_COLLATION=utf8_general_ci WITHOUT_OPENSSL=yes WITHOUT_LINUXTHREADS=yes BUILD_STATIC=yes WITH_INNODB=yes WITHOUT_NDB=yes .endif
CPUTYPE – тип процессора. Указываем свой из:
AMD: opteron, athlon64, athlon-mp, athlon-xp, athlon-4, athlon-tbird, athlon, k8, k6-3, k6-2, k6, k5
Intel: nocona, pentium4[m], prescott, pentium3[m], pentium-m, pentium2, pentiumpro, pentium-mmx, pentium, i486, i386, core2
Via: c3, c3-2
Alpha/AXP: ev67, ev6, pca56, ev56, ev5, ev45, ev4
Intel ia64: itanium2, itanium
Заменяем 51 на свою версию MySQL (если она вообще будет на этом сервере). Если MySQL устанавливаться не будет – можно удалить эту строку и всё, что идёт после неё.
PORTSDIR – РЕАЛЬНЫЙ путь к папке с портами. Обычно /usr/ports, но у меня это /ports, а /usr/ports – символьная ссылка на /ports.
Опции для Sendmail – в данной конфигурации включают поддержку SASL (для SMTP авторизации на сервере). Если сервер не будет использоваться как SMTP сервер (или если не нужна авторизация) – эти строки следует убрать. Если же будет – тогда ещё и SASL поставить прийдётся. Как – расскажу чуть позже.
Ах, да… По поводу NO_USB погорячился. Если на сервере используются USB устройства, следует удалить эту строку :) У меня USB устройств нет, поэтому usbd мне не нужен :)
Далее – приводим /etc/src.conf к такому виду:
WITHOUT_INET6=yes WITHOUT_INET6_SUPPORT=yes WITHOUT_PROFILE=yes
Тут, как мне кажется, всё должно быть понятно...
Далее – идём в /usr/src/sys/amd64/conf (у кого i386 – будут всего два отличия в конфигурации) и приводим файл нашего ядра к такому виду (не забываем про hints):
ident TEM # у кого i386 – пишем здесь i386 вместо amd64 machine amd64 # И тип процессора – соответственно I686_CPU cpu HAMMER maxusers 0 options SCHED_4BSD # Если у вас только одно ядро – SMP делать не нужно – убираем эту строку. options SMP options ADAPTIVE_GIANT options PREEMPTION options COMPAT_43 options COMPAT_FREEBSD4 options COMPAT_FREEBSD5 options SYSVSHM options SYSVSEM options DEVICE_POLLING options SYSVMSG options INET device acpi device ether device loop device bpf options IPFIREWALL options IPFIREWALL_VERBOSE options DUMMYNET options ZERO_COPY_SOCKETS options FFS options SOFTUPDATES options UFS_DIRHASH device random device mem options _KPOSIX_PRIORITY_SCHEDULING options HZ=5000 options PPS_SYNC device pty options LIBICONV device atkbdc device atkbd options ATKBD_DFLT_KEYMAP makeoptions ATKBD_DFLT_KEYMAP=ru.koi8-r options KBD_DISABLE_KEYMAP_LOAD options KBD_INSTALL_CDEV device vga device sc options MAXCONS=8 options SC_DFLT_FONT makeoptions SC_DFLT_FONT=cp866 options SC_DISABLE_KDBKEY options SC_DISABLE_REBOOT options SC_HISTORY_SIZE=100 options SC_MOUSE_CHAR=0x3 options SC_PIXEL_MODE options SC_NO_CUTPASTE options SC_NO_SYSMOUSE device ata device atadisk options ATA_STATIC_ID device miibus device pci device crypto device cryptodev options INIT_PATH=/sbin/init options PANIC_REBOOT_WAIT_TIME=10 options DIRECTIO device pf # У меня обе сетевые карты в сервере Intel PRO/100 # У кого другая карта – соответственно пишем здесь её название # Небольшой выигрыш в производительности можно получить, если вшить драйвер в ядро device fxp options ACCEPT_FILTER_HTTP options ACCEPT_FILTER_DATA options ALTQ options ALTQ_CBQ options ALTQ_RED options ALTQ_RIO options ALTQ_HFSC options ALTQ_CDNR options ALTQ_PRIQ options ALTQ_NOPCC options ALTQ_DEBUG device pflog device pfsync
Компилируем и устанавливаем ядро, как это было описано в моей предыдущей статье :) Это моя рабочая конфигурация ядра...
Перезагружаем сервер. Если всё нормально загрузилось – папку /boot/kernel.old можно удалить (если вам старое ядро, конечно, не нужно сохранять на память)...
Теперь будем пересобирать «мир». Для этого идём в /usr/src и делаем там make buildworld
Далее создаём файл, к примеру, /update.sh такого содержания:
#!/bin/sh fsck -p mount -u / mount -a cd /usr/src mergemaster -p make installworld && make delete-old && mergemaster -i
Выставляем файлу права 0755 и запускаем команду shutdown now, чтобы перейти в однопользовательский режим (подразумевается, что сервер находится перед вами, а не в километрах от вас и по SSH доступу). Соглашаемся с тем, что наш шелл будет /bin/sh, запускаем /update.sh и ждём. Ждать придется некоторое, весьма продолжительное время, которое зависит от вашего железа… После всех этих действий, перезагружаем сервер (нас reboot, а мы крепчаем...) и радуемся, если всё получилось правильно. Не забываем удалить этот скрипт после перезагрузки :)
Дальше, пока не забыли, выполняем такую команду (только если наш сервер будет отдавать почту через POP3 протокол!):
chmod o-x /usr/bin/mail /usr/bin/Mail /usr/bin/mailx /usr/bin/biff
Это нужно, чтобы запретить пользователям пользоваться этими командами. В противном случае, использование этих команд вместе с POP3 сервером, в некоторых случаях, может привести к повреждению почтовых ящиков.
Теперь можно и продолжить с удалением ненужных папок, переносом папок на другие винчестеры и установкой schg флагов. Очень советую поставить schg ещё и на всё в папке /boot, кроме loader.conf
Сделать это можно, например, так (подразумевается, что loader.conf уже есть в папке /boot):
cd /boot
chflags -R schg *
chflags noschg loader.conf
cd /куда/нам/дальше/надо :)
Дальше создаём (или перезаписываем) файл /etc/sysctl.conf таким вот образом:
net.inet.tcp.blackhole=2 net.inet.udp.blackhole=1 net.inet.tcp.always_keepalive=0 net.inet.tcp.keepidle=60000 net.inet.tcp.recvspace=8192 net.inet.tcp.sendspace=16384 kern.ipc.nmbclusters=65536 kern.ipc.somaxconn=32768 kern.ipc.maxsockets=204800 kern.maxfiles=256000 kern.maxfilesperproc=230400 net.inet.ip.portrange.first=1024 net.inet.ip.portrange.last=65535 net.inet.ip.portrange.randomized=0 net.inet.tcp.maxtcptw=40960 net.inet.tcp.msl=15000 net.inet.tcp.syncookies=1 net.inet.tcp.sack.enable=1 net.inet.tcp.nolocaltimewait=1 net.inet.icmp.drop_redirect=1 net.inet.icmp.log_redirect=1 net.inet.ip.redirect=0 kern.polling.enable=1 kern.polling.burst_max=1000 kern.polling.each_burst=50 net.inet.icmp.icmplim=5000 security.bsd.see_other_gids=0 security.bsd.see_other_uids=0
Это увеличивает производительность всего, что работает с сетью, увеличивает максимальное количество одновременно открытых файлов и запрещает пользователям видеть чужие процессы. Зачем нужно последнее – расскажу позже (если конечно нужно)...
Так же, в /boot/loader.conf можно добавить такие строки:
net.inet.tcp.syncache.hashsize=1024 net.inet.tcp.syncache.bucketlimit=100 net.inet.tcp.tcbhashsize=4096
Дальше можно настроить ttys и rc.conf :) Не забываем включить ipfw и pf, они нам будут нужны. Для ftpd задаём единственный флаг – -R. Он нужен, чтобы разрешить режим fxp. А вот стандартный sshd не будем включать. Поставим альтернативный из портов.
Теперь расскажу про firewall'ы:
Файл /etc/pf.conf у меня выглядит таким образом:
ext_if="fxp0" tablepersist block in log quick from pass in on $ext_if proto tcp to $ext_if port www flags S/SA keep state ( max-src-conn-rate 100/10, overload flush)
Это простой Anti-DDoS, который блокирует адрес, если с него идут более 100 подключений на 80-й TCP порт за 10 секунд.
Теперь ipfw (в принципе, можно обойтись и без ipfw, просто, как по мне, он гораздо легче воспринимается). /etc/rc.firewall (у меня он выглядит так):
#!/bin/sh - # Some config... fwcmd="ipfw" # ipfw command ext_if="fxp0" # external interface int_if="fxp1" # internal interface my_net_ext="193.34.96.112/28" # my external network my_net_int="192.168.0.0/24" # my internal network # Delete all rules ${fwcmd} -f flush # Block all traffic while rules are loading ${fwcmd} add 00001 deny all from any to any # Allow all local connections ${fwcmd} add 00002 allow all from any to any via lo0 # Reset all IDENT connections to make SSH faster ${fwcmd} add 00003 reset tcp from any to any ident # Allow all from my (trusted) networks ${fwcmd} add 00004 allow all from ${my_net_ext} to me # incoming, external ${fwcmd} add 00004 allow all from me to ${my_net_ext} # outgoing, external ${fwcmd} add 00005 allow all from ${my_net_int} to me # incoming, internal ${fwcmd} add 00005 allow all from me to ${my_net_int} # outgoing, internal # Deny smartd and mbmon traffic from all except trusted networks described above ${fwcmd} add 00006 deny tcp from any to me smartd ${fwcmd} add 00007 deny tcp from any to me mbmon # Allow all other traffic ${fwcmd} add 65534 allow all from any to any # Delete the first all-blocking rule ${fwcmd} delete 00001
Теперь поехали разбираться… Есть два интерфейса, один внешний и один внутренний (аварийный). Первым делом, мы блокируем весь трафик, до тех пор, пока все правила не загрузятся. Далее, мы разрешаем весь локальный трафик. Потом сбрасываем IDENT соединения (чтобы ssh работал быстрее). Потом я разрешаю весь трафик из своих сетей. Дальше – запрещаю доступ к smartd (программа для контроля состояния винчестеров) и mbmon (мониторинг состояния материнки). Кстати, последняя с новой материнкой работать не захотела… Дальше – разрешаем всё, что не попало под другие правила. Так же, если 6 и 7 правила вам нужны (и на сервере таки будут эти программы), то в файл /etc/services нужно добавить mbmon и smartd. Для mbmon – стандартный порт 12999. Для smartd я сделал порт 13000. Обе программы будут запускаться через inetd.
Теперь несколько слов про /etc/crontab и /etc/newsyslog.conf
Кронтаб у меня выглядит таким образом:
SHELL=/bin/sh PATH=/etc:/bin:/sbin:/usr/bin:/usr/sbin HOME=/var/log #minute hour mday month wday who command #*/5 * * * * root /usr/libexec/atrun */11 * * * * operator /usr/libexec/save-entropy 0 * * * * root newsyslog #1 3 * * * root periodic daily #15 4 * * 6 root periodic weekly #30 5 1 * * root periodic monthly 0 * * * * root pfctl -t ddos -T flush > /dev/null 2>&1 0 * * * * root /root/bashorg > /dev/null 2>&1 0 0 * * * root /root/backup > /dev/null 2>&1 0 0 * * * root portsnap fetch update > /dev/null 2>&1 0 0 * * 1 root fetch –q –p –o /etc/namedb/named.root ftp://ftp.internic.net/domain/named.root > /dev/null 2>&1 0 0 * * 2 root rndc reload > /dev/null 2>&1
1) at я использовать не собираюсь.
2) Периодические отчёты я всё равно не читаю. Зачем их генерировать?
3) Очищаем таблицу заблокированных адресов каждый час.
4) Обновляем приветствие в системе. (этот скрипт я опишу чуть позже)
5) Создаём автоматические бэкапы. (этот скрипт тоже не останется за кадром)
6) Каждую неделю обновляем named.root и перезапускаем named
newsyslog.conf у меня выглядит так:
# logfilename [owner:group] mode count size when flags [/pid_file] [sig_num] /var/log/all.log 600 7 * @T00 J /var/log/amd.log 644 7 100 * J /var/log/auth.log 600 7 100 * JC /var/log/console.log 600 5 100 * J /var/log/cron 600 3 100 * JC #/var/log/daily.log 640 7 * @T00 JN /var/log/debug.log 600 7 100 * JC /var/log/kerberos.log 600 7 100 * J /var/log/lpd-errs 644 7 100 * JC /var/log/maillog 640 7 * @T00 JC /var/log/messages 644 5 100 * JC #/var/log/monthly.log 640 12 * $M1D0 JN /var/log/pflog 600 3 100 * JB /var/run/pflogd.pid #/var/log/ppp.log root:network 640 3 100 * JC /var/log/security 600 10 100 * JC /var/log/sendmail.st 640 10 * 168 B /var/log/slip.log root:network 640 3 100 * JC #/var/log/weekly.log 640 5 1 $W6D0 JN /var/log/wtmp 644 3 * @01T05 B /var/log/xferlog 600 7 100 * JC /var/log/httpd.access.log www:www 644 7 * $D0 B /var/run/lighttpd.pid /var/log/httpd.error.log www:www 644 7 * $D0 B /var/run/lighttpd.pid
Заметим последние две строки – они нужны для ротации логов lighttpd (именно такой сервер мы используем...). Остальное – вроде стандартное :)
/etc/ftpusers в моём случае выглядит так (/etc/ftpchroot описан в прошлой статье):
root toor daemon operator bin tty kmem games news man sshd bind proxy _pflogd _dhcp uucp pop www nobody mailnull smmsp mysql _sphinx cyrus @wheel @mailuser
Создаём группу mailuser:
pw groupadd mailuser
Дальше командой adduser создаём пользователя с UID=1000 и группой www (шелл – csh, домашний каталог, например, /home/web). Этот пользователь будет иметь доступ к ftp и от его имени должен будет работать lighttpd. Очень советую пользователю www сделать такой же UID (в нашей конфигурации 3 пользователя с одинаковым UID'ом, в их числе и www).
Точно так же, можно добавлять почтовых пользователей с шеллом nologin и домашним каталогом /nonexistent
Настройка ntp была описана в прошлой статье, поэтому на этом месте я не останавливаюсь. А вот с named (DNS демон) и sendmail'ом прийдётся немножко поковыряться… И начнём с named.
Первым делом, приведу листинг его рабочей папки (где видны владелец, группа и права):
/usr/var/named/etc/namedb$ ls -al total 22 drwxr-xr-x 5 root wheel 512 Feb 9 20:16 . drwxr-xr-x 3 root wheel 512 Feb 9 00:44 .. drwxr-xr-x 2 bind wheel 512 Nov 26 2008 dynamic drwxr-xr-x 2 root wheel 512 Feb 9 20:11 master -rw-r----- 1 bind wheel 4035 Feb 9 20:19 named.conf -rw-r--r-- 1 bind wheel 2969 Nov 26 2008 named.root -rw-r----- 1 bind wheel 163 Feb 9 20:14 rndc.conf -rw-r----- 1 bind wheel 97 Feb 9 00:44 rndc.key drwxr-xr-x 2 bind wheel 512 Nov 26 2008 slave
Как генерировать ключ я уже рассказывал. Приведу пример named.conf (прямо с нашего сервера):
options { directory "/etc/namedb"; pid-file "/var/run/named/pid"; listen-on { any; }; version "Forget it! :)"; forwarders { // DNS серверы провайдера (так будет быстрее, всё-таки) 193.34.96.113; 193.34.96.1; }; }; key "rndc-key" { algorithm hmac-md5; secret "здесь ключ из rndc.key"; }; controls { inet 127.0.0.1 port 953 allow { 127.0.0.1; } keys { "rndc-key"; }; }; //zone "." { // type hint; // file "named.root"; //}; // Вместо named.root просто делаем slave для всех зон, это будет быстрее // ...при условии, что в /etc/resolv.conf записан 127.0.0.1 на первом месте zone "." { type slave; file "slave/root.slave"; masters { 192.5.5.241; // F.ROOT-SERVERS.NET. }; notify no; }; zone "arpa" { type slave; file "slave/arpa.slave"; masters { 192.5.5.241; // F.ROOT-SERVERS.NET. }; notify no; }; zone "in-addr.arpa" { type slave; file "slave/in-addr.arpa.slave"; masters { 192.5.5.241; // F.ROOT-SERVERS.NET. }; notify no; }; zone "localhost" { type master; file "master/localhost-forward.db"; }; zone "127.in-addr.arpa" { type master; file "master/localhost-reverse.db"; }; zone "255.in-addr.arpa" { type master; file "master/empty.db"; }; zone "0.in-addr.arpa" { type master; file "master/empty.db"; }; zone "10.in-addr.arpa" { type master; file "master/empty.db"; }; zone "16.172.in-addr.arpa" { type master; file "master/empty.db"; }; zone "17.172.in-addr.arpa" { type master; file "master/empty.db"; }; zone "18.172.in-addr.arpa" { type master; file "master/empty.db"; }; zone "19.172.in-addr.arpa" { type master; file "master/empty.db"; }; zone "20.172.in-addr.arpa" { type master; file "master/empty.db"; }; zone "21.172.in-addr.arpa" { type master; file "master/empty.db"; }; zone "22.172.in-addr.arpa" { type master; file "master/empty.db"; }; zone "23.172.in-addr.arpa" { type master; file "master/empty.db"; }; zone "24.172.in-addr.arpa" { type master; file "master/empty.db"; }; zone "25.172.in-addr.arpa" { type master; file "master/empty.db"; }; zone "26.172.in-addr.arpa" { type master; file "master/empty.db"; }; zone "27.172.in-addr.arpa" { type master; file "master/empty.db"; }; zone "28.172.in-addr.arpa" { type master; file "master/empty.db"; }; zone "29.172.in-addr.arpa" { type master; file "master/empty.db"; }; zone "30.172.in-addr.arpa" { type master; file "master/empty.db"; }; zone "31.172.in-addr.arpa" { type master; file "master/empty.db"; }; zone "168.192.in-addr.arpa" { type master; file "master/empty.db"; }; zone "254.169.in-addr.arpa" { type master; file "master/empty.db"; }; zone "2.0.192.in-addr.arpa" { type master; file "master/empty.db"; }; zone "18.198.in-addr.arpa" { type master; file "master/empty.db"; }; zone "19.198.in-addr.arpa" { type master; file "master/empty.db"; }; zone "240.in-addr.arpa" { type master; file "master/empty.db"; }; zone "241.in-addr.arpa" { type master; file "master/empty.db"; }; zone "242.in-addr.arpa" { type master; file "master/empty.db"; }; zone "243.in-addr.arpa" { type master; file "master/empty.db"; }; zone "244.in-addr.arpa" { type master; file "master/empty.db"; }; zone "245.in-addr.arpa" { type master; file "master/empty.db"; }; zone "246.in-addr.arpa" { type master; file "master/empty.db"; }; zone "247.in-addr.arpa" { type master; file "master/empty.db"; }; zone "248.in-addr.arpa" { type master; file "master/empty.db"; }; zone "249.in-addr.arpa" { type master; file "master/empty.db"; }; zone "250.in-addr.arpa" { type master; file "master/empty.db"; }; zone "251.in-addr.arpa" { type master; file "master/empty.db"; }; zone "252.in-addr.arpa" { type master; file "master/empty.db"; }; zone "253.in-addr.arpa" { type master; file "master/empty.db"; }; zone "254.in-addr.arpa" { type master; file "master/empty.db"; }; // Our zones // Наши зоны (как пример) zone "eugen.su" { type master; file "master/eugen.su"; }; zone "gibs0n.name" { type master; file "master/gibs0n.name"; }; zone "symfony.info" { type master; file "master/symfony.info"; };
Приведу пример одного файла зоны (в нашем случае, они все одинаковые):
$TTL 3600 @ IN SOA ns1.tem.dp.ua. tem.tem.dp.ua. ( 2010011001 ; Serial 3600 ; Refresh 900 ; Retry 1209600 ; Expire 3600 ) ; Minimum @ IN NS ns1.tem.dp.ua. @ IN NS ns2.tem.dp.ua. @ IN NS ns.iti.dp.ua. @ IN MX 10 smtp.tem.dp.ua. @ IN MX 20 smtp.iti.dp.ua. @ IN A 193.34.96.114 @ IN A 193.34.96.115 * IN A 193.34.96.114 * IN A 193.34.96.115
Ничего военного. Все эти данные можно получить из DNS :)
Теперь будем настраивать sendmail. Как бы все его не ругали за сложность настройки и т.п. – я его предпочитаю другим MTA...
Первым делом, идём в /etc/mail, где находятся все конфигурационные файлы от sendmail'а.
Настраиваем access. У меня этот файл выглядит вот так:
# local 127.0.0.1 RELAY # our external IPs 193.34.96.114 RELAY 193.34.96.115 RELAY # trusted external network 193.34.96.116 RELAY 193.34.96.117 RELAY 193.34.96.118 RELAY 193.34.96.119 RELAY 193.34.96.120 RELAY 193.34.96.121 RELAY 193.34.96.122 RELAY 193.34.96.123 RELAY 193.34.96.124 RELAY 193.34.96.125 RELAY 193.34.96.126 RELAY # trusted internal (emergency) network 192.168.0 RELAY
Для чего служит этот файл я уже рассказывал :)
Далее редактируем aliases:
root : мой_логин abuse : мой_логин MAILER-DAEMON : /dev/null postmaster : /dev/null _dhcp : /dev/null _pflogd : /dev/null bin : /dev/null bind : /dev/null daemon : /dev/null games : /dev/null kmem : /dev/null mailnull : /dev/null man : /dev/null news : /dev/null nobody : /dev/null operator : /dev/null pop : /dev/null proxy : /dev/null smmsp : /dev/null sshd : /dev/null system : /dev/null toor : /dev/null tty : /dev/null usenet : /dev/null uucp : /dev/null security : /dev/null ftp : /dev/null ftp-bugs : /dev/null hostmaster : /dev/null webmaster : /dev/null www : /dev/null _sphinx : /dev/null mysql : /dev/null cyrus : /dev/null
В нашей системе этот файл выглядит таким образом… Лишнюю почту нам получать не нужно :) Список пользователей можно брать из /etc/ftpusers, например.
Далее создаём пустой файл default-auth-info и local-host-names. В local-host-names записываем все домены нашего сервера, по одному на строку. Например, так:
tem.dp.ua eugen.su gibs0n.name symfony.info
Далее можно создать файл virtusertable и записать в него строки вида:
admin@example.com root
Если добавить сюда все домены, то это даёт возможность рассылать почту, приходящую на адреса admin@<любой_домен_сервера> реальным пользователям – администраторам этих сайтов, например :)
Далее можно выполнить команду rm freebsd* submit* sendmail* (находясь в /etc/mail), чтобы удалить ненужные файлы. Следующим этапом будет редактирование файла, который называется <имя_домена>.mc (в моём случае это eugen.su.mc). В него мы будем добавлять dnsbl серверы, чтобы блокировать спамеров по адресам. Да и ещё некоторые полезные настройки...
Приведу в качестве примера файл eugen.su.mc с нашей системы:
divert(-1) divert(0) VERSIONID(`$FreeBSD: src/etc/sendmail/freebsd.mc,v 1.30.2.6.2.1 2008/10/02 02:57:24 kensmith Exp $') OSTYPE(freebsd6) DOMAIN(generic) FEATURE(access_db, `hash -o -T/etc/mail/access') FEATURE(blacklist_recipients) FEATURE(local_lmtp) FEATURE(mailertable, `hash -o /etc/mail/mailertable') FEATURE(virtusertable, `hash -o /etc/mail/virtusertable') FEATURE(delay_checks) FEATURE(dnsbl, `abuse.rfc-ignorant.org', `550 Spam - abuse.rfc-ignorant.org.') FEATURE(dnsbl, `aspews.ext.sorbs.net', `550 Spam - aspews.ext.sorbs.net.') FEATURE(dnsbl, `b.barracudacentral.org', `550 Spam - b.barracudacentral.org .') FEATURE(dnsbl, `bl.csma.biz', `550 Spam - bl.csma.biz.') FEATURE(dnsbl, `bl.deadbeef.com', `550 Spam - bl.deadbeef.com .') FEATURE(dnsbl, `bl.spamcannibal.org', `550 Spam - bl.spamcannibal.org.') FEATURE(dnsbl, `bl.spamcop.net', `550 Spam - bl.spamcop.net .') FEATURE(dnsbl, `bl.spamcop.net', `550 Spam - bl.spamcop.net.') FEATURE(dnsbl, `blackholes.brainerd.net', `550 Spam - blackholes.brainerd.net.') FEATURE(dnsbl, `blackholes.five-ten-sg.com', `550 Spam - blackholes.five-ten-sg.com.') FEATURE(dnsbl, `blackholes.mail-abuse.org', `550 Spam - blackholes.mail-abuse.org.') FEATURE(dnsbl, `blackholes.uceb.org', `550 Spam - blackholes.uceb.org.') FEATURE(dnsbl, `blackholes.wirehub.net', `550 Spam - blackholes.wirehub.net .') FEATURE(dnsbl, `blacklist.junkemailfilter.com', `550 Spam - blacklist.junkemailfilter.com.') FEATURE(dnsbl, `blacklist.sci.kun.nl', `550 Spam - blacklist.sci.kun.nl.') FEATURE(dnsbl, `blacklist.woody.ch', `550 Spam - blacklist.woody.ch.') FEATURE(dnsbl, `block.dnsbl.sorbs.net', `550 Spam - block.dnsbl.sorbs.net.') FEATURE(dnsbl, `cbl.abuseat.org', `550 Spam - cbl.abuseat.org.') FEATURE(dnsbl, `cbl.anti-spam.org.cn', `550 Spam - cbl.anti-spam.org.cn.') FEATURE(dnsbl, `cblless.anti-spam.org.cn', `550 Spam - cblless.anti-spam.org.cn .') FEATURE(dnsbl, `cblplus.anti-spam.org.cn', `550 Spam - cblplus.anti-spam.org.cn.') FEATURE(dnsbl, `combined.njabl.org', `550 Spam - combined.njabl.org.') FEATURE(dnsbl, `db.wpbl.info', `550 Spam - db.wpbl.info .') FEATURE(dnsbl, `dialups.mail-abuse.org', `550 Spam - dialups.mail-abuse.org.') FEATURE(dnsbl, `dialups.visi.com', `550 Spam - dialups.visi.com.') FEATURE(dnsbl, `dnsbl-0.uceprotect.net', `550 Spam - dnsbl-0.uceprotect.net .') FEATURE(dnsbl, `dnsbl-1.uceprotect.net', `550 Spam - dnsbl-1.uceprotect.net.') FEATURE(dnsbl, `dnsbl-2.uceprotect.net', `550 Spam - dnsbl-2.uceprotect.net.') FEATURE(dnsbl, `dnsbl-3.uceprotect.net', `550 Spam - dnsbl-3.uceprotect.net .') FEATURE(dnsbl, `dnsbl.ahbl.org', `550 Spam - dnsbl.ahbl.org.') FEATURE(dnsbl, `dnsbl.njabl.org', `550 Spam - dnsbl.njabl.org.') FEATURE(dnsbl, `dnsbl.sorbs.net', `550 Spam - dnsbl.sorbs.net .') FEATURE(dnsbl, `dnsbl.sorbs.net', `550 Spam - dnsbl.sorbs.net.') FEATURE(dnsbl, `duinv.aupads.org', `550 Spam - duinv.aupads.org.') FEATURE(dnsbl, `dul.dnsbl.sorbs.net', `550 Spam - dul.dnsbl.sorbs.net.') FEATURE(dnsbl, `dul.ru', `550 Spam - dul.ru.') FEATURE(dnsbl, `dun.dnsrbl.net', `550 Spam - dun.dnsrbl.net.') FEATURE(dnsbl, `ex.dnsbl.org', `550 Spam - ex.dnsbl.org.') FEATURE(dnsbl, `http.dnsbl.sorbs.net', `550 Spam - http.dnsbl.sorbs.net .') FEATURE(dnsbl, `ips.backscatterer.org', `550 Spam - ips.backscatterer.org.') FEATURE(dnsbl, `ircbl.ahbl.org', `550 Spam - ircbl.ahbl.org.') FEATURE(dnsbl, `ix.dnsbl.manitu.net', `550 Spam - ix.dnsbl.manitu.net .') FEATURE(dnsbl, `korea.services.net', `550 Spam - korea.services.net.') FEATURE(dnsbl, `l2.bbfh.ext.sorbs.net', `550 Spam - l2.bbfh.ext.sorbs.net.') FEATURE(dnsbl, `list.dsbl.org', `550 Spam - list.dsbl.org.') FEATURE(dnsbl, `mail-abuse.blacklist.jippg.org', `550 Spam - mail-abuse.blacklist.jippg.org .') FEATURE(dnsbl, `misc.dnsbl.sorbs.net', `550 Spam - misc.dnsbl.sorbs.net.') FEATURE(dnsbl, `msgid.bl.gweep.ca', `550 Spam - msgid.bl.gweep.ca.') FEATURE(dnsbl, `multi.surbl.org', `550 Spam - multi.surbl.org .') FEATURE(dnsbl, `multi.uribl.com', `550 Spam - multi.uribl.com.') FEATURE(dnsbl, `no-more-funn.moensted.dk', `550 Spam - no-more-funn.moensted.dk.') FEATURE(dnsbl, `overdb.aupads.org', `550 Spam - overdb.aupads.org.') FEATURE(dnsbl, `pbl.spamhaus.org', `550 Spam - pbl.spamhaus.org .') FEATURE(dnsbl, `phishing.rbl.msrbl.net', `550 Spam - phishing.rbl.msrbl.net.') FEATURE(dnsbl, `probes.dnsbl.net.au', `550 Spam - probes.dnsbl.net.au.') FEATURE(dnsbl, `proxy.bl.gweep.ca', `550 Spam - proxy.bl.gweep.ca .') FEATURE(dnsbl, `psbl.surriel.com', `550 Spam - psbl.surriel.com.') FEATURE(dnsbl, `rbl-plus.mail-abuse.org', `550 Spam - rbl-plus.mail-abuse.org.') FEATURE(dnsbl, `rbl.snark.net', `550 Spam - rbl.snark.net .') FEATURE(dnsbl, `rdts.dnsbl.net.au', `550 Spam - rdts.dnsbl.net.au.') FEATURE(dnsbl, `relays.bl.gweep.ca', `550 Spam - relays.bl.gweep.ca.') FEATURE(dnsbl, `relays.bl.kundenserver.de', `550 Spam - relays.bl.kundenserver.de .') FEATURE(dnsbl, `relays.mail-abuse.org', `550 Spam - relays.mail-abuse.org.') FEATURE(dnsbl, `relays.nether.net', `550 Spam - relays.nether.net.') FEATURE(dnsbl, `relays.ordb.org', `550 Spam - relays.ordb.org.') FEATURE(dnsbl, `ricn.dnsbl.net.au', `550 Spam - ricn.dnsbl.net.au .') FEATURE(dnsbl, `rsbl.aupads.org', `550 Spam - rsbl.aupads.org.') FEATURE(dnsbl, `sbl-xbl.spamhaus.org', `550 Spam - sbl-xbl.spamhaus.org.') FEATURE(dnsbl, `sbl.spamhaus.org', `550 Spam - sbl.spamhaus.org.') FEATURE(dnsbl, `smtp.dnsbl.sorbs.net', `550 Spam - smtp.dnsbl.sorbs.net .') FEATURE(dnsbl, `socks.dnsbl.sorbs.net', `550 Spam - socks.dnsbl.sorbs.net.') FEATURE(dnsbl, `socks.opm.blitzed.org', `550 Spam - socks.opm.blitzed.org.') FEATURE(dnsbl, `sorbs.dnsbl.net.au', `550 Spam - sorbs.dnsbl.net.au .') FEATURE(dnsbl, `spam.dnsbl.sorbs.net', `550 Spam - spam.dnsbl.sorbs.net.') FEATURE(dnsbl, `spam.olsentech.net', `550 Spam - spam.olsentech.net .') FEATURE(dnsbl, `spamguard.leadmon.net', `550 Spam - spamguard.leadmon.net.') FEATURE(dnsbl, `spamrbl.imp.ch', `550 Spam - spamrbl.imp.ch.') FEATURE(dnsbl, `spamsites.dnsbl.net.au', `550 Spam - spamsites.dnsbl.net.au.') FEATURE(dnsbl, `spamsources.dnsbl.info', `550 Spam - spamsources.dnsbl.info .') FEATURE(dnsbl, `spamsources.fabel.dk', `550 Spam - spamsources.fabel.dk.') FEATURE(dnsbl, `t1.dnsbl.net.au', `550 Spam - t1.dnsbl.net.au.') FEATURE(dnsbl, `ubl.unsubscore.com', `550 Spam - ubl.unsubscore.com.') FEATURE(dnsbl, `ucepn.dnsbl.net.au', `550 Spam - ucepn.dnsbl.net.au .') FEATURE(dnsbl, `virbl.bit.nl', `550 Spam - virbl.bit.nl.') FEATURE(dnsbl, `virbl.dnsbl.bit.nl', `550 Spam - virbl.dnsbl.bit.nl.') FEATURE(dnsbl, `virus.rbl.jp', `550 Spam - virus.rbl.jp .') FEATURE(dnsbl, `virus.rbl.msrbl.net', `550 Spam - virus.rbl.msrbl.net.') FEATURE(dnsbl, `web.dnsbl.sorbs.net', `550 Spam - web.dnsbl.sorbs.net.') FEATURE(dnsbl, `whois.rfc-ignorant.org', `550 Spam - whois.rfc-ignorant.org .') FEATURE(dnsbl, `xbl.spamhaus.org', `550 Spam - xbl.spamhaus.org.') FEATURE(dnsbl, `zen.spamhaus.org', `550 Spam - zen.spamhaus.org.') FEATURE(dnsbl, `zombie.dnsbl.sorbs.net', `550 Spam - zombie.dnsbl.sorbs.net.') define(`confCW_FILE', `-o /etc/mail/local-host-names') dnl set SASL options TRUST_AUTH_MECH(`GSSAPI DIGEST-MD5 CRAM-MD5 LOGIN') define(`confAUTH_MECHANISMS', `GSSAPI DIGEST-MD5 CRAM-MD5 LOGIN PLAIN') DAEMON_OPTIONS(`Name=IPv4, Family=inet') define(`confBIND_OPTS', `WorkAroundBrokenAAAA') define(`confNO_RCPT_ACTION', `add-to-undisclosed') define(`confPRIVACY_FLAGS', `authwarnings,noexpn,novrfy') MAILER(local) MAILER(smtp) LOCAL_RULESETS HX-Mailer: $>CheckMailer HX-Server: $>CheckMailer SCheckMailer RAdvanced Direct Remailer $* $#error $@ 5.7.1 $: "554 Spam." RAdvanced Mass Sender $* $#error $@ 5.7.1 $: "554 Spam." RSpammer $* $#error $@ 5.7.1 $: "554 Spam." R$* Bomber $* $#error $@ 5.7.1 $: "554 Spam." RMega-Mailer $* $#error $@ 5.7.1 $: "554 Spam." RMMailer $* $#error $@ 5.7.1 $: "554 Spam." RMailer $* $#error $@ 5.7.1 $: "554 Spam." RLigra Mailer $* $#error $@ 5.7.1 $: "554 Spam." RDynamic Opt-In Emailer $* $#error $@ 5.7.1 $: "554 Spam." R$* Group Spamer $#error $@ 5.7.1 $: "554 Spam." RMail Sender $* $#error $@ 5.7.1 $: "554 Spam." RMail Service $* $#error $@ 5.7.1 $: "554 Spam." RMailloop $* $#error $@ 5.7.1 $: "554 Spam." RPersMail $* $#error $@ 5.7.1 $: "554 Spam." RLK SendIt $* $#error $@ 5.7.1 $: "554 Spam." RWC Mail $* $#error $@ 5.7.1 $: "554 Spam." RZUBA ZUB $* $#error $@ 5.7.1 $: "554 Spam." RMailList Express $* $#error $@ 5.7.1 $: "554 Spam." RCaretop $* $#error $@ 5.7.1 $: "554 Spam." RMailer Signature $#error $@ 5.7.1 $: "554 Spam." Rnone $#error $@ 5.7.1 $: "554 Spam." RPG-MAILINGLIST $#error $@ 5.7.1 $: "554 Spam." R$* advcomtest $* $#error $@ 5.7.1 $: "554 Spam." Ryo yo mail $#error $@ 5.7.1 $: "554 Spam." RZanziMailer $* $#error $@ 5.7.1 $: "554 Spam." RMicrosoft Outlook Express 5.0 $#error $@ 5.7.1 $: "554 Spam." RVersion 5.0 $#error $@ 5.7.1 $: "554 Spam." Rnethack $@ OK RZ-Mail-SGI $@ OK RDipost $@ OK R$* $: < $1 > R< > $#error $@ 5.7.1 $: "554 Spam." R$* $@ OK HTo: $>CheckTo HCC: $>CheckTo HMessage-ID: $>CheckMessageID SCheckTo R$*Recipient$* $#error $@ 5.7.1 $: "554 Spam." R$*Undisclosed$* $#error $@ 5.7.1 $: "554 Spam." SCheckMessageID R<$+@$+> $@ < $1 @ $2 > R$* $#error $@ 5.5.2 $: "553 Spam." Kdnsname dns -R PTR Kfrmail regex -aFRRRMAIL ^(.*-.*-.*-.*|.*adsl.*|.*dhcp.*)$
Сохраняем этот файл и делаем команду make (находясь в /etc/mail). После этого прийдётся подредактировать файл <имя_домена>.cf:
Находим первую строку
# DNS based IP address spam list
И добавляем перед ней:
# DNS based (DNS-name - PTR-record) R$* $: $&{client_addr} R$-.$-.$-.$- $: $(dnsname $4.$3.$2.$1.in-addr.arpa. $: OK $) ROK $#error $@ 5.7.1 $: 550 Spam. R$+ $: OKSOFAR # DNS based (DNS-name - *-*-*-*) R$* $: $&{client_addr} R$-.$-.$-.$- $: $(dnsname $4.$3.$2.$1.in-addr.arpa. $: OK $) R$* $: $(frmail $1 $) RFRRRMAIL $#error $@ 5.7.1 $: 550 Spam. R$+ $: OKSOFAR
Следующим шагом ищем и раскомментируем строки (по одной):
O DefaultAuthInfo=/etc/mail/default-auth-info
Сразу после неё идёт строка (закомментированная):
O AuthOptions
Её нужно раскомментировать и привести к такому виду:
O AuthOptions=A
После этого выполняем две команды – make и make install. Sendmail только может выругаться на отсутствие SASL, но это не проблема :) Сейчас мы и его установим :)
Редактируем файл /etc/rc.conf, добавляем в конец строку:
saslauthd_enable=«YES»
Я люблю делать это заранее...
Теперь идём в /usr/ports/security/cyrus-sasl2 и набираем уже привычные нам 3 слова – make install clean. При этом, у нас запросят некоторые настройки. Мой выбор был такой:
Options for cyrus-sasl 2.1.23 --------------------------------------------------------- [ ] BDB Use Berkeley DB [ ] MYSQL Use MySQL [ ] PGSQL Use PostgreSQL [ ] SQLITE Use SQLite [ ] DEV_URANDOM Use /dev/urandom [ ] ALWAYSTRUE Enable the alwaystrue password verifier [ ] KEEP_DB_OPEN Keep handle to Berkeley DB open [X] AUTHDAEMOND Enable use of authdaemon [X] LOGIN Enable LOGIN authentication [X] PLAIN Enable PLAIN authentication [X] CRAM Enable CRAM-MD5 authentication [X] DIGEST Enable DIGEST-MD5 authentication [ ] OTP Enable OTP authentication [ ] NTLM Enable NTLM authentication
Дальше установка должна пойти «как обычно» :)
После установки этого порта, идём в /usr/ports/cyrus-sasl2-saslauthd и делаем то же самое. Здесь только настройки будут вот такими (всё пусто):
Options for cyrus-sasl-saslauthd 2.1.23 --------------------------------------------- [ ] BDB Use Berkeley DB [ ] OPENLDAP Use OpenLDAP [ ] HTTPFORM Enable HTTP form authentication
Дальше, в качестве перерыва, можно поставить свои любимые порты – mc, wget, lynx, архиваторы вроде rar/unrar, zip/unzip – это то, что в хозяйстве всегда пригодится :) Обязательно проверяем права на папку /root – очень часто она читаемая для всех. Права на неё должны быть 0700 или drwx------. Иначе быть не должно :)
Теперь расскажу про два скрипта, которые были упомянуты выше – /root/backup и /root/bashorg
И начну я со скрипта для автоматического резервного копирования всех данных (файлов и баз MySQL) пользователей из группы www.
Сам скрипт написан мной на шелле (первая версия была написана на php, но в данном случае это просто неразумное использование ресурсов – можно ведь и на шелле сделать) :)
Алгоритм очень простой :) Скрипт берёт всех пользователей из группы www (задано в переменной), создаёт у них в домашнем каталоге папку backups (задано в переменной) и создаёт в ней архивы (название архива соответствует названию архивируемой папки). Сохраняются 3 последние архива. Точно так же и с базой данных – скрипт архивирует и помещает в этот каталог дампы баз, названия которых соответствуют имени системного пользователя или попадают под маску <имя_пользователя>_*. Так же, самые последние архивы и дампы сохраняются в папке /base/backups (задано в переменной). Естественно, что права на эту папку должны быть соответствующими… Так же, необходимо задать логин/пароль суперпользователя MySQL (который имеет право читать все базы данных) и путь к базам MySQL. Существует возможность задать базы данных (через пробел), которые ни при каких условиях не будут архивироваться. В нашем случае это полезно для одной очень большой базы (несколько гигабайт), которая никогда не изменяется – просто она была скопирована один раз, и нет необходимости архивировать её каждый раз заново. Поскольку скрипт передаёт пароль в аргументах к mysqldump, то здесь очень уместна опция в sysctl.conf, которая запрещает пользователям просматривать чужие процессы.
Собственно, сам скрипт /root/backup:
#!/bin/sh www_group="www" # Name of group to backup mysql_user="root" # MySQL superuser mysql_pass="password" # MySQL password mysql_path="/var/db/mysql" # Path to MySQL's databases mysql_ignore_dbs="abc" # Databases excluded from backup backup_dirname="backups" # Name of backups directory backup_store="/base/backups" # Path to backup storage umask 0277 www_gid=`pw groupshow ${www_group} | awk -F : '{print $3}'` userlst=`cat /etc/passwd | grep -v '^#' | awk -F : '{print $1,$4}' | grep " ${www_gid}\$" | awk '{print $1}'` mysql_dumped='0' for user in ${userlst} do home=`pw usershow ${user} | awk -F : '{print $9}'` if [ -d ${home} ] then backups="${home}/${backup_dirname}" if [ ! -d ${backups} ] then mkdir ${backups} fi chown "${user}:${www_gid}" ${backups} chmod 0500 ${backups} for dir in `ls -Af ${home}` do if [ ${dir} != ${backup_dirname} ] then if [ -f "${backups}/backup.${dir}.2.tgz" ] then mv "${backups}/backup.${dir}.2.tgz" "${backups}/backup.${dir}.3.tgz" fi if [ -f "${backups}/backup.${dir}.1.tgz" ] then mv "${backups}/backup.${dir}.1.tgz" "${backups}/backup.${dir}.2.tgz" fi if [ -d "${home}/${dir}" ] then tar -cz -C ${home} -f "${backups}/backup.${dir}.1.tgz" ${dir} cp "${backups}/backup.${dir}.1.tgz" "${backup_store}/${user}_backup.${dir}.tgz" chown "${user}:${www_gid}" "${backups}/backup.${dir}.1.tgz" fi fi done for djustifyb in `ls -Af ${mysql_path}` do if [ -d "${mysql_path}/${db}" ] then cf=`echo ${user} | wc | awk '{print $3}'` cmp=`echo $db | cut -c "-${cf}"` disabled='0' for ddb in ${mysql_ignore_dbs} do if [ ${db} = ${ddb} ] then disabled='1' break fi done if [ ${cmp} = ${user} -o ${cmp} = "${user}_" -o ${db} = 'mysql' ] then if [ ${disabled} != '1' ] then if [ ${db} = 'mysql' ] then destination="${backup_store}/dump.mysql.gz" if [ ${mysql_dumped} = '1' ] then continue fi mysql_dumped='1' else destination="${backups}/dump.${db}.1.gz" if [ -f "${backups}/dump.${db}.2.gz" ] then mv "${backups}/dump.${db}.2.gz" "${backups}/dump.${db}.3.gz" fi if [ -f "${backups}/dump.${db}.1.gz" ] then mv "${backups}/dump.${db}.1.gz" "${backups}/dump.${db}.2.gz" fi fi mysqlcheck -o -r "--user=${mysql_user}" "--password=${mysql_pass}" ${db} > /dev/null 2>&1 mysqldump "--user=${mysql_user}" "--password=${mysql_pass}" --add-drop-table --add-drop-database --add-locks --extended-insert --quick --skip-comments --quote-names --complete-insert ${db} | gzip -9 > ${destination} fi fi fi done fi done
/root/bashorg – прикольный, но абсолютно бесполезный скрипт :) Обновляет приветствия в системе с bash.org.ru. Первая версия тоже была написана на php, однако буквально несколько дней назад переписал на шелл. Думаю, что обновление цитаты на приветствии в системе один раз в час не будет сильно нагружать сеть… Единственное что, для работы скрипта нужен iconv, который мы дальше будем ставить как модуль для php (но у которого есть и CLI) :) Хотя, вполне вероятно, что iconv у вас уже будет стоять, если вы устанавливали mc...
Скрипт:
#!/bin/sh fetch -q -o - http://bash.org.ru/random | grep -e '
.*
' -m 1 | awk '{gsub(" ","\n",$0);print}' | sed -e 's/<.*>//g' -e 's/"/"/g' -e 's/ / /g' -e 's/<//g' -e 's/©/(c)/g' -e 's/&/&/g' | sed -e 's/^[ \t]*//;s/[ \t]*$//' | awk '{gsub("^[ \t]*","",$0);print}' > /etc/ftpwelcome iconv -c -f CP1251 -t KOI8-R /etc/ftpwelcome > /etc/motd Естественно, что права обоих скриптов должны позволять запускать их. Т.е., например, 0700 (владелец root, и запуск из crontab'а от root'а).
Теперь можно поставить утилиты типа mbmon, smartd, а так же, SSH сервер...
Начнём с mbmon, т.к. это проще всего :)
Идём в /usr/ports/sysutils/mbmon и пишем свои любимые три слова – make install clean :) Конфигурация вот такая:
Options for mbmon 205_5 ------------------------------ [ ] SMB enable smb(4) support
После установки проверяем – выполняем команду mbmon –A –c1 -u, и, если она не ругается, а пишет температуры – значит, всё работает :) Можно добавить в inetd (подразумевается, что все дополнительные TCP порты были добавлены в /etc/services):
mbmon stream tcp nowait/20/0/10 root /usr/local/bin/mbmon mbmon –A –c1 -u
Сразу оговорюсь. Мой inetd.conf выглядит таким образом:
#mbmon stream tcp nowait/20/0/10 root /usr/local/bin/mbmon mbmon -A -c1 -u smartd stream tcp nowait/20/0/10 root /usr/local/bin/smart smart pop3pw stream tcp nowait/50/0/20 root /usr/local/libexec/poppwd poppwd pop3 stream tcp nowait/100/0/50 root /usr/local/libexec/qpopper qpopper -c -C -F -s -S -T 600
Здесь мы видим закомментированный mbmon (он не работает с моей материнкой), smartd, poppwd (сервер для смены паролей) и pop3 сервер. Естественно, inetd должен быть включен в rc.conf.
Установка всех этих сервисов описана далее, однако про inetd я больше не буду делать оговорки :)
Теперь мы будем ставить smartd. Идём в /usr/ports/sysutils/smartmontools и опять запускаем нашу любимую команду из трёх слов :) (в смысле, «make install clean», а не «иди на х..» :) ) Конфигурировать здесь нечего, поэтому просто дожидаемся окончания установки.
После всего этого нужно добавить в rc.conf (а можно и не добавлять, но правильнее будет добавить):
mbmon_enable=«NO»
smartd_enable=«NO»
Нам ведь не нужно, чтобы они запускались самостоятельно...
Теперь нужно создать скрипт для запуска smartctl из inetd. Создаём файл /usr/local/bin/smart, делаем ему права, которые позволят выполнять его, и пишем в него:
#!/bin/sh read disk echo $disk | awk '{print $1}' | xargs /usr/local/sbin/smartctl -iA Дальше с ним можно будет общаться, например, таким php скриптом:
Полный скрипт я приводить не буду, кому нужно, тот разберётся сам.
Последнее, что нам осталось в этой части – это установить SSH и почтовый сервер (вместе с сервером для смены паролей).
Начнём с конца, т.к. это проще :)
cd /usr/ports/mail/poppwd && make install clean – без дополнительных опций, всё просто и красиво :) Это будет сервер для смены паролей (должен работать на 106-м порту).
Теперь pop3:
cd /usr/ports/mail/qpopper && make install clean – а вот POP3 сервер уже запросит некоторые настройки:
Options for qpopper 4.0.9_2 -------------------------------------------------------------- [ ] APOP_ONLY build with APOP authentication only [ ] APOP build with APOP [ ] DOCUMENTATION install pdf documentation [ ] DRAC build with Dynamic Relay Authorization [ ] FULL_POPD_DEBUG build with more verbose debugging [ ] PAM build with PAM authentication [ ] POPPASSD build the poppassd daemon [X] QPOPAUTH_SETUID install qpopauth setuid to pop user [X] SAMPLE_POPUSERS build a default reject file [X] SHY_ENABLED hide qpopper version in POP3 banner [ ] SSL build with SSL/TLS support [ ] STANDALONE_MODE build qpopper to be run without inetd [ ] U_OPTION include support for user .qpopper-options
После всего этого, у нас появится файл /usr/local/etc/qpopper/popusers – список пользователей, которым запрещён вход через POP3. Файл копируется из /etc/ftpusers, поэтому нам прийдётся немного поработать над ним – убрать @mailuser и @www из этого файла, чтобы разрешать доступ к почте пользователям из этих групп. Пример приводить не буду, т.к. уже приводил пример /etc/ftpusers :)
Теперь SSH сервер. Нам нужен хороший, быстрый и безопасный SSH сервер, у которого нет проблем при работе с разными клиентами (например, у стандартного sshd есть проблемы при работе с ShellGuard'ом, и как их решить, я пока не разобрался, хотя ShellGuard – мой любимый SSH клиент).
Первым делом, в rc.conf добавляем строку:
sshd2_enable=«YES»
Теперь идём в /usr/ports/security/ssh2-nox11 и пишем любимые три слова. Опций запрашивать, вроде, не должно. Конфигурационный файл находится по адресу /usr/local/etc/ssh2/sshd2_config
В общем, рассказывать про отдельные опции я не буду, т.к. они абсолютно аналогичны опциям стандартного sshd. Единственное что, всё-таки, приведу пример рабочего конфига :)
## SSH CONFIGURATION FILE FORMAT VERSION 1.1 ## REGEX-SYNTAX egrep ## end of metaconfig ## (leave above lines intact!) ## sshd2_config ## SSH 3.2 Server Configuration File ## ## General # HostKeyFile hostkey # PublicHostKeyFile hostkey.pub # RandomSeedFile random_seed # BannerMessageFile /usr/local/etc/ssh2/ssh_banner_message # BannerMessageFile /etc/issue.net # # VerboseMode no # QuietMode no # SyslogFacility AUTH # SyslogFacility LOCAL7 # SftpSyslogFacility LOCAL7 ## Network # Port is commented out as it is specified by the startup script. Port 22 ListenAddress any ResolveClientHostName no # RequireReverseMapping no # MaxBroadcastsPerSecond 0 # MaxBroadcastsPerSecond 1 # NoDelay no # KeepAlive yes MaxConnections 50 # MaxConnections 0 # 0 == number of connections not limited ## Crypto # Ciphers AnyCipher # Ciphers AnyStdCipher # Ciphers 3des # Following includes "none" 'cipher': # Ciphers AnyStd # # MACs AnyMAC # MACs AnyStdMAC # Following includes "none" 'mac': # MACs AnyStd # # RekeyIntervalSeconds 3600 ## User PrintMotd yes CheckMail yes # StrictModes yes # Specifies 1 hour (you can also use 'w' for week, 'd' for day, 'm' for # minute, 's' for seconds) IdleTimeOut 6h # without specifier, the default number is in seconds # IdleTimeOut 3600 # # UserConfigDirectory "%D/.ssh2" # UserConfigDirectory "/usr/local/etc/ssh2/auth/%U" # AuthorizationFile authorization # This variable is set here, because by default it's empty, and so no # variables can be set. Because of that, we set a few common ones here. SettableEnvironmentVars LANG,LC_(ALL|COLLATE|CTYPE|MONETARY|NUMERIC|TIME),PATH,TERM,TZ ## Tunneling AllowX11Forwarding no # AllowTcpForwarding yes # AllowTcpForwardingForUsers sjl, cowboyneal@slashdot\.org # DenyTcpForwardingForUsers 2[[:digit:]]*4,peelo # AllowTcpForwardingForGroups privileged_tcp_forwarders # DenyTcpForwardingForGroups coming_from_outside # # Local port forwardings to host 10.1.0.25 ports 143 and 25 are # allowed for all users in group users. # Note that forwardings using the name of this host will be allowed (if # it can be resolved from the DNS). # # ForwardACL allow local .*%users \i10\.1\.0\.25%(143|25) # # Local port forwardings requested exactly to host proxy.company.com # port 8080 are allowed for users that have 's' as first character # and belong to the group with group id 10: # # ForwardACL allow local s.*%10 proxy\.company\.com%8080 # # Remote port forwarding is denied for all users to all hosts: # ForwardACL deny remote .* .* ## Authentication ## publickey and password allowed by default # AllowedAuthentications publickey,password # AllowedAuthentications hostbased,publickey,password # AllowedAuthentications hostbased,publickey,keyboard-interactive # RequiredAuthentications publickey,password LoginGraceTime 60 # AuthInteractiveFailureTimeout 2 # # HostbasedAuthForceClientHostnameDNSMatch no # UserKnownHosts yes # # AuthPublicKey.MaxSize 0 # AuthPublicKey.MinSize 0 # AllowAgentForwarding yes # # AuthKbdInt.NumOptional 0 # AuthKbdInt.Optional pam,password # AuthKbdInt.Required password # AuthKbdInt.Retries 3 # PermitEmptyPasswords no PasswordGuesses 3 ## Host restrictions # AllowHosts localhost, foobar.com, friendly.org # ## Next one matches with, for example, taulu.foobar.com, tuoli.com, but ## not tuoli1.com. Note that you have to input string "\." when you want it ## to match only a literal dot. You also have to escape "," when you ## want to use it in the pattern, because otherwise it is considered a list ## separator. ## ## AllowHosts t..l.\..* ## ## The following matches any numerical IP-address (yes, it is cumbersome) ## ## AllowHosts ([[:digit:]]{1\,3}\.){3}[[:digit:]]{1\,3} ## ## Same thing is achieved with using the special prefix "\i" in a ## pattern. This means that the pattern is only used to match ## IP-addresses. ## ## Using the above example: ## ## AllowHosts \i.* ## ## You can probably see the difference between the two. ## ## Also, you can use subnet masks, by using prefix "\m" ## ## AllowHosts \m127.0/8 ## and ## AllowHosts \m127.0.0.0/24 ## ## would match localhost ("127.0.0.1"). ## # DenyHosts evil\.org, aol\.com # AllowSHosts trusted\.host\.org # DenySHosts not\.quite\.trusted\.org # IgnoreRhosts no # IgnoreRootRHosts no # (the above, if not set, is defaulted to the value of IgnoreRHosts) ## User restrictions # AllowUsers sj.*,s[[:digit:]]*,s(jl|amza) # DenyUsers skuuppa,warezdude,31373 # DenyUsers don@untrusted\.org # AllowGroups staff,users DenyGroups mailuser PermitRootLogin yes # PermitRootLogin nopwd ## Chrooted environment # ChRootUsers anonymous,ftp,guest # ChRootGroups sftp,guest ## SSH1 compatibility # Ssh1Compatibility no # Sshd1Path# # This is given as argument to sshd1 with "-f" if sshd2 is invoked # with "-f", otherwise the default configuration for sshd1 is used. # Sshd1ConfigFile /etc/sshd_config_alternate ## subsystem definitions # Subsystems don't have defaults, so this is needed here (uncommented). # subsystem-sftp sftp-server # Also internal sftp-server subsystem can be used. # subsystem-sftp internal://sftp-server ## Subconfiguration # There are no default subconfiguration files. When specified the last # obtained keyword value will prevail. Note that the host specific files # are read before the user specific files. # Following matches (from) any host # # HostSpecificConfig .* /usr/local/etc/ssh2/subconfig/host_ext.example # # Following matches to subnet mask: # # HostSpecificConfig \m192.168.0.0/16 /usr/local/etc/ssh2/subconfig/host_int.example # # Following matches to users from ssh.com that have two character long # username or is sjl and belong to group wheel or wheel[0-9] # # UserSpecificConfig (..|sjl)%wheel[[:digit:]]?@ssh\.com /usr/local/etc/ssh2/subconfig/user.example # # Following matches to the user anonymous from any host # # UserSpecificConfig anonymous@.* /usr/local/etc/ssh2/subconfig/anonymous.example
После настройки нужно запустить команду:
/usr/local/etc/rc.d/sshd2 keygen
Чтобы сервер сгенерировал публичный и приватный ключи. По идее, он это должен сделать сам при первом запуске, но я, как правило, делаю это сам, до его первого запуска.
Вот мы и закончили с первой частью – настройкой системы и её основных компонентов. Теперь приступим к установке MySQL, php (со всеми потрохами) и веб-сервера lighttpd.
Итак, начинаем с MySQL. Сначала редактируем /etc/rc.conf и добавляем в него строку:
mysql_enable=«YES»
Теперь идём в /usr/ports/databases и делаем make install clean. Все опции у нас уже заданы в /etc/make.conf (если не заданы – можно устанавливать MySQL и так, как это было описано мной ранее).
После установки запускаем /usr/local/etc/rc.d/mysql-server start – при первом запуске MySQL создаст все нужные папки и файлы (кроме конфига).
Идём в /var/db/mysql и создаём файл my.cnf (пример):
[client] # Настройки для клиента port = 3306 # Порт по умолчанию socket = /tmp/mysql.sock # Путь к сокету [mysqld] # Настройки для сервера port = 3306 # Порт socket = /tmp/mysql.sock # Путь к сокету back_log = 200 # С этой опцией MySQL вообще не будет использовать TCP порт. # Все соединения будут осуществляться через файл-сокет, что гораздо быстрее skip-networking # Максимальное количество одновременных подключений max_connections = 100 # Максимальное количество ошибок, после которого MySQL будет считать хост «умершим». # Указано максимально допустимое значение, т.к. ошибки авторизации тоже учитываются. # Однако, при успешном подключении, счётчик ошибок будет сбрасываться max_connect_errors = 4294967295 # Этот ключ определяет память, выделяемую для хранения открытых таблиц table_open_cache = 1024 # Максимальный размер пакета max_allowed_packet = 128M # Размер кэша, который будет хранить SQL-операторы для бинарного журнала регистраций # во время транзакции binlog_cache_size = 16M # Максимальный размер таблицы типа HEAP max_heap_table_size = 128M # Размер КЭШа, который будет хранить SQL-операторы для бинарного журнала регистраций. sort_buffer_size = 32 # Размер буфера, используемого при операциях полного соединения таблиц (без индексов) join_buffer_size = 8M # Количество потоков, которое сервер должен поместить в кэш для повторного использования thread_cache_size = 8 # Количество одновременно запускаемых потоков thread_concurrency = 4 # Память, выделяемая для кэширования результатов запросов query_cache_size = 64M # Не помещать в кэш результаты, размер которых больше этого числа query_cache_limit = 2M # Минимальная длина слова для полнотекстового поиска ft_min_word_len = 3 # Тип таблицы по умолчанию default-storage-engine = InnoDB # Размер стека для каждого потока thread_stack = 192K # Изолированность транзакций transaction_isolation = SERIALIZABLE # Максимальный размер временных таблиц tmp_table_size = 128M # Файл бинарного журнала log-bin=mysql-bin # Формат файла бинарного журнала binlog_format=mixed # Время (в секундах), по истечению которого запрос будет считаться «долгим» long_query_time = 5 # Путь ко временной папке tmpdir = /tmp # Номер сервера, если используется сеть MySQL серверов. # Если сервер один – оставляем здесь 1. server-id = 1 # Размер буфера, используемого для хранения блоков индексов key_buffer_size = 256M # Размер буфера для операций чтения read_buffer_size = 16M # Размер буфера для упорядоченного чтения read_rnd_buffer_size = 16M # Размер кэша, используемого при оптимизации групповой вставки bulk_insert_buffer_size = 128M # Буфер, который выделяется для сортировки индексов myisam_sort_buffer_size = 128M # Максимальный размер временного файла myisam_max_sort_file_size = 10G # Количество потоков при восстановлении таблиц myisam_repair_threads = 1 # Память, отведенная для хранения внутренних структур данных InnoDB innodb_additional_mem_pool_size = 16M # Размер кэша InnoDB для буферизации табличных данных и индексов innodb_buffer_pool_size = 256M # Файлы для хранения InnoDB таблиц. # В моём случае – это 4 файла по 4 гигабайта. innodb_data_file_path = ibdata1:4G;ibdata2:4G;ibdata3:4G;ibdata4:4G # Путь к рабочей папке для InnoDB innodb_data_home_dir = /var/db/mysql/ # Количество потоков ввода-вывода для InnoDB innodb_file_io_threads = 4 # Максимальное количество потоков для InnoDB innodb_thread_concurrency = 4 # Записывать ли журнал при завершении транзакции? innodb_flush_log_at_trx_commit = 1 # Размер буфера для записи информации файлов журналов InnoDB на диск. innodb_log_buffer_size = 8M # Размер каждого файла журнала в группе журналов innodb_log_file_size = 256M # Количество файлов журналов в группе журналов innodb_log_files_in_group = 3 # Время простоя (в секундах), на протяжении которого транзакция InnoDB может ожидать # блокировки прежде, чем будет произведен откат innodb_lock_wait_timeout = 120 [mysqldump] # Опции для утилиты mysqldump – быстрый метод и максимальный размер пакета в дампе. quick max_allowed_packet = 128M [mysql] # Автоматическое повторное хеширование не используется no-auto-rehash [myisamchk] # Опции для mysqlcheck – размеры буферов (описаны выше для mysqld) key_buffer_size = 256M sort_buffer_size = 128M read_buffer = 8M write_buffer = 8M [mysqlhotcopy] interactive-timeout [mysqld_safe] # Ограничение на количество одновременно открытых файлов open-files-limit = 8192
С первого взгляда, всё сложно. Но если сесть и разобраться один раз – ничего сложного не будет :) Если что-то не понятно – советую почитать документацию MySQL.
Правильно сконфигурированный MySQL – это весьма важно, если сервер будет обрабатывать большое количество запросов. Настройки, скорее всего, прийдётся изменить, т.к. этот конфиг я взял с одного из своих серверов, где MySQL настроен под мои задачи. В общем, стоит почитать и другие статьи про настройку MySQL, где, возможно, настройка будет описана более подробно...
После конфигурации нужно остановить MySQL сервер командой /usr/local/etc/rc.d/mysql-server stop, удалить из /var/db/mysql все файлы, кроме конфига, удалить папку test (это тестовая база данных) и заново запустить MySQL. Запуск будет долгим, т.к. при этом создаются все файлы для InnoDB (а они могут быть большими).
Удалить лишних пользователей и сменить пароль на root'а можно сейчас, из консоли. А можно и позже, через phpmyadmin. Лично я выбрал phpmyadmin :)
C MySQL разобрались, теперь будем устанавливать php :)
Идём в /usr/ports/lang/php5 (для отсталых от жизни можно и php4 :) ) и набираем любимые три слова.
Опции я выбрал такие:
Options for php5 5.2.12 --------------------------------------------------------------- [X] CLI Build CLI version [X] CGI Build CGI version [ ] APACHE Build Apache module [ ] DEBUG Enable debug [ ] SUHOSIN Enable Suhosin protection system (not for jails) [ ] MULTIBYTE Enable zend multibyte support [ ] IPV6 Enable ipv6 support [X] MAILHEAD Enable mail header patch [ ] REDIRECT Enable force-cgi-redirect support (CGI only) [ ] DISCARD Enable discard-path support (CGI only) [X] FASTCGI Enable fastcgi support (CGI only) [X] PATHINFO Enable path-info-check support (CGI only)
Далее устанавливаем ZendOptimizer. С ним будет один прикол у тех, кто использует amd64 систему.
Обязательно создаём /usr/local/etc/php.ini, можно даже пустой. Идём в /usr/ports/devel/ZendOptimizer и набираем любимые три слова.
Обязательно запоминаем (записываем) строки, которые он выдаст после установки. Можно делать make install clean > A – при этом весь вывод будет перенаправлен в файл A.
Теперь идём в /usr/local/etc и редактируем php.ini.
В моей системе он выглядит так:
[PHP] engine=On zend.ze1_compatibility_mode=Off short_open_tag=On asp_tags=Off precision=14 y2k_compliance=On output_buffering=4096 zlib.output_compression=Off implicit_flush=Off ignore_user_abort=On unserialize_callback_func= serialize_precision=100 allow_call_time_pass_reference=Off safe_mode=Off disable_functions= disable_classes= highlight.string=#DD0000 highlight.comment=#FF9900 highlight.keyword=#007700 highlight.bg=#FFFFFF highlight.default=#0000BB highlight.html=#555555 expose_php=Off max_execution_time=300 max_input_time=300 memory_limit=32M error_reporting=E_ALL & ~E_NOTICE display_errors=On display_startup_errors=Off log_errors=On log_errors_max_len=1024 ignore_repeated_errors=Off ignore_repeated_source=Off report_memleaks=On track_errors=On error_log=syslog variables_order="EGPCS" register_globals=On register_long_arrays=On register_argc_argv=On auto_globals_jit=On post_max_size=16M magic_quotes_gpc=Off magic_quotes_runtime=Off magic_quotes_sybase=Off auto_prepend_file= auto_append_file= default_mimetype="text/html" doc_root= user_dir= enable_dl=On cgi.fix_pathinfo=1 file_uploads=On upload_max_filesize=12M allow_url_fopen=On allow_url_include=Off from="www@tem.dp.ua" user_agent="Eugen.SU" default_socket_timeout=20 [Pcre] pcre.backtrack_limit=1000000000 pcre.recursion_limit=100000000 [Syslog] define_syslog_variables=Off [SQL] sql.safe_mode=Off [MySQL] mysql.allow_persistent=On mysql.max_persistent=-1 mysql.max_links=-1 mysql.default_socket= mysql.default_host= mysql.default_user= mysql.default_password= mysql.connect_timeout=10 mysql.trace_mode=Off [MySQLi] mysqli.max_links=-1 mysqli.default_port=3306 mysqli.default_socket=/tmp/mysql.sock mysqli.default_host= mysqli.default_user= mysqli.default_pw= mysqli.reconnect=Off [Session] session.save_handler=files session.save_path="/tmp" session.use_cookies=1 session.name=sessid session.auto_start=0 session.cookie_lifetime=0 session.cookie_path=/ session.cookie_domain= session.cookie_httponly= session.serialize_handler=php session.gc_probability=1 session.gc_divisor=10 session.gc_maxlifetime=86400 session.bug_compat_42=0 session.bug_compat_warn=1 session.referer_check= session.entropy_length=0 session.entropy_file= session.cache_limiter=nocache session.cache_expire=180 session.use_trans_sid=0 session.hash_function=1 session.hash_bits_per_character=6 url_rewriter.tags="a=href,area=href,frame=src,input=src,form=fakeentry" [Zend] zend_optimizer.optimization_level=2 zend_extension_manager.optimizer="/usr/local/lib/php/20060613/Optimizer" zend_extension_manager.optimizer_ts="/usr/local/lib/php/20060613/Optimizer_TS" zend_extension="/usr/local/lib/php/20060613/ZendExtensionManager.so" zend_extension_ts="/usr/local/lib/php/20060613/ZendExtensionManager_TS.so"
Прошу обратить внимание, что убраны все комментарии. Это ускоряет парсинг конфига при запуске CGI процессов.
Про параметры php в этой статье я рассказывать не буду, но оговорю две вещи:
1. Последние строки в файле взяты из данных, полученных при установке ZendOptimizer :) Для этого мы и сохраняли все данные
2. zend_optimizer.optimization_level равен двум, поскольку иначе в системе amd64 php будет «вылетать в кору».
Теперь устанавливаем модули для php. По инструкции – идём в /usr/ports/lang/php5-extensions (справедливо для php4 :) ) и набираем три слова. Выбираем все модули, кроме явно не нужных.
После установки редактируем /usr/local/etc/php/extensions.ini и удаляем лишние модули. С комментариями – та же история, что и с php.ini :) И ещё оговорюсь – никаких лишних модулей оставлять не нужно. Загружайте только то, что действительно используется. Это экономит память и время, что очень важно для нагруженного сервера.
Напоследок, установим lighttpd в качестве веб-сервера. Сервер хороший, лёгкий, а некоторые его возможности меня просто очень порадовали. К сожалению (а может и к счастью) не поддерживает динамическую конфигурацию (.htaccess), хотя это даже к лучшему – на парсинг этих файлов уходит лишнее время.
Для начала, пойдём в /tmp и создадим там папку wwwcache. Это очень пригодится для модуля mod_compress. Теперь делаем:
chflags sunlnk /tmp/wwwcache – это нужно, чтоб система не удалила эту папку при очистке /tmp
chown www:www /tmp/wwwcache – чтобы сам сервер мог записывать в эту папку.
chmod 0700 /tmp/wwwcache – без комментариев.
Теперь создаём 2 файла с указанными правами:
/var/log/httpd.access.log www:www 644
/var/log/httpd.error.log www:www 644
В /etc/rc.conf добавляем следующие строки:
lighttpd_enable="YES" # Запускать lighttpd? spawn_fcgi_enable="YES" # FastCGI сервер spawn_fcgi_app="/usr/local/bin/php-cgi" # Путь к php spawn_fcgi_app_args="" # php аргументы spawn_fcgi_pidfile="/var/run/spawn-fcgi.pid" # PID файл spawn_fcgi_username="www" # php user spawn_fcgi_groupname="www" # php group spawn_fcgi_chroot_dir="" # php chroot spawn_fcgi_bindaddr="" # TCP использовать не будем spawn_fcgi_bindport="" # ... spawn_fcgi_bindsocket="/tmp/php.sock" # UNIX socket лучше spawn_fcgi_bindsocket_mode="0777" # Socket permissions spawn_fcgi_children="200" # Максимум одновременно обрабатываемых запросов spawn_fcgi_max_requests="1000" # Макс. количество запросов до перезапуска процесса spawn_fcgi_path_env="/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin:/libexec:/usr/libexec:/usr/local/libexec" # PATH
Теперь идём в /usr/ports/www/lighttpd и устанавливаем его.
Мой конфиг:
Options for lighttpd 1.4.26 ------------------------------------------------- [X] BZIP2 Enable Bzip2 support [ ] CML Enable Cache Meta Language support [ ] FAM Enable fam/gamin support [ ] GDBM Enable gdbm storage support [ ] IPV6 Enable IPV6 support [ ] MAGNET Enable magnet support [X] MEMCACHE Enable memory caching support [ ] MYSQL Enable MYSQL support [ ] OPENLDAP Enable LDAP support [ ] OPENSSL Enable SSL support [X] SPAWNFCGI Enable spawn-fcgi utility [ ] VALGRIND Enable valgrind support [ ] WEBDAV Enable WebDAV support
Установили? Теперь перейдём к настройке. Но перед настройкой идём в /usr/local/www и создаём там файл ddos-ban.c – это моя программка для блокировки DDoS-ботов по User-Agent. Программу обязательно сохраняем в кодировке windows-1251 (для перекодировки из KOI8 можно использовать iconv). Как это работает я расскажу чуть позже, когда буду приводить пример конфига.
Текст программы:
#include <stdio.h> #include <stdlib.h> int main() { if (!getenv("REMOTE_ADDR")) { printf("This is CGI-only program.\n"); exit(1); } printf("Content-Type: text/html; charset=windows-1251\r\n\r\n\ <!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n\ <html xmlns=\"http://www.w3.org/1999/xhtml\">\n\ <head>\n\ <meta http-equiv=\"content-type\" content=\"text/html; charset=windows-1251\"/>\n\ <link rel=\"stylesheet\" href=\"/errors_style.css\"/>\n\ <link rel=\"shortcut icon\" type=\"image/x-icon\" href=\"/favicon.ico\"/>\n\ <title>Eugen Server - ошибка 403 - вы забанены</title>\n\ <style type=\"text/css\">\n\ body {\n\ background-color: #0D0D0D;\n\ font-family: Tahoma;\n\ font-size: 14px;\n\ color: #FFFF9B;\n\ }\n\ a:hover {\n\ color: #FFCC00;\n\ }\n\ a {\n\ color: #FFFF9B;\n\ }\n\ input, textarea, select {\n\ color: #FFFF9B;\n\ background: #343434;\n\ border: none;\n\ }\n\ </style>\n\ </head>\n\ <body>\n\ <center>\n\ <br />\n\ <br />\n\ <h1>Ошибка 403</h1>\n\ <h2>Вы забанены Anti-DDoS'ом</h2>\n\ <br />\n\ <div align=\"justify\" style=\"width:500px;overflow:auto\">\n\ \n\ Ваш IP %s заблокирован Anti-DDoS'ом, т.к. в строке User-Agent, посланной вашим браузером найдены соответствия с базой Anti-DDoS.\n\ По вопросам обращайтесь в ICQ 671915. Доступ будет открыт через 1 час после блокировки.\n\ Дальнейшие попытки подключений на 80-й порт будут безуспешными.\n\ </div>\n\ <br />\n\ <strong>© Eugen & gibson</strong>\n\ </center>\n\ </body>\n\ </html>\n", getenv("REMOTE_ADDR")); execl("/usr/sbin/daemon","daemon","-f","/sbin/pfctl","-t","ddos","-T","add",getenv("REMOTE_ADDR"),0); }
Компилируем и делаем программе SUID – она должна запускаться от root'а:
cc ddos-ban.c
strip a.out
mv a.out ddos-ban
chmod a-rw ddos-ban
chmod a+s ddos-ban
Для желающих – rm ddos-ban.c – исходник можно не держать здесь :)
Теперь приступаем к конфигурированию веб-сервера. Редактируем файл /usr/local/etc/lighttpd.conf:
## Загружаемые модули # Для работы необходимы mod_access и mod_accesslog. Остальное по желанию server.modules = ( "mod_rewrite", "mod_redirect", "mod_alias", "mod_access", # "mod_trigger_b4_dl", # "mod_auth", # "mod_status", # "mod_setenv", "mod_fastcgi", # "mod_proxy", # "mod_simple_vhost", # "mod_evhost", # "mod_userdir", "mod_cgi", "mod_evasive", "mod_compress", # "mod_ssi", # "mod_usertrack", # "mod_expire", # "mod_secdownload", # "mod_rrdtool", "mod_accesslog" ) ## document-root по умолчанию server.document-root = "/usr/local/www/data/" ## Лог ошибок server.errorlog = "/var/log/httpd.error.log" # Индексовые файлы index-file.names = ( "index.php", "index.html", "index.htm" ) ## Обработчик событий server.event-handler = "freebsd-kqueue" # Имя сервера (по умолчанию) server.name = "Eugen.SU" # Некоторые системные настройки server.max-keep-alive-requests = 15 server.max-keep-alive-idle = 5 server.max-read-idle = 60 server.max-write-idle = 300 server.max-fds = 2048 # Max connections # Ограничение количества одновременных подключений с одного IP evasive.max-conns-per-ip = 10 # Типы файлов mimetype.assign = ( ".pdf" => "application/pdf", ".sig" => "application/pgp-signature", ".spl" => "application/futuresplash", ".class" => "application/octet-stream", ".ps" => "application/postscript", ".torrent" => "application/x-bittorrent", ".dvi" => "application/x-dvi", ".gz" => "application/x-gzip", ".pac" => "application/x-ns-proxy-autoconfig", ".rar" => "application/rar", ".swf" => "application/x-shockwave-flash", ".tar.gz" => "application/x-tgz", ".tgz" => "application/x-tgz", ".tar" => "application/x-tar", ".zip" => "application/zip", ".mp3" => "audio/mpeg", ".m3u" => "audio/x-mpegurl", ".wma" => "audio/x-ms-wma", ".wax" => "audio/x-ms-wax", ".ogg" => "application/ogg", ".wav" => "audio/x-wav", ".gif" => "image/gif", ".jar" => "application/x-java-archive", ".jpg" => "image/jpeg", ".jpeg" => "image/jpeg", ".png" => "image/png", ".xbm" => "image/x-xbitmap", ".xpm" => "image/x-xpixmap", ".xwd" => "image/x-xwindowdump", ".css" => "text/css", ".html" => "text/html", ".htm" => "text/html", ".js" => "text/javascript", ".asc" => "text/plain", ".c" => "text/plain", ".cpp" => "text/plain", ".log" => "text/plain", ".conf" => "text/plain", ".text" => "text/plain", ".txt" => "text/plain", ".dtd" => "text/xml", ".xml" => "text/xml", ".mpeg" => "video/mpeg", ".mpg" => "video/mpeg", ".mov" => "video/quicktime", ".qt" => "video/quicktime", ".avi" => "video/x-msvideo", ".asf" => "video/x-ms-asf", ".asx" => "video/x-ms-asf", ".wmv" => "video/x-ms-wmv", ".bz2" => "application/x-bzip", ".tbz" => "application/x-bzip-compressed-tar", ".tar.bz2" => "application/x-bzip-compressed-tar", # default mime type "" => "application/octet-stream", ) # Заголовок Server: server.tag = "Eugen.SU" #### Лог доступа accesslog.filename = "/var/log/httpd.access.log" # Расширения НЕ статических файлов static-file.exclude-extensions = ( ".php", ".phtml", ".phps" ) ## Порт server.port = 80 ## Адрес server.bind = "0.0.0.0" ## PID-файл server.pid-file = "/var/run/lighttpd.pid" # chroot() #server.chroot = "/" ## Пользователь (все хосты должны иметь этого владельца или UID'ы должны совпадать) server.username = "www" ## Группа server.groupname = "www" ## Настройки mod_compress для сжатия статических файлов compress.allowed-encodings = ( "bzip2", "gzip", "deflate" ) compress.filetype = ( "text/plain", "text/html" ) compress.cache-dir = "/tmp/wwwcache/" #### fastcgi ## for PHP don't forget to set cgi.fix_pathinfo = 1 in the php.ini ## слушаем совет и не забываем это исправить... ## отдельные два сервера для php и phps fastcgi.map-extensions = ( ".phtml" => ".php" ) fastcgi.server = ( ".phps" => ( "phps" => ( "socket" => "/tmp/phps.sock", "bin-path" => "/usr/local/bin/php-cgi -s", "max-procs" => 10, "bin-environment" => ( "PHP_FCGI_CHILDREN" => "10", "PHP_FCGI_MAX_REQUESTS" => "1000" ) ) ), ".php" => ( "php" => ( "socket" => "/tmp/php.sock" ) ) ) #### ssi #ssi.extension = ( ".shtml" ) # Простой Anti-DDoS # Блокирует IP адрес файрволлом при совпадении User-Agent'а # Вот для этого и была нужна моя программа на C... $HTTP["useragent"] =~ "(vaginamook|2003100|ODI3|Hotbar|4.75|DigExt|20030718|Q312461|H010818|inktomi|googlebawt|FAST-WebCrawler|Microsoft-WebDAV-MiniRedir|FunWebProducts|looksmart|avastye)" { cgi.assign = ( "" => "/usr/local/www/ddos-ban" ) } # Настройки виртуального хостинга # Можно использовать поставляемый модуль, но мы люди продвинутые и пользуемся RegExp :) # Default $HTTP["url"] =~ "^/cgi-bin/" { cgi.assign = ( "" => "" ) } $HTTP["host"] != "admin.eugen.su" { # Задайте это! Путь к phpMyAdmin. url.redirect = ( "^/myadmin/(.*)$" => "http://admin.eugen.su/myadmin/$1", "^/myadmin$" => "http://admin.eugen.su/myadmin/" ) } dir-listing.activate = "disable" # Отключаем листинг папок без индексового файла server.errorfile-prefix = "/usr/local/www/errors/" # Путь к папке с файлами ошибок # 401.html 403.html ... # Убираем www. из Host. # Можно наоборот – добавлять. Или вообще не трогать. Но тогда это нужно предусмотреть. $HTTP["host"] =~ "^www\.(.*)$" { url.redirect = ( "^/(.*)" => "http://%1/$1" ) } # Приведу несколько записей для виртуальных хостов, как пример. # eugen.su $HTTP["host"] == "tem.dp.ua" { server.name = "tem.dp.ua" # Перенаправление url.redirect = ( "^/ub/(.*)" => "http://eugen.su/ru/userbar/$1", "^/ub$" => "http://eugen.su/ru/userbar/", "^/forum/(.*)" => "http://forum.eugen.su/$1", "^/forum$" => "http://forum.eugen.su/", "^/releases/(.*)" => "http://releases.eugen.su/$1", "^/releases$" => "http://releases.eugen.su/", "^/$" => "http://live.eugen.su/", "^/index.php$" => "http://live.eugen.su/" ) } $HTTP["host"] =~ "^(tools\.tem\.dp\.ua|tools\.gibs0n\.name|webtools\.xakepok\.org)$" { url.redirect = ( "^/(.*)" => "http://eugen.su/$1" ) } $HTTP["host"] =~ "eugen\.su$" { # Можно использовать регулярные выражения (Perl) alias.url += ( "/cgi-bin/" => "/home/eugen/cgi-bin/" ) server.errorfile-prefix = "/home/eugen/errors/" } $HTTP["host"] == "eugen.su" { server.name = "eugen.su" server.document-root = "/home/eugen/eugen.su" # document-root $HTTP["url"] =~ "^(/base/|/fonts/|/temp/|/userbar/data/|/userbar/fonts/|/scripts/geoipcity\.dat)" { url.access-deny = ( "" ) # Запрет доступа } # Реврайт ссылок url.rewrite-once = ( "^/([a-zA-Z]{2})/tool/([a-zA-Z0-9]+)" => "/index.php?lang=$1&tool=$2", "^/tool/([a-zA-Z0-9]+)" => "/index.php?tool=$1", "^/([a-zA-Z]{2})[/]?$" => "/index.php?lang=$1", "^/actions\.js$" => "/actions.php", "^/style\.css$" => "/style.php", "^/download/([^/]+)/([0-9a-fA-F]{32})$" => "/scripts/download.php?uiq=$2&type=$1", "^/userbar/im(.*)_(.*)_(.*)_(.*)_(.*)_(.*)\.png$" => "/userbar/img.php?x=$1&sz=$2&bg=$3&fn=$4&cl=$5&text=$6", "^/([a-zA-Z]{2})/userbar[/]?$" => "/userbar/index.php?lang=$1" ) } # Final. По умолчанию все /cgi-bin/ метим в папку "по умолчанию" alias.url += ( "/cgi-bin/" => "/usr/local/www/cgi-bin/" )
Естественно, здесь не все наши virtual-host'ы. :)
Теперь можно перезагрузить сервер. Если всё сделано правильно и вы не запутались с правами/владельцами/UID'ами – то всё должно заработать.
Лично у меня получилось настроить такую конфигурацию с первого раза, когда я делал домашний сервер :) Но с первого раза получается не у всех… Так что, огорчаться не стоит, даже если ничего не получится. Внимательно проверьте права на файлы, логи ошибок и попытайтесь исправить ситуацию. В самом крайнем случае – мой ICQ 671915 :)
С вами был Eugen. Удачи вам в ваших разработках :)
- ВКонтакте
- РћРТвЂВВВВВВВВнокласснРСвЂВВВВВВВВРєРСвЂВВВВВВВВ
Нет комментариев
Добавить комментарий: