Iptables: відповідність вихідного трафіку з conntrack та власником. Працює з дивними краплями


11

У своєму сценарії iptables я експериментував із написанням якомога дрібніших правил. Я обмежую, яким користувачам дозволено використовувати які сервіси, частково для безпеки та частково як навчальні заняття.

Використання iptables v1.4.16.2 на Debian 6.0.6 під керуванням ядра 3.6.2.

Однак я потрапив у проблему, яку я ще не зовсім розумію ...

вихідні порти для всіх користувачів

Це прекрасно працює. У мене немає жодних загальних правил відстеження стану.

## вихідний порт 81
$ IPTABLES -A OUTPUT -p tcp --dport 81 -m conntrack - statestate НОВЕ, ВСТАНОВЛЕНО -j ACCEPT
$ IPTABLES -A INPUT -p tcp --спорт 81 -s $ MYIP -m conntrack --ctstate ВСТАНОВЛЕНО -j ACCEPT

вихідні порти з відповідності користувача

## вихідний порт 80 для використаннярахунку
$ IPTABLES -A ВИХІД - власник відповідника - користувач -idid usecountcount -p tcp --dport 80 -m conntrack - statestate НОВЕ, ВСТАНОВЛЕНО --спорт 1024: 65535 -j ACCEPT
$ IPTABLES -A INPUT -p tcp --спорт 80 --portport 1024: 65535 -d $ MYIP -m conntrack - statestate Встановлено -j ACCEPT

Це дозволяє порту 80 виходити лише для облікового запису "useraccount", але такі правила, як для трафіку TCP, мають проблеми.

## За замовчуванням вихідний журнал + правила блоку
$ IPTABLES -A OUTPUT -j LOG --log-префікс "BAD OUTGOING" --log-ip-options --log-tcp-options --log-uid
$ IPTABLES -A ВИХІД -j DROP

Питання

Вищеописане працює, користувач "useraccount" може отримати файли ідеально чудовими. Жоден інший користувач у системі не може здійснювати вихідні з'єднання з портом 80.

useraccount @ host: $ wget http://cachefly.cachefly.net/10mb.test

Але виджет вище листя x7 викинув записи в моєму системному журналі:

18 жовтня 02:00:35 xxxx ядро: BAD OUTGOING IN = OUT = eth0 SRC = xx.xx.xx.xx DST = 205.234.175.175 LEN = 40 TOS = 0x00 PREC = 0x00 TTL = 64 ID = 12170 DF PROTO = TCP SPT = 37792 DPT = 80 SEQ = 164520678 ACK = 3997126942 WINDOW = 979 RES = 0x00 ACK URGP = 0  

Я не отримую ці краплі для подібних правил із трафіком UDP. У мене вже є правила, які обмежують, які користувачі можуть робити запити DNS.

Викинуті вихідні пакети ACK, схоже, надходять із кореневого акаунта (URGP = 0), який я не розумію. Навіть коли я міняю usecountcount на root.

Я вважаю, що пакети ACK класифікуються як нові, тому що conntrack починає відстежувати з'єднання після 3-го кроку третього рукостискання, але чому вони відкидаються?

Чи можна ці краплі безпечно ігнорувати?

Редагувати

Тому я часто бачу такі правила, які добре працюють для мене:

$ IPTABLES -A OUTPUT -s $ MYIP -p tcp -m tcp - доповідь 80-м стан - держава НОВИЙ, Встановлений -j ПРИХОД
$ IPTABLES -A INPUT -p tcp -m tcp --спорт 80 -d $ MYIP -m стан - держава ВСТАНОВЛЕНО -j ACCEPT

Я поміняв "-m state --state" на "-m conntrack --ctstate", оскільки збіг стану явно застарів.

Чи найкраща практика мати загальні правила відстеження стану? Чи не вважаються наведеними вище правила правильними?

Що б таке краще було для жорсткого контролю над з'єднаннями вихідних користувачів?

$ IPTABLES -A INPUT -m conntrack --ctstate Встановлено -j ACCEPT
$ IPTABLES -A OUTPUT -m conntrack --ctstate ВСТАНОВЛЕНО -j ACCEPT

$ IPTABLES -A OUTPUT -p tcp --dport 80-s $ SERVER_IP_TUNNEL -m conntrack - держава НОВИЙ -власник --uid-власник використання рахунок -j ACCEPT

$ IPTABLES -A OUTPUT -p tcp --portport 80-s $ SERVER_IP_TUNNEL -m conntrack - держава NEW -m власник --uid-власник інший рахунок -j ACCEPT

Чи можете ви опублікувати всі правила, включаючи ланцюг FORWARD та nat та таблицю, якщо цей хост також спрямовує трафік з локальної мережі.
Серж

Цей хост не здійснює жодної маршрутизації, трафік походить з машини з цими правилами. Я розмістив лише відповідні правила для конкретного вихідного трафіку.
arcX

Відповіді:


16

Якщо коротко сказати, ACK було надіслано, коли сокет не належав нікому. Замість того, щоб дозволяти пакетам, що відносяться до сокета, що належить користувачеві x, дозвольте пакети, які стосуються з'єднання, яке ініціювало сокет від користувача x.

Більш довга історія.

Щоб зрозуміти проблему, це допомагає зрозуміти, як wgetі HTTP-запити працюють взагалі.

В

wget http://cachefly.cachefly.net/10mb.test

wgetвстановлює з'єднання TCP до cachefly.cachefly.net, і один раз виникнувши відправляє запит в протоколі HTTP , який говорить: «Будь ласка , надішліть мені зміст /10mb.test( GET /10mb.test HTTP/1.1) і, до речі, НЕ могли б ви , будь ласка , не закривати з'єднання після того, як ви зробили ( Connection: Keep-alive.) Причина це відбувається тому, що у випадку, якщо сервер відповідає з перенаправленням на URL-адресу на ту ж IP-адресу, він може повторно використовувати з'єднання.

Тепер сервер може відповісти будь-яким: "Ось з'являються запитувані вами дані, будьте обережні, що це 10 МБ ( Content-Length: 10485760), і так гаразд, я залишаю з'єднання відкритим" Або якщо він не знає розмір даних, "Ось дані, вибачте, що не можу залишити з'єднання відкритим, але я скажу, коли ви можете перестати завантажувати дані, закривши мій кінець з'єднання".

У наведеній вище URL-адресі ми знаходимося в першому випадку.

Отже, як тільки wgetвін отримав заголовки для відповіді, він знає, що його робота виконана, як тільки він завантажив 10 Мб даних.

В основному, це те, що wgetчитати дані, поки не отримано та не вийде 10 МБ. Але в цей момент потрібно ще зробити. Що з сервером? Кажуть, щоб залишити з'єднання відкритим.

Перед виходом wgetзакриває ( closeсистемний виклик) дескриптор файлу для сокета. Після closeцього система закінчує визнання даних, надісланих сервером, і надсилає відповідь FIN: "Більше я більше не надсилаю дані". У цей момент closeповертається і wgetвиходить. Більше немає жодного сокета, пов’язаного з TCP-з'єднанням (принаймні, жодним користувачем не є). Однак це ще не закінчено. Отримавши це FIN, сервер HTTP бачить кінець файлу під час читання наступного запиту від клієнта. У HTTP це означає, що "більше не буде запиту, я закрию свій кінець". Тож він також надсилає свої FIN, щоб сказати: "Я також нічого не надсилаю, це з'єднання минає".

Отримавши цей FIN, клієнт надсилає "ACK". Але в цей момент wgetдавно минуло, так що ACK не від будь-якого користувача. Ось чому він блокується вашим брандмауером. Оскільки сервер не отримує ACK, він надсилатиме FIN знову і знову, поки він не здасться, і ви побачите більше скинутих ACK. Це також означає, що, скидаючи ці ACK, ви без потреби використовуєте ресурси сервера (який повинен підтримувати сокет у стані LAST-ACK) протягом досить тривалого часу.

Поведінка була б іншою, якби клієнт не запитував "Keep-Live" або сервер не відповів "Keep-Live".

Як уже згадувалося, якщо ви використовуєте трекер з'єднань, то, що ви хочете зробити, це пускати кожен пакет у встановленому та пов'язаному станах і турбуватися лише про NEWпакети.

Якщо ви дозволяєте NEWпакети від користувача, xале не пакети від користувача y, то інші пакети для встановлених з'єднань користувачем xбудуть проходити через те, що не може бути встановлено з'єднання користувачем y(оскільки ми блокуємо NEWпакети, які встановлювали б з'єднання), не буде жодного пакета для yпідключень користувачів, що проходять.


3

Це дозволяє порту 80 вийти лише для рахунку "useraccount"

- ну, принаймні, показані вами правила насправді цього не передбачають.

Існує також кімната для порад - не робіть перевірки користувача на ВСТАНОВЛЕНИХ потоках, просто робіть цю перевірку на НОВОМУ. Я також не бачу сенсу перевіряти вихідний порт під час перевірки Incoming ESTABLISHED, яка різниця в тому, який порт він був, він уже знаходиться у встановленому стані від PoV conntrack. Брандмауер повинен бути максимально простим, але при цьому ефективним, тому підхід бритви Occam найкраще підходить.


1
Дякую за пораду. Зазвичай я все для простоти, але це не сенс цієї конкретної вправи.
arcX

1
@arcX, ця вправа може вийти з ладу через занадто складну та непослідовну - IOW, поведінка, яку ви бачите, може бути пов'язана не з інтерфейсом netfilter (помилки, примхи), а з тим, як ви його використовуєте. Спробуйте спочатку спростити набір правил…
poige
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.