Шпаргалка по Docker на сервере, который уже настроен по статье о первичной настройке и защите. Что внутри: установка из официального репозитория, группа docker и почему она равна root, ловушка «Docker обходит UFW» и два способа её закрыть, ограничение логов контейнеров — и инцидент, после которого сервер остался с открытым периметром. Это не экспертный гайд, а то, что я сам сделал и на чём споткнулся.
Исходные данные
Сервер после первичной настройки: Ubuntu 26.04, пользователь demo с sudo, вход по ключу, UFW открыт на SSH и 80/443 (Caddy, статья о reverse proxy), fail2ban. Схема сайта: reverse proxy Caddy работает на хосте и слушает 80/443, приложение — в контейнере на 127.0.0.1:3000. <IP-сервера> подставляем свой.
💡 Все команды выполняем от demo, где нужен root — с sudo.
1. Установка
Docker запускает приложения в контейнерах: приложение со всеми зависимостями упаковано в образ и работает изолированно от системы. Обновить или откатить приложение — заменить контейнер, на хосте при этом ничего не меняется.
1.1. Какой пакет ставить
Зачем выбирать: Docker есть в двух местах, и пакеты из них конфликтуют — ставим только из одного.
| Откуда | Пакеты | Версия на 2026-10-15 |
|---|---|---|
| Репозиторий Ubuntu | docker.io, docker-compose-v2 | 29.1.3 |
| Официальный репозиторий Docker | docker-ce и плагины | 29.9.1 |
Пакет Ubuntu отстаёт на несколько минорных версий, но рабочий, и docker compose в нём тоже есть (пакет docker-compose-v2). Я ставлю из официального репозитория: свежие версии и исправления приходят сразу, а инструкции в документации Docker пишутся под него.
Проверяем, что не стоит ничего, что конфликтует с пакетами Docker (список — из официальной инструкции):
dpkg --get-selections docker.io docker-compose docker-compose-v2 docker-doc docker-buildx podman-docker containerd runc 2>/dev/null
dpkg --get-selections — какие из перечисленных пакетов установлены; 2>/dev/null — скрыть сообщения no packages found про отсутствующие. Root не нужен.
- ✅ Пустой вывод — ничего мешающего нет
- ❌ Есть строки с
install→ удалить эти пакеты:sudo apt removeи их имена.docker.ioконфликтует сdocker-ce, аcontainerdиruncиз Ubuntu — сcontainerd.ioот Docker
1.2. Ключ репозитория
Зачем: apt ставит пакеты только из репозиториев, подписи которых может проверить. Чтобы он доверял репозиторию Docker, кладём на сервер ключ, которым Docker подписывает пакеты.
sudo apt install ca-certificates curl
ca-certificates — корневые сертификаты для HTTPS, curl — скачать ключ. На Ubuntu Server оба уже стоят, apt ответит is already the newest version.
sudo install -m 0755 -d /etc/apt/keyrings
install -d — создать папку, -m 0755 — сразу с правами «владелец пишет, все читают». Сюда кладут ключи сторонних репозиториев.
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
curl -fsSL— скачать:-f— при ошибке HTTP не сохранять страницу ошибки вместо ключа,-s— без прогресса,-S— но ошибки показать,-L— идти по редиректам;-o …/docker.asc— сохранить в файл;chmod a+r— всем право чтения: apt читает ключ не от root.
1.3. Репозиторий
Зачем: ключ есть, теперь говорим apt, откуда брать пакеты и каким ключом их проверять.
sudo tee /etc/apt/sources.list.d/docker.sources > /dev/null << EOF
Types: deb
URIs: https://download.docker.com/linux/ubuntu
Suites: $(. /etc/os-release && echo "$VERSION_CODENAME")
Components: stable
Architectures: $(dpkg --print-architecture)
Signed-By: /etc/apt/keyrings/docker.asc
EOF
Описание репозитория в формате deb822 — в нём же лежат системные репозитории Ubuntu (/etc/apt/sources.list.d/ubuntu.sources):
URIs— адрес репозитория;Suites— релиз Ubuntu;$(. /etc/os-release && echo "$VERSION_CODENAME")подставляет его кодовое имя, для 26.04 —resolute;Components: stable— только стабильные версии;Architectures— только архитектура сервера (amd64); без строки apt будет спрашивать и другие включённые архитектуры и писатьSkipping acquire…;Signed-By— каким ключом проверять: только этим, а не любым из системных.
sudo tee файл > /dev/null — записать текст в файл, куда пишет только root; > /dev/null — не дублировать текст на экран.
💡 Здесь EOF без кавычек: подстановка $(...) должна выполниться. В статье о первичной настройке (харденинг SSH) было 'EOF' в кавычках — там подстановки не нужны.
sudo apt update
Обновить списки пакетов — теперь вместе с репозиторием Docker.
- ✅ В выводе есть строка
https://download.docker.com/linux/ubuntu resolute InRelease - ❌
404 Not Foundдляresolute→ Docker ещё не выпустил пакеты под этот релиз, ставимdocker.ioиз Ubuntu - ❌
NO_PUBKEY→ ключ не скачался или не читается, повторить 1.2
1.4. Пакеты
sudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
| Пакет | Что это |
|---|---|
docker-ce | Демон dockerd — служба, которая управляет контейнерами |
docker-ce-cli | Команда docker |
containerd.io | Среда, которая непосредственно запускает контейнеры |
docker-buildx-plugin | docker build (сборка образов) |
docker-compose-plugin | docker compose — запуск нескольких контейнеров по compose.yaml |
⚠️ Без -y. Перед подтверждением прочитать блок The following packages will be REMOVED — почему это важно, в разделе 6.
1.5. Проверка
sudo docker version
Версии клиента (docker) и сервера (dockerd). Сервер отвечает, только если служба запущена — apt запускает её и включает в автозагрузку сам.
- ✅ Есть блоки
ClientиServer - ❌
Cannot connect to the Docker daemon→sudo systemctl status docker
2. Группа docker
Что это: docker без sudo работает только у участников группы docker — так Docker решает, кому можно управлять демоном. Удобно, но у удобства есть цена, и решить надо до того, как добавлять.
sudo usermod -aG docker demo
-G docker— добавить в группуdocker;-a— добавить к существующим группам.
⚠️ Без -a пользователь будет удалён из всех остальных групп, в том числе из sudo.
Группа применится при новом входе — перезайти по SSH. Проверка:
id | grep -o docker && docker ps
id — группы текущего пользователя, grep -o docker — вывести слово docker, если оно там есть; && — только тогда выполнить docker ps (список контейнеров) без sudo.
- ✅
dockerи заголовок таблицыCONTAINER ID … - ❌ Пусто → не перезашёл по SSH
⚠️ Группа docker = права root. Любой участник группы может выполнить:
docker run -v /:/host -it ubuntu chroot /host
— смонтировать весь диск хоста в контейнер и войти в него — и получить root-оболочку на хосте без пароля sudo и без записи в auth.log. Второй фактор из статьи о первичной настройке (пароль sudo) для такого пользователя больше не работает: защита держится только на SSH-ключе.
Варианты:
- В группе — удобно, но утёкший ключ
demo= root на сервере. - Без группы,
sudo docker …— на пару символов длиннее, зато парольsudoостаётся вторым фактором.
Пользователей для CI и автодеплоя в группу docker не добавлять никогда: ключ CI лежит в чужой системе (GitHub, GitLab), и если он утечёт, у атакующего будет root на сервере. Для деплоя — отдельный пользователь с правами только на папку сайта.
3. Ловушка: Docker обходит UFW
Зачем проверять: кажется, что UFW закрывает всё, кроме разрешённого. Для портов контейнеров это не так — и ufw status этого не покажет.
Проверено на практике. Запускаем тестовый контейнер с опубликованным портом:
sudo docker run -d --name test -p 8080:80 nginx
sudo ufw status
docker run nginx— скачать образ веб-сервера nginx и запустить контейнер;-d— в фоне,--name test— имя, чтобы потом удалить;-p 8080:80— опубликовать порт: запросы на порт 8080 хоста передавать в порт 80 контейнера.
ufw status показывает 22, 80 и 443 — порта 8080 в нём нет. А он при этом доступен из интернета.
У меня такой вывод:
Status: active
To Action From
-- ------ ----
22/tcp ALLOW Anywhere
80/tcp ALLOW Anywhere # http
443/tcp ALLOW Anywhere # https
443/udp ALLOW Anywhere # http/3
...
Почему. Docker при -p 8080:80 создаёт правило DNAT в цепочке PREROUTING. Пакет переписывается до маршрутизации: к моменту, когда до него могли бы дойти правила UFW в цепочке INPUT, он уже адресован контейнеру и идёт через FORWARD. Там переходы Docker (DOCKER-USER, DOCKER-FORWARD) стоят первыми, раньше цепочек ufw-*, и пропускают пакет — до правил UFW он не доходит.
Проверять только снаружи — с локальной машины:
curl.exe -m 5 -I http://<IP-сервера>:8080
-m 5 — ждать не больше 5 секунд, -I — только заголовки ответа.
- ❌
HTTP/1.1 200 OK→ порт открыт в интернет - ✅
Connection timed out→ закрыт
У меня такой вывод:
HTTP/1.1 200 OK
Server: nginx/1.31.7
Content-Type: text/html
Content-Length: 896
...
200 OK от nginx из контейнера — порт 8080 отвечает снаружи, хотя UFW его не пропускал.
Убираем тестовый контейнер:
sudo docker rm -f test
rm -f — остановить и удалить контейнер одной командой.
4. Решение 1: публиковать порты только на localhost
Зачем: если порт контейнера слушает только 127.0.0.1, из интернета до него не достучаться вообще — неважно, что делает firewall. Наружу его отдаёт reverse proxy на хосте, а порты reverse proxy (80/443) UFW видит и контролирует. Это основное правило — всегда.
В docker run — адрес перед портом:
sudo docker run -d --name test-local -p 127.0.0.1:8080:80 nginx
--name test-local — имя, чтобы потом удалить контейнер по нему; -p 127.0.0.1:8080:80 — порт 8080 только на localhost.
В compose.yaml:
ports:
- "127.0.0.1:3000:3000" # ✅ только с хоста
- "3000:3000" # ❌ = 0.0.0.0:3000, в интернет
Без адреса Docker публикует порт на всех адресах (0.0.0.0 и [::]).
Проверка — на каких адресах опубликованы порты всех контейнеров:
sudo docker ps --format '{{.Names}} {{.Ports}}'
--format — вывести только имя контейнера и его порты.
- ✅
127.0.0.1:3000->3000/tcp - ❌
0.0.0.0:3000->3000/tcpили[::]:3000->3000/tcp→ порт опубликован на всех адресах, исправитьportsи пересоздать контейнер
Убираем тестовый контейнер — иначе он держит порт 8080, и тест в 5.3 не запустится:
sudo docker rm -f test-local
5. Решение 2: страховка в DOCKER-USER
Зачем: решение 1 держится на внимательности: один забытый 127.0.0.1: в compose — и порт снова в интернете. Страховка — правило firewall, которое режет все новые соединения из интернета к контейнерам, что бы ни было написано в ports.
Ставим его в цепочку DOCKER-USER — Docker создаёт её пустой, проверяет до своих правил и никогда не перезаписывает. Правила в остальных своих цепочках Docker при перезапуске затирает.
5.1. Имя внешнего интерфейса
Зачем: правило режет трафик, пришедший из интернета, — для этого нужно знать, через какой сетевой интерфейс он приходит.
ip -br link
Список сетевых интерфейсов, -br — кратко, по строке на интерфейс.
Внешний интерфейс — не lo и не docker0/br-…/veth… (это внутренние сети Docker). На моём сервере — eth0, у других провайдеров бывает ens3, enp1s0. Дальше везде подставляем своё имя.
5.2. Правило
sudo iptables -I DOCKER-USER -i eth0 -m conntrack --ctstate NEW -j DROP
-I DOCKER-USER— вставить правило в начало цепочки;-i eth0— только для пакетов, пришедших из интернета;-m conntrack --ctstate NEW— только новые входящие соединения;-j DROP— отбросить.
⚠️ --ctstate NEW обязателен. Без него правило отбросит и ответы на исходящие соединения контейнеров — они потеряют доступ в интернет (apt, npm install, внешние API).
⚠️ Правило только для IPv4. Пока IPv6 в сетях Docker не включён (по умолчанию — нет), этого достаточно: порты на [::] обслуживает процесс на хосте, и их закрывает UFW. Если включаешь IPv6 для контейнеров — то же правило через ip6tables и вторая строка в юните (5.4).
Reverse proxy на хосте это правило не задевает: он ходит к контейнеру через 127.0.0.1, а не через eth0.
5.3. Проверка по счётчикам
Зачем: правило может быть синтаксически верным, но не стоять на пути трафика (например, не тот интерфейс). Счётчики показывают, срабатывает ли оно на самом деле.
sudo iptables -L DOCKER-USER -n -v
-L DOCKER-USER — правила цепочки, -n — адреса цифрами, без DNS, -v — со счётчиками пакетов (pkts) и байтов.
Запускаем тот же тестовый контейнер, что в разделе 3, — порт 8080 на всех адресах, ровно то, от чего страхует правило:
sudo docker run -d --name test -p 8080:80 nginx
С локальной машины стучимся снаружи — как в разделе 3:
curl.exe -m 5 -I http://<IP-сервера>:8080
И снова счётчики:
sudo iptables -L DOCKER-USER -n -v
- ✅ Счётчик
pktsу правилаDROPвырос,curl.exe— таймаут → правило на пути и работает - ❌
pkts 0при попытках снаружи → правило не на пути трафика (не тот интерфейс?)
Убираем тестовый контейнер — иначе он так и останется на 0.0.0.0:8080:
sudo docker rm -f test
💡 Универсальный приём отладки firewall: не рассуждать о логике правил, а смотреть, какие счётчики двигаются.
5.4. Сохранение после перезагрузки
Зачем: правило, добавленное командой, живёт только до перезагрузки. Сохраняем его systemd-юнитом, который добавляет правило при каждом старте Docker. Не через iptables-persistent — почему, в разделе 6.
sudo tee /etc/systemd/system/docker-user-rule.service > /dev/null << 'EOF'
[Unit]
Description=Add DOCKER-USER firewall rule
After=docker.service
Requires=docker.service
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/bin/sh -c 'iptables -C DOCKER-USER -i eth0 -m conntrack --ctstate NEW -j DROP 2>/dev/null || iptables -I DOCKER-USER -i eth0 -m conntrack --ctstate NEW -j DROP'
[Install]
WantedBy=multi-user.target
EOF
After=+Requires=docker.service— запускаться после Docker и только вместе с ним: цепочкуDOCKER-USERсоздаёт Docker при старте;Type=oneshot— команда выполняется один раз и завершается;RemainAfterExit=yes— после этого юнит всё равно считаетсяactive;iptables -C … || iptables -I …— сначала проверить (-C), есть ли правило, и добавить, только если нет. Без проверки повторный запуск юнита добавит дубль;WantedBy=multi-user.target— запускать при обычной загрузке системы.
sudo systemctl daemon-reload
sudo systemctl enable --now docker-user-rule.service
daemon-reload— systemd перечитывает юниты: файл создан напрямую, без этого systemd о нём не знает;enable --now— включить в автозагрузку и сразу запустить.
Проверка — сколько правил DROP в цепочке:
sudo iptables -L DOCKER-USER -n | grep -c DROP
grep -c — посчитать строки с DROP.
- ✅
1 - ❌
0→ юнит не отработал:sudo systemctl status docker-user-rule - ⚠️
2и больше → дубли, лишние удалить:sudo iptables -D DOCKER-USER -i eth0 -m conntrack --ctstate NEW -j DROP(-D— удалить одно такое правило)
💡 Всё это — для штатного firewall-бэкенда Docker (iptables). В Docker 29 есть экспериментальный бэкенд nftables ("firewall-backend": "nftables" в daemon.json) — в нём цепочки DOCKER-USER нет, и этот раздел не подходит.
6. Инцидент: iptables-persistent удалил UFW
Зачем этот раздел: это самая дорогая ошибка во всей настройке — сервер работал с открытым периметром, и ничто об этом не сообщало. Здесь — как это случилось, как проверить, что с тобой такого нет, и как починить.
Что произошло. Чтобы сохранить правило DOCKER-USER, я поставил iptables-persistent (пакет, который сохраняет правила iptables и загружает их при старте) с флагом -y. apt молча удалил ufw: в пакете ufw прописано Breaks: iptables-persistent, netfilter-persistent — оба управляют netfilter. Флаг -y пропустил подтверждение, и строка The following packages will be REMOVED: ufw утонула в выводе.
Как проявилось — только после перезагрузки:
sudo: ufw: command not found
Chain INPUT (policy ACCEPT) ← должно быть DROP
iptables -L ufw-user-input ← No chain by that name
Каркас цепочек ufw-* восстановился из снимка, но правил внутри не было, а политика — ACCEPT: сервер работал с полностью открытым периметром.
Как нашли: контрольная перезагрузка с чеклистом (раздел 8). Без неё сервер так бы и жил.
На 26.04 воспроизводится. Проверить, не устанавливая:
apt-get -s install iptables-persistent | grep -E 'REMOVED|^Remv'
apt-get -s — симуляция: показать, что apt сделал бы, ничего не меняя; root не нужен. grep — оставить только строки об удалении.
The following packages will be REMOVED:
Remv ufw [0.36.2-9build1]
Диагностика — если есть подозрение, что UFW пропал:
command -v ufw || echo "ufw нет"
zgrep -h '^Remove:.*ufw' /var/log/apt/history.log*
sudo iptables -L INPUT -n | head -1
-
command -v ufw— есть ли командаufw; если нет — напечатать «ufw нет»; -
zgrep … history.log*— история apt, включая сжатые старые файлы: строкиRemove:сufw. Обычное обновление ufw (Upgrade:) сюда не попадает; -
iptables -L INPUT -n | head -1— первая строка цепочкиINPUTс политикой. -
✅ Путь к
ufw,zgrepничего не вывел (ufw через apt не удаляли),policy DROP -
❌
ufw нет,zgrepнашёл строкуRemove: … ufwилиpolicy ACCEPT→ чинить
Починка:
sudo apt remove iptables-persistent netfilter-persistent
sudo apt install ufw
sudo ufw show added
sudo ufw enable
sudo iptables -L INPUT -n | head -1
apt remove …— убрать пакеты, из-за которых удалилсяufw;apt install ufw— вернуть UFW. Его конфиг в/etc/ufw/переживаетremove(удаляется только приpurge), правила сохраняются;ufw show added— список добавленных правил; смотрим доenable: правило22/tcpдолжно быть на месте, иначе можно закрыть себе SSH;ufw enable— включить;iptables -L INPUT … | head -1— обязательно: должно статьpolicy DROP.
Как избежать:
| Правило | Почему |
|---|---|
Не ставить системные и сетевые пакеты с -y | -y убирает не только вопрос, но и момент, когда ты прочитал бы will be REMOVED |
Перед установкой незнакомого пакета — apt-get -s install ПАКЕТ | Покажет, что будет удалено, ничего не меняя |
| Не ставить два менеджера одного ресурса | ufw + iptables-persistent, ufw + firewalld, systemd-resolved + dnsmasq |
| Контрольная перезагрузка на новом сервере | Единственный способ узнать правду до инцидента |
Проверять policy, а не только ufw status | Status: active при policy ACCEPT = периметр открыт |
7. Логи контейнеров и live-restore
Зачем: по умолчанию логи контейнеров растут без ограничений — /var/lib/docker/containers/<id>/<id>-json.log за месяцы съедает диск. Симптом — «куда делись 30 ГБ». Заодно включаем live-restore, чтобы перезапуск Docker не останавливал контейнеры.
Настройки демона Docker лежат в /etc/docker/daemon.json. Сначала смотрим, есть ли он:
cat /etc/docker/daemon.json
- ✅
No such file or directory→ создаём командой ниже - ⚠️ Файл есть → команда ниже его перезапишет: добавить ключи в существующий файл вручную (
sudo nano)
sudo mkdir -p /etc/docker
Страховка: папку /etc/docker создаёт сам пакет docker-ce, но если её нет, tee ниже упадёт с No such file or directory. -p — без ошибки, если папка уже есть.
sudo tee /etc/docker/daemon.json > /dev/null << 'EOF'
{
"log-driver": "json-file",
"log-opts": { "max-size": "10m", "max-file": "3" },
"live-restore": true
}
EOF
log-driver: json-file— логи в файлах (так и по умолчанию),docker logsих читает;max-size/max-file— лог одного контейнера не больше трёх файлов по 10 МБ;live-restore— контейнеры продолжают работать, пока демон Docker перезапускается или обновляется в пределах патч-версии (29.9.1 → 29.9.2). При переходе на следующую версию (29.9 → 29.10) это не поддерживается: демон может потерять связь с контейнерами, и останавливать их придётся вручную.
Проверить файл до перезапуска — с ошибкой в JSON Docker не запустится:
sudo dockerd --validate --config-file /etc/docker/daemon.json
- ✅
configuration OK - ❌ Ошибка разбора → исправить файл, не перезапускать
sudo systemctl restart docker
Применяем настройки перезапуском демона. ⚠️ Запущенные контейнеры при этом остановятся; снова поднимутся только те, у кого задан restart: в compose (unless-stopped/always). На новом сервере контейнеров ещё нет, так что сейчас самое время.
💡 Если контейнеры уже работают, live-restore можно включить без простоя: sudo systemctl reload docker. Лимиты логов reload не применяет: они заработают только после restart демона, и то лишь для новых контейнеров (ниже).
💡 Лимиты логов применяются только к новым контейнерам. Уже запущенные нужно пересоздать: docker compose up -d --force-recreate.
Проверка — спрашиваем демон, а не файл. live-restore:
sudo docker info --format '{{.LiveRestoreEnabled}}'
docker info — настройки работающего демона, --format — вывести одно поле.
- ✅
true - ❌
false→ демон не перечитал файл: перезапуск не прошёл или ключ с опечаткой
Лимиты логов — по тому, что получает новый контейнер (docker info их не показывает, а json-file он выведет и без daemon.json — это драйвер по умолчанию):
sudo docker create --name logtest nginx
sudo docker inspect -f '{{.HostConfig.LogConfig.Type}} {{.HostConfig.LogConfig.Config}}' logtest
sudo docker rm logtest
docker create— создать контейнер, не запуская его: настройки логов он получает уже при создании;inspect -f— вывести из настроек контейнера только драйвер логов и его параметры;rm— удалить тестовый контейнер.- ✅
json-file map[max-file:3 max-size:10m] - ❌
json-file map[]→ лимиты не применились: перезапуск не прошёл или ключи с опечаткой
8. Проверка после перезагрузки
Зачем: правила firewall, юниты и настройки Docker должны пережить перезагрузку. Пока сервер ни разу не перезагружали, это не проверено — именно так нашёлся инцидент из раздела 6. На новом сервере делаем контрольную перезагрузку и сразу проверяем всё одним блоком.
sudo reboot
Через минуту снова подключаемся по SSH и выполняем:
echo "=== UFW ==="; sudo ufw status | head -1; sudo iptables -L INPUT -n | head -1
echo "=== DOCKER ==="; sudo docker ps --format '{{.Names}} {{.Ports}}'
echo "=== DOCKER-USER ==="; sudo iptables -L DOCKER-USER -n | grep -c DROP
echo "=== FAIL2BAN ==="; systemctl is-active fail2ban
Каждая строка — заголовок и проверка из разделов выше: статус и политика UFW, порты контейнеров, правило DOCKER-USER, fail2ban.
| Проверка | ✅ Хорошо | ❌ Плохо → чинить |
|---|---|---|
| UFW | Status: active + policy DROP | inactive / policy ACCEPT / command not found → раздел 6 |
| DOCKER | порты только 127.0.0.1:… | 0.0.0.0:… или [::]:… → раздел 4 |
| DOCKER-USER | 1 | 0 → юнит не отработал, раздел 5.4 |
| FAIL2BAN | active | failed → sudo journalctl -u fail2ban -n 30 --no-pager |
Итог: чеклист
- Docker из официального репозитория, конфликтующих пакетов нет (
dpkg --get-selections docker.io … runcдо установки → пусто) -
sudo docker versionпоказываетClientиServer - Решено, состоит ли
demoв группеdocker(группа = root); CI-пользователи — не в группе - Порты всех контейнеров — на
127.0.0.1(sudo docker ps --format '{{.Names}} {{.Ports}}') - Правило
DOCKER-USERстоит и переживает перезагрузку (sudo iptables -L DOCKER-USER -n | grep -c DROP→1) - Проверка 5.3 пройдена: с тестовым контейнером на
:8080счётчикDROPрастёт,curl.exe— таймаут; контейнер удалён -
iptables-persistentне ставили; UFW на месте,policy DROP(sudo iptables -L INPUT -n | head -1) - Логи контейнеров ограничены (
docker inspectнового контейнера →max-file:3 max-size:10m),live-restoreвключён (docker info→true) - Контрольная перезагрузка пройдена, все проверки раздела 8 — ✅