Методы и примеры

TL;DR

Лучшая практика при запуске контейнера - запускать процесс с пользователем без полномочий root. Обычно это делается с помощью USER instruction в Dockerfile. Но если эта инструкция отсутствует, это не обязательно означает, что процесс запущен от имени пользователя root.

Обоснование

По умолчанию корень в контейнере - тот же корень (uid 0), что и на хост-машине. Если пользователю удается вырваться из приложения, запущенного в контейнере с правами root, он может получить доступ к хосту с тем же пользователем root. Этот доступ было бы еще проще получить, если бы контейнер был запущен с неправильными флагами или с привязкой монтирования папок хоста в R / W.

Запуск контейнера MongoDB

Если вы еще этого не знаете, я настоятельно рекомендую попробовать Play With Docker. Также известная как PWD, это онлайн-площадка, где вы можете протестировать все новейшие функции Docker без необходимости устанавливать что-либо локально. Оказавшись в PWD, вы можете создать экземпляр, и вы будете чувствовать себя так, как будто находитесь в оболочке Linux VM.

Примечание. Под капотом у вас будет оболочка, но в контейнере Alpine, в котором установлен демон Docker. Это то, что называется DinD для Docker в Docker, поскольку демон Docker запускается в контейнере.

Попав в терминал, запустим контейнер на основе образа MongoDB:

[node1] (local) [email protected] ~
$ docker container run -d -p 27017:27017 --name mongo mongo:4.0
8cce38822a23bbacb5349c5af63c50f1d2e371029f5b6332b1144fcc4f8cb723

И проверьте с хост-машины, какой пользователь запускает mongod process:

[node1] (local) [email protected] ~
$ ps aux | grep mongo
 1143 999 0:00 mongod --bind_ip_all

Из выходных данных выше мы видим, что пользователь, обозначенный uid 999, является владельцем процесса mongod. Давайте проверим существующих пользователей на хосте:

$ cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
news:x:9:13:news:/usr/lib/news:/sbin/nologin
uucp:x:10:14:uucp:/var/spool/uucppublic:/sbin/nologin
operator:x:11:0:operator:/root:/bin/sh
man:x:13:15:man:/usr/man:/sbin/nologin
postmaster:x:14:12:postmaster:/var/spool/mail:/sbin/nologin
cron:x:16:16:cron:/var/spool/cron:/sbin/nologin
ftp:x:21:21::/var/lib/ftp:/sbin/nologin
sshd:x:22:22:sshd:/dev/null:/sbin/nologin
at:x:25:25:at:/var/spool/cron/atjobs:/sbin/nologin
squid:x:31:31:Squid:/var/cache/squid:/sbin/nologin
xfs:x:33:33:X Font Server:/etc/X11/fs:/sbin/nologin
games:x:35:35:games:/usr/games:/sbin/nologin
postgres:x:70:70::/var/lib/postgresql:/bin/sh
cyrus:x:85:12::/usr/cyrus:/sbin/nologin
vpopmail:x:89:89::/var/vpopmail:/sbin/nologin
ntp:x:123:123:NTP:/var/empty:/sbin/nologin
smmsp:x:209:209:smmsp:/var/spool/mqueue:/sbin/nologin
guest:x:405:100:guest:/dev/null:/sbin/nologin
nobody:x:65534:65534:nobody:/:/sbin/nologin
dockremap:x:100:101:Linux User,,,:/home/dockremap:/bin/false

Нет пользователя с uid 999, поэтому имя пользователя не может быть сопоставлено с этим uid в предыдущей команде.

Dockerfile

Это Dockerfile, используемый для создания образа MongoDB 4.0:



В этом файле нет инструкции USER, но мы видим, что новый mongodb user создается в образе и добавляется в mongodb group, созданную одновременно. Для этого используется следующая инструкция:

RUN groupadd -r mongodb && useradd -r -g mongodb mongodb

Поскольку это не указано в инструкции USER в файле Dockerfile, этот пользователь не используется во время создания образа; все делается с рутом.

Но если мы внимательно посмотрим на конец Dockerfile, мы увидим как ENTRYPOINT , так и CMD инструкции.

ENTRYPOINT ["docker-entrypoint.sh"]
CMD ["mongod"]

Как вы, наверное, знаете, объединение этих двух инструкций определяет команду, которая запускается при запуске контейнера из образа mongo. Тогда команда будет следующей:

$ docker-entrypoint.sh mongod

ВХОД

Давайте теперь посмотрим на код docker-entrypoint.sh file:



Следующий фрагмент кода в начале файла очень интересен. Это та часть, где пользователь, выполняющий процесс, изменяется с root на mongodb благодаря gosu utility.

# allow the container to be started with ` — user
# all mongo* commands should be dropped to the correct user
if [[ “$originalArgOne” == mongo* ]] && [ “$(id -u)” = ‘0’ ]; then
    if [ “$originalArgOne” = ‘mongod’ ];
        then chown -R mongodb /data/configdb /data/db
    fi 
    # make sure we can write to stdout and stderr as “mongodb”
    # (for our “initdb” code later; see “ — logpath” below)
    chown --dereference mongodb “/proc/$$/fd/1” “/proc/$$/fd/2” || :
    exec gosu mongodb “$BASH_SOURCE” “$@”
fi

Примечание. в файле Dockerfile мы видим, что gosu utility входит в число пакетов, установленных при создании образа.

Изображение на основе Ubuntu

Первая инструкция в Dockerfile указывает, что ubuntu: xenial - это базовый образ, образ, из которого создается образ mongo.

Давайте запустим интерактивный контейнер на основе Ubuntu и перечислим существующих пользователей:

$ docker container run -ti ubuntu:xenial

root@9e367c3d9ca1:/# cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
systemd-timesync:x:100:102:systemd Time Synchronization,,,:/run/systemd:/bin/false
systemd-network:x:101:103:systemd Network Management,,,:/run/systemd/netif:/bin/false
systemd-resolve:x:102:104:systemd Resolver,,,:/run/systemd/resolve:/bin/false
systemd-bus-proxy:x:103:105:systemd Bus Proxy,,,:/run/systemd:/bin/false
_apt:x:104:65534::/nonexistent:/bin/false

Теперь давайте создадим фиктивного пользователя и группу:

root@9e367c3d9ca1:/# groupadd -r mygrp && useradd -r -g mygrp myuser

и еще раз перечислим пользователей:

root@9e367c3d9ca1:/# cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
systemd-timesync:x:100:102:systemd Time Synchronization,,,:/run/systemd:/bin/false
systemd-network:x:101:103:systemd Network Management,,,:/run/systemd/netif:/bin/false
systemd-resolve:x:102:104:systemd Resolver,,,:/run/systemd/resolve:/bin/false
systemd-bus-proxy:x:103:105:systemd Bus Proxy,,,:/run/systemd:/bin/false
_apt:x:104:65534::/nonexistent:/bin/false
myuser:x:999:999::/home/myuser:

Мы можем видеть нового пользователя, созданного как uid 999, который является uid первого пользователя, созданного из свежего ubuntu:xenial изображения. Этот uid используется для запуска mongod process, как мы видели ранее. Как напоминание:

[node1] (local) [email protected] ~
$ ps aux | grep mongo
 1143 999 0:00 mongod --bind_ip_all

Изображение на основе Alpine

Образы приложений не обязательно основаны на ubuntu:xenial.. Многие из них основаны на Alpine (крошечный дистрибутив, ориентированный на безопасность).

Давайте добавим нового пользователя из свежего контейнера alpine и проверим его uid.

$ docker container run -ti alpine:3.8
/ # adduser -D myuser
/ # cat /etc/passwd
root:x:0:0:root:/root:/bin/ash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
news:x:9:13:news:/usr/lib/news:/sbin/nologin
uucp:x:10:14:uucp:/var/spool/uucppublic:/sbin/nologin
operator:x:11:0:operator:/root:/bin/sh
man:x:13:15:man:/usr/man:/sbin/nologin
postmaster:x:14:12:postmaster:/var/spool/mail:/sbin/nologin
cron:x:16:16:cron:/var/spool/cron:/sbin/nologin
ftp:x:21:21::/var/lib/ftp:/sbin/nologin
sshd:x:22:22:sshd:/dev/null:/sbin/nologin
at:x:25:25:at:/var/spool/cron/atjobs:/sbin/nologin
squid:x:31:31:Squid:/var/cache/squid:/sbin/nologin
xfs:x:33:33:X Font Server:/etc/X11/fs:/sbin/nologin
games:x:35:35:games:/usr/games:/sbin/nologin
postgres:x:70:70::/var/lib/postgresql:/bin/sh
cyrus:x:85:12::/usr/cyrus:/sbin/nologin
vpopmail:x:89:89::/var/vpopmail:/sbin/nologin
ntp:x:123:123:NTP:/var/empty:/sbin/nologin
smmsp:x:209:209:smmsp:/var/spool/mqueue:/sbin/nologin
guest:x:405:100:guest:/dev/null:/sbin/nologin
nobody:x:65534:65534:nobody:/:/sbin/nologin
myuser:x:1000:1000:Linux User,,,:/home/myuser:

Как мы видим здесь, идентификатор первого пользователя в alpine image равен 1000, что отличается от uid 999 ubuntu image. Если мы добавим пользователя в alpine image и запустим процесс с этим пользователем (например, используя инструкцию USER в Dockerfile), мы увидим uid 1000 как владельца процесса. Давай попробуем.

Давайте воспользуемся простым файлом Dockerfile, который добавляет пользователя к Alpine image и определяет базовую команду sleep 1000.

FROM alpine:3.8
RUN adduser -D myuser
USER myuser
ENTRYPOINT [“sleep”]
CMD [“1000”]

Создадим из него изображение:

$ docker image build -t sleep:1.0 .
Sending build context to Docker daemon 1.775MB
Step 1/5 : FROM alpine:3.8
3.8: Pulling from library/alpine
4fe2ade4980c: Pull complete
Digest: sha256:621c2f39f8133acb8e64023a94dbdf0d5ca81896102b9e57c0dc184cadaf5528
Status: Downloaded newer image for alpine:3.8
 — -> 196d12cf6ab1
Step 2/5 : RUN adduser -D myuser
 — -> Running in a7474167f27d
Removing intermediate container a7474167f27d
 — -> 7a17f0862780
Step 3/5 : USER myuser
 — -> Running in b0a7eea711a4
Removing intermediate container b0a7eea711a4
 — -> d63533ce5be1
Step 4/5 : ENTRYPOINT [“sleep”]
 — -> Running in f0dfc3ea4495
Removing intermediate container f0dfc3ea4495
 — -> 763dd8ac4f40
Step 5/5 : CMD [“1000”]
 — -> Running in 14db1ea262f9
Removing intermediate container 14db1ea262f9
 — -> 978294e76184
Successfully built 978294e76184
Successfully tagged sleep:1.0

А затем запустите контейнер из вновь созданного образа:

[node1] (local) [email protected] ~
$ docker container run -d sleep:1.0
534e340780a89b3a86917aff2c20405dadbd7d50cfe5cb03e9cb6786a0517f21

Если мы проверим владельца sleep process на хосте, мы увидим, что он принадлежит пользователю с uid 1000, тому, который создан в образе.

[node1] (local) [email protected] ~
$ ps aux | grep sleep
 1181 1000 0:00 sleep 1000

Резюме

Я надеюсь, что эти примеры помогут вам понять некоторые способы запуска контейнеров с пользователем без полномочий root, либо через образ USER instruction в Dockerfile, либо путем изменения пользователя во время выполнения (обычно это делается в entrypoint script). Другой способ, который мы здесь не рассмотрели, - это использование флага --user при запуске контейнера.