Эти утилиты получают информацию о текущих входах в систему из файла utmp (/var/run/utmp
). Вы можете легко проверить, что в обычных обстоятельствах (например, в настольной системе) этот файл содержит что-то вроде следующей строки (здесь qazer
- это мой логин, а tty7
- это TTY, где работает моя среда рабочего стола):
$ cat /var/run/utmp
tty7:0qazer:0�o^�
в то время как в контейнере этот файл ( обычно ) пуст:
$ docker run -it centos
[root@5e91e9e1a28e /]# cat /var/run/utmp
[root@5e91e9e1a28e /]#
Почему?
Файл utmp
обычно изменяется программами, которые аутентифицировать пользователя и запустить сеанс: login(1)
, sshd(8)
, lightdm(1)
. Однако механизм контейнеров не может полагаться на них, так как они могут отсутствовать в файловой системе контейнера, поэтому «вход в систему» и «выполнение от имени» реализованы самым примитивным и простым способом, избегая полагаться на что-либо внутри контейнера.
Когда какой-либо контейнер запущен или в нем есть какая-либо команда exec
d, механизм контейнера just порождает новый процесс, организует некоторые настройки безопасности, вызывает setgid(2)
/ setuid(2)
, чтобы принудительно (без какой-либо аутентификации) изменить UID / GID процесса, а затем выполнить необходимый двоичный файл (точку входа, команду и т. Д.) В этом процессе.
Сказать , Я запускаю контейнер CentOS, запускающий его основной процесс от имени UID 42
:
docker run -it --user 42 centos
, а затем пытаюсь выполнить sleep 1000
внутри него:
docker exec -it $CONTAINER_ID sleep 1000
Механизм контейнера будет выполнять что-то вроде этого:
[pid 10170] setgid(0) = 0
[pid 10170] setuid(42) = 0
...
[pid 10170] execve("/usr/bin/sleep", ["sleep", "1000"], 0xc000159740 /* 4 vars */) = 0
Не будет никаких записей в /var/run/utmp
, поэтому он останется пустым, и who(1)
/ w(1)
не найдет внутри себя никаких логинов контейнер.