Безопасность Docker

Привет всем

Повышаете ли вы как-то безопасность докер контейнеров? Например из того, что известно мне:

security_opt: no-new-privileges - запрещает процессам повышать приведении внутри контейнера (типо nosuid). Обычно не ломает работу приложения

cap_drop: ALL - лишает контейнер всех системных привелегий. Вот тут я не разобрался. Не использую его. Всегда что-то не рабоает

user: 1000:1000 (как пример) - запускает контейнер от обычно пользователя. Вроде как тоже не мешает, если у этого пользователя есть доступ к volumes или прочим путям, которые пробрасываются в контейнер. Если ошибаюсь - подправьте меня

Также вроде как создаётся профиль apparmor по умолчанию. Но он широкий, особо смысла не имеет

Интересуюсь для общего развития. Ну и с учётом того, что некоторые приложения будут открыты в сеть - лишним не будет

Знаю что есть podman. Но с ним не разбирался еще

Из базового харденинга Вы все уже указали, этого хватит уже в большинстве случаев.

  • Вот с apparmor не знаморачиваюсь, не выключаю, но и не занимаюсь тюнингом
  • Остальное имеет место быть

Что можно еще высосать из пальца:

С точки зрения докер админа

  1. Держать докер образы в актуальном виде
  2. Сканировать уязвимости, например, при помощи trivy
  3. Запускать docker не в LXC, а в VM
  4. Readonly образы системы для запуска doker, для кубера есть Talos, для docker-compose не нашел пока
  5. В целом, что-то с секретами придумать, но в для докера я не нашел (писал уже ранее)
  6. Поднять свой реестр типа nexus и запускать образы с него
  7. Включить режим только для чтения read_only: true. Для временных файлов смотнтировать tmpfs
  8. Минимизировать публикацию портоов, часто всякие редисы и прочие БД прописываются без аутентификации
  9. Указать лимины по памяти, процессору и процессам
  10. Указать кастомный seccomp профиль, но надо его собрать
  11. Мониторинг и аудит, например, при помощи falco, auditd, osquery

С точки зрения сетевого админа

  1. Запихнуть в отдельную DMZ сетку
  2. Закрыть фаерволом доступ в интернет, если он не нужен
  3. Поставить WAF перед сервисом

С точки зрения разработчика/сборщика образов

  1. Минималистичные базовые образы, rootless, distroless, scratch
  2. Мультисейджинговая сборка образов
1 лайк

P.S.

Я сейчас в процессе настройки отдельной DMZ сети для LXC контейнеров, но есть пара моментов:

  1. Все еще хочется нормальный DNS, думаю в трафике прописывать вместо service.lan:3000 похожий адрес service.dmz.lan
  2. Вы в курсе, что я топлю на производительность, у меня все, что ниже 2.5Г уже плохо, поэтому. Думаю вместо NAT на роутере добавить 2 сети в traefik контейнеры и он будет пересылать с трафик между сетями напрямую, при этом, входящий трафик из DMZ считается внешним и если у сервиса локальная миддлварка включена, то из DMZ нет доступа
  3. Вообще правильно делать DMZ отдельно для каждого сервиса, если при взломе одного сервиса злоумышленник может выполнить curl http://frigate:5000 и обратиться к другому сервису, минуя reverse-proxy, то это плохая идея, поэтому, запретить интеррконнект между LXC контейнерами, например, условно, выделить диапазон адресов с 1 (роутер) до 10-50, куда можно ходить, а все, что выше 50 под запретом, тогда единым правилом мы запрещаем взаимодействовать сервисам между собой, а до 50 будут находиться reverse-proxy, но при этом все равно запретить доступ на проксик на все порты, кроме 80 и 443
  4. В идеале удалить ssh на контейнерах, но мне это не очень удобно сейчас т.к. ansible ходит в них
  5. Ну и тот же SSH доступ в контейнеры и другие порты настраивать через NAT на роутере
  6. Добавить сбор и обработку логов из этой сети, например при помощи crowdsec
  7. Ограничивать доступ в интернет для LXC контейнеров пока не хочется

Тут недавно обсуждалось что лучше LXC + mp или VM+NFS/SAMBA, так вот в данной схеме у меня для NFS выделен отдельный VLAN, который заходит в хосты PVE, по этому VLAN они могут примонтировать себе NFS шары.
Шара пробрасывается в LXC контейнер, который добавляется в DMZ сеть из которой нет никакого доступа к NAS

Описанный подход к DMZ при использовании моего подхода с LXC контейнером для каждого сервиса позволяет

  1. Получить приемлемый уровень сетевой безопасности, не маниакальный, но и не двери на-распашку, при этом сервисы между собой напрямую взаимодействовать не могут
  2. Получить приемлемый уровень удобства, тут
    2.1. В 90% случаев фаервол для контейнерв будет настроен одинаково через фиксированный набор групп безопасности
    2.2. Замена бриджа для LXC контейнера и домена сервиса в traefik (ну не использую я auto discovery для него) сразу кардинально меняет уровень безопасности конкретного сервиса без тюнинга
  3. Для пользователей локальной сети и PVE кластера с экземпляром трафика на каждой PVE ноде мы имеем все еще высокую сетевую производительность и подстройку под топологию
2 лайка

Список требований:

  1. Должен быть задокументирован список образов, переносимых в продуктивный реестр, с указанием оркестратора/сервера, на котором они будут развернуты.
    Cписок образов предоставляется в формате: image_tag:сервер.
  2. Не допускается размещать непроверенные и недоверенные (полученные из сомнительного источника) пакеты в образе контейнера. Перед установкой каждый образ контейнера должен проверяться на предмет уязвимых пакетов.
  3. Каждый компонент Приложения должен корректно обрабатывать сигналы хостовой операционной системы. В частности, при получении сигнала SIGTERM компонент должен завершить обработку запросов и корректно завершить работу настолько быстро, насколько это возможно.
  4. Образ контейнера должен содержать актуальные версии операционных систем и приложений, не имеющие известных уязвимостей (CVE), собранные образы должны проверяться на наличие уязвимостей.
  5. В качестве базового образа контейнера необходимо использовать разрешенные к использованию в Компании базовые образы.
  6. Не допускается использовать инструкцию ADD в Dockerfile. Допускается использовать инструкцию COPY.
  7. В контейнерах должны отсутсвовать файлы с полномочиями setuid, setgid.
  8. Не допускается использовать отдельной строкой в Dockerfile только инструкцию обновления (например, RUN apt-get update).
  9. Каждый процесс приложения должен быть запущен в отдельном контейнере, связанные контейнеры должны быть объединены в один Pod.
  10. Запрещается запуск контейнеров в привилегированном режиме. В yml файле или параметрах запуска должен отсутствовать флаг privileged: true
  11. Должны быть настроены Liveness, Readiness probe
  12. Запрещается пробрасывать привилегированные порты (до 1024) в контейнер. Состав пробрасываемых в контейнер портов должен быть минимально достаточен для работы. Запрещается запускать ssh-сервер и прочие средства удаленного управления в контейнере.
  13. Не допускается использовать (share) сущности хостовых машин в контейнере согласно списку:
    • network namespaces;
    • process namespaces;
    • IPC namespaces;
    • UTS namespaces;
    • user’s namespaces;
    • устройства;
    • файловые директории: /, /boot, /etc, /lib, /proc, /sys, Usr.
      Хостовые сущности не должны монтироваться. Не должно использоваться конструкции вида:
      shareProcessNamespace: true
      share = ipc
  14. Должны быть установлены лимиты на использование оперативной памяти и ресурсов процессора контейнером. Значения должны быть минимально достаточным для работы контейнеризированного приложения.
    limits:
    cpus: 0.50
    memory: 512M
    reservations:
    cpus: 0.25
    memory: 128M
  15. Состав пакетов в образе контейнера должен быть минимально достаточен для работы. Неиспользуемые в ходе работы контейнеризированного приложения пакеты должны отсутствовать в образе контейнера.
  16. В контейнере должны быть активированы настройки безопасности SELinux.
  17. Корневая файловая система контейнера должна быть смонтирована в режиме «только чтение». Исполняемые файлы в контейнере, владельцем которых является root, не должны быть доступны для записи. Для контейнера должны быть явно выделены ресурсы для записи (например, persistent volume), если необходимость записи предусмотрена архитектурой приложения и согласована.
    Флаг, разрешающий только чтение (readOnlyRootFilesystem, ReadonlyRootfs, read_only), должен быть установлен в значение true.
    securityContext:
    •  readOnlyRootFilesystem: true
      
  18. Приложение должно поддерживать возможность определять конфигурацию через ENV-переменные, либо через конфигурационные файлы.
  19. Контейнер должнен быть максимально ограничен в части использования Linux Capabilities (например, обычно не требуются возможности NET_ADMIN, SYS_ADMIN, SYS_MODULE), а также не должен выходить за рамки Seccomp-профиля OSCP. Исключения прорабатываются отдельно для каждого Приложения.
    В YAML-файле не дожно содержаться конструкций вида:
    –cap-add=NET_ADMIN
    –cap-add=SYS_ADMIN
    –cap-add=SYS_MODULE
    и иных привилегированных возможностей SYS_, NET_, MAC_)
  20. Запрещается запуск сервиса в контейнере от root. Не рекомендуется запуск контейнеров с заранее определенными uid.
  21. Перечень секретов должен быть явно документирован и храниться в защищенном хранилище секретов, например, в OKD/OCP secrets. Хранение паролей в открытом виде в конфигурационных файлах (YAML, Dockerfile и т.п.) запрещено.
  22. Не допускается вести запись логов в эфемерное хранилище Pod / локальное хранилище контейнера. Отправка логов безопасности должна осуществляться в stdout/stderr.
  23. Не допускается вносить изменения в Pod, находящийся в эксплуатации через конструкции exec, rsh, etc.. За исключением случаев по миграции БД.
  24. Докер сокет (сокетфайл) не должен быть смонтирован в контейнер. Не использовать non-root привелегии на docker.sock, также не следует шарить сокет /var/run/docker.sock с контейнерами.
  25. Должна использоваться актуальная версия Docker. Настроить автообновления.
  26. Только доверенные пользователи должны иметь возможность обращаться к Docker-демону. Исключить лишних пользавателей из группы docker, оставить только согласованных пользователей.
  27. Между контейнерами должны быть разрешены только необходимые сетевые взаимодействия. Дефолтная bridge-сеть должна быть запрещена. В настройках конфига docker-демона указать “icc”: false либо передать этот параметр при запуске демона. Либо создавать кастомные сети и подключать к ним только необходимые контейнеры.
  28. Должен быть установлен log level INFO для докер-демона.
  29. Запрещено использовать insecure registry. Использовать только защищенные TLS реестры.
  30. Возможность повышения привилегий из контейнера должна быть отключена. Запретить возможность повышения привилегий в контейнерах через suid или sgid. Добавить необходимый параметр при запуске демона, например в /etc/docker/daemon.json.
  31. Запрещено хранить секреты в Dockerfile.
1 лайк