Файрвол на одном сервере - это одно решение, повторённое много раз: ничто не проходит внутрь, пока вы сами этого не разрешили. UFW - Uncomplicated Firewall - это инструмент, который делает это решение дешёвым в записи на Debian и Ubuntu, и настраивается он пятью командами. Всё интересное начинается после этого: правило для пары UDP-портов игрового сервера, что на самом деле делает limit с SSH, почему добавленное вами правило игнорируется и почему публикация порта в Docker проходит мимо всех правил из этой статьи. Именно последнее ловит тех, кто сделал всё остальное правильно.
Если нужна короткая версия и вы прямо сейчас сидите на новой машине: sudo ufw default deny incoming, sudo ufw default allow outgoing, sudo ufw allow OpenSSH, sudo ufw enable. Потом прочитайте остальное, прежде чем добавлять что-либо ещё, потому что порядок этих четырёх действий - это разница между надёжно закрытым сервером и сервером, на который вы не можете войти.
Что такое UFW и чем он не является#
UFW - не файрвол. Файрвол - это netfilter внутри ядра Linux, и он всё это время был на месте с пустой политикой. UFW - это интерфейс командной строки, который записывает в него правила: в виде правил iptables, которые в современных Debian и Ubuntu транслируются в nftables бэкендом iptables-nft. Когда вы запускаете ufw allow 443/tcp, вы ничего не изобретаете: вы добавляете одну строку в цепочку ufw-user-input, а всё, что вокруг неё, UFW берёт на себя.
Эта окружающая структура и есть настоящий продукт. В руками написанном наборе правил iptables нужно не забыть разрешить loopback-трафик, принимать пакеты соединений, которые вы открыли сами, разумно обойтись с ICMP и всё это повторить для IPv6. UFW поставляет это в виде before.rules и after.rules, помещает ваши правила посередине и держит оба семейства адресов в согласованном состоянии. Файлы лежат в /etc/ufw/, и в итоге вам понадобятся /etc/ufw/before.rules, /etc/ufw/after.rules и /etc/default/ufw.
Чем UFW не является:
- Он не защищает от объёмных атак. Поток, способный забить канал, уже стоил вам полосы к тому моменту, как ядро начнёт отбрасывать пакеты. Такая фильтрация должна происходить выше по маршруту, до машины. Статья DDoS-атаки на игровые серверы простыми словами прямо говорит, что можно и чего нельзя сделать на каждом уровне.
- Он не файрвол уровня приложения. Он сопоставляет адреса, порты и протоколы. Ему неизвестно, чем запрос на порт 443 является - попыткой входа или той же попыткой входа, повторённой девять тысяч раз. Это задача fail2ban, который читает ваши логи и работает вместе с UFW, а не вместо него.
- Он не повод запускать что-то небезопасное. Закрытый порт перед непропатченной службой выигрывает время, а не безопасность.
- Он не фильтрует трафик между контейнерами, и не обязательно фильтрует трафик внутрь них. См. раздел про Docker - он здесь самый длинный не случайно.
Первые пять минут на новой машине#
Ubuntu поставляется с установленным, но выключенным UFW. В Debian нужен apt install ufw. Порядок ниже важен: сначала политики по умолчанию, затем разрешение на обратный путь и только потом включение.
$ sudo apt update && sudo apt install -y ufw$ sudo ufw default deny incoming$ sudo ufw default allow outgoing$ sudo ufw allow OpenSSH$ sudo ufw enableUFW спрашивает подтверждение, прежде чем вас отрезать - Command may disrupt existing ssh connections. Proceed with operation (y|n)? - и это последнее предупреждение. Настройка переживает перезагрузку: ufw enable записывает ENABLED=yes в /etc/ufw/ufw.conf и включает systemd-юнит ufw, так что правила поднимаются раньше сети.
Проверьте, что получилось:
$ sudo ufw status verboseStatus: activeLogging: on (low)Default: deny (incoming), allow (outgoing), disabled (routed)New profiles: skipTo Action From-- -- ----22/tcp (OpenSSH) ALLOW IN Anywhere22/tcp (OpenSSH (v6)) ALLOW IN Anywhere (v6)Читать нужно строку Default:. deny (incoming) - в этом весь смысл; allow (outgoing) - разумное значение по умолчанию для сервера, который вы контролируете, потому что закрывать исходящий трафик на машине, которой нужно скачивать пакеты, забирать образы и общаться с API, - это отдельный проект, а не команда. disabled (routed) означает, что UFW не фильтрует пересылаемый трафик, и именно в эту брешь позже проезжает Docker.
Как писать правила: два синтаксиса#
У UFW есть короткая форма для обычного случая и длинная для всего остального. Правила они создают одного и того же рода.
# Short form: port, optional protocol, optional application profile$ sudo ufw allow 443/tcp$ sudo ufw allow 25565$ sudo ufw allow 'Nginx Full'# Long form: source, destination, port, protocol$ sudo ufw allow from 203.0.113.10 to any port 22 proto tcp$ sudo ufw allow from 10.0.0.0/8 to any port 5432 proto tcp$ sudo ufw deny from 198.51.100.0/24Несколько моментов, которые не очевидны из справки:
- Если не указать протокол, открываются оба.
ufw allow 25565создаёт правило и для TCP, и для UDP. Будьте конкретны, если только не нужны оба; лишние открытые UDP-порты - это то, из-за чего сервер начинает отражать чужой атакующий трафик. - Каждое правило создаётся и для IPv4, и для IPv6, если вы не указали семейство адресов, задав адрес. Если у машины есть публичный IPv6-адрес (проверьте через
ip -6 addr), то правило только для v4 оставляет дыру только для v4 в остальном открытой двери. - `deny` молча отбрасывает, `reject` отвечает. Отброшенный пакет делает порт похожим на выключенную машину; отвергнутый получает ICMP port-unreachable или TCP reset, что быстрее и честнее для внутренних служб. Используйте
denyв сторону интернета иrejectтам, где быстрый отказ полезнее скрытности. - Комментарии поддерживаются, и набрать их стоит.
sudo ufw allow 8443/tcp comment 'matrix bridge'появится вufw statusи избавит вас от полугодового правила, которое никто не может объяснить.
Профили приложений берутся из /etc/ufw/applications.d, и пакеты кладут их туда при установке. ufw app list показывает, что доступно; ufw app info 'Nginx Full' печатает порты за именем. Это удобство, а не возможность: Nginx Full - это 80,443/tcp и ничего больше.
Правила для того, что вы реально запускаете#
| Что вы запускаете | Правило | Примечание |
|---|---|---|
| SSH | ufw limit 22/tcp | С ограничением частоты, см. ниже |
| Сайт через nginx | ufw allow 'Nginx Full' | Порт 80 должен оставаться открытым для ACME |
| Minecraft Java | ufw allow 25565/tcp | Только TCP, вопреки фольклору |
| Valheim | ufw allow 2456:2457/udp | Игровой порт и порт запросов, оба UDP |
| Игра на Source | ufw allow 27015/udp | 27015/tcp добавляйте, только если используете RCON |
| PostgreSQL | ufw allow from 203.0.113.10 to any port 5432 proto tcp | Никогда не allow 5432 в одиночку |
| Приложение Node за прокси | правила нет вовсе | Привяжите его к 127.0.0.1 |
Диапазоны портов записываются через двоеточие и обязаны указывать протокол: ufw allow 27015:27020/udp работает, а ufw allow 27015:27020 отвергается. Длинная форма записывает то же самое как ufw allow proto udp from any to any port 27015:27020.
Последние две строки стоит усвоить особенно. База данных или сервер приложений, которые общаются только с чем-то на той же машине, должны быть привязаны к loopback, и тогда файрвольное правило не нужно, потому что пакет не может прийти снаружи. Файрвольное правило, защищающее службу, которая вообще не должна была слушать публично, - это второй замок на двери, оставленной открытой; статья правила файрвола, которые важны приводит тот же довод с другой стороны, а порты игровых серверов простыми словами объясняет, почему играм так часто нужна пара портов, а не один.
Что касается вопроса, какой порт нужен какой игре, схема почти всегда одна: игровой порт плюс порт запросов на один-два номера выше, оба UDP. Открывайте пару. Правило только для TCP на UDP-игру порождает самую частую заявку в поддержку хостинга: сервер стартует нормально, лог выглядит здоровым, а подключиться или найти его в браузере серверов никто не может.
Порядок, удаление и правило limit#
UFW применяет правила по порядку и останавливается на первом совпадении. Отсюда растёт большинство историй «моё правило ничего не делает». Если вы добавили общее разрешение для порта 80, а затем запрет для одного адреса на порту 80, запрет стоит ниже разрешения и до него дело не доходит.
$ sudo ufw status numberedStatus: active To Action From -- -- ----[ 1] 22/tcp LIMIT IN Anywhere[ 2] 80,443/tcp (Nginx Full) ALLOW IN Anywhere[ 3] 25565/tcp ALLOW IN Anywhere$ sudo ufw insert 1 deny from 198.51.100.7$ sudo ufw delete 4$ sudo ufw delete allow 25565/tcpinsert N ставит правило на конкретную позицию - так вы помещаете запрет перед разрешением. delete N удаляет по номеру и перенумеровывает всё ниже, поэтому удаляйте снизу вверх или запускайте status numbered между удалениями. delete с исходным текстом правила тоже работает и надёжнее в скрипте.
ufw limit заслуживает отдельного абзаца: это самая полезная вещь в инструменте и наименее понятая. ufw limit 22/tcp разрешает порт, но блокирует адрес-источник, который за последние тридцать секунд открыл шесть или больше соединений. На SSH-порту, смотрящем в интернет, это убирает практически весь автоматический шум подбора паролей без какой-либо настройки. Две оговорки: он считает соединения, а не неудачные входы, поэтому законная пачка параллельных заданий scp может его сработать, а в старых версиях UFW он работает только для IPv4 - проверьте, есть ли в ufw status запись LIMIT IN на строке (v6), прежде чем считать, что покрыты оба семейства.
Для более избирательных задач оставьте UFW грубые решения, а поверх поставьте fail2ban для решений, основанных на чтении логов. А на сервере, где SSH-ключи и отключённая парольная аутентификация, попытки подбора становятся шумом, а не риском с самого начала.
Проблема Docker#
Эту часть стоит прочитать дважды. Docker обходит UFW. Если вы запускаете контейнер с -p 8080:80, порт 8080 доступен из интернета, хотя ufw status говорит deny и не показывает для него никакого правила.
Причина структурная, а не баг. Правила UFW живут в цепочке INPUT, которая обрабатывает пакеты, адресованные самому хосту. Опубликованный порт контейнера проходит destination NAT в PREROUTING, а затем идёт через цепочку FORWARD к пространству имён контейнера и INPUT не касается вовсе. Docker ставит в FORWARD собственные правила, разрешающие именно это, и правила Docker вычисляются раньше, чем UFW получает слово. Ваш default deny не ошибочен: его просто спрашивают о другом маршруте.
Четыре выхода, в порядке рассмотрения:
- Публикуйте на loopback.
-p 127.0.0.1:8080:80привязывает опубликованный порт к интерфейсу loopback, так что снаружи машины до него не добраться, а обратный прокси на хосте - может. Это верно на любой версии Docker, не требует дополнительных инструментов и является правильным вариантом по умолчанию для всего, что стоит за обратным прокси nginx. - Не публикуйте вообще. Контейнеры в одной пользовательской сети обращаются друг к другу по имени службы на порту контейнера. Опубликованный порт нужен только входной двери - вокруг этой схемы построено Docker Compose для небольших стеков.
- Используйте цепочку `DOCKER-USER`. Docker предоставляет её именно как точку подключения, и она вычисляется раньше собственных правил Docker, так что это поддерживаемое место для вашей фильтрации контейнерного трафика.
- Используйте `ufw-docker` - скрипт сообщества, который добавляет обработку цепочки forward, чтобы
ufw route allowвёл себя для контейнеров так, как вы ожидаете. Он работает, им широко пользуются, и это ещё одна движущаяся деталь, которую надо понять, прежде чем на неё полагаться.
Минимальное правило DOCKER-USER, ограничивающее опубликованные порты контейнеров одним адресом, выглядит так:
$ sudo iptables -I DOCKER-USER -i eth0 ! -s 203.0.113.10 -j DROP$ sudo iptables -L DOCKER-USER -n --line-numbersДве сноски. Правила, добавленные iptables вручную, не переживают перезагрузку, если их не сохранить: iptables-persistent в Debian и Ubuntu или небольшой systemd-юнит, как описано в systemd-службы для ваших приложений. И поведение Docker в этой области ужесточали в последних крупных релизах, в частности вокруг прямого доступа к адресам контейнеров, так что точные симптомы зависят от версии. Вместо того чтобы отслеживать, какой релиз что делает, усвойте привычку номер один: публикуйте на 127.0.0.1, если только порт действительно не должен быть публичным. Это правильно везде.
IPv6, пересылка и логирование#
В /etc/default/ufw три переключателя, и все три важны на настоящем сервере:
IPV6=yesDEFAULT_INPUT_POLICY="DROP"DEFAULT_OUTPUT_POLICY="ACCEPT"DEFAULT_FORWARD_POLICY="DROP"IPV6=yes - значение по умолчанию в современных релизах, и оно должно таким остаться. Если у вашего VDS есть маршрутизируемый IPv6-адрес, набор правил только для IPv4 защищает половину машины. Убедитесь через sudo ip6tables -L ufw6-user-input -n, что ваши правила есть в обоих семействах, или просто прочитайте строки (v6) в ufw status.
DEFAULT_FORWARD_POLICY управляет маршрутизируемым трафиком: контейнеры, VPN, всё, где машина - промежуточный узел, а не пункт назначения. ufw default deny routed задаёт её из командной строки, а ufw route allow proto tcp from any to 10.0.0.5 port 80 добавляет исключения. После ручной правки файла выполните sudo ufw reload.
Логирование выключено по умолчанию в том смысле, что уровень low тихий. sudo ufw logging medium записывает заблокированные пакеты и новые соединения в /var/log/ufw.log через syslog, чего достаточно, чтобы ответить на вопрос «это моё правило сбрасывает пакет». Этого также достаточно, чтобы забить небольшой диск на загруженном публичном адресе, так что увеличивайте на время отладки и возвращайте обратно - sudo ufw logging low. Общий вид этого компромисса - в статье логи, которые стоит хранить.
Как убедиться, что это работает#
Три проверки, изнутри наружу. Сначала - что на самом деле слушает:
$ sudo ss -lntupNetid State Local Address:Port Processtcp LISTEN 0.0.0.0:22 sshdtcp LISTEN 127.0.0.1:3000 nodeudp UNCONN 0.0.0.0:2456 valheim_server.x86Всё, что привязано к 0.0.0.0 или [::], открыто сети и полагается на файрвол. Всё, что на 127.0.0.1, недоступно снаружи, что бы ни говорил файрвол. Эта одна команда разрешает больше споров, чем любая другая в статье.
Второе - что думает файрвол: sudo ufw status verbose, читать сверху вниз, помня, что побеждает первое совпадение. sudo ufw --dry-run allow 8080/tcp печатает строки iptables, которые породило бы правило, не применяя их, что полезно, когда нужно увидеть, куда в цепочке оно попадёт.
Третье, и единственное, которое считается, - с другого места:
$ nmap -Pn -p 22,80,443,3000,8080 203.0.113.10$ nmap -Pn -sU -p 2456,2457 203.0.113.10$ nc -zv 203.0.113.10 25565Сканируйте с другой машины, а не с самого сервера: loopback игнорирует правила, которые вы пытаетесь проверить. Если порт, который вы считали закрытым, отвечает, идите обратно: опубликован ли он Docker, нет ли правила выше вашего deny, не открыт ли он только по IPv6. Делать это один раз после каждого изменения стека - пятисекундная привычка, ловящая базу данных, которую вы открыли при отладке и забыли.
Когда что-то идёт не так#
Я включил UFW и потерял SSH. Получите оболочку через консоль провайдера, затем sudo ufw allow 22/tcp (или ваш порт) и sudo ufw reload. Если консоли нет, остаётся переустановка. Именно этот сбой эта статья и призвана предотвратить.
Моё правило allow ничего не делает. Что-то выше сработало первым. ufw status numbered, затем ufw insert правила выше или удалите широкое правило, которое его перекрывает.
Порт открыт, но служба недоступна. Файрвол - не ваша проблема. Проверьте ss -lntup на наличие процесса, проверьте, что он привязан к 0.0.0.0, а не к 127.0.0.1, и посмотрите лог службы.
Порт закрыт, хотя я его разрешил. Проверьте протокол: UDP-игра с правилом TCP - классика. Проверьте IPv6. Проверьте, нет ли второго файрвола перед машиной на уровне провайдера.
Контейнер доступен, а я его не разрешал. Docker. См. выше.
Правила пропали после перезагрузки. sudo systemctl is-enabled ufw должен ответить enabled. Написанные вручную правила iptables вне UFW сами не сохраняются.
`ufw reset` всё стёр. Так он и работает: он отключает файрвол и переносит существующие файлы правил в резервные копии с временными метками в /etc/ufw/. Старые правила остаются на диске, если их нужно прочитать обратно.
На VDS от RE:NODE всё это ваше, потому что тариф - это машина с полным root-доступом, а не управляемый контейнер: файрвол, ядро и каждый слушающий порт под вашим контролем и ничьим больше. Такова цена: на управляемом игровом тарифе или тарифе для приложений настраивать файрвол не нужно вовсе, потому что к каждому тарифу прилагаются выделенные порты, и вы добавляете или убираете их на вкладке Network. На VDS вы получаете гибкость и ответственность вместе, и эта статья - часть ответственности. Остальное о первом дне - в статье первый час на новом VDS.
FAQ#
Стоит ли менять порт SSH, чтобы его спрятать?
Это убирает большую часть автоматического шума в логах и ничего не останавливает, если целятся именно в вас. Делайте, если хотите более тихие логи, но вместе с ключами, limit и fail2ban, а не вместо них. Не забудьте разрешить новый порт до перезапуска sshd, а не после.
Достаточно ли одного UFW?
Для одного сервера с горсткой служб политика default deny с коротким списком разрешений - большая часть той пользы, что доступна на этом уровне. Он не ставит патчи, не останавливает подбор учётных данных дальше ограничения частоты и не поможет против потока, забивающего канал. Считайте его одним из четырёх-пяти средств, а не самим средством.
Почему мой контейнер Docker доступен, когда UFW говорит deny?
Потому что опубликованные порты контейнеров пересылаются, а не доставляются хосту, и правила пересылки Docker вычисляются раньше правил UFW. Публикуйте на 127.0.0.1, где возможно, не выводите внутренние службы на опубликованные порты вовсе и используйте цепочку DOCKER-USER, когда публичный порт контейнера действительно нужно ограничить по адресу.
Нужны ли правила для исходящего трафика?
Редко, на одиночном сервере приложений. Фильтрация исходящего полезна на машине, которая никогда не должна инициировать соединения, и это настоящий проект на той, что скачивает пакеты и вызывает API. Если вы идёте этим путём, начните с логирования того, что уходит, прежде чем что-либо блокировать.
В чём разница между deny и reject?
deny отбрасывает пакет без ответа, так что отправитель ждёт таймаута. reject посылает ICMP unreachable или TCP reset, так что отправитель получает отказ сразу. Отбрасывайте в сторону интернета, отвергайте там, где быстрые и понятные отказы полезнее тишины.
Работает ли UFW с nftables?
Да. В современных Debian и Ubuntu команды iptables, которые выдаёт UFW, обрабатываются слоем трансляции iptables-nft и оказываются в ядре правилами nftables. Результат можно посмотреть через sudo nft list ruleset, хотя вывод заметно менее читаем, чем ufw status.




Комментарии
Полностью анонимно: без аккаунта, без почты, без cookie. Мы храним имя, которое вы ввели, текст и время - больше ничего. Количество ссылок ограничено, разметка не отображается.