Статьи‎ > ‎

Настройка в pfSense ограничения потребленного трафика, блокировка пользователей.

    История о том, как в небольшой организации руководство решило оградить некоторых сотрудников от "соблазнов" Интернета.
    В начале стояла задача точно подсчитать потребленный работниками трафик и узнать посещаемые "прожорливые" ресурсы в Сети.
    Итак, около 20 рабочих мест, локальный сервер, с необходимыми сотрудникам ресурсами, и шлюз в Интернет...
    Информация, изложенная далее, может оказаться полезной для того, кто решит "допилить" pfSense под свои нужды.

    В локальной сети DHCP и AD не используются, pfSense уже был установлен на шлюзе и настроен. Установлен и настроен Squid и Lightsquid. Если это не так, то установка производится в разделе System -> Packages на вкладке Available Packages.
Настройка squid производится в разделе Service-> Proxy server.
Настраиваем Lightsquid в разделе Status Proxy Report, вкладка Settings.
Статистика будет доступна на вкладке Lightsquid Report.

    Lightsquid осуществляет сбор и обработку HTTP трафика - только 80 порт (для прозрачного прокси), но конечно это не весь трафик, а только малая его доля. Для учета трафика по всем портам будет использован IPCAD, который будет дополнять данные для отчетов Lightsquid.

        Устанавливаем ipcad

    В первую очередь, в WEB-интерфейсе в разделе System -> Advanced устанавливаем галочку в пункте "Enable Secure Shell" и сохраняем конфигурацию (открыли доступ к pfSense по SSH).
    Для доступа к pfSense по SSH воспользуемся программой WinSCP. Для подключения вводим IP шлюза, соглашаемся принять новый сертификат безопасности и вводим логин root (не admin) и пароль.

    Для сбора статистики ipcad в отчет Lightsquid необходим файл remote shell. Помещаем его в каталог /usr/bin и назначаем ему права 0555.

Для ipcad требуется пакет compat6x, устанавливаем его командой:
pkg_add -r http://ftp-archive.freebsd.org/mirror/FreeBSD-Archive/old-releases/i386/8.1-RELEASE/8.1-RELEASE/packages/Latest/compat6x-i386.tbz

А теперь устанавливаем ipcad:
pkg_add -r http://ftp-archive.freebsd.org/mirror/FreeBSD-Archive/old-releases/i386/8.1-RELEASE/8.1-RELEASE/packages/Latest/ipcad.tbz

В каталоге /usr/local/etc  нам необходимо отредактировать файл ipcad.conf (можно переименовать и использовать ipcad.conf.default):

    Секция # GLOBAL OPTIONS #
Должно быть включено
capture-ports enable;

    Секция # INTERFACE OPTIONS #
#interface <iface> [ promisc ] [ input-only ] [ netflow-disable ] [ filter "<pcap_filter>" ] ;
interface em1 filter "ip and dst net 192.168.0.0/24 and not src net 192.168.0.0/24 and not src port 80";
Где: 
em1 - имя LAN интерфейса (уточнить имя можно в разделе Status - Interfaces). 
192.168.0.0/24 - наша подсеть с маской. 
not src port 80 - порт, на котором не будет учитываться трафик, т.к. по этому порту уже считается Lightsquid'ом.

# aggregate <ip>/<masklen> strip <maskbits> ;
aggregate 192.168.0.0/24 strip 32; /* Don't aggregate internal range */
aggregate 0.0.0.0/0 strip 32; /* Don't! Aggregate external networks */
Будут учитываться отдельно все внутренние и внешние IP.

# aggregate <port_range_start>[-<port_range_end>] into <port> ;
Если не указать группируемые порты, то отчет будет содержать информацию по всем используемым номерам портов, что может затруднить восприятие и увеличить объем ненужных данных для отчета. Можно указать, например:
aggregate 3128 into 0;
aggregate 80-81 into 0;
aggregate 20-21 into 21;
aggregate 22-23 into 22;
aggregate 25 into 25;
aggregate 26-109 into 26;
aggregate 110 into 110;
aggregate 111-142 into 111;
aggregate 143 into 143;
aggregate 144-442 into 144;
aggregate 443 into 443;
aggregate 444-992 into 444;
aggregate 993 into 993;
aggregate 994 into 994;
aggregate 995 into 995;
aggregate 996-65535 into 65535;

    Секция # RSH SERVER OPTIONS #
# Enable RSH Server:
rsh enable at 127.0.0.1;

# RSH access rules:
Достаточно только 
rsh root@127.0.0.1 admin; /* Can shutdown ipcad */
rsh 127.0.0.1 view-only; /* Other users can view current tables */
Но можно указать и дополнительно
rsh backupuser@127.0.0.1 backup; /* Can dump/restore/import accounting table */
rsh user@127.0.0.1; /* Can view and modify accounting tables */

    Секция # OTHER OPTIONS #
Уточнить директорию
chroot=/var/log/ipcad;

Сохраняем и закрываем файл конфигурации ipcad.

Далее в /var/log создаем подкаталог ipcad. В нем будут лежать ipcad.dump и ipcad.pid (их создавать не надо).

Скрипт переноса статистики ipcad в лог squid'а нашелся в Интернете.
В каталоге /root создаем файл tolog.sh с правами 0755:
---
1. #!/bin/sh
2. net1="192.168.0"
3. ttime=`/usr/bin/rsh localhost sh ip acco | grep 'Accounting data saved' | awk '{print ($4)}'`
4. rsh localhost clear ip accounting
5. rsh localhost show ip accounting checkpoint | grep $net1 | awk -v vtime=$ttime '{print (vtime".000",1,$2,"TCP_MISS/200",$4,"CONNECT",$1":"$5,"-","DIRECT/"$1,"-")}' >> /var/squid/logs/access.log
6. chown proxy:proxy /var/squid/logs/access.log
---
Необходимо уточнить net1 - Ваша подсеть, и путь к логам squid'а - /var/squid/logs/access.log (указан в Services - Proxy server - General поле Log store directory).

Необходимо настроить запуск ipcad со стартом системы и поминутный запуск tolog.sh.
В файле /cf/conf/config.xml. в самом начале, перед закрывающим тэгом </system> добавляем строку:
<shellcmd>/usr/local/bin/ipcad -rds</shellcmd>
ближе к концу файла находим конец секции </cron> и вставляем перед ним следующее:
<item>
<task_name>ipcad_add_squid_log</task_name>
<minute>*/1</minute>
<hour>*</hour>
<mday>*</mday>
<month>*</month>
<wday>*</wday>
<who>root</who>
<command>/tolog.sh</command>
</item>

Сохраняем и закрываем config.xml, удаляем файл /tmp/config.cache , перезагружаем pfSense!

    Все, ipcad работает, считает трафик по нужным портам и добавляет данные в отчет squid'а, где Lightsquid'ом можно посмотреть статистику по IP адресам.

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

    Ограничение количества трафика пользователей по IP адресам (блокировка).

    На странице Web интерфейса Squid на странице Access Control в поле "Banned host addresses" необходимо ввести любой неиспользуемый в локальной сети IP адрес.

В каталоге /usr/local/etc создаем файл usertrafficlimit.conf с правами 0755
и файл usertrafficlimit с правами 0644.

Файл usertrafficlimit.conf:
---
#!/usr/bin/perl
# Конфигурация скрипта отключения доступа к Интернет при достижении пользователем установленного лимита

#Пути к необходимым данным

#path to squid access.log
$squidlogpath = "/var/squid/logs";

#path to lightsquid report folder
$lsquidreportpath = "/var/lightsquid/report";

#file squid banned_hosts.acl
$bannedhostsfile = "/var/squid/acl/banned_hosts.acl";

#file "usertrafficlimit"
$usertrafficlimit = "/usr/local/etc/usertrafficlimit";

#Warning script-file
$warningusertrafficlimit= "#ВНИМАНИЕ!!! Не изменяйте первую строку файла. Файл перезаписывается скриптом /utl.pl\n#ip Limit fr Size Status\n";

#Лимит по умолчанию для всех пользователей
$generaluserlimit = "3Gb";

#Лимит по указанным пользователям в виде:
$vipuser{"192.168.0.253"}{limit}="5Gb";
$vipuser{"192.168.0.252"}{limit}="10Gb";
---

Пример usertrafficlimit:
---
Date: 02.2015
#ВНИМАНИЕ!!! Не изменяйте первую строку файла. Файл перезаписывается скриптом /utl.pl
#ip Limit fr Size Status
192.168.0.1 3Gb 1 744 on
192.168.0.10 1Gb 2 1305471852 on
192.168.0.252 3Gb 1 1 on
192.168.0.253 3Gb 1 1 on
---
Где:
ip        - IP адрес пользователя
Limit   - Установленный лимит в файле usertrafficlimit.conf в переменной $generaluserlimit или в 
$vipuser
fr   - Коэффициент (отношение) установленного лимита и потребленного трафика. 
Size - Потребленный трафик
Status - Разрешен доступ в Интернет или заблокирован (on/block)

В каталоге / (в корне) создаем файл utl.pl с правами 0755, Perl скрипт анализа отчетов lightsquid, подсчета трафика по ip и формирования блокировки в файл /var/squid/acl/banned_hosts.acl.

Файл utl.pl:
---
#!/usr/bin/perl

require '/usr/local/etc/usertrafficlimit.conf';

sub traffictobyte {
 my $traffic = $_[0];
 $traffic =~ s/[,]/./;
 if ($traffic =~ /(\d*[\.]?\d+)(\D+)/) {
  if (lc($2) eq "gb") {$traffic = $1*1024*1024*1024;}
  elsif (lc($2) eq "mb") {$traffic = $1*1024*1024;}
  elsif (lc($2) eq "kb") {$traffic = $1*1024;}
  else {$traffic = $1;}
 }
  return $traffic ;
}

($m,$y)=(localtime)[4,5];
 
$filter=sprintf("%02d",$y+1900) . sprintf("%02d",$m+1);
@daylist=glob("$lsquidreportpath/$filter*"); 

foreach $daypath (sort @daylist) {
  open (FF,"<","$daypath/.total"); 
  $totaluser=<FF>;chomp $totaluser;$totaluser=~s/^user: //;
  $totalsize=<FF>;chomp $totalsize;$totalsize=~s/^size: //;
  while (<FF>) {
    ($user,$size)=split;
    $h{$user}{size}+=$size;
  }  
  close FF;
}

my ($user,$limit,$fraction,$size,$status);

open (FF,"<",$usertrafficlimit);
  $monthupdate= <FF>; 
  $mupdate=$monthupdate; chomp $mupdate; $mupdate=~s/Date:\s(\d+)\.\d+/$1/; 
  if ($m+1 == $mupdate) {
    while (<FF>) {
      next if (m/^#/); next if (m/^\s+$/);
      ($user,$limit,$fraction,$size,$status)=split;
      $h{$user}{limit}=$limit;
      $h{$user}{fraction}=$fraction;
      $h{$user}{status}=$status;
    }
  }
  else {
    $monthupdate="Date: ".sprintf("%02d",$m+1).".".sprintf("%02d",$y+1900)."\n";
  }
close FF;

open (FF,">",$usertrafficlimit);
  print FF "$monthupdate"; 
  print FF "$warningusertrafficlimit";
  foreach $user (keys %vipuser) {
    $h{$user}{limit}=$vipuser{$user}{limit}; 
    if (!defined $h{$user}{size}) { $h{$user}{size}=1; }
  }
  foreach $user (keys %h) {
    if (!defined $h{$user}{limit}) { $h{$user}{limit}=$generaluserlimit; }
    if ((!defined $h{$user}{fraction}) or ($h{$user}{fraction}==1)) { $h{$user}{fraction}=1 ; }
    $h{$user}{status}="on";
    if ($h{$user}{fraction} < $h{$user}{size} / traffictobyte($h{$user}{limit})) { 
      $h{$user}{status}="BLOCK";
      $tb{$user}{size}=$h{$user}{size};
    }
    print FF ("$user\t") ;
    print FF ("$h{$user}{limit}\t");
    print FF ("$h{$user}{fraction}\t");
    print FF ("$h{$user}{size}\t");
    print FF ("$h{$user}{status}\n");
  }
close FF;
open (FF,">",$bannedhostsfile);
 print FF ("192.168.0.254\n");  
 foreach $user (keys %tb) {
    print FF ("$user\n");
 }
close FF;

exec '/usr/local/sbin/squid -k reconfigure'
---


Устанавливаем запуск utl.pl каждые 20 минут:
в файл /cf/conf/config.xml  , в конец секции </cron>: добавляем
<item>
<task_name>user_traffic_limit</task_name>
<minute>*/20</minute>
<hour>*</hour>
<mday>*</mday>
<month>*</month>
<wday>*</wday>
<who>root</who>
<command>/utl.pl</command>
</item>

Сохраняем и закрываем config.xml, удаляем файл /tmp/config.cache , перезагружаем pfSense!


    Все. Каждые 20 минут скрипт проверяет, сколько трафика израсходовали работники в текущем месяце и в случае нарушения условий установленного лимита в файле "usertrafficlimit" для IP нарушителя устанавливается статус BLOCK и его адрес добавляется в список banned hosts. Чтобы разрешить данному адресу доступ в Интернет необходимо или увеличить ему лимит или увеличить значение поля "fr" - коэффициент (отношение) установленного лимита и потребленного трафика.
    Для доступа к файлу usertrafficlimit и внесения в него изменений используется программа WinSCP.

    Конечно, в течении недели "продвинутые" сотрудники догадались, что в случае блокировки, можно просто изменить адрес и продолжить развлекаться, но ... если в pfsense установить привязку ip и MAC адресов и не разрешать доступ к сети не совпадающим адресам, то "продвинутый" сотрудник лишается не только Интернета, но и доступа в локальную сеть организации.

    Данный метод не претендует на "абсолютную истину", но для данной организации оказался самым простым и дешевым в реализации. 
    Удачи в решении Ваших задач...
Comments