1. **Ограничить доступ к сокету Docker Daemon**. Владельцем сокет-файла должен быть пользователь root. Нужно понимать, что любой пользователь с доступом к сокету может получить доступ к системе. И запретить это можно, только если запретить маунтить файлы из определенных директорий.
2. **Не прокидывать сокет в контейнер **(Docker-in-Docker). Вместо Docker-in-Docker для сборки можно использовать kaniko — систему, которая позволит собрать образ без использования Docker. В этом случае для подписи образов можно использовать стороннюю систему и подключить ее через API.
3. ** Помнить о настройке авторизации Docker TCP**. Авторизацию настраиваем через сертификат SSL, то есть используем открытые и закрытые ключи и подписанные ключи по иерархии. Таким образом это все реализовано в Kubernetes.
4. **Настроить непривилегированного пользователя**. Запуск контейнеров всегда производим от непривилегированного пользователя. Если речь о Docker в чистом виде, мы просто указываем UID юзера и запускаем контейнер.
```
docker run -u 4000 alpine
```
Во время сборки контейнера мы создаем юзера и переходим на него.
```
FROM alpine
RUN groupadd -r myuser && useradd -r -g myuser
<Здесь еще можно выполнять команды от root-пользователя, например, ставить пакеты>
USER myuser
```
Также включаем поддержку user namespace в Docker daemon.
```
--userns-remap=default
```
5. **Отключить все возможности ядра** (capabilities). Например, так:
```
docker run --cap-drop all --cap-add CHOWN alpine
```
6. **Запретить эскалацию привилегий** (смену юзера на uid0). Используем опцию при запуске:
```
--security-opt=no-new-privileges
```
7. **Отключить межконтейнерное взаимодействие**. По умолчанию такое возможно через сеть docker0, опция при старте демона Docker:
```
--icc=false
```
8. **Ограничить ресурсы**. Так мы сократим риск несанкционированного майнинга:
```
-m или --memory — доступная память до OOM;
--cpus — сколько процессоров доступно, например 1.5;
--cpuset-cpus — можно указать, какие именно процессоры доступны (ядра);
--restart=on-failure:<number_of_restarts> — убираем вариант Restart Always, чтобы контролировать количество перезапусков и вовремя обнаруживать проблемы;
--read-only — файловая система настраивается только на чтение при запуске, особенно если контейнер отдает статику.
```
9. **Не отключать профили безопасности**. По умолчанию Docker уже использует профили для модулей безопасности Linux. Эти правила можно ужесточать, но не наоборот.
Отдельно скажу несколько слов про seccomp — механизм ядра Linuх, позволяющий определять доступные системные вызовы. Если злоумышленник получит возможность выполнить произвольный код, seccomp не даст ему использовать системные вызовы, которые не были заранее разрешены. В стандартной поставке Docker блокирует около 44 вызовов из 300+. Кроме Seccomp можно использовать также профили AppArmor или SELinux.
10. **Анализировать содержимое контейнера**. Для обнаружения контейнеров с уже известными уязвимостями есть инструменты: бесплатный Clair, условно бесплатные Snyk, anchore, платные JFrog XRay и Qualys. Бесплатно уязвимости можно также учесть в Harbor: он ищет известные эксплойты и сообщает о надежности контейнера. Также можно использовать системы асессмента ИБ в целом, например, Open Policy agent.