Безопасность в Linux. Фильтрация пакетов 2

Безопасность в Linux. Фильтрация пакетов Продолжение. Начало в КГ №№ 2, 3
Сегодня мы переходим к непосредственному использованию iptables.

Iptables может быть модулем (называется "iptable_filter.o"), который должен быть автоматически загружен, когда вы впервые запускаете iptables. Он может быть также встроен в ядро постоянно. До того как будет выполнена любая команда iptables (будьте осторожны: некоторые дистрибутивы выполняют iptables в инициализирующих систему скриптах), во встроенных цепочках ("INPUT", "FORWARD" и "OUTPUT") нет ни одного правила, все встроенные цепочки имеют policy по умолчанию ACCEPT. Вы можете изменить policy по умолчанию в цепочке FORWARD, указав ключ "forward=0" для iptable_filter модуля.
Итак, для начала рассмотрим работу фильтра с одним правилом. Замечание: для управления правилами наиболее часто вы, вероятно, будете использовать команды добавления (-A) и удаления (-D). Другие команды (-I для вставки и -R для замены) просто являются расширенными вариантами первых. Каждое правило указывает набор условий, которым пакет должен соответствовать, и что с таким пакетом делать (цель). Пример: вы хотите сбрасывать все ICMP пакеты, приходящие с адреса 127.0.0.1. В этом случае условия должны быть такими: протокол ICMP и исходный адрес 127.0.0.1. Наша цель — "DROP" (сбросить). 127.0.0.1 — это так называемый loopback-интерфейс, который есть в любом Linux, даже неподключенном в сеть. Вы можете использовать утилиту ping, чтобы сгенерировать пакеты типа ICMP (для справки: ping посылает ICMP-пакет 8-го типа (echo request), на который получатель должен ответить ICMP-пакетом 0-го типа (echo reply)). Т.е. мы используем ping для тестирования нашего правила, которое запрещает "пинговать". Если теперь без установленного правила дать команду
ping -c 1 127.0.0.1
(послать один ICMP-пакет на наш адрес), то утилита скажет, что ответ был успешно получен. Для установки правила, используя ранее описанные ключи, даем команду следующего вида:
iptables -A INPUT -s 127.0.0.1 -p icmp -j DROP
Теперь, если дать повторную команду ping, то ответ от хоста получен не будет. Таким образом, можно защититься от пингования. Строка команды добавления правила расшифровывается так: добавляем (-A) в цепочку "INPUT" правило, указывающее, что пакеты с адреса 127.0.0.1 ("-s 127.0.0.1"), использующие ICMP протокол ("-p icmp"), мы должны сбрасывать DROP ("-j DROP"). Как же удалить правило? Мы можем удалить правило двумя путями. Первый способ: так как мы знаем, что это правило единственное в INPUT-цепочке, мы можем удалить его по номеру: iptables -D INPUT 1.
Эта команда удалит правило номер 1 в INPUT-цепочке. Второй способ состоит в том, чтобы повторить полностью введенную команду добавления, заменив при этом ключ "-A" на "-D". Это удобно, когда вы имеете дело со сложной цепочкой правил и вам некогда пересчитывать их, чтобы выяснить в конце, что правило номер 45 именно то, от которого надо избавиться. Для нашего примера команда выглядит так: iptables -D INPUT -s 127.0.0.1 -p icmp -j DROP.
Синтаксис команды должен быть повторен в точности, должны быть указаны те же ключи, что были указаны с командой -A (или -I, или -R). Если в цепочке несколько одинаковых правил, то будет удалено только первое из них.
Мы знаем, что с помощью ключа "-p" мы указываем протокол, а с помощью "-s" — исходящий адрес, но есть также и другие ключи, с помощью которых мы можем указывать характеристики пакета. Ниже дана исчерпывающая информация.
— Исходящий IP адрес и IP адрес назначения. Исходящий IP адрес (ключи "-s", "--source" или "--src") и IP адрес назначения ("-d", "--destination" или "--dst") может быть указан четырьмя способами. Наиболее обычный путь — использовать полное имя, такое как "localhost" или "www.linuxhq.com". Второй путь — указать явный IP адрес "127.0.0.1". Третий и четвертый пути разрешают нам указать группу IP адресов "199.95.207.0/24" или "199.95.207.0/ 255.255.255.0". Оба эти варианта указывают на любые IP адреса от 199.95.207.0 до 199.95.207.255 включительно; цифры после "/" указывают, какая часть IP адресов должна учитываться. "/32" или "/ 255.255.255.255" используется по умолчанию (совпадает с указанным IP адресом полностью). Чтобы указать любой IP адрес, надо использовать "/0".
— Инверсия. Многие ключи, включая "-s" (или "--source") и "-d" ("--destination"), могут использовать аргументы с предшествующим знаком "!" (произносится как "не") это означает все, КРОМЕ указанного. Пример: "-s! localhost" совпадает с любым пакетом, не идущим с localhost.
— Протокол. Протокол можно указать с помощью ключа "-p" (или "--protocol"). Протокол может быть номером (если вы знаете цифровые значения протоколов для IP) или имя протокола "TCP", "UDP" или "ICMP". Регистр не имеет значения, так что "tcp" будет понято так же, как и "TCP". Имя протокола может иметь префикс "!", что инвертирует его — так, "-p! TCP" означает "все пакеты, которые не TCP".
— Интерфейс. Ключи "-i" (или "--in-interface") и "-o" (или "--out-interface") указывают имя интерфейса, который должен совпасть. Интерфейс — это физическое устройство, на которое пакет приходит ("-i") или с которого пакет уходит ("-o"). Вы можете использовать команду ifconfig, чтобы просмотреть, какие устройства в данный момент работают. Пакеты, проходящие цепочку INPUT, не имеют исходящего интерфейса, соответственно, любое правило, использующее "-o", в этой цепочке, никогда не сработает. Пакеты, проходящие через цепочку OUTPUT, не имеют входящего интерфейса, поэтому любое правило, использующее "-i", в этой цепочке, никогда не сработает.
Только пакеты, проходящие через цепочку FORWARD, имеют и входящий, и исходящий интерфейс. Вы можете указать интерфейс, который в данный момент не существует; такое правило не будет работать до тех пор, пока соответствующий интерфейс не "поднимется". Это очень удобно, когда используется PPP-соединения по дозвонке (обычно интерфейс ppp0) и им подобные. Имя интерфейса, заканчивающееся на "+", означает совпадение всех интерфейсов, начинающихся на указанную строку (независимо от того, существуют они или нет). Например, чтобы создать правило, совпадающее со всеми PPP-интерфейсами, следует использовать ключ "-i ppp+". Имя интерфейса может иметь префикс "!", что будет совпадать со всеми интерфейсами, не совпадающими с указанным(и).
— Фрагменты. Иногда пакет слишком велик, чтобы уместиться в канале за раз. Когда это случается, пакет делится на фрагменты и посылается как множество более мелких пакетов. На другом конце канала такой пакет заново формируется из этого множества маленьких пакетов. Проблема заключается в том, что только начальный фрагмент содержит полный набор заголовочных полей (IP + TCP, UDP и ICMP), которые можно изучить, последующие же имеют только ограниченный набор полей из первоначального заголовка (IP без дополнительных полей протокола). Поэтому получение содержимого полей протокола (что делается TCP, UDP и ICMP расширениями) из последующих фрагментов невозможно. Если вы ведете учет всех соединений (connection tracking) или NAT, значит все фрагменты будут собраны в единый пакет, прежде чем он дойдет до кода фильтра пакетов, поэтому вам никогда не придется беспокоиться об оставшихся фрагментах. Иначе, очень важно понимать, как фрагменты проходят через правила фильтра пакетов. Любое правило, требующее информацию, которую мы не будем иметь, не сработает. Это означает, что первый фрагмент будет обрабатываться как и любой другой пакет. Второй и последующие фрагменты — не будут. Так, правило: "-p TCP --sport www" (указывающее исходящий порт "www") никогда не сработает с фрагментом (кроме первого). Так же не будет работать и "-p TCP --sport! www". Однако, вы можете создать правило специально для второго и последующих фрагментов, используя ключ "-f" (или "--fragment"). Можно указать и инверсное правило, которое не будет применяться для второго и последующих пакетов, указав в качестве префикса "!" ("! -f").
Обычно считается безопасным позволять второму и последующим фрагментам проходить, так как фильтр уже повлиял на первый фрагмент, и иначе дальний узел не сможет пересобрать весь пакет; однако, известны ошибки, которые позволяли грохнуть систему, просто посылая фрагменты. Информация для сетевых-голов: неправильные пакеты (TCP, UDP и ICMP пакеты слишком малые для получения информации о портах или ICMP код и тип) при подобных атаках уничтожаются. Итак, TCP фрагменты считаются ими, если они не меньше 8 байтов. Пример, следующее правило будет уничтожать любые фрагменты, идущие на 192.168.1.1:
iptables -A OUTPUT -f -d 192.168.1.1 -j DROP.
— Расширения iptables: новые ключи. Iptables является расширяемым инструментом. Это означает, что ядро и утилита iptables могут быть переписаны для добавления новых возможностей. Некоторые из этих расширений стандартны, другие более экзотичны. Расширения могут делать другие люди и распространять отдельно для определенных людей. Расширения ядра обычно находятся в директории, содержащей модули ядра, такой как /lib/modules/2.4.8/net. Они будут загружены по требованию, если ваше ядро было скомпилированно с опцией CONFIG_KMOD, поэтому у вас нет необходимости вручную загружать их. Расширения для утилиты iptables являются разделяемыми библиотеками и обычно находятся в /usr/local/lib/ iptables/, хотя в некоторых дистрибутивах их могут разместить в /lib/iptables или /usr/lib/iptables. Существуют расширения двух типов: новые цели и новые ключи. Некоторые протоколы автоматически предлагают новые ключи: сейчас это TCP, UDP и ICMP, как показано ниже. Вы можете указывать новые ключи в командной строке после ключа "-p", который загружает указанное расширение. Чтобы получить помощь по определенному расширению, используйте ключи "-p" или "-j" с последующим ключом "-h" или "--help", пример:
iptables -p tcp --help
Поговорим подробнее про расширения, предоставляемые протоколами.
TCP-расширение. TCP-расширение автоматически загружается, если указан ключ "-p tcp". Оно обеспечивает следующие возможности (ни одно из них не работает с фрагментами):
--tcp-flags. После может следовать "!", затем две строки флагов, этот ключ позволяет вам фильтровать пакеты по специфичным TCP-флагам. Первая строка флагов — это маска: список флагов, которые вы хотите исследовать. Вторая строка флагов говорит, какие флаг(и) должны быть установлены. Можно использовать также аргумент "NONE", что значит "никакие флаги не установлены".
--syn. Может быть с префиксом "!", это сокращение для "--tcp-flags SYN,RST,ACK SYN".
--source-port. Может быть с последующим "!", затем с одним TCP портом или с диапазоном портов. Можно использовать имена портов, соответствующие /etc/services или цифры. Диапазоны — это или два имени порта, разделенные ":", или (чтобы указать больше, чем данный порт, либо равный), порт с добавленным ":", или (чтобы указать меньше, чем данный порт, либо равный), порт с префиксом ":".
--sport. Это синоним "--source-port".
--destination-port и -dport. Это то же самое, что и выше, только указывает порт на пункте назначения пакета.
--tcp-option. С последующим необязательным "!" и числом будет совпадать с пакетом TCP, параметр которого равен заданному числу. Пакет, который не будет иметь полного TCP заголовка, будет сброшен автоматически при попытке изучения его TCP-параметра.
Замечание: расшифровка TCP-флагов. Иногда бывает полезно разрешить TCP-соединения в одном направлении и запретить в противоположном. Например, вам необходимо разрешить соединения на внешний WWW сервер, но запретить соединения с него. Первой мыслью приходит в голову — запретить TCP-пакеты, приходящие с того сервера. К сожалению, TCP-соединения для работы требуют прохождения пакетов в обоих направлениях.
Решение проблемы в том, чтобы блокировать только пакеты, используемые для установки соединения. Такие пакеты называются SYN пакетами (если быть точными, то это пакеты с установленным SYN-флагом, а FIN и ACK флаги должны быть чистыми, мы называем такие пакеты SYN-пакетами для краткости). Запретив только эти пакеты, мы сможем прекратить установку соединений в самом начале. Ключ "--syn" используется именно для таких случаев: он действителен только для правил, которые указали протокол TCP как используемый протокол. Например, следущее правило соответствует попытке установки соединения с узла 192.168.1.1:
-p TCP -s 192.168.1.1 --syn
Этот флаг может быть также инвертирован при использовании префикса "!", что будет означать все пакеты, кроме пакетов, используемых для установки соединений.
UDP-расширение. UDP-расширение автоматически загружается при указании ключа "-p udp". Оно обеспечивает возможность использования ключей "--source-port", "--sport", "--destination-port" и "--dport" с тем же синтаксисом, что и для TCP-расширения.
ICMP-расширение. ICMP-расширение автоматически загружается при указании ключа "-p icmp" и обеспечивает только одну новую возможность: ключ --icmp-type, с последующим необязательным "!", затем идет или тип icmp пакета (пример "host-unreachable"), или цифра (номер типа, пример "3"), или номер типа и код, разделенные "/" (пример "3/3"). Список icmp-типов можно получить при помощи команды "-p icmp --help".
Стоит упомянуть, что есть еще и другие расширения, подробнее о них можно прочитать в документации к iptables.

Продолжение следует
Материал подготовлен с использованием "Linux 2.4 Packet Filtering HOWTO by Rusty Russell".

X-Stranger xstranger@tut.by
www.linux4u.narod.ru



Компьютерная газета. Статья была опубликована в номере 04 за 2002 год в рубрике soft :: linux

©1997-2024 Компьютерная газета