Пользователь
0,0
рейтинг
15 января 2015 в 22:37

Разработка → Перманентный бан злоумышленников при помощи Fail2Ban + MikroTik из песочницы

Несколько дней назад я установил Asterisk, загрузил свою старую конфигурацию с маршрутизацией вызовов и намеревался подключиться к местному SIP провайдеру. Буквально через несколько минут после запуска Asterisk'а обнаружил в логах попытки авторизации на сервере, что меня ничуть не удивило, т.к. такая картина наблюдается на любом астериске, смотрящем в Интернет. Было принято волевое решение поиграться с любимым микротиком и не менее любимым питоном, и придумать, что делать с этими злоумышленниками.

Итак, у нас имеется:
  • Ubuntu Server 14.04 (думаю не принципиально, должно работать на других дистрибутивах)
  • Fail2Ban
  • MySQL
  • Asterisk (или любой другой сервис, который нужно защитить от брут форс атак)
  • Роутер MikroTik
  • Руки
  • Желание изобрести велосипед


После прочтения пары статей (один, два) родился следующий концепт:
  1. баним злоумышленника на определённое время при помощи Fail2Ban и добавляем запись с его IP адресом в БД MySQL
  2. после определённого количества выданных банов добавляем IP адрес в список запрещённых на роутере


А теперь к реализации решения.
1. Создаём БД/таблицу, которая будет содержать следующую информацию — IP адрес, код страны, название страны, количество выданных банов, тип атак/сервис (jail name из конфигурации Fail2Ban), последняя попытка, первая попытка (с заделом на будущее, возможно буду как-то ещё использовать эти данные).

Схема
CREATE DATABASE fail2ban CHARACTER SET utf8;

CREATE TABLE `ban_history` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `ip_address` char(15) NOT NULL DEFAULT '',
  `country_code` varchar(5) DEFAULT NULL,
  `country_name` varchar(30) DEFAULT NULL,
  `count` int(11) NOT NULL,
  `type` varchar(30) DEFAULT NULL,
  `last_attempt` datetime NOT NULL,
  `first_attempt` datetime NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;



2. Создаём скрипт для добавления записей в БД. Скрипт написан на питоне и требует для своей работы следующие дополнительные модули — pygeoip и MySQL-python. Оба модуля легко устанавливаются при помощи пакетного менеджера pip:

pip install pygeoip MySQL-python

Скрипт
#!/usr/bin/env python2
# -*- coding: utf-8 -*-

import os
import urllib
import gzip
import StringIO
import logging
import logging.handlers
import MySQLdb
import MySQLdb.cursors
import ConfigParser
import pygeoip
from datetime import datetime
from sys import exit
from optparse import OptionParser


def main(config, logger, ip_addr, attack_type, GEOIP_DAT):
	url = urllib.urlopen('http://geolite.maxmind.com/download/geoip/database/GeoLiteCountry/GeoIP.dat.gz')
	url_f = StringIO.StringIO(url.read())
	handle = gzip.GzipFile(fileobj=url_f)
	with open(GEOIP_DAT, 'w') as out:
		for line in handle:
			out.write(line)

	if config.has_option('general', 'mysql_ip') and config.has_option('general', 'mysql_user') and config.has_option('general', 'mysql_password') and config.has_option('general', 'mysql_db'):
		try:
			logger.info("Connecting to MySQL host: %s" % config.get('general', 'mysql_ip'))
			db = MySQLdb.connect(
				host=config.get('general', 'mysql_ip'),
				user=config.get('general', 'mysql_user'),
				passwd=config.get('general', 'mysql_password'),
				db=config.get('general', 'mysql_db'),
				cursorclass=MySQLdb.cursors.DictCursor
			)

			cursor = db.cursor()
			logger.debug("Connected")
		except MySQLdb.Error, e:
			logger.error("Error %d: %s" % (e.args[0], e.args[1]))
			exit(2)
		else:
			query = """select * from ban_history where ip_address='%s' and type='%s'""" % (ip_addr, attack_type)
			result = run_query(cursor, query, logger)
			result = cursor.fetchall()
			now = datetime.now()
			gi = pygeoip.GeoIP(GEOIP_DAT, flags=pygeoip.const.MEMORY_CACHE)
			country_code = gi.country_code_by_addr(ip_addr)
			country_name = gi.country_name_by_addr(ip_addr)
			if len(result) > 0:
				logger.info("Updating blacklist DB record for IP-address %s" % ip_addr)
				result = result[0]
				count = result['count'] + 1
				query = """update ban_history set count=%s, last_attempt='%s', country_code='%s', country_name='%s' where id=%s""" % (count, now, country_code, country_name, result['id'])
				result = run_query(cursor, query, logger)
				db.commit()
			else:
				logger.info("Adding IP-address %s into blacklist DB" % ip_addr)
				count = 1
				query = """insert into ban_history (ip_address, country_code, country_name, count, type, last_attempt, first_attempt) values('%s', '%s', '%s', %s, '%s', '%s', '%s')""" % (ip_addr, country_code, country_name, count, attack_type, now, now)
				result = run_query(cursor, query, logger)
				db.commit()

	else:
		logger.error("Configuration incomplete")
		exit(3)


def run_query(cursor, query, logger):
	try:
		logger.debug("Running query \'%s\'" % query)
		cursor.execute(query)
	except MySQLdb.Error, e:
		logger.error("Error %d: %s" % (e.args[0], e.args[1]))
		exit(2)
	else:
		return True


if __name__ == '__main__':
	try:
		ROOT_PATH = os.path.dirname(os.path.realpath(__file__))
		GEOIP_DAT = os.path.join(ROOT_PATH, 'GeoIP.dat')
		parser = OptionParser(usage="usage: %prog [-c <configuration_file>] [-v] --ip IP-ADDRESS --type TYPE")
		parser.add_option("-v", "--verbose",
			action="store_true",
			default=False,
			dest="verbose",
			help="Verbose output")
		parser.add_option("-c", "--config",
			action="store",
			default=False,
			dest="cfg_file",
			help="Full path to configuration file")
		parser.add_option("--ip",
			action="store",
			default=False,
			dest="ip_addr",
			help="Attacker IP address")
		parser.add_option("--type",
			action="store",
			default=False,
			dest="attack_type",
			help="Type of attack (service)")

		(options, args) = parser.parse_args()
		verbose = options.verbose

		ip_addr = options.ip_addr
		attack_type = options.attack_type

		# Reading configuration file
		cfg_file = options.cfg_file
		if not cfg_file:
			cfg_file = os.path.join(ROOT_PATH, 'blacklist_db.cfg')
		config = ConfigParser.RawConfigParser()
		config.read(cfg_file)

		# Logging
		if config.get('general', 'log_file'):
			LOGFILE = config.get('general', 'log_file')
		else:
			LOGFILE = '/tmp/blacklist_db.log'

		FORMAT = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s', datefmt='%Y-%m-%d %H:%M:%S')
		try:
			rotatetime = logging.handlers.TimedRotatingFileHandler(LOGFILE, when="midnight", interval=1, backupCount=14)
		except IOError, e:
			print "ERROR %s: Can not open log file - %s"  % (e[0], e[1])
			exit(1)
		except Exception, e:
			print "Can not configure logger - %s"  % e
			exit(1)
	        
		formatter = logging.Formatter('%(asctime)s: %(message)s','%y-%m-%d %H:%M:%S')

		rotatetime.setFormatter(FORMAT)
		logger = logging.getLogger('BLACKLIST-DB')
		logger.addHandler(rotatetime)

		if verbose:
			lvl = logging.DEBUG
			console = logging.StreamHandler()
			formatter = logging.Formatter('%(asctime)s: %(message)s','%y-%m-%d %H:%M:%S')
			console.setFormatter(formatter)
			logger.addHandler(console)
		else:
			lvl = logging.INFO

		logger.setLevel(lvl)

		if ip_addr and attack_type:
			main(config, logger, ip_addr, attack_type, GEOIP_DAT)
		else:
			logger.error("IP address and attack type are needed but not specified")
			exit(1)

	except (KeyboardInterrupt):
		logger.info("CTRL-C... exit")
		exit(0)

	except (SystemExit):
		logger.info("Exit")
		exit(0)
		



Данные для подключения к БД скрипт берёт из конфигурационного файла, который по умолчанию пытается найти в той же директории, так же можно задать путь при помощи ключа "-c".

Пример кофигурационного файла
[general]
log_file = /var/log/blacklist_db.log
mysql_ip = localhost
mysql_user = db_user
mysql_password = db_pass
mysql_db = fail2ban

# Количество банов, после которого мы добавляем IP адрес в блэклист, по умолчанию 10
#ban_count = 10


Ключевой момент — скрипт выполняется вместе с добавлением правил в iptables, посему я отредактировал следующие файлы:
/etc/fail2ban/action.d/iptables-allports.conf
# Исходный вариант
actionban = iptables -I fail2ban-<name> 1 -s <ip> -j <blocktype>

# Изменённый вариант
actionban = iptables -I fail2ban-<name> 1 -s <ip> -j <blocktype>
            /путь/к/скрипту -v --ip <ip> --type <name>


/etc/fail2ban/action.d/iptables-multiport.conf
# Исходный вариант
actionban = iptables -I fail2ban-<name> 1 -s <ip> -j <blocktype>

# Изменённый вариант
actionban = iptables -I fail2ban-<name> 1 -s <ip> -j <blocktype>
            /путь/к/скрипту -v --ip <ip> --type <name>


/etc/fail2ban/action.d/iptables-new.conf
(не уверен для чего используется это действие, внёс изменения для верности)
# Исходный вариант
actionban = iptables -I fail2ban-<name> 1 -s <ip> -j <blocktype>

# Изменённый вариант
actionban = iptables -I fail2ban-<name> 1 -s <ip> -j <blocktype>
            /путь/к/скрипту -v --ip <ip> --type <name>


Таким образом, после добавления соответствующих правил в iptables исполняется наш скрипт и добавляет, либо обновляет данные в БД.

3. Создаём скрипт для генерирования блэклистов, которые впоследствие будут импортированы в наш микротик. Скрипт использует тот же конфигурационный файл для получения настроек, необходимых для подключения к БД и так же ищет его в своей корневой директории, опять же можно задать путь при помощи ключа "-c". На выходе создаётся скрипт/список адресов для импорта в микротик, опять же в той же самой директории, можно указать альтернативный путь при помощи ключа "-o".

Скрипт
#!/usr/bin/env python2
# -*- coding: utf-8 -*-

import os
import logging
import logging.handlers
import MySQLdb
import MySQLdb.cursors
import ConfigParser
from sys import exit
from optparse import OptionParser


def main(config, logger, output):
	if config.has_option('general', 'ban_count'):
		ban_count = config.getint('general', 'ban_count')
	else:
		ban_count = 10

	if config.has_option('general', 'mysql_ip') and config.has_option('general', 'mysql_user') and config.has_option('general', 'mysql_password') and config.has_option('general', 'mysql_db'):
		try:
			logger.info("Connecting to MySQL host: %s" % config.get('general', 'mysql_ip'))
			db = MySQLdb.connect(
				host=config.get('general', 'mysql_ip'),
				user=config.get('general', 'mysql_user'),
				passwd=config.get('general', 'mysql_password'),
				db=config.get('general', 'mysql_db'),
				cursorclass=MySQLdb.cursors.DictCursor
			)

			cursor = db.cursor()
			logger.debug("Connected")
		except MySQLdb.Error, e:
			logger.error("Error %d: %s" % (e.args[0], e.args[1]))
			exit(2)
		else:
			contents = ['/ip firewall address-list']
			logger.info('Fetching adresses from the blacklist DB')
			query = """select * from ban_history"""
			result = run_query(cursor, query, logger)
			result = cursor.fetchall()
			for ip in result:
				if ip['count'] >= ban_count:
					list_name = '%s_BLC' % ip['type'].upper()
					logger.info('Adding IP %s into \'%s\' list' % (ip['ip_address'], list_name))
					list_line = 'add address=%s list=%s comment=BLACKLIST' % (ip['ip_address'], list_name)
					contents.append(list_line)

			if len(contents) > 1:
				logger.info('Generating mikrotik rsc script...')
				script_file = open(output, 'w')
				for item in contents:
					script_file.write("%s\r\n" % item)

				script_file.close()

			logger.info('Done')

	else:
		logger.error("Configuration incomplete")
		exit(3)


def run_query(cursor, query, logger):
	try:
		logger.debug("Running query \'%s\'" % query)
		cursor.execute(query)
	except MySQLdb.Error, e:
		logger.error("Error %d: %s" % (e.args[0], e.args[1]))
		exit(2)
	else:
		return True


if __name__ == '__main__':
	try:
		ROOT_PATH = os.path.dirname(os.path.realpath(__file__))
		parser = OptionParser(usage="usage: %prog [-c <configuration_file>] [-v] [-o <output_file_path>]")
		parser.add_option("-v", "--verbose",
			action="store_true",
			default=False,
			dest="verbose",
			help="Verbose output")
		parser.add_option("-c", "--config",
			action="store",
			default=False,
			dest="cfg_file",
			help="Full path to configuration file")
		parser.add_option("-o",
			action="store",
			default=False,
			dest="output",
			help="Full path for the generated script file")

		(options, args) = parser.parse_args()
		verbose = options.verbose
		output = options.output

		if not output:
			output = os.path.join(ROOT_PATH, 'blacklists.rsc')

		# Reading configuration file
		cfg_file = options.cfg_file
		if not cfg_file:
			cfg_file = os.path.join(ROOT_PATH, 'blacklist_db.cfg')
		config = ConfigParser.RawConfigParser()
		config.read(cfg_file)

		# Logging
		if config.get('general', 'log_file'):
			LOGFILE = config.get('general', 'log_file')
		else:
			LOGFILE = '/tmp/blacklist_db.log'

		FORMAT = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s', datefmt='%Y-%m-%d %H:%M:%S')
		try:
			rotatetime = logging.handlers.TimedRotatingFileHandler(LOGFILE, when="midnight", interval=1, backupCount=14)
		except IOError, e:
			print "ERROR %s: Can not open log file - %s"  % (e[0], e[1])
			exit(1)
		except Exception, e:
			print "Can not configure logger - %s"  % e
			exit(1)
	        
		formatter = logging.Formatter('%(asctime)s: %(message)s','%y-%m-%d %H:%M:%S')

		rotatetime.setFormatter(FORMAT)
		logger = logging.getLogger('BLACKLIST-DB')
		logger.addHandler(rotatetime)

		if verbose:
			lvl = logging.DEBUG
			console = logging.StreamHandler()
			formatter = logging.Formatter('%(asctime)s: %(message)s','%y-%m-%d %H:%M:%S')
			console.setFormatter(formatter)
			logger.addHandler(console)
		else:
			lvl = logging.INFO

		logger.setLevel(lvl)

		main(config, logger, output)

	except (KeyboardInterrupt):
		logger.info("CTRL-C... exit")
		exit(0)

	except (SystemExit):
		logger.info("Exit")
		exit(0)




Этот скрипт исполняется при помощи крона, я выставил периодичность запуска в 15 минут.
*/15 * * * * /путь/к/скрипту > /dev/null 2>&1


4. Импорт полученного списка в наш роутер.

Полученный скрипт для микротика нужно поместить в директорию нашего web-сервера, у меня стоит nginx в конфигурацию которого я добавил следующие строки:
    location /blacklists.rsc {
        root /путь/к/директории/содержащей/скрипт;
    }

Вместо web-сервера Вы можете использовать ftp или tftp, тут всё зависит от Вашего вкуса.

Данная часть практически полностью «украдена» из второй статьи.

Раз в час файл скачивается с сервера по протоколу HTTP при помощи следующего скрипта (ниже скрипт и правило планировщика для микротика):
# Скрипт для скачивания блэклиста, замените example.com на доменное имя, либо IP адрес Вашего сервера
/system script add name="Download_blacklists" source={
/tool fetch url="http://example.com/blacklists.rsc" mode=http;
:log info "Downloaded blacklists.rsc";
}

# Правило планировщика для его исполнения
/system scheduler add comment="Download blacklists" interval=1h name="DownloadBlackLists" on-event=Download_blacklists start-date=jan/01/1970 start-time=01:05:00


Скрипт для импорта блэклиста:
# Скрипт
/system script add name="Update_blacklists" source={
/ip firewall address-list remove [/ip firewall address-list find comment="BLACKLIST"];
/import file-name=blacklists.rsc;
:log info "Removal old blacklists and add new";
}

# Правило планировщика
/system scheduler add comment="Update BlackList" interval=1h name="InstallBlackLists" on-event=Update_blacklists start-date=jan/01/1970 start-time=01:15:00


Для использования этого списка создаются запрещающие правила и помещаются перед разрешающими (т.к. правила обрабатываются по порядку), в данном примере созданы 2 правила, для SSH соединений и SIP:
/ip firewall filter
add action=reject chain=forward comment="SIP: Reject Blacklisted IP addresses" dst-port=5060-5061 in-interface=ID-Net protocol=udp src-address-list=ASTERISK_BLC
add action=reject chain=forward comment="SSH: Reject Blacklisted IP addresses" dst-port=22 in-interface=ID-Net protocol=tcp src-address-list=SSH_BLC


Где ID-Net имя моего внешнего интерфейса.

Данный «велосипед» ни на что не претендует и был собран «на коленке» за пару-тройку часов.
Надеюсь на конструктивную критику хабровчан и предложения по возможным улучшениям.

Архив со всеми представленными скриптами и схемой БД.
Yevgeniy Valeyev @MaZaY_Dead
карма
6,0
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама

Самое читаемое Разработка

Комментарии (12)

  • 0
    Это конечно все хорошо, но меня, как пользователя микротика, интересует один вопрос: какое количество filter-rules микротик может обработать без напряга? После какого числа он начнет напрягаться? 10 тысяч, 100 тысяч, 10 миллионов записей?
    Ну и второй вопрос… Можно ли как-нибудь заставить микротик (желательно именно его) добавлять в блэклист (допустимо добавление даже на час), в случае если с определенного IP прилетает очень много коннектов (брутфорсят SSH) за короткий промежуток времени?
    • +1
      Это конечно все хорошо, но меня, как пользователя микротика, интересует один вопрос: какое количество filter-rules микротик может обработать без напряга? После какого числа он начнет напрягаться? 10 тысяч, 100 тысяч, 10 миллионов записей?

      Это зависит от конфигурации Вашего роутера, объёма ОЗУ, ЦПУ, количества пропускаемого трафика, типов используемых фильтров, погоды за бортом, влажности, и т.д. и т.п.

      Ну и второй вопрос… Можно ли как-нибудь заставить микротик (желательно именно его) добавлять в блэклист (допустимо добавление даже на час), в случае если с определенного IP прилетает очень много коннектов (брутфорсят SSH) за короткий промежуток времени?

      Теоретически следующие правила должны это сделать, практически — надо тестировать.
      Первое правило добавляет IP адреса в список BLACKLIST есть количество коннектов равно, либо превышает 100, второе правило блокирует коннекты для IP адресов находящихся в списке BLACKLIST. Повторюсь схема теоретическая, нужно тестирование.
      add action=add-src-to-address-list address-list=BLACKLIST chain=forward connection-limit=100,32 dst-port=22 in-interface=Internet protocol=tcp address-list-timeout=1h comment="Limit SSH connections" tcp-flags=syn
      add action=drop chain=forward comment="Drop SSH connections for blacklisted hosts" dst-port=22 in-interface=Internet protocol=tcp src-address-list=BLACKLIST
      
      • +1
        Я пользуюсь этим:

        /ip firewall filter
        add action=drop chain=input comment="drop ssh brute forcers" disabled=no \
            dst-port=22 protocol=tcp src-address-list=ssh_blacklist
        add action=add-src-to-address-list address-list=ssh_blacklist \
            address-list-timeout=1w3d chain=input comment="" connection-state=new \
            disabled=no dst-port=22 protocol=tcp src-address-list=ssh_stage3
        add action=add-src-to-address-list address-list=ssh_stage3 \
            address-list-timeout=1m chain=input comment="" connection-state=new \
            disabled=no dst-port=22 protocol=tcp src-address-list=ssh_stage2
        add action=add-src-to-address-list address-list=ssh_stage2 \
            address-list-timeout=1m chain=input comment="" connection-state=new \
            disabled=no dst-port=22 protocol=tcp src-address-list=ssh_stage1
        add action=add-src-to-address-list address-list=ssh_stage1 \
            address-list-timeout=1m chain=input comment="" connection-state=new \
            disabled=no dst-port=22 protocol=tcp
        
        
        • 0
          Данное решение не работает, если атакующий будет совершать 1 попытку в минуту, он не поднимется выше списка ssh_stage1.
          • 0
            Я больше скажу, данное решение вообще не смотрит, была попытка авторизации успешной или нет. Оно тупо не даёт логиниться чаще, чем три раза в минуту. Как показывают мои логи, левые попытки авторизации идут частыми сериями. «Медленных» переборов я в логах за несколько лет не встречал.
        • 0
          Спасибо! Работает отлично! Только пришлось заменить цепочку с input на forward, а то у меня есть dstnat правило для ssh.
  • 0
    Это зависит от конфигурации Вашего роутера, объёма ОЗУ, ЦПУ, количества пропускаемого трафика, типов используемых фильтров, погоды за бортом, влажности, и т.д. и т.п.

    Роутер обычный, 951G-2HnD, RAM/HDD по 128Mb.
    На текущий момент маршрутизируется в среднем 5mbit (в ближайшем будущем подумываю притянуть еще один аплинк, и там будет где-то +5mbit +udp multicast, итого, где-то около 20-40mbit в общей сложенности, в пиках), в общем, не так уж и много.
    И да, на текущий момент фильтров почти нет (для определенных src addr/dst port прописаны дропы), влажность в пределах нормы, температура комнатная :D
    Теоретически следующие правила должны это сделать, практически — надо тестировать.
    Первое правило добавляет IP адреса в список BLACKLIST есть количество коннектов равно, либо превышает 100, второе правило блокирует коннекты для IP адресов находящихся в списке BLACKLIST. Повторюсь схема теоретическая, нужно тестирование.

    Спасибо! Завтра-послезавтра попробую! :)
    • +1
      Имеется схожий по характеристикам микротик — RB2011UaS-2HnD. Настроено 35 различных фильтров, 15 правил для пометки соединений/пакетов (mangle) в зависимости от адреса/порта назначения, с большими (очень большими) списками адресов, для последующей приоритезации трафика (simple queue). Так же добавлено 18 правил в цепочку NAT.

      В пиках роутер пропускает порядка 80Mbit, при этом загрузка ЦПУ поднимается до 80-90%, в штатном режиме — 20-30Mbit ЦПУ загружен на 30%. ОЗУ используется на 30% при любом количестве трафика.

      Хотелось бы уточнить, допустим Вы добавили 100 тысяч фильтров и всё работает нормально. Как Вы потом будете это администрировать?
      Обычно создаются какие-то общие правила, при необходимости добавляются списки адресов для разрешения/запрещения трафика.
    • 0
      для статистики: мой старенький MT RB450 (MIPS 24K V7.4 cpu @ 300MHz, 32M RAM) работал с 160+ правилами ip firewall (18 списков, много mangle) на стомегабитном торрент-трафике без каких-либо нареканий. Сейчас правил куда меньше. Но ip firewall в нём адски производительный, тормоза начинаются, если мутить бридж с фильтрами over EoIP over AES-encrypted VPN over PPPoE. Вот на такой задаче мой старичок выше 5 Мбит никогда не прыгал.
  • 0
    На текущий момент у меня мало правил, всего парочка дропов, а штук 10 правил в цепочке dstnat. Пока проблем в поддержке не возникает (ну, это, как вы понимаете, пока). Приоретизацию трафика хотел пощупать (есть игровой сервер, и надо бы для него помечать трафик как высокоприоритетный, но пока руки не дошли).
    Пока-что нагрузка в среднем 1-5% на проц. Я не представляю как я буду разруливать не то что 100 тысяч, я не представляю как я буду разруливать 100-200 правил (я все-таки программист, а не админ).

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

    Правда пока-что даже не представляю как себя поведет микротик когда появится мультикаст в моей сети, и тем более не представляю как он себя поведет, когда я этот мультикаст заверну в VPN =))
    • 0
      На текущий момент у меня мало правил, всего парочка дропов, а штук 10 правил в цепочке dstnat.

      То есть Вы открыты всему свету за исключением «избранных», чьи коннекты дропаются?

      По хорошему у Вас должно быть как-то так:
      # Дропаем невалидные форварды
      chain=forward action=drop connection-state=invalid
      # Аналогичные 10 правил, только в цепочке forward, например SSH
      chain=forward action=accept protocol=tcp in-interface=Internet dst-port=22

      # Разрешаем форвардинг и локальной сети в Интернет
      chain=forward action=accept in-interface=bridge-local out-interface=Internet
      # Дропаем все остальные форварды
      chain=forward action=drop
      # Закрываем наш микротик от внешнего мира
      chain=input action=drop connection-state=invalid in-interface=Internet
      # Разрешаем ICMP, если надо
      chain=input action=accept protocol=icmp in-interface=Internet
      chain=input action=accept connection-state=established in-interface=Internet
      chain=input action=accept connection-state=related in-interface=Internet
      # Остальное дропаем
      chain=input action=drop in-interface=Internet

      Пока-что нагрузка в среднем 1-5% на проц. Я не представляю как я буду разруливать не то что 100 тысяч, я не представляю как я буду разруливать 100-200 правил (я все-таки программист, а не админ).

      Так Вы и подойдите к этому делу как программист :D
      Напишите пару правил-функций и пуска в них попадают коннекты по определённому признаку, например из определённого списка.
  • 0

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.