Русский
Русский
English
Статистика
Реклама

Realtime

Создание автоматической системы борьбы с злоумышленниками на сайте (фродом)

29.08.2020 12:21:18 | Автор: admin
Последние примерно полгода я занимался созданием системы борьбы с фродом (fraudulent activity, fraud, etc.) без какой-либо начальной инфраструктуры для этого. Сегодняшние идеи, которые мы нашли и реализовали в нашей системе, помогают нам обнаруживать множество мошеннических действий и анализировать их. В этой статье я хотел бы рассказать о принципах, которым мы следовали, и о том, что мы сделали для достижения текущего состояния нашей системы, не углубляясь в техническую часть.


Принципы нашей системы


Когда вы слышите такие термины, как automatic и fraud, вы, скорее всего, начинаете думать о машинном обучении, Apache Spark, Hadoop, Python, Airflow и других технологиях экосистемы Apache Foundation и области Data Science. Я думаю, что есть один аспект использования этих инструментов, который, обычно, не упоминается: они требуют наличия определенных предварительных условий в вашей корпоративной системе, прежде чем начать их использовать. Короче говоря, вам нужна корпоративная платформа данных, которая включает озеро данных и хранилище. Но что, если у вас нет такой платформы, и вам все еще нужно развивать эту практику? Следующие принципы, о которых я рассказываю ниже, помогли нам достичь момента, когда мы можем сосредоточиться на улучшении наших идей, а не на поиске работающей. Тем не менее, это не плато проекта. В плане еще много вещей с технологической и продуктовой точек зрения.

Принцип 1: ценность для бизнеса в первую очередь


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

Принцип 2: Расширенный интеллект человека (augmented intelligence)


Бьюсь об заклад, большинство людей, которые не вовлечены глубоко в разработку решений машинного обучения, могут подумать, что замена людей это цель. На самом деле, решения машинного обучения далеки от совершенства и только в определенных областях возможна замена. Мы отказались от этой идеи с самого начала по нескольким причинам: несбалансированные данные о мошеннической деятельности и невозможность предоставить исчерпывающий список функций для моделей машинного обучения. В отличие от этого, мы выбрали вариант с расширенным интеллектом. Это альтернативная концепция искусственного интеллекта, которая фокусируется на вспомогательной роли ИИ, подчеркивая тот факт, что когнитивные технологии предназначены для улучшения человеческого интеллекта, а не для его замены. [1]

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

Принцип 3: платформа обширных аналитических данных


Самая сложная часть нашей системы это сквозная проверка рабочего процесса системы. Аналитики и разработчики должны легко получать наборы данных за прошлые периоды со всеми метриками, которые использовались для анализа. Кроме того, платформа данных должна предоставлять простой способ дополнить существующий набор показателей новым. Процессы, которые мы создаем, а это не только программные процессы, должны позволять легко пересчитывать предыдущие периоды, добавлять новые метрики и изменять прогноз данных. Мы могли бы добиться этого, накапливая все данные, которые генерирует наша производственная система. В таком случае данные постепенно стали бы помехой. Нам нужно было бы хранить растущий объем данных, которые мы не используем, и защищать их. В таком сценарии со временем данные будут становиться все более и более нерелевантными, но по-прежнему требуют наших усилий для управления ими. Для нас накопление данных (data hoarding) не имело смысла, и мы решили использовать другой подход. Мы решили организовать хранилища данных в реальном времени вокруг целевых сущностей, которые мы хотим классифицировать, и хранить только те данные, которые позволяют проверять самые последние и актуальные периоды. Сложность этих усилий заключается в том, что наша система является неоднородной с несколькими хранилищами данных и программными модулями, которые требуют тщательного планирования для согласованной работы.

Конструктивные концепции нашей системы


У нас есть четыре основных компонента в нашей системе: система приема (ingestion system), вычисления (computational), анализ (BI analysis) и система отслеживания (tracking system). Они служат для конкретных изолированных целей, и мы держим их изолированными, следуя определенным подходам в разработке.

image

Дизайн на основе контрактов


Прежде всего, мы договорились, что компоненты должны полагаться только на определенные структуры данных (контракты), которые передаются между ними. Это позволяет легко интегрировать между ними и не навязывать конкретный состав (и порядок) компонентов. Например, в некоторых случаях это позволяет нам напрямую интегрировать систему приема с системой отслеживания предупреждений. В таком случае это будет сделано в соответствии с согласованным контрактом на оповещения. Это означает, что оба компонента будут интегрированы с использованием контракта, который может использовать любой другой компонент. Мы не будем добавлять дополнительный контракт на добавление предупреждений в систему отслеживания из системы ввода. Такой подход требует использования заранее определенного минимального количества контрактов и упрощает систему и коммуникации. По сути, мы используем подход, который называется Contract First Design, и применяем его к контрактам потоковой передачи данных. [2]

Стриминг везде


Сохранение и управление состоянием в системе неизбежно приведет к усложнениям в ее реализации. В общем случае, состояние должно быть доступным из любого компонента, оно должно быть согласованным и предоставлять наиболее актуальное значение для всех компонентов, и оно должно быть надежным с правильными значениями. Кроме того, наличие вызовов к постоянному хранилищу для получения последнего состояния увеличит количество операций ввода-вывода и сложность алгоритмов, использующихся в наших конвейерах реального времени. Из-за этого мы решили убраться хранение состояния, по возможности, полностью из нашей системы. Этот подход требует включения всех необходимых данных в передаваемый блок данных (сообщение). Например, если нам нужно вычислить общее количество некоторых наблюдений (количество операций или случаев с определенными характеристиками), мы вычисляем его в памяти и генерируем поток таких значений. Зависимые модули будут использовать разбиение (partition) и пакетирование (batch) для дробления потока по сущностям и оперирования последними значениями. Этот подход устранил необходимость иметь постоянное дисковое хранилище для таких данных. Наша система использует Kafka в качестве брокера сообщений, и его можно использовать как базу данных с KSQL. [3] Но его использование сильно связало бы наше решение с Kafka, и мы решили не использовать его. Выбранный нами подход позволяет заменить Kafka другим брокером сообщений без серьезных внутренних изменений системы.

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

Проблемы нашей системы


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

  • Нам все еще необходимо определить процессы и политики, которые способствуют накоплению значимых и актуальных данных для нашего автоматического анализа, обнаружения и исследования данных.
  • Внедрение результатов анализа человеком в процесс автоматической настройки системы для её обновления на последних данных. Это не только обновление нашей модели, но также обновление процессов и улучшение понимания наших данных.
  • Нахождение баланса между детерминированным подходом IF-ELSE и ML. Кто-то сказал: ML это инструмент для отчаявшихся. Это означает, что вы захотите использовать ML, когда больше не понимаете, как оптимизировать и улучшить свои алгоритмы. С другой стороны, детерминированный подход не позволяет обнаружение аномалий, которые не были предвидены.
  • Нам нужен простой способ проверить наши гипотезы или коореляции между метриками в данных.
  • Система должна иметь несколько уровней истинно положительных (true positive) результатов. Случаи мошенничества это лишь часть всех случаев, которые можно считать положительными для системы. Например, аналитики хотят получать все подозрительные случаи для проверки, и лишь небольшая часть из них мошенничество. Система должна эффективно предоставлять аналитикам все случаи, независимо от того, является ли это реальным мошенничеством или просто подозрительным поведением.
  • Платформа данных должна позволять получать наборы данных за прошлые периоды с вычислениями, созданными и рассчитанными на лету.
  • Простое и автоматическое развертывание любого из компонентов системы как минимум в трех различных средах: производственной, экспериментальной (бета) и для разработчиков.
  • И последнее, но не менее важное. Нам необходимо создать обширную платформу проверки производительности, на которой мы сможем анализировать наши модели. [4]


Ссылки


  1. What is Augmented Intelligence?
  2. Implementing an API-First Design Methodology
  3. Kafka Transforming Into Event Streaming Database
  4. Understanding AUC ROC Curve
Подробнее..

Asterisk Как управлять мультидоменом в реалтайм не привлекая внимания санитаров

22.12.2020 02:11:35 | Автор: admin

О чем собственно речь?

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

Есть у нас, к примеру, несколько (ну ладно, не несколько) однотипных филиальчиков на 10-100 сотрудников. Организовать телефонию можно разными путями:

  • Взять готовое решение в облаке провайдера

  • Поставить на каждый филиал свой мини-сервер и астериск

  • Сделать единый астериск в центре

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

Если же мы хотим оптимизации, экономии, упростить администрирование, то рано или поздно мы приходим к тому, что сервер в центре будет наиболее удачным решением. Но, как только мы попытаемся стандартизировать номерной план, мы столкнемся с проблемой плоской нумерации. Мы начинаем добавлять префиксы, наращивать длину номеров, и вот у нас уже не 3х значная нумерация, а 6ти значная. Сотрудники все чаще поглядывают косо - чтобы набрать соседа надо нажать 6 кнопок ("6 кнопок Карл! Я сюда не кнопки нажимать пришел!"), а заявление босса что компания еще немного выросла и появился еще один филиал приводит к судорожному перебиранию записей на листочке в поисках свободного префикса в 6ти значной нумерации.

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

Попробуем разобраться, что это и как это настроить. Прежде всего, домен - это просто текстовая строка, но которую мы можем материализовать в виде днс-имени. А можем и не материализовать и оставить просто строкой. Как правильно? Ну как говорится, все сильно зависит от задачи - работать будет и так и так, но если много софтфонов, то лучше сделать ДНС - на одно поле меньше придется заполнять при настройке.

Тестировать будем астериск 18.1 со стеком PJSIP в связке с реалтайм базой данных.

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

Поэтому начнем с создания удобной для нас таблички (Numbers), без оглядки на мануалы. И так, что мы должны знать об абоненте?

  • Конечно же, его номер (Number)

  • Пароль тоже нужен - вдруг это враг? (Secret)

  • Домен - ну раз тема про домен, очевидно, он тоже должен быть (domain)

  • CID - мы ж хотим чтобы он как то отображался при звонке

  • Опционально: протокол, DTMF, пикап-группа

  • Поле Flags - свои флаги-опции, которые станут канальными переменными. Адептов полного реалтайм просьба расслабиться, канальные переменные в разы быстрее реалтайма, поэтому такие флаги могут сильно сократить нагрузку на базу данных.

Будем считать, что табличка у нас есть. Что дальше? Ок, такую табличку астериск не прочитает, увы. Но у базы данных есть секретное оружие - вьюшки, старые добрые вьюшки, VIEW. Они позволяют взять данные, в удобном для нас виде, и представить их в удобном виде для астериска. Всего нам потребуется 3 шт:

  • Endpoints (ps_endpoints)

  • Авторизация (ps_auths)

  • Aors (ps_aors)

Это минимальный набор, по вкусу в это блюдо можно добавить и другие, для реалтайма или "волшебства" PJSIP. Позволю себе побыть лентяем и дам просто готовый код SQL:

/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;/*!40101 SET NAMES utf8 */;/*!50503 SET NAMES utf8mb4 */;/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;CREATE ALGORITHM=UNDEFINED SQL SECURITY DEFINER VIEW `ps_aors` AS select concat(`Numbers`.`Number`,if(`Numbers`.`domain` is not null,concat('@',`Numbers`.`domain`),'')) AS `id`,180 AS `default_expiration`,2 AS `max_contacts`,30 AS `minimum_expiration`,'yes' AS `remove_existing`,'' AS `contact` from `Numbers`;CREATE ALGORITHM=UNDEFINED SQL SECURITY DEFINER VIEW `ps_auths` AS select concat(`Numbers`.`Number`,if(`Numbers`.`domain` is not null,concat('@',`Numbers`.`domain`),'')) AS `id`,'userpass' AS `auth_type`,3600 AS `nonce_lifetime`,`Numbers`.`Secret` AS `password`,`Numbers`.`Number` AS `username`,`Numbers`.`domain` AS `realm` from `Numbers` where `Numbers`.`Secret` <> '';CREATE ALGORITHM=UNDEFINED SQL SECURITY DEFINER VIEW `ps_endpoints` AS select concat(`Numbers`.`Number`,if(`Numbers`.`domain` is not null,concat('@',`Numbers`.`domain`),'')) AS `id`,concat('transport-',lcase(`Numbers`.`Protocol`)) AS `transport`,concat(`Numbers`.`Number`,if(`Numbers`.`domain` is not null,concat('@',`Numbers`.`domain`),'')) AS `aors`,if(`Numbers`.`Secret` <> '',concat(`Numbers`.`Number`,if(`Numbers`.`domain` is not null,concat('@',`Numbers`.`domain`),'')),NULL) AS `auth`,'SIP' AS `context`,'all' AS `disallow`,concat('ulaw,alaw,opus',if(find_in_set('Video',`Numbers`.`Flags`),',h263,h261,h263p,h264','')) AS `allow`,concat(`Numbers`.`Number`,if(`Numbers`.`domain` is not null,concat('@',`Numbers`.`domain`),'')) AS `outbound_auth`,concat(`Numbers`.`CID`,' <',`Numbers`.`Number`,'>') AS `callerid`,if(`Numbers`.`Protocol` = 'TLS','sdes','no') AS `media_encryption`,`Numbers`.`PickupGroup` AS `named_pickup_group`,2 AS `device_state_busy_at`,concat('vRecord=',if(find_in_set('Record',`Numbers`.`Flags`) > 0,'yes','no'),';','vRussia=',if(find_in_set('Russia',`Numbers`.`Flags`) > 0,'yes','no'),';','vAbroad=',if(find_in_set('Abroad',`Numbers`.`Flags`) > 0,'yes','no'),';','vVoicemail=',if(find_in_set('Voicemail',`Numbers`.`Flags`) > 0,'yes','no'),';','vFilterCID=no') AS `set_var`,`Numbers`.`NumberID` AS `callerid_tag`,'username,auth_username,ip' AS `identify_by` from `Numbers`;/*!40101 SET SQL_MODE=IFNULL(@OLD_SQL_MODE, '') */;/*!40014 SET FOREIGN_KEY_CHECKS=IF(@OLD_FOREIGN_KEY_CHECKS IS NULL, 1, @OLD_FOREIGN_KEY_CHECKS) */;/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;

Здесь немного поясню. Решил немного усложнить задачу и дополнить условием - одновременно должно существовать два типа нумерации - "глобальная" и "местная":

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

  • Местная, пусть это будет 3х знак, некоторый универсальный номерной план внутри филиала, номера могут повторяться от филиала к филиалу, что позволяет нам сделать стандарт, типа, у директора филиала номер всегда 101, главного бухгалтера 102, зав склада по прозвищу "Вжик" 007 и т.д.

Ок, теперь заполним таблицу Numbers некоторыми тестовыми данными и взглянем на наши вьюшки.

Таблица Numbers:

Отлично, "входная точка для админа" выглядит вполне хорошо, минимум полей, максимум наглядности.

ps_endpoints

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

ps_auth

ps_aors

Так, выглядит замечательно. Более того, править будем только таблицу Numbers, поэтому самое время выдохнуть с облегчением.

Думаю многие уже догадались, где скрывается домен. Для тех кто пока еще не понял - посмотрите на столбцы id. Для 5ти знака, там записан просто номер - потому что мы не связываем с каким либо определённым доменом эту нумерацию. А вот для 3х знака мы прописываем после номера через @ домен. Дело в том, что астериск сначала ищет номер и авторизацию с доменом, и если такой в таблице нет - без.

Хочу акцентировать внимание на еще одном моменте. Он не очевиден, и многие спотыкаются и бросают эту тему. Имя AOR для endpointa ДОЛЖНО содержать домен, т.е. быть в формате номер@домен, если номер указан с доменом. Дело в том, что если в номере (id) присутствует домен, то астериск будет искать AOR с доменом. Это захардкорено в код. Еще раз - дать произвольное имя для AOR НЕ получится, это просто не будет работать. Нюанс, однако.

И так, почти все готово, давайте быстро пробежим по настройке связки астериска с базой.

pjsip.conf

Тут давайте немного тормознем, и посмотрим на опции.

disable multi domain

Это опция интересна тем, что по умолчанию она no. Серьезно, чаще всего инсталляции идут без поддержки домена. Но значение по-умолчанию дает астериску команду проверять домены. Если у Вас нет доменов, не поленитесь, поставьте здесь yes. Это сэкономит по 2 обращения на каждую авторизацию и каждый инвайт. База скажет вам спасибо.

endpoint identifier order

Эта опция интересна тем, что правильная ее настройка, экономит нам такты процессора. Значение по умолчанию у ней = ip,username. В такой конфигурации, астериск заточен на работу с транками без авторизации, т.е. когда авторизация идет по IP адресу. Если же у нас высоконагруженный сервер регистрации, мы можем вперед выставить username, что позволит астериску находить нужный endpoint на пару десятков тактов процессора быстрее.

по опциям все, погнали дальше по конфигам.

res config mysql.conf

Тут все очень просто, так что просто листинг - тестовая база стоит локально в этом примере:

extconfig.conf

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

ps_contacts можно не связывать, она прекрасно будет себя чувствовать и в astdb.

Spoiler

Наблюдательный читать обратил внимание на некоторый идентификатор CDR. Это один из моих экспериментов, по написанию модуля для выгрузки статистики звонков через механизмы реалтайм. Если заинтересовало, велком на GitHub

sorcery.conf

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

Перезапускаем астериск.

Собственно, где результат?

Если все сделано правильно, то у нас должны отобразиться такие endpoint в консоли астериска:

Не буду приводить всю портянку, суть видна на скрине и так. "Глобальные номера" у нас как и обычно без домена. А вот "местная нумерация" вся идет с доменном.

Обращаю внимание, что в диалплане нужно учитывать домен. Т.е., на 5ти знаки:

Dial(PJSIP/19960)

а вот на 3х знаки

Dial(PJSIP/123@test3)

Диалплан получается немного хитрый, но тем не менее, реализовать его не сложно.

Как зарегистрировать абонента?

Microsip, настройки будут выглядеть вот так:

Теперь каждый филиал может иметь свой номер 101 для директора. Разве это не прекрасно?

Подробнее..
Категории: Asterisk , Voip , Mysql , Realtime , Домены

Полку ARM прибыло представлен первый 64-битный процессор ARM Cortex-R82

06.09.2020 20:21:10 | Автор: admin

ARM-процессоры занимают новые для себя ниши. Это уже давно не только маломощные чипы с минимальным энергопотреблением. Недавно анонсировано третье поколение ARM-процессоров Thunder-X, о чем мы уже рассказывали. Теперь представлен процессор, который превосходит все предыдущие решения в серии R.

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



Для того, чтобы процессор мог выполнять все требуемые операции, его архитектуру несколько модернизировали и обновили. Так, Cortex-R характеризуется улучшенной аппаратной обработкой прерыванией, включая детерминированную. Были усовершенствованы аппаратные инструкции деления, защита памяти (MPU), коррекция ошибок на всех уровнях (включая как системные шины, так и кеш L1). Есть и еще одна особенность мгновенное горячее резервирование на тот случай, если выходит из строя одно из ядер.


Чип будет работать в системах с оптимизированным ПО, которое экономно использует аппаратные ресурсы. Это не пользовательские приложения. Поэтому разработчики сделали представителей серии R производительными, но без рекордных показателей, как у серии А. Напомним, что чипы серии А используются как в смартфонах, так и в серверах.

Новые процессоры ARM серии R планируется использовать, в частности, в compute-on-storage drives. Это умные накопители, которые способны выполнять сложные задачи в автономном режиме, не нагружая ими хост-системы. Соответственно, производительность хост-систем при выполнении тех же задач будет выше.

Разработчики утверждают: серия Cortex-R может брать на себя такие задачи, как транскодирование видео на лету, ускорение работы с базами данных или анализ информации в режиме реального времени. R82, по мнению разработчиков, в два раза превосходит R8 по производительности. В типовых же нагрузках при работе с нейросетями новые процессоры превосходят коллег в 14 раз.


Благодаря MMU процессор имеет возможность работать с виртуальной памятью без ограничения физическим объемом DRAM. Кроме того, R82 опционально поддерживает и выполнение специфических SIMD-инструкций (ARM NEON), обеспечивая параллелизм на уровне данных.

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

Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru