# Защита хоста Docker

Первая категория охватывает все, что вы можете сделать для защиты вашего хоста докеров.

## 1. Поддерживайте актуальность Docker Host

Здесь действительно не нужно никаких объяснений. Это самая простая из лучших практик безопасности Docker, и она буквально занимает секунды.

Держите свою хост-систему Docker в курсе обновлений безопасности. В моем стеке Docker Traefik на базе Linux я часто обновляю пакеты и обновляю систему, используя следующие команды:

```bash
sudo apt-get update
sudo apt-get upgrade
```

## 2. Используйте обратный прокси

Лучший способ — не открывать какие-либо порты для Интернета, а вместо этого использовать VPN в вашей частной сети и получать локальный доступ к приложениям. Но это слишком громоздко, и размещение приложений за обратным прокси-сервером — удобная, но худшая альтернатива.

Вам нужно будет настроить переадресацию портов на вашем интернет-шлюзе, чтобы перенаправить определенные порты на хост Docker.

Я настоятельно рекомендую не выставлять все приложения Docker в Интернет. Вместо этого поместите их за обратным прокси. Я использую Traefik и выставляю в Интернет только порты **80** и **443**. Даже приборная панель traefik, использующая порт **8080**, находится за обратным прокси-сервером.

# Защита Докера

Следующая большая категория: Docker. Давайте рассмотрим, как я считаю, контрольный список безопасности Docker , чтобы убедиться, что ваша установка защищена.

## 3. Не меняйте владельца сокета Docker

Не связывайтесь с владением Docker Socket (`/var/run/docker.sock` в Linux). По умолчанию сокет принадлежит пользователю **root** и группе **docker** .

Для удобства я рекомендовал добавить себя (ваше имя пользователя) в группу докеров в прошлом.

Преимущество заключается в том, что вы можете запускать команды docker без использования sudo. Но это угроза безопасности. Я отошел от него и не рекомендую.

## 4. Не запускайте контейнеры Docker от имени root

По умолчанию контейнеры запускаются от имени пользователя root внутри контейнера, что дает привилегии root. Это угроза безопасности.

Одна из лучших практик безопасности Docker — запускать контейнер от имени пользователя без полномочий root (UID не 0). Известные и надежные образы используют эту передовую практику безопасности при создании образов. Например, LinuxServer.io предоставляет образы докеров для нескольких приложений домашнего сервера. Их образы позволяют явно указать UID и GID в качестве переменных окружения.

В идеале, это должно выглядеть так, как показано в блоке кода ниже.
```yaml
    environment:
      - PUID=$PUID
      - PGID=$PGID
```
## 5. Осторожно используйте привилегированный режим

По умолчанию контейнеры Docker работают в «непривилегированном» режиме. Это означает, что эти контейнеры не могут запускать демон Docker внутри себя. Это также запрещает использование хост-устройств или определенных функций ядра.

Обычно это делается путем добавления следующей строки в services:

```yaml
    privileged: true
```

Для некоторых служб требуется привилегированный режим. Например, только четыре службы в моем файле компоновки Docker используют привилегированный режим:

1.  Home Assistant — для доступа к USB-контроллеру Z-wave.
2.  Socket Proxy — требование для Socket proxy, повышающее безопасность.
3.  Glances – для мониторинга системы
4.  APCUPSD — для связи демона ИБП APC с ИБП через USB.

В таких ситуациях используйте образы докеров только из надежных источников (подробнее об этом позже). Еще лучший подход — использовать возможности докеров .

Кроме того, добавление следующей строки гарантирует, что контейнеры не получат дополнительных привилегий:

```yaml
    security_opt:
      - no-new-privileges:true
```

## 6. Используйте доверенные образы Docker

По возможности всегда используйте изображения от проверенных издателей или официальных источников.

Это дает немедленное доверие и обеспечивает безопасность контейнера Docker.

С непопулярными образами трудно предсказать или предположить, были ли соблюдены/реализованы передовые методы безопасности Docker.

!!! note
  К сожалению, для многих приложений для домашних серверов, таких как Sonarr, Radarr и т. д., нет «официальных» или «проверенных» издателей. Поэтому вам придется ориентироваться на популярность изображения (количество загрузок/звезд).

## 7. Используйте секреты Docker

Указание всей вашей конфиденциальной информации (например, ключей API) в файле `.env`, `/etc/environment` или файле `docker-compose.yml` может представлять угрозу безопасности.

Именно поэтому были введены [секреты Docker](https://www.docker.com/blog/docker-secrets-management/): для управления конфиденциальными данными.

Реализация секретов Docker для вашего стека — это многоэтапный процесс.

### А. Создать папку секретов

Сначала создайте папку secrets внутри корневой папки docker .

```bash
sudo mkdir secrets
```

Установите для этой папки права доступа 600 , принадлежащие пользователю root и группе root .

```bash
sudo chown root:root ~/docker/secrets
sudo chmod 600 ~/docker/secrets
```
Это делает эту папку доступной только для пользователя *root*, добавляя уровень безопасности при доступе к конфиденциальной информации.

### B. Создайте секретные файлы

Затем вам нужно будет поместить вашу конфиденциальную информацию в файл. В качестве примера давайте определим секрет для электронной почты учетной записи Cloudflare.

Давайте создадим файл внутри папки секретов с именем **cloudflare_email** . Помните, что для создания файла вам потребуются root-права. В моей системе Ubuntu я использую:

```bash
sudo su
nano cloudflare_email
```
Вы можете использовать любой другой текстовый редактор.

Единственное, что нужно добавить в файл, — это адрес электронной почты вашей учетной записи Cloudflare.

Сохранить и выйти.

### C. Определите секреты в файле Docker Compose

Теперь, когда секрет Docker создан, давайте определим его в файле компоновки Docker. Делается это с помощью блока: secrets.

В приведенном ниже примере показаны два секрета: `cloudflare_email` и `cloudflare_api_key`.

```yaml
########################### SECRETS

secrets:
 cloudflare_email:
   file: $SECRETSDIR/cloudflare_email
 cloudflare_api_key:
   file: $SECRETSDIR/cloudflare_api_key
```

`$SECRETSDIR` — это переменная окружения, содержащая путь к папке секретов Docker.

### D. Используйте секреты в службах Docker

После глобального определения мы можем использовать секреты в сниппетах docker-compose для отдельных сервисов. Поскольку мы добавили данные учетной записи Cloudflare в качестве секретов Docker, давайте посмотрим, как их использовать во фрагменте кода docker-compose для Traefik.

Во-первых, мы должны сделать секреты доступными внутри контейнера Traefik. Для этого вам нужно добавить следующий блок в сниппет docker-compose для Traefik:

```yaml
    secrets:
    - cloudflare_email
    - cloudflare_api_key
```

Это делает секретный файл доступным в папке `/run/secrets` внутри контейнера.

Затем мы можем установить переменные среды для чтения конфиденциальных данных из этих секретных файлов с помощью блока `environment:` как показано ниже:

```yaml
environment:
 - CF_API_EMAIL_FILE=/run/secrets/cloudflare_email
 - CF_API_KEY_FILE=/run/secrets/cloudflare_api_key
```

Обратите внимание, что к переменным окружения теперь добавляется **_FILE** в конце. Не пропустите это, иначе это не сработает.

Сохраните и заново создайте службу и проверьте журналы на наличие ошибок. Если контейнер не сможет правильно прочитать секреты, вы увидите это как ошибку в журналах.

!!! note
  Чтобы секреты Docker работали правильно, базовый образ контейнера должен их поддерживать. Если образ имеет репутацию/надежный образ, очень высока вероятность того, что разработчики внедрили передовые методы обеспечения безопасности Docker , включая секреты Docker.

## 8. Используйте прокси-сервер Docker Socket

Каждый раз, когда вы предоставляете сокет Docker службе, вы упрощаете для контейнера получение root-доступа в хост-системе.

Но некоторым приложениям требуется доступ к сокету Docker и API (например, Traefik, Glances, Dozzle, Watchtower и т. д.).

Если Traefik будет скомпрометирован, ваша хост-система может быть скомпрометирована. Собственная документация Traefik перечисляет использование Socket Proxy в качестве решения.

Прокси-сервер сокета похож на брандмауэр для сокета/API докера. Вы можете разрешить или запретить доступ к определенному API.

Я начал с [Socket Proxy от Tecnativa](https://hub.docker.com/r/tecnativa/docker-socket-proxy), но недавно перешел на [Socket Proxy от FluenceLab](https://hub.docker.com/r/fluencelabs/docker-socket-proxy), так как он обеспечивает более детальный контроль .

Добавьте сеть в свой файл compose (игнорируйте первую строку, если у вас уже есть блок network:):

```yaml
networks:
 socket_proxy:
   name: socket_proxy
   driver: bridge
   ipam:
     config:
       - subnet: 192.168.91.0/24
```

И, наконец, вот фрагмент кода docker-compose для добавления прокси-сервера сокета для повышения безопасности докера:

```yaml
# Docker Socket Proxy - Security Enchanced Proxy for Docker Socket
  socket-proxy:
    container_name: socket-proxy
    image: tecnativa/docker-socket-proxy
    restart: always
    networks:
      socket_proxy:
        ipv4_address: 192.168.91.254 # You can specify a static IP
    # privileged: true # true for VM. False for unprivileged LXC container.
    ports:
      - "127.0.0.1:2375:2375" # Port 2375 should only ever get exposed to the internal network. When possible use this line.
    # I use the next line instead, as I want portainer to manage multiple docker endpoints within my home network.
    # - "2375:2375"
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock"
    environment:
      - LOG_LEVEL=info # debug,info,notice,warning,err,crit,alert,emerg
      ## Variables match the URL prefix (i.e. AUTH blocks access to /auth/* parts of the API, etc.).
      # 0 to revoke access.
      # 1 to grant access.
      ## Granted by Default
      - EVENTS=1
      - PING=1
      - VERSION=1
      ## Revoked by Default
      # Security critical
      - AUTH=0
      - SECRETS=0
      - POST=1 # Watchtower
      # Not always needed
      - BUILD=0
      - COMMIT=0
      - CONFIGS=0
      - CONTAINERS=1 # Traefik, portainer, etc.
      - DISTRIBUTION=0
      - EXEC=0
      - IMAGES=1 # Portainer
      - INFO=1 # Portainer
      - NETWORKS=1 # Portainer
      - NODES=0
      - PLUGINS=0
      - SERVICES=1 # Portainer
      - SESSION=0
      - SWARM=0
      - SYSTEM=0
      - TASKS=1 # Portainer
      - VOLUMES=1 # Portainer
```


!!! warning "Внимание"
   никогда не открывайте порт 2375 для доступа в Интернет. Вас взломают/ Это еще более важно для виртуальных частных серверов, которые обычно предоставляют доступ ко всем портам. Включите брандмауэр, чтобы разрешить только порты 80 и 443 (и заблокировать остальные) для прохождения на ваш сервер, а также реализовать обходной путь Docker IP Tables, описанный далее в этом руководстве.

!!! note
  Кроме того, порт `2375` должен быть открыт только для внутренней сети (*127.0.0.1:2375*).

В блоке **environment:** указываем раздел Docker API, который хотим открыть или закрыть. Я добавил комментарии, чтобы описать, какие службы требуют каких разделов API. Например, если вы не используете WatchTower, вы можете ввести **0** для нескольких разделов API.

После запуска контейнера прокси-сервера Socket вы можете заменить прямой доступ к сокету Docker прокси-сервером Socket для всех служб, которые в нем нуждаются. Это можно сделать несколькими способами, в зависимости от того, как это поддерживает образ контейнера.

Для Traefik замените следующий аргумент CLI (если вы используете аргументы CLI вместо статических конфигураций):

`- --providers.docker.endpoint=unix:///var/run/docker.sock`

на

`- --providers.docker.endpoint=tcp://socket-proxy:2375`

Для других служб вы можете удалить указание Docker Socket в качестве тома (следующая строка под томами: ):

`- /var/run/docker.sock:/var/run/docker.sock:ro`

и добавьте переменную окружения DOCKER_HOST , как показано ниже:

`DOCKER_HOST: tcp://socket-proxy:2375`

Это то, что я делаю для Portainer и WatchTower.

Воссоздайте свой стек, и ваши службы должны использовать защищенный прокси-сервер сокета Docker вместо сокета Docker.

## 9. Измените DOCKER_OPTS на Respect IP Table Firewall

Случайно наткнулся на эту тему. Я включил UFW, как всегда делаю на своем VPS Digital Ocean, и заблокировал все, кроме 80 и 443. Я непреднамеренно попытался получить доступ к одной из служб, используя номер порта, и был шокирован тем, что я был подключен.

Покопавшись дальше, я наткнулся на этот открытый вопрос на GitHub . Почему это было открыто больше года, несмотря на огромное количество людей, запрашивающих исправление, мне не понятно.

Поэтому, если у вас включен прокси-сервер сокета и включен брандмауэр, из-за недостатка безопасности в докере хакеры все равно могут взломать вашу систему, используя порт прокси-сервера сокета (2375).

К счастью, есть обходной путь. В системах на базе Ubuntu/Debian отредактируйте **/etc/default/docker** и добавьте следующую строку:

`DOCKER_OPTS="--iptables=false"`

Сохраните файл и перезапустите службу Docker.

Попробуйте подтвердить исправление, обратившись к одной из ваших служб с помощью WAN-IP:PORT.

## 10. Контролируйте использование ресурсов Docker

Мне понравилось, что я могу устанавливать ресурсы для сервисов Docker. К сожалению, это возможно только с [Docker-Compose версии 2](https://docs.docker.com/compose/compose-file/compose-file-v2/#cpu-and-other-resources) или режимом Docker Swarm.

Если вы используете любой из них, вы можете установить [ограничения ресурсов для служб Docker](https://docs.docker.com/config/containers/resource_constraints/) .

Вот пример docker-compose для установки ограничений ресурсов в режиме Docker Swarm:

```yaml
deploy:
 resources:
   limits:
     cpus: '0.50'
     memory: 50M
   reservations:
     cpus: '0.25'
     memory: 20M
```

Установив ограничения ресурсов, вы можете ограничить любую службу, которая становится мошеннической и потребляет ваши системные ресурсы.

**Защита приложений Docker с помощью Traefik**

Есть вещи, которые можно сделать на стороне Traefik, чтобы защитить ваш стек от вредоносных атак. Давайте рассмотрим некоторые меры безопасности Traefik, которые можно реализовать.

## 11. Ограничение скорости

Ограничение скорости довольно распространено для смягчения атак методом грубой силы или отказа в обслуживании. В моем стеке Traefik Docker есть промежуточное ПО для определения ограничения скорости .

```yaml
middlewares-rate-limit:
 rateLimit:
   average: 100
   burst: 50
```

Приведенный выше общий набор чисел отлично работает для меня. Его можно настроить в соответствии с вашей ситуацией, используя документацию Traefik по ограничению скорости .

## 12. Заголовки безопасности Traefik

Заголовки безопасности — это основные требования к безопасности веб-сайта. Они защищают от различных атак, включая XSS, кликджекинг, внедрение кода и многое другое.

Вот заголовки безопасности Traefik, которые я определил как промежуточное ПО:

```yaml
middlewares-secure-headers:
 headers:
   accessControlAllowMethods:
     - GET
     - OPTIONS
     - PUT
   accessControlMaxAge: 100
   hostsProxyHeaders:
     - "X-Forwarded-Host"
   sslRedirect: true
   stsSeconds: 63072000
   stsIncludeSubdomains: true
   stsPreload: true
   forceSTSHeader: true
   # frameDeny: true #overwritten by customFrameOptionsValue
   customFrameOptionsValue: "allow-from https:example.com" #CSP takes care of this but may be needed for organizr.
   contentTypeNosniff: true
   browserXssFilter: true
   # sslForceHost: true # add sslHost to all of the services
   # sslHost: "example.com"
   referrerPolicy: "same-origin"
   # Setting contentSecurityPolicy is more secure but it can break things. Proper auth will reduce the risk.
   # the below line also breaks some apps due to 'none' - sonarr, radarr, etc.
   # contentSecurityPolicy: "frame-ancestors '*.example.com:*';object-src 'none';script-src 'none';"
   featurePolicy: "camera 'none'; geolocation 'none'; microphone 'none'; payment 'none'; usb 'none'; vr 'none';"
   customResponseHeaders:
     X-Robots-Tag: "none,noarchive,nosnippet,notranslate,noimageindex,"
     server: ""
```

В Traefik была ошибка, из-за которой нельзя было определить заголовки безопасности как в динамической, так и в статической конфигурации. Это с тех пор было закрыто.

Так что теперь можно добавить **sslForceHost** и **sslHost** к отдельным службам, если хотите, для дополнительной безопасности.

## 13. Параметры TLS

Параметры TLS позволяют настраивать соединения TLS для защиты соединения между клиентом и вашей службой. Дополнительные пояснения можно найти в [документации TLS от Traefik](https://doc.traefik.io/traefik/https/tls/).

В моей настройке я определил следующие параметры TLS для Traefik:

```yaml
tls:
 options:
   default:
     minVersion: VersionTLS12
     sniStrict: true
     cipherSuites:
       - TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
       - TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
       - TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305
       - TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256
       - TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
       - TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305
       - TLS_AES_128_GCM_SHA256
       - TLS_AES_256_GCM_SHA384
       - TLS_CHACHA20_POLY1305_SHA256
     curvePreferences:
       - CurveP521
       - CurveP384
```

## 14. Многофакторная аутентификация

Это становится все более и более очевидным / обязательным. Если вы еще не защитили свои приложения Docker с помощью многофакторной аутентификации, сделайте это прямо сейчас. Я тестировал и использовал три системы аутентификации: Google OAuth, Authelia и Keycloak.

## 15. Защитите контейнеры Docker с помощью Cloudflare

Вот краткое изложение ключевых настроек Cloudflare для повышения безопасности контейнеров Docker при подключении к Интернету.

-   **Cloudflare Proxy** — позволяет использовать улучшения безопасности и производительности Cloudflare.
-   **Режим SSL** — полный или строгий. Это шифрует соединение между исходным сервером и Cloudflare и между Cloudflare и клиентом.
-   **Пограничные сертификаты**:
    -   **Всегда использовать HTTPS**: ВКЛ.
    -   **HTTP Strict Transport Security (HSTS)**: Включить (будьте осторожны)
    -   **Минимальная версия TLS**: 1.2
    -   **Оппортунистическое шифрование**: ВКЛ.
    -   **TLS 1.3**: ВКЛ.
    -   **Автоматическая перезапись HTTPS**: ВКЛ.
    -   **Мониторинг прозрачности сертификата**: ВКЛ.
-   Правила брандмауэра. Создайте правила для разрешения или запрета определенного трафика (например, я разрешаю трафик только из РФ в мои частные приложения, так как я получаю к нему доступ только из РФ).
-   **Настройки брандмауэра**:
    -   **Уровень безопасности**: Высокий
    -   **Режим боя с ботом**: ВКЛ.
    -   **Прохождение испытания**: 30 минут
    -   **Проверка целостности браузера**: ВКЛ.

Это настройки Cloudflare, связанные с безопасностью Docker.

**Другие улучшения безопасности для стека Docker Traefik**

Все вышеперечисленные рекомендации по безопасности докеров — это то, что я реализовал до сих пор. Но есть и другие, и я настоятельно рекомендую изучить следующее для дополнительной безопасности.

## 16. Fail2ban

Fail2ban сканирует ваши лог-файлы и блокирует IP-адреса, которые показывают злонамеренные намерения (например, поиск эксплойтов, неудачных паролей и т. д.). При обнаружении подозрительной активности он обновляет правила брандмауэра, чтобы заблокировать IP-адрес на указанный период времени.

## 17. Безопасность Docker Bench

Docker Bench for Security — это скрипт, который проверяет десятки распространенных передовых методов развертывания контейнеров Docker в рабочей среде .

## 18. RBAC

RBAC — это управление доступом на основе ролей. Если вы представляете предприятие или имеете несколько пользователей, это обязательно. Это может быть довольно дорого реализовать, но [portainer делает это очень простым](https://www.funkypenguin.co.nz/note/docker-rbac-with-portainer/) (поскольку кулинарная книга FunkyPenguin потрясающая … проверьте ее, если вы этого не сделали) за символическую плату.

## 19. Сканер уязвимостей контейнеров

Последним в списке лучших практик для безопасности докеров является сканер уязвимостей. Здесь есть несколько примеров, но я перечислю только один: Clair.

Clair — проект с открытым исходным кодом для статического анализа уязвимостей в контейнерах приложений.

Он использует Clair API для индексации образов контейнеров, а затем сопоставляет их с известными уязвимостями безопасности Docker.