Описание: Кандидат BSDA должен понимать преимущества запуска сервисов в изолированной среде на машинах открытых для Интернет, и какие утилиты предназначены для этого в какой BSD.
Практика: chroot(8); jail(8); systrace(1); Стороннее приложение Xen.
Реалии современного програмного обеспечения таковы, что многие сетевые (и не только сетевые) сервисы могут быть взломаны. Широко известны атаки типа «переполнение буфера». Программа запрашивает некоторый параметр у пользователя и не проверяет какой длины данные ей передали. Таким образом, злоумышленник получает возможность записать данные в область памяти занятую кодом программы, на который в последствии будет передано управление. В результате злоумышленник получает возможность выполнять произвольные действия от имени данной службы.
Чтобы противодействовать данному виду атак, многие службы запускают в изолированной среде — «sandbox» или «песочнице». Существует множество способов построения «песочниц»:
Это, пожалуй, самое радикальное средство изоляции сервисов. Вы запускаете образ машины, устанавливаете на неё любую полюбившуюся операционную систему и выставляете эту «машину» в Интернет. В случае взлома вы просто восстанавливаете её из образа. В этой ситуации риск повреждения материнской системы практически полностью исключён. Однако накладные расходы очень велики — быстродействие виртуальной машины в десятки раз ниже быстродействия материнской.
Известные эмуляторы: vmware — коммерческий продукт выпускающийся под Linux его можно запустить в FreeBSD используя «эмулятор» Linux. Другое решение — qemu. Qemu ставится из портов в любую систему, это OpenSource, но выбор эмулируемого железа в нём ограничен.
Суть явления примерно та же, что и в предыдущем случае, однако псевдоэмулятор не занимается эмуляцией железа. Псевдоэмулятор работает на уровне ядра операционной системы. С одной стороны, это сужает возможности эмулятора, так как вы лишаетесь возможности эмулировать другие архитектуры. С другой стороны вы получаете существенный прирост в производительности. И возможность одновременного запуска нескольких различных операционных систем.
Примером такого рода эмуляции является рассмотренный ниже Xen, реализованный в NetBSD, OpenBSD и FreeBSD.
Это один из самых простых и один из самых древних методов
построения «песочницы». Хотя системый вызов
chroot(2)
не входит в стандарт
POSIX, он реализован практически
повсеместно. Приложение выполняет системный вызов chroot(2)
, после чего любое
обращение к корневому каталогу ядро транслирует в
некоторый другой каталог — корень
«песочницы». Таким образом, приложение лишено
возможности испортить файлы за пределами
«песочницы». В тоже время, приложение работает с
сокетами материнской системы, если мы создаём в песочнице
каталог /dev
с файлами устройств, то
оно будет иметь доступ к устройствам на низком уровне и,
при достаточном количестве полномочий может даже вырваться
за пределы песочницы.
Данный метод построения «песочниц» очень распространён. Существуют сервисы, такие как BIND, которые по умолчанию запускаются в окружении chroot(8).
Команда chroot(8) позволяет запускать приложения в ограниченной среде путём смены корневого каталога. Для начала, давайте попробуем запустить в ограниченной среде программу csh(1). В дополнение к ней мы скопируем в ограниченную среду команду tree(1), чтобы с её помощью убедиться что у нас всё получилось. Для этого нам надо выполнить следующие действия:
/bin
и /lib
.
$
mkdir -p sandbox/bin sandbox/lib$
ldd /bin/csh /bin/csh: libncurses.so.6 => /lib/libncurses.so.6 (0x280b9000) libcrypt.so.3 => /lib/libcrypt.so.3 (0x280f8000) libc.so.6 => /lib/libc.so.6 (0x28110000)$
ldd /usr/local/bin/tree /usr/local/bin/tree: libc.so.5 => /lib/libc.so.5 (0x2807c000)$
cp /lib/libc.so.5 /lib/libncurses.so.6 /lib/libcrypt.so.3 /lib/libc.so.6 sandbox/lib/$
cp /bin/csh /usr/local/bin/tree sandbox/bin/#
chroot sandbox/ csh Password: ELF interpreter /libexec/ld-elf.so.1 not found Abort trap: 6$
mkdir sandbox/libexec$
cp /libexec/ld-elf.so.1 sandbox/libexec/#
chroot sandbox/ csh csh: Cannot open /etc/termcap. csh: using dumb terminal settings.%
pwd pwd: Command not found.%
tree / / |-- bin | |-- csh | `-- tree |-- lib | |-- libc.so.5 | |-- libc.so.6 | |-- libcrypt.so.3 | `-- libncurses.so.6 `-- libexec `-- ld-elf.so.1 3 directories, 7 files%
exit exit
Некоторым приложениям может понадобиться наличие каталога
/dev
с файлами устройств. Для систем не
поддерживающих devfs надо создать эти файлы
при помощи команды mknod(8), а для систем
поддерживающих devfs, например для
FreeBSD, понадобится смонтировать в
«песочницу» файловую систему devfs.
Однако при этом вам может понадобиться создать не все, а только
некоторые файлы устройств. Для этого надо воспользоваться
утилитой devfs(8):
$
mkdir sandbox/dev#
mount_devfs devfs sandbox/dev/ Password:$
ls sandbox/dev/ acd0 console fd/ nfs4 sysmouse ttyv5 acd0t01 consolectl fd0 nfslock ttyd0 ttyv6 acpi ctty fido null ttyd0.init ttyv7 ad0 cuad0 geom.ctl pass0 ttyd0.lock ttyv8 ad0s1 cuad0.init io pci ttyd1 ttyv9 ad0s1a cuad0.lock kbd0@ psm0 ttyd1.init ttyva ad0s1b cuad1 klog ptyp0 ttyd1.lock ttyvb ad0s1c cuad1.init kmem ptyp1 ttyp0 ttyvc ad0s1d cuad1.lock lpt0 ptyp2 ttyp1 ttyvd agpgart devctl lpt0.ctl ptyp3 ttyp2 ttyve apm devstat mdctl ptyp4 ttyp3 ttyvf ata dri/ mem random ttyp4 urandom@ atkbd0 dsp0.0 mixer0 rtc ttyv0 usb audio0.0 dsp0.1 net/ sndstat ttyv1 usb0 audio0.1 dspW0.0 net1@ stderr@ ttyv2 usb1 bpsm0 dspW0.1 net2@ stdin@ ttyv3 xpt0 cd0 dspr0.1 network stdout@ ttyv4 zero#
devfs -m sandbox/dev/ rule apply hide$
ls sandbox/dev/#
devfs -m sandbox/dev/ rule apply path zero unhide#
devfs -m sandbox/dev/ rule apply path null unhide#
devfs -m sandbox/dev/ rule apply path random unhide$
ls sandbox/dev/ null random zero#
chroot sandbox/ csh csh: Cannot open /etc/termcap. csh: using dumb terminal settings.%
tree / / |-- bin | |-- csh | `-- tree |-- dev | |-- null | |-- random | `-- zero |-- lib | |-- libc.so.5 | |-- libc.so.6 | |-- libcrypt.so.3 | `-- libncurses.so.6 `-- libexec `-- ld-elf.so.1 4 directories, 10 files%
exit exit
Команда jail(8) может рассматриваться как
средство для запуска программы в ограниченном окружении, а может
рассматриваться как средство виртуализации. В первом случае
настройка jail(8) выглядит аналогично
рассмотренному выше chroot(8). К уже
имеющемуся окружению sandbox/
мы добавим
команду ifconfig(8):
#
ifconfig rl0 add 172.19.0.234/24$
ifconfig rl0 rl0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> mtu 1500 options=8<VLAN_MTU> inet 172.19.0.5 netmask 0xffffff00 broadcast 172.19.0.255 inet 172.19.0.234 netmask 0xffffff00 broadcast 172.19.0.255 ether 4c:00:10:54:dd:8e media: Ethernet autoselect (100baseTX <full-duplex>) status: active lo0: flags=8049<UP,LOOPBACK,RUNNING,MULTICAST> mtu 16384 inet 127.0.0.1 netmask 0xff000000$
mkdir sandbox/sbin$
cp /sbin/ifconfig sandbox/sbin$
ldd /sbin/ifconfig /sbin/ifconfig: libipx.so.3 => /lib/libipx.so.3 (0x28082000) libc.so.6 => /lib/libc.so.6 (0x28085000)$
cp /lib/libipx.so.3 sandbox/lib#
jail sandbox/ testhostname 172.19.0.234 /bin/csh csh: Cannot open /etc/termcap. csh: using dumb terminal settings.%
/sbin/ifconfig rl0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> mtu 1500 options=8<VLAN_MTU> inet 172.19.0.234 netmask 0xffffff00 broadcast 172.19.0.255 ether 4c:00:10:54:dd:8e media: Ethernet autoselect (100baseTX <full-duplex>) status: active lo0: flags=8049<UP,LOOPBACK,RUNNING,MULTICAST> mtu 16384%
exit exit
Если теперь мы добавим адрес 192.168.0.34 как алиас для интерфейса материнской машины, то по нему мы сможем взаимодействовать с внутренней машиной.
Поговорим об использовании jail(8) в качестве эмулятора.
Таблица 2.1. Опции запуска jail(8)
Опция | Описание |
---|---|
Необязательные аргументы | |
-i | Вывести идентификатор созданной «тюрьмы». |
-J JidFile | Создать JidFile, аналогично PidFile. В него записывается jailid, путь к sandbox, hostname, IP адрес, и команда запущенная в jail. |
-l |
Выполнить программу в чистых переменных окружения.
Переменные окружения уничтожаются за исключением
переменных HOME ,
SHELL , TERM и
USER . Переменная
TERM импортируется из текущего
окружения, остальные выставляются согласно выполненному
в песочнице логину.
|
-s securelevel |
Устанавливает переменную ядра
kern.securelevel в указанное значение
внутри созданной «тюрьмы». Эта опция появилась
только в FreeBSD 6.2
|
-u username | Имя пользователя от имени которого осуществляется запуск jail(8) |
-U username | Имя пользователя от имени которого выполняется команда внутри jail(8). |
Обязательные аргументы | |
path | Путь к sandbox |
hostname | hostname внутри jail(8) |
IP | Адрес jail(8). Пока на один jail(8) можно дать только один адрес. |
command | Команда, которая будет выполнена в jail(8) |
Ниже я перечислю нужные для этого команды, естественно удалив их стандартный вывод — он огромен.
$
D=/path/to/sandbox$
mkdir -p $D$
cd /usr/src#
make world DESTDIR=$D#
make distribution DESTDIR=$D#
mount_devfs devfs $D/dev
Таким образом, будет собрана вторая копия
FreeBSD в каталоге
/path/to/sandbox/
. Разумеется, если
ваша цель состоит только в запуске какого-то конкретного
сервиса, а не предоставления виртуального хостинга, то
данные действия, мягко говоря, избыточны. вероятно имеет
смысл создать маленькую «тюрьму» и добавлять в неё
файлы, пока она не заработает. Такой путь сложнее чем путь
удаления файлов из «толстой тюрьмы», но приводит к
лучшему результату. Кроме того, монтирование всей файловой
системы devfs, тоже небезопасно, поэтому далее следует
исключить некоторые файлы устройств (как минимум жёсткие
диски) способом описанным выше, в Раздел 2.11.1, «chroot(8)».
Прежде всего, следует исключить ситуацию, когда сервисы
материнской системы слушают адрес присвоенный
jail(8). Некоторые сервисы придётся
отключить, некоторые перенастроить. В частности, надо
заставить суперсервер inetd(8) слушать
некоторый конкретный адрес, принадлежащий материнской
системе. Имеет смысл добавить в файл
/etc/rc.conf
следующие строки:
sendmail_enable="NO" inetd_flags="-wW -a 192.168.11.23" rpcbind_enable="NO"
Где 192.168.11.23 — адрес материнской системы.
Демоны запущенные не через inetd(8)
должны быть переконфигурированы. Некоторые могут быть
перенастроены при помощи /etc/rc.conf
,
некоторые через свои конфигурационные файлы. В некоторых
клинических случаях демонов придётся пересобирать.
Для конфигурирования sshd(8) следует
воспользоваться файлом
/etc/ssh/sshd_config(5)
.
Для конфигурирования
sendmail(8) —
/etc/mail/sendmail.cf
named(8) —
/etc/namedb/named.conf
.
Сервисы, основанные на rpc(3), такие как rpcbind(8), nfsd(8), mountd(8) придётся пересобирать.
Так или иначе, сервисы, которым невозможно объяснить какой они слушают адрес не должны запускаться на материнской системе, если они не должны интерферировать с программами в jail(8).
Первый запуск jail(8) происходит в системе, в которой не настроены сетевые интерфейсы, нет учётных записей пользователей и т.д. Некоторые из этих вещей можно настроить только если у вас запущен виртуальный сервер внутри jail(8). Запустите jail(8):
#
jail /data/jail/192.168.11.100 testhostname 192.168.11.100 /bin/sh
Если всё будет в порядке, то теперь в этом окружении можно
выполнить настройку системы при помощи утилиты
/usr/sbin/sysinstall(8) или вручную
отредактировать /etc/rc.conf
в
окружении jail(8).
Выполните следующие шаги:
/etc/fstab
дабы
избежать сообщений о его отсутствии.
rpcbind_enable="NO"
в
/etc/rc.conf
)
/etc/resolv.conf
network_interfaces=""
в
/etc/rc.conf
)
/usr/share/zoneinfo/Europe/Moscow
)
под именем /etc/localtime
.
Кроме того, вам возможно понадобится сконфигурировать
какое-нибудь програмное обеспечение внутри
jail(8). Например web-сервер, ssh-сервер
и т.д. Возможно вы захотите чтобы
syslogd(8) материнской системы слушал
сокет в jail(8). В рассматриваемом
примере его надо направить на сокет
/data/jail/192.168.11.100/var/run/log
.
После произведённых настроек можно выйти из оболочки запущенной в jail(8).
Теперь вы готовы запускать виртуальный сервер в jail(8). Для этого надо выполнить в jail(8) скрипт /etc/rc.
Важно | |
---|---|
Если вы собираетесь предоставлять доступ неизвестным
пользователям с правами root в jail(8),
выставите переменную ядра
security.jail.set_hostname_allowed в 0 до
запуска jail(8). В Раздел 2.11.2.1.5, «Управление jail(8)» сказано чем это может
быть полезно.
|
Вот команды для запуска виртуального сервера:
#
ifconfig bge0 add 172.19.0.133/24#
mount_devfs devfs /opt/sandbox/dev#
jail /opt/sandbox/ jail-hp.house.hcn-strela.ru 172.19.0.133 /bin/sh /etc/rc Loading configuration files. jail-hp.house.hcn-strela.ru Setting hostname: jail-hp.house.hcn-strela.ru. Creating and/or trimming log files:. ln: /dev/log: Operation not permitted Starting syslogd. ELF ldconfig path: /lib /usr/lib /usr/lib/compat a.out ldconfig path: /usr/lib/aout /usr/lib/compat/aout Starting local daemons:. Updating motd. Starting sshd. Starting cron. Local package initialization:. Thu Feb 22 11:41:09 MSK 2007$
ssh guest@172.19.0.133 The authenticity of host '172.19.0.133 (172.19.0.133)' can't be established. DSA key fingerprint is 95:cc:e5:38:e7:19:9e:0a:aa:40:a0:04:80:7b:be:53. Are you sure you want to continue connecting (yes/no)? yes Warning: Permanently added '172.19.0.133' (DSA) to the list of known hosts. Password: Last login: Thu Feb 22 10:58:50 2007 from 172.19.0.33 Copyright (c) 1980, 1983, 1986, 1988, 1990, 1991, 1993, 1994 The Regents of the University of California. All rights reserved. ...............................................................................$
ifconfig fwe0: flags=108802<BROADCAST,SIMPLEX,MULTICAST,NEEDSGIANT> mtu 1500 options=8<VLAN_MTU> ether 02:02:3f:15:33:0c ch 1 dma -1 bge0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> mtu 1500 options=1b<RXCSUM,TXCSUM,VLAN_MTU,VLAN_HWTAGGING> inet 172.19.0.133 netmask 0xffffff00 broadcast 172.19.0.255 ether 00:17:08:2f:a6:90 media: Ethernet autoselect (100baseTX <full-duplex>) status: active plip0: flags=108810<POINTOPOINT,SIMPLEX,MULTICAST,NEEDSGIANT> mtu 1500 lo0: flags=8049<UP,LOOPBACK,RUNNING,MULTICAST> mtu 16384
Заходим в тюрьму по ssh(1). Учётная запись guest и sshd(8) демон настроены заранее. | |
Это уже команда выполненная внутри тюрьмы. |
Вы получите некоторое количество предупреждений связанных с тем, что внутри jail(8) нельзя выполнить большинство вызовов sysctl(8). Однако всё должно работать. Вы можете увидеть при помощи команды ps(1) процессы запущенные в jail(8) с флагом J.
Можно запускать jail(8) автоматически при
старте системы. Для этого надо вписать строки типа jail_*
в
/etc/rc.conf(5)
.
Например, для того, чтобы при старте системы автоматически
запускалось три jail'а, надо поместить в
/etc/rc.conf(5)
следующие строки:
jail_enable="YES" jail_list="one,two,three" jail_one_hostname="www.propeller.ru" jail_two_hostname="www.samovar.ru" jail_three_hostname="www.avtoclav.ru" jail_one_ip="192.168.11.100" jail_two_ip="192.168.11.101" jail_three_ip="192.168.11.102" jail_one_rootdir="/data/jail/192.168.11.100" jail_two_rootdir="/data/jail/192.168.11.101" jail_three_rootdir="/data/jail/192.168.11.102"
Это программа-минимум. С помощью других опций описанных в
/etc/rc.conf(5)
вы можете оговорить
нужно ли перед запуском jail(8)
монтировать внутри него procfs,
devfs, какие устройства нужно
активировать внутри devfs и т.п.
Можно управлять jail(8) при помощи стартового скрипта /etc/rc.d/jail:
#
/etc/rc.d/jail start#
/etc/rc.d/jail stop#
/etc/rc.d/jail start myjail#
/etc/rc.d/jail stop myjail
Обычные команды типа shutdown(8), halt(8) или reboot(8) в jail(8) не работают. Вместо них можно зайти в jail(8) и выполнить одну из команд:
#
kill -TERM -1#
kill -KILL -1
в зависимости от того, что вы хотите сделать. Возможно вы захотите выполнить скрипт /etc/rc.sutdown внутри jail(8).
Если вы находитесь снаружи jail(8) и хотите выполнить команду внутри него, вы можете воспользоваться командой jexec(8):
#
jexec 6 kill -KILL -1
Эта команда выполнит команду kill(1) внутри jail с jid=6.
В каталоге /proc
, если вы его
используете, в файле
/proc/<pid>/status
в последнем
поле находится имя хоста для jail или
знак -
если процесс запущен не в
jail. Кроме того, команда
ps(1) показывает знак J
если процесс запущен в
jail. Однако hostname может быть изменён
внутри jail и тогда значение из файла
/proc/<pid>/status
оказывается ни
с чем не связано. Чтобы запретить смену hostname надо
выставить переменную ядра
security.jail.set_hostname_allowed
в 0.
(см. Раздел 5.6, «Изменение на лету переменных ядра»). Это повлияет на всю
«пенитенциарную систему».
Чтобы увидеть список процессов с их Jail ID вы можете выполнить команду
$
ps ax -o pid,jid,args
Чтобы увидеть процессы в jail(8) номер 3, и послать им сигналы, можно использовать команды
$
pgrep -lfj 3#
pkill -j 3
или
#
killall -j 3
Таблица 2.2. Переменные ядра (MIB) связанные с jail(8)
Переменная | Умолчание | Описание |
---|---|---|
security.jail.allow_raw_sockets | 0 | Переменная определяет может ли root в «тюрьме» открывать сырые сокеты. Установка переменной в 1 позволит запускать в тюрьме такие утилиты как ping(8) и traceroute(8). |
security.jail.enforce_statfs | 2 | Определяет какая информация о точках монтирования доступна для процессов в jail(8). 0 — все точки монтирования доступны без ограничений; 1 — Доступны только точки монтирвания внутри каталога jail(8), путь к каталогу jail(8) удаляется; 2 — можно работать только с точкой монтирования в которой разположен jail(8). |
security.jail.set_hostname_allowed | 1 | Определяет может ли приложение в jail(8) сменить hostname. |
security.jail.socket_unixiproute_only | 0 | По умолчанию процессы в jail(8) могут взаимодействовать с доменными сокетами UNIX, IPv4 сокетами и routing sockets. Смена данной переменной приведёт к тому, что процессам в jail(8) станут доступны и другие сокеты. |
security.jail.sysvipc_allowed | 0 | Могут ли процессы в jail(8) использовать примитивы System V IPC. Установка этой переменной в 1 позволит процессам в jail(8) взаимодействовать с процессами в других «тюрьмах» и с процессами материнской системы. |
security.jail.chflags_allowed | 0 |
Как взаимодействует root в
jail(8) с флагами выставленными
командой chflags(1).
0 — root считается непривилегированным
пользователем и не может менять этот флаг.
1 — root считается привилегированным
пользователем и может манипулировать флагами
согласно секущему уровню
kern.securelevel .
|
Существуют две переменные, которые можно менять внутри jail(8) их значение будет действовать только внутри данной «тюрьмы». | ||
kern.securelevel | ||
kern.hostname |
jls(8). Программа предоставляет список запущенных тюрем:
$
jls
JID IP Address Hostname Path
6 172.19.0.133 jail-hp.house.hcn-strela.ru /opt/sandbox
jexec(8). Программа позволяет выполнить в jail(8) произвольную команду:
#
jexec 6 kill -TERM -1
Эта команда приведёт к остановке 6-й тюрьмы.
security/jailaudit. Порт генерирующий вывод команды portaudit(1) для тюрем.
sysutils/ezjail. Порт предназначенный для облегчения создания и управления тюрьмами.
sysutils/jailadmin. Порт для администрирования тюрьмами.
sysutils/jailctl. Порт для администрирования тюрьмами.
sysutils/jailer. Порт для управления запуском и остановкой тюрем.
sysutils/jailutils. Порт с несколькими программами для манипулирования тюрьмами.
Для получения более подробной информации о портах выполните команду:
$
cat /usr/ports/<category>/<port>/pkg-descr
$
while :; do cat /dev/zero | md5 & done