Міжнародна конференція розробників
і користувачів вільного програмного забезпечення

clsync progess: security and porting to freebsd

Dmitry Okunev, Moscow, Russia

LVEE 2014

The report focuses on methods to increase a level of security of "clsync" (free live sync utility). Multiple approaches to reduce risks of attack were used, and practical experience of applying these approaches is given. Used methods include unsharing unnecessary resources, splitting the process to privileged and non-privileged threads and using Linux-specific security-related API. Also problems of porting Linux-based clsync to FreeBSD are described, including variants and problems of FS monitoring in FreeBSD.

Введение

Проект clsync возник как альтернативный высокопроизводительный инструмент для синхронизации LXC-контейнеров между узлами HA-кластера и их резервного копирования, изначально ориентированный под внутренние нужды отдела UNIX-технологий НИЯУ МИФИ 1, 2. Данная статья является отчётом о достижениях по развитию программы «clsync» в вопросах повышения уровня безопасности и портируемости на другие ОС в 2014 году.

Средства защиты

Процесс отслеживания изменений на ФС часто требует привилегий root (или capability “CAP_DAC_READ_SEARCH” 3 в Linux). Однако известные программы 4 для синхронизации на базе inotify 5 имеют достаточно малое сообщество и при этом не имеют никаких встроенных механизмов повышения безопасности (сброс лишних привилегий, namespaces и т.п.). В результате становится маловозможной настройка синхронизации посредством inotify в системах, требующих особой защиты. Далее будут рассмотрены средства для минимизации последствий эксплуатации уязвимостей, реализованные в «clsync» летом 2014-го года.

Задача

Задача заключается в минимизации последствий обнаружения и эксплуатации уязвимостей в clsync злоумышленником.

Предполагается, что:

  • Необходимо предоставить процессу «clsync» полный доступ к некоторому файловому дереву, содержащему объекты (файлы, директории и т.п.) с произвольными наборами прав.
  • Злоумышленник имеет возможность управлять содержимым данного файлового дерева, но не имеет других путей для манипуляции над процессом «clsync».
  • «clsync» не наблюдает за файлами, чтение которых злоумышленником привело бы к ущербу.
  • Процессу «clsync» необходимо запускать внешний процесс (например «rsync») для осуществления синхронизации.

Практическим применением данной задачи является хостинг на базе LXC, требующий синхронизации на другой узел для реализации High Availability 1, 6. А для синхронизации запускается по одному процессу «clsync» на каждый контейнер. В такой конфигурации содержимым контейнера управляют малодоверенные пользователи, которые могут иметь интерес выйти за пределы предоставленных им пространств имен (namespaces), эксплуатируя уязвимость в «clsync» (который в свою очередь запущен с хост-системы, то есть извне пространств имен контейнера).

Решение

В качестве мер для повышения безопасности предлагается:

  • Применить unshare() 7 для отсоединения от пространств имен хост-системы для сети, IPC и других ресурсов.
  • Сбросить привилегии с помощью setuid()/setgid(), предварительно сохранив capability «CAP_DAC_READ_SEARCH» для сохранения полного доступа к файловому дереву.
  • Производить chroot(), pivot_root() 8 и umount() для запрета доступа к файлам вне наблюдаемой директории.
  • Разделять процесс на «большой непривилегированный» и «малый привилегированный».
  • Использовать seccomp filter 9 для запрета лишних системных вызовов (syscalls) внутри непривилегированного потока.

unshare()

Применение unshare() позволяет запретить доступ к сети (и сервисам, работающим на localhost), IPC и другим ресурсам. Это необходимо, чтобы предотвратить взлом процессов по цепочке (когда злоумышленник пытается взламывать другие сервисы через уязвимость clsync).

Сброс привилегий

Сброс привилегий — это тривиальная и очень распространенная процедура. В «clsync» предлагается производить сброс с помощью setuid() и setgid() с предварительным сохранением capability «CAP_DAC_READ_SEARCH» для доступа к файлам. Однако в рамках сформулированной задачи необходимо решить следующие проблемы:

  1. Запускаемому процессу для синхронизации (например «rsync») аналогично необходим доступ на чтение всего синхронизируемого файлового дерева. И наследовать для этого capability «CAP_DAC_READ_SEARCH» недостаточно, так как кроме этого необходимо его активировать (добавить в effective capabilities bitmask). Но такого рода процессы обычно не имеют поддержки активации capabilities. Поэтому данную проблему необходимо решить возможностями «clsync». Для решения данной проблемы можно либо добавить в связку sudo (или аналог), или делать setuid()/setgid() назад на root. Использование дополнительного ПО существенно снижает уровень безопасности, поэтому setuid()/setgid() является более желательным вариантом. Но сохранение capabilities «CAP_SETUID» и «CAP_SETGID» фактически полностью ликвидирует защиту за счёт сброса привилегий. Чтобы решить данную проблему предложен способ разделения процесса на «большой привилегированный» и «малый непривилегированный».
  2. «CAP_DAC_READ_SEARCH» предоставляет доступ на чтение всех файлов, что недопустимо в рамках сформулированной задачи. В качестве решения проблемы предлагается использовать chroot(), pivot_root() и umount().

Namespace файлового дерева

Как уже говорилось, для запрета доступа к файлам вне наблюдаемой директории предлагается использовать pivot_root(), chroot() и umount(). В автоматической форме это делается по следующему алгоритму:

  1. Открепление от namespace точек монтирования с помощью unshare().
  2. Монтирование chroot-окружения в отдельную директорию с помощью bind (с опцией режима “только на чтение”).
  3. Переход в данную директорию.
  4. Вызов pivot_root() для последующего отмонтирования rootfs хост-системы.
  5. Вызов chroot() на данную директорию.
  6. Отмонтирование старого rootfs (что спровоцирует каскадное отмонтирование всех лишних точек монтирования).

Данный подход даёт возможность достаточно надёжно защитить лишние файлы от чтения/изменения процессом «clsync». Однако существует риск, вызванный уязвимостью, описанной в статье 10. Данная проблема решается за счёт разделения процесса на «большой непривилегированный» и «малый привилегированный».

Splitting

Технически разделение процесса «clsync» на «большой непривилегированный» и «малый привилегированный» предполагает запуск дополнительного потока (thread) для обращения к системным вызовам, требующим привилегий выше чем «nobody». С точки зрения безопасности, это способ многократно снизить площадь для атаки на clsync. Если предположить, что потенциальные уязвимости распределены по всему коду равномерно, то данный подход позволяет ликвидировать последствия взлома почти полностью для более чем 90% уязвимостей, и чем меньше будет код привилегированного потока, тем выше окажется эффективность метода. Однако данный подход требует применения блокировок и передачи сообщений между потоками, что существенно снижает производительность (время выполнения увеличивалось в несколько раз).

Для уменьшения потерь производительности был реализован механизм блокировок, комбинирующий pthread_mutex_* и spinlock, что позволило достичь на многоядерных системах времени выполнения близкого к прежнему (без данного разделения потоков), но со значительно большим расходом ресурсов CPU.

Однако, так как разделение производится при помощи pthread, то у непривилегированного потока есть доступ ко всей памяти привилегированного потока, включая доступ к stack и т.п. Это позволяет без особых сложностей провести атаку на привилегированный поток из непривилегированного. Чтобы решить данную проблему, предлагается использовать seccomp filter для запрета вызова mprotect из непривилегированного потока, а также «забыть» максимальное количество информации о привилегированном потоке. Если первое является реальным средством защиты, то второе – лишь способ усложнить эксплуатацию потенциальной уязвимости.

Seccomp

Seccomp, как уже было сказано выше, предлагается использовать для фильтрации лишних системных вызовов для непривилегированного потока, что позволяет:

  • защитить привилегированный поток с помощью mprotect 11.
  • дополнительно снизить последствия эксплуатации уязвимости, так как набор действий злоумышленника будет ограничен до конкретного короткого списка системных вызовов.

Портирование на FreeBSD

FreeBSD (как и другие BSD-системы) до сих пор активно используются для реализации различных сервисов высокой доступности, однако в стандартном дереве портов данной ОС не предоставляется никаких production-ready решений для живой синхронизации файлов (без использования специальных файловых систем). Далее будет описаны основные проблемы при портировании программы для живой синхронизации «clsync» под FreeBSD.

Оригинальный «clsync» использует inotify для наблюдения за ФС, однако данный API специфичен для Linux и не предоставляется во FreeBSD. В результате при портировании в качестве альтернатив для наблюдения за событями на ФС были выбраны «kqueue» 12, «bsm» 13 и «dtrace» 14.

Основными проблемами при использовании «kqueue» являются:

  • использование большого количества файловых дескрипторов (в сравнении с inotify);
  • невозможность получать детали по создаваемым файлами и директориям (что было решено полным пересканированием директории при появлении в ней нового объекта);
  • необходимость «вручную» вычислять переносы файлов и директорий (и различать их с созданием жестких ссылок, т.е. hard links);
  • множество сложноучитываемых эффектов (например, необходимо учитывать, что нельзя открывать pipes).

Основной проблемой при использовании «bsm» является требование глобальной перенастройки auditd.

А «dtrace», как выяснилось в ходе реализации его поддержки в «clsync», реализован не в соответствии с оригинальным «dtrace» 15. Причем различия построенным таким образом, что становится очень затруднительным получить полный путь файла, которому соответствует пойманное событие. В результате, реализация «dtrace» во FreeBSD непригодна для наблюдения за событиями ФС.

На данный момент самая стабильная работа clsync во FreeBSD обеспечена с применением библиотеки libinotify-kqueue 16, которая представляет собой реализацию API inotify на базе kqueue.

Список литературы

  1. Окунев Д.Ю. «clsync — live sync utility», материалы Международная конференция разработчиков и пользователей свободного программного обеспечения Linux Vacation / Eastern Europe (LVEE Winter 2014), http://lvee.org/en/reports/materials_lvee_winter_2014
  2. «file live sync daemon based on inotify, written in GNU C», https://github.com/xaionaro/clsync
  3. «http://linux.die.net/man/7/capabilities»
  4. «Manual to Lsyncd 2.1.x», https://github.com/axkibe/lsyncd/wiki/Manual-to-Lsyncd-2.1.x
  5. «inotify – monitoring file system events», http://linux.die.net/man/7/inotify
  6. Окунев Д.Ю. «Опыт внедрения отказоустойчивого web-кластера для портала приёмной комиссии НИЯУ МИФИ», научная сессия НИЯУ МИФИ, 2012, http://www.pandia.ru/text/78/343/297.php
  7. http://linux.die.net/man/2/unshare
  8. http://linux.die.net/man/2/pivot_root
  9. http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/Documentation/prctl/seccomp_filter.txt
  10. «Выявлена уязвимость, позволяющая выйти за пределы контейнеров Docker», http://www.opennet.ru/opennews/art.shtml?num=40046
  11. «mprotect – set protection on a region of memory», http://linux.die.net/man/2/mprotect
  12. «kqueue, kevent — kernel event notification mechanism», http://www.freebsd.org/cgi/man.cgi?query=kqueue&sektion=2
  13. «auditd — audit log management daemon», http://www.freebsd.org/cgi/man.cgi?query=auditd&sektion=8
  14. «dtrace — DTrace dynamic tracing compiler and tracing utility», http://www.unix.com/man-page/freebsd/1/dtrace/
  15. «DTrace Built-in Variables», Oracle, http://docs.oracle.com/cd/E18752_01/html/819-5488/gcfpz.html
  16. «NetBSD Google Summer of Code 2011 project (#2)», https://github.com/dmatveev/libinotify-kqueue

Abstract licensed under Creative Commons Attribution-ShareAlike 4.0 license

Назад