Налаштування простого firewall на ipfw і NAT через natd у FreeBSD
Записуємо в rc.conf параметри нашої домашньої мережі та локальної мережі. vr0 - зовнішній (публічний) інтерфейс, vr2 - внутрішній інтерфейс підключений до домашньої мережі.
ifconfig_vr0 = "inet 123.123.123.123 netmask 255.255.255.0"
ifconfig_vr0_alias0 = "link 00: aa: aa: aa: aa: aa"
ifconfig_vr2 = "inet 192.168.12.254 netmask 255.255.255.0"
defaultrouter = "123.123.123.1"
Дописуємо в наш конфиг ядра наступні рядки:
options IPFIREWALL
options IPFIREWALL_VERBOSE
options IPFIREWALL_VERBOSE_LIMIT = 100
options IPFIREWALL_FORWARD
options IPDIVERT
options DUMMYNET
Опис даних опцій:
options IPFIREWALL_VERBOSE - Підтримка запису проходять пакетів в лог-файл, при використанні опції log в правилах і активації log-а для пакетів в rc.conf або через sysctl змінну.
options IPFIREWALL_VERBOSE_LIMIT = 100 - Вказує кількість пакетів які будуть записані в log для кожного правила. Значення за замовчуванням. Значення за замовчуванням може бути перевизначити за допомогою змінної sysctl - net.inet.ip.fw.verbose_limit. Для конкретного правила ipfw може бути перевизначити за допомогою ipfw log logamount на інше бажане значення. наприклад:
ipfw add deny log logamount 300 ip from any to any
options IPFIREWALL_FORWARD - Підтримка перенаправлення пакетів між мережевими інтерфейсами.
options IPDIVERT - Підтримка NAT і natd зокрема.
options DUMMYNET - Підтримка обмежувача швидкості.
Крім того є параметр
options IPFIREWALL_DEFAULT_TO_ACCEPT
Якщо ядро зібрати з цією опцією, і включити firewall - правило за замовчуванням буде:
allow ip from any to any
Інакше ж правило за замовчуванням буде:
deny ip from any to any
Якщо сервер налаштовується через ssh - то можна для підстраховки включити цю опцію в ядрі, зібрати ядро, встановити, потім після закінчення настройки ipfw і отримання робочої конфігурації - вимкнути цю опцію, і пересобрать ядро знову вже без неї (для безпеки).
отже - збираємо ядро:
cd / usr / src
make buildkernel KERNCONF = MY_KERNEL
make installkernel KERNCONF = MY_KERNEL
про збірку ядра - читаємо відповідний розділ handbook-а.
Включаємо підтримку firewall в системі і пишемо простенький скрипт заглушку
Для того що б ipfw і natd заробили потрібно внести зміни в rc.conf, дописавши приблизно такі рядки:
firewall_enable = "YES"
firewall_script = "/ etc / ipfw.rules"
firewall_logging = "YES"
gateway_enable = "YES"
natd_enable = "YES"
natd_interface = "vr0"
natd_flags = "- m"
firewall_enable = «YES» - включити власне firewall. Якщо firewall є в ядрі - він включається, в іншому випадку подгружается модуль.
Обережно! При завантаженні модулем, правило за замовчуванням deny ip from any to any, так що якщо ви включили options IPFIREWALL_DEFAULT_TO_ACCEPT в ядрі, забули встановити нове ядро, і включили firewall - в надії на правило allow ip from any to any - вас може спіткати розчарування
firewall_script = "/ etc / ipfw.rules» - Звичайний shell скрипт який запуститься при старті firewall - по ідеї там повинні знаходитися команди ipfw які опишуть наш firewall. Ця точки може й не бути - тоді запуститися скрипт за замовчуванням - /etc/rc.firewall.
firewall_logging = «YES» - Включити запис проходять пакетів в log. За умови що в правилі для зазначено ключове слово log.
gateway_enable = «YES» - форвардного пакетів між інтерфейсами. Присутність цієї опції в rc.conf встановлює значення sysctl змінної net.inet.ip.forwarding в 1 при запуску комп'ютера.
natd_enable = «YES» - Включення NAT демона - natd.
natd_interface = «vr0» - Зовнішній інтерфейс для NAT. Публічний інтерфейс.
natd_flags = »- m» - Прапори які передаються natd. Детальніше дивіться в man natd.
Після внесення опція в rc.conf - пишемо скрипт заглушку для нашого firewall-а в файлі /etc/ipfw.rules. Цей firewall дозволятиме доступ звідусіль.
#! / Bin / sh
# Ipfw cmd
fwcmd = "/ sbin / ipfw"
$ -q -f flush
$ Add allow ip from any to any
Після чого перезавантажуємося.
reboot
Якщо все зробили правильно - то після перезавантаження - ми
зможемо знову потрапити на свою машину по ssh.
побачимо в виведенні ipfw show - наші правила (поки всього одне правило і правило за замовчуванням)
Пишемо простий firewall
У ipfw правила виконуються наступним чином: Що проходить пакет перевіряється на відповідність правилам, в тому порядку в якому вони розташовані, до тих пір поки зустрінеться підходяще для нього - після чого над пакетом виконується дія описане в правилі.
Правила ipfw - нумеруються і першим перевіряється правило з меншим номером, і т.д. Після того як пакет потрапив під яке-небудь правило - він вивалюється з firewall-а.
Виняток - пакети які потрапили під divert (і наприклад в NAT через natd), після дій над ними в divert вони повертаються в firewall, в наступне після divert правило, і йдуть далі за списком правил, до тих пір поки зустрінуть відповідне правило.
Це поведінку можна змінити, але в даній статті подробиці про це опущу. Синтаксис ipfw - опущу, щоб уникати зайвого повторення і захаращення тексту статті. Дивіться man ipfw, відповідний розділ handbook-а, і посилання в кінці статті.
Мені сподобався наступний метод налагодження firewall:
створюємо файл для тестування firewall, наприклад /etc/ipfw.rules.test.
робимо його виконуваним. Вносимо в нього команди для формування нашого firewall.
якщо результат нас задовольняє - то вносимо вміст тестового firewall-а в скрипт який буде виконуватися при завантаженні машини. В іншому випадку - правимо скрипт і повторюємо з пункту 3.
Якщо ми створили в файлі /etc/ipfw.rules.test firewall який заблокував нам віддалений доступ до машини - машину потрібно перезавантажити, після цього завантажиться firewall з /etc/ipfw.rules який напевно працює.
Корисні команди ipfw:
ipfw list - показує список правил
ipfw show - теж показує список правил, але разом з числом пакетів і байт, що потрапили під ці правила. Дуже зручно для налагодження.
ipfw -d show - показує динамічні правила (keep state).
ipfw zero - обнуління всіх лічильників. Якщо вести у вигляді ipfw zero # _правіла то буде обнулений тільки лічильник правила з цим номером.
Має сенс дотримуватися таку послідовність правил:
Чому дозволяють правила потрібно писати за правилами для NAT? Тому що якщо ви напишіть їх перед NAT, то як тільки таке правило зустрітися, воно буде виконане і трафік піде повз NAT, тому що NAT далі за списком. Машинам за NAT це дозволяють правило буде марно, тобто перестане для них працювати. Якщо ж дійсно потрібно обмежити машини за NAT в якому аспекті - краще робити це явно, ніж таким заплутаним чином.
Після налагодження у мене вийшов, такий ось простий firewall:
#! / Bin / sh
# Ipfw cmd
fwcmd = "/ sbin / ipfw"
# Internal Interface
iif = "vr2"
# External Interface
oif = "vr0"
# Internal IP address
iip = "192.168.78.254"
# External IP address
oip = "178.165.65.79"
# Internal LAN
ilan = "192.168.78.0/24"
# Check dynamic rules
$ Add check-state
# Allow localhost internal activity
$ Add allow ip from any to any via lo0
# Don # 't allow localhost to send packets outside
$ Add deny ip from any to 127.0.0.0/8
$ Add deny ip from 127.0.0.0/8 to any
# Deny fragmented icmp packets
$ Add deny icmp from any to any frag
# Deny connections to internal network from external network.
$ Add deny ip from any to $ in via $
# Deny broadcast icmp on external interface
$ Add deny log icmp from any to 255.255.255.255 in via $
$ Add deny log icmp from any to 255.255.255.255 out via $
# Do NAT for internal network
$ Add divert natd ip from $ to any out via $
$ Add divert natd ip from any to $ in via $
# Allow established connects
$ Add allow tcp from any to any established
# Allow outgoing traffic
$ Add allow ip from $ to any out xmit $
# Allow multicast
$ Add allow igmp from any to 224.0.0.0/8 in via $
$ Add allow igmp from any to 239.0.0.0/8 in via $
$ Add allow udp from any to 239.0.0.0/8 in via $
# Allow DNS
$ Add allow udp from any 53 to any via $
# Allow NTP
$ Add allow udp from any to any 123 via $
# Allow SSH connection to server
$ Add allow tcp from any to me 22 in via $
# Allow HTTP connection to the server
$ Add allow tcp from any to me 80 in via $
# Allow FTP connection to the server
$ Add allow tcp from any to me 20,21,64000-64999 via $
# Allow icmp echo request, echo reply and expire packet TTL
$ Add allow icmp from any to any icmptypes 0,8,11
# Allow connects from localnetwork
$ Add allow ip from any to any via $
# Deny other activity
$ Add deny log logamount 10000 ip from any to any
Так само, можливо, будуть цікаві такі параметри sysctl що відносяться до firewall:
# Sysctl -a | grep ip.fw
net.inet.ip.fw.static_count: 17
net.inet.ip.fw.default_to_accept: 0
net.inet.ip.fw.tables_max: 128
net.inet.ip.fw.default_rule: 65535
net.inet.ip.fw.verbose_limit: 100
net.inet.ip.fw.verbose: 1
net.inet.ip.fw.autoinc_step: 100
net.inet.ip.fw.one_pass: 1
net.inet.ip.fw.dyn_keepalive: 1
net.inet.ip.fw.dyn_short_lifetime: 5
net.inet.ip.fw.dyn_udp_lifetime: 10
net.inet.ip.fw.dyn_rst_lifetime: 1
net.inet.ip.fw.dyn_fin_lifetime: 1
net.inet.ip.fw.dyn_syn_lifetime: 20
net.inet.ip.fw.dyn_ack_lifetime: 300
net.inet.ip.fw.dyn_max: 4096
net.inet.ip.fw.dyn_count: 0
net.inet.ip.fw.curr_dyn_buckets: 256
net.inet.ip.fw.dyn_buckets: 256
net.inet.ip.fw.enable: 1
правила check-state і tcp from any to any established на початку firewall-а можуть підняти продуктивність, тому що перевірятимуть динамічні правила і встановлені з'єднання на самому початку, в результаті чого кількість перевірок зменшиться.