Simple stateful firewall (Русский)

From ArchWiki
Состояние перевода: На этой странице представлен перевод статьи Simple stateful firewall. Дата последней синхронизации: 11 июля 2021. Вы можете помочь синхронизировать перевод, если в английской версии произошли изменения.

В статье рассмотрена настройка межсетевого экрана с контекстной фильтрацией (stateful firewall) посредством iptables, с описанием основных правил и их назначения. Для удобства статья разбита на две части. В первой объясняется настройка межсетевого экрана на одиночной машине, во второй — настройка NAT-шлюза в дополнение к файрволу.

Важно: Правила ниже приводятся в порядке выполнения; такая настройка возможна только при локальном входе в систему. Если вы работаете с удалённой системой, то в процессе добавления правил соединение с ней будет потеряно. Чтобы этого избежать, используйте готовый файл настроек.

Требования

Примечание: Ядро Linux на вашей машине должно быть скомпилировано с поддержкой iptables. В стандартных ядрах Arch Linux поддержка iptables включена.

Прежде всего установите пакет iptables с набором пользовательских утилит, если он ещё не установлен.

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

# iptables-save
# Generated by iptables-save v1.4.19.1 on Thu Aug  1 19:28:53 2013
*filter
:INPUT ACCEPT [50:3763]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [30:3472]
COMMIT
# Completed on Thu Aug  1 19:28:53 2013

или

# iptables -nvL --line-numbers
Chain INPUT (policy ACCEPT 156 packets, 12541 bytes)
num   pkts bytes target     prot opt in     out     source               destination

Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
num   pkts bytes target     prot opt in     out     source               destination

Chain OUTPUT (policy ACCEPT 82 packets, 8672 bytes)
num   pkts bytes target     prot opt in     out     source               destination

Если всё же какие-то правила существуют, можно сбросить их, загрузив базовый набор:

# iptables-restore < /etc/iptables/empty.rules

Другие способы сброса правил можно найти в статье iptables#Сброс правил.

Настройка межсетевого экрана

Примечание: Поскольку iptables проверяет правила в цепочке последовательно, сверху вниз, то имеет смысл переместить часто срабатывающие правила ближе к её началу. Разумеется, этот подход имеет свои ограничения, в зависимости от реализуемой логики. Кроме того, поскольку правила имеют определённую "стоимость" выполнения, не стоит изменять их порядок исключительно на основе эмпирических наблюдений за счётчиком байтов/пакетов.

Создание необходимых цепочек

Создадим две пользовательские цепочки, которые будут использоваться для открытия портов.

# iptables -N TCP
# iptables -N UDP

В дальнейшем при назначений правил для этих цепочек мы будем всякий раз указывать тип протокола (например, флагом -p tcp). Этим обусловлен выбор названий цепочек, но вообще говоря, названия могут быть любыми.

Цепочка FORWARD

Если вы хотите настроить свою систему в качестве NAT-шлюза, изучите раздел #Настройка NAT-шлюза. Для обычной системы можно просто задать политику DROP для цепочки FORWARD:

# iptables -P FORWARD DROP

Цепочка OUTPUT

Цепочка OUTPUT может быть крайне полезной в деле фильтрации исходящего трафика, особено для серверов и других устройств, не использующих веб-браузеры и peer-to-peer программы для соединения с произвольными узлами сети Интернет. Тем не менее, правильная настройка цепочки OUTPUT требует понимания назначения конкретной системы. Наборы правил безопасности для настольной системы, ноутбука, облачного или домашнего сервера будут сильно отличаться.

В этом примере весь исходящий трафик разрешён, поэтому для цепочки OUTPUT задаётся политика ACCEPT. Этого недостаточно для полной безопасности, но будет работать на большинстве систем.

# iptables -P OUTPUT ACCEPT

Цепочка INPUT

Назначаем политику DROP для цепочки INPUT на случай, если что-то каким-то образом проскочит мимо наших правил. Лучший способ создать надёжный файрвол — запретить весь трафик, отдельно указав то, что разрешено.

Важно: Если вы вошли в систему через SSH, следующий шаг немедленно прервёт SSH-сеанс. Чтобы этого избежать: (1) добавьте первое правило для цепочки INPUT, приведённое далее в этом же разделе; правило позволит сохранить существующее соединение, (2) добавьте обычное правило, разрешающее входящее SSH-подключение (чтобы иметь возможность восстановить соединение в случае потери связи) и только после этого (3) задайте политику.
# iptables -P INPUT DROP

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

Первое правило цепочки INPUT будет разрешать трафик установленных соединений и любой новый трафик, относящийся к ним, например, сообщения ICMP об ошибке или эхо-ответы (пакеты, которые хост возвращает, когда его пингуют). ICMP — протокол управляющих сообщений (Internet Control Message Protocol). Некоторые сообщения ICMP имеют важное значение для управления перегрузками и определения MTU, и мы разрешаем их этим правилом:

# iptables -A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT

Состояние соединения ESTABLISHED подразумевает одну из двух ситуаций: либо первичная (--ctstate NEW) попытка соединения была ранее одобрена другим правилом, либо соединение уже было активно (например, удалённое SSH-подключение) на момент задания правила.

Второе правило разрешит весь трафик от петлевого (loopback) интерфейса, который необходим многим приложениям и службам:

# iptables -A INPUT -i lo -j ACCEPT
Примечание: Аналогичным образом можно добавить и другие доверенные интерфейсы (например, "enp2s0"), если вы не хотите, чтобы проходящий через них трафик обрабатывался межсетевым экраном. Тем не менее, вы должны понимать, весь этот трафик, вне зависимости от его происхождения, пройдёт дальше, невзирая ни на какие иные настройки.

Третье правило будет отбрасывать все пакеты с состоянием INVALID. Существует четыре категории состояния (state): NEW, ESTABLISHED, RELATED и INVALID. Именно наличие категорий отличает межсетевой экран с контекстной фильтрацией от менее безопасного экрана без оной. Состояния отслеживаются модулями ядра nf_conntrack_*, которые загружаются автоматически после добавления правил.

Примечание:
  • Это правило будет отбрасывать все пакеты с неверными заголовками или контрольными суммами, неверными флагами TCP, неправильными ICMP-сообщениями (например, входящее сообщение "порт недостижим", если мы не посылали ничего другому хосту), а также пакеты с неправильным порядковым номером, что может быть признаком атаки. Политика DROP означает отбрасывание пакетов безо всякого ответа, в то время как REJECT отклоняет их вежливо, с уведомлением отправителя. Мы используем DROP, поскольку для INVALID-пакетов не существует подходящего REJECT-ответа и в целом подтверждать их получение нет никакой необходимости.
  • Пакеты ICMPv6 Neighbor Discovery остаются неотслеживаемыми и всегда классифицируются как INVALID, хотя они по всем параметрам являются годными. Если их необходимо разрешить, то выполните iptables -A INPUT -p 41 -j ACCEPT с правами root перед следующей командой.
# iptables -A INPUT -m conntrack --ctstate INVALID -j DROP

Следующее правило разрешает входящие ICMP эхо-запросы (ECHO_REQUEST), известные как пинги. Только первый пакет будет считаться относящимся к категории NEW, остальные будут обрабатываться правилом "RELATED, ESTABLISHED". Если компьютер не является маршрутизатором, нет необходимости разрешать какой-либо другой ICMP-трафик с состоянием NEW.

# iptables -A INPUT -p icmp --icmp-type 8 -m conntrack --ctstate NEW -j ACCEPT

Теперь мы прикрепим TCP- и UDP-цепочки к цепочке INPUT для обработки новых входящих соединений. Если соединение разрешено цепочкой TCP или UDP, оно обрабатывается правилом "RELATED, ESTABLISHED". TCP или UDP цепочки будут либо разрешать новые входящие соединения, либо вежливо отклонять их. Новые TCP соединения должны начинаться с SYN-сегмента.

Примечание: NEW-пакет без SYN-флага — единственный неверный TCP-флаг, который не подпадает под состояние INVALID. Это связано с тем, что данные пакеты редко бывают вредоносными и не должны молча отбрасываться (drop). Вместо этого они отклоняются (reject) с сообщением TCP RESET в соответствии со следующим правилом.
# iptables -A INPUT -p udp -m conntrack --ctstate NEW -j UDP
# iptables -A INPUT -p tcp --syn -m conntrack --ctstate NEW -j TCP

Мы отклоняем TCP-соединения пакетами TCP RESET, а UDP-потоки — сообщениями ICMP "port unreachable", если запрашиваемый порт закрыт. Это имитирует стандартное поведение Linux (в соответствии с RFC), и позволяет отправителю быстро закрыть соединение.

# iptables -A INPUT -p udp -j REJECT --reject-with icmp-port-unreachable
# iptables -A INPUT -p tcp -j REJECT --reject-with tcp-reset

Для прочих протоколов мы добавляем последнее правило в цепочку INPUT, чтобы отклонить остальной входящий трафик с ICMP-сообщением "protocol unreachable". Это также соответствует стандартному поведению Linux.

# iptables -A INPUT -j REJECT --reject-with icmp-proto-unreachable

Итоговый файл iptables.rules

Пример файла iptables.rules после выполнения всех команд выше:

/etc/iptables/iptables.rules
# Generated by iptables-save v1.4.18 on Sun Mar 17 14:21:12 2013
*filter
:INPUT DROP [0:0]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [0:0]
:TCP - [0:0]
:UDP - [0:0]
-A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A INPUT -i lo -j ACCEPT
-A INPUT -m conntrack --ctstate INVALID -j DROP
-A INPUT -p icmp -m icmp --icmp-type 8 -m conntrack --ctstate NEW -j ACCEPT
-A INPUT -p udp -m conntrack --ctstate NEW -j UDP
-A INPUT -p tcp --tcp-flags FIN,SYN,RST,ACK SYN -m conntrack --ctstate NEW -j TCP
-A INPUT -p udp -j REJECT --reject-with icmp-port-unreachable
-A INPUT -p tcp -j REJECT --reject-with tcp-reset
-A INPUT -j REJECT --reject-with icmp-proto-unreachable
COMMIT
# Completed on Sun Mar 17 14:21:12 2013

Файл генерируется и сохраняется командой

# iptables-save -f /etc/iptables/iptables.rules

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

# iptables -A TCP -p tcp --dport 22 -j ACCEPT

Цепочки TCP и UDP

Цепочки TCP и UDP содержат правила для разрешения новых TCP-соединений и UDP-потоков к определённым портам.

Примечание: Также в эти цепочки можно добавлять правила для разрешения удалённых соединений, таких как SSH, HTTP и других служб, к которым вы желаете иметь удалённый доступ.

Открытие портов для входящих соединений

Разрешить входящие TCP-соединения на порт 80 для веб-сервера (HTTP):

# iptables -A TCP -p tcp --dport 80 -j ACCEPT

Разрешить входящие TCP-соединения на порт 443 для веб-сервера (HTTPS):

# iptables -A TCP -p tcp --dport 443 -j ACCEPT

Разрешить удаленные SSH-соединения (на порт 22):

# iptables -A TCP -p tcp --dport 22 -j ACCEPT

Разрешить входящие TCP/UDP запросы для DNS-сервера (порт 53):

# iptables -A TCP -p tcp --dport 53 -j ACCEPT
# iptables -A UDP -p udp --dport 53 -j ACCEPT

Более сложные правила, вроде проверки по нескольким портам, можно найти в iptables(8).

Port knocking

Port knocking — способ открыть извне порты, которые файрвол по умолчанию держит закрытыми. Port knocking заключается в создании последовательности попыток соединений с заранее выбранными закрытыми портами. При получении корректной последовательности "простукиваний" межсетевой экран открывает определенный порт и разрешает соединение. Подробнее см. Port knocking.

Защита от спуфинга

Примечание: В файле /usr/lib/sysctl.d/50-default.conf параметр rp_filter установлен в значение 2, поэтому в описанных ниже действиях нет необходимости.

Если из внешней сети пришёл пакет с зарезервированным (т.е. локальным) адресом отправителя, то имеет место подмена адреса (address spoofing). Стандартный способ блокирования таких пакетов — установить с помощью sysctl параметр rp_filter (Reverse Path Filter) в значение 1, что включит встроенную в ядро Linux проверку адреса отправителя пакета. Встроенная проверка будет работать лучше, чем отдельные правила iptables на каждый случай. Добавьте в файл /etc/sysctl.d/90-firewall.conf (подробнее см. sysctl) следующую строку:

net.ipv4.conf.all.rp_filter=1

То же самое можно сделать посредством netfilter, если необходимо ведение статистики и лог-файлов:

# iptables -t raw -I PREROUTING -m rpfilter --invert -j DROP
Примечание: Включать эту функцию одновременно в двух местах не нужно. Реализованная в netfilter проверка вполне удовлетворительна и, кроме того, работает с адресами IPv6.

Для случая асинхронной маршрутизации используйте значение rp_filter=2. Флаг --loose в модуле rpfilter делает то же самое посредством netfilter.

Защита от обнаружения

Если вы хотите сделать вашу машину менее заметной в сети, хорошей идеей будет блокировать некоторые входящие запросы.

Блокирование ping-запросов

Запрос "ping" представляет собой ICMP-пакет, посланный с целью убедиться, что между двумя хостами есть связь. Если сеть в порядке, вы можете безопасно блокировать все ping-запросы. Нужно отметить, что это не сделает ваш компьютер необнаружимым — каждый входящий пакет будет отклоняться, поэтому вы всё ещё будете видны при простом "ping-сканировании" по диапазону IP-адресов посредством nmap. Кроме того, нужно иметь в виду, что эта элементарная "защита" усложнит вам жизнь случае возникновения необходимости отладки сети.

Чтобы заблокировать эхо-запросы (echo requests), добавьте следующую строку в файл /etc/sysctl.d/90-firewall.conf (подробную информацию можно найти в статье sysctl):

net.ipv4.icmp_echo_ignore_all = 1

Больше сведений об этой защите вы найдёте в руководстве iptables(8), а также в документации и примерах на странице http://www.snowman.net/projects/ipt_recent/

Обман сканеров портов

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

Состояние INVALID в правилах iptables "позаботится" обо всех типах сканирования, за исключением сканирований UDP, ACK и SYN (флаги nmap -sU, -sA и -sS соответственно).

ACK-сканирование не используется для определения открытых портов, но зато покажет порты, защищённые межсетевым экраном. Подобно SYN-пакету в TCP-соединениях с состоянием NEW, каждый пакет ACK-сканирования будет отклонен с отправкой ответа TCP RESET по обратному адресу. Некоторые межсетевые экраны вместо этого просто отбрасывают такие пакеты, что позволяет атакующему определить действующие правила.

Модуль recent поможет обмануть остальные типы сканирования портов. Он добавляет хосты к списку недавних соединений, который используется для обнаружения и блокирования попыток атак. Просмотреть списки недавних соединений можно в каталоге /proc/net/xt_recent/.

Примечание: Использование модуля resent для защиты от сканирований может привести к тому, что:
  • Система станет уязвимой к разновидности DoS-атаки. Атакующий посылает пакеты с подменёными IP-адресами, чтобы добиться их блокировки вашими службами.
  • Может оказаться заблокированным обычный IP-адрес, если несколько пакетов с этого адреса на порт получателя будут признаны INVALID модулем conntrack. Чтобы избежать занесения в чёрный список, следует разрешить все пакеты, поступающие на этот порт.
SYN-сканирование

При SYN-сканировании сканер портов посылает синхронизационные пакеты на каждый порт с целью создать TCP-соединение. Если порт закрыт, то возвращается ответ TCP RESET, межсетевой экран просто отбрасывает входящий пакет, а открытый порт возвращает ответ SYN ACK.

Модуль recent может использоваться для отслеживания хостов с отклонёнными попытками соединения и возвращения ответа TCP RESET для каждого SYN-пакета, поступившего на открытый порт, как если бы порт был закрыт. Если открытый порт оказался первым в порядке сканирования, то будет возвращён ответ SYN ACK, поэтому приложения вроде ssh следует размещать на нестандартных портах.

Сначала добавьте правило в начало цепочки TCP. Это правило будет отвечать пакетом TCP RESET любому хосту, входившему в список TCP-PORTSCAN в течение последних 60 секунд. Флаг --update управляет периодическим обновлением списка.

# iptables -I TCP -p tcp -m recent --update --rsource --seconds 60 --name TCP-PORTSCAN -j REJECT --reject-with tcp-reset

Затем необходимо модифицировать правило отклонения TCP-пакетов, чтобы добавлять все хосты с отклонёнными пакетами к списку TCP-PORTSCAN:

# iptables -D INPUT -p tcp -j REJECT --reject-with tcp-reset
# iptables -A INPUT -p tcp -m recent --set --rsource --name TCP-PORTSCAN -j REJECT --reject-with tcp-reset
UDP-сканирование

Сканирование UDP схоже со сканированием TCP SYN за исключением того факта, что UDP является протоколом без установления соединения. В нём нет "рукопожатий" и подтверждений. Вместо этого сканер посылает UDP-пакеты на каждый UDP-порт. Закрытые порты должны возвращать сообщение ICMP port unreachable, а открытые не возвращают ничего. Поскольку UDP — "ненадежный" протокол, у сканера нет возможности узнать о потере пакетов, поэтому он посылает серию запросов на каждый порт, с которого не вернулся ответ.

Ядро Linux посылает сообщения ICMP port unreachable довольно медленно, поэтому продолжительность полного UDP-сканирования может превысить 10 часов. Однако часто используемые порты проверяются гораздо быстрее, поэтому хорошей идеей будет применить контрмеры, аналогичные защите от SYN-сканирований.

Сначала добавляем правило отклонения пакетов от хостов из списка UDP-PORTSCAN в начало цепочки UDP:

# iptables -I UDP -p udp -m recent --update --rsource --seconds 60 --name UDP-PORTSCAN -j REJECT --reject-with icmp-port-unreachable

Затем модифицируем правило отклонения пакетов для UDP:

# iptables -D INPUT -p udp -j REJECT --reject-with icmp-port-unreachable
# iptables -A INPUT -p udp -m recent --set --rsource --name UDP-PORTSCAN -j REJECT --reject-with icmp-port-unreachable
Восстановление последнего правила

Если вы применили хотя бы один из способов защиты выше, бывшее последним правило цепочки INPUT более таковым не является. Теперь оно находится перед правилами защиты от сканирования и те по сути бесполезны. Просто удалите (-D) это правило, а затем добавьте его снова (-A), это переместит его в конец цепочки.

# iptables -D INPUT -j REJECT --reject-with icmp-proto-unreachable
# iptables -A INPUT -j REJECT --reject-with icmp-proto-unreachable

Защита от других типов атак

В статье sysctl#TCP/IP stack hardening можно найти описание важных с точки зрения безопасности параметров ядра.

Атака полным перебором

Доступные по внешнему IP-адресу сервисы подвергаются атакам полным перебором довольно часто. Реализовать атаку этого типа несложно, а инструментарий — обширен и доступен. К счастью, существует несколько способов защиты от атак полным перебором. Первый способ заключается в создании правил iptables, которые заносят IP-адрес в чёрный список после нескольких попыток установить соединение. При втором способе защиты запускается специализированный демон, который отслеживает лог-файл на предмет неудачных попыток соединения.

Важно: Защита посредством занесения адресов в чёрный список остановит простые атаки, но она полагается на дополнительный демон и успешное логирование (в случае мощной атаки может закончиться свободное место разделе, содержащем каталог /var с лог-файлами). Кроме того, узнав ваш IP-адрес, атакующий может посылать пакеты с подменённым адресом отправителя, чтобы добиться вашей блокировки. Элегантное решение этой проблемы заключается в использовании ключей SSH.

Приложения Fail2ban и (в случае sshd) Sshguard используются для блокировки IP-адресов при превышении допустимого количества попыток аутентификации. Суть их работы состоит в обновлении правил iptables с целью временно или навсегда воспрепятствовать будущим соединениям атакующих.

Ниже представлен пример правил iptables для предотвращения атак полным перебором на сервис SSH.

# iptables -N IN_SSH
# iptables -N LOG_AND_DROP
# iptables -A INPUT -p tcp --dport ssh -m conntrack --ctstate NEW -j IN_SSH
# iptables -A IN_SSH -m recent --name sshbf --rttl --rcheck --hitcount 3 --seconds 10 -j LOG_AND_DROP
# iptables -A IN_SSH -m recent --name sshbf --rttl --rcheck --hitcount 4 --seconds 1800 -j LOG_AND_DROP 
# iptables -A IN_SSH -m recent --name sshbf --set -j ACCEPT
# iptables -A LOG_AND_DROP -j LOG --log-prefix "iptables deny: " --log-level 7
# iptables -A LOG_AND_DROP -j DROP

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

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

Необходимо также убедиться, что правило -A INPUT -p tcp --dport ssh -m conntrack --ctstate NEW -j IN_SSH находится в верной позиции в последовательности iptables, перед точкой прикрепления цепочки TCP к цепочке INPUT. Это позволит успешно перехватывать новые попытки установления SSH-соединений. Если вы выполнили все предыдущие шаги в этой статье, порядок правил должен быть следующим:

...
-A INPUT -m conntrack --ctstate INVALID -j DROP
-A INPUT -p icmp -m icmp --icmp-type 8 -m conntrack --ctstate NEW -j ACCEPT
-A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW -j IN_SSH
-A INPUT -p udp -m conntrack --ctstate NEW -j UDP
-A INPUT -p tcp --tcp-flags FIN,SYN,RST,ACK SYN -m conntrack --ctstate NEW -j TCP
...
Совет: При проверке правил после настройки реальное занесение в чёрный список может замедлить тесты, что усложнит тонкую настройку. Входящие попытки соединений можно отслеживать посредством команды cat /proc/net/xt_recent/sshbf. Чтобы разблокировать собственный IP-адрес во время тестирования, вам понадобятся права root: echo / > /proc/net/xt_recent/sshbf.

IPv6

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

Скопируйте созданные ранее правила для протокола IPv4 и замените все IPv4-адреса на адреса формата IPv6:

# cp /etc/iptables/iptables.rules /etc/iptables/ip6tables.rules

Некоторые правила нужно адаптировать под IPv6. Так, для IPv6 используется обновлённая версия протокола ICMP, и коды ответов при отклонении соединений --reject-with icmp-port-unreachable и --reject-with icmp-proto-unreachable необходимо преобразовать в коды ICMPv6.

Коды ошибок ICMPv6 перечислены в RFC 4443, согласно которому при блокировке межсетевым экраном попыток установления соединения необходимо использовать код --reject-with icmp6-adm-prohibited. Это проинформирует удалённую систему о том, что соединение было отклонено брандмауэром, а не прослушивающей порт службой.

Если уведомлять удалённую систему о наличии файрвола нежелательно, то можно отклонить пакет без сообщения:

 -A INPUT -j REJECT

Отклонение пакетов по этому правилу будет производиться с сообщением об ошибке --reject-with icmp6-port-unreachable. Следует однако отметить, что одной из основных функций приложений-сканеров является как раз обнаружение межсетевых экранов и обмануть их этим правилом не получится.

Tango-view-fullscreen.pngThis article or section needs expansion.Tango-view-fullscreen.png

Reason: Какие особенности ICMPv6 нужно добавить, чтобы привести правила в полное соответствие с правилами IPv4? (Discuss in Talk:Simple_stateful_firewall#ICMP blocking)

Следующее правило для протокола IPv6 настроит поведение межсетевого экрана по отношению к новым входящим пингам (ICMP echo requests):

# ip6tables -A INPUT -p ipv6-icmp --icmpv6-type 128 -m conntrack --ctstate NEW -j ACCEPT

Модуль conntrack не отслеживает действия ICMPv6 Neighbor Discovery Protocol (аналог протокола ARP), поэтому необходимо разрешить трафик ICMPv6 вне зависимости от его состояния для всех прилежащих подсетей. Следующее правило нужно вставить после правила отбрасывания некорректных пакетов --ctstate INVALID, но перед любыми другими правилами DROP или REJECT. Создаётся по одному правилу на каждую подсеть:

# ip6tables -A INPUT -s fe80::/10 -p ipv6-icmp -j ACCEPT

Если вы желаете включить DHCPv6, разрешите входящие соединения на UDP-порт 546:

# ip6tables -A INPUT -p udp --sport 547 --dport 546 -j ACCEPT

Поскольку в ядре Linux нет встроенной фильтрации по обратному маршруту (reverse path filter) для протокола IPv6, то стоит включить её посредством ip6tables:

# ip6tables -t raw -A PREROUTING -m rpfilter -j ACCEPT
# ip6tables -t raw -A PREROUTING -j DROP

Сохранение правил

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

Сохраняем правила IPv4 и IPv6 командами:

# iptables-save -f /etc/iptables/iptables.rules
# ip6tables-save -f /etc/iptables/ip6tables.rules

Итоговый файл ip6tables.rules

Пример файла правил ip6tables.rules после выполнения представленных выше команд:

/etc/iptables/ip6tables.rules
# Generated by ip6tables-save v1.8.2 on Sat Apr 20 10:53:41 2019
*filter
:INPUT DROP [0:0]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [0:0]
:TCP - [0:0]
:UDP - [0:0]
-A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A INPUT -i lo -j ACCEPT
-A INPUT -m conntrack --ctstate INVALID -j DROP
-A INPUT -s fe80::/10 -p ipv6-icmp -j ACCEPT
-A INPUT -p udp --sport 547 --dport 546 -j ACCEPT
-A INPUT -p udp -m conntrack --ctstate NEW -j UDP
-A INPUT -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -m conntrack --ctstate NEW -j TCP
-A INPUT -p udp -j REJECT --reject-with icmp6-adm-prohibited
-A INPUT -p tcp -j REJECT --reject-with tcp-reset
-A INPUT -j REJECT --reject-with icmp6-adm-prohibited
-A INPUT -p ipv6-icmp -m icmp6 --icmpv6-type 128 -m conntrack --ctstate NEW -j ACCEPT
COMMIT
# Completed on Sat Apr 20 10:53:41 2019

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

Настройка NAT-шлюза

В этом разделе рассмотрена настройка межсетевого экрана для NAT-шлюза. Предполагается, что вы уже прочитали первую часть данного руководства и настроили цепочки INPUT, OUTPUT, TCP и UDP как было предложено. До этого момента созданные правила относились к таблице filter, при настройке NAT-шлюза нам также понадобится таблица nat.

Таблица filter

Создание необходимых цепочек

Создадим две новые цепочки — fw-interfaces и fw-open:

# iptables -N fw-interfaces
# iptables -N fw-open

Цепочка FORWARD

Настройка цепочки FORWARD схожа с настройкой цепочки INPUT в первой части.

Сначала добавляем правило с модулем conntrack, идентичное правилу из цепочки INPUT:

# iptables -A FORWARD -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT

Затем включаем пересылку для доверенных интерфейсов и пропускаем все пакеты через цепочку fw-open:

# iptables -A FORWARD -j fw-interfaces 
# iptables -A FORWARD -j fw-open 

Остальные пакеты блокируются с отправкой ICMP-сообщения:

# iptables -A FORWARD -j REJECT --reject-with icmp-host-unreachable
# iptables -P FORWARD DROP

Цепочки fw-interfaces и fw-open

Назначение цепочек fw-interfaces и fw-open будет объяснено позже, когда мы будем работать с цепочками POSTROUTING и PREROUTING соответственно в таблице nat.

Таблица nat

В этом разделе предполагается, что исходящий интерфейс (с публичным IP-адресом) носит имя ppp0. Если ваш интерфейс называется иначе, то во всех приведённых ниже правилах следует заменить название на настоящее.

Цепочка POSTROUTING

Сначала мы должны определить, кому разрешено подключаться к сети Интернет. Предположим, имеется подсеть 192.168.0.0/24 (т.е. в неё входят все адреса в диапазоне 192.168.0.0-255), подключённая к интерфейсу eth0. Чтобы разрешить исходящие соединения хостам в этой подсети, настраиваем цепочку fw-interfaces в таблице FORWARD:

# iptables -A fw-interfaces -i eth0 -j ACCEPT

Затем необходимо отредактировать все исходящие пакеты, чтобы в поле "адрес отправителя" значился публичный адрес шлюза вместо локального LAN-адреса. Для этого используем таргет MASQUERADE:

# iptables -t nat -A POSTROUTING -s 192.168.0.0/24 -o ppp0 -j MASQUERADE

Не забудьте указать параметр -o ppp0, потому что в противном случае сеть не будет функционировать.

Предположим, что есть другая подсеть, 10.3.0.0/16 (с адресами 10.3.*.*), подключённая к интерфейсу eth1. Добавляем аналогичные правила:

# iptables -A fw-interfaces -i eth1 -j ACCEPT
# iptables -t nat -A POSTROUTING -s 10.3.0.0/16 -o ppp0 -j MASQUERADE

Наконец, нужно разрешить пересылку пакетов (если она ещё не включена).

Хосты данных подсетей теперь могут использовать вашу NAT-систему в качестве шлюза. Возможно, вы также захотите настроить DNS- и DHCP-сервер, например, dnsmasq или комбинацию BIND и dhcpd, с целью упрощения настройки разрешения имён (DNS resolving) на клиентских машинах, но эта тема выходит за рамки данного руководства.

Цепочка PREROUTING

В некоторых случаях может понадобиться изменить адрес получателя в заголовке входящего пакета с адреса шлюза на адрес хоста в локальной сети. Для этого нужно настроить созданную ранее цепочку fw-open, а также цепочку PREROUTING таблицы nat.

Например, чтобы изменить адрес получателя входящих SSH-пакетов (порт 22) на адрес ssh-сервера 192.168.0.5 выполните команды:

# iptables -t nat -A PREROUTING -i ppp0 -p tcp --dport 22 -j DNAT --to 192.168.0.5
# iptables -A fw-open -d 192.168.0.5 -p tcp --dport 22 -j ACCEPT

Во втором примере меняется не только адрес получателя, но и порт. Порт входящего соединения 8000 заменяется на порт 80 веб-сервера по адресу 192.168.0.6:

# iptables -t nat -A PREROUTING -i ppp0 -p tcp --dport 8000 -j DNAT --to 192.168.0.6:80
# iptables -A fw-open -d 192.168.0.6 -p tcp --dport 80 -j ACCEPT

Настройка для UDP-пакетов производится аналогично.

Сохранение правил

Чтобы сохранить новые правила межсетевого экрана для NAT-шлюза, выполните:

# iptables-save -f /etc/iptables/iptables.rules

При этом поздразумевается, что служба systemd iptables.service уже работает, потому что была включена ранее.

Смотрите также