Docker: user namespaces

С самого начала пользователей Докера, обеспокоенных вопросами безопасности, волновал тот факт, что внутри контейнеров идентификаторы пользователей и групп начинаются с нуля. Иными словами, root в контейнере равносилен root-у во внешней системе. И даже нашлись сценарии (не самые тривиальные, конечно), в которых root выбирается наружу.

В Docker 1.10 добавили поддержку так называемых user namespaces, которые призваны исправить эту ситуацию.

Нужно ли скорее бежать и включать их у себя?

Не спешите! Во всей этой истории предполагается, что зловредный пользователь контейнера хочет атаковать внешнюю систему. Такое возможно в сценариях хостинга и аренды. Если же вы используете Docker сами и на своих серверах, то, конечно же, не будете этого делать, а наоборот будете соблюдать правила безопасности внутри контейнеров. “Внутренний” root заслуживает такого же бережного обращения как и внешний.

Внимание: глобальная настройка!

User namespaces — не та опция, с которой можно легко побаловаться. Из-за технических особенностей (а именно — из-за желания избежать копирования больших объемов данных и выставления этим копиям chown) включение, выключение или изменение настроек user namespaces предполагает рестарт Docker engine, повторное вытягивание всех образов и, как следствие, запуск с чистого листа всех контейнеров.

Как это работает?

Итак, полезли в технические дебри.

В современных Линуксах для всех пользователей и групп, чьи идентификаторы попадают в интервалы UID_MIN - UID_MAX и GID_MIN - GID_MAX соответственно, выделяются диапазоны из SUB_UID_COUNT и SUB_GID_COUNT штук так называемых subordinate-идентификаторов. Они записываются в файлы /etc/subuid и /etc/subgid, и принимают значения от SUB_UID_MIN до SUB_UID_MAX и от SUB_GID_MIN до SUB_GID_MAX. Кто не понял — я не виноват 😃

Значения констант можно подсмотреть в man useradd.

Вот так может выглядеть файл /etc/subuid:

user1:100000:65536
user2:165536:65536

Для включения поддержки user namespaces Docker запускается с параметром --userns-remap=user (если user опустить или задать в default, то будет создана специальная учетная запись с именем dockremap).

Суть: subuid и subgid этого пользователя будут прибавляться ко всем uid и gid внутри контейнеров.

Папка с данными Докера переедет из /var/lib/docker в /var/lib/docker/(subuid):(subgid).

root внутри контейнеров для внешней системы будет выглядеть как subordinate-пользователь, и возможность “рутования” наружу будет предотвращена. (Ура!)

Ограничения и особенности

User namespaces несовместимы с некоторыми другими настройками (см. User namespace known limitations) и осложняют монтирование внешних директорий (см. обсуждение на StackOverflow).