---
title: 'Docker против файервола: как закрыть уязвимости портов | Zomro'
description: 'Docker автоматически изменяет iptables и может обходить ваш UFW. Узнайте, почему это происходит, как проверить порты и настроить реальный firewall-контроль.'
image: 'https://cdn.zomro.com/uploads/blog/yaroslav.baidatskyi%40zomro.org/2025/10/17/pasted-image-20251016225647_q50.jpg'
---

![opened image]()

  * [Zomro](https://zomro.com/rus/)
  * [FAQ](https://zomro.com/rus/blog/faq/)
  * Docker против файервола: как закрыть уязвимости портов 



# Docker против файервола: как закрыть уязвимости портов

17-10-2025

## Введение

  
Docker упрощает развёртывание приложений в контейнерах, но его поведение в отношении сетевых правил часто становится источником вопросов безопасности. Типичная ситуация: администратор настраивает UFW или добавляет правила в iptables для блокировки портов, но контейнеры остаются доступными извне. Причина кроется в том, как Docker управляет сетевым стеком Linux на низком уровне.  


Данная статья рассматривает механизмы взаимодействия Docker с подсистемой netfilter (iptables/nftables), объясняет причины неожиданного поведения firewall-правил и предоставляет проверенные методы защиты контейнерной инфраструктуры.   


**Ключевой вызов** : Docker автоматически модифицирует правила iptables для обеспечения сетевой связности контейнеров, часто обходя стандартные firewall-механизмы на уровне приоритетов и порядка обработки правил.  
  


## Теория: как Docker работает с сетевым стеком Linux  


### Архитектура взаимодействия с netfilter  


Docker напрямую интегрируется с netfilter - подсистемой ядра Linux для фильтрации и трансляции сетевых пакетов. При запуске Docker создаёт несколько пользовательских цепочек (chains) в таблицах `nat` и `filter`:  


**Таблица nat:**

  * DOCKER - содержит правила `DNAT` для проброса портов с хост-системы в контейнеры
  * Правила в `PREROUTING` и` OUTPUT` перенаправляют трафик в цепочку DOCKER



  
**Таблица filter:**

  * DOCKER - правила фильтрации для трафика, направленного в контейнеры
  * DOCKER-ISOLATION-STAGE-1 и DOCKER-ISOLATION-STAGE-2 - обеспечивают изоляцию между различными Docker-сетями
  * DOCKER-USER - специальная цепочка для пользовательских правил (появилась в Docker 17.06)



###   
Порядок обработки правил  


Критически важен порядок прохождения пакета через цепочки. Для входящего трафика:

  1. `PREROUTING` (таблица `nat`) - здесь Docker вставляет переход в цепочку `DOCKER` перед любыми пользовательскими правилами
  2. `FORWARD` (таблица `filter`) - проходит через `DOCKER-USER`, затем `DOCKER-ISOLATION`, затем `DOCKER`
  3. `POSTROUTING` (таблица `nat`) - применяется `SNAT`/`MASQUERADE`



  
Docker использует операции ``-I`` (insert) для добавления правил в начало цепочек, что обеспечивает их приоритетное выполнение перед большинством пользовательских конфигураций.  


### Взаимодействие с nftables  


Современные дистрибутивы Linux переходят с `iptables` на `nftables` — новую подсистему netfilter. Существует несколько вариантов конфигурации:

  * **iptables-legacy** \- классическая реализация, использует отдельный набор правил ядра
  * **iptables-nft** \- обёртка над nftables, транслирующая команды iptables в синтаксис nftables
  * **nftables native** \- прямое использование команды `nft`



  
Проверить активный бэкенд можно командой:
    
    
    iptables -V
    # iptables v1.8.7 (nf_tables) — nftables is used
    # iptables v1.8.7 (legacy) — uses legacy
    
    update-alternatives --display iptables

Docker совместим с обоими вариантами, но возникают нюансы:

  * При использовании `iptables-nft` правила Docker видны через `nft list ruleset`, но имеют отдельную таблицу совместимости
  * Смешивание `iptables-legacy` и `nftables` приводит к работе с разными наборами правил, что создаёт иллюзию неработающих firewall-правил



##   
  
Почему UFW может «не видеть» открытые Docker-порты  


UFW (Uncomplicated Firewall) — это высокоуровневая обёртка над iptables, упрощающая управление правилами. UFW добавляет свои правила в стандартные цепочки `INPUT`, `OUTPUT` и `FORWARD`.  
  


### Механизм обхода UFW  


Когда Docker пробрасывает порт командой `-p 8080:80`, происходит следующее:

  1. Docker добавляет правило DNAT в таблице `nat`, цепочке `DOCKER`:` DNAT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:8080 to:172.17.0.2:80`
  2. Это правило применяется в цепочке `PREROUTING`, **до** того как пакет попадает в цепочку `FORWARD`, где работают правила UFW
  3. После трансляции адреса пакет направляется в `FORWARD` → `DOCKER-USER` → `DOCKER`
  4. Правила UFW в `ufw-before-forward` и `ufw-after-forward` проверяют **уже изменённый** адрес назначения (IP контейнера), а не исходный порт хоста



Результат: UFW показывает статус "порт закрыт", но трафик беспрепятственно проходит в контейнер через правила Docker в таблице `nat`.

**Иллюстрация трудности** :

  
UFW показывает закрытый порт:

![Docker и сетевая безопасность Linux взаимодействие с iptables, UFW и nftables 1](https://cdn.zomro.com/uploads/blog/yaroslav.baidatskyi%40zomro.org/2025/10/17/pasted-image-20251016225647_q50.jpg)

Но ss показывает слушающий порт

![Docker и сетевая безопасность Linux взаимодействие с iptables, UFW и nftables 2](https://cdn.zomro.com/uploads/blog/yaroslav.baidatskyi%40zomro.org/2025/10/17/pasted-image-20251016225705_q50.jpg)

  


## Почему простое добавление правил в iptables иногда НЕ эффективно

### Распространённая ошибка конфигурации

Типичная попытка заблокировать порт контейнера:
    
    
    sudo iptables -A INPUT -p tcp --dport 8080 -j DROP

Эта команда **не работает** по нескольким причинам:

###   


### Причина 1: Неправильная цепочка

Правило добавлено в цепочку `INPUT`, которая обрабатывает пакеты, предназначенные для самого хоста. Трафик в контейнеры проходит через цепочку `FORWARD`, поэтому правило просто не применяется.

Правильная попытка:
    
    
    sudo iptables -A FORWARD -p tcp --dport 80 -d 172.17.0.2 -j DROP

> Необходимо проверить и при необходимости изменить свой IP-адрес контейнера.

Но и это может не сработать из-за следующей причины.

### 

### Причина 2: Приоритет правил Docker

Docker использует операцию `-I` (insert) для добавления правил в **начало** цепочек. При использовании `-A` (append) пользовательское правило добавляется в конец, **после** правил Docker, которые уже разрешили трафик.

Просмотр цепочки `FORWARD`:
    
    
    sudo iptables -L FORWARD -n --line-numbers

Типичный вывод:

![Docker и сетевая безопасность Linux взаимодействие с iptables, UFW и nftables 3](https://cdn.zomro.com/uploads/blog/yaroslav.baidatskyi%40zomro.org/2025/10/17/pasted-image-20251016230756_q50.jpg)

![Docker и сетевая безопасность Linux взаимодействие с iptables, UFW и nftables 4](https://cdn.zomro.com/uploads/blog/yaroslav.baidatskyi%40zomro.org/2025/10/17/pasted-image-20251016230816_q50.jpg)  


Правило DROP на позиции 99 бесполезно — трафик уже разрешён на позиции 4.

### 

### Причина 3: Конфликт бэкендов iptables

При использовании `iptables-legacy` на системе с активным `nftables`, правила добавляются в разные наборы:

  * Docker может использовать `iptables-nft` (если это дефолт системы)
  * Администратор добавляет правила через `iptables-legacy`



Проверка:
    
    
    # Rules via nft backend
    sudo iptables -L -n | wc -l
    
    # Rules via legacy backend
    sudo iptables-legacy -L -n | wc -l
    
    # Native nftables rules
    sudo nft list ruleset
    

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

### Причина 4: Перезапись правил при рестарте Docker

Docker пересоздаёт свои правила при:

  * Перезапуске демона Docker
  * Запуске/остановке контейнеров с публикацией портов
  * Создании/удалении сетей



Если пользовательские правила добавлены вручную, они могут быть удалены или "сдвинуты" вниз по приоритету. Для стабильности требуется либо использовать `DOCKER-USER`, либо автоматизировать восстановление правил (netfilter-persistent, скрипты).

### Корректное решение

**Не рекомендуется** : вставка правил в начало цепочки `FORWARD`:
    
    
    sudo iptables -I FORWARD 1 -p tcp --dport 80 -d 172.17.0.2 -j DROP

> Необходимо проверить и при необходимости изменить свой IP-адрес контейнера.

Недостаток: при следующем запуске контейнера Docker вставит свои правила ещё раньше.

**Рекомендуется** : использование цепочки `DOCKER-USER` (описано в разделе "Надёжные способы защиты").

  


## Практическая часть: как проверить, какие порты действительно открыты

### Проверка слушающих портов на хосте

Команда `ss` (socket statistics) показывает активные сокеты:
    
    
    sudo ss -lntp

Пример вывода:

![Docker и сетевая безопасность Linux взаимодействие с iptables, UFW и nftables 6](https://cdn.zomro.com/uploads/blog/yaroslav.baidatskyi%40zomro.org/2025/10/17/pasted-image-20251016231922_YYjWhIk_q50.jpg)

Интерпретация:

  * `0.0.0.0:8080` — порт доступен на всех сетевых интерфейсах (включая внешние)
  * `127.0.0.1:6379` — порт доступен только локально
  * `docker-proxy` — процесс, созданный Docker для проброса портов в userland (режим userland proxy)



Альтернатива с netstat:
    
    
    sudo netstat -tulpn | grep docker-proxy

  


### Анализ правил iptables

**Таблица NAT** (правила DNAT):
    
    
    sudo iptables -t nat -L DOCKER -n --line-numbers

Пример вывода:

![Docker и сетевая безопасность Linux взаимодействие с iptables, UFW и nftables 7 ](https://cdn.zomro.com/uploads/blog/yaroslav.baidatskyi%40zomro.org/2025/10/17/pasted-image-20251016224604_q50.jpg)

Строка 2: любой TCP-трафик на порт 8080 хоста перенаправляется на 172.17.0.3:80.

**Таблица FILTER** (правила доступа):
    
    
    sudo iptables -L DOCKER -n --line-numbers

  
Пример:

![Docker и сетевая безопасность Linux взаимодействие с iptables, UFW и nftables 8](https://cdn.zomro.com/uploads/blog/yaroslav.baidatskyi%40zomro.org/2025/10/17/pasted-image-20251016224645_q50.jpg)

**Цепочка DOCKER-USER** (пользовательские правила):
    
    
    sudo iptables -L DOCKER-USER -n --line-numbers

  
По умолчанию (до добавления правил):

![Docker и сетевая безопасность Linux взаимодействие с iptables, UFW и nftables 9](https://cdn.zomro.com/uploads/blog/yaroslav.baidatskyi%40zomro.org/2025/10/17/pasted-image-20251016232412_q50.jpg)

`RETURN` означает возврат в вызывающую цепочку без блокировки.

###   
Анализ конфигурации контейнеров  


Получение информации о проброшенных портах:
    
    
    docker ps --format "table {{.Names}}\t{{.Ports}}"

  
Пример:

![Docker и сетевая безопасность Linux взаимодействие с iptables, UFW и nftables 10](https://cdn.zomro.com/uploads/blog/yaroslav.baidatskyi%40zomro.org/2025/10/17/pasted-image-20251016232603_q50.jpg)

  
Детальная информация о контейнере:
    
    
    docker inspect webapp | jq '.[0].HostConfig.PortBindings'

Вывод:

![Docker и сетевая безопасность Linux взаимодействие с iptables, UFW и nftables 11](https://cdn.zomro.com/uploads/blog/yaroslav.baidatskyi%40zomro.org/2025/10/17/pasted-image-20251016225036_q50.jpg)

  * `"HostIp": "0.0.0.0"` \- порт доступен на всех интерфейсах (небезопасно)
  * `"HostIp": "127.0.0.1"` \- порт доступен только локально (безопасно)
  * Отсутствие `PortBindings` \- контейнер в пользовательской сети без публикации портов



###    
Проверка через nftables  


Если система использует nftables:
    
    
    sudo nft list ruleset | grep -A 10 "chain DOCKER"

Правила Docker будут в таблице `ip filter` или `inet filter` (для совместимости).

##    
  
Практическая часть: проверка доступности портов извне  


### Локальное сканирование с другого хоста  


**Nmap** — наиболее функциональный инструмент:
    
    
    nmap -Pn -p 8080,3306,6379 203.0.113.50

> Необходимо изменить IP адрес на свой сервер, и также указать необходимые Вам порты.

Параметры:

  * `-Pn` — пропустить ping-проверку (если ICMP заблокирован)
  * `-p` — список портов для сканирования



Пример вывода:

![Docker и сетевая безопасность Linux взаимодействие с iptables, UFW и nftables 12](https://cdn.zomro.com/uploads/blog/yaroslav.baidatskyi%40zomro.org/2025/10/17/pasted-image-20251016232925_q50.jpg)

Интерпретация:

  * `open` — порт открыт и отвечает
  * `filtered` — пакеты отбрасываются firewall (нет ответа)
  * `closed` — порт закрыт, сервис не слушает (RST-пакет от хоста)



**  
Netcat** — простая проверка доступности:
    
    
    nc -vz 203.0.113.50 8080

> Необходимо изменить IP адрес на свой сервер, и также указать необходимый Вам порт.

Вывод:

![Docker и сетевая безопасность Linux взаимодействие с iptables, UFW и nftables 13](https://cdn.zomro.com/uploads/blog/yaroslav.baidatskyi%40zomro.org/2025/10/17/pasted-image-20251016233005_q50.jpg)

**Telnet** — альтернатива netcat:
    
    
    telnet 203.0.113.50 8080

> Необходимо изменить IP адрес на свой сервер, и также указать необходимый Вам порт.

Успешное подключение:

![Docker и сетевая безопасность Linux взаимодействие с iptables, UFW и nftables 14](https://cdn.zomro.com/uploads/blog/yaroslav.baidatskyi%40zomro.org/2025/10/17/pasted-image-20251016233034_q50.jpg)

**cURL** — проверка HTTP-сервисов:
    
    
    curl -v --connect-timeout 5 http://203.0.113.50:8080/

> Необходимо изменить IP адрес на свой сервер, и также указать необходимый Вам порт.

Успешный ответ означает открытый порт с HTTP-сервисом.

### Использование онлайн-сканеров  


Для проверки из внешних сетей (если локальная проверка может дать ложные результаты из-за внутренних firewall-правил):

  * **Can You See Me** (canyouseeme.org) - простой веб-интерфейс
  * **Shodan** (shodan.io) - база проиндексированных портов (проверка через CLI: `shodan host 203.0.113.50`)
  * **Censys** (censys.io) - аналог Shodan



**Внимание** : использование внешних сканеров может нарушать политику провайдера или законодательство. Применять только для собственных серверов.

###   
Интерпретация результатов  


Результат | Значение | Действие  
---|---|---  
Порт открыт снаружи, но должен быть закрыт | Docker публикует порт на 0.0.0.0 | Изменить привязку на 127.0.0.1 или добавить правила в DOCKER-USER  
Порт фильтруется, но сервис должен быть доступен | Firewall корректно работает | Добавить исключение для нужных IP  
Порт закрыт, контейнер запущен | Контейнер в пользовательской сети без -p | Нормальное поведение для внутренних сервисов  
  
##    
  
Надёжные способы защиты и предотвращения утечек портов  
  


### Метод 1: Привязка портов к localhost  


Наиболее простой и безопасный способ для сервисов, доступ к которым должен осуществляться через reverse proxy:
    
    
    docker run -d -p 127.0.0.1:8080:80 nginx

  
В docker-compose.yml:
    
    
    services:
      webapp:
        image: nginx
        ports:
          - "127.0.0.1:8080:80"

После применения:
    
    
    sudo ss -lntp | grep :8080

  
Вывод:

![Docker и сетевая безопасность Linux взаимодействие с iptables, UFW и nftables 15](https://cdn.zomro.com/uploads/blog/yaroslav.baidatskyi%40zomro.org/2025/10/17/pasted-image-20251016233302_q50.jpg)

  
Порт доступен только локально. Внешний доступ настраивается через Nginx/Traefik/Caddy с SSL-терминацией и дополнительной защитой.

**Применимость** : веб-приложения, API, базы данных - любые сервисы, не требующие прямого доступа из интернета.

###    
Метод 2: Использование цепочки DOCKER-USER  


Docker гарантирует, что цепочка `DOCKER-USER` обрабатывается **перед** любыми правилами разрешения Docker. Это официальный механизм для добавления пользовательских правил.  


#### Базовая конфигурация  


Разрешить established/related соединения:
    
    
    sudo iptables -I DOCKER-USER -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT

  
Разрешить доступ с конкретной подсети:
    
    
    sudo iptables -I DOCKER-USER -s 203.0.113.0/24 -j ACCEPT

> Необходимо указать свой вариант подсети.

  
Разрешить конкретный порт контейнера с определённого IP:
    
    
    sudo iptables -I DOCKER-USER -p tcp -s 198.51.100.50 -d 172.17.0.2 --dport 80 -j ACCEPT

> Необходимо указать свой порт и соответствующий IP-адрес.

  
Заблокировать всё остальное:
    
    
    sudo iptables -A DOCKER-USER -j DROP

#### Полный пример с логированием
    
    
    #!/bin/bash
    
    # Clear existing DOCKER-USER rules (except RETURN)
    sudo iptables -F DOCKER-USER
    
    # Established/Related
    sudo iptables -I DOCKER-USER 1 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
    
    # Allow access from the office network
    sudo iptables -I DOCKER-USER 2 -s 203.0.113.0/24 -j ACCEPT
    
    # Allow access from monitoring servers
    sudo iptables -I DOCKER-USER 3 -s 198.51.100.10 -j ACCEPT
    sudo iptables -I DOCKER-USER 4 -s 198.51.100.11 -j ACCEPT
    
    # Log blocked connections
    sudo iptables -I DOCKER-USER 5 -m limit --limit 5/min -j LOG --log-prefix "DOCKER-BLOCK: " --log-level 4
    
    # Default blocking
    sudo iptables -A DOCKER-USER -j DROP

> Необходимо указать свои варианты IP-адресов.

  
Проверка:
    
    
    sudo iptables -L DOCKER-USER -n --line-numbers -v

  
Вывод:

![Docker и сетевая безопасность Linux взаимодействие с iptables, UFW и nftables 16](https://cdn.zomro.com/uploads/blog/yaroslav.baidatskyi%40zomro.org/2025/10/17/pasted-image-20251016222831_q50.jpg)

#### Сохранение правил  


**Debian/Ubuntu** :
    
    
    sudo apt install iptables-persistent
    sudo netfilter-persistent save

**RHEL/CentOS** :
    
    
    sudo service iptables save

**Альтернатива** \- скрипт в `/etc/docker/docker-firewall.sh`, вызываемый из systemd-юнита.

###    
Метод 3: Пользовательские Docker-сети без публикации портов  


Для микросервисной архитектуры, где сервисы общаются внутри кластера:
    
    
    docker network create --driver bridge internal-net
    docker run -d --name webapp --network internal-net nginx
    docker run -d --name api --network internal-net python:app

Контейнеры доступны друг другу по именам (DNS внутри сети), но не имеют публичных портов. Доступ из интернета только через gateway-контейнер (Nginx, Traefik) с `-p 127.0.0.1:443:443`.

  
В docker-compose.yml:
    
    
    version: '3.8'
    
    services:
      webapp:
        image: nginx
        networks:
          - internal
    
      database:
        image: postgres
        networks:
          - internal
    
      gateway:
        image: traefik
        ports:
          - "127.0.0.1:443:443"
        networks:
          - internal
    
    networks:
      internal:
        driver: bridge
        internal: false  # false for internet access, true for complete isolation

### Метод 4: Отключение автоматического управления iptables  


**Внимание** : требует глубоких знаний сетевой конфигурации. Не рекомендуется для большинства случаев.

  
Файл `/etc/docker/daemon.json`:
    
    
    {
      "iptables": false
    }

  
После применения:
    
    
    sudo systemctl restart docker

  
Последствия:

  * Docker **не создаёт** правила NAT, маршрутизацию и изоляцию сетей
  * Необходимо вручную настроить DNAT/SNAT для каждого контейнера
  * Контейнеры теряют доступ в интернет без ручной настройки MASQUERADE



Пример ручной конфигурации:
    
    
    # Enable IP forwarding
    echo 1 > /proc/sys/net/ipv4/ip_forward
    
    # MASQUERADE for outgoing traffic
    sudo iptables -t nat -A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE
    
    # DNAT for incoming traffic
    sudo iptables -t nat -A PREROUTING -p tcp --dport 8080 -j DNAT --to-destination 172.17.0.2:80
    
    # Allow forwarding
    sudo iptables -A FORWARD -d 172.17.0.2 -p tcp --dport 80 -j ACCEPT
    sudo iptables -A FORWARD -s 172.17.0.2 -j ACCEPT

  
**Применимость** : специфичные сценарии с нестандартной сетевой архитектурой, использование внешних SDN-решений.

###    
Метод 5: Комбинация UFW + DOCKER-USER  


UFW можно использовать совместно с `DOCKER-USER` для управления доступом к хосту и контейнерам:

Файл `/etc/ufw/after.rules` (добавить в конец):
    
    
    # Rules for Docker
    *filter
    :DOCKER-USER - [0:0]
    
    # Allow established
    -A DOCKER-USER -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
    
    # Allow access from the office
    -A DOCKER-USER -s 203.0.113.0/24 -j ACCEPT
    
    # Logging
    -A DOCKER-USER -m limit --limit 3/min -j LOG --log-prefix "UFW-DOCKER-BLOCK: "
    
    # Blocking
    -A DOCKER-USER -j DROP
    
    COMMIT

Применение:
    
    
    sudo ufw reload

UFW управляет доступом к хосту, `DOCKER-USER` \- к контейнерам.

###    
Метод 6: Мониторинг и автоматические проверки  


Скрипт для проверки открытых портов (сохранить как `/usr/local/bin/check-docker-ports.sh`):
    
    
    #!/bin/bash
    TELEGRAM_BOT_TOKEN="your_token"
    TELEGRAM_CHAT_ID="your_chat_id"
    
    EXPOSED_PORTS=$(docker ps --format '{{.Ports}}' | grep -E '0\.0\.0\.0:[0-9]' || true)
    
    if [ -n "$EXPOSED_PORTS" ]; then
        MESSAGE="⚠️ WARNING: Docker containers with exposed ports:%0A$EXPOSED_PORTS"
        curl -s -X POST "https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMessage" \
             -d "chat_id=${TELEGRAM_CHAT_ID}" \
             -d "text=${MESSAGE}"
    fi

> Необходимо указать токен Вашего телеграм бота(`TELEGRAM_BOT_TOKEN`) и Ваш телеграм ID(`TELEGRAM_CHAT_ID`), чтобы получить оповещения от скрипта в свой Telegram-аккаунт.

Добавить в cron:
    
    
    0 */6 * * * /usr/local/bin/check-docker-ports.sh

  
Мониторинг новых слушающих портов (сохранить в `/usr/local/bin/port-monitor.sh`):
    
    
    #!/bin/bash
    
    TELEGRAM_BOT_TOKEN="your_token"
    TELEGRAM_CHAT_ID="your_chat_id"
    
    BASELINE="/var/log/listening-ports-baseline.txt"
    
    mkdir -p /var/log
    touch "$BASELINE" 2>/dev/null || { echo "Error: sudo privileges required"; exit 1; }
    
    CURRENT=$(docker ps --format '{{.Names}}: {{.Ports}}' | grep -E '(0\.0\.0\.0|\[::\])' | sort)
    
    if [ -z "$CURRENT" ]; then
            echo "ℹ️  No Docker containers with exposed ports found at $(date)"
            exit 0
    fi
    
    if [ -f "$BASELINE" ] && [ -s "$BASELINE" ]; then
        DIFF=$(diff <(cat "$BASELINE") <(echo "$CURRENT"))
        
        if [ -n "$DIFF" ]; then
            MESSAGE="🚨 Docker port change:%0A%0A$DIFF%0A%0A⏰ $(date '+%Y-%m-%d %H:%M:%S')"
                        
            curl -s -X POST "https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMessage" \
                -d "chat_id=${TELEGRAM_CHAT_ID}" \
                -d "text=${MESSAGE}" > /dev/null
                                
            echo "⚠️  New listening ports detected at $(date)"
        else
            echo "✓ No changes detected"
        fi
    else
        echo "📝 Creating baseline at $(date)"
    fi
    
    echo "$CURRENT" > "$BASELINE"

> Необходимо указать свои данные в `TELEGRAM_BOT_TOKEN` и `TELEGRAM_CHAT_ID`.

###   
Рекомендации по тестированию и CI/CD  


Интеграция проверок безопасности в pipeline:

**GitLab CI** пример:
    
    
    stages:
      - test
    
    security-audit:
      stage: test
      image: alpine:latest
      before_script:
        - apk add --no-cache nmap docker-cli docker-compose grep bash
      variables:
        DEPLOY_HOST: "scanme.nmap.org"
      script:
        - |
          if grep -E '^\s*-\s*"[0-9]+:' docker-compose.yml | grep -v '127.0.0.1'; then
            echo "ERROR: Found port bindings without localhost restriction"
            exit 1
          fi
        - |
          nmap -Pn -p 1-10000 $DEPLOY_HOST | grep -E '^[0-9]+/tcp\s+open' > open_ports.txt
          if [ -s open_ports.txt ]; then
            echo "WARNING: Open ports detected:"
            cat open_ports.txt
          fi
      only:
        - main
        - production

> Перед использованием измените `DEPLOY_HOST` на свой IP-адрес продакшена. Также проверьте названия веток в элементе `only`.

**GitHub Actions** пример:
    
    
    name: Security Audit
    
    on:
      push:
        branches: [main]
      pull_request:
    
    jobs:
      docker-security:
        runs-on: ubuntu-latest
        steps:
          - uses: actions/checkout@v3
    
          - name: Check for unsafe port bindings
            run: |
              if grep -E '^\s*-\s*"[0-9]+:' docker-compose.yml | grep -v '127.0.0.1'; then
                echo "::error::Unsafe port binding detected in file (not bound to 127.0.0.1)"
                exit 1
              fi

**Скрипт для автоматической проверки после деплоя** :
    
    
    #!/bin/bash
    # deploy-security-check.sh
    
    set -e
    
    DEPLOY_HOST="${1:-localhost}"
    EXPECTED_PORTS="${2:-443}"
    
    echo "=== Running post-deploy security check on $DEPLOY_HOST ==="
    
    # 1. Checking docker inspect on 0.0.0.0 bindings
    echo "[*] Checking container port bindings..."
    UNSAFE_BINDINGS=$(docker ps -q | xargs -I {} docker inspect {} \
      | jq -r '.[].HostConfig.PortBindings | to_entries[] | select(.value[].HostIp == "0.0.0.0") | .key' \
      | wc -l)
    
    if [ "$UNSAFE_BINDINGS" -gt 0 ]; then
        echo "[!] ERROR: Found $UNSAFE_BINDINGS containers with 0.0.0.0 port bindings"
        docker ps -q | xargs -I {} docker inspect {} \
          | jq -r '.[].Name, .[].HostConfig.PortBindings | to_entries[] | select(.value[].HostIp == "0.0.0.0")'
        exit 1
    fi
    
    # 2. Checking DOCKER-USER rules
    echo "[*] Checking DOCKER-USER chain..."
    DOCKERUSER_RULES=$(sudo iptables -L DOCKER-USER -n | grep -c "DROP\|REJECT" || true)
    
    if [ "$DOCKERUSER_RULES" -eq 0 ]; then
        echo "[!] WARNING: No blocking rules in DOCKER-USER chain"
    fi
    
    # 3. External port scanning
    if [ "$DEPLOY_HOST" != "localhost" ]; then
        echo "[*] Scanning $DEPLOY_HOST for open ports..."
        OPEN_PORTS=$(nmap -Pn -p 1-10000 --open "$DEPLOY_HOST" 2>/dev/null \
          | grep -E '^[0-9]+/tcp\s+open' \
          | grep -v "$EXPECTED_PORTS" \
          | wc -l)
        
        if [ "$OPEN_PORTS" -gt 0 ]; then
            echo "[!] ERROR: Found unexpected open ports:"
            nmap -Pn -p 1-10000 --open "$DEPLOY_HOST" | grep -E '^[0-9]+/tcp\s+open'
            exit 1
        fi
    fi
    
    # 4. Checking docker-proxy processes on 0.0.0.0
    echo "[*] Checking docker-proxy listeners..."
    EXPOSED=$(sudo ss -lntp | grep docker-proxy | grep '0.0.0.0:' | grep -v '127.0.0.1:' || true)
    
    if [ -n "$EXPOSED" ]; then
        echo "[!] ERROR: docker-proxy listening on 0.0.0.0:"
        echo "$EXPOSED"
        exit 1
    fi
    
    echo "[✓] Security check passed"

  
Использование в деплой-скрипте:
    
    
    #!/bin/bash
    # deploy.sh
    
    docker-compose up -d
    sleep 5
    ./deploy-security-check.sh $(hostname -I | awk '{print $1}') "443,22"
    

##    
  
  
План действий и чек-лист для администраторов  


### Немедленные действия (первый аудит)  


1\. **Инвентаризация открытых портов** :
    
    
    sudo ss -lntp | grep docker-proxy | grep '0.0.0.0:' | awk '{print $4}'
    sudo docker ps --format 'table {{.Names}}\t{{.Ports}}'

2\. **Проверка правил iptables** :
    
    
    sudo iptables -t nat -L DOCKER -n
    sudo iptables -L DOCKER-USER -n

3\. **Анализ конфигурации контейнеров** :
    
    
    docker ps -q | xargs docker inspect | jq '.[].HostConfig.PortBindings'

4\. **Внешнее сканирование** (с другого хоста):
    
    
    nmap -Pn -p 1-65535 your-server-ip

> Вместо `your-server-ip` нужно указать IP-адрес своего сервера.

###    
Краткосрочные меры 

5\. **Переконфигурация критичных сервисов** :

  * Изменить биндинги на `127.0.0.1` для внутренних сервисов
  * Обновить `docker-compose.yml` файлы
  * Перезапустить контейнеры



6\. **Настройка DOCKER-USER** :

  * Создать и применить скрипт с правилами
  * Протестировать доступность сервисов
  * Сохранить правила через `netfilter-persistent`



7\. **Настройка мониторинга** :

  * Установить скрипты проверки портов
  * Добавить задачи в cron
  * Настроить алерты



###   
Долгосрочные улучшения (постоянная практика)  


8\. **Интеграция в CI/CD** :

  * Добавить проверки в pipeline
  * Автоматизировать сканирование после деплоя
  * Документировать процедуры



9\. **Регулярный аудит** (ежемесячно):

  * Проверка новых контейнеров
  * Обновление правил firewall
  * Анализ логов блокировок



10\. **Обучение команды** :

  * Распространение best practices
  * Код-ревью docker-compose файлов
  * Обновление документации



##   
Заключение и ключевые выводы

Docker существенно упрощает развёртывание приложений, но его прямая интеграция с netfilter требует глубокого понимания механизмов работы с iptables/nftables. Основные трудности безопасности возникают не из-за фундаментальных уязвимостей, а из-за неправильных конфигурационных предположений.

### Ключевые принципы

  1. **Docker НЕ обходит iptables** \- он активно использует netfilter, но управляет правилами на более высоком приоритете
  2. **UFW работает на другом уровне** \- его правила применяются после NAT-трансляции Docker
  3. **Порядок правил критичен** \- ручные правила должны добавляться в `DOCKER-USER` или через `-I` (insert)
  4. **Привязка к localhost** \- простейший и надёжный способ защиты внутренних сервисов
  5. **Регулярный аудит обязателен** \- конфигурация со временем дрейфует, необходим автоматический контроль



###   
Рекомендуемый подход

Для production-окружений рекомендуется комбинированная стратегия:

  * **Внутренние сервисы** (БД, кеш, очереди) - привязка к `127.0.0.1` без исключений
  * **Веб-приложения** \- доступ через reverse proxy (Nginx/Traefik) на localhost с SSL-терминацией
  * **API и микросервисы** \- пользовательские Docker-сети без публикации портов
  * **Внешний доступ** \- строгая фильтрация через `DOCKER-USER` по IP-адресам/подсетям
  * **Мониторинг** \- автоматические проверки и алерты на изменения конфигурации



### 

### Финальная проверка

После применения защитных мер необходимо провести полный цикл проверок:
    
    
    # 1. Local check
    sudo ss -lntp | grep '0.0.0.0:' | grep docker-proxy
    
    # 2. Check iptables
    sudo iptables -L DOCKER-USER -n -v | grep DROP
    
    # 3. Container check
    docker ps -q | xargs docker inspect | jq -r '.[] | select(.HostConfig.PortBindings != null) | .Name, .HostConfig.PortBindings'
    
    # 4. External scan
    nmap -Pn -p- your-server-ip

> Вместо `your-server-ip` нужно указать IP-адрес своего сервера.

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

Похожие статьи

[HestiaCP: Как выдать SSL сертификат для домена, и установить Wordpress](https://zomro.com/rus/blog/faq/252-hestiacp-kak-vydat-ssl-sertifikat-dlja-domena-i-ustanovit-wordpress) [Как запустить круглосуточную трансляцию YouTube на linux](https://zomro.com/rus/blog/faq/213-kak-zapustit-kruglosutochnuju-transljaciju-youtube-na-linux) [Снимки Cloud VPS: как создать, восстановить и перенести сервер](https://zomro.com/rus/blog/articles/645-cloud-vps-snapshots-how-to-create-restore-and-migrate-a-server) [Как произвести трассировку](https://zomro.com/rus/blog/faq/331-kak-proizvesti-trassirovku) [WordPress Multisite: Как создать и управлять несколькими сайтами из одной админ панели](https://zomro.com/rus/blog/faq/322-wordpress-multisite-how-to-create-and-manage-multiple-sites-from-one-admin-panel)

[ Выделенный сервер с GPU от €174.80/мес ](/dedicated-servers/type=gpu)
