Решение проблемы ограничения PTRACE_ATTACH в контейнерах Docker +11



В последние два года мы широко используем Docker как для разработки, так и для выполнения систем в производственной среде, и все текущие продукты для наших клиентов разрабатываются именно с учетом данной системы контейнеризации. Стоит отметить, что Docker достаточно сильно изменяется от версии к версии, добавляя как дополнительные возможности (Swarm, Compose), так и дополнительные инструменты повышения защищенности и контроля приложений.


Так с большим удивлением мы недавно обнаружили, что контейнер, который был разработан и протестирован некоторое время назад, не работает в текущей версии Docker. Дело в том, что контейнер был не совсем типовым и внутри него использовалась утилита strace для анализа поведения процесса. Про данное применение утилиты мы ранее подробно писали на Habrahabr.


Сегодня у наших разработчиков наконец-то дошли руки до внедрения данного проблемного
контейнера в приложение и они обнаружили, что он больше не работает. Начав разбираться в чем причина такого поведения, мы увидели, что strace, а конкретно ptrace, не может присоединяться к процессам и выдает ошибку вида:


strace: attach: ptrace(PTRACE_ATTACH, ...): Operation not permitted Could not attach to process.  If your uid matches the uid of the target process, check the setting of /proc/sys/kernel/yama/ptrace_scope, or try again as the root user.  For more details, see /etc/sysctl.d/10-ptrace.conf

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


Подсистема Seccomp


В новых ядрах ОС Linux новый Docker использует функцию ядра seccomp, которая позволяет блокировать системные вызовы внутри контейнера. Документация Docker по поводу данной возможности находится здесь. Ptrace находится среди заблокированных системных вызовов. Тонкая настройка механизма осуществляется с помощью аргумента Docker run --security-opt seccomp=/path/to/file.json, который позволяет указать файл, в котором описывается что разрешено, а что нет. Поскольку наш контейнер работает в защищенной среде, то мы отключаем данную возможность полностью: --security-opt seccomp=unconfined.


Поведение Ptrace


Strace выдала нам подсказку о том, что есть некий псевдофайл /proc/sys/kernel/yama/ptrace_scope, в котором тоже описываются ограничения ptrace. Смотрим в документацию ядра и узнаем, что не все так просто, как было раньше. Значения 0-3, находящиеся в данном псевдофайле существенно меняют поведение ptrace:


  • 0 — UID трассирующего процесса должен совпадать с UID трассируемого или быть 0 (поведение как было раньше);
  • 1 — ограниченный ptrace, процессы должны быть родственными, трассируемый процесс должен быть потомком
    трассирующего или трассирующий процесс должен иметь UID=0;
  • 2 — для трассирующего процесса должен быть установлен флаг CAP_SYS_PTRACE или потомок устанавливает себе флаг PTRACE_TRACEME;
  • 3 — трассирование запрещено.

Выполнив


docker exec -it <container> cat /proc/sys/kernel/yama/ptrace_scope

мы обнаружили, что в файле установлено значение 1, соответственно, не будучи родственным процессом, strace не мог подключиться к трассируемому процессу. При этом, попытка установить в /proc/sys/kernel/yama/ptrace_scope значение 0 не увенчалась успехом, было получено сообщение о том, что "/proc is read only".


Привилегированный запуск контейнера


Преодолеть данное ограничение нам помог флаг Docker, разрешающий в привилегированное исполнение: --privileged, а в инициализацию приложения мы добавили установку значения "0" в псевдофайл /proc/sys/kernel/yama/ptrace_scope.


echo 0 > /proc/sys/kernel/yama/ptrace_scope

В итоге проблема была успешно решена. Пример решения и аргументы для запуска контейнера можно найти в нашем GitHub репозитории для web ssh прокси.


Было интересно заметить, что ядро Linux получает существенные улучшения безопасности и дополнительные инструменты управления настройками безопасности, о которых достаточно трудно узнать, если не держать постоянно руку "на пульсе" и работать в рамках приложений, которые развиваются в рамках продолжительных жизненных циклов в стабильных средах. В один прекрасный день обнаруживаешь новый удивительный мир улучшений, которые "внезапно" проявили себя благодаря вот такому досадному случаю.
-->


К сожалению, не доступен сервер mySQL