SSD Nodes Learn Hosting plans →
Руководства Matt ConnorАвтор: Matt Connor · Обновлено 2026-09-04

Настройка firewalld в Rocky Linux и AlmaLinux

Руководство по управлению firewalld: открытие портов SSH и HTTP, закрытие доступа и сохранение настроек через --permanent. Узнайте, как избежать сброса правил после перезагрузки.

Что такое firewalld и почему Rocky и AlmaLinux поставляют его по умолчанию

firewalld — это менеджер межсетевого экрана, установленный по умолчанию в Rocky Linux, AlmaLinux и других дистрибутивах, основанных на Red Hat Enterprise Linux (RHEL). Оба дистрибутива унаследовали этот выбор, а не сделали его самостоятельно. Это становится понятнее, если знать как Rocky и AlmaLinux начали пересобирать продукты Red Hat после смены курса CentOS. Сам по себе firewalld не занимается инспекцией пакетов. Он хранит конфигурацию и преобразует её в правила nftables. Одна команда, firewall-cmd, позволяет изменять настройки без остановки работы сервера. Инструкции в этом руководстве идентичны для обоих дистрибутивов, так как различия между Rocky и AlmaLinux касаются только обязательств по совместимости и списка поддерживаемых процессоров, а не работы межсетевого экрана.

Если вы уже знаете, как работает ufw на Ubuntu VPS, то понимаете суть задачи. firewalld добавляет две концепции, которых нет в ufw. Первая — это зоны: именованные политики, по которым распределяются пакеты. Вторая — разделение между текущими правилами в памяти и сохраненными правилами на диске; за это отвечает флаг --permanent, который является главным источником путаницы при работе с этим инструментом.

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

Откройте SSH перед выполнением любых других действий

В большинстве дистрибутивов Rocky и AlmaLinux служба firewalld уже установлена и запущена, а стандартная конфигурация разрешает SSH. Некоторые минимальные образы для облачных платформ не содержат этих настроек. Проверяйте состояние, а не полагайтесь на предположения.

sudo dnf install -y firewalld
sudo systemctl enable --now firewalld
sudo firewall-cmd --state

firewall-cmd --state выводит running. Если служба остановлена, любой другой вызов firewall-cmd вернет FirewallD is not running и завершится с ненулевым кодом. Это первое, что нужно проверить, если команда кажется нерабочей.

Теперь ознакомьтесь с текущими разрешенными правилами.

sudo firewall-cmd --list-all

Реальный вывод содержит еще несколько строк. Важны следующие:

public (active)
  target: default
  interfaces: eth0
  sources:
  services: cockpit dhcpv6-client ssh
  ports:
  rich rules:

Наличие ssh в строке services: — причина, по которой ваша сессия все еще активна. Если этой записи нет, добавьте ее перед внесением любых изменений. Запуск межсетевого экрана без правила для SSH приведет к разрыву соединения, и вы не сможете подключиться снова.

sudo firewall-cmd --permanent --add-service=ssh
sudo firewall-cmd --reload

target: default означает, что пакет, не соответствующий ни одному правилу, отклоняется с ответом ICMP (internet control message protocol) host-prohibited. В результате клиент, обращающийся к закрытому порту, немедленно получает No route to host. Установка цели в DROP заставляет сервер игнорировать такие запросы, из-за чего сканеры будут ожидать истечения времени ожидания (timeout).

sudo firewall-cmd --permanent --zone=public --set-target=DROP
sudo firewall-cmd --reload

Учитывайте последствия перед выполнением этой команды: DROP также запрещает серверу отвечать на ping, поэтому ваша система мониторинга перестанет получать данные.

Почему моё правило исчезло? Флаг --permanent

firewalld хранит две конфигурации одновременно. Runtime-конфигурация — это то, что ядро применяет прямо сейчас. Постоянная конфигурация (permanent) находится в /etc/firewalld/zones/public.xml и восстанавливается после перезагрузки службы или системы.

Команда без --permanent меняет только runtime-конфигурацию. Она вступает в силу немедленно, но пропадает после перезагрузки. Команда с --permanent записывает изменения в файл, но не меняет текущее состояние, поэтому порт останется закрытым до выполнения reload. Ни одно из этих поведений не является ошибкой. Оба варианта часто удивляют пользователей, так как команда в обоих случаях выводит success.

Всегда используйте оба варианта.

sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --reload

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

sudo firewall-cmd --list-services
sudo firewall-cmd --permanent --list-services

Первая команда выводит текущий набор правил. Вторая — сохранённый. Если в текущем наборе есть служба, которой нет в сохранённом, это правило исчезнет при следующей перезагрузке. Если в сохранённом наборе есть служба, которой нет в текущем, вы забыли выполнить reload. sudo firewall-cmd --runtime-to-permanent копирует все текущие настройки в файл постоянной конфигурации, что полезно после серии экспериментов.

--reload сохраняет состояние отслеживания соединений, поэтому ваша SSH-сессия не прервётся. --complete-reload также перезагружает модули ядра и сбрасывает это состояние, что обычно приводит к разрыву всех активных соединений, включая ваше. Используйте обычный reload.

В систему встроен механизм защиты. Runtime-правило может иметь срок действия.

sudo firewall-cmd --add-service=http --timeout=5m

Это правило удалится само через пять минут. Его нельзя использовать вместе с --permanent, и в этом смысл: оно существует для тестирования изменений, в которых вы не уверены. Старый метод защиты надёжнее. Держите вторую SSH-сессию открытой во время редактирования правил и не закрывайте её, пока не убедитесь, что новые правила работают, успешно выполнив вход в систему.

Зоны и почему на VPS важна только зона по умолчанию

Зона — это именованный набор разрешений с присвоенным уровнем доверия. firewalld помещает каждый входящий пакет ровно в одну зону. Сначала он сопоставляет исходный адрес пакета со списком sources: каждой зоны. Если совпадений нет, используется зона, к которой привязан входящий интерфейс. Если интерфейс не привязан ни к одной зоне, пакет попадает в зону по умолчанию.

sudo firewall-cmd --get-default-zone
sudo firewall-cmd --get-active-zones

На VPS с одним сетевым интерфейсом ответом почти всегда является public, и это единственная зона, которую вы будете использовать. firewall-cmd без аргумента --zone= применяется к зоне по умолчанию, поэтому каждая короткая команда в этом руководстве работает без указания имени зоны.

Вот ошибка, на которую можно потратить целый день. Если интерфейс привязан к другой зоне, ваши правила попадают в public, в то время как трафик обрабатывается в другом месте. В результате добавленные правила не работают, а система не выдает никаких предупреждений. --get-active-zones показывает текущую привязку:

public
  interfaces: eth0

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

sudo firewall-cmd --permanent --zone=public --change-interface=eth0
sudo firewall-cmd --reload

NetworkManager управляет интерфейсами в Rocky и AlmaLinux и восстанавливает зону при поднятии соединения. Установите зону там же, чтобы перезагрузка не отменила ваши настройки. Имя соединения возьмите из первой команды, так как оно редко совпадает с именем устройства.

sudo nmcli connection show
sudo nmcli connection modify "System eth0" connection.zone public

Сопоставление по источнику имеет приоритет над сопоставлением по интерфейсу, что позволяет применять разные политики для одного адреса. Встроенная зона trusted принимает всё.

sudo firewall-cmd --permanent --zone=trusted --add-source=203.0.113.10/32
sudo firewall-cmd --reload

Будьте осторожны с этой зоной. Она открывает все порты сервера для указанного адреса, включая базу данных, которую вы считали приватной. Используйте rich rule, если хотите открыть один порт, а не весь хост.

Что такое сервис в firewalld?

Сервис — это именованный набор портов, поставляемый в виде XML-файла. --add-service=https открывает 443/tcp, потому что /usr/lib/firewalld/services/https.xml определяет, что именно означает https.

sudo firewall-cmd --get-services
sudo firewall-cmd --info-service=https

--info-service выводит порты, скрывающиеся за этим именем:

https
  ports: 443/tcp

Используйте имя, если оно существует. Это упрощает чтение конфигурации в --list-all спустя полгода, а такие пакеты, как Cockpit, устанавливают собственные файлы сервисов. Используйте --add-port для всего, что не имеет определения.

Важный нюанс: сервис ssh означает только 22/tcp и ничего больше. Если вы перенесли SSH на другой порт в процессе укрепления доступа по SSH к серверу, то --add-service=ssh не откроет порт, который вы используете на самом деле.

sudo firewall-cmd --permanent --add-port=2222/tcp
sudo firewall-cmd --reload

В дистрибутивах на базе RHEL существует дополнительный уровень защиты. SELinux (Security-Enhanced Linux) присваивает метки номерам портов, и sshd не может привязаться к порту, который не соответствует его меткам. В этом случае сервис отказывается запускаться, а в логах появляется сообщение error: Bind to port 2222 on 0.0.0.0 failed: Permission denied.. Сначала назначьте порту соответствующую метку.

sudo dnf install -y policycoreutils-python-utils
sudo semanage port -a -t ssh_port_t -p tcp 2222

Как узнать, какие порты открыты прямо сейчас?

sudo firewall-cmd --list-all
sudo firewall-cmd --list-rich-rules
sudo nft list table inet firewalld | head -n 40

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

Ни один из этих методов не является доказательством работы. Проведите проверку с другого компьютера:

nc -zv 203.0.113.20 443

Не запускайте тест на самом сервере. firewalld разрешает любой трафик, поступающий через интерфейс loopback, поэтому curl http://localhost:8080 будет успешным независимо от ваших правил. Этот тест лишь подтверждает, что сервис запущен. Он ничего не говорит о состоянии межсетевого экрана.

Разрешение веб-порта

sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --reload
sudo firewall-cmd --list-services

После выполнения последней команды в списке должна появиться запись http https наряду с уже имевшимися там ранее. Если сайт по-прежнему не отвечает, проблема, вероятно, не в межсетевом экране. Правило лишь разрешает прохождение пакета. Процесс всё равно должен ожидать соединение на этом порту.

sudo ss -tlnp

Сокет, отображаемый как 0.0.0.0:443 или *:443, принимает соединения с любого адреса. Сокет, отображаемый как 127.0.0.1:443, отвечает только на loopback-интерфейсе, и никакое правило межсетевого экрана не сделает его доступным извне. В статье Порты и слушающие сокеты в Linux эта разница описана более подробно.

Как снова закрыть порт?

sudo firewall-cmd --permanent --remove-service=http
sudo firewall-cmd --reload

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

Удаление несуществующего правила выводит Warning: NOT_ENABLED: http и завершается с кодом 0. Повторное добавление одного и того же правила выводит Warning: ALREADY_ENABLED: http. Оба действия безопасны. Ошибка в названии — другое дело: Error: INVALID_SERVICE означает, что в firewalld нет определения с таким именем и изменения не были внесены.

Если в вашем --list-all отображается cockpit, а вы не используете веб-консоль Cockpit на порту 9090, удалите её. Каждый открытый порт — это сервис, который необходимо своевременно обновлять. Для тех сервисов, которые вы решили оставить, dnf-automatic может устанавливать обновления безопасности по расписанию, чтобы эта задача не зависела от вашей памяти. Однако установка патча не означает его применение, поэтому needs-restarting показывает, какие сервисы продолжают использовать старые библиотеки после установки обновлений.

Ограничение доступа к порту для одного IP-адреса

Rich rules — это расширенный синтаксис, который используется, когда стандартного имени сервиса недостаточно для описания задачи. Чтобы ограничить доступ к SSH для одного офисного адреса, требуется выполнить две команды, причем вторую часто забывают.

sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="203.0.113.10/32" service name="ssh" accept'
sudo firewall-cmd --permanent --remove-service=ssh
sudo firewall-cmd --reload

Зона — это набор разрешений, а не нумерованный список, работающий до первого совпадения. Rich rule добавляет правило accept для конкретного адреса, но оно никого не блокирует. Пока ssh остается в строке services:, весь интернет по-прежнему имеет доступ к порту 22, и rich rule не дает никакого измеримого эффекта. Удалите общее правило, иначе частное будет лишь формальностью.

Если у порта нет имени сервиса, укажите номер порта напрямую.

sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="203.0.113.10/32" port port="5432" protocol="tcp" accept'

Чтобы отбросить нежелательный трафик и сохранить запись об этом, поместите элемент log перед действием — именно такой порядок ожидает синтаксис rich rules.

sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="198.51.100.0/24" log prefix="fw-drop " level="info" limit value="3/m" drop'
sudo firewall-cmd --reload
sudo journalctl -k -g fw-drop

Значение limit предотвращает переполнение журнала потоком пакетов. Прежде чем ограничивать SSH одним адресом, убедитесь, что этот адрес стабилен. Домашнее подключение с динамическим IP-адресом заблокирует вам доступ в день смены адреса, поэтому сначала проверьте работоспособность консольного доступа, предоставляемого вашим провайдером.

Команды ufw и их аналоги в firewall-cmd

Те же задачи, другой инструмент. Каждая строка --permanent требует после себя sudo firewall-cmd --reload, чего список такого типа показать не может.

  • sudo ufw enable превращается в sudo systemctl enable --now firewalld
  • sudo ufw disable превращается в sudo systemctl disable --now firewalld
  • sudo ufw status verbose превращается в sudo firewall-cmd --list-all
  • sudo ufw allow OpenSSH превращается в sudo firewall-cmd --permanent --add-service=ssh
  • sudo ufw allow 443/tcp превращается в sudo firewall-cmd --permanent --add-port=443/tcp
  • sudo ufw delete allow 443/tcp превращается в sudo firewall-cmd --permanent --remove-port=443/tcp
  • sudo ufw allow from 203.0.113.10 to any port 22 превращается в расширенное правило (rich rule), показанное выше
  • sudo ufw reload превращается в sudo firewall-cmd --reload
  • sudo ufw default deny incoming — это стандартное поведение зоны public, а --set-target=DROP — её «тихая» версия
  • sudo ufw logging on превращается в sudo firewall-cmd --set-log-denied=all

Стоит чётко обозначить одно различие. ufw поддерживает нумерованный список, и вы можете вставить правило на позицию 1. В firewalld нет номеров правил, поэтому команда «поставить это правило первым» здесь не имеет смысла. Если две записи в firewalld противоречат друг другу, побеждает общее разрешение (accept), так как в наборе правил нет запретов. Широкое правило необходимо удалять вручную.

Почему мой Docker-контейнер доступен, хотя файрвол выглядит закрытым?

Потому что опубликованный порт контейнера никогда не доходит до той части файрвола, которую контролирует ваша зона. docker run -d -p 8080:80 nginx заставляет Docker записывать собственные правила NAT (трансляции сетевых адресов) и пересылки. Пакет, приходящий на 8080, перезаписывается и перенаправляется в контейнер, поэтому он пересылается (forwarded), а не доставляется на хост. Строки services: и ports: в вашей зоне управляют пакетами, доставляемыми на хост. Правила Docker управляют путем пересылки, и они разрешают трафик.

В результате получается сервер, где sudo firewall-cmd --list-all не показывает порт 8080, а nc -zv 203.0.113.20 8080 с другой машины всё равно подключается. Посмотрите, что установил Docker:

sudo iptables -t nat -L DOCKER -n

Решение заключается в использовании флага публикации. Привяжите порт к loopback-интерфейсу и поставьте перед ним reverse proxy.

docker run -d -p 127.0.0.1:8080:80 nginx

Теперь контейнер отвечает на curl http://127.0.0.1:8080 только на самом сервере, и никто снаружи не может к нему обратиться. Пользователи Ubuntu сталкиваются с той же проблемой, описанной в почему Docker-контейнеры публикуют порты в обход ufw. Rootful Podman, который Rocky и AlmaLinux предоставляют в базовых репозиториях, публикует порты с использованием того же подхода NAT, поэтому проводите тестирование с другой машины, а не доверяйте списку зон. Это наложение также является причиной, по которой установка Docker Engine в этих дистрибутивах требует нескольких шагов, о которых никогда не упоминается в руководствах для Ubuntu, начиная с того, что Podman уже занимает команду docker.

Обеспечение автозапуска после перезагрузки и типичные ошибки

sudo systemctl is-enabled firewalld
sudo systemctl status firewalld

enabled и active (running) — это именно то, что вам нужно. Запущенный, но не добавленный в автозагрузку межсетевой экран защищает сервер только до первой перезагрузки. Эту проверку следует включить в список действий в первые десять минут работы с новым VPS, рядом с настройкой SSH-ключей и обновлением системы.

Прямые команды nftables и firewalld несовместимы. firewalld владеет таблицей с именем inet firewalld. Команда sudo nft flush ruleset удаляет её, после чего сервер становится полностью открытым, а firewall-cmd --list-all продолжает выводить вашу конфигурацию, так как firewalld сообщает о том, что он считает правильным, а не о том, что реально загружено в ядро. Команда sudo firewall-cmd --reload восстанавливает правила. Записывайте правила с помощью firewall-cmd, чтобы они применялись автоматически после перезагрузки.

Два менеджера межсетевого экрана на одном сервере. Установка ufw или iptables-services параллельно с firewalld приводит к тому, что две программы создают правила, не зная о существовании друг друга, и результат зависит от того, какой сервис запустился последним. Выберите что-то одно. В Rocky и AlmaLinux именно firewalld имеет официальную поддержку дистрибутива.

Сетевой экран провайдера перед сервером. Многие панели управления VPS имеют отдельный сетевой экран. Если --list-all показывает, что порт открыт, а соединение снаружи всё равно не устанавливается, проверьте панель управления, прежде чем менять что-либо на сервере. Это работает и в обратную сторону: правило, разрешающее трафик в панели, не поможет, если firewalld отбрасывает пакеты.

Запуск firewall-cmd без sudo. Каждое изменение требует прав root. Без них запрос отклоняется проверкой авторизации, и ничего не меняется, что на первый взгляд выглядит так, будто команда была проигнорирована.

Шесть команд покрывают большинство повседневных задач: --list-all для чтения состояния, --permanent --add-service или --add-port для открытия порта, --permanent --remove-service для его закрытия, --reload для применения сохранённого файла и --runtime-to-permanent после серии экспериментов. Зона по умолчанию — public, флаг — --permanent, а единственная достоверная проверка выполняется с другого компьютера.

FAQ

Почему мое правило firewalld исчезло после перезагрузки?

Правило было добавлено только в текущую конфигурацию (runtime). sudo firewall-cmd --add-service=http применяется немедленно, но отбрасывается при следующей перезагрузке или перезапуске службы, так как сохраненная конфигурация в /etc/firewalld/zones/public.xml не была изменена. Добавьте --permanent, а затем выполните sudo firewall-cmd --reload. Чтобы сохранить правила, которые вы уже добавили вручную, выполните sudo firewall-cmd --runtime-to-permanent: эта команда копирует текущий набор правил в файл постоянной конфигурации.

Почему ничего не меняется после добавления правила с флагом --permanent?

Потому что --permanent записывает файл, не затрагивая работающий межсетевой экран. Порт остается закрытым до тех пор, пока sudo firewall-cmd --reload не загрузит сохраненную конфигурацию в ядро. Сравните вывод sudo firewall-cmd --list-services и sudo firewall-cmd --permanent --list-services: если в сохраненном списке есть запись, которой нет в текущем, значит, вы пропустили перезагрузку конфигурации.

Что лучше использовать: --add-service или --add-port?

Используйте --add-service, если для вашего приложения существует предопределенное имя. Это отражает суть настройки, а sudo firewall-cmd --info-service=https позволяет увидеть, какие именно порты открывает данное имя. Используйте --add-port, если для вашего сервиса нет определения или он слушает нестандартный порт. Сервис ssh означает только 22/tcp, поэтому для SSH, перенесенного на порт 2222, потребуется --add-port=2222/tcp и установка метки SELinux для этого порта.

Почему мой Docker-контейнер доступен, хотя firewall-cmd показывает, что порт закрыт?

Опубликованный порт перенаправляется собственными правилами NAT в Docker напрямую в контейнер. Пакет не доставляется хосту, а списки сервисов и портов в зоне относятся только к пакетам, адресованным самому хосту. Контейнер отвечает на запросы из интернета, в то время как --list-all ничего не показывает. Опубликуйте порт на интерфейсе обратной петли (loopback) с помощью docker run -d -p 127.0.0.1:8080:80 nginx и установите перед ним обратный прокси-сервер.

Можно ли установить ufw на Rocky Linux вместо firewalld?

Два менеджера межсетевого экрана на одном сервере создают правила, не учитывая действия друг друга. Итоговый набор правил будет зависеть от того, какая служба запустилась последней. firewalld — это поддерживаемый инструмент в Rocky Linux и AlmaLinux; он уже установлен и управляет тем же бэкендом nftables, который использовал бы ufw. Изучите работу зон по умолчанию и флаг --permanent — этого достаточно для полноценного управления инструментом.