Блог

Защита срещу бягство от контейнер: Практическо ръководство за Docker изолация при хостинг за множество клиенти

Научете как да защитите Docker контейнери срещу уязвимости за бягство и провали в изолацията в среди за множество клиенти с конкретни стъпки и примери.

Резюме

Docker контейнерите споделят хост ядрото, което прави изолацията критична — особено при хостинг за множество клиенти, където едно бягство от контейнер може да компрометира всички клиенти. Много разработчици предполагат, че контейнерите са напълно изолирани виртуални машини, но реалността е различна. Тази статия обяснява функциите на Linux ядрото, които стоят зад Docker изолацията (namespaces, cgroups) и векторите на атака, които ги застрашават. Ще научите практически стъпки за заздравяване на вашата Docker настройка: ограничаване на привилегиите, използване на сигурна среда за изпълнение, сканиране на образи и прилагане на мрежова сегментация. Следвайки пример от реалния свят на доставчик на хостинг за WordPress за множество клиенти, ще видите как да приложите тези защити. Разглеждаме също и уговорки като компромиси в производителността и използването на seccomp/AppArmor. Целта е да ви дадем стабилна стратегия за изолация, която предотвратява бягство от контейнер и пази вашите клиенти в безопасност.

Въведение

Ако управлявате платформа за хостинг за множество клиенти — било то споделен WordPress хостинг, SaaS приложение или услуга за среда за разработка — бягството от контейнер е кошмарният сценарий. Уязвимост в ядрото или неправилна конфигурация може да позволи на един клиент да избяга от своя контейнер и да получи достъп до данните на други клиенти или до самия хост. Docker изолацията разчита на функции на Linux ядрото като namespaces и cgroups, но конфигурациите „извън кутията“ често са недостатъчни за стабилна сигурност. Тази статия ще ви преведе през векторите на атака и ще предостави приложими стъпки за заключване на вашите Docker контейнери, илюстрирани с пример от реалния свят за WordPress хостинг за множество клиенти. За по-широк поглед върху оркестрацията в продукционна среда, вижте нашето ръководство за Оркестриране на готови за продукция контейнерни приложения.

Разбиране на Docker изолацията

Docker контейнерите използват Linux namespaces за осигуряване на изолация на ниво процес: PID namespaces изолират дърветата на процесите, network namespaces разделят мрежовите интерфейси, mount namespaces изолират монтиранията на файловата система, а user namespaces позволяват картографиране на root на контейнера към непривилегирован хост потребител. Control groups (cgroups) ограничават използването на ресурси като CPU, памет и дисково I/O. Тези функции заедно създават „пясъчник“ около всеки контейнер. Въпреки това, за разлика от виртуална машина, която изпълнява отделно ядро, контейнерите споделят хост ядрото. Това означава, че уязвимост в ядрото (напр. CVE-2022-0492) може да бъде експлоатирана за бягство от изолацията на namespace на контейнера. Освен това, неправилни конфигурации като стартиране на контейнери като root вътре в контейнера, предоставяне на всички възможности на контейнера или не отпадане на ненужни Linux възможности могат да разширят повърхността за атака.

Вектори на атака

Честите вектори на атака включват:

  • Експлойти на ядрото: Експлоатиране на грешка в хост ядрото за получаване на достъп до хоста.
  • Привилегировани контейнери: Стартирането с --privileged предоставя всички възможности и заобикаля повечето изолации.
  • Злоупотреба с възможности: Дори без пълен привилегирован режим, контейнер с опасни възможности като CAP_SYS_ADMIN или CAP_NET_ADMIN може да монтира файлови системи или да манипулира мрежови настройки.
  • Несигурни практики при образи: Използване на базови образи с известни уязвимости или включване на ненужни инструменти като компилатори или интерпретатори на шел.
  • Споделени mount namespaces: Монтирането на хост директории в контейнери може да позволи бягство, ако не са само за четене.

Практически стъпки за сигурност

1. Стартирайте контейнери като потребител, който не е root

По подразбиране Docker стартира контейнери като root вътре в контейнера. Ако нападател получи root достъп вътре в контейнера, той има повече лостове. Създайте потребител във вашия Dockerfile и използвайте директивата USER. Също така, избягвайте да използвате флага --user в Docker Compose за картографиране към произволен хост потребител, ако е възможно.

2. Отпадайте всички възможности и добавяйте само необходимите

Linux възможностите разделят привилегиите на суперпотребител на по-малки единици. В Docker Compose използвайте cap_drop: ALL, след което cap_add само необходимите (напр. NET_BIND_SERVICE). Избягвайте опасни възможности като SYS_ADMIN, NET_ADMIN, SYS_PTRACE.

3. Използвайте файлова система на root само за четене

Задайте read_only: true в дефиницията на вашия контейнер. Това предотвратява записването от нападатели във файловата система на контейнера. Ако вашето приложение се нуждае от запис на временни файлове, монтирайте tmpfs том на това място.

4. Активирайте прекартиране на User Namespace

Прекартирането на User Namespace картографира root потребителя на контейнера към непривилегирован хост потребител. Това добавя слой изолация, тъй като дори ако root на контейнера избяга, той ще има привилегиите на прекартирания потребител. Активирайте го в /etc/docker/daemon.json с "userns-remap": "default". Имайте предвид, че това може да усложни разрешенията за томове. За повече подробности вижте Овладяване на Docker изолацията за сигурен и ефективен уеб хостинг.

5. Прилагайте Seccomp и AppArmor/AppArmor профили

Seccomp ограничава системните извиквания, които контейнерът може да направи. Docker предоставя профил за seccomp по подразбиране, който блокира опасни системни извиквания. Можете също да създавате персонализирани профили. Подобно, AppArmor (или SELinux) осигурява задължителен контрол на достъпа. Използвайте AppArmor, за да ограничите вашия контейнер до минимален набор от разрешени операции. Профилът за сигурност може да бъде зададен чрез security_opt в Docker Compose.

6. Използвайте минимални базови образи и сканирайте за уязвимости

Избирайте малки образи като Alpine или Distroless, които имат по-малка повърхност за атака. Редовно сканирайте образи с инструменти като Docker Scout, Trivy или Clair. Интегрирайте сканирането във вашия CI/CD пайплайн, за да предотвратите разполагането на уязвими образи.

7. Мрежова сегментация с персонализирани мостови мрежи

Създавайте отделни мостови мрежи за всеки клиент или слой на приложението. Това ограничава трафика между услугите (east-west). В Docker Compose дефинирайте мрежи и изолирайте услугите. Използвайте internal: true, ако услугата не се нуждае от изходящ достъп до интернет. Правилата на защитната стена на хоста допълнително ограничават трафика между контейнерите.

8. Ограничавайте ресурсите с Cgroups

Задайте лимити за CPU и памет в Docker Compose, използвайки deploy.resources.limits. Това предотвратява компрометиран контейнер да стартира атака за изчерпване на ресурсите. Освен това задайте kernel_memory и memory_reservation за по-фина настройка.

Пример от реалния свят: Хостинг за WordPress за множество клиенти с Docker Compose

Разгледайте сценарий, при който хоствате множество WordPress сайтове за различни клиенти, всеки в собствен Docker контейнер. Несигурна настройка може да изглежда така:

version: '3'
services:
  wordpress:
    image: wordpress:latest
    ports:
      - "8080:80"
    environment:
      WORDPRESS_DB_HOST: db
      WORDPRESS_DB_USER: exampleuser
      WORDPRESS_DB_PASSWORD: examplepass
      WORDPRESS_DB_NAME: exampledb
    volumes:
      - ./wp-content:/var/www/html/wp-content
  db:
    image: mysql:5.7
    environment:
      MYSQL_DATABASE: exampledb
      MYSQL_USER: exampleuser
      MYSQL_PASSWORD: examplepass
      MYSQL_ROOT_PASSWORD: somewordpress
    volumes:
      - db_data:/var/lib/mysql
volumes:
  db_data:

Тази настройка е уязвима: WordPress контейнерът се изпълнява като root вътре, има всички възможности (тъй като нито една не е отпаднала), монтира хост директория с достъп за запис и има неограничен мрежов достъп.

Сега да я заздравим:

version: '3'
services:
  wordpress:
    image: wordpress:latest
    user: www-data
    cap_drop:
      - ALL
    cap_add:
      - NET_BIND_SERVICE
    read_only: true
    tmpfs:
      - /var/www/html/wp-content/plugins
    security_opt:
      - seccomp=seccomp-profile.json
      - apparmor=wordpress-profile
    networks:
      - frontend
    ports:
      - "8080:80"
    environment:
      WORDPRESS_DB_HOST: db
      WORDPRESS_DB_USER: exampleuser
      WORDPRESS_DB_PASSWORD: examplepass
      WORDPRESS_DB_NAME: exampledb
    volumes:
      - wp-uploads:/var/www/html/wp-content/uploads
    deploy:
      resources:
        limits:
          cpus: '0.5'
          memory: 256M
  db:
    image: mysql:5.7
    user: mysql
    cap_drop:
      - ALL
    cap_add:
      - NET_BIND_SERVICE
    networks:
      - backend
    environment:
      MYSQL_DATABASE: exampledb
      MYSQL_USER: exampleuser
      MYSQL_PASSWORD: examplepass
      MYSQL_ROOT_PASSWORD: somewordpress
    volumes:
      - db_data:/var/lib/mysql
    deploy:
      resources:
        limits:
          cpus: '0.25'
          memory: 128M
networks:
  frontend:
    driver: bridge
    internal: false
  backend:
    driver: bridge
    internal: true
volumes:
  wp-uploads:
  db_data:

Ключови подобрения:

  • И двата контейнера се изпълняват като потребители, които не са root (www-data и mysql).
  • Всички възможности са отпаднали, добавена е само NET_BIND_SERVICE.
  • Файловата система на WordPress е само за четене, с изключение на tmpfs монтиране и volume за качвания.
  • Приложени са seccomp и AppArmor профили (ще трябва да предоставите персонализирани профили).
  • Отделни мрежи изолират уеб от базата данни, като мрежата на базата данни е вътрешна.
  • Лимитите на ресурсите предотвратяват изчерпване на ресурсите.

За повече информация относно специфично за WordPress Docker заздравяване, вижте Docker за WordPress: Защо изолираните контейнери променят всичко.

Уговорки

  • Прекартиране на User Namespace: Макар и мощно, то нарушава монтирането на томове, тъй като прекартираното UID на хоста не е същото като UID на контейнера. Може да се наложи предварително да създадете директории с правилни разрешения или да използвате Docker томове с поддръжка на прекартиране.
  • Seccomp/AppArmor Профили: Персонализираните профили изискват разбиране на системните извиквания и моделите на достъп до файлове на вашето приложение. Прекалено рестриктивните профили могат да нарушат функционалността. Тествайте щателно.
  • Производителност: Допълнителните слоеве за сигурност като seccomp и AppArmor имат минимален допълнителен разход, но лимитите на ресурсите и файловите системи само за четене могат да повлияят на приложения, които извършват много записи.
  • Сложност на оркестрацията: В среда за множество клиенти, управлението на Docker Compose файлове за всеки клиент може да стане тромаво. Помислете за използване на инструмент за оркестрация от по-високо ниво като Kubernetes, но това въвежда свои собствени съображения за сигурност.

Заключение

Бягството от контейнер е реална заплаха при хостинг за множество клиенти с Docker, но е предотвратимо. Като разбирате механизмите за изолация и прилагате защита в дълбочина — отпадане на възможности, стартиране като потребител, който не е root, активиране на user namespaces, seccomp, AppArmor, мрежова сегментация и редовно сканиране на образи — можете драстично да намалите риска. Не забравяйте, че настройките по подразбиране на Docker не са готови за продукционна среда при натоварвания за множество клиенти. Приложете тези стъпки днес, за да защитите вашите клиенти и вашата инфраструктура. За цялостен преглед на най-добрите практики за сигурност на Docker, вижте Защита на вашите уеб приложения с Docker: Практическо ръководство за изолация и най-добри практики.

Sources (5)