Безопасность и надежность программной системы обратно пропорциональны ее сложности. Но к современным Linux-дистрибутивам эту формулу применить довольно проблематично, так как, по общепринятому мнению, они безопасны сами по себе. К сожалению, это не так, но в этой статье я расскажу и покажу, как сделать Linux действительно простой и очень устойчивой к взломам системой.

 

INFO

Хорошей практикой будет установка утилиты rkhunter, которая сверяет контрольные суммы системы, чтобы выявить факт модификации файлов.

Чтобы запутать взломщика, можно удалить команды uname и dmesg, переименовать ядро и внести соответствующие изменения в /boot/grub/menu.lst, то есть «обезличить» дистрибутив.

Защиту операционной системы принято строить послойно. Первый, внешний слой отвечает за взаимодействие ОС с внешним миром, то есть этот слой составляют сетевые сервисы, которые слушают определенные порты и отвечают на запросы клиентов. К таким сервисам относятся любые демоны, способные принимать сетевые сообщения, например web- и ftp-сервера, сервер DNS, почтовый сервер и т. д. Защиту этого слоя обеспечивают программисты, которые следят за безопасностью кода, и администратор, своевременно накладывающий заплатки и правильно конфигурирующий сетевые сервисы. Второй слой, или рубеж обороны, — это сама система, которая не должна позволить взломщику добраться до конфиденциальных данных или изменить важные системные файлы в случае нарушения границ первого слоя.

В идеале второму слою защиты нужно уделять не меньше внимания, чем первому, однако многие сисадмины пренебрегают этим, предпочитая полагаться на разработчиков дистрибутива, которые якобы уже позаботились о надежной защите своих продуктов. Это, конечно же, не так. Современные дистрибутивы — это универсальные операционные системы, рассчитанные на применение в самых разных областях и на решение различных задач. В них входит множество компонентов, которые создают потенциальную опасность и при этом вряд ли когда-нибудь тебе понадобятся. Многие дистрибутивы по умолчанию включают в себя средства сборки приложений, различные сетевые и диагностические инструменты, каждый из которых может использовать взломщик.

Самый верный способ обезопасить сервер в этой ситуации — это самому создать дистрибутив, ориентированный на решение конкретной задачи и лишенный всех тех компонентов, которые взломщик может использовать для получения сведений о системе, повышения своих прав или установки бэкдора.

 

Часть 1. Постановка задачи

Итак, мы решили сделать свой профильный дистрибутив. Прежде всего мы должны определиться с функциями, которые будет выполнять ОС. Здесь можно придумать массу вариантов, но я предлагаю остановиться на самом распространенном: дистрибутив для web-сервера. Наш будущий дистрибутив будет отвечать за хостинг web-сайтов, причем не статических, а написанных с использованием фреймворка Django. Почему не PHP+Djoomla/Drupal? Да потому, что я их не люблю!

Таким образом, в дистрибутив должны входить как минимум пять сервисов:

  1. Сам web-сервер (к черту Apache, nginx — наше все).
  2. Дистрибутив Python, который будет отвечать за работу Django.
  3. Django, на котором будут написаны сайты.
  4. PostgreSQL, которая будет хранить данные.
  5. SSH для удаленного управления.

Все остальные компоненты типичного сервера, такие как FTP-сервер, sendmail, и прочую шелуху можно выкинуть. Чем меньше в системе лишних сервисов, тем меньше шансов у взломщика.

Загрузка ArchLinux
Загрузка ArchLinux
 

Часть 2. Подготовка каркаса

Существует множество способов собрать собственный дистрибутив в домашних условиях, но все они требуют времени и терпения. Мы поступим проще: возьмем обычный Linux-дистрибутив и вырежем из него все лишнее. На роль подопытного хорошо подойдет ArchLinux, один из самых простых и легких дистрибутивов (ты, конечно, можешь выбрать что-нибудь другое вроде Slackware или Gentoo, но сути это не поменяет).

Получить базовую версию ArchLinux можно, например, с сервера Yandex (прямой линк на x86_64-сборку: http://goo.gl/EZRtQ). После загрузки ISO-образ следует скормить виртуальной машине и установить дистрибутив на виртуальный жесткий диск. Проблем во время установки возникнуть не должно: запускаем инсталлятор командой /arch/setup, выбираем «Select Source», жмем , выбираем «Prepare hard drive(s)», в ответ на все вопросы жмем , в последнем окне выбираем ФС ext2. Далее «Select Packages», в ответ на все вопросы. Затем выбираем «Configure system», жмем , в следующем окне выбираем Done. Выбираем «Install bootloader», выходим из редактора, а когда получаем предложение изменить /boot/grub.conf, жмем и, наконец, «Exit install». Перезагружаем машину (не забываем отключить ISO-образ).

Главное окно инсталлятора
Главное окно инсталлятора

После установки системы и загрузки виртуальной машины заходим в систему под учетной записью root (пустой пароль) и начинаем разбираться с содержимым системы. Прежде всего необходимо обновить все ее пакеты. Но сначала следует настроить сеть. Нормальные виртуалки имеют встроенный DHCP-сервер, поэтому обычно достаточно только запустить DHCP-клиент:

# dhcpcd eth0

Теперь, чтобы указать менеджеру Pacman на нужный репозиторий, добавляем в файл /etc/pacman.d/mirrorlist две строки:

Server = ftp://mirror.yandex.ru/archlinux/$repo/os/$arch
Server = http://mirror.yandex.ru/archlinux/$repo/
os/$arch

И запускаем процесс обновления:

# pacman -Syu

Возможно, при первом запуске обновится только сам Pacman. В этом случае следует выполнить команду второй раз. Далее устанавливаем необходимые для функционирования сервера компоненты. В моем случае это nginx, Python, Django и PostgreSQL:

# pacman -S nginx python2 django

Запоминаем, какие зависимости были установлены в процессе (чтобы ненароком не удалить их на следующем этапе ковыряния). У меня получилось всего четыре зависимости: libffi, postgresql-libs, libxml2 и sqlite3, но ее как раз можно удалить, поскольку есть Постгрес. Делаем снимок состояния диска виртуальной машины (это очень важно). При необходимости выключаем ВМ, делаем снимок и снова включаем.

Теперь мы должны удалить все лишние пакеты. Для начала выясним, что вообще сейчас установлено:

# pacman -Qs

Список не такой уж и длинный, но больше половины из всего вышеперечисленного нам точно не нужно. Во-первых, мы должны избавиться от инструментов сборки. По умолчанию в Арче нет GCC, но зато есть пакет binutils, который содержит линковщик, архиватор статических библиотек и другие инструменты:

pacman -R binutils

Далее удалим ненужные пакеты, например документацию и man-страницы:

# pacman -R licenses groff man-db man-pages texinfo

Избавимся также от инструментов управления различными файловыми системами и настройки RAID-массивов. Те из них, которые могут быть нужны на сервере, оставим на месте:

# pacman -R cryptsetup device-mapper lvm2 mdadm \
   xfsprogs jfsutils reiserfsprogs

Далее можно избавиться от неиспользуемых инструментов управления сетью и связанных с ними библиотек и зависимостей:

# pacman -R iputils keyutils krb5 heirloom-mailx ppp \
   wget dbus-core wpa_supplicant libpcap libnl libldap

Различные утилиты для управления USB-оборудованием, PCI- и PCMCIA-устройствами на сервере нам тоже не нужны:

# pacman -R usbutils pcmciautils sysfsutils

Также можно избавиться от некоторых оставшихся библиотек:

# pacman -R libpipeline libsasl libgcrypt libgpg-error

На этом процесс подготовки закончен, и мы можем переходить к основной задаче.

В окне, предлагающем варианты разбивки диска, следует выбрать весь диск
В окне, предлагающем варианты разбивки диска, следует выбрать весь диск
 

Часть 3. Режем все, что ушло из-под ножа

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

А лишнего много. Например, ArchLinux не разделяет пакеты на -dev и бинарные, поэтому каталог /usr/include содержит в себе множество совершенно бесполезных и даже опасных заголовочных файлов. Избавимся от них с помощью следующей простой команды:

# rm -rf /usr/include

По той же причине каталог /usr/lib содержит большое количество статических библиотек, которые не нужны для работы системы, но могут быть использованы для сборки софта. Удаляем их:

# rm /usr/lib/*.a

В каталогах /bin, /sbin, /usr/bin, /usr/sbin имеется множество утилит и команд, которые были установлены как часть других пакетов, но сами по себе могут нанести вред серверу. В качестве примера можно привести различные утилиты для манипулирования файловой системой и таблицей разделов, утилиты для управления расширенными атрибутами файлов, создания пакетов и т. д. На работающем настроенном сервере пользы от них не так много.

Для начала избавимся от низкуровневых утилит управления файловой системой:

# rm /sbin/{badblocks,debugfs,dumpe2fs,e2image,e2label,
e2undo,resize2fs,tune2fs}

Теперь удалим инструменты управления таблицей разделов. Вряд ли взломщик будет их использовать, но чем черт не шутит:

# rm /sbin/{fdisk,cfdisk,sfdisk}

Также можно избавиться от утилит для создания файловых систем и swap-разделов:

# rm /sbin/mkfs.*
# rm /sbin/mkswap

Заодно удалим dd и install:

# rm /bin/{dd,install}

Теперь самое важное. Хакеры очень любят использовать различные утилиты для манипулирования расширенными атрибутами файловой системы, правами ACL, файловыми capabilities для защиты своих файлов от удаления/модификации или для делегирования приложениям расширенных прав доступа. Многие админы могут даже не знать обо всех этих методах, поэтому они пользуются особой популярностью. К счастью, мы можем защитить себя от подобных махинаций, просто удалив такие утилиты (вообще-то, они распространяются в отдельных пакетах, но кроме самих утилит эти пакеты включают в себя еще и библиотеки, необходимые для работы других нужных нам софтин).

Список зависимых библиотек можно получить с помощью ldd
Список зависимых библиотек можно получить с помощью ldd

Итак, избавляемся от утилит управления мандатными правами доступа (пакет acl):

# rm /usr/bin/{chacl,getfacl,setfacl}

Далее удаляем утилиты для манипулирования расширенными атрибутами файлов (пакет attr):

# rm /usr/bin/{chattr,lsattr,getfattr,setfattr}

И конечно же, утилиты управления capablities (пакет libcap):

# rm /usr/sbin/{getcap,setcap}

Также отправим в /dev/null утилиту для изменения контекста безопасности:

# rm /usr/sbin/chcon

На этом можно было бы остановиться, но Арч по умолчанию содержит набор инструментов cracklib, который необходим для некоторых (весьма полезных, кстати) модулей PAM, но при этом может быть использован для взлома паролей. Его, естественно, лучше удалить:

# rm /usr/sbin/cracklib*
# rm -rf /usr/share/{cracklib,dict}

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

# rm /usr/bin/pacman*
# rm /usr/bin/makepkg
# rm /etc/pacman*
# rm -rf /var/cache/pacman

Ну и подчистим разные ненужности. Например, удалим файлы, требующиеся для сборки образа initrd:

# rm -rf /lib/initcpio

Уберем из системы ненужные каталоги:

# rm -rf /media /opt /usr/local

Удалим ядро с отладочной информацией (вместе с каталогом /usr/src):

# rm -rf /usr/src

Теперь, когда мы избавились от лишнего софта, можно пройтись по каталогам /lib /usr/lib и удалить неиспользуемые библиотеки (вряд ли их будет много, но лучше сделать это). Список нужных библиотек можно получить с помощью следующей команды:

# find /bin /sbin /usr/bin /usr/sbin |\
    xargs ldd | grep '\.so' |\
    cut -d ' ' -f 1 | sed 's/^[ \t]*//' |\
    sort | uniq

Его можно сохранить в файле и с помощью команды diff сравнить с выводом команды «ls -1 /lib /usr/lib». Все не совпадающие библиотеки можно смело удалять.

Устанавливаем нужный софт
Устанавливаем нужный софт
 

Часть 4. Настраиваем сервер

Теперь у нас есть система, очищенная от хлама, но к работе она пока не готова. Сейчас мы должны настроить сервер, то есть nginx, Постгрес, Django и другие компоненты. Как это сделать, я рассказывать не буду, все уже описано до меня. Скажу только, что корнем веб-сервера и Django-проектов следует сделать какой-нибудь каталог в корне файловой системы, например /www (в нем можно создать два каталога: http — статика и django — скрипты). Это необходимо для того, чтобы в дальнейшем со строгими ограничениями смонтировать для каталога отдельный раздел.

Чтобы все работало, выполним базовую настройку системы и добавим в автозагрузку nginx и остальные демоны. ArchLinux не делает это автоматически, поэтому придется править /etc/rc.conf:

# Имя хоста
HOSTNAME="example.com"

# Настройка сетевого интерфейса
interface=eth0
address=1.2.3.4
netmask=255.255.255.0
broadcast=1.2.3.4
gateway=1.2.3.4

# Запускаемые при старте системы демоны
DAEMONS=(hwclock syslog-ng network crond postresql nginx)

Для индивидуального запуска/остановки демонов можно использовать скрипты каталога /etc/rc.d, например:

# /etc/rc.d/nginx restart

Далее мы должны предпринять некоторые меры по обеспечению внешней безопасности сервера. Статья, конечно, не об этом, но о базовой настройке рассказать все же стоит. Во-первых, установим пароль для рута:

# passwd

Во-вторых, заведем непривилегированного пользователя:

# useradd vasya

В-третьих, отключим root-логин по SSH:

# echo 'PermitRootLogin no' > /etc/ssh/sshd_config
# /etc/rc.d/sshd restart

Теперь отредактируем sysctl.conf, чтобы применить несколько классических техник защиты:

# Отключаем форвардинг пакетов
net.ipv4.ip_forward = 0
# Включаем верификация маршрута пакета
net.ipv4.conf.default.rp_filter = 1
net.ipv4.conf.default.accept_source_route = 0
# Уменьшаем количество повторных передач SYNACK
net.ipv4.tcp_synack_retries = 2
# Отключаем редиректы
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.all.secure_redirects = 0
# Игнорируем широковещательные PING-запросы
net.ipv4.icmp_echo_ignore_broadcasts = 1
# Защита от SYN-флуда
net.ipv4.tcp_syncookies = 1

Настроим брандмауэр так, чтобы открытыми оставались только порты 22 и 80 (SSH и HTTP):

# Стандартная политика
iptables -P INPUT DROP
iptables -A INPUT -i lo -j ACCEPT
# Небольшая защита от DoS
iptables -A INPUT -p tcp -m tcp --tcp-flags \
   SYN,ACK,FIN,RST RST -m limit --limit 1/s -j ACCEPT
# Разрешаем уже установленные соединения
iptables -A INPUT -p all -m state --state \
   RELATED,ESTABLISHED -j ACCEPT
# Наши порты
iptables -A INPUT -i eth0 -p tcp --dport 22 -j ACCEPT
iptables -A INPUT -i eth0 -p tcp --dport 80 -j ACCEPT

# Разрешаем некоторые ICMP-сообщения
iptables -A INPUT -i eth0 -p icmp -m icmp \
  --icmp-type 3 -j ACCEPT
iptables -A INPUT -i eth0 -p icmp -m icmp \
  --icmp-type 11 -j ACCEPT
iptables -A INPUT -i eth0 -p icmp -m icmp \
  --icmp-type 12 -j ACCEPT

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

# echo 'echo $((0xffffffff ^ (1 << 16))) > \
   /proc/sys/kernel/cap-bound' >> /etc/rc.local
# echo 'echo $((0xffffffff ^ (1 << 18))) > \
   /proc/sys/kernel/cap-bound' >> /etc/rc.local
# echo 'echo $((0xffffffff ^ (1 << 19))) > \
   /proc/sys/kernel/cap-bound' >> /etc/rc.local
# echo 'echo $((0xffffffff ^ (1 << 21))) > \
   /proc/sys/kernel/cap-bound' >> /etc/rc.local

Эти строки добавляют в ядро после загрузки ограничения на загрузку модулей (для защиты от бэкдоров и руткитов), на выполнение системного вызова chroot, на выполнение ptrace, то есть на трассировку процессов, и на различные системные действия, например на монтирование файловых систем и подключение swap. Пока система не будет перезагружена, отменить ограничения не сможет никто, включая root.

Список установленных пакетов
Список установленных пакетов
 

Часть 5. Перенос системы на сервер и окончательная настройка

Все, теперь у нас есть минималистичный Linux-дистрибутив, выполняющий строго определенные функции, но как установить его на реальный сервер? Очень просто — скопировать. Для этого понадобятся установочный образ ArchLinux, чистая болванка, флешка и немного терпения. Выполняем весь процесс как указано ниже.

  1. Завершаем работу виртуальной машины.
  2. Настраиваем ее загрузку с ISO-образа ArchLinux, вставляем в комп флешку и прокидываем ее в виртуальную машину (с помощью VirtualBox это легко сделать).
  3. Подключаем разделы дистрибутива к каталогу /mnt. На моем виртуальном диске было три раздела: /dev/sda3 - корень, /dev/sda1 - /boot, /dev/sda4 - /home. Монтируем:
    # mount /dev/sda3 /mnt
    # mount /dev/sda1 /mnt/boot
    # mount /dev/sda4 /mnt/home
    
  4. Подключаем флешку к каталогу /media:
    # mount /dev/sdb1 /media
    
  5. Запаковываем систему в архив и кладем его на флешку:
    # cd /mnt
    # tar -czf /media/root.tar.gz .
    # sync
    
  6. Завершаем работу виртуальной машины, нарезаем ISO-образ ArchLinux на болванку и идем к серверу, прихватив с собой флешку и болванку.
  7. Загружаем сервер с болванки и разбиваем диск с помощью cfdisk, руководствуясь следующей схемой:
    sda1 — swap (размер: объем ОЗУ * 2);
    sda2 — корень (размер: 1 Гб);
    sda3 — /www (размер: столько, сколько нужно для хранения всех файлов веб-сайта);
    sda4 — /var (размер: от 1 Гб).
    
  8. Создаем в разделах файловые системы:
    # mkfs.ext4 /dev/sda{2,3,4}
    
  9. Монтируем разделы к /mnt:
    # mount /dev/sda2 /mnt
    # mkdir /mnt/www /mnt/var
    # mount /dev/sda3 /mnt/www
    # mount /dev/sda4 /mnt/var
    
  10. Монтируем флешку и распаковываем архив с системой:
    # mount /dev/sdb1 /media
    # cd /mnt
    # tar -xzf /media/root.tar.gz
    
  11. Устанавливаем загрузчик:
    # chroot /mnt
    # grub-install /dev/sda1
    
  12. Правим конфиг загрузчика:
    title  Arch Linux
    root   (hd0,1)
    kernel /boot/vmlinuz30 root=/dev/sda6 ro
    initrd /boot/kernel30.img
    
  13. Правим /etc/fstab следующим образом:
    devpts /dev/pts devpts defaults 0 0
    shm /dev/shm tmpfs nodev,nosuid 0 0
    /dev/sda1 swap swap defaults 0 0
    /dev/sda2 / ext4 defaults 0 1
    /dev/sda3 /www ext4 defaults,noexec,nodev 0 1
    /dev/sda4 /var ext4 defaults,noexec,nodev 0 1
    tmpfs /tmp tmpfs defaults,noexec,nodev 0 0
    

Каталог /www будет смонтирован без возможности запуска файлов и создания устройств, так что если взломщик проникнет в систему через дыру в веб-сервере и получит права пользователя www, он даже не сможет запустить эксплойт. Для каталогов /var и /tmp действуют те же правила.

Все, теперь можно перезагрузить машину и посмотреть, как все работает.

 

Часть 6. Обновление

Обновить полученный дистрибутив не так-то просто. Для этого потребуется виртуальная машина с установленным ArchLinux (самое время восстановить снимок, созданный в начале процесса) и sshfs. Процесс выглядит так:

  1. Включаем виртуальную машину.
  2. Устанавливаем sshfs.
  3. Обновляем базу данных пакетов и нужные софтины (например, nginx, PostgreSQL, SSH):
    # pacman -Sy
    # pacman -S nginx postgresql ssh
    
  4. Запоминаем, какие пакеты обновились, и для всех этих пакетов, включая nginx, PostgreSQL и SSH, смотрим список файлов:
    # pacman -Ql nginx postgresql ssh
    
  5. Перекидываем файлы на наш сервер с помощью sshfs:
    # sshfs сервер /mnt
    # for file in `pacman -Ql nginx | cut -d ' ' -f 2`; do\
        cp $file /mnt/$file;\
        done
    

Готово! Конечно, это не автоматические обновления каждую ночь, но и не ночной кошмар, тем более что весь процесс вполне можно реализовать в скрипте.

 

Часть 7. Выводы

В этой статье приведены только базовые сведения о том, как обезопасить дистрибутив. Каждый сервер имеет свои особенности, которые необходимо учитывать. В любом случае главное правило: чем меньше компонентов, тем лучше.

 

  • Подпишись на наc в Telegram!

    Только важные новости и лучшие статьи

    Подписаться

  • Подписаться
    Уведомить о
    0 комментариев
    Межтекстовые Отзывы
    Посмотреть все комментарии