SSD Nodes Learn
Руководства Matt ConnorАвтор: Matt Connor · Обновлено 2026-07-24

UFW и IPv6: как закрыть уязвимость на VPS

Ваш firewall может защищать только IPv4. Узнайте, как обнаружить открытые порты в IPv6 на Ubuntu 24.04 и правильно настроить правила для полной безопасности.

Ловушка IPv6 в межсетевом экране в одном предложении

Ваш межсетевой экран защищает IPv4. Ваш VPS почти наверняка также имеет публичный IPv6 адрес, и многие службы по умолчанию прослушивают его. Если ваш межсетевой экран защищает только IPv4 или если вы используете облачный межсетевой экран с фильтрацией только IPv4, то все эти службы будут доступны из всего интернета через IPv6, в то время как сторона IPv4 будет выглядеть защищенной. Вы проверяете порт с помощью curl, видите отклоненное соединение и считаете, что вы в безопасности. Злоумышленник подключается к тому же порту через IPv6 и получает доступ.

В этом руководстве показано, как возникает этот пробел в обычной VPS на Ubuntu 24.04, как точно определить открытые порты и как закрыть этот доступ. UFW здесь не является причиной проблемы. В современной установке Ubuntu UFW уже обрабатывает IPv6. Уязвимость возникает из-за внешних уровней защиты и служб, о прослушивании которых вы не знали.

Почему ваш VPS изначально использует IPv6

Почти каждый современный VPS поставляется с публичным IPv6-адресом, часто с целой /64, наряду с IPv4-адресом. Проверьте свой:

ip -6 addr show scope global
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> ...
    inet6 2001:db8:2a::1/64 scope global

Этот 2001:db8:2a::1 доступен из любой точки интернета, точно так же, как и ваш IPv4-адрес. Теперь посмотрите, какие процессы прослушивают порты:

sudo ss -tlnp
State   Recv-Q  Local Address:Port   Process
LISTEN  0       0.0.0.0:22           sshd
LISTEN  0       [::]:22              sshd
LISTEN  0       127.0.0.1:5432       postgres
LISTEN  0       [::]:8080            docker-proxy

Внимательно изучите столбец Local Address. 0.0.0.0:22 означает «прослушивание на всех IPv4-адресах». [::]:22 означает «прослушивание на всех IPv6-адресах». 127.0.0.1:5432 привязан к loopback и не является публичным, поэтому строка Postgres безопасна. Две строки [::] отвечают на запросы из всего интернета по IPv6, а строка docker-proxy — это тип, о запуске которого вы можете забыть.

Большинство демонов по умолчанию привязываются к ::, потому что в Linux сокет :: обычно принимает также и IPv4. Таким образом, стандартное состояние нового сервера — «отвечать на обоих стеках, везде». Единственное, что ограничивает это — ваш firewall. Именно поэтому firewall, который видит только один стек, является серьезной проблемой.

Причины возникновения пробелов в защите IPv6

Существует четыре основные причины. На конкретном сервере может быть одна из них или несколько одновременно.

1. Облачный firewall, который фильтрует только IPv4. Многие межсетевые экраны провайдеров и продукты для управления security-groups создавались под IPv4. Они либо игнорируют IPv6, либо требуют ручного добавления отдельных правил для IPv6. Если ваш единственный firewall находится в панели управления провайдера и он не поддерживает IPv6, ваши [::] сервисы будут открыты, независимо от настроек порта 22 для IPv4. Изучите документацию провайдера и ищите упоминание IPv6.

2. Настроенный вручную iptables без использования ip6tables. Команда iptables изменяет только таблицы IPv4. Для IPv6 существует отдельная команда, ip6tables, со своими собственными правилами. Если вы написали скрипт firewall, состоящий из строк iptables -A INPUT ..., но не создали соответствующие правила ip6tables, ваш firewall для IPv6 будет пустым. Пустая цепочка INPUT с политикой по умолчанию ACCEPT разрешает любой трафик:

sudo ip6tables -L INPUT -n
Chain INPUT (policy ACCEPT)
target     prot opt source               destination

Этот вывод на одном экране описывает всю проблему. IPv4 фильтруется, а IPv6 принимает всё.

3. Docker, публикующий порты в обход вашего firewall. При запуске docker run -p 8080:80 Docker вставляет свои собственные правила перед правилами UFW. В результате опубликованный порт остается доступным, даже если ufw status запрещает этот порт. В современных версиях Docker это же правило применимо и к IPv6. В статье Почему Docker обходит UFW и как правильно фильтровать порты контейнеров описан этот механизм и способы решения. См. основы Docker Compose на VPS, чтобы узнать, как объявляются эти опубликованные порты.

4. UFW с отключенным IPv6. UFW поддерживает IPv6, но только если это включено в настройках. Проверьте параметр:

grep IPV6 /etc/default/ufw

В современных версиях Ubuntu установлен IPV6=yes, поэтому UFW применяет каждое правило к обоим стекам. Если вы видите IPV6=no (в старых образах или руководствах), то все ваши правила UFW работают только для IPv4, а IPv6 остается без управления.

Проверьте объем открытых портов

Не используйте предположения. Проведите измерения из внешней сети. Сначала выведите список слушателей и найдите все процессы, привязанные к :::

sudo ss -tlnp | grep '::'

Затем с другого устройства подключитесь к публичному IPv6-адресу сервера и попробуйте порт, который вы считаете закрытым:

curl -6 -v http://[2001:db8:2a::1]:8080/

Если вы получили страницу или баннер, значит, порт открыт в IPv6. При закрытом порте вы получите Connection refused или тайм-аут. Для получения полной картины выполните сканирование IPv6-адреса с помощью nmap с удаленной машины:

nmap -6 2001:db8:2a::1

Любой порт, который nmap пометит как открытый в IPv6, доступен из всего интернета, независимо от результатов сканирования IPv4. Сравнение результатов сканирования IPv4 и IPv6 — самый быстрый способ обнаружить уязвимость: если порт открыт на -6, но закрыт в IPv4, значит, ваш firewall не блокирует этот сервис.

Устраните уязвимости

Настройте UFW для работы с обоими стеками и установите запрет по умолчанию. Проверьте изменения, затем установите политику запрета входящих соединений (default-deny) и разрешите только необходимые службы:

sudo sed -i 's/^IPV6=no/IPV6=yes/' /etc/default/ufw
sudo ufw default deny incoming
sudo ufw allow 22/tcp
sudo ufw enable
sudo ufw status verbose

Если UFW уже был активен при изменении IPV6=yes, изменения вступят в силу только после выполнения команды sudo ufw reload.

ufw status содержит каждое правило дважды: в обычном виде и с суффиксом (v6). Если вы видите строки с (v6), значит UFW фильтрует IPv6:

22/tcp                     ALLOW IN    Anywhere
22/tcp (v6)                ALLOW IN    Anywhere (v6)

Если вы настраиваете iptables вручную, продублируйте каждое правило в ip6tables, либо перейдите на nftables. Таблицы inet в nftables объединяют IPv4 и IPv6, что исключает подобные ошибки. Использование одной таблицы фильтрации nftables inet является наиболее эффективным решением при ручной настройке правил.

Привязывайте непубличные службы к loopback. Базам данных, панелям администратора или эндпоинтам метрик редко требуется публичный адрес. Привязывайте их к 127.0.0.1 и ::1, чтобы они не прослушивали маршрутизируемые адреса. Для Postgres установите listen_addresses = 'localhost'. Для сервера приложений используйте привязку к 127.0.0.1 и установите перед ним реверс-прокси. Закрытие порта на уровне прослушивания надежнее, чем фильтрация через firewall, так как к порту невозможно будет обратиться.

Не полагайтесь на UFW для защиты опубликованных портов Docker. Публикуйте порты контейнеров на конкретный адрес, а не на все интерфейсы (например, -p 127.0.0.1:8080:80). В этом случае порт будет доступен только с хоста и через намеренно настроенный прокси. Если контейнеру действительно необходим публичный доступ, поместите его за реверс-прокси Traefik и публикуйте только прокси, а не каждое приложение.

Добавьте правила IPv6 в firewall вашего провайдера, либо примите тот факт, что он не защищает ваш IPv6, и используйте UFW или nftables на хосте.

Убедитесь, что соединение действительно закрыто

После внесения изменений выполните тот же внешний тест:

curl -6 -v http://[2001:db8:2a::1]:8080/
nmap -6 2001:db8:2a::1

Порт, который отвечал ранее, теперь должен сбрасывать соединение или выдавать тайм-аут. nmap должен сообщить, что порт является filtered или closed. Если порт все еще открыт, проверьте четыре вышеуказанных источника: сервис все еще привязан к :: без соответствующего правила, правило Docker стоит перед UFW или брандмауэр провайдера не поддерживает IPv6.

Полное отключение критически важных сервисов от публичного интернета обеспечивает более высокий уровень безопасности. Настройте SSH и панели администратора через WireGuard VPN и закройте их порты в firewall так, чтобы они отвечали только внутри туннеля; в этом случае вопрос утечки через IPv6 для них не актуален. Чтобы замедлить brute-force сканирования публичных сервисов, используйте Fail2ban перед SSH в дополнение к firewall с политикой default-deny.

Если вы только начинаете работать с портами, сначала прочитайте статью что такое порты и как работают сервисы.

FAQ

Блокирует ли UFW IPv6 по умолчанию?

В современной установке Ubuntu 24.04 — да. UFW считывает IPV6=yes из /etc/default/ufw и применяет каждое правило как для IPv4, так и для IPv6. При этом ufw status отображает правила IPv6 с суффиксом (v6). Проблемы возникают при использовании IPV6=no (из старых образов или руководств), при использовании межсетевого экрана провайдера, который фильтрует только IPv4, или когда Docker публикует порт в обход UFW. Проверьте настройки с помощью grep IPV6 /etc/default/ufw.

Как проверить, какие порты мой VPS открывает в IPv6?

Выполните sudo ss -tlnp и обратите внимание на все слушатели, чей локальный адрес начинается с [::]. Это означает, что сервис отвечает на всех интерфейсах IPv6. Затем с другого устройства протестируйте публичный IPv6-адрес сервера напрямую с помощью curl -6 -v http://[YOUR:IPV6::ADDR]:PORT/ или просканируйте его с помощью nmap -6 YOUR:IPV6::ADDR. Любой порт, открытый при сканировании IPv6, но закрытый в IPv4, является уязвимостью.

Почему я могу получить доступ к порту Docker-контейнера, если UFW сообщает о блокировке?

Docker вставляет собственные правила межсетевого экрана перед правилами UFW, когда вы публикуете порт с помощью -p. В результате опубликованный порт остается доступным, даже если ufw status помечает его как запрещенный. Это происходит в IPv4, а также в IPv6, если включена поддержка IPv6 в Docker. Публикуйте порты на конкретный адрес, например -p 127.0.0.1:8080:80, или используйте обратный прокси-сервер, публикуя только его.

Нужен ли мне IPv6 firewall, если мой IPv4 firewall настроен правильно?

Да. IPv4 и IPv6 — это разные сетевые стеки с отдельными правилами межсетевого экрана. Набор правил IPv4 никак не защищает трафик IPv6. Если у вашего VPS есть публичный IPv6-адрес (а они есть почти у всех), то любой сервис, слушающий на ::, остается доступным через IPv6, пока его не остановит правило IPv6 firewall или привязка к loopback-интерфейсу.

Как заставить сервис слушать только IPv4 или только localhost?

Укажите адрес привязки (bind address) в конфигурационном файле сервиса. Используйте 127.0.0.1 для привязки только к IPv4 loopback или 0.0.0.0 для всех IPv4-адресов без создания слушателя в IPv6. Postgres использует listen_addresses, SSH использует ListenAddress, а большинство серверных приложений предоставляют флаги host или bind. Проверьте результат с помощью sudo ss -tlnp и убедитесь, что Local Address больше не показывает [::].