
Если в компании налажен контроль доступа к внутренним ИТ-ресурсам, хорошо не только спецам по ИБ, но и многим другим. Например, лидам, которые отвечают за доступы к базам данных в своих командах. И новым сотрудникам, избавленным от необходимости вручную собирать логины-пароли, а затем где-то их самостоятельно хранить.
В Сравни больше 300 технических спецов, и в силу тематики наших продуктов зачастую они работают с чувствительными данными. Нам требовалось решение, которое поможет не просто централизованно управлять политиками доступов, но и проводить полноценный аудит всех действий в системе (предыдущий процесс логирования явно нуждался в модернизации).
Под обе эти задачи заточены так называемые PAM-инструменты (Privileged Access Management). Под катом рассказываем, как мы обеспечили контроль доступов и аудит инфраструктуры с помощью одного из них. В том числе об особенностях его внедрения, возможностях и некоторых ограничениях, выявленных на практике.
Привет, Хабр! Меня зовут Евгений, я Head of Systems Engineering в Сравни. Выбирая PAM-решение, мы составили список основных задач, в которых он был бы полезен нашей команде ИБ. Вот что нам требовалось в плане функциональности:
1. Ограничить прямой доступ к базам данных — сотрудники не должны подключаться напрямую, только через авторизованный шлюз.
2. Логировать каждое действие — включая команды SQL, открытие сессий и даже временные файлы.
3. Централизовать доступы — управление должно быть удобным, с разграничением прав по ролям и группам.
4. Интегрироваться с текущей инфраструктурой (LDAP) — без лишних костылей и с учётом существующих процессов.
5. Автоматизировать создание сущностей на PAM в соответствии с текущей инфраструктурой — PAM должен отражать актуальное состояние текущей инфраструктуры и автоматически поддерживать актуальное состояние.
Из других пожеланий к инструменту: хотелось, чтобы его можно было быстро и удобно развернуть, а в дальнейшем поддерживать без особых трудозатрат — в общем, искали классический вариант «дёшево и сердито».
После общения с вендорами и изучения функциональности решений, остановились на конкретном варианте: JumpServer. Это PAM-система, ориентированная на контроль и мониторинг привилегированных доступов к ресурсам (SSH, RDP, базы данных, web-интерфейсы). Есть как бесплатная (open-source), так и enterprise-версия.
Помимо очевидных возможностей (гибкое централизованное управление доступами, запись сессий), подкупили дополнительные фичи. Например, возможность интегрировать решение с внутренними инструментами компании посредством API, поддержка работы с облачными БД (в нашем случае это Яндекс Облако), интеграция с AD из коробки и наличие SSO.
Забегая вперед: решение полностью покрыло наши потребности в плане контроля доступов и аудита инфраструктуры; спустя полгода использования JumpServer об этом можно говорить смело. Мы обеспечили изоляцию подключения к базам, централизованное управление доступами, полную запись действий и автоматизацию через API.
Но чтобы всё заработало как надо, самого инструмента было недостаточно — требовалось логичным образом интегрировать его в нашу имеющуюся ИБ-систему. Об этом и поговорим ниже.
Реализация: от изоляции к полному контролю
Для начала обозначу, как инструмент в целом вписан в нашу архитектуру:
JumpServer PAM установлен в изолированной подсети.
Подключение к базам данных осуществляется только через JumpServer.
Интеграция с RDS-сервером — реализована поддержка подключения через опубликованные приложения (например, DBeaver через веб-интерфейс JumpServer).
Интеграция с Active Directory — реализована аутентификация, авторизация и построение RBAC-модели на базе групп и пользователей нашей AD.
Интеграция с Terraform — автоматическое создание ассетов (серверов, баз данных) в PAM при развёртывании ресурсов в облаке
Репозиторий в GitLab с workflow для управления составом AD групп — эта опция позволяет лидам (они отвечают за БД своих команд) оперировать доступами к ресурсам PAM, минуя при этом прямой доступ к AD.
HMAC-аутентификация — для автоматических сервисов и скриптов.
А теперь пройдемся подробнее по специфике каждого пункта.
JumpServer PAM в изолированной подсети
Инструмент был развёрнут в отдельной подсети внутри on-prem инфраструктуры с ограниченным доступом из внешнего мира. Это позволило нам контролировать трафик к JumpServer с помощью ACL и Security Group, плюс создать уровень изоляции между пользовательским трафиком, инфраструктурой и базами данных.
Подход обеспечил «бастионную» модель, где все подключения к ресурсам проходят через один проверяемый и логируемый шлюз.
Интеграция с RDS (Remote Desktop Services) и опубликованными приложениями
Для предоставления пользователям доступа к графическим инструментам (например, DBeaver\MongoDB Compas и т.д.) без установки на локальный компьютер, мы использовали RDS:
В разделе RemoteApp на JumpServer загрузили приложение из App Market (DBeaver, MongoDB Compass, Redis Desktop Manager и т.д.).
-
Эти приложения — преднастроенные шаблоны .app (по сути, конфиги), которые:
определяют команду запуска (например: C:\Program Files\DBeaver\dbeaver.exe);
включают иконку, описание и категорию (Database, Web и т.д.);
могут быть установлены автоматически на RDS-хост.
JumpServer сам устанавливает эти приложения, через PAM-компонент Tink.
Как это работает:
Tink, подключаясь по WinRM, проверяет, установлено ли приложение на RDS-хосте.
Если нет — автоматически его загружает и устанавливает, при наличии соответствующего дистрибутива.
Приложения устанавливаются на сервере с настроенным Remote Desktop Services (RDS) под Windows.
RDS добавлен в JumpServer как remote app machine и используется в режиме «Published Application».
Через JumpServer пользователю становится доступно опубликованное приложение (например, DBeaver) — оно запускается удалённо на RDS-сервере, но отображается в веб-интерфейсе пользователя.
Всё подключение происходит через RDP, проксируемое и логируемое PAM.
К подобному решению я пришел, когда при работе с JumpServer столкнулся с ограничениями для пользователей MongoDB (об этом расскажу в далее в статье) — подход позволил не обделять их функциональностью.
В целом решение позволяет:
запускать GUI-инструменты из браузера без установки на рабочую станцию;
строго контролировать, какие приложения доступны пользователю;
вести запись и аудит действий, включая запуск приложений и взаимодействие с интерфейсом;
при необходимости — ограничивать файловый доступ и буфер обмена.
Подключение к базам данных только через JumpServer
Модель доступа к ресурсам выглядит следующим образом:
1. Пользователь проходит аутентификацию на PAM-сервере.
2. Во время аутентификации JumpServer получает список групп пользователя из AD.
3. На основе полученных групп, пользователю назначаются права доступа к ассетам и разрешенные действия.
В рамках политики безопасности было запрещено прямое подключение к базам и использование учетных данных непосредственно от БД.
JumpServer стал единой точкой входа. Пользователь сначала аутентифицируется в PAM, после чего может подключиться к назначенной базе.
Режимы подключения:
Web-интерфейс (CLI, GUI, Applet — позволяет в веб-интерфейсе JumpServer открыть опубликованное на RDS приложение).
DBguide — генерация временных кредов для подключения базе через JumpServer, с их помощью можно подключиться к базам из стандартных профильных приложений для работы с БД, при этом PAM будет проксировать это подключение.
JumpServer client — локальный клиент, который может запустить профильное ПО для работы с базой, например SSMS, DBeaver и прокинуть в них коннекшен-стринги для автоматического подключения к БД.
Таким образом вся активность фиксируется, и реальные учётные данные баз остаются скрыты от конечных пользователей.
Интеграция с Active Directory (LDAP)
Аутентификация и авторизация пользователей реализована через LDAP-интеграцию с существующим Active Directory. Это дало следующие преимущества:
единый вход;
автоматическая синхронизация пользователей и групп;
построение RBAC на базе AD-групп;
прозрачное управление доступами на уровне PAM через привязку групп к ресурсам.
Доступ к инфраструктурным ресурсам регулируется через механизм Asset Authorization Rules и Command Filter внутри JumpServer.
Основные компоненты, которые позволяют управлять доступом к ресурсам:
PAM-аутентификация. Пользователь проходит аутентификацию через JumpServer, как правило, с использованием LDAP или другого внешнего провайдера.
-
Asset Authorization Rules. Это центральный механизм контроля доступа. В нём задаются:
группы пользователей из AD (AD user group);
целевые ресурсы (Target Asset);
разрешённые аккаунты на ресурсах (Target account);
разрешённые протоколы подключения (например, SSH, RDP);
разрешённые действия (доступ, передача файлов, буфер обмена и т.д.).
-
Command Filter (Фильтр команд). Для повышения уровня безопасности доступ может ограничиваться только разрешёнными командами. Используются:
регулярные выражения для определения допустимых команд;
назначение групп команд (Command group) пользователям и активам.
-
Asset (Ресурсы). Конечные целевые системы (БД, серверы и т.д.), к которым осуществляется подключение. Связь происходит по:
строке подключения (Connection string);
указанным аккаунтам (DB accounts).
При этом поддерживаются действия (Permissions): Connect, Transfer, Upload, Download, Delete, Clipboard, Copy, Paste, Share.
Вот пример работы схемы:
Пользователь проходит аутентификацию.
На основании его группы в AD проверяется наличие правил доступа (authorization rules).
Если доступ разрешён, проверяется соответствие командам (если активен фильтр команд).
Предоставляется доступ к целевому ресурсу с ограничениями по действиям и командам.

Таким образом добавление пользователя в группу в AD автоматически даёт ему нужный уровень доступа в JumpServer.
Интеграция с Terraform
Для автоматического добавления ресурсов в PAM мы реализовали интеграцию на основе анализа Terraform state-файлов, без прямой связи с Terraform-провайдерами.
Как это работает:
Terraform state-файлы хранятся в Object Storage (например, S3-совместимом);
-
PowerShell-скрипт:
подключается к бакету с помощью ключей доступа;
читает .tfstate;
извлекает из него данные об инфраструктуре (имя ресурса, IP-адрес, теги, тип — VM или база).
-
После парсинга данных:
создаются или обновляются ассеты в JumpServer через API (с HMAC-подписью);
происходит автоматическое распределение по группам, согласно тегам или шаблонам.
Вследствие такого подхода у нас нет зависимости от Terraform-провайдеров или Git-хуков, а скрипт можно запускать по расписанию (cron/CI/CD). При этом обнаруживаются как новые ресурсы, так и изменения существующих.
Плюс, работая со стейтами Terraform, я могу сам контролировать ситуацию с добавлением ресурсов — без необходимости привлекать DevOps-команду. Сканирую текущую инфру, делаю скан PAM-сервера, вычисляю дельту, смотрю, чего не хватает и добавляю на PAM-сервер в автоматическом режиме недостающие элементы: учётные записи, ассеты и т.д.
HMAC-аутентификация для автоматизации
Вместо базовой авторизации мы внедрили безопасную HMAC-подпись для запросов к API JumpServer.
HMAC-подпись формируется из AccessKey, SecretKey и Timestamp, после чего передаётся в заголовках запроса. Это исключает риски, связанные с хранением паролей в скриптах, MitM-атаками, и позволяет точно определить, какой сервис выполнил какой вызов.
Рабочий пример:
function Generate-Signature {
param (
[string]$Key, # Секретный ключ (SecretID) для HMAC
[string]$Data # Строка, которую нужно подписать (stringToSign)
)
# Преобразуем ключ и данные в байты
$KeyBytes = [System.Text.Encoding]::UTF8.GetBytes($Key)
$DataBytes = [System.Text.Encoding]::UTF8.GetBytes($Data)
# Создаем объект HMAC-SHA256 и задаем ключ
$HMAC = New-Object System.Security.Cryptography.HMACSHA256
$HMAC.Key = $KeyBytes
# Вычисляем хэш и возвращаем его в формате Base64
return [Convert]::ToBase64String($HMAC.ComputeHash($DataBytes))
}
# Функция Get-AuthHeaders
# Назначение: формирует заголовки для HMAC-аутентифицированного запроса к JumpServer API.
function Get-AuthHeaders {
param (
[string]$KeyID, # Идентификатор ключа (используется в поле keyId)
[string]$SecretID, # Секретный ключ для подписи
[string]$Path, # Путь запроса, например: "/api/assets/"
[string]$Method # HTTP-метод запроса (GET, POST и т.д.)
)
# Получаем текущее время в формате RFC1123 (обязательный заголовок 'date')
$Date = (Get-Date -Format "R")
# Формируем строку request-target для подписи
$RequestTarget = "(request-target): $Method $Path"
# Формируем полную строку, которую нужно подписать
$StringToSign = "$RequestTarget`naccept: application/json`ndate: $Date"
# Генерируем подпись с помощью функции Generate-Signature
$Signature = Generate-Signature -Key $SecretID -Data $StringToSign
# Возвращаем словарь (hashtable) с заголовками для запроса
return @{
"Accept" = "application/json"
"Content-Type" = "application/json"
"X-JMS-ORG" = "00000000-0000-0000-0000-000000000002" # ID организации в JumpServer
"Authorization" = "Signature keyId=`"$KeyID`",algorithm=`"hmac-sha256`",headers=`"(request-target) accept date`",signature=`"$Signature`""
"Date" = $Date
}
}
Пример использования:
# Функция Get-Assets-By-Name
# Назначение: выполняет запрос к JumpServer API для поиска ассетов (ресурсов) по имени.
# Возвращает массив ассетов, имя которых соответствует строке поиска.
# Параметры:
# - $JmsUrl — базовый URL JumpServer (например, https://pam.example.local)
# - $KeyID — идентификатор HMAC-ключа
# - $SecretID — секретный ключ HMAC
# - $SearchName — строка для поиска ассетов (имя, полное или частичное)
function Get-Assets-By-Name {
param (
[string]$JmsUrl,
[string]$KeyID,
[string]$SecretID,
[string]$SearchName
)
# Формируем путь запроса с параметром поиска
$Path = "/api/v1/assets/assets/?search=$SearchName"
# Генерируем заголовки с HMAC-подписью
$Headers = Get-AuthHeaders -KeyID $KeyID -SecretID $SecretID -Path $Path -Method "get"
try {
# Выполняем GET-запрос к JumpServer API
$response = Invoke-RestMethod -Uri "$JmsUrl$Path" -Method Get -Headers $Headers -UseBasicParsing
# Если ассеты не найдены — возвращаем пустой массив
if ($response.Count -eq 0) {
# Write-Warning "Ассет не найден на ПАМ | '$SearchName'"
return @()
}
# Фильтруем результаты по имени ассета (точное или частичное совпадение)
return $response | Where-Object { $_.Name -like "$SearchName" }
}
catch {
# Обработка ошибок при выполнении запроса
Write-Error "Ошибка при запросе списка ассетов: $_"
return @()
}
}
$assets = Get-Assets-By-Name -JmsUrl $JmsUrl -KeyID $KeyID -SecretID $SecretID -SearchName $SearchName
Репозиторий в GitLab с workflow
Этот механизм пока не внедрён в наш прод, но уже протестирован.
В теории доступами в PAM можно управлять вручную через GUI JumpServer, используя разделение на воркспейсы и организационные сущности («организации»). Но этот подход не был для нас оптимальным.
Во-первых, смущала невозможность прямой синхронизации OU из AD в раздельные воркспейсы PAM. То есть пользователей, импортированных из AD, пришлось бы вручную перемещать по организациям, либо дополнительно автоматизировать этот процесс через API.
Во-вторых, нам требуется централизованное и автоматизированное управление доступом через AD, в контексте интеграции PAM с внутренними сервисами.
В результате выбрали такое решение: GitLab → AD → PAM.
Общая схема:

Конфигурации AD-групп хранятся в GitLab-репозитории в виде JSON-файлов. Управление доступом реализовано через механизм Merge Request и CI/CD Pipeline. Процесс выглядит следующим образом:
Создание изменений. Тимлид создает Merge Request (MR), в котором редактирует JSON-файл: добавляет или помечает на удаление пользователей (например, с флагом ##).
Проверка и утверждение. Ответственный инженер по безопасности (или указанный в CODEOWNERS) проверяет и утверждает MR.
-
CI/CD-пайплайн после слияния. После слияния MR запускается GitLab CI/CD Pipeline, который выполняет:
Валидацию JSON. Проверка синтаксиса и структуры JSON-файлов.
-
Запуск скрипта управления AD и PAM. Скрипт (на PowerShell или Python) выполняет:
проверку, что имена групп и ассетов соответствуют разрешённым шаблонам (исключение управления чужими группами);
проверку наличия групп и их состава в AD;
создание новых групп в AD, если они отсутствуют;
обновление состава существующих групп в AD — добавление и удаление пользователей;
проверку, что группы синхронизированы в PAM;
создание или обновление правил доступа к ассетам в PAM через API с использованием AD-групп.
-
Обновление репозитория (при необходимости). Скрипт вносит изменения в JSON-файл в репозитории только в следующих случаях:
если пользователь был помечен на удаление с флагом ## — он удаляется из АД группы, а сам файл актуализируется;
если были автоисправления (например, напрямую из АД удалили пользователя);
при добавлении пользователей через MR скрипт не изменяет файл — только применяет изменения в AD.
Безопасность исполнения. Все действия выполняются self-hosted GitLab Runner’ом, работающим во внутренней сети с доступом к AD и PAM.
Логика синхронизации Git ↔ AD ↔ PAM выглядит так:
Добавление пользователей:
Если пользователь присутствует в AD-группе, но отсутствует в JSON-файле — он автоматически добавляется с постфиксом # (автодобавление).
Если пользователь прописан вручную в JSON, но не найден в AD-группе — он автоматически добавляется в группу AD.
Удаление пользователей:
-
Если пользователь помечен как ##:
он удаляется из AD-группы;
удаляется из JSON-файла;
коммит с изменениями формируется автоматически и пушится в GitLab при выполнении скрипта.
-
Если пользователь удалён напрямую из AD, но в JSON-файле имел #:
он считается автодобавленным и удаляется из файла.
-
Если пользователь удалён из AD, но в JSON указан без #:
скрипт восстанавливает его членство в AD-группе.
И немного о том, как в рамках процесса мы работаем с безопасностью:
Все изменения фиксируются в истории GitLab (MR, commit-log).
Журнал изменений в AD может быть синхронизирован с SIEM.
-
Доступ к репозиторию ограничен:
только тимлиды и секьюрити-инженеры имеют права на запись;
используется CODEOWNERS для обязательного ревью.
-
Секреты (например, LDAP_USER, LDAP_PASS) хранятся в GitLab CI/CD Variables:
помечены как Protected и Masked;
доступны только в нужных окружениях (prod, staging).
Рекомендуется использовать подписанные коммиты, Protected branches, Environments для разграничения prod/dev логики.
Заметки из опыта внедрения
Проблем, с которыми мы столкнулись в ходе внедрения, по факту было больше, чем я указываю в этом разделе. Но поскольку многие из них довольно специфичны, подсвечу наиболее значимые загвоздки. (К слову, с некоторыми вопросами нам быстро помогла разобраться русскоязычная поддержка продукта: в том числе с их помощью можно отправлять запросы разработчикам.)
-
Проблемы с RDS и русифицированной ОС
После успешной интеграции Remote Desktop Services (RDS) с JumpServer возникла проблема: новые пользователи перестали автоматически пробрасываться с PAM-сервера на RDS. Причина оказалась в русифицированной ОС, где системные группы имели локализованные (русские) названия. JumpServer не мог корректно их интерпретировать.
Рекомендация: использовать ОС без локализации. По последним данным, проблема была решена в более поздних релизах JumpServer.
-
Отображение JSON в Web-интерфейсе PostgreSQL
При выполнении SQL-запросов с JSON-ответами данные отображаются некорректно в веб-интерфейсе JumpServer.
Временное решение:SELECT response_body::text FROM ...
Рекомендация: поскольку приведение JSON к тексту позволяет обойти проблему отображения, при работе с JSON-ответами PostgreSQL имеет смысл использовать текстовую интерпретацию — до появления нативной поддержки.
-
Ограничения при работе с кластерными managed-базами
При работе с MongoDB в JumpServer отсутствовала поддержка проксирования — разработчик уже это исправил, но мы с нюансом успели столкнуться.
Также в многоадресной строке подключения MongoDB не поддерживается формат mongodb://host1,host2,host3/... Подключение к каждому хосту кластера выполняется отдельно — каждый в своём ассете.
Есть нюансы и при использовании Redis Sentinel. Так, JumpServer не поддерживает Sentinel-режим при подключении через ассеты. Подключение к Redis происходит напрямую, минуя механизмы отказоустойчивости Sentinel.
Рекомендация: в проектах с MongoDB и Redis предусматривать временные архитектурные обходные пути (например, отдельные ассеты или использование клиентов вне PAM до появления поддержки).
***
В итоге мы не просто получили в распоряжение подходящий инструмент для контроля привилегированных доступов. Но и на практике убедились: JumpServer можно гибко адаптировать под реальные процессы — от интеграции с AD и Terraform до публикации GUI-приложений через RDS. Отдельно радует, что продукт развивается: появляются новые фичи и фиксятся баги, а разработчики оперативно реагируют на обратную связь по проблемам, возникающим в ходе эксплуатации.
Будем рады узнать о вашем опыте использования подобных инструментов — и ответить на вопросы по нашей реализации. Пишите в комменты!