Введение
Docker – мощный инструмент для контейнеризации приложений, и понимание того, как управлять возможностями контейнеров, имеет решающее значение для оптимизации безопасности и производительности. Этот учебник проведет вас через процесс добавления и удаления возможностей с помощью команд Docker, помогая вам адаптировать среду контейнера к конкретным потребностям.
В этой лабораторной работе вы узнаете, что такое возможности Docker (Docker capabilities), как они повышают безопасность контейнеров и как эффективно ими управлять. К концу этого учебника вы сможете уверенно добавлять и удалять возможности из своих контейнеров Docker.
Понимание возможностей Docker (Docker Capabilities)
Возможности Docker (Docker capabilities) – это функция безопасности, которая позволяет предоставлять или отзывать определенные разрешения ядра Linux для контейнера. Прежде чем мы начнем экспериментировать с возможностями, давайте разберемся, что они собой представляют и почему они важны.
Что такое возможности Docker (Docker Capabilities)?
Возможности в Docker основаны на системе возможностей ядра Linux, которая разделяет привилегии, традиционно связанные с пользователем root, на отдельные единицы. Этот подход более безопасен, чем традиционная модель привилегий root «все или ничего».
По умолчанию контейнеры Docker запускаются с ограниченным набором возможностей, обеспечивая разумный баланс между функциональностью и безопасностью. Однако вам может потребоваться добавить или удалить возможности в зависимости от требований вашего приложения.
Давайте рассмотрим возможности Docker контейнера по умолчанию. Выполните следующую команду, чтобы запустить контейнер и просмотреть его возможности:
docker run --rm -it ubuntu:22.04 capsh --print
Вы должны увидеть вывод, похожий на этот:
Current: cap_chown,cap_dac_override,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_net_bind_service,cap_net_raw,cap_sys_chroot,cap_mknod,cap_audit_write,cap_setfcap=ep
Bounding set =cap_chown,cap_dac_override,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_net_bind_service,cap_net_raw,cap_sys_chroot,cap_mknod,cap_audit_write,cap_setfcap
Ambient set =
Current IAB: cap_chown,cap_dac_override,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_net_bind_service,cap_net_raw,cap_sys_chroot,cap_mknod,cap_audit_write,cap_setfcap=ep
Securebits: 00/0x0/1'b0
secure-noroot: no (unlocked)
secure-no-suid-fixup: no (unlocked)
secure-keep-caps: no (unlocked)
secure-no-ambient-raise: no (unlocked)
uid=0(root) gid=0(root) groups=0(root)
Этот вывод показывает возможности, предоставленные контейнеру по умолчанию. Эти возможности контролируют, что контейнер может делать в системе.
Почему важны возможности
Правильное управление возможностями Docker имеет решающее значение для:
- Повышенной безопасности: Ограничивая возможности, вы уменьшаете потенциальный ущерб, если контейнер будет взломан.
- Детального контроля: Вы можете разрешить определенные привилегированные операции, не предоставляя полный доступ root.
- Принципа наименьших привилегий: Контейнеры должны иметь только те возможности, которые им необходимы для правильной работы.
Далее давайте рассмотрим, как добавить возможности в контейнеры Docker.
Добавление возможностей в контейнеры Docker
На этом шаге мы узнаем, как добавить определенные возможности в контейнер Docker, используя флаг --cap-add. Это полезно, когда вашему приложению требуются определенные привилегии, которые не включены в набор по умолчанию.
Основной синтаксис для добавления возможностей
Основной синтаксис для добавления возможности в контейнер Docker:
docker run --cap-add=<CAPABILITY> <IMAGE> <COMMAND>
Давайте попробуем добавить возможность NET_ADMIN, которая позволяет контейнеру выполнять различные сетевые операции:
docker run --rm -it --cap-add=NET_ADMIN ubuntu:22.04 capsh --print
Вывод покажет, что возможность NET_ADMIN была добавлена в контейнер:
Current: cap_chown,cap_dac_override,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_net_bind_service,cap_net_raw,cap_sys_chroot,cap_mknod,cap_audit_write,cap_setfcap,cap_net_admin=ep
Обратите внимание на добавление cap_net_admin в конце списка возможностей.
Добавление нескольких возможностей
Часто вам может потребоваться добавить несколько возможностей в контейнер. Это можно сделать, указав флаг --cap-add несколько раз:
docker run --rm -it --cap-add=NET_ADMIN --cap-add=SYS_TIME ubuntu:22.04 capsh --print
Эта команда добавляет в контейнер возможности NET_ADMIN и SYS_TIME. Возможность SYS_TIME позволяет контейнеру изменять системные часы.
Практический пример: изменение сетевых интерфейсов
Чтобы продемонстрировать практическое использование возможностей, давайте создадим контейнер с возможностью NET_ADMIN и попытаемся изменить настройки сетевого интерфейса:
docker run --rm -it --cap-add=NET_ADMIN ubuntu:22.04 /bin/bash
Теперь вы находитесь внутри контейнера с оболочкой bash. Давайте установим пакет iproute2 для работы с сетевыми интерфейсами:
apt-get update && apt-get install -y iproute2
Теперь попробуйте создать фиктивный сетевой интерфейс:
ip link add dummy0 type dummy
ip link show dummy0
Вы должны увидеть вывод, показывающий вновь созданный фиктивный сетевой интерфейс:
6: dummy0: <BROADCAST,NOARP> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ether 2a:d5:cd:70:91:f4 brd ff:ff:ff:ff:ff:ff
Эта операция завершилась бы неудачей без возможности NET_ADMIN. Вы можете выйти из контейнера, набрав exit.
Просмотр возможностей контейнера
Чтобы проверить возможности запущенного контейнера, вы можете использовать команду docker inspect. Сначала давайте запустим контейнер с добавленными возможностями в detached mode:
docker run -d --name cap-test --cap-add=NET_ADMIN ubuntu:22.04 sleep 3600
Теперь проверьте контейнер, чтобы просмотреть его возможности:
docker inspect cap-test | grep -A 20 CapAdd
Вывод покажет, что возможность NET_ADMIN была добавлена:
"CapAdd": [
"NET_ADMIN"
],
Не забудьте очистить после этого шага:
docker stop cap-test
docker rm cap-test
Понимание того, как добавлять возможности в контейнеры Docker, дает вам больше контроля над тем, что могут делать ваши контейнеры, сохраняя при этом безопасность.
Удаление возможностей из контейнеров Docker
На этом шаге мы узнаем, как удалять возможности из контейнеров Docker, используя флаг --cap-drop. Это важная практика безопасности, которая следует принципу наименьших привилегий – контейнеры должны иметь только те возможности, которые им абсолютно необходимы.
Основной синтаксис для удаления возможностей
Основной синтаксис для удаления возможности из контейнера Docker:
docker run --cap-drop=<CAPABILITY> <IMAGE> <COMMAND>
Давайте попробуем удалить возможность CHOWN, которая позволяет изменять владельца файла:
docker run --rm -it --cap-drop=CHOWN ubuntu:22.04 capsh --print
В выводе вы заметите, что cap_chown больше не указана среди возможностей:
Current: cap_dac_override,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_net_bind_service,cap_net_raw,cap_sys_chroot,cap_mknod,cap_audit_write,cap_setfcap=ep
Удаление нескольких возможностей
Вы можете удалить несколько возможностей, указав флаг --cap-drop несколько раз:
docker run --rm -it --cap-drop=CHOWN --cap-drop=NET_RAW ubuntu:22.04 capsh --print
Эта команда удаляет из контейнера возможности CHOWN и NET_RAW.
Практический пример: тестирование ограничений возможностей
Давайте создадим контейнер с удаленной возможностью CHOWN и попытаемся изменить владельца файла:
docker run --rm -it --cap-drop=CHOWN ubuntu:22.04 /bin/bash
Внутри контейнера давайте создадим тестовый файл и попытаемся изменить его владельца:
touch test_file
ls -l test_file
chown nobody:nogroup test_file
Вы должны увидеть сообщение об ошибке, указывающее на то, что операция не разрешена:
chown: changing ownership of 'test_file': Operation not permitted
Это демонстрирует, что удаление возможности CHOWN не позволяет контейнеру изменять владельца файла, даже если контейнер работает от имени root. Введите exit, чтобы выйти из контейнера.
Использование одновременно --cap-add и --cap-drop
Вы можете использовать флаги --cap-add и --cap-drop в одной и той же команде, чтобы точно контролировать возможности вашего контейнера:
docker run --rm -it --cap-add=NET_ADMIN --cap-drop=CHOWN ubuntu:22.04 capsh --print
Эта команда добавляет возможность NET_ADMIN, удаляя при этом возможность CHOWN.
Удаление всех возможностей и добавление определенных
Для максимальной безопасности вы можете удалить все возможности, а затем добавить только те, которые необходимы вашему приложению:
docker run --rm -it --cap-drop=ALL --cap-add=NET_BIND_SERVICE ubuntu:22.04 capsh --print
Эта команда создает контейнер только с возможностью NET_BIND_SERVICE, которая позволяет привязываться к привилегированным портам (ниже 1024).
Тестирование контейнера со всеми удаленными возможностями
Давайте создадим контейнер со всеми удаленными возможностями и понаблюдаем за ограничениями:
docker run -d --name no-caps --cap-drop=ALL ubuntu:22.04 sleep 3600
Теперь давайте подключимся к контейнеру и попытаемся выполнить различные операции:
docker exec -it no-caps /bin/bash
Внутри контейнера попробуйте пропинговать внешний хост:
apt-get update && apt-get install -y iputils-ping
ping -c 1 google.com
Вы, вероятно, увидите ошибку, потому что у контейнера нет необходимых возможностей для создания необработанных сетевых сокетов, необходимых для ping.
Выйдите из контейнера, набрав exit, а затем очистите:
docker stop no-caps
docker rm no-caps
Понимая, как удалять возможности из контейнеров Docker, вы можете значительно повысить безопасность ваших контейнерных приложений, строго ограничивая то, что может делать каждый контейнер.
Объединение управления возможностями с лучшими практиками безопасности
На этом заключительном шаге мы рассмотрим, как объединить управление возможностями Docker с другими лучшими практиками безопасности для создания безопасных контейнеров с минимальными привилегиями.
Создание контейнера с профилем пользовательских возможностей
Давайте создадим более сложный пример, который объединяет управление возможностями с другими функциями безопасности:
docker run -d --name secure-container \
--cap-drop=ALL \
--cap-add=NET_BIND_SERVICE \
--read-only \
--tmpfs /tmp \
ubuntu:22.04 sleep 3600
Эта команда:
- Удаляет все возможности
- Добавляет только возможность
NET_BIND_SERVICE - Делает файловую систему контейнера доступной только для чтения
- Создает временную файловую систему в
/tmpдля операций записи
Давайте проверим этот контейнер, чтобы увидеть его конфигурацию:
docker inspect secure-container | grep -A 5 CapAdd
docker inspect secure-container | grep -A 5 CapDrop
docker inspect secure-container | grep ReadonlyRootfs
Вы должны увидеть вывод, подтверждающий эти параметры безопасности:
"CapAdd": [
"NET_BIND_SERVICE"
],
"CapDrop": [
"ALL"
],
"ReadonlyRootfs": true,
Тестирование ограничений
Давайте подключимся к нашему безопасному контейнеру и протестируем ограничения:
docker exec -it secure-container /bin/bash
В контейнере попробуйте изменить системный файл:
echo "test" > /etc/test
Вы должны увидеть ошибку, потому что файловая система доступна только для чтения:
bash: /etc/test: Read-only file system
Теперь попробуйте записать в каталог /tmp:
echo "test" > /tmp/test
cat /tmp/test
Это должно сработать, потому что мы смонтировали записываемый tmpfs в /tmp:
test
Выйдите из контейнера, набрав exit.
Использование возможностей с пользователями, не являющимися root
Для дополнительной безопасности вы можете запускать контейнеры от имени пользователей, не являющихся root, при этом управляя возможностями. Сначала давайте создадим новый контейнер, который объединяет пользователя, не являющегося root, с определенными возможностями:
docker run -d --name nonroot-container \
--cap-drop=ALL \
--cap-add=NET_BIND_SERVICE \
--user 1000:1000 \
ubuntu:22.04 sleep 3600
Обратите внимание, что даже если мы добавили возможность NET_BIND_SERVICE и работаем от имени пользователя, не являющегося root, возможности Linux по умолчанию применяются только к процессам, работающим от имени root. Чтобы позволить пользователям, не являющимся root, использовать возможности, потребуются дополнительные конфигурации, такие как двоичные файлы setuid или ambient capabilities.
Возможности в docker-compose
Если вы используете docker-compose для управления несколькими контейнерами, вы можете указать возможности в своем файле docker-compose.yml:
version: "3"
services:
webapp:
image: ubuntu:22.04
cap_drop:
- ALL
cap_add:
- NET_BIND_SERVICE
read_only: true
tmpfs:
- /tmp
Это обеспечивает последовательный способ управления возможностями в ваших развертываниях контейнеров.
Очистка
Давайте очистим созданные нами контейнеры:
docker stop secure-container nonroot-container
docker rm secure-container nonroot-container
Сводка лучших практик
Вот некоторые лучшие практики для управления возможностями Docker:
- Удалите все возможности и добавляйте только то, что вам нужно
- Объедините управление возможностями с другими функциями безопасности:
- Файловая система только для чтения
- Пользователи, не являющиеся root
- Профили Seccomp
- AppArmor или SELinux
- Регулярно проверяйте возможности контейнера
- Поддерживайте Docker и образы контейнеров в актуальном состоянии
- Используйте инструменты сканирования уязвимостей контейнеров
Следуя этим практикам, вы можете значительно повысить безопасность ваших контейнеров Docker.
Резюме
В этой лабораторной работе вы узнали, как эффективно управлять возможностями контейнеров Docker для повышения безопасности и функциональности. Вот краткое изложение того, что вы сделали:
- Вы поняли, что такое возможности Docker и почему они важны для безопасности контейнеров.
- Вы узнали, как добавлять возможности в контейнеры, используя флаг
--cap-add, что позволяет контейнерам выполнять определенные привилегированные операции. - Вы попрактиковались в удалении возможностей с помощью флага
--cap-drop, реализуя принцип наименьших привилегий. - Вы изучили лучшие практики объединения управления возможностями с другими функциями безопасности для создания безопасных контейнерных сред.
Применяя эти методы, вы можете создавать контейнеры, которые имеют именно те разрешения, которые им нужны, не больше и не меньше. Этот подход значительно уменьшает потенциальную поверхность атаки ваших контейнерных приложений, обеспечивая при этом наличие необходимой функциональности для правильной работы.
Продолжайте изучать функции безопасности Docker и не забывайте регулярно проверять и обновлять конфигурации ваших контейнеров, чтобы поддерживать надежную систему безопасности в ваших контейнерных средах.



