Антипаттерн: firewall «разрешить всё, потом разберёмся»
Сервер только поднят, ssh подключился, а приложение снаружи не отвечает — и вместо того чтобы разобраться, какое именно правило блокирует нужный порт, firewall просто выключают целиком: ufw disable, iptables -P INPUT ACCEPT, в облачной консоли — правило 0.0.0.0/0 на все порты сразу. Ошибка исчезает мгновенно, отладка идёт быстрее, а «настрою нормально, как только заработает» остаётся заметкой, к которой никто не возвращается. Разберём, как именно это делают, что конкретно ломается технически, и как настроить firewall правильно с первого дня — так, чтобы возвращаться к этому вопросу вообще не пришлось.
Содержание
Как это выглядит на практике
Антипаттерн редко выглядит как сознательное решение отказаться от защиты — обычно это способ побыстрее пройти момент, когда что-то не подключается, а разбираться в правилах некогда.
Вариант первый — полное отключение ufw. Приложение не отвечает на внешний запрос, разработчик не уверен, firewall ли тому причина, и вместо диагностики убирает переменную целиком:
ufw disable
Порт сразу становится доступен, проблема с точки зрения «работает — не трогай» решена. Возвращаться и включать ufw обратно с правильными правилами — отдельная задача, которая в план работ обычно не попадает.
Вариант второй — сброс политики iptables напрямую. Более низкоуровневый, но не менее частый случай — на сервере без ufw просто открывают всё через iptables:
iptables -P INPUT ACCEPT
iptables -P FORWARD ACCEPT
iptables -F
-F сбрасывает все существующие цепочки правил, а -P INPUT ACCEPT меняет политику по умолчанию на «пропускать всё, что не подошло ни под одно правило». После этой команды на сервере физически нет ни одного фильтра входящего трафика — открыт весь диапазон портов, а не только тот, ради которого чинили проблему.
Вариант третий — security group «на всё» в облачной консоли. В AWS, Yandex Cloud, VK Cloud или любой другой панели то же самое делается кликами: вместо правила «TCP 8080 от конкретного источника» добавляют правило с диапазоном портов 0-65535, протоколом All traffic и источником 0.0.0.0/0. Часто это делается ровно с той же формулировкой в голове — «сейчас отлажу, потом сузю до нужных портов и IP».
Общая черта всех трёх вариантов: задача «убрать ошибку подключения» решена, но решена не точечно — не добавлением конкретного разрешающего правила для конкретного порта, а снятием всех ограничений разом.
Почему это работает и почему это ловушка
Отключение firewall решает проблему подключения гарантированно и мгновенно — потому что причины ошибки может быть три (не то правило в firewall, не тот порт слушает приложение, не тот путь до сервера в сети), а отключение firewall убирает сразу первую из них, не требуя понимать, в чём была настоящая причина. Именно поэтому решение так соблазнительно: результат виден сразу, а разбираться, было ли дело именно в правиле, не нужно.
Ловушка в двух вещах сразу. Во-первых, отключение firewall — это диагностика через устранение, а не через понимание: если проблема была не в firewall, а, например, в том, что приложение слушает 127.0.0.1 вместо 0.0.0.0, отключённый firewall ничего не даст, а вернуть его включённым после уже никто не вспомнит, потому что «а вдруг опять сломается». Во-вторых, и это главное, — «потом настрою нормально» технически ничем не отличается от постоянного решения, пока кто-то не вернётся и не сделает эту работу руками. А мотивация возвращаться пропадает ровно в тот момент, когда всё заработало.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать VPSПроблема первая: временное становится постоянным
Это тот же механизм, что и с «потом закрою порт после отладки»: как только сервер отвечает и приложение доступно, задача считается закрытой в голове — хотя на самом деле закрыта только видимая часть, работоспособность, а не безопасность. Заметка «сузить security group» или «включить ufw обратно» откладывается до следующего релиза, следующий релиз откладывает её ещё раз, а через несколько месяцев про неё уже никто не помнит — ни автор изменения, ни тем более человек, который придёт в проект позже и увидит правило 0.0.0.0/0 all ports, не зная, было ли оно осознанным решением или забытым временным костылём.
Проблема в том, что firewall, в отличие от одного забытого порта, — это не точечная дыра, а полное отсутствие периметра. О том, как именно единичный забытый порт превращается в реальный инцидент, разобрано в статье сервер взломали через забытый порт — реконструкция инцидента — с полностью открытым firewall тот же сценарий срабатывает не для одного порта, а для всех сразу, и вероятность, что хотя бы один из них окажется уязвим, растёт кратно.
Проблема вторая: сервер виден сканерам по всем портам, а не только по нужным
Интернет непрерывно сканируется — это не гипотетическая угроза, а фоновый процесс. Автоматические сканеры (массовые сканы вроде masscan, поисковые системы устройств вроде Shodan и Censys, а также заметно менее благонамеренные боты) проходят весь диапазон IPv4-адресов по всем 65535 портам постоянно, без привязки к тому, что именно на сервере предполагалось открыть.
Пока firewall настроен разрешать только нужное — открыты, скажем, 22, 80 и 443 — сканер видит ровно эти три порта, и поверхность атаки ограничена тремя сервисами, которые администратор осознанно выставил наружу и, как предполагается, настроил с оглядкой на это. Как только firewall отключён целиком, сканер видит всё, что на сервере вообще слушает какой-либо интерфейс, — а это почти никогда не совпадает со списком «нужных» сервисов:
- отладочный порт приложения (
3000,8000,5000), который разработчик поднял локально для проверки и забыл остановить; - базу данных, поднятую с дефолтными настройками и без пароля или с паролем по умолчанию — Redis на
6379, MongoDB на27017, часто без аутентификации вообще, потому что «это же внутренний сервис»; - панель администрирования, поднятую на время миграции или настройки — Adminer, phpMyAdmin, Portainer без пароля, потому что «это временно, я один»;
- служебные порты систем оркестрации и мониторинга, которые по умолчанию слушают все интерфейсы и не рассчитаны на прямой доступ из интернета.
Ни один из этих сервисов не предполагался как публичный, но при отключённом firewall каждый из них становится доступен всему интернету наравне с намеренно открытыми 80 и 443. Поверхность атаки увеличивается не на проценты, а на порядки — с трёх контролируемых входных точек до всего, что физически запущено на машине в любой момент времени. Проверить, что реально слушает сервер прямо сейчас, можно командой ss -tulnp — и почти всегда список окажется длиннее, чем ожидалось; подробнее о том, как находить и закрывать лишнее, — в статье открытые порты на сервере: как проверить и закрыть.
Проблема третья: allow-by-default вместо deny-by-default
В основе всей путаницы лежит выбор между двумя противоположными философиями firewall, и антипаттерн выбирает более удобную в моменте, но менее безопасную по своей природе.
Allow-by-default («разрешить всё, потом закрывать ненужное») — политика по умолчанию ACCEPT, а закрывающие правила добавляются по мере того, как вспоминают, что стоит закрыть. У этого подхода есть фундаментальный изъян: он безопасен ровно настолько, насколько администратор не забыл добавить запрещающее правило для каждого нового сервиса, порта или временного процесса, который появится на сервере в будущем. Установили новый пакет, который поднимает свой порт, — он открыт, пока кто-то не вспомнит его закрыть. Забыли одно правило — оно осталось открытым и никак себя не проявляет, пока не станет проблемой.
Deny-by-default («запретить всё, разрешить только нужное») — политика по умолчанию DROP/REJECT, а входящий трафик проходит только туда, для чего явно прописано разрешение. У этого подхода противоположное свойство: он безопасен по умолчанию даже тогда, когда администратор ничего не забыл закрыть, — потому что закрыто изначально всё, и забыть можно только *открыть* то, что нужно, а это сразу заметно как не работающее соединение, а не как невидимая дыра. Ошибка в deny-by-default проявляется как «не подключается» — раздражающе, но безопасно. Ошибка в allow-by-default проявляется как «работает» — незаметно и потенциально годами.
| Подход | Что происходит при забытом правиле | Отказоустойчивость |
|---|---|---|
| Allow-by-default (разрешить всё, потом закрывать) | Забытый сервис остаётся открытым и виден всему интернету | Fail-open — ошибка = дыра в безопасности |
| Deny-by-default (запретить всё, потом разрешать) | Забытый сервис остаётся закрытым, максимум — не работает нужное | Fail-safe — ошибка = неудобство, не инцидент |
Это ровно тот принцип, который отключают вместе с ufw disable: сервер без firewall — это allow-by-default в чистом виде, разрешено абсолютно всё, и никакого закрывающего правила не появится, пока кто-то не вернётся и не настроит его руками.
Как настроить правильно с самого начала
Правильный порядок действий — deny-by-default с момента создания сервера, а не как отложенная задача после того, как «всё заработает». В ufw это делается четырьмя командами, и важен именно их порядок.
apt update && apt install ufw
# политика по умолчанию: входящий трафик запрещён, исходящий разрешён
ufw default deny incoming
ufw default allow outgoing
# сначала разрешаем SSH — иначе после enable потеряете доступ к серверу
ufw allow 22/tcp
# затем — конкретные порты, которые реально нужны
ufw allow 80,443/tcp
# включаем, только когда правило для SSH уже добавлено
ufw enable
Ключевой нюанс порядка: ufw allow 22/tcp должен быть выполнен до ufw enable, иначе при включении firewall с политикой deny incoming текущая SSH-сессия обрывается, а новая уже не подключится — сервер придётся восстанавливать через консоль провайдера (VNC/serial-консоль в панели облака), если такая есть, или переустанавливать. Если работаете по SSH на нестандартном порте или хотите дополнительно ограничить доступ по IP, используйте более точное правило:
ufw allow from 203.0.113.10 to any port 22 proto tcp
После включения обязательно проверьте фактическое состояние, а не то, что предполагали настроить:
ufw status verbose
Тот же принцип deny-by-default стоит применять и к облачной security group, если она есть отдельно от firewall внутри самой системы, — это второй, независимый слой, и оба должны быть настроены одинаково строго: конкретные порты, по возможности с ограничением по источнику для административных портов (SSH, панели управления), и никогда 0.0.0.0/0 на диапазон 0-65535 даже «на время». Если базовая настройка firewall на сервере ещё не сделана вообще, порядок первых шагов — от установки пакета до проверки правил — подробно разобран в статье как установить и настроить firewall ufw на VPS, а полный список того, что стоит проверить при первичной настройке нового сервера, помимо самого firewall, — в статье чек-лист безопасности нового сервера.
Отдельная практическая деталь: ufw управляет правилами iptables, но Docker при публикации портов (-p 8080:8080) добавляет собственные правила NAT напрямую в iptables, в обход цепочек, которыми управляет ufw. Это значит, что опубликованный через Docker порт может оказаться доступен снаружи даже при deny-by-default политике ufw — если это не учитывать, можно настроить firewall правильно и всё равно получить открытый порт мимо него. Проверять реальную картину стоит не по выводу ufw status, а по фактическому прослушиванию портов (ss -tulnp) и по правилам самого iptables (iptables -L -n -t nat).
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать VPSНужны сами нейросети для контента?
Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.
Частые вопросы
После ufw enable пропал доступ по SSH — что делать?
Значит, правило ufw allow 22/tcp (или для вашего нестандартного SSH-порта) не было добавлено до включения. Восстановить доступ можно через веб-консоль провайдера (VNC/serial console в панели VPS), зайти локально и выполнить ufw allow 22/tcp до повторного ufw enable — либо, если консоли нет, пересоздать сервер и в этот раз соблюсти порядок команд.
Как проверить, что сейчас реально открыто на уже работающем сервере, а не то, что должно быть открыто по документации?
ss -tulnp покажет все процессы, слушающие порты, локально на машине; ufw status verbose — какие правила действуют во встроенном firewall; отдельно нужно свериться с правилами security group в панели облака, потому что это независимый слой, который ufw status не показывает.
Политика default deny incoming не помешает ли исходящим соединениям приложения — например, к внешнему API или базе данных в облаке?
Нет, если оставлена ufw default allow outgoing, как в примере выше, — она разрешает любые исходящие соединения от сервера. Ограничивается только входящий трафик, инициированный извне, что и требуется для defense-by-default.
Если временно нужно открыть всё для теста — есть ли безопасный способ так сделать, не отключая firewall совсем?
Да — открыть конкретный порт для конкретного диапазона IP на время теста (ufw allow from <IP> to any port <порт>) и удалить правило командой ufw delete allow from <IP> to any port <порт> сразу после теста, вместо ufw disable. Это даёт ту же временную возможность подключиться, но не открывает остальные порты и не требует помнить о полном откате состояния всего firewall.
Правила ufw настроены верно, но порт всё равно доступен снаружи — в чём может быть дело?
Частая причина — публикация порта через Docker (-p), который добавляет собственные правила iptables в обход цепочек ufw. Проверяйте фактическую картину через iptables -L -n -t nat и ss -tulnp, а не только через ufw status, и рассматривайте привязку публикуемых портов к 127.0.0.1 с отдельным reverse-proxy там, где сервис не должен быть доступен напрямую.
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →