- PVSM.RU - https://www.pvsm.ru -
Когда я редактировала страницу о возможностях контейнеров для журнала «How Containers Work [1]», мне потребовалось объяснить, почему в Docker не работает strace
. Вот что случалось при запуске strace
в Docker-контейнере на моем ноутбуке:
$ docker run -it ubuntu:18.04 /bin/bash
$ # ... install strace ...
root@e27f594da870:/# strace ls
strace: 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
все исправляет?
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
продолжил корректно работать даже без привилегии. Почему?!
Моя следующая (и намного хуже обоснованная) гипотеза звучала в духе «хмм, возможно процесс находится в другом пользовательском пространстве имен и 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
причин. Как можно видеть, гипотеза оказалась так себе, но тогда я еще не осознавала, что пользователи в контейнере и на хосте совпадают, и этот поход казался мне интересным.
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 ls
execve("/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! В чем же дело?
Если уже и документация не помогает, все что нам остается это погрузиться в исходники.
В Go есть одна приятная особенность: благодаря вендорингу зависимостей в репозитории Go вы через grep
можете пройтись по всему репозиторию и найти интересующий вас код. Так что я склонировала github.com/moby/moby
и прошерстила его в поисках выражений вида rg CAP_SYS_PTRACE
.
На мой взгляд, тут происходит вот что: в имплементации seccomp в контейнере, в разделе contrib/seccomp/seccomp_default.go [2] полно кода, который через правило 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 [3], и для seccomp профиля по определению [4] проводит похожие операции, так что, наверное, мы нашли наш ответ!
--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 и выше благодаря этому коммиту [5] в Docker 19.03 наконец-то разрешены системные вызовы ptrace
. Вот только на моем ноутбуке Docker все еще версии 18.09.7, и этот коммит, очевидно, отсутствует.
Разбираться с этой проблемой оказалось интересно, и я думаю, что это хороший пример нетривиально взаимодействующей между собой подвижной «начинки» контейнеров.
Если вам понравился этот пост, вам может понравиться и мой журнал «How Containers Work [6]», на его 24 страницах объясняются особенности ядра Linux по организации работы контейнеров. Там же вы можете ознакомиться с привилегиями [7] и seccomp-bpf [8].
Автор: ITSumma
Источник [9]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/virtualizatsiya/354596
Ссылки в тексте:
[1] How Containers Work: https://wizardzines.com/zines/containers/
[2] contrib/seccomp/seccomp_default.go: https://github.com/containerd/containerd/blob/4be98fa28b62e8a012491d655a4d6818ef87b080/contrib/seccomp/seccomp_default.go#L527-L537
[3] profiles/seccomp/seccomp.go: https://github.com/moby/moby/blob/cc0dfb6e7b22ad120c60a9ce770ea15415767cf9/profiles/seccomp/seccomp.go#L126-L132
[4] seccomp профиля по определению: https://github.com/moby/moby/blob/master/profiles/seccomp/default.json#L723-L739
[5] этому коммиту: https://github.com/moby/moby/commit/1124543ca8071074a537a15db251af46a5189907
[6] How Containers Work: https://wizardzines.com/zines/containers
[7] привилегиями: https://wizardzines.com/comics/capabilities/
[8] seccomp-bpf: https://wizardzines.com/comics/seccomp-bpf/
[9] Источник: https://habr.com/ru/post/508990/?utm_source=habrahabr&utm_medium=rss&utm_campaign=508990
Нажмите здесь для печати.