The OpenNET Project / Index page

[ новости /+++ | форум | теги | ]



Индекс форумов
Составление сообщения

Исходное сообщение
"Раздел полезных советов: Построение системы фильтрации конте..."
Отправлено auto_tips, 25-Ноя-13 10:53 
В статье подробно рассмотрен один из вариантов фильтрации трафика по реестру Роскомнадзора с помощью SQUID на базе FreeBSD, состыкованного с бекбоне CISCO по протоколу WCCP2.

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

[[IMG /opennews/pics_base/0_1385359495.png]]

Трафик HTTP и HTTPS, выходящий из облака нашей сети (в примере VLAN90) через наш бордер CISCO, мы будем сравнивать с ACL, содержащим IP-адреса, извлеченные из списка Роскомнадзора. В случае совпадения, мы отправляем трафик по протоколу WCCP (Web Cache Communication Protocol) на сервер(а)  SQUID. Данная схема позволит нам избежать анализа всего трафика и анализировать только тот, который идет на заявленные IP, что не может не сказаться положительно на общей производительности системы.

Из минусов данной связки следует, что допустим если внесенный в реестр ресурс с IP=3.3.3.3, доменом=bad.xxx и URL=http://www.bad.xxx/1.jpg переезжает на другой IP, то фильтр работать не будет. Но с другой стороны актуализация списков блокировки, это не наша проблема. Данный подход позволит нам с одной стороны не тратится на дорогостоящие системы анализа проходящего трафика, с другой стороны исключить «пионерский» подход блокировки всего ресурса по IP, а блокировать только указанный в реестре Роскомнадзора контент. Связку по WCCP лучше поднимать на приватных адресах, что бы не писать лишние ACL в SQUID .

И так, приступим к реализации. Для начала нам надо вытащить электронную подпись с рутокена, полученного в роскомнадзоре. Для этого воспользуемся утилитой
[[http://www.ps-ax.ru/wordpress/wp-content/uploads/2013/10/P12... P12FromGostCSP]], и экспортируем подпись в формате PKSC#12.

Если экспорт удался, то можем перейти далее к построению системы. Если экспорт не удался (у меня в одном случае из четырех было именно так), то автоматическая загрузка реестра Роскомнадзора будет не доступна, и Вам придется файл качать и выкладывать реестр руками.

Берем любой сервер на базе FreeBSD (я предпочитаю данную ОС, но ничего не мешает Вам сделать по образу и подобию на любой *NIX машине), на котором мы будем заниматься загрузкой базы Роскомнадзора, ее обработкой и у меня этот же сервер как правило обеспечивает анализ и фильтрацию контента. Так как системный OpenSSL в FreeBSD не поддерживает шифрование ГОСТ, ставим свежую версию из портов. Это не затрагивает работоспособность всей системы, и портовый OpenSSL прекрасно существует параллельно системному:

   # which openssl
   /usr/bin/openssl
   # openssl version
   OpenSSL 0.9.8y 5 Feb 2013
   # /usr/local/bin/openssl version
   OpenSSL 1.0.1e 11 Feb 2013
   #

Находим файл /usr/local/openssl/openssl.cnf и пишем в конец файла:

   [openssl_def]
   engines = engine_section
   [engine_section]
   gost = gost_section
   [gost_section]
   default_algorithms = ALL
   engine_id = gost
   #dynamic_path = /usr/local/lib/engines/libgost.so
   CRYPT_PARAMS = id-Gost28147-89-CryptoPro-A-ParamSet
  
В начало этого файла пишем :

   openssl_conf = openssl_def

Копируем на сервер наш сертификат в формате PKSC#12 и конвертируем его в формат PEM:

   /usr/local/bin/openssl pkcs12 -in p12.pfx -out cert.pem -nodes -clcerts

Подготавливаем структуру директорий для работы наших скриптов и копируем туда сконвертированный сертификат:

   mkdir /var/db/zapret-info/
   mkdir /var/db/zapret-info/cfg
   mkdir /var/db/zapret-info/arch
   cp cert.pem /var/db/zapret-info/cfg/
   mkdir /tftpboot

Создаем файл запрос в соответствии с требованиями Роскомнадзора (где данные между тэгами изменены на данные вашей фирмы):

   echo '<operatorName>ООО "РОГА И КОПЫТА"</<operatorName>' > /var/db/zapret-info/cfg/request.xml
   echo '<inn>00XXXXXXXXXX</inn>' >> /var/db/zapret-info/cfg/request.xml
   echo '<ogrn>XXXXXXXXXXXXX</ogrn>' >> /var/db/zapret-info/cfg/request.xml
   echo '<email>info@rogaikopita.ru</email>' >> /var/db/zapret-info/cfg/request.xml

В зависимости от раскладок системной консоли полученный файл /var/db/zapret-info/cfg/request.xml необходимо сконвертировать в кодировку WINDOWS-1251.

Скачиваем нижеприложенные скрипты. Скрипт download.pl был найден мной на просторах Интернета и  упрощен, в оригинале он был чуток более замороченный. Задача данного скрипта — создать, подписать и отправить запрос в Роскомнадзор и загрузить реестр после отправки.


   #!/usr/bin/perl -w

   use strict;
   use SOAP::Lite;
   use MIME::Base64;
   use Sys::Syslog qw(:DEFAULT setlogsock);
   use POSIX qw(strftime);

   # XXX dirty!!!
   binmode STDOUT, ':utf8';
   binmode STDERR, ':utf8';


   my $BASE = "/var/db/zapret-info";

   my %n;
   ($n{sec},$n{min},$n{hour},$n{mday},$n{mon},$n{year},$n{wday},$n{yday},   $n{isdst}) = localtime(time());
   $n{year}+=1900;
   $n{mon}++;
   $n{mon}=~s/^(\d)$/0$1/;
   $n{mday}=~s/^(\d)$/0$1/;
   $n{hour}=~s/^(\d)$/0$1/;
   $n{min}=~s/^(\d)$/0$1/;
   $n{sec}=~s/^(\d)$/0$1/;
   my $dt="$n{year}-$n{mon}-$n{mday}T$n{hour}:$n{min}:$n{sec}.000+04:00";
   my $dd="$n{year}$n{mon}$n{mday}$n{hour}";

   my $service = SOAP::Lite->service("http://www.zapret-info.gov.ru/services/OperatorRequest?wsdl&...);

   my $reqfn = "$BASE/request-$dd.xml";
   my $binfn = "$BASE/request-$dd.bin";
   my $xmlreqfn = "$BASE/cfg/request.xml";
   my $certfn = "$BASE/cfg/cert.pem";

   my $request="" ;
   open (XML, "<$xmlreqfn");
   while (<XML>) { $request .= $_; }
   close (XML);

   die "Resuest template not found in $xmlreqfn." if (length($request)==0);

   open (XMLREQ, ">$reqfn") or die "Can't create new request";
   print XMLREQ "<?xml version=\"1.0\" encoding=\"windows-1251\"?>\n";
   print XMLREQ "<request>\n\t<requestTime>$dt</requestTime>\n";
   print XMLREQ $request;
   print XMLREQ "</request>";
   close XMLREQ;

   system ("/usr/local/bin/openssl smime -sign -in $reqfn -out $binfn".
            " -signer $certfn -outform DER -nodetach");


   open XMLREQ, "<$reqfn" or die "Can't open $reqfn";
   my $xmlreq = do { local $/ = undef; <XMLREQ>; };
   close XMLREQ;

   open XMLREQSIG, "<$binfn" or die "Can't open $binfn";
   my $xmlreqsig = do { local $/ = undef; <XMLREQSIG>; };
   close XMLREQSIG;

   my @sendresult = $service->sendRequest(
        $xmlreq,
        $xmlreqsig );

   if ($sendresult[0] eq 'false') {
       mylog("error request $sendresult[1]");
   } elsif ($sendresult[0] eq 'true') {
       open (CODESTRING, ">$BASE/codestring");
       print CODESTRING $sendresult[2];
       close CODESTRING;
       mylog("sent request $binfn: $sendresult[1]");
   };

   if (-e "$BASE/codestring") {
       open CODESTRING, "$BASE/codestring";
       my $codestring = <CODESTRING>;
       close CODESTRING;

       my $cnt = 0;
       while(1) {
           my @getresult = $service->getResult($codestring);
           if ($getresult[0] eq 'false') {
               mylog ("$getresult[1]");
               sleep 60;
           } elsif ($getresult[0] eq 'true') {
               my $outarch = decode_base64($getresult[1]);
               open (OUT, ">$BASE/out.zip");
               print OUT $outarch;
               close OUT;
               unlink "$BASE/codestring";

               if (-e "$BASE/out.zip") {
                   system("/bin/mv $reqfn $BASE/arch/");
                   system("/bin/mv $binfn $BASE/arch/");
                   system("/bin/cp $BASE/out.zip $BASE/arch/out-$dt.zip");
                   system("/usr/bin/unzip -o $BASE/out.zip -d $BASE/dump");
                   unlink "$BASE/out.zip";
                   mylog ("Done.  Everything seems to be ok.");
                   exit 0;
               };
               mylog("Shouldn't reach. DEBUG ME!!!");
               exit 255;
           } else {
               mylog ("getresult is unknown");
               exit 255;
           };
           $cnt++;
           if ($cnt>30) {
               mylog ("too much tries, bailing out");
               exit 255;
           }
           };
           # notreached
  
   } else {
           mylog ("codestring file not found");
   };
  
   sub mylog {
       my $logstring = $_[0];
       my($now_string);
       $now_string = strftime "%d-%m-%Y %H:%M:%S", localtime;
       print(STDERR $now_string." ".$logstring."\n");
   };

  
В задачи скрипта xml-paser.pl входит обработка полученного реестра и подготовка двух файлов — block.acl и denied.conf. Файл block.acl содержит правила для нашей cisco и как правило загружается по tftp. Файл denied.conf содержит URL для редиректора SQUID. Скрипт анализирует, префикс http или https указан в URL и в соотвествии с данным префиксом генерирует ACL для CISCO. Для ресурсов с множественныйми IP и/или URL, скрипт генерирует два ACL — как по порту www, так и по порту 443 (так как реестр сам по себе сырой, и такие записи не предусматривают однозначной трактовки какой протокол на каком IP использовать).

   #!/usr/bin/perl
  
   use XML::Simple;
   use Data::Dumper ;
   use Encode ;
   use URI::Escape;
   use Text::Iconv;
  
   $db_path='/var/db/zapret-info/' ;
   $tftp_root='/tftpboot' ;
   $query_ip='XXX.XXX.XXX.XXX' ;
  
   $simple=XML::Simple->new(       KeepRoot=>1,
                                   RootName=>'reg:register'
   );
   $xml=$simple->XMLin($db_path.'dump/dump.xml');
   my %list = %{$xml->{'reg:register'}->{'content'}};
  
   open(FURL, ">${db_path}denied.conf");
   open(FACL, ">${tftp_root}/block.acl");
   open(FTMP, ">/tmp/block.ip");
   print(FACL "no ip access-list extended WCCP_REDIRECT\n");
   print(FACL "ip access-list extended WCCP_REDIRECT\n");
   print(FACL "deny tcp host ${query_ip} any\n");
  
   sub _url {
       eval {
           $url=shift;
           $url=~s/ //g;
           $url=uri_escape_utf8($url, "^A-Za-z0-9\-\._~:/\?\=\&\%");
           print(FURL $url."\n");
       }
   }
  
   while ( my($key, $value) = each %list) {
       my $domain=$value->{'domain'} ;
       my $ip=$value->{'ip'} ;
       my $url=$value->{'url'} ;
  
       if(ref($ip) eq 'ARRAY'){
           my @ip_list=@{$ip};
           foreach $ip (@ip_list) {
               print(FTMP "$ip eq $port\n");
               print(FTMP "$ip eq 443\n");
           }
       } else {
           my $proto=substr($url, 0, 5) ;
           if ("\L$proto" eq 'https') {
               $port = '443';
           } else {
               $port = 'www';
           }
           print(FTMP "$ip eq $port\n");
       }
  
       if(ref($url) eq 'ARRAY'){
           my @url_list=@{$url};
           foreach $url (@url_list) { _url($url); }
       } else { _url($url); }
   }
  
   close(FTMP);
   open(FTMP ,"sort -u /tmp/block.ip|");
   while (<FTMP>) {
       $ip=$_;
       $ip=~s/
   //g;
       print(FACL "permit tcp any host $ip \n");
   }
   close(FTMP);
   print(FACL "deny ip any any\n");
   print(FACL "!\n");
   print(FACL "end\n");
   close(FACL);
   close(FURL);
  
   open(SER ,"${db_path}/serial");
   read(SER,$serial,6);
   close(SER);
  
   if (length($serial)==0) { $serial=0; }
  
   $serial+=1;
  
   open(SER ,">${db_path}/serial");
   printf(SER "%06d",$serial);
   close(SER);
  
   exit 0;
  

В начале данного файла надо установить значение переменных, в соответсвии с Вашими настройками. Переменная $tftp_root должна быть установлена в соответствии с настройками Вашего tftp-сервера, переменная $query_ip должна содержать  IP-адрес интерфейса сервера, через который посылается в Интернет фильтрованный трафик, для исключения образования кольцевой обработки запросов.

Для работы скриптов нам требуется установить следующие порты :

   cd /usr/ports/net/p5-SOAP-Lite; make install
   cd /usr/ports/converters/p5-MIME-Base64; make install
   cd /usr/ports/sysutils/p5-Sys-Syslog; make install
   cd /usr/ports/textproc/p5-XML-Simple; make install
   cd /usr/ports/devel/p5-Data-Dumper; make install
   cd /usr/ports/converters/p5-Encode; make install
   cd /usr/ports/net/p5-URI; make install
   cd /usr/ports/converters/p5-Text-Iconv; make install

На данном этапе мы реализовали получение и разбор реестра Роскомнадзора в требуемый для нас вид. Переходим к настройке нашего железа.

Конфигурируем CISCO для работы с сервером SQUID по WCCP. Группа 0 используется для отсылки http-трафика, группа 70 — для отсылки https-трафика.

   ip wccp 0 redirect-list WCCP_REDIRECT group-list 10 accelerated
   ip wccp 70 redirect-list WCCP_REDIRECT group-list 10 accelerated
   access-list 10 remark +++ WCCP_SQUID_PROXY +++
   access-list 10 permit 1.1.2.2

Создаем пользователя, который будет грузить access-листы и настраиваем rsh.

   username blocker privilege 15 password password
   no ip rcmd domain-lookup
   ip rcmd rsh-enable
   ip rcmd remote-host blocker 1.1.2.2 root enable

Ставим из портов SQUID. При выборе опций обязательно контролируем, что WCCP и WCCP2 протоколы включены. Создаем конфиг (в примере для версии 3.2) /usr/local/etc/squid/squid.conf

   http_port 1.1.2.2:9090
   http_port 1.1.2.2:3128 transparent
   https_port 1.1.2.2:3129 transparent ssl-bump generate-host-certificates=on dynamic_cert_mem_cache_size=4MB cert=/usr/local/etc/squid/squid.pem
   always_direct allow all
   ssl_bump allow all
   sslproxy_cert_error allow all
   sslproxy_flags DONT_VERIFY_PEER
   sslcrtd_program /usr/local/libexec/squid/ssl_crtd -s /var/squid/ssl_db -M 4MB
   icp_port 0
   hierarchy_stoplist cgi-bin ?
   acl QUERY urlpath_regex cgi-bin \?
   cache deny QUERY
   acl apache rep_header Server ^Apache
   cache_mem 1 MB
   cache_swap_low 90
   cache_swap_high 95
   maximum_object_size 1 KB
   maximum_object_size_in_memory 50 KB
   cache_replacement_policy heap LFUDA
   cache_dir ufs /var/squid/cache 1 1 1 no-store
   logfile_rotate 7
   dns_nameservers 8.8.8.8 8.8.4.4
   hosts_file /etc/hosts
   refresh_pattern ^ftp: 1440 20% 10080
   refresh_pattern ^gopher: 1440 0% 1440
   refresh_pattern . 0 20% 4320
   quick_abort_min 0 KB
   quick_abort_max 0 KB
   half_closed_clients off
   acl purge method PURGE
   acl CONNECT method CONNECT
   acl SSL_ports port 443
   acl Safe_ports port 80
   acl Safe_ports port 443 # https
   acl Safe_ports port 1025-65535 # unregistered ports
   http_access allow manager localhost
   http_access deny manager
   http_access allow purge localhost
   http_access deny purge
   http_access deny !Safe_ports
   http_access deny CONNECT !SSL_ports
   http_access allow localhost
   http_access allow all
   http_reply_access allow all
   icp_access allow all
   cache_mgr info@my.domain
   cache_effective_group proxy
   memory_pools off
   log_icp_queries off
   cachemgr_passwd q1w2e3r4 all
   client_db off
   buffered_logs on
   wccp2_router 1.1.2.1
   wccp2_forwarding_method 2
   wccp2_return_method 2
   wccp2_assignment_method 2
   wccp2_service dynamic 0
   wccp2_service_info 0 protocol=tcp flags=dst_ip_hash,dst_port_hash priority=240 ports=80
   wccp2_service dynamic 70
   wccp2_service_info 70 protocol=tcp flags=dst_ip_hash,dst_port_hash priority=240 ports=443
   wccp2_address 1.1.2.2
   redirect_program /usr/local/etc/squid/redirector.pl
   url_rewrite_children 20 startup=10 idle=1 concurrency=0

Обратите внимание на строки wccp2_router и wccp2_address. Соответственно тут должны быть адреса на интерфейсе со стороны CISCO и SQUID. Трафик на сервер SQUID посылается путем замены MAC-адреса, без организации дополнительного GRE-тунеля.

Скрипт редиректора копируется в  /usr/local/etc/squid/:

   #!/usr/bin/perl

   use IO::Handle;
   use POSIX qw(strftime);
   use POSIX qw(setsid);
  
   my $db_path     = "/var/db/zapret-info/";
   my $block_url   = "http://my.domain/block/";
   my $log_file    = "/var/log/squid/redirector.log";
  
   $0 = 'redirect' ;
   $| = 1 ;
   my @banners ;
   my @X ;
   my $serial      =0;
  
   sub _log {
       my $log_str = shift;
       my $now_str = strftime "%d-%m-%Y %H:%M:%S", localtime;
       printf(FLOG "[%s] %s\n", $now_str, $log_str);
   }
  
   sub _rule_refresh {
       open(SER ,"${db_path}/serial");
       read(SER,$num,6);
       close(SER);
  
       if (length($num)==0) { $num=0; }
       if ($num > $serial) {
           _log("Reading file ${db_path}/denied.conf serial ${num}");
           open (IN_FILE, "${db_path}/denied.conf") || die $!;
           my @tmp_data = <IN_FILE>;
           chomp @tmp_data;
           @banners=();
           push @banners, map { qr /\Q$_\E/ }  grep { ! /^\s*$/ }       @tmp_data;
           close(IN_FILE);
           $serial=$num;
       }
   }
  
   $SIG{INT} = \&_rule_refresh;
   open(FLOG, ">>${log_file}");
   $| = 1 ;
   FLOG->autoflush(1);
   _log("Redirector started.");
  
   while (<>) {
       chomp ;
       @X = split ;
       ($url, $who, $ident, $method) = @X ;
       _rule_refresh ;
       $url = $block_url if grep ($url=~/$_/i, @banners) ;
       print "$url $who $ident $method\n" ;
   }
  
   _log("Redirector exited.");
   close(FLOG);
   exit 0;


В начале данного файла мы должны установить значение переменных $db_path, $block_url, $log_file в соответствии с Вашими настройками. $db_path содержит путь до директории с файлом denied.conf и serial, $block_url должен содержать адрес страницы на которую мы перекидываем клиента при блокировании ресурса.

Что бы анализировать HTTPS-трафик, нам нужно сгенерировать самоподписанный сертификат:

   cd /usr/local/etc/squid/
   openssl req -new -newkey rsa:1024 -days 365 -nodes -x509 -keyout squid.pem -out squid.pem

Запускаем SQUID. Если все настроено верно, то на CISCO по команде sh ip wccp, мы увидим что-то похожее на такое:

   #sh ip wccp
   Global WCCP information:
    Router information:
    Router Identifier: 1.1.1.1
    Protocol Version: 2.0
  
   Service Identifier: 0
    Number of Service Group Clients: 1
    Number of Service Group Routers: 1
   ...
    Redirect access-list: WCCP_REDIRECT
   ...

   Service Identifier: 70
    Number of Service Group Clients: 1
    Number of Service Group Routers: 1
   ...
    Redirect access-list: WCCP_REDIRECT
   ...

Если соединение WCCP2 установилось, мы может попробовать запустить трафик на SQUID. Для этого в ipfw мы должны поставить правила переадресации трафика :

   ipfw add 100 fwd 1.1.2.2,3128 tcp from not me to any dst-port 80 via ${wccp_intf} in
   ipfw add 100 fwd 1.1.2.2,3129 tcp from not me to any dst-port 443 via ${wccp_intf} in

(где ${wccp_intf} — интерфейс для обмена wccp-трафиком)

На CISCO запускаем трафик на SQUID :

   conf t
   int vlan90
    ip wccp 0 redirect in
    ip wccp 70 redirect in

Для загрузки acl в CISCO поднимаем tftp-сервер, раскомментировав соответствующую строку в /etc/inetd.conf и перезапустив inetd . Проверяем загрузку правил по rsh, выполнив из консоли сервера :

   ( sleep 2 ; echo '' ; sleep 15 ) | rsh -l blocker 1.1.2.1 copy tftp://1.1.2.2/block.acl running-config ;

Проверяем в CISCO что правила загрузились. Если все отлично, создаем скрипт, который мы включаем в на выполнение в крон :

   #!/bin/sh
   . /var/db/zapret-info/cfg/loader.conf
   /scripts/download.pl

   if [ $? -eq 0 ];
   then
    /scripts/xml-parser.pl
   else
    exit 254 ;
   fi
   if [ $? -eq 0 ];
   then
    ( sleep 2 ; echo '' ; sleep 15 ) | rsh -l blocker  ${cisco_address} copy tftp://${tftp_address}/block.acl running-config ;
   else
    exit 253 ;
   fi
   exit 0;

Создадим файл конфигурации скрипта в /var/db/zapret-info/cfg/loader.conf

   echo 'cisco_address="1.1.2.1"' > /var/db/zapret-info/cfg/loader.conf
   echo 'tftp_address="1.1.2.2"' >> /var/db/zapret-into/cfg/loader.conf

Если Вы все сделали без ошибок и поняли идею, Вы получили работающий полностью в автоматическом режиме сервер фильтрации трафика, синхронизированный с реестром Роскомнадзора, фильтрующий ВСЕ (!) ресурсы внесенные в реестр, в том числе URI написанные на русском языке и URI на защищенных SSL-ресурсах (для пользователя соединение отдаётся с использованием самоподписанного сертификата).


URL: http://www.ps-ax.ru/?p=35
Обсуждается: https://www.opennet.ru/tips/info/2810.shtml

 

Ваше сообщение
Имя*:
EMail:
Для отправки ответов на email укажите знак ! перед адресом, например, !user@host.ru (!! - не показывать email).
Более тонкая настройка отправки ответов производится в профиле зарегистрированного участника форума.
Заголовок*:
Сообщение*:
 
При общении не допускается: неуважительное отношение к собеседнику, хамство, унизительное обращение, ненормативная лексика, переход на личности, агрессивное поведение, обесценивание собеседника, провоцирование флейма голословными и заведомо ложными заявлениями. Не отвечайте на сообщения, явно нарушающие правила - удаляются не только сами нарушения, но и все ответы на них. Лог модерирования.



Партнёры:
PostgresPro
Inferno Solutions
Hosting by Hoster.ru
Хостинг:

Закладки на сайте
Проследить за страницей
Created 1996-2024 by Maxim Chirkov
Добавить, Поддержать, Вебмастеру