Настройка файрвола UFW без потери доступа к серверу
Верный порядок команд при настройке UFW, правила IPv6, nftables в роли бэкенда, ограничение частоты через ufw limit и путь назад через консоль, если что-то всё-таки пошло не так.
Пакетный фильтр на root-сервере не роскошь, а базовое оснащение. UFW (Uncomplicated Firewall) делает работу с ним приятно простой, но у него есть особенность, из-за которой каждый год тысячи администраторов теряют доступ к собственным серверам: команда, включающая файрвол, способна оборвать ту самую SSH-сессию, из которой её запускают. В этом руководстве показан порядок действий, при котором такого не происходит, и, что почти важнее, путь назад, если это всё-таки случилось.
Все сведения относятся к Debian 13 (trixie), Debian 12 (bookworm), Ubuntu 24.04 LTS и Ubuntu 22.04 LTS. Команды рассчитаны на работу от имени root. Если вы работаете под обычным пользователем, добавляйте перед каждой командой sudo.
Почему порядок действий решает всё
Классическая ошибка выглядит так: сначала политика по умолчанию выставляется на «отбрасывать весь входящий трафик», затем включается файрвол, а правило для SSH планируется дописать потом, уже спокойно. Ровно между этими шагами и лежит промежуток, в котором сервер перестаёт быть доступным.
Причина, по которой эта ошибка так часто остаётся незамеченной, коварнее самой ошибки. В файле /etc/ufw/before.rules у UFW есть правило, пропускающее уже установленные соединения:
-A ufw-before-input -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
Ваша текущая SSH-сессия — это уже установленное соединение. Она переживёт включение файрвола даже в том случае, если правила для SSH нет вообще. Приглашение командной строки остаётся на месте, всё выглядит нормально. И только следующая попытка подключения, то есть обычно ваш вход на следующее утро, упирается в таймаут. Отсюда правило: пока файрвол не проверен второй, заново установленной сессией, первую сессию закрывать нельзя.
Простое правило: сначала разрешить, потом запретить, потом включить, потом проверить второй сессией, и только после этого закрывать первую.
До первой команды: запасной путь и инвентаризация
Прежде чем менять что-либо в пакетной фильтрации, проясните два вопроса.
1. Как вы попадёте на сервер без SSH?
У KVM root-серверов и выделенных серверов KernelHost VNC-консоль доступна прямо в личном кабинете. Эта консоль подключена не к сетевому стеку гостевой системы, а к слою виртуализации, соответственно к самому сетевому подключению. Поэтому правило файрвола внутри гостя её заблокировать не может. Зайдите через эту консоль один раз заранее и убедитесь, что знаете пароль root. Запасной путь, который впервые пробуют уже в аварийной ситуации, запасным путём не является.
2. Что вообще слушает на этом сервере?
Правила для служб, которых нет, безвредны. А вот служба, которую вы упустили из виду, обойдётся вам потерей доступа или простоем. Составьте общую картину:
ss -lntup
Столбец Local Address:Port при этом чётко разделяет 0.0.0.0:22 (только IPv4), [::]:22 (IPv6, а через dual-stack сокет обычно и IPv4) и 127.0.0.1:3306 (только локально, правило файрвола не нужно). Всё, что привязано к 127.0.0.1 или ::1, открывать не требуется.
Особенно важен фактический порт SSH. Не угадывать, а посмотреть. Команде нужны права на чтение хостовых ключей, поэтому она запускается от root либо через sudo:
sudo sshd -T | grep -i "^port "
Деталь, о которой умалчивают многие руководства: в Ubuntu 24.04 служба SSH запускается через активацию по сокету, то есть через ssh.socket, а не через ssh.service. Там systemctl is-enabled ssh.socket выдаёт enabled, а ssh.service выдаёт disabled. В Debian 12, Debian 13 и Ubuntu 22.04 всё ровно наоборот, там работает классическая постоянно запущенная служба.
Последствие для нашей темы неприятно конкретное: в Ubuntu 24.04 порт, о котором сообщает sshd -T, не обязательно совпадает с портом, который прослушивается на самом деле. Решающим там является параметр ListenStream в /lib/systemd/system/ssh.socket либо в дополняющем файле в каталоге /etc/systemd/system/ssh.socket.d/. Кто сменил свой порт SSH и положился на sshd -T, откроет в UFW неверный номер порта и потеряет доступ при следующем входе. На всех четырёх системах надёжен только один способ: посмотреть на процесс, который действительно слушает:
sudo ss -lntp | grep sshd
3. Аварийный выключатель
На случай, если что-то пойдёт не так, перед рискованным изменением поставьте таймер, который через десять минут сам выключит файрвол:
nohup sh -c 'sleep 600; ufw disable' >/dev/null 2>&1 &
Если всё заработало, отмените его:
pkill -f 'sleep 600; ufw disable'
Команда pkill завершает только внешний процесс оболочки. Процесс sleep продолжает работать осиротевшим и заканчивается без последствий, потому что вызвать после него ufw disable уже некому.
Порядок действий, при котором доступ не теряется
В Debian UFW, как правило, не предустановлен, в Ubuntu Server обычно уже есть. Установка в любом случае не повредит:
apt-get update
apt-get install -y ufw
ufw version
Теперь сам порядок, и именно в таком виде:
ufw allow 22/tcp comment 'SSH'
ufw default deny incoming
ufw default allow outgoing
ufw --force enable
Четыре замечания к этому:
- Разрешающее правило идёт раньше всего остального. UFW принимает правила и в выключенном состоянии, сохраняя их в
/etc/ufw/user.rules. При включении они действуют сразу. - Вместо
22/tcpможно использовать профиль приложения, напримерufw allow OpenSSH. Но полагаться на это вслепую не стоит: профили поставляет не сам UFW, а установленные пакеты. В Ubuntu каталог/etc/ufw/applications.d/без установленных служб пуст, иufw app listвыводит там только заголовокAvailable applications:без единой записи. В Debian уже сам пакет ufw приносит около 38 профилей, и профиль для SSH называется тамSSH. ПрофильOpenSSHв обоих дистрибутивах приходит из пакетаopenssh-server, то есть на серверах это обычная ситуация. При нестандартном порте SSH профиль в любом случае окажется бесполезным. Какие профили знает ваша система, покажетufw app list; запись через порт,ufw allow 22/tcp, работает на всех четырёх системах одинаково и потому надёжнее. ufw enableзадаёт интерактивный вопрос:Command may disrupt existing ssh connections. Proceed with operation (y|n)?. В скриптах и в ролях Ansible используйтеufw --force enable, иначе выполнение зависнет.- Комментарий после
commentпоявляется в выводеufw status. Иначе через полгода вы уже не вспомните, зачем открыт порт 8443.
Остальные службы добавляются после, например веб-сервер:
ufw allow 80/tcp comment 'HTTP'
ufw allow 443/tcp comment 'HTTPS'
И только теперь откройте второй терминал и войдите заново. Дело закрыто лишь тогда, когда этот вход прошёл успешно.
IPv6: второе семейство адресов, о котором забывают
IPv6 есть на каждом современном сервере, причём чаще всего без того, чтобы кто-то настраивал его специально. Если думать только про IPv4, в итоге получится файрвол, который управляет ровно половиной трафика, а вторую половину пропускает без разбора. Сначала проверьте, настроены ли вообще глобальные адреса IPv6:
ip -6 addr show scope global
Хорошая новость: во всех четырёх рассматриваемых дистрибутивах в /etc/default/ufw с завода стоит IPV6=yes. Тогда UFW параллельно ведёт правило IPv6 к каждому правилу IPv4. Проверять, а не доверять:
grep '^IPV6' /etc/default/ufw
Второе, более жёсткое доказательство — сама политика по умолчанию у цепочки IPv6:
ip6tables -L INPUT -n
В первой строке должно стоять Chain INPUT (policy DROP). Если там policy ACCEPT, а ниже нет цепочек ufw, значит по IPv6 ваш сервер полностью открыт, независимо от того, насколько хорошо выглядят правила IPv4.
Две ловушки:
- Изменение параметра
IPV6в/etc/default/ufwне вступает в силу черезufw reload. Нуженufw disable, а следомufw enable. Ровно в этом промежутке файрвола у вас на короткое время нет, поэтому не делайте этого мимоходом на системе, открытой в интернет. - Кто блокирует ICMPv6 целиком, разрушает собственную связность. Neighbor Discovery и «Packet too big» в IPv6 не опция, а часть протокола. Нужные типы UFW разрешает уже в
/etc/ufw/before6.rules. Трогайте этот файл, только если точно понимаете, что делаете.
Если правило по порту корректно создано в обоих семействах адресов, при добавлении UFW выводит две строки: Rule added и Rule added (v6). Нет второй строки, нет и половины защиты. Исключение составляют правила с ограничением по источнику: для ufw allow from 203.0.113.10 to any port 22 proto tcp появится только Rule added, и это правильно, потому что у адреса-источника IPv4 нет соответствия в IPv6.
Что UFW пишет на самом деле: nftables в роли бэкенда
Здесь много полузнания. На Debian 12, Debian 13, Ubuntu 22.04 и Ubuntu 24.04 картина одинаковая: UFW по-прежнему говорит на синтаксисе iptables, но сама команда iptables на всех четырёх системах представляет собой средство совместимости iptables-nft. То есть правила попадают в подсистему nftables ядра. Доказательство в одну строку:
iptables -V
Вывод заканчивается на (nf_tables). Если там (legacy), ваша система работает со старым бэкендом. Это работоспособно, но приводит к тому, что в ядре рядом лежат правила из двух разных миров и перекрывают друг друга. Какой вариант выбран, показывает update-alternatives --display iptables.
Со стороны nftables это выглядит так. Важно, что смотреть нужно только после включения файрвола:
apt-get install -y nftables
nft list tables
Дело в том, что пока UFW неактивен, таблицы попросту не существует: iptables-nft создаёт её лишь тогда, когда действительно загружаются правила, то есть не раньше ufw --force enable. До этого nft list tables остаётся пустым, а nft list table ip filter завершается сообщением Error: No such file or directory. Это не дефект, а ожидаемое состояние. Заглядывать внутрь имеет смысл только тогда, когда в списке появились table ip filter и table ip6 filter:
nft list table ip filter
Вывод начинается со строки Warning: table ip filter is managed by iptables-nft, do not touch!, и понимать её следует буквально: смотреть можно, править руками нельзя. Ниже расположены цепочки вида ufw-before-input, ufw-user-input и ufw-after-input. Именно здесь становится виден самый важный конфликт: не смешивайте UFW и написанные вручную правила nft. Команда nft flush ruleset удаляет из ядра все правила UFW, а сам UFW об этом ничего не узнает. После неё ufw status по-прежнему сообщает Status: active, хотя фактически не действует ни одно правило. Это один из самых неприятных источников ошибок вообще, потому что инструмент, которым вы проверяете, вас обманывает. Путь назад:
ufw reload
Поэтому после каждого вмешательства других инструментов работы с файрволом (Docker, Kubernetes, VPN-программы, iptables-persistent) проверяйте не статус UFW, а фактический набор правил в ядре.
ufw limit против перебора паролей, и где он заканчивается
Для SSH в UFW предусмотрено ограничение частоты подключений:
ufw limit 22/tcp comment 'SSH rate limit'
Семантика чётко описана в руководстве: соединения разрешаются обычным образом, но отбрасываются, как только один адрес источника устанавливает шесть или больше новых соединений в течение 30 секунд. Значения зашиты жёстко и через интерфейс UFW не меняются. Под капотом работает модуль recent, его видно в выводе iptables -S. Для IPv6 UFW создаёт равнозначное правило, если доступен соответствующий модуль ядра, а он доступен во всех четырёх дистрибутивах.
Если раньше вы уже задали ufw allow 22/tcp, сейчас появится второе правило. Старое нужно убрать, иначе оно сработает первым и ограничение окажется бесполезным:
ufw status numbered
ufw delete allow 22/tcp
Если вместо этого удалять по номеру (ufw delete 3), учтите две особенности. Во-первых, ufw status numbered нумерует правила IPv4 и IPv6 сквозным образом в одном общем списке, и после каждого удаления все последующие номера сдвигаются. Поэтому удаляйте всегда по одному правилу и выводите список заново, либо идите от самого большого номера вниз. Во-вторых, UFW при этом задаёт интерактивный вопрос: Proceed with operation (y|n)?.
А теперь честная оценка, которой в большинстве руководств нет:
- Против распределённых атак это не помогает. Подсчёт ведётся по каждому адресу источника отдельно. Ботнет с тысячей адресов делает по пять попыток с адреса и остаётся ниже порога.
- Оно бьёт по вашей собственной автоматизации. Скрипт резервного копирования с множеством отдельных вызовов
rsync, запуск Ansible с несколькими форками или задание CI могут упереться в тот же предел. Тогда без доступа останется не атакующий, а ваш собственный конвейер развёртывания. Для таких источников лучше поставить перед ограничением явное исключение, напримерufw allow from 203.0.113.10 to any port 22 proto tcpс реальным адресом вашего сборочного сервера. - Оно не заменяет аккуратную настройку SSH. Самый действенный шаг против подбора паролей: отключить пароли вовсе, то есть
PasswordAuthentication noв/etc/ssh/sshd_configлибо в файле в каталоге/etc/ssh/sshd_config.d/. Кто не принимает пароли, у того их и не подберут. В дополнение имеет смысл Fail2ban: в отличие отufw limit, он реагирует на записи в журналах и блокирует дольше.
Как понять, что всё действительно сработало
«Команда отработала без ошибок» — это не доказательство. Четыре проверки, которые действительно о чём-то говорят:
Первое: общее состояние.
ufw status verbose
Ожидается вывод такого вида:
Status: active
Logging: on (low)
Default: deny (incoming), allow (outgoing), disabled (routed)
New profiles: skip
To Action From
-- ------ ----
22/tcp LIMIT IN Anywhere
22/tcp (v6) LIMIT IN Anywhere (v6)
Решающей является вторая строка с пометкой (v6). Без неё покрытие IPv6 отсутствует.
Второе: перезагрузка. Файрвол, который не переживает перезагрузку, бесполезен.
systemctl is-enabled ufw
Ответ должен быть enabled. А затем действительно перезагрузите сервер один раз и войдите в него. Это единственная проверка, которая по-настоящему отвечает на вопрос.
Третье: взгляд снаружи. Проверьте с другого хоста, действительно ли закрыт порт, который вы намеренно не открывали, например командой nc -zv ВАШ-IP 3306. Важно: проверка с localhost не доказывает ничего, потому что трафик через интерфейс обратной петли UFW пропускает всегда.
Четвёртое: журналы. Здесь между дистрибутивами есть настоящая разница. В настройке по умолчанию low UFW записывает заблокированные пакеты через журнал ядра. В Ubuntu 22.04 и 24.04 присутствует rsyslog, поэтому сообщения дополнительно попадают в /var/log/ufw.log. В Debian 12, и особенно в Debian 13, в минимальных установках rsyslog отсутствует, и этого файла там просто нет. Способ, который работает везде:
journalctl -k -n 50
Искать нужно строки, начинающиеся с [UFW BLOCK]. Если хочется видеть больше, поднимите уровень (ufw logging medium), а затем верните его обратно на ufw logging low. На сервере с публичным трафиком повышенный уровень заполняет накопитель быстрее, чем кажется.
Сообщения об ошибках дословно
ERROR: problem running ufw-init: чаще всего причина в конфликте со вторым инструментом работы с файрволом, обычно с nftables.service или iptables-persistent, либо в смешении бэкендов legacy и nft. Проверьте iptables -V и отключите конкурирующие службы. Кроме того, в составе UFW идёт проверочный скрипт, который последовательно проходит требования к ядру и сообщает, какое требование к модулю не выполняется.
ERROR: Could not find a profile matching 'OpenSSH': профиля приложения нет, потому что пакет openssh-server не установлен или файл в каталоге /etc/ufw/applications.d/ был удалён. В Ubuntu этот каталог без установленных служб и так пуст. Используйте вместо профиля номер порта.
ERROR: Bad port: обычно опечатка или имя службы, которого нет в /etc/services. Номера портов всегда однозначны.
Skipping adding existing rule, соответственно Skipping adding existing rule (v6): это не сообщение об ошибке, а указание на то, что правило уже существует. Если вам кажется, что правило изменено, а оно остаётся прежним, причина именно в этом.
ERROR: Invalid position '0': при удалении и вставке UFW считает с 1, а не с 0. Номера берутся из ufw status numbered и сдвигаются после каждого удаления. Поэтому удаляйте всегда от самого большого номера вниз.
WARN: Rules updated but not applied: правило лежит в конфигурации, но файрвол неактивен. Не хватает команды ufw enable.
Различия дистрибутивов одним взглядом
- Debian 13 (trixie): UFW нужно доустановить. Бэкенд nf_tables.
IPV6=yesс завода. В минимальных установках rsyslog отсутствует, поэтому журналы читаются черезjournalctl -k. SSH черезssh.service. - Debian 12 (bookworm): UFW нужно доустановить. Бэкенд nf_tables.
IPV6=yesс завода. rsyslog присутствует в зависимости от варианта установки, поэтому наличие/var/log/ufw.logне гарантировано. SSH черезssh.service. - Ubuntu 24.04 LTS: UFW есть в серверных образах, но выключен. Бэкенд nf_tables.
IPV6=yesс завода. Файл/var/log/ufw.logприсутствует. SSH через активацию по сокетуssh.socket, поэтому прослушиваемый порт всегда проверяйте командойsudo ss -lntp | grep sshd, а не черезsshd -T. - Ubuntu 22.04 LTS: UFW есть в серверных образах, но выключен. Бэкенд nf_tables.
IPV6=yesс завода. Файл/var/log/ufw.logприсутствует. SSH черезssh.service.
Что одинаково на всех четырёх системах: после установки UFW всегда выключен. Никто не включит вам файрвол незаметно, и никто не выключит его незаметно.
Docker обходит UFW стороной
Если на сервере работает Docker, действует важное ограничение: опубликованные порты контейнеров игнорируют ваши правила UFW. Docker создаёт собственные цепочки и работает с преобразованием адреса назначения, поэтому пакеты проходят мимо цепочек UFW в INPUT. То есть docker run -p 8080:80 доступен снаружи, хотя ufw status показывает аккуратное «deny incoming».
Самая простая и устойчивая контрмера состоит в том, чтобы вообще не публиковать порт на всех адресах, а только локально, и вести доступ через reverse proxy:
docker run -d -p 127.0.0.1:8080:80 nginx
В файле Compose этому соответствует запись порта "127.0.0.1:8080:80". Как вариант, существует возможность встраивать собственные правила UFW в зарезервированную Docker цепочку DOCKER-USER. Это действенно, но требует сопровождения и повторной проверки при каждом обновлении Docker. Привязка к 127.0.0.1 решает проблему в корне.
Если доступ всё-таки потерян
Это случилось, SSH больше не отвечает. По порядку:
- Не перезагружать. Перезагрузка не поможет, ведь UFW включён как служба systemd и восстанавливает свои правила при старте. Сервер вернётся ровно таким же закрытым, каким его выключили.
- Открыть VNC-консоль в личном кабинете и войти там под root.
- Выключить файрвол:
ufw disable. Так вы снова в игре, но и снова без защиты. - Найти причину, а не гадать. Посмотрите
ufw status numberedи фактический порт SSH изss -lntup. В девяти случаях из десяти причина одна из трёх: разрешающего правила для SSH не было вовсе, правило указывает на порт 22, тогда как sshd слушает другой порт, либо доступ был ограничен IP-адресом, которого у вашего подключения уже нет (динамическая выдача адресов у интернет-провайдера). - Исправить и включить заново, на этот раз в правильном порядке и с поставленным аварийным выключателем.
Если нужно полностью сбросить набор правил, есть команда ufw reset. Важно знать: она выключает файрвол и кладёт резервные копии прежних файлов правил в /etc/ufw/, добавляя отметку времени в имя файла. То есть в спорном случае можно перечитать, что действовало раньше. Никогда не запускайте её по SSH-соединению, если рядом не открыта консоль.
Разумная начальная конфигурация
Для типичного веб-сервера полная последовательность выглядит так:
apt-get update
apt-get install -y ufw
ufw default deny incoming
ufw default allow outgoing
ufw limit 22/tcp comment 'SSH rate limit'
ufw allow 80/tcp comment 'HTTP'
ufw allow 443/tcp comment 'HTTPS'
ufw --force enable
ufw status verbose
Обратите внимание: здесь default deny incoming хотя и стоит перед правилом для SSH, но включение происходит в самом конце. Пока UFW неактивен, политика по умолчанию вреда не приносит. Критично исключительно состояние в момент выполнения enable, а к этому времени правило для SSH давно сохранено.
Кто хочет вдобавок сделать административные доступы достижимыми только из собственной сети, работает с правилами, ограниченными по источнику, по образцу ufw allow from 203.0.113.0/24 to any port 22 proto tcp. Ещё чище вовсе не выставлять административные службы в открытую сеть, а подключать их через VPN. Экземпляр WireGuard настраивается на любой из четырёх систем за несколько минут и заменяет целую стопку исключений в файрволе одним открытым портом UDP.
И напоследок оценка, которую стоит держать в голове: UFW фильтрует на самом сервере. Против объёмных атак, насыщающих канал, это по своей природе не помогает, ведь к моменту, когда ядро отбрасывает пакеты, они уже прошли по линии. Для этого нужна фильтрация в сети перед сервером. В KernelHost этим занимается вышестоящая инфраструктура в дата-центре maincubes во Франкфурте-на-Майне с фильтрацией Arbor в реальном времени. Ваш локальный файрвол и защита на уровне сети решают две разные задачи, и нужны обе.
Частые вопросы
Отключит ли «ufw enable» мою текущую SSH-сессию сразу же?
Нужно ли включать IPv6 в UFW отдельно?
UFW использует iptables или nftables?
Что именно делает ufw limit?
Я больше не могу зайти на сервер по SSH. Поможет ли перезагрузка?
Почему мои контейнеры Docker доступны снаружи, несмотря на активный файрвол UFW?
Почему на моём сервере с Debian нет файла /var/log/ufw.log?
2026 KernelHost GmbH. Все права защищены. Эта инструкция охраняется авторским правом. Публикация на других сайтах, в том числе частично или в изменённом виде, без нашего письменного согласия не разрешается. Цитирование с указанием источника и активной ссылкой мы приветствуем.

