The OpenNET Project / Index page

[ новости /+++ | форум | теги | ]

Как работает firewall в Linux (iptables linux netfilter module firewall)


<< Предыдущая ИНДЕКС Правка src / Печать Следующая >>
Ключевые слова: iptables, linux, netfilter, module, firewall,  (найти похожие документы)
From: ilya :: HackZona.ru Date: Wed, 2 Aug 2007 18:21:07 +0000 (UTC) Subject: Как работает firewall в Linux Оригинал: http://www.hackzona.ru/hz.php?name=News&file=article&sid=3605 Каждый уважающий себя администратор Linux должен уметь не только настраивать iptables, но и знать, как он работает. В этой статье речь пойдет не о том, как правильно настраивать iptables или какой-нибудь другой firewall, а о том, как работают firewall'ы в Linux. В первую очередь, эта статья нацелена на читателей, которые занимаются (начинают или только хотят начать) программированием модулей ядра Linux (Linux Kernel Module - LKM), а также, надеюсь, поможет некоторым администраторам более детально разобраться в работе iptables. Оглавление : 1) Netfilter Hacking 2) Firewall своими руками 3) Пример norm.c Netfilter Hacking Что такое netfilter и какое отношение он имеет к Firewall'ам ? В основном, Netfilter представляет собой набор функций (hook) ,расположенных в ядре, при помощи которых firewall'ы могут получать доступ к пакетам и,основываясь на правилах, решать, как с ними поступать дальше. Netfilter содержит 5 основных hook-функций, которые описаны в linux/netfilter_ipv4.h. Графически их можно изобразить так : [INPUT]---> --->[ROUTE]--->[3]--->[4]--->[OUTPUT]]] ---------------------------|--------------------^ ---------------------------|---------------------| ---------------------------|---------------- [ROUTE] ---------------------------v---------------------| -------------------------[5] ---------------------------|--------------------- ^ ---------------------------|----------------------| ---------------------------v----------------------| ---------------------- [INPUT*]-----------[OUTPUT*] [1] NF_IP_PRE_ROUTING [2] NF_IP_LOCAL_IN [3] NF_IP_FORWARD [4] NF_IP_POST_ROUTING [5] NF_IP_LOCAL_OUT NF_IP_PRE_ROUTING - функция срабатывает как только мы получаем пакет, даже если он проходящий. Если мы хотим иметь доступ ко всем пакетам, проходящим через наш интерфейс, то мы должны использовать эту функцию. NF_IP_LOCAL_IN - срабатывает в случае, когда пакет адресован нам, перед поступлением пакета в сетевой стек. NF_IP_FORWARD - если пакет необходимо смаршрутизировать с одного интерфейса на другой. NF_IP_POST_ROUTING - для исходящих пакетов из нашего сетевого стека. NF_IP_LOCAL_OUT - для всех исходящих пакетов. Более подробную схему обработки пакетов вы можете посмотреть : http://open-source.arkoon.net/kernel/kernel_net.png После вызова функции и проведения нехитрых проверок над пакетом, нам нужно вынести вердикт, что делать с этим пакетом дальше. В нашем распоряжение 5 вариантов : [1] NF_ACCEPT : пропускает пакет дальше [2] NF_DROP : отбрасывает пакет [3] NF_REPEAT : повторный вызов функции [4] NF_STOLEN : забирает пакет (прекращается передвижение) [5] NF_QUEUE : ставит пакет в очередь, как правило для передачи в пользовательское пространство (мы ведь работаем в пространстве ядра) Вот собственно и все, что нужно для нормальной работы любого firewall'а в Linux. С одной стороны, набор функций, позволяющий получать доступ к пакетам практически в любой точке сетевого стека, а с другой, набор решений, как поступить с пакетом дальше. /* Я думаю, что администраторам дальше можно не читать, там пойдет объяснение структур, правильность их заполнение, а также примеры использования. Вся теория работы firewall'а заканчивается */ Теперь попытаемся разобраться, как все это работает! Первым делом нам нужно познакомиться со структурой nf_hook_ops, она и будет нашим проводником в мир netfilter'a. Описание её можно найти в /Linux/netfilter.h : 44 struct nf_hook_ops 45 { 46 struct list_head list; 47 48 /* User fills in from here down. */ 49 nf_hookfn *hook; 50 int pf; 51 int hooknum; 52 /* Hooks are ordered in ascending priority. */ 53 int priority; 54 }; Первое, что мы видим, это <<struct list_head list>> - это структура, которая содержит список всех hook-функций, но нас она не сильно интересует. nf_hookfn *hook - указатель на нашу функцию, в которой мы и будем проводить все наши проверки. Возвращаемое значение должно быть одно из 5-и поведений (NF_ACCEPT, NF_DROP, ...). int pf - служит для определения протокола, с которым мы хотим работать (PF_INET) int hooknum - а вот и место нашего вызова. (например NF_IP_PRE_ROUTING) int priority - приоритет. В случае, если определено несколько функций на один вызов, первым сработает тот, у кого выше приоритет. Мы будем использовать - NF_IP_PRI_FIRST. Не поверите, но это все! Остается лишь маленькое дополнение. После того, как мы объявим и заполним нашу структуру, её необходимо зарегистрировать. Для этого служат 2-е функции, которые объявлены все в том же /Linux/netfilter.h : 89 /* Function to register/unregister hook points. */ 90 int nf_register_hook(struct nf_hook_ops *reg); 91 void nf_unregister_hook(struct nf_hook_ops *reg); nf_register_hook - для регистрации нашей hook-функции nf_unregister_hook - для удаление нашей функции из цепочки. Ничего особенного, просто банальное предупреждение. Обязательно выгружайте ваши функции при выгрузке модуля из памяти при помощи nf_unregister_hook. Если этого не сделать, произойдет очень неприятная вещь. Придет пакет, сработает наш вызов, ядро попытается обратиться к странице памяти для вызова нашей функции для обработки, а там.... Эээ в лучшем случае ничего, в худшем ..кто-то занял наше место и тогда результат будет непредсказуем. Firewall своими руками Для примера напишем маленький firewall. Который будет беспощадно уничтожать все входящие и исходящие пакеты. bash$ > cat firewall.c #define __KERNEL__ #define MODULE #define LINUX #include <module.h> #include <kernel.h> #include <netfilter.h> #include <netfilter_ipv4.h> MODULE_LICENSE("GPL"); MODULE_AUTHOR("ilya"); /* Объявление структур. Мы объявим 2-е структуры. */ /* 1-я для входящих пакетов */ /* 2-я для исходящих пакетов */ struct nf_hook_ops nf_incoming; struct nf_hook_ops nf_outgoing; /* наша функция обработки */ unsigned int main_hook (unsigned int hooknum, struct sk_buff **skb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff*)) { /* Для примера, мы будем отбрасывать все пакеты */ return NF_DROP; } int init_module () { /* Заполнение структур */ /* Сначала, заполним структуру для входящих пакетов */ nf_incoming.hook = main_hook; /* указатель на нашу функцию */ nf_incoming.pf = PF_INET; nf_incoming.hooknum = NF_IP_PRE_ROUTING; nf_incoming.priority = NF_IP_PRI_FIRST; /* Теперь для исходящих */ nf_outgoing.hook = main_hook; nf_outgoing.pf = PF_INET; nf_outgoing.hooknum = NF_IP_PRE_ROUTING; nf_outgoing.priority = NF_IP_PRI_FIRST; /* Вроде все, осталось только зарегистрировать наши функции */ nf_register_hook(&nf_incoming); nf_register_hook(&nf_outgoing); printk ("FireWall loaded обратный слеш n"); return 0; } void cleanup_module () { /* Не забываем удалить наши вызовы J, а то конфуз может случится */ nf_unregister_hook(&nf_incoming); nf_unregister_hook(&nf_outgoing); printk ("FireWall unload обратный слэш n "); } Ну вот. Все очень просто! Теперь компилируем наш модуль : Gcc -c firewall.c -I- -I /usr/src/Linux/include А еще лучше, написать makefile! Но это уже на ваше усмотрение. И запускаем : insmod firewall.o (иногда приходится запускать с ключем -f : insmod -f firewall.o, а то ему версии не нравятся, но кому не лень, можно в модуле прописать все данные о версии ядра) Посмотрите /var/log/messages - увидите <<FireWall loaded>> Значит наш модуль загрузился. Теперь, если вы попробуйте подключится к кому-нибудь или, наоборот, кто-то захочет к вам подключится, ничего не выйдет. Наш модуль не пропустит ни одного пакета. Что бы вернуть все на место, просто выгрузите модуль : Rmmod firewall Вот пример firewall'a в 60 строк, включая заголовки. Не сложно правда..!!! :)) Теперь перейдем к более сложным вещам. Но совсем на чуть-чуть :). Пример norm.c В этом примере мы будем проводить небольшой анализ захваченного нами пакета. Наша программа будет анализировать заголовки пакета и в случае неудовлетворения правилам, будет удалять его или править. Правила я брал из статьи <<Нормализация пакета>> (Спасибо автору, очень познавательная). Итак, небольшое введение в структуру sk_buff: Sk_buff - это буфер для работы с пакетами. Как только приходит пакет или появляется необходимость его отправить, создается sk_buff, куда и помещается пакет, а также сопутствующая информация, откуда, куда, для чего... На протяжение всего путешествия пакета в сетевом стеке используется sk_buff. Как только пакет отправлен или данные переданы пользователю, структура уничтожается, тем самым освобождая память. Описание этой структуры можно найти в linux/skbuff.h Она очень большая, я не буду выкладывать её сюда :) Все что мы будем использовать из неё, это : Protocol - чтобы знать, с каким протоколом серевого уровня мы имеем дело Data - место, где лежит пакет. Более подробно о работе sk_buff можно почитать в Интернете, информации о нем море, а что касается практической части, советую почитать статью из phrack No. 55 "Building Into The Linux Network Layer" http://www.phrack.org/show.php?p=55&a=12 Ну вроде все, с теорией маленько разобрались. Теперь определимся, что и как мы будем делать. Так как это лишь пример использования, я не буду заострять внимание над нормализацией конкретного протокола, просто пробежимся немного по протоколам и все : 1) IP - проверка протокола следующего уровня (пропускать только TCP, UDP, ICMP) 2) IP - если поле TTL cat norm.c #define __KERNEL__ #define MODULE #define LINUX #include <module.h>* Эйй, мы ведь пишем модуль к ядру */ #include <kernel.h> #include <netfilter.h> #include <netfilter_ipv4.h> /*подключаем заголовки для работы с сетевыми протоколами */ /* и собственно говоря sk_buff */ #include <skbuff.h> #include <inet.h> #include <ip.h> #include <ip.h> #include <tcp.h> #include <icmp.h> #include <uaccess.h> /* определяем дериктивы для преобразования байт из сетевого в нормальный :) */ /* и наоборот */ #define ntohs(x) __be16_to_cpu(x) #define htons(x) __cpu_to_be16(x) /* Увековечим свое имя Aha-ha... */ MODULE_LICENSE("GPL"); MODULE_AUTHOR("llya"); struct nf_hook_ops nf_incoming; struct sk_buff *skbf; struct tcphdr *th; struct icmphdr *icmph; struct iphdr *iph; unsigned int main_hook (unsigned int hooknum, struct sk_buff **skb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff*)) { skbf=*skb; /* Работаем только с IP */ if (skbf->protocol != htons(ETH_P_IP)) return NF_ACCEPT; /* Пропускаем только ICMP, TCP & UDP */ if (skbf->nh.iph->protocol != IPPROTO_TCP && skbf->nh.iph->protocol != IPPROTO_ICMP && skbf->nh.iph->protocol != IPPROTO_UDP) return NF_DROP; /* Проверка поля ttl */ if (ntohs(skbf->nh.iph->ttl)nh.iph->ttl=htons(100); ip_send_check(skbf->nh.iph); /* подсчет checksum */ return NF_ACCEPT; } /* Проверка ICMP, что мы здесь делаем я думаю вы знаете :) */ if (skbf->nh.iph->protocol==IPPROTO_ICMP) { skbf->h.icmph=(struct icmphdr *)(skbf->data+(skbf->nh.iph->ihl*4)); if (skbf->h.icmph->type==ICMP_ECHOREPLY && skbf->h.icmph->code==0) return NF_ACCEPT; if (skbf->h.icmph->type==ICMP_DEST_UNREACH) return NF_ACCEPT; if (skbf->h.icmph->type==ICMP_ECHO && skbf->h.icmph->code==0) return NF_ACCEPT; return NF_DROP; } /* Блокируем TCP, если порт источника или назначение 31337 и при этом делаем запись */ /* в messages */ if (skbf->nh.iph->protocol==IPPROTO_TCP) { skbf->h.th=(struct tcphdr *)(skbf->data+(skbf->nh.iph->ihl*4)); if (skbf->h.th->dest==htons(31337) || skbf->h.th->source==htons(31337)) { printk ("Hacking attempt :) Good bye, young kiddies "); return NF_DROP; } return NF_ACCEPT; } /* Хех, если все прошло гладко, и никто не попал под наш мини-нормализатор */ /* милости просим в сетевой стек!!! */ return NF_ACCEPT; } int init_module () { nf_incoming.hook = main_hook; nf_incoming.pf = PF_INET; nf_incoming.hooknum = NF_IP_PRE_ROUTING; nf_incoming.priority = NF_IP_PRI_FIRST; nf_register_hook(&nf_incoming); printk ("FireWall loaded обратный слэш n "); return 0; } void cleanup_module () { nf_unregister_hook(&nf_incoming); printk ("FireWall unload обратный слэш n "); } Ну вот и все ребята. Теперь вы знаете, как работают firewall'ы в Linux и даже сможете написать свой собственный. Знаете для чего служит netfilter и немного познакомились с работай сетевого стека ядра. Как видите - ничего сложного. Пару строк кода, и вы получаете доступ к святая святых, вы можете вертеть протоколам как угодно, менять их заголовки, отслеживать неприятеля и многое многое другое... Напоследок, для тех кто хочет заниматься программированием LKM, вот полезная литература : 1) Энциклопедия разработчика модулей ядра Linux (Linux Kernel Module Programming Guide) (https://www.opennet.ru/docs/RUS/lkmpg/) 2) The Linux Kernel Module Programming Guide (http://www.linuxcenter.ru/lib/books/lkmpg.phtml) (на русском) 3) Unreliable Guide To Hacking The LinuxKernel (http://people.netfilter.org/~rusty/unreliable-guides/kernel-hacking/lk-hacking-guide.html) 4) Hacking the Linux Kernel Network Stack (http://www.phrack.org/show.php?p=61&a=13) 5) Удобный ресурс для просматривания исходников ядра : (http://lxr.linux.no/source/)

<< Предыдущая ИНДЕКС Правка src / Печать Следующая >>

Обсуждение [ RSS ]
  • 1, Tima (??), 15:50, 14/10/2007 [ответить]  
  • +/
    Уважаемый ilya!
    Спасибо за статью, очень много для себя нашел нового? Есть вопрос - как я могу на основе того, что Вы написали, написать свой firewall, где будет происходить динамическая фильтрация траффика по IP-адрессу? (IP адресс будет передаваться модулю)
     
  • 2, LeNiN (ok), 12:44, 31/07/2009 [ответить]  
  • +/
    Лучше читать с оригинальной страницы, там форматирование правильное — например, не сбиты номера на первом рисунке.

    Статья отличная, спасибо!

     
  • 3, dekkard (?), 08:58, 25/11/2009 [ответить]  
  • +/
    читайте оригинал!
    http://www.linuxjournal.com/article/7184
    там ошибок меньше..

    вместо
    nf_outgoing.hooknum = NF_IP_PRE_ROUTING;
    должно быть
    netfilter_ops_out.hooknum = NF_IP_POST_ROUTING;

     

     Добавить комментарий
    Имя:
    E-Mail:
    Заголовок:
    Текст:




    Партнёры:
    PostgresPro
    Inferno Solutions
    Hosting by Hoster.ru
    Хостинг:

    Закладки на сайте
    Проследить за страницей
    Created 1996-2024 by Maxim Chirkov
    Добавить, Поддержать, Вебмастеру