NAT у FreeBSD за допомогою IPFilter (ipnat)

Зміст

Синдром гамака є одним із ключових моментів деградації сисадміну. Він починається з того, що стабільність та нерухомість стають синонімами. Колись, перш ніж IPFilter включили до складу FreeBSD, єдиним нормальним способом зробити NAT у FreeBSD був natd. Обов'язковою умовою для повноцінної роботи natd є наявність dummynet (4). natd і досі може бути в деяких випадках актуальним. Ми ж розглянемо використання ipnat з IPFilter як засіб для Network Address Translation (NAT).

Цей документ не є покроковим howto для чайників — мається на увазі, що не потрібно пояснювати, що після зміни /etc/syslog.conf йому потрібно надіслати сигнал HUP, а файли, куди він буде писати, створювати потрібно самостійно і як «застосовувати» змінені правила . Якщо Ви вважаєте, що ще не зовсім освоїлися з *nix і з FreeBSD зокрема, то на цю тему можна почитати: ipnat(1), ipnat(5), ipnat(8) ipf(5), ipf(8), ipfstat(8), ipftest(1), ipmon(8)

Тут і далі вважатиметься, що IPFilter має як мінімум версії 3.4.29. Якщо у Вашому випадку це не так — то Ви ще не зовсім вилізли з гамака, хоча вже той факт, що Ви читаєте цей текст, є доказом Вашого бажання.

Підготовка

Отже, розглянемо ситуацію: ми маємо 2 інтерфейси - fxp0 (200.200.200.1), що дивиться у великий і злий інтернет, і dc0 (192.168.0.1), що дивиться у внутрішню мережу.

Завдання: зробити можливість користувачам внутрішньої мережі створювати з'єднання із зовнішніми хостами без використання socks5/http proxy тощо.

У ядрі нам знадобляться всього 2 рядки: options IPFILTER options IPFILTER_LOG

Після цього IPFilter доступний нам у всій своїй красі.

Також в /etc/rc.conf потрібно додати рядок gateway_enable="yes" або /etc/sysctl.conf додати рядок net.inet.ip.forwarding=1.

Простий stateful NAT

Почнемо із простого stateful NAT тобто. IP пакети із зовнішнього інтерфейсу для одержувача у внутрішній мережі транслюватимуться лише в тому випадку, якщо користувач із внутрішньої мережі сам встановив це з'єднання. Всі інші пакети, які прийшли на зовнішній інтерфейс для одержувача у внутрішній мережі, будемо блокувати (див. redirect далі, якщо якісь пакети ззовні потрібно завжди прокидатися всередину).

Для з'єднань, встановлених користувачем, ми будемо створювати запис у state table за допомогою простого правила IPFilter:>pass out quick on fxp0 proto udp from 192.168.1.149 to any keep state (для UDP-з'єднань). Можна замість двох правил зробити одне спільне як TCP, так UDP: pass out quick on fxp0 proto tcp/udp from 192.168.1.149 to any keep state , однак у такому правилі ми не зможемо відстежувати tcp-пакети з прапором SYN за допомогою flags, в результаті чого будь-який пакет із внутрішньої мережі буде створювати запис у state table (якщо її не було), а не ті пакети, які є першими в установці TCP-з'єднання (з прапором SYN). Що зручніше/краще/більше підходить вирішуйте самі.

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

Тепер сама трансляція.Занесемо у файл трансляцій (за замовчуванням це /etc/ipnat.rules) правило: map fxp0 192.168.1.149/32 -> 200.200.200.1/32 portmap tcp/udp 20000:20099

Все, що залишилося - застосувати правила. 😉

Для зручності відстеження роботи NAT можна додати рядок в syslog.conf: local0.* /var/log/ipmon.log і запустити утиліту моніторингу роботи IPFilter - ipmon з ключами -Dvas (стати демоном, показувати більш детальну інформацію, відстежувати всі утрої IPFilter ( NAT, state table і сам IPFilter) і працювати через syslog). Якщо log-файл, що отримується, здається сумбурним - не поспішайте включати log тільки NAT опцією -o N. Правила IPFilter що потрапляють в лог можна туди направляти з іншої facility. Для цього достатньо в кожне правило з прапором log додати facilty: block in log level .block.log. В результаті цього дії в /var/log/ipmon.log потраплятиме інформація про NAT (створені трансляції, закриті трансляції) і state table (створення запису та її видалення), а файл /var/log/ipfilter.block.log пакети , які були заблоковані.

Якщо не бажаєте, щоб у великій кількості правил була вказана facility і правила були коротшими - можна відкинути можливість роботи ipmon через syslog і змусити його писати прямо у файл; наприклад так: ipmon -Dv -o NS /var/log/NAT.log (трансляції та state table протоколуємо у файл /var/log/NAT.log) та ipmon -Dv -o I /var/log/ipfilter.log ( роботу самого фільтра протокольуємо до файлу /var/log/ipfilter.log).

Варто зауважити, що одним правилом можна транслювати кілька хостів. Наприклад замінивши в правилах ipnat та IPFilter 192.168.1.149/32 на 192.168.128/25 ми транслюватимемо пакети хостів звнутрішньої мережі з діапазону від 192.168.1.128 до 192.168.1.254.

Перенаправлення (редирект)

Якщо є необхідність безумовно транслювати якісь пакети, що прийшли ззовні (наприклад, щоб WWW/SMTP сервер із внутрішньої мережі) був доступний зовні – використовується редирект (redirect).

Простий варіант: транслювати пакети від будь-якого хоста.

rdr fxp0 200.200.200.1/32 port 8080 -> 192.168.1.17 port 80 tcp

В даному випадку будь-який пакет, що прийшов на порт 8080 зовнішнього інтерфейсу 200.200.200.1 буде «прокинутий» на 192.168.1.18 порт 80. Природно, що хост 192.168.1.17 краще помістити в таблицю трансляцій як було описано в того, що ініціюватися з'єднання буде зовні - додати правила IPFilter, які дозволяли б їх. У випадку, якщо ipmon протоколює роботу IPFilter, помилку можна буде легко побачити.

Редирект можна робити не всіх пакетів, а вибірково на підставі їх відправника: rdr fxp0 from 212.118.165.100/32 to 200.200.200.1/32 port = 6000 -> 192.168.1.89 port 6000

Цим правилом транслюватимуться пакети, що прийшли на 200.200.200.1 порт 6000лише з хоста 212.118.165.100

Не слід забувати створювати правила IPFilter, що дозволяють проходження таких пакетів. Хоча, якщо ipmon налаштований і запущений, він швидко про це нагадає. 😉

proxy-правило маєобов'язково стояти перед іншими правилами (за винятком redirect).

Безумовна повна трансляція

Контроль над трансляцією

Безумовно безглуздо налаштовувати NAT не дивлячись у протоколи, що генеруються ipmon'ом. Також буде корисним обмежувати здатність встановлювати з'єднання зсередини до привілейованих портів зовнішніх хостів. Все це можнаі потрібно налаштувати за бажанням.

Хорошим помічником може бути старий добрий root-tail (/usr/ports/sysutils/roottail).