Linux Capabilities и Tame из OpenBSD

В Linux хватает возможностей улучшить безопасность приложения. Это и разделение по пользователям, и chroot, и SELinux, и capabilities, и много чего еще. Само собой, идеального и подходящего всем варианта нет, а то, что есть, работает порой не очень интуитивно. Причем "интуитивно" даже для опытных разработчиков.

Приведем простой пример. Приложение запускается от пользователя - как ему позволить выполнить привилегированную операцию? Ну, например, открыть порт 80? Стандартный способ на сегодняшний день, начиная с RHEL 6, это установить на исполняемый файл нужные capabilities:

/usr/sbin/setcap 'cap_net_bind_service=+ep' /usr/bin/myexecutable


Что такое, cap_net_bind_service=+ep предлагаем почитать в man 7 capabilities и в man 3 cap_from_text. Раньше громоздили еще более странные конструкции - вы можете погуглить по "ports less than 1024", "bind to privileged ports" и т.п.

Это работает, но есть нюанс. Любой пользователь, кто может запустить помеченный таким образом исполняемый файл, получается, что может и открыть привилегированный порт. Чтоб не смог, надо сменить у исполняемого файла группу, сделать -x этой группе, и добавить нежелательных пользователей в эту группу (про этот трюк с группами вы уже могли слышать). Исполняемый файл можно куда-нибудь скопировать, но он потеряет capabilities, привязанные к файловому пути.

А можно ли лучше? Скажем, запустить исполняемый файл с помощью fork+exec, и как-то добавить нужные capabilities перед exec или еще где-нибудь? Вот этот вопрос как раз и обеспокоил нашего коллегу, Florian Weimer. Интересно, что в systemd есть пара интересных директив - CapabilityBoundingSet и Capabilities, и о них даже упоминает Lennart Poettering, но они позволяют делать обратное - отбирать capabilities у запущенного от суперпользователя процесса, а не добавлять capabilities в процесс, запущенный от непривилегированного пользователя.

На самом деле, это известная проблема, и те, кто по разным причинам не хотят ставить метку на исполняемый файл, громоздят страшно выглядящие конструкции в своих service-файлах - к примеру, использовать iptables чтобы дать доступ к порту 80 непривилегированному процессу. В случае с портами можно еще использовать socket activation для контейнеров, но даже это порой требует модификации исполняемого файла. Но насчет того, можно ли добавить capabilities в процесс, запущенный от простого пользователя, чтобы решить задачу в общем случае, Andy Lutomirski ответил однозначно - нет, это невозможно. Придется ждать до Linux 4.3, где появятся ambient capabilities, над которым как раз и работает Andy.

Таким образом прямо сейчас функционал capabilities, если не использовать метки на исполняемых файлах, позволяет лишь в runtime уменьшать поверхность атаки для процессов, запущенных от суперпользователя. Это, разумеется, серьезное архитектурное ограничение. Мы даже и не ожидали, что Theo de Raadt возьмет за базовую модель для недавно предложенного Tame именно Linux capabilities.

Вообще, мы привыкли, что идеи, предложенные нашими друзьями, растаскиваются нашими критиками, когда те дорастают до понимания предметной области. Например, после шумной критики systemd, аналог начали реализовывать и в FreeBSD (вы недавно могли об этом слышать), и даже в Haiku (открытый аналог Amiga OS). Это, конечно, сделало неактуальным наш совет systemd-хейтерам, но тут ничего не поделаешь.

Так вот, изначально были предложения разработать для OpenBSD аналог seccomp из Linux (например тут), но Theo высказал соображение, что писать программу для того, чтобы обезопасить другую программу, это нерационально. После этого, из вариантов был лишь один - составить список привилегий, необходимых для каких-то действий (причем ограничиться списком, который показался разумным для Theo), и представить функцию для установки флагов. Выглядит это очень похоже на capabilities, за исключением заведомо урезанного API. Ну, что-то типа покрывает 80% функционала за 20% реализации. Первый вариант в процессе рассмотрения, так что схожесть по функционалу (и, соответственно, по объему реализации) может и возрасти.

К сожалению, специалисты по безопасности, мельком глянув на предложенный вариант реализации, уже нашли фундаментальные ошибки. Проблемы с архитектурой и ее качеством исполнения совершенно неудивительны, учитывая все сокращающееся количество BSD-разработчиков, вдобавок к этому более-менее равномерно распределенное среди дюжины несовместимых форков BSD. Brad Spengler замечает, что это не первый случай полурабочей реализации в OpenBSD функционала из других операционных систем, и делает вывод:

...наивный минималистический подход на основе syscall обязательно столкнется с проблемой - любая более-менее сложная программа обязательно потребует столько привилегий, что ей хватит, чтобы "выстрелить себе в ногу". ...возможно Tame будет достаточно для неинтерактивно работающего на i386-машине файерволла (типичная инсталляция OpenBSD), но на реальный мир и реальные приложения такой подход не перенести. Если Tame не защищает от простейшего наколеночного эксплойта, тогда в чем его смысл, кроме иллюзии применимости OpenBSD для приложений безопасности?