настраиваем iptables для шлюза

или как настроить межсетевой экран под Linux

Постановка задачи: Есть сервер с двумя интерфесами eth0 и eth1. По интерфейсу eth1 создан тунель для PPPoE соединений с провайдером ppp0 и ppp1. Необходимо обезопасить нашу локальную сеть от нехорошего влияния извне, а так же сам Интернет от влияния нашей маленькой сети. Понятно, что за основу был взят скрипт из бескрайной паутины, но был адаптирован для нашей сети для выполнения следующих целей: запретить все; разрешить только то, что можно; дать с помощью маскарадинга интернет в локальную сеть (кому положено); сделать редирект на прозрачный сквид; дать торрент для одной машины из сети.

На самом деле есть библия по IpTables, где изложено все! Абсолютно все про IpTables... www.opennet.ru/docs/RUS/iptables/index.html Итак, начнём – cоздадим новый скрипт в каталоге /etc/init.d:

server:/# touch /etc/init.d/rc.firewall
server:/# vim /etc/init.d/rc.firewall  

Примечание: В любом случае, чтобы применять этот скрипт, нужно чётко понимать что он делает до мельчайших деталей. иначе Вы можете только себе навредить. Идея правильного межсетевого экрана проста – запретить всё, кроме того, что явно разрешено. Всегда отталкивайтесь от этого тезиса и будет Вам счастье.

Важным моментом является текстовый файл, в котором перечислены IP-адреса разрешенных к доступу к сети Интернет /etc/allow.txt... Нет ну серьёзно, Вы же не намерены открывать доступ к сети Интернет для всех желающих в Вашей сети?!

server:/# cat /etc/allow.txt

192.168.1.2
192.168.1.5
192.168.1.6
192.168.1.7
192.168.1.8
192.168.1.9
192.168.1.12
192.168.1.14
...
192.168.1.128
192.168.1.187
192.168.1.224
192.168.1.254

Дополнтельно обратите внимание на текстовый файл с перечнем пиринговых сетей, которые мы пускаем в обход шейпера на основе squid3, так как на то он и пиринг, чтобы его не ограничивать...

server:/# cat /etc/perring.txt

80.94.160.0/24
80.94.162.0/24
80.94.164.0/24
86.57.253.204/32

Скрипт понимает несколько ключей запуска, таких как start|stop|save|restart. Назначение их вполне ясное. Другие же, важные опции расписаны в коде самого скрипта, генерирующего наши правила для iptables.

#! /bin/sh
### BEGIN INIT INFO
# Provides:          rc.firewall
# Required-Start:    
# Required-Stop:
# Default-Start:     3
# Default-Stop:	     0 6 	
# Short-Description: Run /etc/init.d/rc.firewall if it exist
### END INIT INFO
######################################################################################
# Configured by Demon, 2009
######################################################################################
# Внешний интерфейс (к ADSL модему 1)
 EXTIF="eth1"
# Внешний интерфейс (тунель по EXTIF - канал Интернета провайдера, объединяет ppp0 и ppp1)
 PPPIF="ppp+"
# Внутренний интерфейс (Локальная сеть)
 INTIF="eth0"

# 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'

 UNPRIPORTS="1024:65535"
# Перечень ip адресов закрытых для FORWARD 
 FILENAME_DENY="/etc/deny_inet_hosts"
 FILENAME_ALLOW="/etc/allow.txt"
 FILENAME_PERRING="/etc/perring.txt"

start_fw()
{
	echo ""	
	echo "IpTables configured by Demon"
	# 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

	# Установка переменных среды для внешнего интерфейса ETH1
	 EXTIP="192.168.2.2"
	 EXTBC="192.168.2.255"
	 EXTMSK="255.255.255.0"
	 EXTNET="$EXTIP/$EXTMSK"
 	 echo ""
	 echo "EXTIP=$EXTIP EXTBC=$EXTBC EXTMSK=$EXTMSK EXTNET=$EXTNET"

	# Устанвка переменных среды для внутреннего интерфейса
	 INTIP="192.168.1.1"
	 INTBC="192.168.1.255"
	 INTMSK="255.255.255.0"
	 INTNET="192.168.1.0/24"
	 echo ""
	 echo "INTIP=$INTIP INTBC=$INTBC INTMSK=$INTMSK INTNET=$INTNET"

	# Отключаем сообщения о том, что цепочки уже существуют (чтобы перезапуск был без мусора)
	 $IPT -N DROP   2> /dev/null
	 $IPT -N REJECT 2> /dev/null
	
	# Весь траффик от устройства 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   $INTIP  -j ACCEPT

	# Широковещательные пакеты
	 $IPT -A INPUT   -i $INTIF -d   $INTBC  -j ACCEPT
	 $IPT -A OUTPUT  -o $EXTIF -d   $EXTBC  -j ACCEPT
	 $IPT -A OUTPUT  -o $INTIF -d   $INTBC  -j ACCEPT
	 $IPT -A FORWARD -o $EXTIF -d   $EXTBC  -j ACCEPT
	 $IPT -A FORWARD -o $INTIF -d   $INTBC  -j ACCEPT
	
	# Блокируем доступ к внутренней сети из WAN
	# Это также призвано не дать нечестивым крякерам использовать нашу сетку
	# в качестве отправной точки для атак на других людей
	# Перевод с языка iptables:
	# "если пришедшие на наружный интерфейс пакеты были отправлены не с выданного
	# nefarious адреса, выкинуть их как горячую картошку"
	 $IPT -A INPUT  -i $EXTIF -d ! $EXTIP  -j DROP

	# А сейчас мы блокируем внутренние адреса, кроме двух, присвоенных нашим двум
	# внутренним интерфейсам.....только помните, что если вы воткнете свой лэптоп или
	# какой другой pc в напрямую в одну из этих сетевых карт, то нужно удостовериться,
	# что они имеют именно эти IP-адреса или добавить соответствующий адрес отдельно.
	# Первый интерфейс/внутренняя сеть
	 $IPT -A INPUT   -i $INTIF -s ! $INTNET -j DROP
	 $IPT -A OUTPUT  -o $INTIF -d ! $INTNET -j DROP
	 $IPT -A FORWARD -i $INTIF -s ! $INTNET -j DROP
	 $IPT -A FORWARD -o $INTIF -d ! $INTNET -j DROP

	# Дополнительная Egress-проверка
	 $IPT -A OUTPUT  -o $EXTIF -s ! $EXTNET -j DROP

	# Разрешаем пакеты ICMP (смысла особого их блокировать я не нашел)
	 $IPT -A INPUT -p icmp -j ACCEPT
	 $IPT -A OUTPUT -p icmp -j ACCEPT
	 $IPT -A FORWARD -p icmp -j ACCEPT
	 
	# Косяное PPPoE треубет динамической настройки размера передаваемого пакета средствами iptables 
	# иначе часть сайтов работать не будет... Вообще! 
	 $IPT -A FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --set-mss 1300

	# печально известные порты:
	# 0 - tcpmux; у SGI есть уязвимость, через которую можно атаковать
	# 13 - daytime
	# 98 - Linuxconf
	# 111 - sunrpc (portmap)
	# SNMP: 161,2
	# Флотилия Squid: 8000, 8008, 8080 
	# 1214 - Morpheus или KaZaA
	# 2049 - NFS
	# 3049 - очень заразный троян для Linux, часто путаемый с NFS
	# Часто атакуемые: 1999, 4329, 6346
	# Частые трояны 12345 65535
	 COMBLOCK="0:1 13 98 111 161:162 1214 1999 2049 3049 4329 6346 8000 8008 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 520 123 517:518 1427 9000"

	 echo ""	 
	 echo "Blocking attacks to TCP port"
	 for i in $TCPBLOCK;
	 do
	  echo -n "$i "
	   $IPT -A INPUT   -p tcp --dport $i  -j DROP
	   $IPT -A OUTPUT  -p tcp --dport $i  -j DROP
	   $IPT -A FORWARD -p tcp --dport $i  -j DROP
	 done
	 echo ""
	 echo "Blocking attacks to UDP port "
	 for i in $UDPBLOCK;
	 do
	  echo -n "$i "
	   $IPT -A INPUT   -p udp --dport $i  -j DROP
	   $IPT -A OUTPUT  -p udp --dport $i  -j DROP
	   $IPT -A FORWARD -p udp --dport $i  -j DROP
	 done
	 echo ""
	# Открываем отлеживание соединений по ftp
	  MODULES="ip_nat_ftp ip_conntrack_ftp"
	  for i in $MODULES;
	  do
	   echo "Inserting module $i"
	   modprobe $i
	  done

	# Защищаем некоторые распространенные клиенты для чата.
	# Уберите из списка допустимых лишние для пущей безопасности или добавьте новые.

	 IRC='ircd'                                 # сервис IRC
	 MSN=1863                                   # сервис MSN
	 WebMoney=2802                              # сервис WebMoney
	 ICQ=5190                                   # сервис ICQ
	 SSH2=122                                   # наш порт SSH2
	 ADD="81 88"                                # "редкие" порты ТСР 
	 Lineage="2106 6666 7777 9923 17453"        # игра Lineage
	 CS="27015 27016 27017 27018"               # игра Half-Life, CS
	 Cardsh="10300 10500 11150 14450"           # порты для кардшаринга 
	 RA="3389 4899 2083 9010 3128 8080"         # удаленное администрирование 
	 NFS='sunrpc'                               # распределенная файловая система
	 PORTAGE='rsync'
	 OpenPGP_HTTP_Keyserver=11371

	# Все альясы портов сервисов читаются из /etc/services и преобразуются в цифровое значние
	 TCPSERV="domain http https ftp ftp-data mail pop3 pop3s imap3 imaps imap2 time www webmin $WebMoney $SSH2 $RA $PORTAGE $MSN $ICQ $IRC $Lineage $Cardsh $ADD $CS $OpenPGP_HTTP_Keyserver"

	 UDPSERV="domain time netbios-ns netbios-dgm netbios-ssn microsoft-ds $CS"

	# Теперь разрешаем открытые порты на всех интерфесах и для всех, кому Интернет открыт из файла FILENAME_ALLOW
	 echo ""	
	 echo -n "Allowing inside systems to use service TCP:"
	 echo ""
	 for i in $TCPSERV;
	 do
	  echo -n "$i "
	   $IPT -A INPUT  -i $INTIF -p tcp --dport $i --syn -m state --state NEW -j ACCEPT
	   $IPT -A INPUT  -i $EXTIF -p tcp --dport $i --syn -m state --state NEW -j ACCEPT
	   $IPT -A INPUT  -i $PPPIF -p tcp --dport $i --syn -m state --state NEW -j ACCEPT

  	   $IPT -A OUTPUT -o $INTIF -p tcp --dport $i --syn -m state --state NEW -j ACCEPT
	   $IPT -A OUTPUT -o $EXTIF -p tcp --dport $i --syn -m state --state NEW -j ACCEPT
	   $IPT -A OUTPUT -o $PPPIF -p tcp --dport $i --syn -m state --state NEW -j ACCEPT
         
	  # форвардинг по TCP разрешаем только тем кому можно (кто имеет доступ Интернет из локальной сети
	   for ip in $(cat $FILENAME_ALLOW); do
	     $IPT -A FORWARD -i $INTIF -p tcp -s $ip --dport $i --syn -m state --state NEW -j ACCEPT
	   done
	 done

	 echo ""
	 echo -n "Allowing inside systems to use service UDP:"
	 echo ""
	 for i in $UDPSERV;
	 do
	  echo -n "$i "
	   $IPT -A INPUT  -i $INTIF -p udp --dport $i -m state --state NEW -j ACCEPT
	   $IPT -A OUTPUT -o $INTIF -p udp --dport $i -m state --state NEW -j ACCEPT
	   $IPT -A OUTPUT -o $EXTIF -p udp --dport $i -m state --state NEW -j ACCEPT
	   # форвардинг по UDP разрешаем только тем кому можно (кто имеет доступ 
	   # в интернет из локальной сети
	     for ip in $(cat $FILENAME_ALLOW); do
	       $IPT -A FORWARD -i $INTIF -p udp -s $ip --dport $i -m state --state NEW -j ACCEPT
	     done
	 done

	# надо открыть наружу UDP порт domain=53 (иначе не работает DNS)
	 $IPT -A OUTPUT -o $PPPIF -p udp --dport domain -m state --state NEW -j ACCEPT
	 $IPT -A INPUT -i $PPPIF -p udp --dport domain -m state --state NEW -j ACCEPT

	# DNS rndc сервер на Lo разрешаем. (обход ошибки connection refused)
	 $IPT -A OUTPUT -p tcp -m tcp -o $LPDIF --dport 953 --sport $UNPRIPORTS -j ACCEPT


	 echo ""
	 echo "SQUID exceptions and REDIRECT"

	# у нас есть пиринг! нет смысла заворачивать его на squid3? иначе он начнет резать скорость, 
	# банеры и проч. А пиринг - это хорошо, потому что быстро! Поэтому еще на этапе PREROUTING 
	# мы принимаем пакеты и выпускаем их в локальную сеть минуя squid

	 for ip in $(cat $FILENAME_PERRING); do
	    $IPT -t nat -A PREROUTING -p tcp -d $ip --dport 80 -j ACCEPT
	 done

	# Заворачиваем весь остальной трафик по 80 порту на наш прозрачный SQUID		
	 $IPT -t nat -A PREROUTING -i eth0 -p tcp -s $INTNET -d ! 192.168.1.1 -m multiport --dport 80 -j REDIRECT --to-port 3128

	 echo ""
	 echo "NAT up!"

	 $IPT -t nat -A PREROUTING -j ACCEPT
	 $IPT -t nat -A POSTROUTING -o $PPPIF -s $INTNET -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 DROP
	 $IPT -A OUTPUT -j REJECT
	 $IPT -A FORWARD -j DROP
}
flush_fw()
{
	# Сброс всех цепочек, стандартных, нестандартных. Обнуление нашего iptables
	$IPT -P INPUT ACCEPT
	$IPT -P FORWARD ACCEPT
	$IPT -P OUTPUT ACCEPT
	$IPT -t nat -P PREROUTING ACCEPT
	$IPT -t nat -P POSTROUTING ACCEPT
	$IPT -t nat -P OUTPUT ACCEPT
	$IPT -t mangle -P PREROUTING ACCEPT
	$IPT -t mangle -P OUTPUT ACCEPT

	$IPT -F
	$IPT -t nat -F
	$IPT -t mangle -F

	$IPT -X
	$IPT -t nat -X
	$IPT -t mangle -X
}
case "$1" in
start)	echo -n "Starting firewall: iptables"
	start_fw
	echo ""
        echo -n "Starting firewall Ok!"
	echo ""	
	;;
stop)	echo -n "Stopping firewall: iptables"
	flush_fw
	echo ""
        echo -n "Stopping firewall Ok!"
	echo ""
        ;;
save)	echo -n "Saving firewall: iptables"
	iptables-save > /etc/rules-save
	echo ""	
	echo -n "Status save Ok!."
	echo ""
	;;
restart) echo -n "Restarting firewall: iptables"
	flush_fw
	start_fw
	echo ""
	echo -n "Status restart Ok!"
	echo ""
        ;;
*)	echo "Usage: /etc/init.d/rc.firewall start|stop|save|restart"
        exit 1
        ;;
esac
exit 0

Выставим правильные права на сам скрипт:

server:/# chown root:root /etc/init.d/rc.firewall
server:/# chmod +x /etc/init.d/rc.firewall  

И добавим скрипт в автозагрузку:

server:/# ln -s /etc/init.d/rc.firewall /etc/rc3.d/S99rc.firewall 
Обратите внимание на то, что ссылка создается в каталог /etc/rc3.d Если у Вас в /etc/inittab установлен не 3 режим работы сервера (id:3:initdefault: многопользовательская платформа с поддержкой сети), а второй (id:2:initdefault: однопользовательская платформа с поддержкой сети) то необходимо создать эту сслыку в каталог rc2.d!!!

Другой вариант добавления в автозагрузку заключается в использовании утилиты update-rc.d. Используя ее мы добавляем наш скрипт под номером 98 в 2,3 уровни запуска и под номером 90 в останов системы и перезагрузку с командами start и stop соответственно.

server:/# cd /etc/init.d
server:/# update-rc.d rc.firewall start 98 2 3 . stop 90 0 6 .

Комментарии 6

  1. Дмитрий — Apr 09, 2015 at 04:32 PM

    Расскажите пожалуйста как Вы справились с https и прозрачным squid. Будьте добры. Очень интересует Ваша точка зрения.

  2. Дмитрий Владимирович — Dec 26, 2014 at 10:26 AM

    sergei, это проблема вёрстки - тут был перенос инструкции на новую строчку (исправил). Должно было быть вот так:
    $IPT -t nat -A PREROUTING -i eth0 -p tcp -s $INTNET -d ! 192.168.1.1 -m multiport \
    --dport 80 -j REDIRECT --to-port 3128

    Дополнительно скажу, что в данном случае "-m multiport" избыточно. Был момент, когда я отправлял не только HTTP (80) трафик но и HTTPS (443) на squid пробуя шейпить/блокировать сайты по HTTPS (например, https://vk.com) и это место выглядело так:
    -m multiport --dport 80, 443

  3. sergei — Dec 26, 2014 at 01:54 AM

    В строке 272, в "-m multiport \ --dport 80", символ "\" к чему относится? кто нить знает?

  4. Дмитрий Владимирович — Nov 03, 2014 at 11:42 AM

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

    Флаг -P нужен чтобы определиться с политиками цепочек по-умолчанию. Политика по-умолчанию устанавливается командой:
    iptables [-P {chain} {policy}]
    Политика по-умолчанию представляет собой действие, которое применяется к пакету, не попавшему под действие ни одного из правил в цепочке.

    Флаг же -A добавляет в конец конкретных цепочек (например пользовательских) правило "отбросить все". Вот и вся разница.

    В данном примере можно было оставить только -P. Но лучше все цепочки и всгда закрывать еще и по -A.

  5. Павел — Nov 01, 2014 at 07:03 PM

    Зачем писать
    $IPT -A INPUT -p udp --dport $i -j DROP
    $IPT -A OUTPUT -p udp --dport $i -j DROP
    $IPT -A FORWARD -p udp --dport $i -j DROP

    Когда по умолчанию все вначале запрещено?

    $IPT -P INPUT DROP
    $IPT -P OUTPUT DROP
    $IPT -P FORWARD DROP

  6. Mikolai — Feb 12, 2013 at 11:19 AM

    А как настроить файер, если у меня нет PPPoE, а только eth0 и eth1?
    Спасибо заранее!

  • 1

Комментарии отключены, сожалеем