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

В Сравни больше 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:

  1. В разделе RemoteApp на JumpServer загрузили приложение из App Market (DBeaver, MongoDB Compass, Redis Desktop Manager и т.д.).

  2. Эти приложения — преднастроенные шаблоны .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.

Основные компоненты, которые позволяют управлять доступом к ресурсам:

  1. PAM-аутентификация. Пользователь проходит аутентификацию через JumpServer, как правило, с использованием LDAP или другого внешнего провайдера.

  2. Asset Authorization Rules. Это центральный механизм контроля доступа. В нём задаются:

    • группы пользователей из AD (AD user group);

    • целевые ресурсы (Target Asset);

    • разрешённые аккаунты на ресурсах (Target account);

    • разрешённые протоколы подключения (например, SSH, RDP);

    • разрешённые действия (доступ, передача файлов, буфер обмена и т.д.).

  3. Command Filter (Фильтр команд). Для повышения уровня безопасности доступ может ограничиваться только разрешёнными командами. Используются:

    • регулярные выражения для определения допустимых команд;

    • назначение групп команд (Command group) пользователям и активам.

  4. Asset (Ресурсы). Конечные целевые системы (БД, серверы и т.д.), к которым осуществляется подключение. Связь происходит по:

    • строке подключения (Connection string);

    • указанным аккаунтам (DB accounts).

При этом поддерживаются действия (Permissions): Connect, Transfer, Upload, Download, Delete, Clipboard, Copy, Paste, Share.

Вот пример работы схемы:

  1. Пользователь проходит аутентификацию.

  2. На основании его группы в AD проверяется наличие правил доступа (authorization rules).

  3. Если доступ разрешён, проверяется соответствие командам (если активен фильтр команд).

  4. Предоставляется доступ к целевому ресурсу с ограничениями по действиям и командам.

Таким образом добавление пользователя в группу в AD автоматически даёт ему нужный уровень доступа в JumpServer.

Интеграция с Terraform

Для автоматического добавления ресурсов в PAM мы реализовали интеграцию на основе анализа Terraform state-файлов, без прямой связи с Terraform-провайдерами.

Как это работает:

  1. Terraform state-файлы хранятся в Object Storage (например, S3-совместимом);

  2. PowerShell-скрипт:

    • подключается к бакету с помощью ключей доступа;

    • читает .tfstate;

    • извлекает из него данные об инфраструктуре (имя ресурса, IP-адрес, теги, тип — VM или база).

  3. После парсинга данных:

    • создаются или обновляются ассеты в 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. Процесс выглядит следующим образом:

  1. Создание изменений. Тимлид создает Merge Request (MR), в котором редактирует JSON-файл: добавляет или помечает на удаление пользователей (например, с флагом ##).

  2. Проверка и утверждение. Ответственный инженер по безопасности (или указанный в CODEOWNERS) проверяет и утверждает MR.

  3. CI/CD-пайплайн после слияния. После слияния MR запускается GitLab CI/CD Pipeline, который выполняет:

    • Валидацию JSON. Проверка синтаксиса и структуры JSON-файлов.

    • Запуск скрипта управления AD и PAM. Скрипт (на PowerShell или Python) выполняет:

      • проверку, что имена групп и ассетов соответствуют разрешённым шаблонам (исключение управления чужими группами);

      • проверку наличия групп и их состава в AD;

      • создание новых групп в AD, если они отсутствуют;

      • обновление состава существующих групп в AD — добавление и удаление пользователей;

      • проверку, что группы синхронизированы в PAM;

      • создание или обновление правил доступа к ассетам в PAM через API с использованием AD-групп.

    • Обновление репозитория (при необходимости). Скрипт вносит изменения в JSON-файл в репозитории только в следующих случаях:

      • если пользователь был помечен на удаление с флагом ## — он удаляется из АД группы, а сам файл актуализируется;

      • если были автоисправления (например, напрямую из АД удалили пользователя);

      • при добавлении пользователей через MR скрипт не изменяет файл — только применяет изменения в AD.

  4. Безопасность исполнения. Все действия выполняются 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 логики.

Заметки из опыта внедрения

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

  1. Проблемы с RDS и русифицированной ОС

    После успешной интеграции Remote Desktop Services (RDS) с JumpServer возникла проблема: новые пользователи перестали автоматически пробрасываться с PAM-сервера на RDS. Причина оказалась в русифицированной ОС, где системные группы имели локализованные (русские) названия. JumpServer не мог корректно их интерпретировать. 

    Рекомендация: использовать ОС без локализации. По последним данным, проблема была решена в более поздних релизах JumpServer.

  2. Отображение JSON в Web-интерфейсе PostgreSQL

    При выполнении SQL-запросов с JSON-ответами данные отображаются некорректно в веб-интерфейсе JumpServer.

    Временное решение:

    SELECT response_body::text FROM ...

    Рекомендация: поскольку приведение JSON к тексту позволяет обойти проблему отображения, при работе с JSON-ответами PostgreSQL имеет смысл использовать текстовую интерпретацию — до появления нативной поддержки.

  3. Ограничения при работе с кластерными managed-базами

    При работе с MongoDB в JumpServer отсутствовала поддержка проксирования — разработчик уже это исправил, но мы с нюансом успели столкнуться. 

    Также в многоадресной строке подключения MongoDB не поддерживается формат mongodb://host1,host2,host3/... Подключение к каждому хосту кластера выполняется отдельно — каждый в своём ассете.

    Есть нюансы  и при использовании Redis Sentinel. Так, JumpServer не поддерживает Sentinel-режим при подключении через ассеты. Подключение к Redis происходит напрямую, минуя механизмы отказоустойчивости Sentinel.

    Рекомендация: в проектах с MongoDB и Redis предусматривать временные архитектурные обходные пути (например, отдельные ассеты или использование клиентов вне PAM до появления поддержки).

***

В итоге мы не просто получили в распоряжение подходящий инструмент для контроля привилегированных доступов. Но и на практике убедились: JumpServer можно гибко адаптировать под реальные процессы — от интеграции с AD и Terraform до публикации GUI-приложений через RDS. Отдельно радует, что продукт развивается: появляются новые фичи и фиксятся баги, а разработчики оперативно реагируют на обратную связь по проблемам, возникающим в ходе эксплуатации.

Будем рады узнать о вашем опыте использования подобных инструментов — и ответить на вопросы по нашей реализации. Пишите в комменты!

Комментарии (0)