Simult Chek

Материал из noname.com.ua
Перейти к навигацииПерейти к поиску

Описание проблемы

В сети используется VPN (accel-pptp) с авторизацией на центральном radius-сервере (freeradius). При попытке авторизации 2 и более клиентов с одинаковыми UserName может возникнуть ситуация когда пытающиеся авторизоваться (или часть из них) пройдут авторизацию успешно.

Это связано с особенностью провеки одновременности подключений - запрос

simul_count_query = "SELECT COUNT(*) FROM ${acct_table1} WHERE UserName=\'%{SQL-User-Name}\' AND AcctStopTime = 0"

проверяет тольк наличие сессий в таблице acct_table1 (обычно radacct). Сессии в таблице acct_table1 создаются только при получении Acct-пакета от NAS-а (VPN-серверов в моем случае ). В результате, возможна ситуация когда из-за различных причин, как например, нагрузка NAS-a или потерь в сети, в результате возникает некоторый промежмежуток времени, в течении которого возможно авторизоваться повторно.


Следующяя схема илюстрирует эту ситуацию: С UserName=test пробуют соединиться 2 различных клиента.

mysql> select * from radcheck where username='test';
+-------+-----------+------------------+----+-----------+
| id    | UserName  | Attribute        | op | Value     |
+-------+-----------+------------------+----+-----------+
|  9295 | test      | Pool-Name        | := | ippool_1  |
|  9294 | test      | Password         | == | password  |
|  9293 | test      | Auth-Type        | := | MS-CHAP   |
|  9296 | test      | Simultaneous-Use | := | 1         |
+-------+-----------+------------------+----+-----------+
4 rows in set (0.00 sec)
Время ("кванты")Первый клиент (UserName=test)Второй клиент (UserName=test)Radius-Сервер
1Установка соединения (ppp)
2Запрос к радиусу Auth-RequestУстановка соединения (ppp)Получение запроса от Клиента 1, проверка атрибутов, Вычисление значения Simultaneous-Use (=1 т.к. пользователь не был подключен, отправка Access-Accept на NAS)
3Получение Access-Accept, авторизация клиента, создание интерфейса, и т.п.Запрос к радиусу Auth-RequestПолучение запроса от Клиента 2, проверка атрибутов, Вычисление значения Simultaneous-Use (=1 т.к. сессия первого клиента еще не попала в acct_table1)
4NAS формирует пакет Acct-Start, задержка с отправкой из-за нагрузки на CPUПолучение Access-Accept, авторизация клиента, создание интерфейса, и т.п.Ожидание Acct-Start от NAS
5Отправка Acct-StartОтправка Acct-StartЗанесенеее 2 сессий в acct_table1

Несмотря на то, что на первый взгляд такая ситуация кажется маловероятной, это совсем не так. Я толкнулся в своей сети с тем, что абоненты согласовывая (вероятно, по телефону) время включения, использовали 1 аккаунт 2 раза. В тестовых условиях при подключении нескольких компьютеров в одной комнате удавалось подключить 4 одновременных сессии с одним UserName.

Варианты решения

Простой

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

Для внесения задержки можно модифицировать запрос

authorize_check_query = "SELECT id, UserName, Attribute, Value, op \
 FROM ${authcheck_table} \
 WHERE Username = '%{SQL-User-Name}' \
 ORDER BY id"

следующим образом

authorize_reply_query = "SELECT id+sleep(FLOOR(0 + (RAND() * 10))), UserName, Attribute, Value, op \
 FROM ${authreply_table} \
 WHERE Username = '%{SQL-User-Name}' \
 ORDER BY id"

Этот запрос будет выполняться с задержкой 0-10 секунд.

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

Однако, присутвуют следующие недостатки:

  1. Существует вероятность отличная от нуля что функция RAND() вернет 2 раза одинаковые значения и запросы все же остануться "одновременными".
  2. Внесение задержки создает неудобства для абонентов (были жалобы).
  3. Слишком большое значение множителя ( более 10 секуд) может приводить к ошибкам авторизации по таймауту.

Под "одновременными" запросами авторизации подразумевается что времени между запросами окажется недостаточно для внесения данных в acct_table1 по той или инной причине.