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

logo

В последние два года мы широко используем 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

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

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

  2. 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”.

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

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