firewalld на Rocky та AlmaLinux: базове налаштування
Як відкрити SSH і web-порт, закрити зайвий доступ та зберегти правила після перезавантаження. Пояснюємо зони й пастку прапорця --permanent.
Що таке firewalld і чому Rocky та AlmaLinux постачають його
firewalld — це менеджер брандмауера, встановлений за замовчуванням у Rocky Linux, AlmaLinux та інших збірках Red Hat Enterprise Linux (RHEL). Він не перевіряє пакети самостійно. Менеджер зберігає конфігурацію та перетворює її на правила nftables. Команда firewall-cmd змінює ці правила без зупинки сервера.
Якщо ви вже знаєте як працює ufw на Ubuntu VPS, то розумієте принцип. firewalld додає дві концепції, яких немає в ufw. Перша — зони: іменовані політики, до яких розподіляються пакети. Друга — розділення активних і збережених правил. Для цього використовується прапорець --permanent, який є головним джерелом непорозумінь під час роботи з цим інструментом.
Усі наведені нижче команди потрібно виконувати на власному сервері. Перевіряйте кожну зміну з другого комп’ютера, оскільки правило, яке виглядає правильним на сервері, може бути неправильним під час підключення з Інтернету.
Відкрийте SSH перед будь-якими іншими діями
У більшості інсталяцій Rocky та AlmaLinux firewalld уже встановлено й запущено, а конфігурація, що постачається разом із системою, дозволяє SSH. У деяких мінімальних хмарних образах його видалено. Перевірте це, а не робіть припущень.
sudo dnf install -y firewalld
sudo systemctl enable --now firewalld
sudo firewall-cmd --statefirewall-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: — причина, чому поточний сеанс досі працює. Якщо цього правила немає, додайте його перед будь-якими іншими діями, оскільки запуск firewall без правила для SSH завершить сеанс і не дасть підключитися знову.
sudo firewall-cmd --permanent --add-service=ssh
sudo firewall-cmd --reloadtarget: default означає, що пакет, який не відповідає жодному правилу, відхиляється з відповіддю ICMP (internet control message protocol) host-prohibited, тому клієнт, який звертається до закритого порту, одразу отримує No route to host. Якщо встановити target у DROP, сервер натомість не відповідатиме, а сканери чекатимуть на тайм-аут.
sudo firewall-cmd --permanent --zone=public --set-target=DROP
sudo firewall-cmd --reloadПеред виконанням цієї команди врахуйте наслідки: DROP також припиняє відповіді сервера на ping, тому ваш власний моніторинг теж замовкне.
Чому моє правило зникло? Прапорець --permanent
firewalld одночасно зберігає дві конфігурації. Конфігурація runtime — це правила, які ядро застосовує зараз. Постійна конфігурація зберігається в /etc/firewalld/zones/public.xml і відновлюється після перезавантаження конфігурації або сервера.
Команда без --permanent змінює лише runtime-конфігурацію. Вона починає діяти одразу, але зникає після наступного перезавантаження конфігурації або системи. Команда з --permanent записує зміни у файл, але не змінює активну конфігурацію, тому порт залишатиметься закритим, доки ви не виконаєте перезавантаження конфігурації. Жодна з цих особливостей не є помилкою. Обидві часто вводять в оману, оскільки команда в обох випадках виводить success.
Завжди записуйте обидві команди.
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --reloadМожна переглянути обидві конфігурації. Це найшвидший спосіб з’ясувати, яку саме з двох помилок ви зробили.
sudo firewall-cmd --list-services
sudo firewall-cmd --permanent --list-servicesПерша команда виводить активний набір правил. Друга виводить збережений набір. Якщо активний набір містить сервіс, якого немає у збереженому наборі, це правило зникне після наступного перезавантаження конфігурації. Якщо збережений набір містить правило, якого немає в активному наборі, ви забули виконати перезавантаження конфігурації. sudo firewall-cmd --runtime-to-permanent копіює всі активні правила у збережений файл. Це зручно після серії експериментів.
--reload зберігає стан відстеження з’єднань, тому ваш SSH-сеанс продовжує працювати. --complete-reload також перезавантажує модулі ядра й втрачає цей стан, що зазвичай завершує всі відкриті з’єднання, зокрема ваше. Використовуйте звичайне перезавантаження конфігурації.
Вбудовано механізм безпеки. Runtime-правило може автоматично завершити дію.
sudo firewall-cmd --add-service=http --timeout=5mЦе правило видаляється через п’ять хвилин. Його не можна поєднувати з --permanent, і саме в цьому полягає його призначення: воно дає змогу перевірити зміни, у яких ви не впевнені. Старий механізм безпеки надійніший. Під час редагування правил тримайте відкритим другий SSH-сеанс і не закривайте його, доки новий вхід не підтвердить, що нові правила працюють.
Зони та чому на VPS важлива лише default zone
Зона — це іменований набір дозволів із визначеним рівнем довіри. firewalld призначає кожен вхідний пакет рівно одній зоні. Спочатку він порівнює адресу джерела пакета зі списком sources: кожної зони. Якщо збігу немає, використовується зона, до якої прив’язано вхідний інтерфейс. Якщо інтерфейс не прив’язано до жодної зони, пакет потрапляє до default zone.
sudo firewall-cmd --get-default-zone
sudo firewall-cmd --get-active-zonesНа VPS з одним мережевим інтерфейсом першою відповіддю майже завжди буде public, і це єдина зона, яку ви використовуватимете. firewall-cmd без аргументу --zone= працює з default zone, тому кожна коротка команда в цьому посібнику працює без явного зазначення зони.
Ось помилка, через яку можна витратити цілий день. Якщо інтерфейс прив’язано до іншої зони, ваші правила потрапляють до public, тоді як трафік обробляється в іншій зоні. Тому додані правила не мають жодного ефекту, і жодного попередження не з’являється. --get-active-zones показує прив’язку:
public
interfaces: eth0Якщо інтерфейс відображається під іншою назвою зони, або додайте правила до цієї зони за допомогою --zone=, або перенесіть інтерфейс до потрібної зони.
sudo firewall-cmd --permanent --zone=public --change-interface=eth0
sudo firewall-cmd --reloadNetworkManager керує інтерфейсами в Rocky та AlmaLinux і повторно встановлює зону під час активації з’єднання. Налаштуйте її також у NetworkManager, щоб перезавантаження не скасувало зміни. Назву з’єднання візьміть із першої команди, оскільки вона рідко збігається з назвою пристрою.
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.
Що таке service у firewalld?
Service — це іменований набір портів, збережений у 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 встановлюють власні service-файли. Для всього, що не має визначення, використовуйте --add-port.
Зверніть увагу на важливе обмеження: service ssh означає лише 22/tcp. Якщо ви перенесли SSH на інший порт під час посилення захисту доступу SSH до сервера, --add-service=ssh не відкриє порт, який ви фактично використовуєте.
sudo firewall-cmd --permanent --add-port=2222/tcp
sudo firewall-cmd --reloadУ RHEL rebuild є додаткове обмеження. 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 буде успішною незалежно від правил. Ця перевірка показує, що сервіс працює. Вона нічого не говорить про стан firewall.
Дозвольте вебпорт
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 разом із тим, що було виведено раніше. Якщо сайт і далі не відповідає, проблема може бути не у firewall. Правило дозволяє пакет. Але процес усе одно має прослуховувати цей порт.
sudo ss -tlnpСокет зі станом 0.0.0.0:443 або *:443 приймає з’єднання з будь-якої адреси. Сокет зі станом 127.0.0.1:443 відповідає лише через loopback, і жодне правило firewall не зробить його доступним ззовні. Докладніше про цю різницю див. у матеріалі Порти та сокети, що прослуховуються, у 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, видаліть його. Кожен відкритий порт — це сервіс, який потрібно своєчасно оновлювати.
Обмеження порту однією адресою-джерелом
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 додає дозвіл для однієї адреси. Вона нічого не забороняє. Поки 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 firewalldsudo ufw disableперетворюється наsudo systemctl disable --now firewalldsudo ufw status verboseперетворюється наsudo firewall-cmd --list-allsudo ufw allow OpenSSHперетворюється наsudo firewall-cmd --permanent --add-service=sshsudo ufw allow 443/tcpперетворюється наsudo firewall-cmd --permanent --add-port=443/tcpsudo ufw delete allow 443/tcpперетворюється наsudo firewall-cmd --permanent --remove-port=443/tcpsudo ufw allow from 203.0.113.10 to any port 22перетворюється на правило rich rule, наведене вищеsudo ufw reloadперетворюється наsudo firewall-cmd --reloadsudo ufw default deny incoming— це вже поведінка зониpublic, а--set-target=DROP— її тихий варіантsudo ufw logging onперетворюється наsudo firewall-cmd --set-log-denied=all
Є одна відмінність, про яку варто сказати прямо. ufw зберігає нумерований список, і правило можна вставити на позицію 1. У firewalld немає номерів правил, тому поняття «поставити це правило першим» тут нічого не означає. Якщо два записи firewalld здаються суперечливими, перемагає широке правило allow, оскільки в наборі немає правила deny. Широкий запис потрібно видалити вручну.
Чому мій Docker-контейнер доступний, хоча брандмауер, здається, закритий?
Тому що опублікований порт контейнера не проходить через ту частину брандмауера, якою керує ваша зона. docker run -d -p 8080:80 nginx вказує Docker створити власні правила NAT (перетворення мережевих адрес) і пересилання. Пакет, що надходить на 8080, переписується та маршрутизується до контейнера, тому він пересилається, а не доставляється хосту. Рядки 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, тому перевіряйте доступ з іншого комп’ютера, а не покладайтеся на список зони.
Налаштування збереження після перезавантаження та типові помилки
sudo systemctl is-enabled firewalld
sudo systemctl status firewalldenabled і active (running) — це саме те, що потрібно. Запущений, але не ввімкнений firewall захищає сервер лише до першого перезавантаження. Цю перевірку слід виконати в межах перших десяти хвилин на новому VPS, разом із налаштуванням SSH-ключів і встановленням оновлень.
Сирі команди nftables і firewalld не можна використовувати разом. firewalld керує таблицею з назвою inet firewalld. Команда sudo nft flush ruleset видаляє її, після чого сервер стає відкритим для всього трафіку, але firewall-cmd --list-all і далі показує заплановану конфігурацію, оскільки firewalld повідомляє, що він вважає налаштованим, а не те, що фактично зберігається в ядрі. Команда sudo firewall-cmd --reload повторно встановлює правила. Створюйте правила за допомогою firewall-cmd, щоб вони відновлювалися після перезавантаження конфігурації.
Два менеджери firewall на одному сервері. Якщо встановити ufw або iptables-services разом із firewalld, два програми записуватимуть правила незалежно одна від одної, а результат залежатиме від того, який сервіс запустився останнім. Виберіть один менеджер. У Rocky і AlmaLinux саме firewalld має підтримку дистрибутива.
Firewall провайдера перед сервером. Багато панелей керування VPS мають окремий мережевий firewall. Якщо --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 записує файл і не змінює активний firewall. Порт залишається закритим, доки 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-контейнер доступний, хоча firewalld показує порт як закритий?
Опублікований порт перенаправляється власними правилами NAT Docker до контейнера, тому пакет не доставляється хосту. Списки сервісів і портів зони охоплюють лише пакети, доставлені хосту. Контейнер відповідає з інтернету, хоча --list-all нічого не показує. Публікуйте порт лише на loopback за допомогою docker run -d -p 127.0.0.1:8080:80 nginx і розмістіть перед контейнером reverse proxy.
Чи можна встановити ufw на Rocky Linux замість firewalld?
Два firewall-менеджери на одному сервері записують правила незалежно один від одного. Який набір правил залишиться активним, залежить від того, який сервіс запустився останнім. firewalld є підтримуваним інструментом у Rocky Linux і AlmaLinux, уже встановлений у системі та використовує той самий бекенд nftables, що й ufw. Достатньо один раз розібратися з default zone і прапором --permanent — і ви матимете всі необхідні інструменти.