Наши партнеры

UnixForum





Библиотека сайта rus-linux.net

Гибкий механизм управления доступом к сетевым ресурсам на основе прокси-сервера Squid

Оригинал: Flexible Access Control with Squid Proxy
Автор: Mike Diehl
Дата публикации: 23 марта 2015 г.
Перевод: А.Панин
Дата перевода: 1 сентября 2015 г.

Гибкий механизм управления доступом к сетевым ресурсам на основе прокси-сервера Squid

Большие предприятия и лаборатории, работающие с радиоактивными материалами, не являются единственными организациями, в которых должна быть введена политика ограничения доступа к интернет-ресурсам, а также должны использоваться различные средства для ее реализации. В моем доме также введена политика ограничения доступа к интернет-ресурсам, а технология ее реализации должна быть доступна практически каждой организации. В нашем случае я не очень беспокоюсь по поводу внешних угроз безопасности систем. Наша сеть находится за маршрутизатором с NAT, а в беспроводной сети используется удивительно простой пароль. Наши рабочие станции работают под управлением либо дистрибутивов Linux, либо Windows со всеми обновлениями (если, конечно, это как-то помогает). В отличие от большинства случаев, наши опасения исходят из нашей сети: наши дети очень любят играть в сетевые игры, которые обычно мешают им выполнять домашние задания.

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

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

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

  1. Я не хочу, чтобы управление доступом моих детей к сети Интернет стало моей повседневной работой. Я хочу иметь возможность устанавливать политику доступа, которую можно предварительно создать.

  2. Моя жена не хочет разбираться в том, как войти в систему, модифицировать файл конфигурации и перезапустить демон прокси-сервера. Она хочет иметь возможность перейти по указанному адресу с помощью веб-браузера, установить несколько флажков и заняться своими делами.

  3. Я не хочу писать слишком много кода. Я не против написания небольшого объема кода, но я не слишком заинтересован в изобретении уже существующего колеса.

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

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

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

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

Мне не удалось найти какой-либо программный пакет, который являлся бы достаточно гибким для решения описанной мной задачи, а также настолько простым в использовании, что у меня и моей жены не возникало бы проблем при работе с ним. Я выяснил, что прокси-сервер Squid имеет потенциал в плане выполнения описанных действий при условии написания небольшого объема дополнительного кода. Мой код должен сообщать прокси-серверу о том, как обрабатывать каждый из запросов по мере его поступления. Прокси-сервер должен либо выполнить запрос пользователя, либо отправить пользователю веб-страницу с указанием на то, что сайт, на который пытается войти пользователь, заблокирован. Таким образом прокси-сервер должен работать при использовании любой из наших политик.

Я решил, что имеет смысл предоставить каждому члену семьи один из четырех уровней доступа к сети Интернет. Двумя наиболее "радикальными" уровнями доступа является уровень "открытый доступ" ("open"), обладающие которым члены семьи могут переходить на любой ресурс, на который они пожелают, и уровень "заблокированный доступ" ("blocked"), обладающие которым члены семьи не могут перейти ни на какой из ресурсов сети Интернет. К примеру, моя жена и я имеем уровень открытого доступа к ресурсам глобальной сети. Если же один из наших мальчиков отстранен от использования сети Интернет, мы просто устанавливаем для него уровень "заблокированный доступ".

Однако, было бы неплохо также иметь возможность разрешить нашим детям переходить на сайты из заранее составленного списка, к примеру, в учебных целях. В этом случае нам понадобится уровень доступа "на основе белого списка". Наконец, я рассматривал вопрос создания уровня доступа "с фильтрацией", который позволял бы осуществлять более гибкое ограничение доступа к ресурсам и блокировать такие ресурсы, как ресурсы с музыкальными файлами, флэш-играми и апплетами Java. Этот уровень доступа, который должен использоваться нашими мальчиками в большинстве случаев. Впоследствии мы можем сказать "хватит игр" и использовать прокси-сервер для принудительного использования соответствующей политики доступа.

Ввиду того, что я не хотел разрабатывать интерфейс для управления прокси-сервером, я использовал веб-приложение phpMyAdmin для обновления значений в базе данных и установки соответствующей политики доступа к ресурсам (Рисунок 1). Для установки определенного уровня доступа я просто обновляю значение в соответствующей ячейке таблицы, причем значение 1 соответствует активации политики, а значение 0 - ее деактивации.

Интерфейс веб-приложения phpMyAdmin, используемый для изменения политики доступа к сетевым ресурсам

Рисунок 1. Интерфейс веб-приложения phpMyAdmin, используемый для изменения политики доступа к сетевым ресурсам

Использование политики доступа к сетевым ресурсам также потребует настройки клиента, о которой я и расскажу сейчас. Однако, я также хочу рассказать об использовании системы OpenDNS в качестве инструмента для фильтрации сетевых ресурсов, на тестирование которого я предпочел не тратить свое время. Это хороший пример системы глубокой фильтрации сетевых ресурсов.

Я настроил фильтр OpenDNS для фильтрации ресурсов, в отношении которых у меня уже сложилось однозначное мнение. Я не считаю, что существует какая-либо причина, по которой члены моей семьи должны иметь доступ к сайтам знакомств, игорным сайтам или сайтам для взрослых (Рисунок 2). Хотя система OpenDNS не является идеальной, ее разработчики делают огромную работу в плане фильтрации описанных сайтов без необходимости дополнительного тестирования со стороны пользователя. В том случае, если тестирование завершится неудачей, могут возникнуть неловкие моменты - я же просто предположу, что данная система отлично работает.

Система OpenDNS позволяет без лишних сложностей фильтровать сетевые ресурсы

Рисунок 2. Система OpenDNS позволяет без лишних сложностей фильтровать сетевые ресурсы

Ранее в данной статье я упоминал о том, что для использования описанной системы потребуется настройка клиентского программного обеспечения. Большинство веб-браузеров позволяют указать параметры для доступа к прокси-серверу, который будет использоваться для доступа к ресурсам сети Интернет. Исходя из этого, наиболее простой вариант настройки веб-браузера будет заключаться в активации режима доступа к сетевым ресурсам посредством прокси-сервера с помощью установки соответствующего флажка. Однако, в том случае, если мои дети возьмут свои ноутбуки с собой в библиотеку, в сети которой не используется прокси-сервер, они не смогут получить доступ к ресурсам сети Интернет из нее, что входит в разрез с сформулированным мной ранее требованием номер пять. Исходя из этого, я принял решение об использовании механизма автоматической настройки доступа через прокси-сервер, который поддерживается большинством современных веб-браузеров. Для использования данного механизма мне потребуется разработать простую функцию JavaScript, которая будет использоваться для выбора метода доступа к веб-сайтам, либо напрямую, либо посредством прокси-сервера (Листинг 1).

Листинг 1. Сценарий для автоматической настройки доступа через прокси-сервер

 1  function FindProxyForURL(url, host) {
 2
 3      if (!isResolvable("proxy.example.com") {
 4              return "DIRECT";
 5      }
 6
 7      if (shExpMatch(host, "*.example.com")) {
 8              return "DIRECT";
 9      }
10
11      if (isInNet(host, "10.0.0.0", "255.0.0.0")) {
12              return "DIRECT";
13      }
14
15      return "PROXY 10.1.1.158:3128; DIRECT";
16  }

Каждый раз, когда ваш веб-браузер получает команду перехода на веб-сайт, он вызывает функцию FindProxyForURL() для выбора метода, который будет использоваться для доступа к этому веб-сайту: напрямую или через прокси-сервер. Функция, показанная в Листинге 1 является всего лишь примером, но при этом она демонстрирует несколько методов работы с механизмом автоматической настройки доступа через прокси-сервер, о которых стоит упомянуть. Как вы можете увидеть в строке 15, вы можете возвращать разделенный символами точки с запятой список методов доступа к ресурсу. Ваш веб-браузер попытается использовать их в порядке следования для получения данных с ресурса. В данном случае при недоступности прокси-сервера будет использован идентификатор метода доступа DIRECT, соответствующий непосредственному доступу к необходимому веб-сайту. В более ограниченном окружении данная политика может оказаться неприемлемой.

В строке 11 вы можете обнаружить проверку для непосредственного доступа к ресурсам из нашей локальной сети. В строке 7 я демонстрирую методику проверки имен определенных узлов. Существует несколько веб-сайтов, на которые я захожу через VPN-туннель с моей рабочей станции, из-за чего я не могу использовать прокси-сервер. Наконец, в строке 3 вы можете увидеть кое-что интересное. В данной строке я проверяю, возможно ли преобразование заданного имени узла в IP-адрес. Я настроил сервер DNS нашей локальной сети для разрешения этого доменного имени, а любой другой сервер не сможет осуществить его разрешение. Таким образом, если наши дети заберут свои ноутбуки из нашей сети, их веб-браузеры не будут пытаться использовать наш прокси-сервер. Разумеется, мы можем просто установить приоритет режима непосредственного доступа к сетевым ресурсам таким образом, как это сделано в строке 15, но при этом будет тратиться достаточно много времени в случае невозможности установки соединений посредством прокси-сервера.

Механизм автоматической настройки доступа через прокси-сервер является механизмом, который может обойти любой квалифицированный пользователь. При этом существуют дополнения для многих веб-браузеров, позволяющие запретить пользователю изменять их настройки. Однако, эти дополнения не запрещают пользователям устанавливать новые веб-браузеры или запускать веб-браузер Firefox с новым профилем. Простейший метод противодействия обходу данного механизма заключается в настройке маршрутизатора: нужно создать всего лишь одно правило межсетевого экрана, которое позволит предотвратить доступ к ресурсам сети Интернет со всех IP-адресов за исключением IP-адреса системы с прокси-сервером. При необходимости данное правило может быть расширено для разрешения доступа с отдельных клиентских машин и узлов.

В процессе добавления правил межсетевого экрана на уровне вашего маршрутизатора у вас может возникнуть желание настроить маршрутизатор для передачи всего веб-тафика через прокси-сервер, создав так называемый прозрачный прокси-сервер. Однако, в соответствии со стандартом RFC 3143, данная конфигурация не является рекомендуемой, так как она препятствует корректной работе механизмов веб-браузера для добавления данных в кэш и сохранения истории посещений ресурсов.

После обсуждения настроек клиентских приложений, сервера DNS и возможных настроек маршрутизатора, пришло время рассмотреть настройки прокси-сервера Squid. Процесс установки прокси-сервера полностью очевиден. Я просто использовал систему управления пакетами программного обеспечения своего дистрибутива, которую не стоит обсуждать в рамках данной главы. Прокси-сервер Squid предоставляет множество настроек, с помощью которых вы можете оптимизировать работу его механизма кэширования, а также скорость работы вашего соединения с сетью Интернет. Несмотря на то, что оптимизации производительности значительно влияют на работу прокси-сервера, их обсуждение выходит за рамки данной дискуссии. Таким образом, остается одна модификация файла конфигурации прокси-сервера, которую необходимо осуществить для интеграции разработанного мною кода в систему. Все, что нам требуется - это отредактировать файл /etc/squid/squid.conf, добавив в него единственную строку:

redirect_program /etc/squid/redirector.pl

Данная директива сообщает прокси-серверу Squid о необходимости "спрашивать" у моей программы о том, как обрабатывать каждый из отправленных клиентом запросов. Логика работы упомянутой программы достаточно проста:

  1. Ожидать запросов в потоке стандартного ввода STDIN.

  2. Разобрать полученный запрос.

  3. Принять решение на основе используемой политики.

  4. Вернуть ответ прокси-серверу.

Давайте рассмотрим исходный код программы, приведенный в Листинге 2.

Листинг 2. Программа для перенаправления запросов через прокси-сервер

 1  #!/usr/bin/perl
 2
 3  use DBI;
 4
 5  $blocked = "http://192.168.1.10/blocked.html";
 6
 7  my $dbh = DBI->connect("dbi:mysql:authentication:host=
 192.168.1.10", "user", "password") || die("Can\'t 
  connect to database.\n");
 8
 9  $|=1;
10
11  while (<STDIN>) {
12          my($sth, $r, $c);
13          my($url, $client, $d, $method, $proxy_ip, $proxy_port);
14
15          chomp($r = $_);
16
17          if ($r !~ m/\S+/) { next; }
18
19          ($url, $client, $d, $method, $proxy_ip, $proxy_port) 
              = split(/\s/, $r);
20
21          $client =~ s/\/-//;
22          $proxy_ip =~ s/myip=//;
23          $proxy_port =~ s/myport=//;
24
25          $sth = $dbh->prepare("select * from web_clients 
              where ip=\'$client\'");
26          $sth->execute();
27          $c = $sth->fetchrow_hashref();
28
29          if ($c->{blocked} eq "1") {
30                  send_answer($blocked);
31                  next;
32          }
33
34          if ($c->{whitelist_only} eq "1") {
35                  if (!is_on_list("dom_whitelist", $url)) {
36                          send_answer($blocked);
37                          next;
38                  }
39          }
40
41          if ($c->{filtered} eq "1") {
42                  if ($c->{games} eq "0") {
43                          # Check URL to see if it's 
                              on our games list
44                  }
45
46                  if ($c->{flash} eq "0") {
47                          # Check URL to see if it looks 
                               like flash
48                  }
49
50                  send_answer($url);
51                  next;
52          }
53
54          if ($c->{open} eq "1") {
55                  send_answer($url);
56                  next;
57          }
58
59          send_answer($url);
60          next;
61  }
62
63  exit 0;
64
65  #############################################################
66
67  sub     send_answer {
68          my($a) = @_;
69          print "$a\n";
70  }
71
72  sub     is_on_list {
73          my($list, $url) = @_;
74          my($o, @a, $i, @b, $b, $sth, $c);
75
76          $url =~ s/^https*:\/\///;
77          $url =~ s/^.+\@//;
78          $url =~ s/[:\/].*//;
79
80          @a = reverse(split(/\./, $url));
81
82          foreach $i (0 .. $#a) {
83                  push(@b, $a[$i]);
84                  $b = join(".", reverse(@b));
85
86                  $sth = $dbh->prepare("select count(*) from 
                      $list where name=\'$b\'");
87                  $sth->execute();
88                  ($c) = $sth->fetchrow_array();
89
90                  if ($c > 0) { return $c; }
91          }
92
93          return $c+0;
94  }
95

Код главного цикла обработки событий начинается в строке 11, где осуществляется чтение данных из стандартного потока ввода STDIN. Строки 11-24 связаны главным образом с разбором запроса, переданного прокси-сервером Squid. В строках 25-28 приведен код, с помощью которого программа отправляет запрос, направленный на получение информации о правах определенного клиента, серверу базы данных. В строках 29-57 приведен код для обработки и применения информации о правах пользователя, полученной из базы данных. Для случая, когда клиент имеет уровень доступа к сети Интернет "с фильтрацией", я также разработал прототип кода с множеством логических операций. Я не хотел бы перегружать данную статью тривиальным кодом, поэтому не буду приводить его. На мой взгляд, гораздо важнее продемонстрировать структуру и общую логику программы для перенаправления запросов через прокси-сервер Squid, чем приводить полный код этой программы. Далее я продемонстрирую методику реализации практически любой мыслимой политики доступа к сетевым ресурсам с помощью нескольких строк кода и регулярных выражений.

Функция send_answer(), начинающаяся со строки 67, на самом деле не выполняет какой-либо работы в данный момент, но в будущем я смогу достаточно просто добавить в нее код для записи информации об обрабатываемых запросах в файл журнала.

Функция is_on_list(), начинающаяся со строки 72, скорее всего, будет более интересной. Данная функция принимает имя сетевого ресурса, на который пытается перейти клиент, и разделяет его на список поддоменов. После этого в данная функция осуществляет проверку этих поддоменов на наличие в таблице базы данных, имя которой передано в качестве параметра. Таким образом, я могу просто поместить имя ресурса example.com в базу данных, после чего данная функция будет устанавливать наличие в базе данных, к примеру, таких доменов и поддоменов, как example.com, www.example.com или webamail.eaxample.com.

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

Как вы видите, код в действительности не является достаточно сложным. Но, после его небольшого усложнения, у меня появится возможность принудительного использования практически любой вообразимой политики доступа к сетевым ресурсам. Однако, существует одна область, в которой программа требует усовершенствования. Как было сказано ранее, программа обращается к базе данных по нескольку раз при обработке каждого из переданных ей запросов доступа к ресурсу. Это ужасно неэффективно и, к моменту, когда вы будете читать данную статью, я наверняка разработаю какой-либо механизм кэширования данных.

Однако, кэширование данных также сделает систему менее отзывчивой как к изменениям политики доступа или списков контроля доступа к сетевым ресурсам, так и к необходимости ожидания истечения времени действия кэшированных данных или принудительного перезапуска демона прокси-сервера.

В ходе практического использования системы я обнаружил один эффект, о котором стоит упомянуть. В большинстве веб-браузеров реализованы механизмы кэширования данных. Ввиду наличия данного механизма кэширования, при изменении политики доступа к сетевым ресурсам на уровне прокси-сервера, ваши клиентские системы не всегда будут использовать введенный в строй прокси-сервер. При "открытии" доступа к ранее заблокированным ресурсам сети через прокси-сервер вашим пользователям придется обновить кэш своих веб-браузеров для того, чтобы получить доступ к ранее заблокированному содержимому сетевых ресурсов. При ограничении доступа к сетевым ресурсам содержимое этих ресурсов может быть все также доступным до момента истечения срока хранения кэшированных данных. Одно из решений заключается в установке нулевого объема локального хранилища кэшированных данных и использовании исключительно кэша данных на уровне прокси-сервера.

Также, ввиду того, что клиенты были настроены для работы через прокси-сервер при подключении к локальной сети, появляется возможность использования различных прокси-серверов или даже цепочек прокси-серверов без необходимости осуществления каких-либо манипуляций на стороне клиентов. Это обстоятельство открывает возможность использования таких демонов, как, например, Dan's Guardian, предназначенный для фильтрации передаваемых по сети данных в дополнение к управлению доступом к сетевым ресурсам.

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