Linux 2.4 Packet Filtering HOWTO

Rusty Russell

http://www.linuxcenter.ru/lib/articles/networking/packet_filtering_howto.phtml

1. Введение

Добро пожаловать, уважаемый читатель.

Предполагается что вы уже знакомы с такими понятиями как IP адрес, адрес сети, маска, роутинг и DNS. Если нет, я рекомендую вам ознакомиться сперва с Network Concepts HOWTO.

Это HOWTO начинается с легкого введения (которое оставит у вас чувство тепла и неясности, и совсем незащищенным в реальной жизни) и заканчивается жестким разоблачением (которое оставит всех, кроме крутых профи, запутанными, панически ищущими что-нибудь потяжелее ;) )

Выша сеть не безопасна. Проблема в том чтобы разрешить быстрые, удобные связи и в то же время, ограничить использование их только в хороших намерениях... это приблизительно как разрешить обычный разговор и в то же время запретить кричать "Пожар!" в переполненном театре. Так вот, - эта ваша проблема не ставится задачей этого HOWTO.

Итак, только вы сами можете решить свои проблемы. Я только помогу вам в использовании некоторых утилит и предупрежу о некоторых уязвимостях, в надежде что вы используете это не с плохими намерениями.

2. Наш официальный Web-сайт. Наш лист рассылки.

Существуют три наших официальных сайта:

Официальный лист рассылки: Samba's Listserver.

3. Что такое - фильтр пакетов?

Фильтр пакетов это программа которая просматривает заголовки пакетов по мере их прохождения, и решает дальнейшую судьбу всего пакета. Фильтр может сбросить DROP пакет (т.е. как будто пакет и не приходил вовсе), принять ACCEPT пакет (т.е. пакет может пройти дальше), или сделать с ним что-то еще более сложное.

Под Linux, фильтр пакетов встроен в ядро (как модуль, или как неотъемлемая часть ядра), и мы можем делать с пакетами несколько хитроумных вещей, но основной принцип в просмотре заголовков пакетов и решении их дальнейшей судьбы сохраняется.

3.1 Почему я должен использовать фильтр пакетов?

Управление. Безопасность. Бдительность.

Управление:

когда вы используете linux для соединения вашей внутренней сети с другой сетью (скажем, с Интернетом) вы имеете возможность разрешать и запрещать отдельные типы траффика. Например, заголовок пакета содержит адрес назначения пакета, так что вы можете запретить пакеты идущие в определенные участки другой сети. Другой пример, я использую Netscape для доступа к архивам Дилберта. Netscape постоянно загружает рекламу doubleclick.net баннеры которой находятся на страницах этих архивов, что отнимает мое время. Запрет через фильтр пакетов на прохождение всех пакетов к doubleclick.net или обратно, решает эту проблему (хотя есть другие более лучшие пути решения этой проблемы: ищи Junkbuster)

Безопасность:

когда ваша linux машина, единственная преграда, между хаосом Интернета и вашей классной и правильной сетью, было бы хорошо знать что вы можете запретить то что ломиться к вам. Например, вы можете разрешить все что идет из вашей сети, и позаботиться о всем известном 'Ping of Death' приходящего от злобных внешних пользователей. Другой пример, вы не хотите чтобы внешние пользователи соединялись с вашей машиной по telnet протоколу, даже несмотря на то что все аккаунты защищены паролями. Может быть вы хотите (как большинство людей) быть наблюдателем в Интернете, а не сервером(желая того или нет). Просто не позвольте никому соединяться с вашим компьютером, запретив в фильтре пакетов все входящие пакеты используемые при установке соединения.

Бдительность:

временами плохо сконфигурированная машина в сети решает извергнуть во внешний мир какие-либо пакеты. Есть отличная возможность указать фильтру пакетов то чтобы он давал вам знать если что-либо ненормальное случилось; возможно, вы что-то сможете сделать, или возможно вы просто любопытны по своей природе.

3.2 Как я могу сделать фильтрование пакетов в Linux?

Ядра Linux имеют способность отфильтровывать пакеты еще с 1.1 серий. Первое поколение фильтров, основанное на ipfw от BSD, было спортированно Alan Cox в конце 1994г. Оно было улучшено Jos Vos и другими в Linux 2.0; пользовательская утилита "ipfwadm" контролировала правила фильтра в ядре. В середине 1998, для Linux 2.2, я с помощью Michael Neuling, переработал ядро достаточно основательно, и представил новую утилиту для управления фильтром - "ipchains". Наконец, в середине 1999, код ядра был снова полностью преписан для 2.4 версии, появилась утилита четвертого поколения "iptables". Это именна та утилита, которая послужила поводом для написания этого HOWTO.

Вам необходимо ядро, которое имеет netfilter-инфраструктуру: netfilter - это основа внутри Linux ядра, в которую могут встраиваться другие компоненты (такие как модуль iptables). Это означает что вам необходимо ядро 2.3.15 или выше, и вам необходимо ответить 'Y' на опцию CONFIG_NETFILTER при конфигурировании ядра.

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

iptables

Утилита iptables вставляет и удаляет правила из таблицы фильтра пакетов ядра. Это означает, что, чтобы вы не устанавливали или настраивали это будет потеряно при перезагрузке; смотри Делаем правила постоянными для того чтобы гарантировать что правила восстановятся после перезагрузки машины.

iptables это заменя для ipfwadm и ipchains: смотри Применение ipchains и ipfwadm для того чтобы избежать использования iptables если вы используете одно из вышеуказанных.

Делаем правила постоянными

Ваша текущая настройка firewall храниться в ядре, и соответственна будет потеряна при перезагрузке системы. Написание iptables-save и iptables-restore уже естьв моем TODO списке. Когда они будут созданы, они будут отличными инструментами - обещаю (на момент перевода эти утилиты уже созданы прим. перевод.).

Пока, вставляйте необходимые команды нужные для настройки вашего firewall в инициализирующий скрипт. Убедитесь, что вы создали нечно продвинутое.. такое - чтобы реагировало на ошибки при исполнении этого скрипта (обычно `exec /sbin/sulogin').

4. Кто ты такой и почему ты роешься в моем ядре?

Я Rusty; Linux IP Firewall разработчик и просто программист который оказался в нужном месте в нужной время. Я написал ipchains (смотри Как я могу сделать фильтрование пакетов в Linux? выше благодаря людям, которые сделали основную работу), и многому научился. Я надеюсь.

WatchGuard, отличная компания, специализирующаяся на защите сетей и продающая действительно хороший плагин Firebox, платит мне за ни за что, так что я могу тратить все свое время на разработку нового и поддержание старого фильтра пакетов. Я предсказывал что напишу его за 6 месяцев, а он отнял у меня 12, но я чувствал под конец, что работа над фильтром закончена. Множество переработок, отказ винта, кража ноутбука, пара испорченных файловых систем и один разбитый монитор позже... и вот наконец все.

Пока я с вами, хочу прояснить некоторые недопонимания в отношении меня: Я не гуру по ядру. Я знаю это, потому что моя работа связанная с ядром, позволила общаться с некоторыми из них: David S. Miller, Alexey Kuznetsov, Andi Kleen, Alan Cox. Однако, именно они занимались "глубинной магией", дав мне возможность пробираться через мелководье к безопасному месту.

5. Краткая инструкция по использованию фильтра пакетов от Rusty

Большинство людей имеет единственное PPP соединение к Интернет, и не хотят никого кто бы вошел в их сеть или firewall-хост:


## Загрузим модули для отслеживания соединений(не нужно если они встроены в ядро)
# insmod ip_conntrack
# insmod ip_conntrack_ftp

## Создаем цепь которая блокирует новые соединения, исключая исходящие изнутри сети.
# iptables -N block
# iptables -A block -m state --state ESTABLISHED,RELATED -j ACCEPT
# iptables -A block -m state --state NEW -i ! ppp0 -j ACCEPT
# iptables -A block -j DROP

## Назначим всему входящему и маршрутизируемому траффику проход через наше правило.
# iptables -A INPUT -j block
# iptables -A FORWARD -j block

6. Как пакеты проходят фильтры

Ядро запускается с тремя списками правил в таблице фильтра пакетов; эти списки называются firewall chains или просто chains (цепочки). Три цепочки называются так: INPUT, OUTPUT и FORWARD.

Это сильно отличается от того как работали ядра серий 2.0 и 2.2

Для фанатов ASCII-графики, цепочки устроены примерно так:

                          _____
Входящий                 /     \         Исходящий
       -->[Routing ]--->|FORWARD|------->
          [Decision]     \_____/        ^
               |                        |
               v                      ____
              ___                    /    \
             /   \                  |OUTPUT|
            |INPUT|                  \____/
             \___/                      ^
               |                        |
                --> локальные процессы -

Три круга представляют три цепочки. Когда пакет приходит в определенный круг, цепочка исследуется для того, чтобы определить, что делать с пакетом в дальнейшем. Если цепь говорит что надо сбросить (DROP) пакет, пакет разрушается, если цепь разрешает прохождение пакета (ACCEPT), пакет проходит к следующему шагу по диаграмме.

Цепочка - это набор определенных правил. Каждое правило говорит "если заголовок пакета выглядит так, значит надо делать с пакетом то-то". Если правило не совпадает с пакетом, пакет переходит к следующему правилу в данной цепочке. Наконец, если в цепочке не осталось больше правил совсем, ядро проверяет установленный policy (правило действующее по умолчанию для всей цепочки) для данной цепочки, чтобы решить что делать с пакетом дальше. В защищенных системах, policy обычно говорит ядру - разрушить пакет (DROP).

  1. Когда пакет приходит (скажем, через Ethernet карту) ядро сперва просматривает адрес назначения пакета: это называется "маршрутизацией".
  2. Если пакет предназначен для этой машины, пакет проходит через диаграмму в цепочку INPUT. Если это так то любой процесс ожидающий данный пакет получит его.
  3. Иначе, если в ядре отключена возможность маршрутизации, или если ядро не знает как этот пакет маршрутизировать, пакет разрушается. Если маршрутизация разрешена, и пакет предназначен для другого сетевого интерфейса (если вы имеете его), пакет идет по нашей диаграмме прямо в цепочку FORWARD. если он разрешен (ACCEPT), он будет маршрутизирован.
  4. Наконец, программа работающая на самой машине может посылать пакеты. Такие пакеты будут проходить через цепочку OUTPUT: если цепочка разрешает (ACCEPT) их, значит пакет продолжает свой путь на интерфейс для которого он предназначен.

7. Применение iptables

iptables имеет достаточно хорошо написанную и подробную manual страницу (man iptables), там вы можете найти все интересующие вас моменты. Те из вас кто знаком с ipchains могут просто просмотреть Различия между iptables и ipchains; они очень похожи.

Есть несколько различных вещей которые вы можете сделать с помощью iptables. Вы начинаете с тремя встроенными цепочками INPUT, OUTPUT и FORWARD, которые вы не можете удалить. Давайте посмотрим, что мы можем делать с цепочками:

  1. Создать новую цепочку (-N).
  2. Удалить пустую цепочку (-X).
  3. Изменить policy для встроенной цепочки (-P).
  4. Просмотреть правила в цепочке (-L).
  5. Очистить все правила в цепочке (-F).
  6. Обнулить счетчики пакетов и байтов во всех правилах в цепочке (-Z).

Есть несколько путей управления правилами внутри цепочки:

  1. Добавить новое правило к цепочке (-A).
  2. Вставить новое правило в цепочку (-I).
  3. заменить правило в цепочке (-R).
  4. Удалить правило в цепочке (-D).
  5. Удалить первое правило в цепочке соответствующее указанному (-D).
7.1 Что вы видите когда ваш компьютер грузится

iptables может быть модулем (называется "iptable_filter.o"), который должен быть автоматически загружен когда вы впервые запускаете iptables. Он может быть также встроен в ядро постоянно.

До того как будет выполнена любая команда iptables (будьте осторожны: некоторые дистрибьютивы выполняют iptables в инициализирующих систему скриптах), во встроенных цепочках не находиться ни одно правило (`INPUT', `FORWARD' и `OUTPUT'), все встроенные цепочки имеют policy по умолчанию ACCEPT. Вы можете изменить policy по умолчанию в цепочке FORWARD указав ключ "forward=0" для iptable_filter модуля.

7.2 Работа с одним правилом

Это вода и хлеб или основа работы фильтра пакетов; управление правилами. Наиболее часто вы, вероятно, будете использовать команды добавления (-A) и удаления (-D). Другие команды (-I для вставки и -R для замены) просто являются расширенными вариантами первых.

Каждое правило указывает набор условий, которым пакет должен соответствовать, и что с таким пакетом делать (цель). Пример, вы хотите сбрасывать все ICMP пакеты приходящие с адреса 127.0.0.1. В этом случае условия должны быть такими: протокол ICMP и исходный адрес 127.0.0.1. Наша цель - "DROP".

127.0.0.1 это так называемый `loopback' интерфейс, который есть у вас, даже если не подключены в реальную сеть. Вы можете использовать "ping" чтобы сгенерировать такие пакеты (ping посылает ICMP пакет 8 типа (echo request) на который получатель должен ответить ICMP пакетом типа 0 (echo reply)). Это делает ping полезным для тестирования.

# ping -c 1 127.0.0.1
PING 127.0.0.1 (127.0.0.1): 56 data bytes
64 bytes from 127.0.0.1: icmp_seq=0 ttl=64 time=0.2 ms

--- 127.0.0.1 ping statistics ---
1 packets transmitted, 1 packets received, 0% packet loss
round-trip min/avg/max = 0.2/0.2/0.2 ms
# iptables -A INPUT -s 127.0.0.1 -p icmp -j DROP
# ping -c 1 127.0.0.1
PING 127.0.0.1 (127.0.0.1): 56 data bytes

--- 127.0.0.1 ping statistics ---
1 packets transmitted, 0 packets received, 100% packet loss

Вы видите что первый ping был успешен ("-c 1" указывает команде ping послать только один пакет).

затем мы добавили (-A) в цепочку "INPUT", правило указывающее что пакеты с адреса 127.0.0.1 ("-s 127.0.0.1") использующие ICMP протокол ("-p icmp") мы должны сбрасывать DROP ("-j DROP").

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

Мы можем удалить правило двумя путями. Првый способ, так как мы знаем что это правило единственное в INPUT цепочке мы можем удалить его по номеру, как здесь:

# iptables -D INPUT 1

Эта команда удалит правило номер 1 в INPUT цепочке.

Второй способ, состоит в том чтобы отразить полностью введенную -A команду, заменив -A на -D. Это удобно когда вы имеете дело со сложной цепочкой правил и вам некогда пересчитывать их, чтобы выяснить в конце что правило номер 37 то от которого надо избавиться. В этом случае это выглядит так:

# iptables -D INPUT -s 127.0.0.1 -p icmp -j DROP

Синтаксис команды -D должен повторить в точности те же ключи что были указаны с командой -A (или -I или -R). Если в цепочке несколько одинаковых правил, то будет удалено только первое из них.

7.3 Спецификации фильтра

Мы знаем что с помощью ключа "-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 0/0' здесь лишнее. ]
# iptables -A INPUT -s 0/0 -j DROP
#

Такая запись редко используется, так как она соответствует установке по умолчанию, то есть если мы не укажем "-s" совсем.

Указываем инверсию

Многие ключи, включая "-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.3.15/net. Они будут загружены по требованию, если ваше ядро было скомпилированно с опцией CONFIG_KMOD, поэтому у вас нет необходимости вручную загружать их.

Расширения для утилиты iptables являются разделяемыми библиотеками, и обычно находятся в /usr/local/lib/iptables/, хотя в некоторых дистрибьютивах их могут разместить в /lib/iptables или /usr/lib/iptables.

Существуют расширения двух типов: новые цели, и новые соответствия (мы поговорим о новых целях немного позднее). Некоторые протоколы автоматически предлагают новые соответствия: сейчас это TCP, UDP и ICMP как показано ниже.

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

Чтобы получить помощь по определенному расширению, используйте следующие ключи ("-p", "-j" или "-m") с последующим ключом "-h" или "--help", пример:

# iptables -p tcp --help
#

TCP расширение

TCP расширение автоматически загружаются если указан ключ "-p tcp". Оно обеспечивает следующие возможности (ни одно из них не работает с фрагментами).

--tcp-flags

После может следовать "!", затем две строки флагов, этот ключ позволяет вам фильтровать пакеты по специфичным TCP флагам. Первая строка флагов - это маска: список флагов которые вы хотите исследовать. Вторая строка флагов, говорит какие флаг(и) должны быть установлены. Пример,

# iptables -A INPUT --protocol tcp --tcp-flags ALL SYN,ACK -j DENY

Это означает что все флаги должны быть проверены ("ALL" это синоним "SYN,ACK,FIN,RST,URG,PSH"), но только SYN и ACK должны быть установлены. Можно использовать также аргумент "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".

Другие расширения условий

Другие расширения в netfilter пакете даны как демонстрационные расширения, которые (если установленны) могут быть вызваны с помощью ключа "-m".

mac

Этот модуль может быть вызван с помощью ключа "-m mac" или "--match mac". Он используется для проверки исходного Ethernet (MAC) адреса входящего пакета, и поэтому полезен только при использовании в цепочках PREROUTING и INPUT. Модуль обеспечивает только одну опцию:

--mac-source

с последующим необязательным "!", затем указывается ethernet адрес в виде разделенных ":" шестнадцетеричных чисел, пример "--mac-source 00:60:08:91:CC:B7".

limit

Этот модуль может быть вызван с помощью ключа "-m limit" или "--match limit". И используется для ограничения скорости срабатывания правил, например для ограничения записи логов. Только ограниченное число раз в секунду, правило будет срабатывать (по умолчанию, три элемента в час, с очередью из 5 элементов). Модуль имеет два необязательных аргумента:

--limit

с последующим числом; указывает максимальное среднее число элементов срабатывающих в секунду. После числа можно указать временную единицу, используя "/second", "/minute", "/hour" или "/day", или сокращение ( "5/second" то же самое что и "5/s").

--limit-burst

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

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

# iptables -A FORWARD -m limit -j LOG

Когда это правило сработает впервые - информация о пакете попадет в лог; так как очередь по умолчанию равна 5, первые пять пакетов будут записаны. После этого, только по истечении двадцати минут, будет занесена новая информация о последующем пакете, независимо от того сколько пакетов уже прошло через это правило. Также, каждые двадцать минут, во время которых ни один пакет не подходит под это правило, один из элементов очереди освобождается; если в течении 100 минут ни один из пакетов не попал под это правило, то очередь полностью освободиться; и мы вернемся к тому с чего начали.

Информация: в данный момент вы не можете создать правило с временем освобождения очереди большим чем 59 часов, поэтому если вы установите среднюю величину восстановления в один в день, то ваша очередь должна быть меньше чем 3.

Вы можете также использовать этот модуль для того чтобы избежать различного рода (DoS) атаки.

защита от Syn-flood:

# iptables -A FORWARD -p tcp --syn -m limit --limit 1/s -j ACCEPT

защита от скрытого сканирования портов:

# iptables -A FORWARD -p tcp --tcp-flags SYN,ACK,FIN,RST RST -m limit --limit 1/s -j ACCEPT

всем известный Ping of death:

# iptables -A FORWARD -p icmp --icmp-type echo-request -m limit --limit 1/s -j ACCEPT

Этот модуль работает как "hysteresis door" (дверь с запаздыванием), как показано ниже на графике.


rate (pkt/s)
^ .---.
| / DoS \
| / \
Edge of DoS -|.....:.........\.......................
= (limit * | /: \
limit-burst) | / : \ .-.
| / : \ / \
| / : \ / \
End of DoS -|/....:..............:.../.......\..../.
= limit | : :`-' `--'
-------------+-----+--------------+------------------> time (s)
LOGIC => Match | Didn't Match | Match

Скажем, мы говорим - в одну секунду должен проходить один пакет, с очередью в 5 пакетов, но приходит 4 пакета в секунду в течении трех следующих секунд, затем происходит то же самое опять в течении трех секунд.




<--Flood 1--> <---Flood 2--->

Total ^ Line __-- YNNN
Packets| Rate __-- YNNN
| mum __-- YNNN
10 | Maxi __-- Y
| __-- Y
| __-- Y
| __-- YNNN
|- YNNN
5 | Y
| Y Key: Y -> Сработавшее правило
| Y N -> Несработавшее правило
| Y
|Y
0 +--------------------------------------------------> Time (seconds)
0 1 2 3 4 5 6 7 8 9 10 11 12

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

owner

Этот модуль пытается сравнить различные характеристики пакета и его создателя, для локально сгенерированных пакетов. Модуль работает только с цепочкой OUTPUT, и даже тогда, есть некоторые типы пакетов (такие как ICMP ответы на пинг) могут не иметь создателя, и соответственно такое правило с ними не сработает.

--uid-owner userid

Правило срабатывает, если пакет был создан процессом с указанным действительным (цифровым) user id.

--uid-owner groupid

Правило срабатывает, если пакет был создан процессом с указанным действительным (цифровым) group id.

--pid-owner processid

Правило срабатывает, если пакет был создан процессом с указанным process id.

--sid-owner sessionid

Правило срабатывает, если пакет был создан процессом с указанным session id.

unclean

Этот экспериментальный модуль может быть загружен с помощью ключей "-m unclean" или "--match unclean". Он производит различные проверки пакета на правильность. Этот модуль еще недостаточно отлажен, и не должен использоваться для обеспечения безопасности. (он вероятно может наделать много бед, так как сам может содержать дыры). В нем нет дополнительных ключей.

State расширение

Наиболее полезный критерий соответствия обеспечивается "state" расширением, которое интерпретирует выводы сделанные модулем отслеживания соединений "ip_conntrack". Это расширение крайне рекомендовано к использованию.

Указывая ключ "-m state" дает нам возможность использовать дополнительный ключ "--state", со списком аргументов разделенных запятой (знак "!" указывает, что пакет не совпадает с указанным набором аргументов). Аргументы таковы:

NEW

Пакет, который создает соединение.

ESTABLISHED

Пакет, который принадлежит уже установленному соединению (например, соединению которое получало ответные пакеты).

RELATED

Пакет, который относиться к, но не часть существующего соединения, такой как ICMP сообщение об ошибке, или (с загруженным FTP модулем), пакет устанавливающий ftp соединение для данных.

INVALID

Пакет, который не был идентифицирован по какой-либо причине: что включает в себя, нехватку памяти и ICMP ошибки, которые не соответствуют ни одному из известных соединений. Обычно такие пакеты должны быть сброшены.

7.4 Спецификация целей

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

Есть два очень простых встроенных правила: DROP и ACCEPT. Мы уже встречали с ними. Если правило совпадает с пакетом и цель правило соответствует одной из этих двух целей, то больше никакие правила не просматриваются: судьба пакета уже решена.

Есть два типа целей отличающихся от встроенных: расширения и пользовательские цепочки.

Пользовательские цепочки

Одна из мощных возможностей iptables наследованная от ipchains - это возможность создавать пользовательские цепочки, в дополнение к встроенным (INPUT, FORWARD и OUTPUT). По общему согласию, пользовательские цепочки всегда создаються в малом регистре, чтобы их можно было легче различать. (Мы опишем как создавать такие цепочки ниже в Операции над целой цепочкой).

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

Снова пришло время для ASCII художеств. Представте две (простые) цепочки: INPUT (встроенная цепочка) и test ( пользовательская цепочка).


`INPUT' `test'
---------------------------- ----------------------------
| Правило1: -p ICMP -j DROP| | Правило1: -s 192.168.1.1 |
|--------------------------| |--------------------------|
| Правило2: -p TCP -j test | | Правило2: -d 192.168.1.1 |
|--------------------------| ----------------------------
| Правило3: -p UDP -j DROP |
----------------------------

Представте TCP пакет приходящий с 192.168.1.1, и идущий к 1.2.3.4. Он попадает в INPUT цепочку, и проходит проверку в Правиле1 - не совпадает. Правило2 совпадает, и его цель - test, поэтому следующее правило, которое сработает - это Правило1 в test цепочке. Это правило в test совпадает - но не имеет цели, поэтому пакет перемещается к следующему правилу - Правилу2. Оно не совпадает, итак мы достигли конца цепочки. Мы возвращаемся в цепочку INPUT, где мы только что закончили обработку Правила2, так что теперь мы переходим к Правилу3, которое также не совпадает.

Итак путь пакето можно отобразить так:


v __________________________
`INPUT' | / `test' v
------------------------|--/ -----------------------|----
| Правило1 | /| | Правило1 | |
|-----------------------|/-| |----------------------|---|
| Правило2 / | | Правило2 | |
|--------------------------| -----------------------v----
| Правило3 /--+___________________________/
------------------------|---
v

Пользовательские цепочки могут иметь в качестве цели другие пользовательские цепочки (но не создавайте замкнутых циклов: выши пакеты будут сброшены, если фильтр определит что они зациклены).

Расширения к iptables: Новые цели

Другой тип цели - это расширение цели. Расширение цели состоит из модуля ядра, и необязательного расширения в iptables для обеспечения новых ключей командной строки. Есть несколько устанавливаемых по умолчанию в пакете netfilter расширений:

LOG Этот модуль обеспечивает ведения лога для совпавших с правилом пакетов. Он имеет дополнительные ключи:
--log-level С последующим числом или именем уровня. Правильные имена это "debug", "info", "notice", "warning", "err", "crit", "alert" and "emerg", соответствующие числам от "7" до "0". Вы можете ознакомиться с информацией по данным уровням в man странице для syslog.conf.

--log-prefix

С последующей строкой длинной до 29 символов, эта строка будет помещена в начало сообщения записанного в лог файл, для идентификации события.

Этот модуль наиболее полезен в совокупности с расширением limit, так вы не позволите переполнить ваши лог-файлы.

REJECT

Этот модуль создает тот же эффект что и цель "DROP", с одним исключением - отправителю пакета посылается ICMP пакет с сообщением об ошибке "port unreachable". заметьте, что ICMP сообщение не будет послано, если (смотрите RFC 1122):

REJECT имеет необязательный аргумент `--reject-with' который изменяет ответный пакет: смотри man страницу.

Специальные встроенные цели

Есть две специальные встроенные цели: RETURN и QUEUE.

RETURN создает тот же эффект как при достижении конца цепочки: для правила находящегося во встроенной цепочке, начинает действовать policy данной цепочки. Для правила, находящегося в пользовательской цепочке, пакет продолжает свою проверку в предыдущей цепочке, со следующего после сработавшего правила.

QUEUE - это специальная цель, которая ставит пакет в очередь на обработку пользовательскому процессу. Для того чтобы эта цель была полезна, необходимы еще два компонента:

Стандартный обработчик очереди для IPv4 - это модуль ip-queue, который распространяется с ядром и помечен как экспериментальный.

Ниже дан пример, как можно использовать iptables для передачи пакетов в пользовательское приложение:

# modprobe iptable_filter
# modprobe ip_queue
# iptables -A OUTPUT -p icmp -j QUEUE

С этим правилом, созданные локально пакеты ICMP типа (такие, что создаются скажем при помощи команды ping) попадают в модуль ip_queue, который затем пытается передать их в пользовательское приложение. Если ни одно из таких приложений не найдено, пакеты сбрасываются.

Чтобы написать пользовательскую программу обработки пакетов, используйте libipq API. Оно распространяется с пакетом iptables. Примеры можно найти в testsuite tools (например redirect.c) на CVS.

Статус ip_queue можно проверить с помощью:

/proc/net/ip_queue

Максимальную длинну очереди (то есть, число пакетов передаваемых в пользовательское приложение без подтверждения обработки) можно контролировать с помощью:

/proc/sys/net/ipv4/ip_queue_maxlen

По умолчанию - максимальная длинна очереди равна 1024. Как только этот предел достигается, новые пакеты будут сбрасываться, пока очередь не снизиться ниже данного предела. Хорошие протоколы, такие как TCP интерпретируют сброшенные пакеты как перегруженность канала передачи, и успешно с этим справляются (насколько я помню, пакет будет просто переслан заново удаленной стороной, прим. перевод.). Однако, может потребоваться некоторого рода эксперементирование, чтобы определить оптимальную длинну очереди в каждом конкретном случае, если по умолчанию очередь слишком мала.

7.5 Операции над целой цепочкой

Очень полезное свойство iptables это возможность группировать взаимозависимые правила в цепочки. Вы можете называть ваши цепочки как вам удобнее, но я рекомендую вам использовать буквы малого регистра, чтобы избежать путаницы со встроенными цепочками и целями. Имена цепочек могут быть длинной до 31 символа.

Создание новой цепочки

Давайте создадим новую цепочку. Так как я парень с развитым воображением, я назову ее test. Используем ключи "-N" или "--new-chain":

# iptables -N test

Это просто. Теперь вы можете создавать правила как указано выше.

Удаление цепочки

Удалить цепочку так же просто, как и создать ее, используем ключи "-X" или "--delete-chain". Почему "-X"? Да потому что все хорошие буквы мы уже использовали.

# iptables -X test

Есть пара ограничений, при удалении цепочек: они должны быть пустыми (смотри Очистка цепочек ниже) и они не должны использоваться как цель в любом из правил. Вы не сможете удалить ни одну из трех встроенных цепочек.

Если вы не укажете имя удаляемой цепочки, то, если это возможно, все пользовательские цепочки будут удалены.

Очистка цепочек

Есть простой путь очистки всех правил из цепочки, используя ключ "-F" (или "--flush").

# iptables -F FORWARD

Если вы не укажете имя цепочки, то все цепочки будут очищены.

Просмотр цепочек

Вы можете просмотреть все правила в цепочке используя ключ "-L" (или "--list").

значение "refcnt" (счетчик) показанный для каждой пользовательской цепочки это число правил ссылающихся на данную цепочку, как на цель. Он должен быть равен нулю (и цепочка должна быть пустой) прежде чем цепочку можно будет удалить.

Если имя цепочки не указано, все цепочки будут показаны, даже пустые.

Есть три параметра которые могут быть использованы с ключом "-L". Это "-n" (числа только) - полезен, так как предотвращает iptables от преобразования IP адресов в доменные имена, что (если вы используете DNS как большинство людей) вызовет значительные задержки, если ваша DNS служба, не настроена корректно, или если вы отфильтровываете DNS запросы. Также с этим параметром, TCP и UDP порты отображаются в цифровом виде, а не как имена сервисов, которые на них обычно запущены.

Параметр "-v" выдаст детальную информацию по каждой цепочке, такую как счетчик пакетов и байт, сравнение TOS, и интерфейсы. Иначе все эти значение не выдаются.

Счетчики пакетов и байт, печатаются с использованием суффиксов "K", "M" или "G" для 1000, 1,000,000 и 1,000,000,000 соответственно. Используя "-x" (расширенные числа) можно вывести полное числа, независимо от их величин.

Сбрасывание (Обнуление) счетчиков

Полезно иметь возможность сбрасывать счетчики. Это можно сделать с помощью ключа "-Z" (или "--zero").

Выполним следующее:

# iptables -L FORWARD
# iptables -Z FORWARD

В вышеприведенном примере, некоторые пакеты могли пройти во время интервала, между выполнением первой "-L" и второй команды "-Z". По этой причине, вы можете использовать эти две команды совместно, чтобы сбросить счетчики, во время просмотра.

Установка policy(политики по умолчанию)

Мы умолчали о том что случиться с пакетом, при достижении конца встроенной цепочки, когда ранее обсуждали как пакет проходит через цепочки. В этом случае policy данной цепочки будет определять судьбу пакета. Только встроенные (INPUT, OUTPUT и FORWARD) цепочки имеют policy, так как если пакет достигнет конца пользовательской цепочки, то он продолжит свой путь в предыдущей цепочке.

policy может быть или ACCEPT или DROP, например:

# iptables -P FORWARD DROP
8. Применение ipchains и ipfwadm

В пакете netfilter есть специальные модули: ipchains.o и ipfwadm.o. Загрузите один из них в ваше ядро (ВНИМАНИЕ: они не совместимы с ip_tables.o). После этого вы сможете использовать ipchains или ipfwadm как в старые добрые дни.

Они (модули ipchains и ipfwadm) будут поддерживаться еще некоторое время. Я думаю разумная формула такова 2 * [заметка о пререходе на новую структуру - время официального стабильного релиза], до даты когда стабильный релиз замены будет готов.

Это значит что для ipfwadm конец поддержки будет в:

2 * [Октябрь 1997 (2.1.102 релиз) - Март 1995 (ipfwadm 1.0)]
+ Январь 1999 (2.2.0 релиз)
= Март 2004.

Для ipchains, конец поддержки (поправтье меня: используйте настоящие даты):

2 * [Август 1999 (2.3.15 релиз) - Октябрь 1997 (2.2.0 релиз)]
+ Июль 2000 (2.4.0 релиз?)
= Март 2004.

Вообщем, вам не о чем беспокоиться до Марта 2004 года.

9. Совместная работа NAT и фильтра пакетов

Обычно люди хотят использовать совместно NAT (смотри NAT HOWTO) и фильтр пакетов. Хорошие новости в том что они работают совместно очень хорошо.

Вы разрабатываете свои правила для фильтра пакетов, полностью игнорируя NAT. Исходные адреса и адреса назначения пакетов, которые будет обрабатывать фильтр, будут "настоящими" исходными адресами и адресами назначения. Пример, если вы делаете DNAT, чтобы любые соединения на 1.2.3.4 порт 80 перенаправлялись на 10.1.1.1 порт 8080, фильтр пакетов будет видеть только пакеты идущие к 10.1.1.1 порт 8080 (настоящий пункт назначения), а не 1.2.3.4 порт 80. Подобно этому, вы можете игнорировать маскарад: для фильтра все пакеты будут казаться приходящими с их настоящих внутренних IP адресов (скажем 10.1.1.1), и ответы на эти пакеты будут как для настоящих IP адресов (ответы будут идти на 10.1.1.1 прим. перевод.).

Вы можете использовать "state" расширение без какой-либо дополнительной нагрузки на фильтр, так как NAT требует отслеживание пакетов в любом случае. Чтобы улучшить простой пример по маскараду данный в NAT HOWTO и запретить любые новые соединения приходящие с ppp0 интерфейса, вы должны сделать следующее:

# Маскарадим ppp0.
iptables -t nat -A POSTROUTING -o ppp0 -j MASQUERADE

# Запрещаем NEW и INVALID входящие или требуемые маршрутизации пакеты с ppp0.
iptables -A INPUT -i ppp0 -m state --state NEW,INVALID -j DROP
iptables -A FORWARD -i ppp0 0 -m state --state NEW,INVALID -j DROP

# Включаем маршрутизацию пакетов.
echo 1 > /proc/sys/net/ipv4/ip_forward
10. Различия между iptables и ipchains  

11. Советы по организации правил для фильтра пакетов

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

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

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

Я рекомендую обезопасить себя как можно сильнее: комбинируйте использование tcp-wrapperов (для соединений идущих на сам firewall), прокси (для соединений идущих транзитом через firewall), проверку маршрутов и использование фильтра пакетов. Проверка маршрутов - когда пакет приходит с интерфейса, с которого он не должен приходить, такой пакет должен быть разрушен (DROP): пример, если ваша внутренняя сеть имеет адрес 10.1.1.0/24, и пакет с таким исходным адресом приходит на ваш внешний интерфейс, он будет разрушен (DROP). Это можно сделать для одного интерфейса (ppp0) так:

# echo 1 > /proc/sys/net/ipv4/conf/ppp0/rp_filter

или для всех уже существующих интерфейсов и созданных в будущем:

# for f in /proc/sys/net/ipv4/conf/*/rp_filter; do
# echo 1 > $f
# done

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

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

Я крайне рекомендую использование отслеживания соединений на защищенных системах: конечно это отнимает часть ресурсов, так как все соединения регистрируются, но это очень полезное свойство для контролирования доступа к ваши сетям. Вам может понадобиться загрузить "ip_conntrack.o" модуль, если ваше ядро не загружает модули автоматически, и если он не встроен в ядро. Если вам необходимо отслеживать сложные протоколы, вам надо будет загрузить соответствующий модуль (например "ip_conntrack_ftp.o").

# iptables -N no-conns-from-ppp0
# iptables -A no-conns-from-ppp0 -m state --state ESTABLISHED,RELATED -j ACCEPT
# iptables -A no-conns-from-ppp0 -m state --state NEW -i ! ppp0 -j ACCEPT
# iptables -A no-conns-from-ppp0 -i ppp0 -m limit -j LOG --log-prefix "Bad packet from ppp0:"
# iptables -A no-conns-from-ppp0 -i ! ppp0 -m limit -j LOG --log-prefix "Bad packet not from ppp0:"
# iptables -A no-conns-from-ppp0 -j DROP

# iptables -A INPUT -j no-conns-from-ppp0
# iptables -A FORWARD -j no-conns-from-ppp0

Настройка и создание хорошего firewall не входит в данный HOWTO, но мой совет "всегда будьте минималистами". Просмотрите Security HOWTO для большей информации.