RFC (Request for Comments, Запрос на комментарии) - серия документов, публикуемая сообществом исследователей и разработчиков, руководствующихся практическими интересами, в которой описывается набор протоколов и обобщается опыт функционирования Интернет.
Сейчас мы попробуем продумать и разработать защитный комплекс, который позволит системному администратору спать чуточку спокойней. Многие сисадмины теорией пренебрегают чаще всего потому, что просто о ней не знают – документаций и статей по этому вопросу практически никто не писал. Ведь объяснить на словах концепцию чего-либо без конкретных примеров – очень и очень сложно. Поэтому, после теоретического ликбеза мы разберем концепцию на примере хостинга, описанного в статье «Могучий хостинг» из этого номера. А начнем мы, пожалуй, с основ – с теории информационной безопасности.
Tеория::About
Немного утрируя, можно сказать что хакер отличается от скрипткидиса не только тем, что умеет мыслить нестандартно, но и тем, что хорошо знаком с теорией предмета. То же можно сказать и про хорошего крэкера – нужно не только хорошо уметь пользоваться дизассемблером, а еще и знать, например, как устроен тот или иной тип бинарика, что такое коллгейт (нет, не паста :)) и из чего он состоит. Поэтому самый первый кирпичик в основе грамотного системного администрирования – изучение матчасти. Невозможно достойно противостоять напору взломщиков, не понимая, как работает твоя система, и что происходит при вызове того или иного процесса. При разработке защиты так же важно понимать, что хороший хакер, имеющий конкретную цель, взломает любой сервер. C’est la vie. Если кто-то очень сильно захочет посмотреть пару файлов на твоем сервере – он это сделает. Поверь, на Пентагон и ФБР работают не самые плохие специалисты, и, тем не менее, их ломали. И неоднократно. Идеальную защиту можно сравнить с вечным двигателем – теоретически она есть, но пока ее еще никто не создал.
Теория::основные принципы
По сути, защита любой системы, предоставляющей сервисы, состоит из двух основных направлений – защита от внешней атаки и защита от атаки изнутри. Атака изнутри – это когда атакующий является легальным авторизированным пользователем, который пытается так или иначе поднять свои привилегии в системе. Например, банковский работник, пытающийся просмотреть файлы, к которым ему не давали доступа, или клиент хостинга, у которого есть шелл, пытающийся дефейснуть другой сайт на сервере – это типичные примеры подобной атаки. Атаки второго типа отбивать намного сложнее, ведь у пользователя есть не только данные о системе, но и валидный юзер. Также необходимо учитывать, что, с целью поломать твою систему, хакер может купить аккаунт или украсть пароль у юзера.
Теория::организация
В вопросах организации и структуры все зависит от масштаба проекта, в данном случае - хостинга. Если речь идет о построении крупного хостинга,- разумным решением будет использовать, например, распределенную систему, связанную внутри локальной гигабитной сетью. Один из примеров реализации такого решения может выглядеть как на рисунке 1.
В данном случае машины A, B, и C являются веб-серверами. На них бегают только веб и фтп-демоны. D - почтовый сервер, обслуживающий наших клиентов. Сервер E представляет из себя кластер БД, а F - бэкапный сервер. Как видно на схеме, все сервера хостинга соединены между собой в локалку, причем таким образом, что сервер E и F не имеют доступа в Инет. Эта схема имеет довольно серьезные преимущества в плане безопасности – все запросы внутри локальной сети происходят в одностороннем порядке, а значит ситуацию уже намного легче контролировать. Разумеется, и у такой схемы есть недостатки – например, время запроса к MySQL возрастает, так как запрос выполняется через TCP, а не через (локальный) unix socket.
Таких вариантов построения хостинга - сотни, и каждый из них имеет свои преимущества и недостатки. Систему, описанную выше, использует, например, весьма солидный хостинг ipowerweb.com, о взломе которого я уже рассказывал полтора года назад. Тогда мне помогла лишь полная беспечность админа, оставившего суидный скрипт после инсталляции какого-то софта. Скажу честно: если бы не эта неосторожность с его стороны – я врядли бы достиг цели. Поэтому помни: на систему надейся, а сам не плошай!
System
Теперь попробуем реализовать на практике вышеописанные теоретические доводы. Первым делом мы займемся системой. Рассмотрим простой пример – пользователь купил у нас хостинг и запустил мега-движок на PHP, который написал ему приятель-школьник за 100 рублей. Кроме основного действия скрипта функция N зацикливается, попутно вычисляя некое сложное действие. Как результат – высокая загрузка процессора. Это очень типичная ситуация для хостинга. Чтобы предотвратить подобные ненамеренные (и намеренные) атаки, необходимо ограничивать юзера в плане ресурсов. У *BSD для таких целей существует система профилей пользователей. Это значит, что мы можем легко ограничить ресурсы каждого пользователя в отдельности. Открываем /etc/login.conf:
Листинг файла /etc/login.conf
# Имя профиля hosting:\ :copyright=/etc/COPYRIGHT:\ :welcome=/etc/motd:\ :setenv=MAIL=/var/mail/$,BLOCKSIZE=K:\ :path=~/bin /bin /usr/bin /usr/local/bin:\ :manpath=/usr/share/man /usr/local/man:\ :nologin=/var/run/nologin:\ # Максимальное время использования процессора :cputime=1h30m:\ # Максимальное кол-во памяти, выделяемой программе под данные # Сам код программы и стэк не учитываются :datasize=10M:\ # Сколько выделяем для стека программы :stacksize=3M:\ # Максимальный размер физической памяти, выделяемой процессу :memoryuse=16M:\ # Максимальный размер файла :filesize=50M:\ # Максимальный размер core файлов :coredumpsize=1M:\ # Сколько файлов может открывать каждый процесс :openfiles=128:\ # Сколько процессов может запускать пользователь :maxproc=64:\ # Пускать юзера в систему только если его домашняя дира существует и доступна # юзеру :requirehome:true\ # Время устаревания пароля :passwordtime=90d:\ # Остальное берем из профиля default :tc=default:
Здесь я указал лишь основные параметры. Список всех параметров и их описание можно найти в Handbook (см. врезку)
Hard
Теперь к вопросу о файловой системе. Самый важный файл настроек в данном случае - /etc/fstab. Он применяется при загрузке, и указывает системе, как работать с тем или иным разделом файловой системы, что с ним может делать пользователь, а что - нет. Открываем /etc/fstab и приводим его примерно в такой вид:
Очень важно поставить параметр noexec на /tmp. Данная опция запрещает запускать что-либо на файловой системе, даже если на файле установлен chmod 777. Я лично видел очень много взломов отлично защищенных серверов именно из-за этой ошибки, которую, почему-то, очень многие допускают. Нельзя забывать, что в /tmp может писать почти любой процесс в системе. Опция nosuid говорит системе игнорировать suid-биты файлов, а nodev запрещает создание/существование в разделе специальных устройств.
System::sysctl
Теперь перейдем к тюнингу самой операционной системы. Открываем /etc/sysctl.conf и пишем туда следующее:
Листинг файла /etc/sysctl.conf
# Запрещает юзерам видеть процессы соседа, и, разумеется рутовые. security.bsd.see_other_uids=0 # Пускаем запросы на закрытые порты в черные дыры net.inet.tcp.blackhole=2 net.inet.udp.blackhole=1 # Указываем размер очереди сокета kern.ipc.somaxconn=1024 # Отрубаем ip-редиректы net.inet.icmp.drop_redirect=1 net.inet.icmp.log_redirect=1 net.inet.ip.redirect=0 # Назначаем размеры буфера для TCP-подключений. Если на сервер ожидается большая # нагрузка, и у него много памяти – лучше поставить 65535. Значение выше 65535 # не рекомендуется. net.inet.tcp.sendspace=32768 net.inet.tcp.recvspace=32768 # Обновляем ARP-таблицу каждые 20 минут net.link.ether.inet.max_age=1200 # Запрещаем отвечать на все лишние запросы. net.inet.icmp.maskrepl=0 net.inet.ip.sourceroute=0 net.inet.ip.accept_sourceroute=0 net.inet.icmp.bmcastecho=0
Конечно же здесь указаны не все параметры sysctl. Для полного описания всех возможностей не хватило бы и журнала, поэтому я указал самые основные и необходимые. Многие параметры для sysctl можно изменять и динамически:
sysctl <параметр>=<значение>
Например: sysctl kern.maxprocperuid=1000
Теперь необходимо продублировать часть настроек в /etc/rc.conf:
Очень важным аспектом системного администрирования является слежение за поведением сервера. Для этого существует отличная утилита logcheck. Устанавливаем:
# cd /usr/ports/security/logcheck # make install clean
Утилита написана на sh скриптах, и занимает всего 29 Кб в архиве. После установки в /usr/local/etc у тебя появятся четыре конфига: переименуй их, убрав из названия файла «sample»:
logcheck.hacking – о каких странностях сообщать; logcheck.violations – о каких попытках взлома сообщать; logcheck.ignore – какие странности игнорировать; logcheck.violations.ignore – какие попытки взлома игнорировать.
В целом и общем, первый файл от второго ничем не отличается, равно как и третий от четвертого :). Просто разработчики скрипта решили разнести сообщения о подозрительной активности и сообщения о явной атаке в разные конфиги. Затем необходимо перенести файл logcheck.sh:
Теперь можно запускать скрипт по крону, хотя бы раз в сутки:
0 4 * * * /usr/local/sbin/logcheck.sh
Так же следует учесть тот факт, что, при большой активности хостящихся сайтов, логи веб-сервера начнут занимать немало места. И в то же время их надо сохранять. Тут есть два варианта. Можно использовать утилиту logrotate (/usr/ports/sysutils/logrotate), но я использую небольшой самописный скрипт. Его основной конфиг я поясню, а скрипт целиком можно найти на диске (rLog.sh).
# Где лежат логи вхостов? logs_dir=/var/log/www # Куда складывать архивы с логами? arc_logs=/var/wwwlogs # Кого устанавливать владельцем архивов? user=root group=wheel # Темп-файл rLog_l=/tmp/rLog.log
Скрипт заархивирует все логи виртуальных хостов, создаст архив в указанном тобой месте, обнулит логи и пошлет отчет руту. Разумеется, скрипт нужно прописать в crontab:
0 6 * * * /usr/local/sbin/rLog.sh
Apache
К сожалению, на сегодняшний день не существует хотя бы близкой к безупречности модели защиты Apache. Любая конфигурация этого веб-сервера так или иначе оставляет возможности для взлома, даже при использовании suexec или cgi врапперов. Единственный более-менее интересный вариант – это модуль PerUser для Apache 2. Он позволяет запускать Апач с привилегиями пользователя, который указан в конфиге виртуального хоста. Это значит, что каждый вхост (читай домен) на сервере запускается из-под отдельного системного юзера. Даже если поломают один сайт то, чтобы добраться до других, хакеру нужно будет поднимать привилегии до рута. А на хорошо настроенной BSD-системе сделать это намного сложнее, нежели залить веб-шелл через багу в скрипте. Однако у модуля PerUser есть три серьезных недостатка. Во-первых, он существует только для Apache второй версии, которая еще не совсем стабильна, во-вторых – проект уже довольно долгое время находится в стадии альфа-тестинга, и в-третьих – модуль (по второй причине) невозможно установить из портов, а значит веб-сервер придется собирать из исходников, а это не только дополнительная возможность наделать ошибок, но и немалый геморрой с апдейтами. Думаю, что если разработчики все же разродятся стабильным релизом этого модуля – это решит множество проблем для админов хостинговых серверов.
Scripting
Рассмотрим самое уязвимое место хостинговой системы – выполняемые файлы, в частности, PHP скрипты. Открываем конфиг PHP:
Выключить эти функции очень важно. Хоть они и недоступны при включенном safe mode, пользователь может без труда провести успешную атаку, указав в файле .htaccess:
php_flag safe_mode off
Виртуальные хосты
Теперь нам необходимо задать ограничения в конфиге каждого вхоста. Добавляем следующие параметры:
<IfModule mod_php4.c> # Включаем Safe mode php_admin_flag safe_mode on php_admin_flag safe_mode_gid on # Папка, выше которой скрипт не может видеть php_admin_value open_basedir /home/domain.ru php_admin_value safe_mode_exec_dir /home/domain.ru # Temp дира юзера php_admin_value upload_tmp_dir /home/domain.ru/tmp # Не начинать PHP сессию автоматически php_admin_flag session.auto_start off # Где сохранять файлы сессий php_admin_value session.save_path /home/domain.ru/tmp </IfModule>
Как известно, немалая часть взломов (SQL Injection, XSS атаки, инклюдинг) происходит по сути посредством хитрого HTTP запроса. Логично предположить, что эти самые запросы неплохо было бы фильтровать. Решение проблемы существует в виде модуля к Апачу, и называется оно mod_security. Ставим:
# cd /usr/ports/www/mod_security/ # make install clean
После установки – идем конфигурировать. Открываем любой конфиг виртуального хоста, например 001.admin.hosting.ru, над которым мы уже экспериментировали. Все значения надо вводить между тегами
<Virtualhost *:80> и </Virtualhost>. # Включаем mod_security SecFilterEngine On # Проверяем запросы SecFilterScanPOST On # Проверяем ответы SecFilterScanOutput On # Проверяем, правильно ли закодирован URL SecFilterCheckURLEncoding On # Включаем этот параметр, если сайт в Unicode SecFilterCheckUnicodeEncoding Off # Задаем диапазон байтов SecFilterForceByteRange 1 255 # Сохраняем в лог только срабатывания механизма SecAuditEngine RelevantOnly # Где живет лог :) SecAuditLog logs/audit_log # Возвращаем ошибку 500 при срабатывании SecFilterDefaultAction "deny,log,status:500" # Перекрываем dots-bug SecFilter "\.\./" # Не забываем про XSS SecFilter "<(.|\n)+>" SecFilter "<[[:space:]]*script" # SQL injection, куда же без него :) SecFilter "delete[[:space:]]+from" SecFilter "insert[[:space:]]+into" SecFilter "select.+from" # Перекрываем возможность передачи переменных PHP SecFilterSelective ARG_b2inc "!^$" # Исключаем возможность раскрытия пути SecFilterSelective OUTPUT "Fatal error:"
У этого модуля – на редкость удачная дефолтная конфигурация. К ней мало что можно добавить, так как большинство настроек – специфичны. Общий принцип составления правил мы рассмотрели, а остальное можно добавить по своему усмотрению.
Firewall
Ну и, разумеется, ключевой момент защиты – файрвол. Открываем конфиг файрвола (/etc/rc.firewall) и приводим нужную секцию примерно в следующий вид:
ip="123.31.123.123" # IP сервера myip="90.90.90.90" # Твой статический ип
Листинг файла /etc/rc.firewall
# С этих диапазонов ничего хорошего за всю историю РуНета не приходило. ${fwcmd} add deny log logamount 1000 ip from 217.17.248.0/24 to any ${fwcmd} add deny log logamount 1000 ip from 62.149.64.0/18 to any ${fwcmd} add deny log logamount 1000 ip from 212.93.192.0/19 to any ${fwcmd} add deny log logamount 1000 ip from 212.106.64.0/19 to any ${fwcmd} add deny log logamount 1000 ip from 62.215.0.0/16 to any ${fwcmd} add deny log logamount 1000 ip from 200.0.0.0/8 to any # Сервисы # Пропускаем фтп ${fwcmd} add pass tcp from any to ${ip} 21 setup # Открываем ssh только для себя ${fwcmd} add pass tcp from ${myip} to ${ip} 22 setup # Веб ${fwcmd} add pass tcp from any to ${ip} 80 setup # Https ${fwcmd} add pass tcp from any to ${ip} 443 setup # Разрешаем трафик для всех уже установленных соединений ${fwcmd} add pass tcp from any to any established # Пропускаем IP-фрагменты ${fwcmd} add pass all from any to any frag # Разрешаем исходящие TCP ${fwcmd} add pass tcp from ${ip} to any setup # А теперь все идут лесом ${fwcmd} add deny tcp from any to any setup # Разрешаем DNS-трафик ${fwcmd} add pass udp from ${ip} to any 53 keep-state # Разрешаем NTP-запросы ${fwcmd} add pass udp from ${ip} to any 123 keep-state
В зависимости от того, как ты настроил passive-mode в конфиге pure-ftpd, не забудь открыть нужный диапазон портов.
Chroot
Когда ко мне прибежал очередной знакомый админ и с выпученными глазами сообщил, что теперь у него стоит mod_chroot для Апача – я задал ему вполне легитимный вопрос: «Зачем?». Ответ был стандартный – «Потому что секурно». Мы с ним рассмотрели минусы решения: потеря производительности, лишний немаленький модуль, дополнительный конфиг. Смотрим плюсы: а что дает модуль? А ничего он не дает. То же можно сказать и про обычный chroot – он ничего не дает в плане безопасности. Дело в том, что грамотно выставленные chmod’ы дают тот же эффект, но без потерь в ресурсах и дополнительных заморочек. Для системы, которую мы рассматриваем на примере, chroot не пригодится. Однако существует множество случаев, когда он необходим. В основном, к помощи chroot’а имеет смысл прибегать, когда требуется обезопасить отдельный сервис. Например, ISC BIND, который, мягко говоря, небезопасен – создавать ему «песочницу» необходимо, даже если DNS – это единственный сервис, запущенный на системе.
Jail
В связи с тем, что вопрос безопасности сегодня стоит очень остро, постоянно появляются все новые и новые решения. К сожалению, большинство из них далеки от идеала. Выбирая любое решение, необходимо сначала рассматривать его недостатки, и только потом – преимущества, так как обычно игра не стоит свеч. Например, Jail в BSD. С одной стороны – все замечательно и прекрасно, если походить по форумам – везде восторженные отклики «профессионалов» о том, как у них все теперь секурно. На примере того же хостинга давай посмотрим, что будет, если мы поставим туда jail’ы. Во-первых, в джейл нам придется загонять каждый вхост, а значит – мы серьезно теряем в производительности. Во-вторых, jail еще очень плохо документирован, а значит, уже нужно быть готовым к сюрпризам (не всегда приятным). В-третьих, jail еще мало кто использует, и я бы не был на 100% уверен в его стабильности, и, уж тем более, безопасности. В-четвертых, если хакер достаточно квалифицирован, чтобы поднять привилегии внутри джейла, то ему не составит большого труда вылезти из него в основную систему. А теперь рассмотрим плюсы: джейл позволяет нам создавать ОС внутри ОС. Круто. Но как показывают его минусы – в данном случае - бессмысленно. Существуют варианты, когда такие системы необходимы – это при раздаче шеллов или VDS пользователям. Например, проект firstvds.ru работает на основе модифицированного FreeBSD jail.
Резюме
Суть примеров с Chroot и Jail заключается в том, чтобы донести до нас одну из ключевых аксиом – ОС, софт и настройки следует выбирать исходя из задачи. Не стоит ставить ту или иную фишку, если она не помогает решить задач, которые выполняет сервер. Этим грешат очень многие системщики, и часто именно эти излишки и открывают хакеру лазейку в систему. В целом и общем можно сказать, что чем больше админ знает о функциях, которые будут выполнять процессы сервера – тем лучше он сможет защитить сервер.
Разумеется, все вышеописанное не претендует на идеально защищенную систему. Как уже было сказано, идеальная защита – миф. Кому-то данная концепция подойдет, кому-то - нет. В любом случае, надеюсь, что данная статья поможет тебе сориентироваться в основах обеспечения безопасности собственного сервера.