Методы и примеры
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
при запуске контейнера.