Печать
Категория: Linux
Просмотров: 3710

оригинал тут http://www.k-max.name/linux/iptables-v-primerax/

Настройка netfilter/iptables для рабочей станции

 

Давайте начнем с элементарной задачи - реализация сетевого экрана Linux на десктопе. В большинстве случаев на десктопных дистрибутивах линукса нет острой необходимости использовать файервол, т.к. на таких дистрибутивах не запущены какие-либо сервисы, слушающие сетевые порты, но ради профилактики организовать защиту не будет лишним. Ибо ядро тоже не застраховано от дыр. Итак, мы имеем Linux, с настроенным сетевым интерфейсом eth0, не важно по DHCP или статически...

 

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

 

netfilter:~# iptables -L

Chain INPUT (policy ACCEPT)

target prot opt source destination

 

Chain FORWARD (policy ACCEPT)

target prot opt source destination

 

Chain OUTPUT (policy ACCEPT)

target prot opt source destination

 

Это значит, что политика по умолчанию для таблицы filter во всех цепочках - ACCEPT и нет никаких других правил, что-либо запрещающих. Поэтому давайте сначала запретим ВСЁ входящие, исходящие и проходящие пакеты (не вздумайте это делать удаленно-тут же потеряете доступ):

 

netfilter:~# iptables -P INPUT DROP

netfilter:~# iptables -P OUTPUT DROP

netfilter:~# iptables -P FORWARD DROP

 

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

 

netfilter:~# ping -c2 127.0.0.1

PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.

ping: sendmsg: Operation not permitted

ping: sendmsg: Operation not permitted

 

--- localhost ping statistics ---

2 packets transmitted, 0 received, 100% packet loss, time 1004ms

 

На самом деле это полностью не функционирующая сеть и это не очень хорошо, т.к. некоторые демоны используют для обмена между собой петлевой интерфейс, который после проделанных действий более не функционирует. Это может нарушить работу подобных сервисов. Поэтому в первую очередь в обязательном порядке разрешим передачу пакетов через входящий петлевой интерфейс и исходящий петлевой интерфейс в таблицах INPUT (для возможности получения отправленных пакетов) и OUTPUT (для возможности отправки пакетов) соответственно, в обязательном порядке:

 

netfilter:~# iptables -A INPUT -i lo -j ACCEPT 

netfilter:~# iptables -A OUTPUT -o lo -j ACCEPT

 

После этого пинг на локалхост заработает:

 

netfilter:~# ping -c1 127.0.0.1

PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.

64 bytes from 127.0.0.1 (127.0.0.1): icmp_seq=1 ttl=64 time=0.116 ms

 

--- 127.0.0.1 ping statistics ---

1 packets transmitted, 1 received, 0% packet loss, time 116ms

rtt min/avg/max/mdev = 0.116/0.116/0.116/0.116 ms

 

Если подходить к настройке файервола не шибко фанатично, то можно разрешить работу протокола ICMP:

 

netfilter:~# iptables -A INPUT -p icmp -j ACCEPT

netfilter:~# iptables -A OUTPUT -p icmp -j ACCEPT

 

Более безопасно будет указать следующую аналогичную команду iptables:

 

netfilter:~# iptables -A INPUT -p icmp --icmp-type 0 -j ACCEPT

netfilter:~# iptables -A INPUT -p icmp --icmp-type 8 -j ACCEPT

netfilter:~# iptables -A OUTPUT -p icmp -j ACCEPT

 

Данная команда разрешит типы ICMP пакета эхо-запрос и эхо-ответ, что повысит безопасность.

 

Зная, что наш комп не заражен (ведь это так?) и он устанавливает только безопасные исходящие соединения. А так же, зная, что безопасные соединения - это соединения из т.н. эфимерного диапазона портов, который задается ядром в файле /proc/sys/net/ipv4/ip_local_port_range, можно разрешить исходящие соединения с этих безопасных портов:

 

netfilter:~# cat /proc/sys/net/ipv4/ip_local_port_range

32768 61000

netfilter:~# iptables -A OUTPUT -p TCP --sport 32768:61000 -j ACCEPT

netfilter:~# iptables -A OUTPUT -p UDP --sport 32768:61000 -j ACCEPT

 

Если подходить к ограничению исходящих пакетов не параноидально, то можно было ограничиться одной командой iptables, разрешающей все исхолящие соединения оп всем протоколам и портам:

 

netfilter:~# iptables -A OUTPUT -j ACCEPT

netfilter:~# # или просто задать политику по умолчанию ACCEPT для цепочки OUTPUT

netfilter:~# iptables -P OUTPUT ACCEPT

 

Далее, зная что в netfilter сетевые соединения имеют 4 состояния (NEW, ESTABLISHED, RELATED и INVALID) и новые исходящие соединения с локального компьютера (с состоянием NEW) у нас разрешены в прошлых двух командах iptables, что уже установленные соединения и дополнительные имеют состояния ESTABLISHED и RELATED, соответственно, а так же зная, что входящие соединения к локальной системе приходят через цепочку INPUT, можно разрешить попадание на наш компьютер только тех TCP- и UDP-пакетов, которые были запрошены локальными приложениями:

 

netfilter:~# iptables -A INPUT -p TCP -m state --state ESTABLISHED,RELATED -j ACCEPT

netfilter:~# iptables -A INPUT -p UDP -m state --state ESTABLISHED,RELATED -j ACCEPT

 

Это собственно, все! Если на десктопе все же работает какая-то сетевая служба, то необходимо добавить соответствующие правила для входящих соединений и для исходящих. Например, для работы ssh-сервера, который принимает и отправляет запросы на 22 TCP-порту, необходимо добавить следующие iptables-правила:

 

netfilter:~# iptables -A INPUT -i eth0 -p TCP --dport 22 -j ACCEPT 

netfilter:~# iptables -A OUTPUT -o eth0 -p TCP --sport 22 -j ACCEPT

 

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

 

Настройка netfilter/iptables для подключения нескольких клиентов к одному соединению.

 

Давайте теперь рассмотрим наш Linux в качестве шлюза для локальной сети во внешнюю сеть Internet. Предположим, что интерфейс eth0 подключен к интернету и имеет IP 198.166.0.200, а интерфейс eth1 подключен к локальной сети и имеет IP 10.0.0.1. По умолчанию, в ядре Linux пересылка пакетов через цепочку FORWARD (пакетов, не предназначенных локальной системе) отключена. Чтобы включить данную функцию, необходимо задать значение 1 в файле /proc/sys/net/ipv4/ip_forward:

 

netfilter:~# echo 1 > /proc/sys/net/ipv4/ip_forward

 

Чтобы форвардинг пакетов сохранился после перезагрузки, необходимо в файле /etc/sysctl.conf раскомментировать (или просто добавить) строку net.ipv4.ip_forward=1.

 

Итак, у нас есть внешний адрес (198.166.0.200), в локальной сети имеется некоторое количество гипотетических клиентов, которые имеют адреса из диапазона локальной сети и посылают запросы во внешнюю сеть. Если эти клиенты будут отправлять во внешнюю сеть запросы через шлюз "как есть", без преобразования, то удаленный сервер не сможет на них ответить, т.к. обратным адресом будет получатель из "локальной сети". Для того, чтобы эта схема корректно работала, необходимо подменять адрес отправителя, на внешний адрес шлюза Linux. Это достигается за счет действия MASQUERADE (маскарадинг) в цепочке POSTROUTING, в таблице nat.

 

netfilter:~# iptables -A FORWARD -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT

netfilter:~# iptables -A FORWARD -m conntrack --ctstate NEW -i eth1 -s 10.0.0.1/24 -j ACCEPT

netfilter:~# iptables -P FORWARD DROP

netfilter:~# iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE

 

Итак, по порядку сверху-вниз мы разрешаем уже установленные соединения в цепочке FORWARD, таблице filter, далее мы разрешаем устанавливать новые соединения в цепочке FORWARD, таблице filter, которые пришли с интерфейса eth1 и из сети 10.0.0.1/24. Все остальные пакеты, которые проходят через цепочку FORWARD - отбрасывать. Далее, выполняем маскирование (подмену адреса отправителя пакета в заголовках) всех пакетов, исходящих с интерфейса eth0.

 

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

 

netfilter:~# iptables -P INPUT DROP

netfilter:~# iptables -P OUTPUT DROP

netfilter:~# iptables -A INPUT -i lo -j ACCEPT 

netfilter:~# iptables -A OUTPUT -o lo -j ACCEPT

netfilter:~# iptables -A INPUT -p icmp --icmp-type 0 -j ACCEPT

netfilter:~# iptables -A INPUT -p icmp --icmp-type 8 -j ACCEPT

netfilter:~# iptables -A OUTPUT -p icmp -j ACCEPT

netfilter:~# cat /proc/sys/net/ipv4/ip_local_port_range

32768 61000

netfilter:~# iptables -A OUTPUT -p TCP --sport 32768:61000 -j ACCEPT

netfilter:~# iptables -A OUTPUT -p UDP --sport 32768:61000 -j ACCEPT

netfilter:~# iptables -A INPUT -p TCP -m state --state ESTABLISHED,RELATED -j ACCEPT

netfilter:~# iptables -A INPUT -p UDP -m state --state ESTABLISHED,RELATED -j ACCEPT

 

В результате, если один из хостов локальной сети, например 10.0.0.2, попытается связаться с одним из интернет-хостов, например, 93.158.134.3 (ya.ru), при проходе его пакетов через шлюз, их исходный адрес будет подменяться на внешний адрес шлюза в цепочке POSTROUTING таблице nat, то есть исходящий IP  10.0.0.2 будет заменен на 198.166.0.200. С точки зрения удаленного хоста (ya.ru), это будет выглядеть, как будто с ним связывается непосредственно сам шлюз. Когда же удаленный хост начнет ответную передачу данных, он будет адресовать их именно шлюзу, то есть 198.166.0.200. Однако, на шлюзе адрес назначения этих пакетов будет подменяться на 10.0.0.2, после чего пакеты будут передаваться настоящему получателю в локальной сети. Для такого обратного преобразования никаких дополнительных правил указывать не нужно — это будет делать все та же операция MASQUERADE, которая помнит какой хост из локальной сети отправил запрос и какому хосту необходимо вернуть пришедший ответ.

 

Примечание: желательно негласно принято, перед всеми командами iptables очищать цепочки, в которые будут добавляться правила:

 

netfilter:~# iptables -F ИМЯ_ЦЕПОЧКИ

 

Предоставление доступа к сервисам на шлюзе

 

Предположим, что на нашем шлюзе запущен некий сервис, который должен отвечать на запросы поступающие из сети интернет. Допустим он работает на некотором TCP порту nn. Чтобы предоставить доступ к данной службе, необходимо модифицировать таблицу filter в цепочке INPUT (для возможности получения сетевых пакетов, адресованных локальному сервису) и таблицу filter в цепочке OUTPUT (для разрешения ответов на пришедшие запросы).

 

Итак, мы имеем настроенный шлюз, который маскарадит (заменяет адрес отправителя на врешний) пакеты во внешнюю сеть. И разрешает принимать все установленные соединения. Предоставление доступа к сервису будет осуществляться с помощью следующих разрешающих правил:

 

netfilter:~# iptables -A INPUT -p TCP --dport nn -j ACCEPT 

netfilter:~# iptables -A OUTPUT -p TCP --sport nn -j ACCEPT

 

Данные правила разрешают входящие соединения по протоколу tcp на порт nn и исходящие соединения по протоколу tcp с порта nn. Кроме этого, можно добавить дополнительные ограничивающие параметры, например разрешить входящие соединения только с внешнего интерфейса eth0 (ключ -i eth0) и т.п.

 

Предоставление доступа к сервисам в локальной сети

 

Предположим, что в нашей локальной сети имеется какой-то хост с IP X.Y.Z.1, который должен отвечать на сетевые запросы из внешней сети на TCP-порту xxx. Для того чтобы при обращении удаленного клиента ко внешнему IP на порт xxx происходил корректный ответ сервиса из локальной сети, необходимо направить запросы, приходящие на внешний IP порт xxx на соответствующий хост в локальной сети. Это достигается модификацией адреса получателя в пакете, приходящем на указанный порт. Это действие называется DNAT и применяется в цепочке PREROUTING в таблице nat. А так же разрешить прохождение данный пакетов в цепочке FORWARD в таблице filter.

 

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

 

netfilter:~# iptables -t nat -A PREROUTING -p tcp -d 198.166.0.200 --dport xxx -j DNAT --to-destination X.Y.Z.1

netfilter:~# iptables -A FORWARD -i eth0 -p tcp -d X.Y.Z.1 --dport xxx -j ACCEPT

 

Сохранение введенных правил при перезагрузке

 

Все введенные в консоли правила - после перезагрузки ОС будут сброшены в первоначальное состояние (читай - удалены). Для того чтобы сохранить все введенные команды iptables, существует несколько путей. Например, один из них - задать все правила брандмауэра в файле инициализации rc.local. Но у данного способа есть существенный недостаток: весь промежуток времени с запуска сетевой подсистемы, до запуска последней службы и далее скрипта rc.local из SystemV операционная система будет не защищена. Представьте ситуацию, например, если какая-нибудь служба (например NFS) стартует последней и при ее запуске произойдет какой-либо сбой и до запуска скрипта rc.local. Соответственно, rc.local так и не запуститься, а наша система превращается в одну большую дыру.

 

Поэтому самой лучшей идеей будет инициализировать правила netfilter/iptables при загрузке сетевой подсистемы. Для этого в Debian есть отличный инструмент - каталог /etc/network/if-up.d/, в  который можно поместить скрипты, которые будут запускаться при старте сети. А так же есть команды iptables-save и iptables-restore, которые сохраняют создают дамп правил netfilter из ядра на стандартный вывод и восстанавливают в ядро правила со стандартного ввода соответственно.

 

Итак, алгоритм сохранения iptables примерно следующий:

 

 

# cat /etc/network/if-up.d/firewall

#!/bin/bash

/sbin/iptables-restore < /etc/iptables.rules

exit 0

# chmod +x /etc/network/if-pre-up.d/firewall

 

Дамп правил, полученный командой iptables-save имеет текстовый формат, соответственно пригоден для редактирования. Синтаксис вывода команды iptables-save следующий:

 

# Generated by iptables-save v1.4.5 on Sat Dec 24 22:35:13 2011

*filter

:INPUT ACCEPT [0:0] 

:FORWARD ACCEPT [0:0]

.......

# комментарий

-A INPUT -i lo -j ACCEPT

-A INPUT ! -i lo -d 127.0.0.0/8 -j REJECT

...........

-A FORWARD -j REJECT

COMMIT

# Completed on Sat Dec 24 22:35:13 2011

# Generated by iptables-save v1.4.5 on Sat Dec 24 22:35:13 2011

*raw

......

COMMIT

 

Строки, начинающиеся на # - комментарии, строки на * - это название таблиц, между названием таблицы и словом COMMIT содержатся параметры, передаваемые команде iptables. Параметр COMMIT - указывает на завершение параметров для вышеназванной таблицы. Строки, начинающиеся на двоеточие задают цепочки, в которых содержится данная таблица в формате:

 

:цепочка политика [пакеты:байты]

 

где цепочка - имя цепочки, политика - политика цепочки по-умолчанию для данной таблицы, а далее счетчики пакетов и байтов на момент выполнения команды.

 

В RedHat функции хранения команд iptables выполняемых при старте и останове сети выполняет файл /etc/sysconfig/iptables. А управление данным файлом лежит на демоне iptables.

 

Как еще один вариант сохранения правил, можно рассмотреть использование параметра up в файле /etc/network/interfaces с аргументом в виде файла, хранящего команды iptables, задающие необходимые правила.

 

Итог

 

На сегодня будет достаточно. Более сложные реализации межсетевого экрана я обязательно будут публиковаться в следующих статьях.