Подробная настройка iptables

Материал из Ай да Linux Wiki
Перейти к навигации Перейти к поиску

Введение

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

Так же примите во внимание, что будет использовано pppoe соединение и 2.6.x ядро. Для настройки сетевой карты надо будет заменить ppp0 на eth0 (или подходящий по смыслу ваш сетевой интерфейс глядящий в интернет)

Прежде чем двигаться дальше - убедитесь, что ...

  • 1.Всё ваше аппаратное обеспечение работоспособно. То есть вы все подключили, модули грузятся, устройства видны в системе. Полезно вначале проверить, что соединение с интернетом возможно и без всяких там iptables. Нет ничего хуже, чем в течение многих часов возиться с упрямой программой, ругая её почём зря, а потом понять, что сетевая плата не работает или модем сконфигурирован неправильно.
  • 2.Вы читали маны по теме. Предполагается, что, пока вы изучаете это руководство, man iptables постоянно открыт в соседнем терминале для точного понимания и уточнения, что же та или иная команда означает на самом деле.
  • 3.Вы имеете представление о сетевых технологиях и Вы знакомы с администрированием Linux и Gentoo Linux в частности. То есть необходимы навыки работы с такими базовыми вещами как ifconfig, rc-update, /etc/conf.d/net, и так далее. Если для вас это пока пустые звуки, то, пожалуйста предварительно изучите The Gentoo Handbook и Linux Help's Networking Basics 101

Конфигурация ядра

Все что вам нужно - это включить поддержку iptables.

Linux Kernel Configuration: Включение IPTables
Device Drivers--->
 Networking Support--->
  Networking Options---->
   Network Packet Filtering (replace Ipchains)--->
    Netfilter Configuration


Linux Kernel Configuration: Включение IPTables (kernel 2.6.35)
-> Networking support
  -> Networking options 
    -> Network packet filtering framework (Netfilter)
      -> Core Netfilter Configuration


Я включил все опции как модули (с тем рассчетом, что я захочу попробовать другие опции позже) и добавил ip_tables в modules.autoload. Это загрузит еще несколько модулей в качестве зависимостей. Модуль ip_conntrack необходим для "statefull" фильтрования, то есть для отслеживания соединений. Для запуска скриптов выполните команду :

# modprobe ip_tables

Необходимые утилиты

Далее вы должны установить пакет iptables:

# emerge iptables

Конфигурация интерфейсов

Сетевые протоколы
Прикладной уровень HTTP, SMTP, SSH, SNMP, FTP, NNTP, NTP, LDAP, ...
Сеансовый уровень TLS, SSL, RPC, WSP...
Транспортный уровень TCP, UDP, SCTP, ICMP, OSPF, RSVP, VRRP, RTP, DCCP ...
Сетевой уровень IPv4, IPv6, ARP, RARP, MPLS, IPX ...
Канальный уровень Ethernet, 802.11, xDSL, Fibre Channel, FDDI, ATM, ISDN ...

В моем случае имеется 3 сетевых адаптера. Один подключен к WAN через pppoe. Другие два - к моей внутренней сети. Для того, чтобы не было проблем с iptables и маскардингом (NAT'ом), они должны быть сконфигурированы для различных подсетей. Для примера, 2 сетевых адаптера подключены к моим внутренним компьютерам (внутренние сетевые интерфейсы). Им присвоены IP-адреса: 192.168.1.1 и 192.168.2.1.

Следует заметить, что будет лучше если подключать эти внутренние адаптеры в любое сетевое устройство, такие как свитч и хаб. Для pppoe подключений мы должны убедиться, что сетевой адаптер подключен к внешнему миру, то есть внешним интерфейсам не присвоены никакие IP-адреса. Его запись в /etc/conf.d/net должна оставаться пустой. Это делается потому, что pppoe выступает в качестве виртуального устройства, которое включается вслед за сетевым интерфейсом. Мы также должны присвоить правильные сетевые маски и широковещательные адреса для этих интерфейсов. Ваш conf.d/ должна выглядеть примерно так:

Сервер

Файл: /etc/conf.d/net
 
 # Для pppoe подключений вы не должны указывать значения для ppp0, 
 # просто добавьте net.ppp0 или rc-pppoe в default уровень загрузки.
 iface_eth1="192.168.1.1 broadcast 192.168.1.255 netmask 255.255.255.0"
 iface_eth2="192.168.2.1 broadcast 192.168.2.255 netmask 255.255.255.0"
Заметьте, что не было указано никаких шлюзов.

Клиент1

Файл: /etc/conf.d/net
 
 iface_eth0="192.168.1.77 broadcast 192.168.1.255 netmask 255.255.255.0"
 gateway="eth0/192.168.1.1"

Клиент2

Файл: /etc/conf.d/net
 iface_eth0="192.168.2.77 broadcast 192.168.2.255 netmask 255.255.255.0"
 gateway="eth0/192.168.2.1"

Шлюз для клиентов установлен на внутренний IP сетевого интерфейса сервера, что и логично. Теперь добавьте все интерфейсы в default уровень загрузки и перезапустите подключения:

# rc-update add net.eth1 default && rc-update add net.eth2 default && rc-update add net.ppp0 default

и

# /etc/init.d/net.eth1 start && /etc/init.d/net.eth2 start && /etc/init.d/net.ppp0 start

  • Для клиентов:

# /etc/init.d/net.eth0 restart

Проверка настроек

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

Code: ping
ping www.google.com; 
ping 192.168.1.77
ping 192.168.2.77
Убедитесь что у клиентов правильно указаны DNS-сервера в /etc/resolv.conf

Scripting

Теперь интересная часть... iptables и NAT(трансляция адресов). Для начала сделаем простое перенаправление адресов с минимальными правилами, чтобы убедиться что можем выходить в сеть.
Предупреждение: Если вы параноик, то это не самое секретное, что можно сделать... мы открываемся в сеть с мизерной защитой. Однако будем считать, что сеть настраиваем для дома или для игрового класса.
Файл: /var/lib/iptables/rules-save
#!/bin/bash
IPTABLES='/sbin/iptables'

# Определяем интерфейсы
EXTIF='ppp0'
INTIF1='eth1'
INTIF2='eth2'

# Включаем форвардинг ip в ядре.
/bin/echo 1 > /proc/sys/net/ipv4/ip_forward

# Сбросить правила и удалить цепочки 
$IPTABLES -F
$IPTABLES -t nat -F
$IPTABLES -t mangle -F
$IPTABLES -X
$IPTABLES -t nat -X
$IPTABLES -t mangle -X

# Включаем маскарадинг для разрешения доступа в интернет
$IPTABLES -t nat -A POSTROUTING -o $EXTIF -j MASQUERADE

# Форвардить сетевой трафик с $INTIF1 на интернетовский интерфейс $EXTIF
$IPTABLES -A FORWARD -i $INTIF1 -o $EXTIF -m state --state NEW,ESTABLISHED -j ACCEPT

# Форвардить сетевой трафик с $INTIF2 на интернетовский интерфейс $EXTIF
$IPTABLES -A FORWARD -i $INTIF2 -o $EXTIF -m state --state NEW,ESTABLISHED -j ACCEPT

#echo -e "       - Разрешаем доступ к SSH серверу"
$IPTABLES -A INPUT --protocol tcp --dport 22 -j ACCEPT
#echo -e "       - Разрешаем доступ к HTTP серверу"
$IPTABLES -A INPUT --protocol tcp --dport 80 -j ACCEPT
# Блокируем все прочие попытки доступа на $EXTIF
$IPTABLES -A INPUT -i $EXTIF -m state --state NEW,INVALID -j DROP
$IPTABLES -A FORWARD -i $EXTIF -m state --state NEW,INVALID -j DROP

Проверка на работоспособность

На заметку: Этот скрипт написан кем-то в сетевом форуме... Адрес автора утерян, имя... В общем, спасибо ему, пусть и без имени. (Надеюсь, он не обидится).
Теперь проверяем могут ли наши пользователи выйти в интернет или подключиться к серверу по ssh. Если все нормально, то можно переходить ниже по тексту. Если нет - проверьте синтаксические ошибки и прочее... Удостоверьтесь, что IP-адреса и маски клиентов и сервера введены правильно... Ну или...
Если все работает, как задумали, сохраняем конфигурацию:

# /etc/init.d/iptables save

И бэкапим вашу рабочую конфигурацию для возможного восстановления "как было":

# cp /var/lib/iptables/rules-save /var/lib/iptables/rules.working

Проверим iptables start-up скрипт перед тем как добавить iptables в default runlevel:
Code: Проверка скрипта
  1. /etc/init.d/iptables start
  2. /etc/init.d/iptables stop
  3. /etc/init.d/iptables start
Смысл в запуске-остановке-запуске в том, что у нас нет скрипта запуска iptables... поэтому нужно “инициализировать” статус перед остановкой. Остановка, по существу, обнуляет настройки и возвращает все к исходному. Перезапуск покажет нам работает ли наша сеть после перезагрузки. Если все в порядке, то добавляем iptables в default runlevel:

rc-update add iptables default

Не забудем также установить в /etc/sysctl.conf:

net.ipv4.ip_forward = 1

Часть II рассматривает правила установок и политики для обороны Вашего маршрутизатора.

ЧАСТЬ III - Оборона Firewall

Далее мы сделаем наш, уже работающий файрвол безопасным, т.е. защищающим нашу систему от проникновений извне. На самом деле нам придется настроить файрвол таким образом, чтобы он не только защищал нас, но и защищал внешнюю сеть от нас. :) Защита такого рода является обязательной, она нужна для того, чтобы, в том случае, если наша система все-таки была взломана, взломщик не смог воспользоваться нашими ресурсами для дальнейшей атаки любого рода. Этот аспект является важнейшим в сетях класса SOHO, т.е. небольших офисах. Обыкновенно вирусы не оказывают никакого влияния на малые сети и заражение ими редко приводит к потере данных. Для нас, пользователей *nix, этой проблемы практически не существует. В любом случае, т.к. небольшие сети, как правило, защищены гораздо хуже больших, кракеры стараются использовать их в качестве “опорной базы” для DoS атак, или другой своей вредоносной активности.
В последующем я опишу всю конфигурацию по кусочкам, дабы мы смогли бы проверить пошагово каждый фрагмент. Каждый шаг может потребовать от вас вставки чего-либо до, после или в середину указываемого скрипта. Все действия производятся так, чтобы (я надеюсь) ваша сеть не работала только лишь короткий промежуток времени во время настройки. Это сделано мною потому как я предположил что у многих из вас (таких как я) выделен сервер под Firewall. И так как я предпочитаю настраивать свой сервер через SSH, отключение сети может иметь пренеприятные посделствия, такие как ползания под столами или хуже того. Если же вы смелы, то вы можете скопировать скрипт в конце этого документа и запустить его на своей машине. Этот скрипт должен быть работоспособен на все сто, но тестировал я его только на своей машине, так что ymmv.


Установка переменных окружения

Вы можете установить необходимые переменные окружения следующим скриптом:
Code:
#!/bin/sh
#
# Внешний интерфейс
EXTIF="ppp0"

# Внутренний интерфейс
INTIF="eth1"

# Loop device/localhost
LPDIF="lo"
LPDIP="127.0.0.1"
LPDMSK="255.0.0.0"
LPDNET="$LPDIP/$LPDMSK"

# Необходимые утилиты
IPT="/sbin/iptables"
IFC="/sbin/ifconfig"
G="/bin/grep"
SED="/bin/sed"
AWK="/usr/bin/awk"
ECHO="/bin/echo"

# Последующие команды могут работать некорректно при локализации.

# Установка переменных окружения внешнего интерфейса
EXTIP="`$IFC $EXTIF | $AWK /$EXTIF/'{next}//{split($0,a,":");split(a[2],a," ");print a[1];exit}'`"
EXTBC="255.255.255.255" 
#EXTMSK="`$IFC $EXTIF | $G Mask:|$SED 's/.*Mask:\([^ ]*\)/\1/'`"
EXTMSK="`$IFC $EXTIF | $AWK /$EXTIF/'{next}//{split($0,a,":");split(a[4],a," ");print a[1];exit}'`"
EXTNET="$EXTIP/$EXTMSK"
$ECHO "EXTIP=$EXTIP EXTBC=$EXTBC EXTMSK=$EXTMSK EXTNET=$EXTNET"

# Due to absence of EXTBC I manually set it to 255.255.255.255
# this (hopefully) will serve the same purpose

# Установка переменных окружения внутреннего интерфейса
INTIP="`$IFC $INTIF | $AWK /$INTIF/'{next}//{split($0,a,":");split(a[2],a," ");print a[1];exit}'`"
INTBC="`$IFC $INTIF | $AWK /$INTIF/'{next}//{split($0,a,":");split(a[3],a," ");print a[1];exit}'`"
INTMSK="`$IFC $INTIF | $AWK /$INTIF/'{next}//{split($0,a,":");split(a[4],a," ");print a[1];exit}'`"
INTNET="$INTIP/$INTMSK"
$ECHO "INTIP=$INTIP INTBC=$INTBC INTMSK=$INTMSK INTNET=$INTNET"

iptables ACCEPTS

Теперь мы должны установить ACCEPTы, так, чтобы мы могли соединяться с нашим сервером. На самом деле это очень больной вопрос. Правила для надежного маршрутизатора должны по умолчанию запрещать нежели разрешать. Однако, если вы сделаете это, то потеряете все соединения. Продолжайте тестирование пока не будете уверены что ваши ACCEPTы работают как надо. Однако думаю, что сперва мы введем следующее и это будет предпоследним правилом в окончательном скрипте.
Code:
$IPT -t nat -A PREROUTING -j ACCEPT
# $IPT -t nat -A POSTROUTING -o $EXTIF -s $INTNET -j SNAT --to $EXTIP

# Закомментируйте последующие строки (которые содержат "MASQUERADE") 
# для сетей без трансляции адресов (NAT)
$IPT -t nat -A POSTROUTING -o $EXTIF -s $INTNET1 -j MASQUERADE
$IPT -t nat -A POSTROUTING -o $EXTIF -s $INTNET2 -j MASQUERADE
$IPT -t nat -A POSTROUTING                       -j ACCEPT
$IPT -t nat -A OUTPUT                            -j ACCEPT
$IPT -A INPUT   -p tcp --dport auth --syn -m state --state NEW -j ACCEPT
$IPT -A INPUT   -m state --state ESTABLISHED,RELATED -j ACCEPT
$IPT -A OUTPUT  -m state --state ESTABLISHED,RELATED -j ACCEPT
$IPT -A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT

iptables DROP & REJECT

Теперь мы определим пару цепочек (chains) которые будут фиксировать события DROP и REJECT. Таким образом нам не придется вводить отдельные строки для каждой введенной команды. Сообщения о событиях будут отправлены сервису syslog, (обычно они фиксируются в /var/log/messages). Позже я (переводчик не имеет к этому никакого отношения) собираюсь написать скрипт на sed/grep по разбору событий для облегченного просмотра и установлю его как ежедневную работу для сервиса cron.
Эти строки следует вставить сразу после текста выше, в тот же скрипт. Когда вы это сделаете, запустите скрипт снова. Это не окажет влияния на вашу сеть, вы пока просто устанавливаете правила. Но это поможет убедиться что мы не сделали ошибок на данном этапе.
Code:
# ********** Цепочки журналирования событий **********
#
# Теперь мы определяем несколько цепочек которые служат для записи 
# событий о сбрасываемых пакетах. Это позволит нам избежать ввода 
# команд для каждого правила. Сперва мы фиксируем DROP, а потом REJECT.
# Не жалуйтесь, если цепочки уже существуют (однако это не приведет к ошибкам???)
$IPT -N DROPl   2> /dev/null
$IPT -A DROPl -m limit --limit 3/minute --limit-burst 10 -j LOG --log-prefix 'FIREWALL DROP BLOCKED:'
$IPT -A DROPl   -j DROP
$IPT -N REJECTl 2> /dev/null
$IPT -A REJECTl -m limit --limit 3/minute --limit-burst 10 -j LOG --log-prefix 'FIREWALL REJECT BLOCKED:'
$IPT -A REJECTl -j REJECT
$IPT -N DROP2   2> /dev/null
$IPT -A DROP2 -m limit --limit 3/second --limit-burst 10 -j LOG --log-prefix 'FIREWALL DROP UNKNOWN:'
$IPT -A DROP2   -j DROP
$IPT -N REJECT2 2> /dev/null
$IPT -A REJECT2 -m limit --limit 3/second --limit-burst 10 -j LOG --log-prefix 'FIREWALL REJECT UNKNOWN:'
$IPT -A REJECT2 -j REJECT

# Для тестирования фиксируем события ACCEPT
$IPT -N ACCEPTl   2> /dev/null
$IPT -A ACCEPTl -m limit --limit 10/second --limit-burst 50 -j LOG --log-prefix 'FIREWALL ACCEPT:'
$IPT -A ACCEPTl   -j ACCEPT

Сброс правил

Теперь, когда мы видим наши устройства, правильно определенные, вставляем команду сброса правил. Однако все уже назначенные правила будут сброшены. Эти строки должны быть вставлены после определения утилит, которые заканчиваются строкой: ECHO='/bin/echo'
Code:
# Сброс всех существующих и очистка персональных цепочек.
CHAINS=`cat /proc/net/ip_tables_names 2>/dev/null`

for i in $CHAINS
do
    $IPT -t $i -F
done

for i in $CHAINS
do
    $IPT -t $i -X
done

Локальные интерфейсы

Теперь мы готовы для того, чтобы описать некоторые правила. Для начала мы разрешим все пакеты с loopback интерфейса, имеющие в качестве адреса назначения один из адресов наших интерфейсов.
Code:
$IPT -A INPUT   -i $LPDIF -s   $LPDIP  -j ACCEPT
$IPT -A INPUT   -i $LPDIF -s   $EXTIP  -j ACCEPT
$IPT -A INPUT   -i $LPDIF -s   $INTIP1  -j ACCEPT
$IPT -A INPUT   -i $LPDIF -s   $INTIP2  -j ACCEPT

Блокировка широковещательных пакетов

Теперь мы должны заблокировать все входящие и исходящие широковещательные пакеты. Это предотвратит DoS атаки против нас, и не позволит нашим клиентам проводить DoS атаки против кого либо другого. Если бы все системные администраторы следовали этим правилам, тогда много суровых и дорогих DoS атак не состоялись или были максимально ограниченными.
Code: Блокировка широковещательных пакетов
$IPT -A INPUT   -i $EXTIF -d   $EXTBC  -j DROPl
$IPT -A INPUT   -i $INTIF1 -d  $INTBC1  -j DROPl
$IPT -A INPUT   -i $INTIF2 -d  $INTBC2  -j DROPl
$IPT -A OUTPUT  -o $EXTIF -d   $EXTBC  -j DROPl
$IPT -A OUTPUT  -o $INTIF1 -d  $INTBC1  -j DROPl
$IPT -A OUTPUT  -o $INTIF2 -d  $INTBC2  -j DROPl
$IPT -A FORWARD -o $EXTIF -d   $EXTBC  -j DROPl
$IPT -A FORWARD -o $INTIF1 -d  $INTBC1  -j DROPl
$IPT -A FORWARD -o $INTIF2 -d  $INTBC2  -j DROPl
Теперь проверим скрипт еще раз, чтобы убедиться в том, что мы не наделали синтаксических ошибок. Также отмечу, что мы используем определенные нами DROP1 цепочки (chains). Это означает, что отбрасываемые пакеты будут отмечены в журнале событий (log file).

Блокировка доступа в локальную сеть из глобальной

Теперь мы блокируем доступ из глобальной сети в нашу локальную сеть, если мы не хотим что бы интернет провайдер назначал IP адреса для нашей внутренней сети.
Code:
# Блокировать внешний доступ к локальной сети
# Это позволит остановить боевых кракеров от использования 
# нашей сети как стартовой точки для других атак.
#
# Нижеприведенная строчка на человеческом языке будет выглядеть как
# "если входящий пакет, пришедший на наш внешний интерфейс,
# имеет адрес назначения, отличный от адреса нашего внешнего интерфейса,
# то этот пакет будет залогирован цепочкой DROPl, затем блокирован 
# (не будет пропущен)."
$IPT -A INPUT   -i $EXTIF -d ! $EXTIP  -j DROPl

Изолирование локальных сетей

Теперь мы предпримем некоторые действия для наших локальных сетей. Другими словами - все пакеты не относящиеся к локальным сетям должны быть блокированы.
Code:
# Теперь мы должны заблокировать все пакеты не относящиеся к 
# адресному пространству наших локальных сетей.
# Запомните, если вы подключите свой ноутбук к другому разъему, 
# вам надо убедиться, что ваш сетевой адрес соответствует адресам этой сети.
#
# Первая локальная сеть
$IPT -A INPUT   -i $INTIF1 -s ! $INTNET1 -j DROPl
$IPT -A OUTPUT  -o $INTIF1 -d ! $INTNET1 -j DROPl
$IPT -A FORWARD -i $INTIF1 -s ! $INTNET1 -j DROPl
$IPT -A FORWARD -o $INTIF1 -d ! $INTNET1 -j DROPl
# Вторая локальная сеть
$IPT -A INPUT   -i $INTIF2 -s ! $INTNET2 -j DROPl
$IPT -A OUTPUT  -o $INTIF2 -d ! $INTNET2 -j DROPl
$IPT -A FORWARD -i $INTIF2 -s ! $INTNET2 -j DROPl
$IPT -A FORWARD -o $INTIF2 -d ! $INTNET2 -j DROPl
Дальше мы сделаем некоторые дополнительные проверки исходящих пакетов и остановим все icmp пакеты кроме ping.
Code:
# Дополнительная проверка
$IPT -A OUTPUT  -o $EXTIF -s ! $EXTNET -j DROPl
# Блокируем исходящие ICMP (кроме PING)
$IPT -A OUTPUT  -o $EXTIF -p icmp --icmp-type ! 8 -j DROPl
$IPT -A FORWARD -o $EXTIF -p icmp --icmp-type ! 8 -j DROPl
Замечательно. Двигаемся дальше и проверяем скрипт на ошибки.

Ports

Предполагая что у нас все сработало мы заткнем еще несколько портов, доступ по которым может представлять для нас серьезную опасность:
 # COMmon ports:
 # 0 is tcpmux; SGI had vulnerability, 1 is common attack
 # 13 is daytime
 # 98 is Linuxconf
 # 111 is sunrpc (portmap)
 # 137:139, 445 is Microsoft
 # SNMP: 161,2
 # Squid flotilla: 3128, 8000, 8008, 8080
 # 1214 is Morpheus or KaZaA
 # 2049 is NFS
 # 3049 is very virulent Linux Trojan, mistakable for NFS
 # Common attacks: 1999, 4329, 6346
 # Common Trojans 12345 65535
  COMBLOCK="0:1 13 98 111 137:139 161:162 445 1214 1999 2049 3049 4329 6346 3128 8000 8008 8080 12345 65535"
 # TCP ports:
 # 98 is Linuxconf
 # 512-515 is rexec, rlogin, rsh, printer(lpd)
 #   [very serious vulnerabilities; attacks continue daily]
 # 1080 is Socks proxy server
 # 6000 is X (NOTE X over SSH is secure and runs on TCP 22)
 # Block 6112 (Sun's/HP's CDE)
  TCPBLOCK="$COMBLOCK 98 512:515 1080 6000:6009 6112"
 
 # UDP ports:
 # 161:162 is SNMP
 # 520=RIP, 9000 is Sangoma
 # 517:518 are talk and ntalk (more annoying than anything)
  UDPBLOCK="$COMBLOCK 161:162 520 123 517:518 1427 9000 9 6346 3128 8000 8008 8080 12345 65535"
После определения переменных окружения нам останется только пробежаться по ним циклом:
 echo -n "FW: Blocking attacks to TCP port"
 for i in $TCPBLOCK;
 do
  echo -n "$i "
   $IPT -A INPUT   -p tcp --dport $i  -j DROPl
   $IPT -A OUTPUT  -p tcp --dport $i  -j DROPl
   $IPT -A FORWARD -p tcp --dport $i  -j DROPl
 done
 echo ""

 echo -n "FW: Blocking attacks to UDP port "
 for i in $UDPBLOCK;
 do
  echo -n "$i "
    $IPT -A INPUT   -p udp --dport $i  -j DROPl
    $IPT -A OUTPUT  -p udp --dport $i  -j DROPl
    $IPT -A FORWARD -p udp --dport $i  -j DROPl
 done
 echo ""
Ну что ж, теперь каждый раз, когда мы запускаем скрипт, эти строчки просто добавляются к уже существующим... что создаёт небольшой бардак. По этой причине мы собираемся перепрыгнуть в начало скрипта... сразу после переменных окружения для sed и grep, но перед переменными для EXTIP и EXTBC - там мы добавляем цикл, который производит очистку. Так мы будем уверены, что работаем в чистой среде. Нам не приходилось задуываться об этом ранее, потому что мы не имели возможности оттестить скрипт без обрыва соединения либо закрытия файрвола. Этот скрипт сначала выставляет всю политику в DROP, после чего очищает и удаляет наши цепочки. Чтобы убедиться, что мы по-прежнему можем залогиниться по ssh на наш сервер после того, как скрипт перезапустится, мы добавим цепочку INPUT для ssh. Пока что поставим её в конец скрипта. Это делается для того чтобы избежать открытия дыры в момент установки новых правил, что является довольно распространённой ошибкой:
 # Отказываем, потом принимаем: это уберёт дыру в момент,
 # когда мы закрываем порты
  $IPT        -P INPUT       DROP
  $IPT        -P OUTPUT      DROP
  $IPT        -P FORWARD     DROP

 # Очищаем все существуещие цепочки и стираем дополнительные
  CHAINS=`cat /proc/net/ip_tables_names 2>/dev/null`
  for i in $CHAINS;
  do
    $IPT -t $i -F
  done
  for i in $CHAINS;
  do
    $IPT -t $i -X
  done
  $IPT -A INPUT   -i $INTIF1 -p tcp --dport 22 --syn -m state --state NEW -j ACCEPT

Sysctl'ы

Сразу после этого мы запустим sysctl'ы для tcp_syncookies, icmp_echo_ignore_broadcasts, rp_filter и accept_source_routе. До этого момента многие правила, которые мы "проверяли", фактически не выполнялись. По сути, мы просто делали проверку синтаксических ошибок. Теперь наши правила будут выполняться в полной мере:
 echo 1 > /proc/sys/net/ipv4/tcp_syncookies
 echo 1 > /proc/sys/net/ipv4/icmp_echo_ignore_broadcasts

 # Проверка адреса источника
 for f in /proc/sys/net/ipv4/conf/*/rp_filter;
 do
  echo 1 > $f
 done
 # Отключаем роутинг источника IP и ICMP-редиректы
 for f in /proc/sys/net/ipv4/conf/*/accept_source_route;
 do
  echo 0 > $f
 done
 for f in /proc/sys/net/ipv4/conf/*/accept_redirects;
 do
  echo 0 > $f
 done
 echo 1 > /proc/sys/net/ipv4/ip_forward
:Теперь мы собираемся добавить трекинг ftp-соединения, так что нам не доведётся наблюдать ошибки PASV при установке паков:
 # Запускаем трекинг ftp-соединения
 MODULES="ip_nat_ftp ip_conntrack_ftp"
 for i in $MODULES;
 do
  echo "Добавляем модуль $i"
  modprobe $i
 done

Базовая конфигурация NAT

А сейчас возвратимся к концу нашего скрипта, поскольку мы собираемся открыть сервисы для хостов, находящихся за файерволом. Я включил следующие сервисы: IRC, MSN, ICQ, and NFS, FTP, domain,time и некоторые другие. Самое главное здесь то, что эти сервисы могут использоваться ТОЛЬКО ЗА файерволом.Таким образом никто не сможет их использовать по ftp внутри Вашей локальной сетки:
 IRC='ircd'
 MSN=1863
 ICQ=5190
 NFS='sunrpc'
 # Мы пользуемся sync!!
 PORTAGE='rsync'
 OpenPGP_HTTP_Keyserver=11371
 # Все порты сервисов считываются из /etc/services
 TCPSERV="domain ssh http https ftp ftp-data mail pop3 pop3s imap3 imaps imap2 time $PORTAGE $IRC $MSN $ICQ $OpenPGP_HTTP_Keyserver"
 UDPSERV="domain time"
 echo -n "FW: Allowing inside systems to use service:"
 for i in $TCPSERV;
 do
  echo -n "$i "
  $IPT -A OUTPUT  -o $EXTIF -p tcp -s $EXTIP --dport $i --syn -m state --state NEW -j ACCEPT
  $IPT -A FORWARD -i $INTIF1 -p tcp -s $INTNET1 --dport $i --syn -m state --state NEW -j ACCEPT
  $IPT -A FORWARD -i $INTIF2 -p tcp -s $INTNET2 --dport $i --syn -m state --state NEW -j ACCEPT
 done
 echo ""

 echo -n "FW: Allowing inside systems to use service:"
 for i in $UDPSERV;
 do
  echo -n "$i "
  $IPT -A OUTPUT  -o $EXTIF -p udp -s $EXTIP --dport $i -m state --state NEW -j ACCEPT
    $IPT -A FORWARD -i $INTIF1 -p udp -s $INTNET1 --dport $i -m state --state NEW -j ACCEPT
    $IPT -A FORWARD -i $INTIF2 -p udp -s $INTNET2 --dport $i -m state --state NEW -j ACCEPT
 done
 echo ""
Теперь сделаем так, чтобы файервол позволил нам пинговать внешний мир. Для этого разрешим прохождение icmp-пакетов через внешний интерфейс:
 # Разрешаем внешнее пингование
 $IPT -A OUTPUT  -o $EXTIF -p icmp -s $EXTIP --icmp-type 8 -m state --state NEW -j ACCEPT
 $IPT -A FORWARD -i $INTIF1 -p icmp -s $INTNET1 --icmp-type 8 -m state --state NEW -j ACCEPT
 $IPT -A FORWARD -i $INTIF2 -p icmp -s $INTNET2 --icmp-type 8 -m state --state NEW -j ACCEPT
                                                                               
 # Разрешим файерволу пинговать наши внутренние сетки:
 $IPT -A OUTPUT  -o $INTIF1 -p icmp -s $INTNET1 --icmp-type 8 -m state --state NEW -j ACCEPT
 $IPT -A OUTPUT  -o $INTIF2 -p icmp -s $INTNET2 --icmp-type 8 -m state --state NEW -j ACCEPT
Теперь мы по умолчанию будем записывать в журнал все прочие запросы, но никак не будем на них реагировать. Все что нам надо было принять мы описали в начале наших правил. Так что заканчиваем правила блокировкой всего, что специально не разрешено:
 # Заблокируем все, что осталось:
  $IPT -A INPUT             -j DROPl
  $IPT -A OUTPUT            -j REJECTl
  $IPT -A FORWARD           -j DROPl
Итак, все сделано. я имею дружественные nmap и nessus для моих соединений с вышеописанным набором правил и ничего не препятствует использованию IRC, MSN, ICQ, и emerge sync.

The full script

А сейчас, полноценный скрипт во всей своей красе (Заодно я поместил форвардинг ssh в более подходящее для него место):
 # Внешний интерфейс
  EXTIF=ppp0
 # Внутренний интерфейс
  INTIF1=eth1
  INTIF2=eth2
 # Loop-устройство/localhost
  LPDIF=lo
  LPDIP=127.0.0.1
  LPDMSK=255.0.0.0
  LPDNET="$LPDIP/$LPDMSK"
 # Переменные текстовых инструментов
  IPT='/sbin/iptables'
  IFC='/sbin/ifconfig'
  G='/bin/grep'
  SED='/bin/sed'
 # Последнее (но немаловажное) - пользователи
  JAMES=192.168.1.77
  TERESA=192.168.2.77
 # Deny вместо accept: предотвращает открытие "дыр"
 # в то время, как мы закрываем порты и все такое
  $IPT        -P INPUT       DROP
  $IPT        -P OUTPUT      DROP
  $IPT        -P FORWARD     DROP
 # Сброс всех существующих цепочек и стирание персональных цепочек
  CHAINS=`cat /proc/net/ip_tables_names 2>/dev/null`
  for i in $CHAINS
  do
   $IPT -t $i -F
   $IPT -t $i -X
  done
  echo 1 > /proc/sys/net/ipv4/tcp_syncookies
  echo 1 > /proc/sys/net/ipv4/icmp_echo_ignore_broadcasts
 # Проверка адреса источника
  for f in /proc/sys/net/ipv4/conf/*/rp_filter;
  do
   echo 1 > $f
  done
 # Запрет маршрутизации IP от источника и редиректов ICMP
  for f in /proc/sys/net/ipv4/conf/*/accept_source_route;
  do
   echo 0 > $f
  done
  for f in /proc/sys/net/ipv4/conf/*/accept_redirects;
  do
   echo 0 > $f
  done
  echo 1 > /proc/sys/net/ipv4/ip_forward
 # Установка переменных среды для внешнего интерфейса
  EXTIP="`$IFC $EXTIF|$G addr:|$SED 's/.*addr:\([^ ]*\) .*/\1/'`"
  #EXTBC="`$IFC $EXTIF|$G Bcast:|$SED 's/.*Bcast:\([^ ]*\) .*/\1/'`"
  EXTBC="255.255.255.255"
  EXTMSK="`$IFC $EXTIF|$G Mask:|$SED 's/.*Mask:\([^ ]*\)/\1/'`"
  EXTNET="$EXTIP/$EXTMSK"
  #echo "EXTIP=$EXTIP EXTBC=$EXTBC EXTMSK=$EXTMSK EXTNET=$EXTNET"
  echo "EXTIP=$EXTIP EXTBC=$EXTBC EXTMSK=$EXTMSK EXTNET=$EXTNET"
 # Так как EXTBC отсутствует, я устанавливаю ее вручную как it to 255.255.255.255
 # Это (надеюсь) послужит тем же целям
 # Устанвка переменных среды для первого внутреннего интерфейса
  INTIP1="`$IFC $INTIF1|$G addr:|$SED 's/.*addr:\([^ ]*\) .*/\1/'`"
  INTBC1="`$IFC $INTIF1|$G Bcast:|$SED 's/.*Bcast:\([^ ]*\) .*/\1/'`"
  INTMSK1="`$IFC $INTIF1|$G Mask:|$SED 's/.*Mask:\([^ ]*\)/\1/'`"
  INTNET1="$INTIP1/$INTMSK1"
  echo "INTIP1=$INTIP1 INTBC1=$INTBC1 INTMSK1=$INTMSK1 INTNET1=$INTNET1"
 # Установка переменных среды для второго внутреннего интерфейса
  INTIP2="`$IFC $INTIF2|$G addr:|$SED 's/.*addr:\([^ ]*\) .*/\1/'`"
  INTBC2="`$IFC $INTIF2|$G Bcast:|$SED 's/.*Bcast:\([^ ]*\) .*/\1/'`"
  INTMSK2="`$IFC $INTIF2|$G Mask:|$SED 's/.*Mask:\([^ ]*\)/\1/'`"
  INTNET2="$INTIP2/$INTMSK2"
  echo "INTIP2=$INTIP2 INTBC2=$INTBC2 INTMSK2=$INTMSK2 INTNET2=$INTNET2"
 # Сейчас мы собираемся создать несколько собственных цепочек, результатом работы 
 # которых будет логгинг отброшенных пакетов. Это поможет нам избежать необходимости
 # вводить команду log перед каждым отбрасыванием пакета, что мы хотим запротоколировать.
 # Первыми идут лог отброшенных пакетов и собственно отброс, затем лог пакетов с отказами
 # и собственно отказы.
 # Отключаем сообщения о том, что цепочки уже существуют (чтобы перезапуск был без мусора)
  $IPT -N DROPl   2> /dev/null
  $IPT -A DROPl   -j LOG --log-prefix 'DROPl:'
  $IPT -A DROPl   -j DROP
  $IPT -N REJECTl 2> /dev/null
  $IPT -A REJECTl -j LOG --log-prefix 'REJECTl:'
  $IPT -A REJECTl -j REJECT
 # Весь траффик от устройства loopback принимается
 # если IP совпадает с любым из наших интерфейсов.
  $IPT -A INPUT   -i $LPDIF -s   $LPDIP  -j ACCEPT
  $IPT -A INPUT   -i $LPDIF -s   $EXTIP  -j ACCEPT
  $IPT -A INPUT   -i $LPDIF -s   $INTIP1  -j ACCEPT
  $IPT -A INPUT   -i $LPDIF -s   $INTIP2  -j ACCEPT
 # Широковещательные пакеты блокируем
  $IPT -A INPUT   -i $EXTIF -d   $EXTBC  -j DROPl
  $IPT -A INPUT   -i $INTIF1 -d   $INTBC1  -j DROPl
  $IPT -A INPUT   -i $INTIF2 -d   $INTBC2  -j DROPl
  $IPT -A OUTPUT  -o $EXTIF -d   $EXTBC  -j DROPl
  $IPT -A OUTPUT  -o $INTIF1 -d   $INTBC1  -j DROPl
  $IPT -A OUTPUT  -o $INTIF2 -d   $INTBC2  -j DROPl
  $IPT -A FORWARD -o $EXTIF -d   $EXTBC  -j DROPl
  $IPT -A FORWARD -o $INTIF1 -d   $INTBC1  -j DROPl
  $IPT -A FORWARD -o $INTIF2 -d   $INTBC2  -j DROPl
 # Блокируем доступ к внутренней сети из WAN
 # Это также призвано не дать нечестивым крякерам использовать нашу сетку 
 # в качестве отправной точки для атак на других людей
 # Перевод с языка iptables:
 # "если пришедшие на наружный интерфейс пакеты были отправлены не с выданного
 # nefarious адреса, выкинуть их как горячую картошку"
  $IPT -A INPUT   -i $EXTIF -d ! $EXTIP  -j DROPl
 # А сейчас мы блокируем внутренние адреса, кроме двух, присвоенных нашим двум
 # внутренним интерфейсам.....только помните, что если вы воткнете свой лэптоп или
 # какой другой pc в напрямую в одну из этих сетевых карт, то нужно удостовериться,
 # что они имеют именно эти IP-адреса или добавить соответствующий адрес отдельно.               
 # Первый интерфейс/первая внутренняя сеть
  $IPT -A INPUT   -i $INTIF1 -s ! $INTNET1 -j DROPl
  $IPT -A OUTPUT  -o $INTIF1 -d ! $INTNET1 -j DROPl
  $IPT -A FORWARD -i $INTIF1 -s ! $INTNET1 -j DROPl
  $IPT -A FORWARD -o $INTIF1 -d ! $INTNET1 -j DROPl
 # Второй интерфейс/вторая внутренняя сеть
  $IPT -A INPUT   -i $INTIF2 -s ! $INTNET2 -j DROPl
  $IPT -A OUTPUT  -o $INTIF2 -d ! $INTNET2 -j DROPl
  $IPT -A FORWARD -i $INTIF2 -s ! $INTNET2 -j DROPl
  $IPT -A FORWARD -o $INTIF2 -d ! $INTNET2 -j DROPl
 # Дополнительная Egress-проверка
  $IPT -A OUTPUT  -o $EXTIF -s ! $EXTNET -j DROPl
 # Блокируем исходящиие пакеты ICMP (за исключением PING)
  $IPT -A OUTPUT  -o $EXTIF -p icmp --icmp-type ! 8 -j DROPl
  $IPT -A FORWARD -o $EXTIF -p icmp --icmp-type ! 8 -j DROPl
 # печально известные порты:
 # 0 - tcpmux; у SGI есть уязвимость, через которую можно атаковать
 # 13 - daytime
 # 98 - Linuxconf
 # 111 - sunrpc (portmap)
 # 137:139, 445 - Microsoft
 # SNMP: 161,2
 # Флотилия Squid: 3128, 8000, 8008, 8080
 # 1214 - Morpheus или KaZaA
 # 2049 - NFS
 # 3049 - очень заразный троян для Linux, часто путаемый с NFS
 # Часто атакуемые: 1999, 4329, 6346
 # Частые трояны 12345 65535
  COMBLOCK="0:1 13 98 111 137:139 161:162 445 1214 1999 2049 3049 4329 6346 3128 8000 8008 8080 12345 65535"
 # Порты TCP:
 # 98 - Linuxconf
 # 512-515 - rexec, rlogin, rsh, printer(lpd)
 #   [очень серьезеные уязвимости; продолжаются ежедневные атаки]
 # 1080 - прокси-серверы Socks
 # 6000 - X (ЗАМЕЧАНИЕ. X через SSH - безопасен, и работает на порту TCP 22)
 # Блокировка 6112 (CDE у Sun и HP)
  TCPBLOCK="$COMBLOCK 98 512:515 1080 6000:6009 6112"
 # Порты UDP:
 # 161:162 - SNMP
 # 520=RIP, 9000 - Sangoma
 # 517:518 - talk и ntalk (самые надоедливые)
  UDPBLOCK="$COMBLOCK 161:162 520 123 517:518 1427 9000"
 echo -n "FW: Blocking attacks to TCP port"
 for i in $TCPBLOCK;
 do
  echo -n "$i "
   $IPT -A INPUT   -p tcp --dport $i  -j DROPl
   $IPT -A OUTPUT  -p tcp --dport $i  -j DROPl
   $IPT -A FORWARD -p tcp --dport $i  -j DROPl
 done
 echo ""
 echo -n "FW: Blocking attacks to UDP port "
 for i in $UDPBLOCK;
 do
  echo -n "$i "
   $IPT -A INPUT   -p udp --dport $i  -j DROPl
   $IPT -A OUTPUT  -p udp --dport $i  -j DROPl
   $IPT -A FORWARD -p udp --dport $i  -j DROPl
 done
 echo ""
 # Открываем отлеживание соединений по ftp
  MODULES="ip_nat_ftp ip_conntrack_ftp"
  for i in $MODULES;
  do
   echo "Inserting module $i"
   modprobe $i
  done
 # Защищаем некоторые распространенные клиенты для чата.
 # Уберите из списка допустимых для пущей безопасности.
  IRC='ircd'
  MSN=1863
  ICQ=5190
  NFS='sunrpc'
 # Нам нужно синхронизировать данные!!
  PORTAGE='rsync'
  OpenPGP_HTTP_Keyserver=11371
 # Все порты сервисов читаются из /etc/services
  TCPSERV="domain ssh http https ftp ftp-data mail pop3 pop3s imap3 imaps imap2 time $PORTAGE $IRC $MSN $ICQ  $OpenPGP_HTTP_Keyserver" UDPSERV="domain time"
 echo -n "FW: Allowing inside systems to use service:"
 for i in $TCPSERV;
 do
  echo -n "$i "
   $IPT -A OUTPUT  -o $EXTIF -p tcp -s $EXTIP --dport $i --syn -m state --state NEW -j ACCEPT
   $IPT -A FORWARD -i $INTIF1 -p tcp -s $INTNET1 --dport $i --syn -m state --state NEW -j ACCEPT
   $IPT -A FORWARD -i $INTIF2 -p tcp -s $INTNET2 --dport $i --syn -m state --state NEW -j ACCEPT
 done
 echo ""
 echo -n "FW: Allowing inside systems to use service:"
 for i in $UDPSERV;
 do
  echo -n "$i "
   $IPT -A OUTPUT  -o $EXTIF -p udp -s $EXTIP --dport $i -m state --state NEW -j ACCEPT
   $IPT -A FORWARD -i $INTIF1 -p udp -s $INTNET1 --dport $i -m state --state NEW -j ACCEPT
   $IPT -A FORWARD -i $INTIF2 -p udp -s $INTNET2 --dport $i -m state --state NEW -j ACCEPT
 done
 echo ""
 # Разрешается ping наружу
 $IPT -A OUTPUT  -o $EXTIF -p icmp -s $EXTIP --icmp-type 8 -m state --state NEW -j ACCEPT
 $IPT -A FORWARD -i $INTIF1 -p icmp -s $INTNET1 --icmp-type 8 -m state --state NEW -j ACCEPT
 $IPT -A FORWARD -i $INTIF2 -p icmp -s $INTNET2 --icmp-type 8 -m state --state NEW -j ACCEPT
 # Файерволу разрешается ping внутренних систем
 $IPT -A OUTPUT  -o $INTIF1 -p icmp -s $INTNET1 --icmp-type 8 -m state --state NEW -j ACCEPT
 $IPT -A OUTPUT  -o $INTIF2 -p icmp -s $INTNET2 --icmp-type 8 -m state --state NEW -j ACCEPT                                                                                                                                                             
 $IPT -A INPUT   -i $INTIF1 -p tcp --dport 22 --syn -m state --state NEW -j ACCEPT
 $IPT -t nat -A PREROUTING -j ACCEPT
 $IPT -t nat -A POSTROUTING -o $EXTIF -s $INTNET1 -j MASQUERADE
 $IPT -t nat -A POSTROUTING -o $EXTIF -s $INTNET2 -j MASQUERADE
 $IPT -t nat -A POSTROUTING -j ACCEPT
 $IPT -t nat -A OUTPUT -j ACCEPT
 $IPT -A INPUT -p tcp --dport auth --syn -m state --state NEW -j ACCEPT
 $IPT -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
 $IPT -A OUTPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
 $IPT -A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT
 # Заблокировать и запротоколировать все, что мы могли забыть.
 $IPT -A INPUT -j DROPl
 $IPT -A OUTPUT -j REJECTl
 $IPT -A FORWARD -j DROPl