Привет! Меня зовут Александр Савельев, я занимаюсь front-end разработкой в проекте Solar WebProxy. Это приложение класса Secure Gateway – шлюз безопасности, дополненный функциями глубокого анализа, фильтрации веб-трафика, прокси, разграничения пользовательских прав. Наше решение присутствует на рынке с 2019 года. В среднем мы выпускаем по четыре релиза в год. Продукт внесен в реестр отечественного ПО и имеет сертификат ФСТЭК России. Solar webProxy отличается высокой производительностью, подтвержденной в рамках участия в проекте ПАО «Ростелеком» и Минцифры по созданию ЕСПД для школ – единственное в России внедрение SWG федерального масштаба на 1 000 000 пользователей в образовательные учреждения. Решение также успешно применяется в крупном бизнесе в сферах банкинга, металлургии и телекоммуникаций.
Что такое политики Solar webProxy и как они формировались раньше
Хотя приложение в большей степени является backend-ориентированным, важно чтобы и UI был удобным для администратора. В версии 4.2, которая вышла на рынок 5 декабря, одним из ключевых направлений работы стала переработка конструктора политик для улучшения пользовательского опыта.
Что же из себя представляет конструктор политик? В Solar webProxy – это раздел панели администратора, где можно создать или отредактировать набор правил фильтрации трафика, разделенных по типам слоев – фильтрация запросов, фильтрация ответов, маршрутизация соединений и т.д.
Для каждого правила, в зависимости от его типа, можно указать фильтрующие данные (т.е. данные, на основе которых применяется то или иное правило) и действия, которые необходимо произвести. Все эти данные можно отредактировать в редакторе правил.
Для источников, назначений и некоторых других фильтров в правилах можно создавать списки и справочники (например, списки ресурсов или IP-адресов), для которых предусмотрен отдельный раздел политик. В дальнейшем внутри правила можно использовать тот или иной справочник, что позволяет уменьшить объем работы администратора безопасности на внесение данных в систему.
Все это работало, однако некоторые пользователи отмечали возможности для улучшения UX. Например:
Пагинация объектов и справочников происходила на стороне front-end, вследствие чего при большом объеме справочника требовалось несколько секунд для загрузки всего списка, плюс ко всему из-за этого действовало ограничение на объем списка в 10 тысяч объектов.
Объекты и справочники могли быть отредактированы только на отдельной странице, из-за чего администратору приходилось при редактировании правила открывать отдельную страницу со справочником, вносить там необходимые изменения и затем возвращаться на страницу с правилами и перезагружать ее для применения изменений.
Что внутри?
Внутри front-end Solar webProxy представляет собой Angular приложение 12-й версии и если для пользователя все вышеперечисленные аспекты выливались в неудобства в UX, то с точки зрения кода применялись архитектурные решения не свойственные для Angular-приложений. Например, каждая таблица с правилами или справочниками представляла собой Angular-компонент, унаследованный от другого корневого компонента-таблицы-родителя. Если типы правил были очень близки между собой по структуре, то компонент для нового слоя наследовался от аналогичного, что в итоге приводило к усложнению поддержки кода. Например, если требовалось добавить какой-то фильтр в один тип слоя, то при малейшей невнимательности разработчика происходил «импакт» на другие слои политик и отследить это было очень сложно.
В целом, весь проект представлял собой legacy-код с множеством deprecated-зависимостей, что в итоге приводило к ряду трудностей:
осложненная поддержка приложения из-за использования нестандартных для Angular архитектурных решений
при разработке новой фичи для одного типа слоя мог быть поломан, либо внесен какой-то незапланированный «импакт» в другой слой
из-за большого количества deprecated-зависимостей было невозможно обновить angular до актуальной версии
слабая производительность front-end как из-за огромного количества данных, загружаемых со стороны back-end, так и из-за отсутствия какой-либо оптимизации (использовалась стратегия изменений Default, не использовались преимущества RxJS для оптимизации запросов и т.д.)
Как же мы решили эти трудности?
Очевидно, был необходим рефакторинг, однако для бизнеса важно, чтобы рефакторинг помимо самого рефакторинга создавал реальную ценность для продукта здесь и сейчас. И вот весной этого года появилась неплохая причина полностью переписать редактор политик.
Новая фича – центр управления
Несколько крупных заказчиков захотели управлять инсталляциями Solar webProxy удаленно, т.е. иметь некий «главный» узел с конструктором политик, который будет рассылать собранные конфигурации слоев и списков по подчиненным инсталляциям Solar webProxy. Аналитиками была поставлена задача разработки центра управления для webProxy – Solar MultiProxy. Эта фича требовала больших изменений в кодовой базе редактора политик как на front-end, так и на стороне back-end. После сбора оценок оказалось, что время для доработки имеющегося конструктора политик в соответствие с требованиями, примерно равнялось времени разработки такого конструктора с нуля. В результате менеджеры согласились, что целесообразно избавиться от legacy-кода и переписать конструктор с чистого листа. Забегая вперед – это полностью оправдалось, т.к. оценки на реализацию фич для следующих релизов оказались в 2-3 раза ниже, чем ожидалось до рефакторинга.
Архитектурное решение
Проанализировав структуру имеющегося конструктора политик, было решено разделить всю логику на два основных компонента:
таблица для отображения списка правил или справочников (далее app-advanced-table)
визуализатор форм (далее app-form-display)
Оба компонента должны работать по схожему принципу, на вход должны приходить данные для визуализации и объект с конфигурациями правил визуализации (например, каким образом рисовать столбец в таблице, или какой вид формы и с каким валидатором использовать для редактирования данных).
Разберем несколько примеров конфигураций. Вот так выглядит конфигурация таблицы для вывода правил доступа пользователей без аутентификации:
export const SOCKS_AUTH_BYPASS_CONFIG = new InjectionToken('Socks5 Auth Bypass Table Configuration', {
factory: (): AdvTableSettings => {
return {
uid: 'socks_auth_bypass',
editorType: 'modal',
rowsReorderable: true,
columns: [
DRAG_HANDLER,
{ ...NAME, sortId: undefined },
SOURCES,
inject(ACTION_DONT_AUTHENTICATE_AND),
ACTION_LINK_MANUALLY_PERSON,
...(inject(ProductLicenseService).isControlCenter(true) ? [] : [NODES]),
AUDIT_TRAIL,
COMMENT_IN_ICON,
ACTIONS_ENABLED,
ACTIONS_COPY_AND_DELETE,
],
};
},
});
Здесь видно, что мы задали уникальное имя для таблицы (используется для сохранения некоторых «стейтов» таблицы в localStorage), задали возможность изменять последовательность строк в таблице, а также обозначили каким образом визуализировать тот или иной столбец таблицы. Например, столбец sources (источники) описан таким образом:
export const SOURCES: AdvTableColumn = {
id: 'source',
name: 'policy.rule.source',
renderer: {
type: 'ExpandableList',
options: {
noDataLabel: 'ui.text.anyHe',
formatterFunc: (obj: PolicyLayerType): Array<AdvTableColumnExpandableItem> =>
obj.sources?.map(source => convertBackToTable(source as ConditionTypes, obj.distributed)) ?? [],
},
},
};
Здесь задано id столбца, его заголовок, а также способ визуализации – расширяемые список. Результат работы этого кода представлен ниже:
Теперь пример конфигурации компонента app-form-display. Для того же слоя доступа без аутентификации визуализатор форм конфигурируется следующим образом:
export const SOCKS_FILTER_CONN_EDITOR_CONFIG = new InjectionToken('Socks5 Editor Configuration', {
factory: (): FormConfiguration => {
return {
containers: [
FORM_EXCLUDE_ID,
FORM_SUBLAYER_ID,
FORM_ENABLED_AND_INSTRUCTION_TYPE,
inject(FORM_INSTRUCTION_NAME),
inject(FORM_COMMENT),
inject(FORM_PRIORITY)('FilterConnectionsSocks'),
FORM_DIVIDER_ACTIONS,
inject(FORM_LAYER_ACTION)(['AllowSocks', 'Deny'], [{ type: 'NoLog' }, { type: 'Notify' }], 'policy.layer.request.action.main'),
FORM_DIVIDER_CONDITIONS,
inject(FORM_LAYER_CONDITION)('sources', ['Person', 'Group', 'IpLiteral', 'IpReference', 'SubnetMask', 'NonAuthenticated']),
inject(FORM_LAYER_CONDITION)('destinations', ['UrlReference', 'UrlLiteral', 'UrlCategory', 'IpLiteral', 'IpReference', 'SubnetMask']),
FORM_ADVANCED_CHECKBOX,
FORM_SOCKS_METHODS,
FORM_PORTS,
FORM_SCHEDULES,
FORM_TRAFFIC_LIMITS,
...(inject(ProductLicenseService).isControlCenter(true) ? [] : [FORM_SOCKS_FILTERING_NODE]),
],
validators: [inject(UNIQUE_INSTRUCTION_NAME_VALIDATOR)('FilterConnectionsSocks')],
transformations: [
ADVANCED_SETTINGS_TRANSFORMATION(['adv_methods', 'adv_ports', 'adv_traffic_limits', 'adv_schedules', 'adv_filtering_node']),
EXCLUSION_ACTION_TRANSFORMATION,
],
};
},
});
Здесь мы видим, что для формы есть 3 основных раздела:
containers — список всех форм с описанием способа их визуализации.
validators — список глобальных валидаторов формы (в случае, если для валидации требуется сразу несколько контролов)
transformations — список трансформаций формы (например, в данном случае скрывается несколько фильтров при некоторых условиях).
Для примера раскроем еще каким образом описывается каждая форма, валидатор и трансформация. Форма описывается следующим образом:
export const FORM_INSTRUCTION_NAME = new InjectionToken('Form instruction name', {
factory: (): FormContainer => ({
label: 'policy.object.name',
controls: [
{
id: 'instruction_name',
path: ['name'],
type: 'TextInput',
options: {
placeholder: 'policy.wizard.name.placeholder',
},
validators: [REQUIRED_VALIDATOR, inject(MAXLENGTH_VALIDATOR)(200)],
},
],
}),
});
Здесь мы видим, что задан лейбл для формы, указан путь к данным в объекте с данными, какой именно контрол формы использовать, а также список валидаторов для именно этой формы. Помимо TextInput есть еще множество видов контролов, таких как Select, RichTextEditor, либо можно вставить любой другой кастомный компонент, который будет реализовывать интерфейс ControlValueAccessor.
Валидаторы описываются примерно так:
export const REQUIRED_VALIDATOR: ValidatorItem = {
type: 'standard',
func: Validators.required,
errorTrigger: 'required',
errorString: 'ui.validation.required',
};
Трансформации так:
export const EXCLUSION_ACTION_TRANSFORMATION: FormTransformation = {
// Hide actions for instruction
condition: (values: { [key: string]: unknown }): boolean => values.instruction_type === 'rule',
actions: [
{
action: 'show',
formIds: ['action_selector', 'actions_divider', 'priority_selector'],
},
{
action: 'function',
formIds: ['action'],
successFunc: control => control.enable(),
failureFunc: control => control.disable(),
},
],
};
Здесь представлено два глобальных раздела – condition и actions. Первое представляет собой стрелочную функцию, которая в случае возвращения true будет выполнять действия из массива actions. Действия в свою очередь могут быть как обычными преобразованиям на формой, так и изменения «стейта» формы. Такие стейты заранее прописываются в конфигурационном файле, и можно, например, в зависимости от положения чекбокса, поменять плейсхолдер в TextInput.
Конфигурация такой формы визуализируется вот так:
Интересно, что компонент app-form-display не привязан к контейнеру, в котором он располагается, так что можно его использовать, например, в сайдпейдже:
Все вышеприведенные примеры максимально простые, однако неплохо демонстрируют идею нашего рефакторинга. Другие слои имеют более сложную структуру.
К чему всё привело
Как итог, у нас получилось реализовать два компонента, которые могут быть использованы (и будут использованы) не только в конструкторе политик, но и в других разделах приложения. Более того, благодаря тому, что конфигурационные файлы собираются из мелких частей, такие части могут быть переиспользованы во многих частях приложения. Например, поле для ввода пароля со всеми валидаторами используется не только в конструкторе политик, но и при логинации пользователя.
Ниже приведена примерная схема, каким образом можно будет использовать разработанный нами компонент app-form-display для других разделов нашего приложения.
Приведем только некоторые положительные стороны нашего рефакторинга:
Разработка нового функционала центра управления Solar MultiProxy заняла меньшее количество времени, нежели если бы его делали на базе legacy‑кода;
Улучшили User Experience. Теперь объекты и справочники можно редактировать не переходя в другой раздел конструктора;
Выросла производительность как за счет перехода пагинации и сортировки на back‑end, так и за счет использования методов оптимизации Angular (стратегия детектирования изменений OnPush, разные оптимизации на базе RxJS);
Избавились от огромной части legacy‑кода в проекте. Теперь любые задачи в радость =);
Судя по разработке следующего релиза — затрачиваемое время на новые фичи значительно снизилось;
Убрали несколько устаревших библиотек из зависимостей, что в перспективе позволит проще и безболезненнее обновить версию Angular;
Получили бесценный опыт рефакторинга крупных модулей;
Заключение
Версия Solar webProxy 4.2 вышла 5 декабря. Теперь администраторы могут легко и эффективно создавать и редактировать правила фильтрации трафика, не переходя на отдельные страницы, что значительно повышает скорость их работы. Благодаря оптимизации производительности и переработке архитектуры, загрузка данных стала происходить быстрее, а управление инсталляциями возможно удаленно с помощью нового центра управления Solar MultiProxy. Устранение устаревших зависимостей и улучшение пользовательского опыта позволили сократить время на разработку новых функций и значительно упростить процесс их внедрения. Как результат, офицеры безопасности получили более эффективный инструмент для защиты образовательных и бизнес-структур от веб-угроз и утечки конфиденциальной информации через веб-канал.
Спасибо, что дочитали! Будем рады комментариям и обратной связи.
Комментарии (3)
amarhgil
13.12.2024 04:51Когда вы уже сделаете самое главное,
1) когда можно вбить учётку и URL и проверить, будет ли разрешен траффик или какое конкретное правило его блокирует. Самое нужное, что есть. Я вам про это писал ещё в 22 году.
2) Когда будет полнотекстовый поиск по логам. Я хочу написать слово, например "pdf", и чтобы мне вышли все результаты всех пользователей, которые на ресурсы с таким наборов букв в названии заходили. Про это тоже ещё в 22 году писал. А воз и ныне там
eliondel
После обновления на эту версию отъехали правила аутентификации по ip. Пришлось откатываться, благо перед обновлением сделали снапшот...
Solar_webProxy Автор
Добрый день! Напишите, пожалуйста, в нашу техподдержку: support@rt-solar.ru, и мы оперативно разберемся в проблеме, с которой вы столкнулись.