Русский
Русский
English
Статистика
Реклама

Перевод Почему в Docker не работает Strace

Когда я редактировала страницу о возможностях контейнеров для журнала How Containers Work, мне потребовалось объяснить, почему в Docker не работает strace. Вот что случалось при запуске strace в Docker-контейнере на моем ноутбуке:

$ docker run  -it ubuntu:18.04 /bin/bash$ # ... install strace ...root@e27f594da870:/# strace lsstrace: ptrace(PTRACE_TRACEME, ...): Operation not permitted

strace работает через системный вызов ptrace, поэтому без разрешения для ptrace ничего не заработает! Но это легко исправить, и на моем ноутбуке я все сделала вот так:

docker run --cap-add=SYS_PTRACE  -it ubuntu:18.04 /bin/bash

Но мне было интересно не решить проблему, а разобраться, почему эта ситуация вообще возникает. Так почему же strace не работает, а --cap-add=SYS_PTRACE все исправляет?

Гипотеза 1: У процессов контейнеров нет собственной привилегии CAP_SYS_PTRACE


Так как проблема стабильно решается через --cap-add=SYS_PTRACE, мне всегда казалось, что у процессов контейнеров Docker по определению нет собственной привилегии CAP_SYS_PTRACE, но по двум причинам тут кое-что не сходится.

Причина 1: В виде эксперимента я, будучи залогиненной под обычным пользователем, без труда могла запустить strace к любому процессу, однако проверка наличия у моего текущего процесса привилегии CAP_SYS_PTRACE ничего не находила:

$ getpcaps $$Capabilities for `11589': =

Причина 2: в man capabilities о привилегии CAP_SYS_PTRACE сказано следующее:

CAP_SYS_PTRACE       * Trace arbitrary processes using ptrace(2);

Вся суть CAP_SYS_PTRACE заключается в том, чтобы мы, по аналогии с root, могли перехватывать контроль над произвольным процессом любого пользователя. Для ptrace обычного процесса вашего пользователя такая привилегия не нужна.

Кроме того, я провела еще одну проверку: я запустила Docker контейнер через docker run --cap-add=SYS_PTRACE -it ubuntu:18.04 /bin/bash, затем отменила привилегию CAP_SYS_PTRACE и strace продолжил корректно работать даже без привилегии. Почему?!

Гипотеза 2: Дело в пользовательском пространстве имен?


Моя следующая (и намного хуже обоснованная) гипотеза звучала в духе хмм, возможно процесс находится в другом пользовательском пространстве имен и strace не работает просто потому что? Выглядит как набор не очень связных высказываний, но я все же попробовала посмотреть на проблему и с этой стороны.

Итак, находится ли процесс в другом пользовательском пространстве имен? Так он выглядит в контейнере:

root@e27f594da870:/# ls /proc/$$/ns/user -l... /proc/1/ns/user -> 'user:[4026531837]'

А так он выглядит на хосте:

bork@kiwi:~$ ls /proc/$$/ns/user -l... /proc/12177/ns/user -> 'user:[4026531837]'

root в контейнере это тот же самый пользователь, что и root на хосте, поскольку у них общий идентификатор в пользовательском пространстве имен (4026531837), так что с этой стороны не должно быть никаких мешающих работе strace причин. Как можно видеть, гипотеза оказалась так себе, но тогда я еще не осознавала, что пользователи в контейнере и на хосте совпадают, и этот поход казался мне интересным.

Гипотеза 3: Системный вызов ptrace блокируется правилом seccomp-bpf


Я уже знала, что для ограничения запуска большого числа системных вызовов процессорами контейнеров в Docker есть правило seccomp-bpf, и оказалось, что в его списке блокируемых по определению вызовов есть и ptrace! (На самом деле список вызовов это лист исключений и ptrace попросту в него не попадает, но результат от этого не меняется.)

Теперь понятно, почему в Docker контейнере не работает strace, ведь очевидно, что полностью заблокированный ptrace вызвать не получится.

Давайте проверим эту гипотезу и посмотрим, сможем ли мы воспользоваться strace в Docker контейнере, если отключим все правила seccomp:

$ docker run --security-opt seccomp=unconfined -it ubuntu:18.04  /bin/bash$ strace lsexecve("/bin/ls", ["ls"], 0x7ffc69a65580 /* 8 vars */) = 0... it works fine ...

Отлично! Все работает, и секрет раскрыт! Вот только

Почему --cap-add=SYS_PTRACE решает проблему?


Мы все еще не объяснили почему --cap-add=SYS_PTRACE решает возникающую проблему с вызовами. Главная страница docker run следующим образом объясняет работу аргумента --cap-add:

--cap-add=[]   Add Linux capabilities

Все это не имеет никакого отношения к правилам seccomp! В чем же дело?

Давайте посмотрим на исходный код Docker.


Если уже и документация не помогает, все что нам остается это погрузиться в исходники.
В Go есть одна приятная особенность: благодаря вендорингу зависимостей в репозитории Go вы через grep можете пройтись по всему репозиторию и найти интересующий вас код. Так что я склонировала github.com/moby/moby и прошерстила его в поисках выражений вида rg CAP_SYS_PTRACE.

На мой взгляд, тут происходит вот что: в имплементации seccomp в контейнере, в разделе contrib/seccomp/seccomp_default.go полно кода, который через правило seccomp проверяет, есть ли у процесса с привилегиями разрешение на использование системных вызовов в соответствии с этой привилегией.

case "CAP_SYS_PTRACE":s.Syscalls = append(s.Syscalls, specs.LinuxSyscall{Names: []string{"kcmp","process_vm_readv","process_vm_writev","ptrace",},Action: specs.ActAllow,Args:   []specs.LinuxSeccompArg{},})


Там еще есть код, который в moby и для profiles/seccomp/seccomp.go, и для seccomp профиля по определению проводит похожие операции, так что, наверное, мы нашли наш ответ!

В Docker --cap-add способен на большее, чем сказано


В итоге, похоже, --cap-add делает не совсем то, что написано на главной странице, и скорее должен выглядеть как --cap-add-and-also-whitelist-some-extra-system-calls-if-required. И это похоже на правду: если у вас есть привилегия в духе CAP_SYS_PTRACE, которая разрешает вам пользоваться системным вызовом process_vm_readv, но этот вызов блокируется профилем seccomp, вам это мало чем поможет, так что разрешение на использование системных вызовов process_vm_readv и ptrace через CAP_SYS_PTRACE выглядит разумно.

Оказывается, strace работает в последних версиях Docker


Для версий ядра 4.8 и выше благодаря этому коммиту в Docker 19.03 наконец-то разрешены системные вызовы ptrace. Вот только на моем ноутбуке Docker все еще версии 18.09.7, и этот коммит, очевидно, отсутствует.

Вот и все!


Разбираться с этой проблемой оказалось интересно, и я думаю, что это хороший пример нетривиально взаимодействующей между собой подвижной начинки контейнеров.

Если вам понравился этот пост, вам может понравиться и мой журнал How Containers Work, на его 24 страницах объясняются особенности ядра Linux по организации работы контейнеров. Там же вы можете ознакомиться с привилегиями и seccomp-bpf.
Источник: habr.com
К списку статей
Опубликовано: 02.07.2020 10:04:35
0

Сейчас читают

Комментариев (0)
Имя
Электронная почта

Блог компании itsumma

Настройка linux

Системное администрирование

Виртуализация

Itsumma

Docker

Strace

Ptrace

Процессы

Пользователи

Под капотом

Категории

Последние комментарии

© 2006-2020, personeltest.ru