Avanpost FAM/MFA+ : 4.3.9 Настройка Avanpost ЕСИА Адаптер

Avanpost ЕСИА Адаптер представляет собой OpenID Connect-сервер, который перенаправляет аутентификацию пользователя в ЕСИА. Компонент реализует функции сервера OpenID Connect и клиента ЕСИА, позволяя осуществлять простую и централизованную аутентификацию в OIDC-приложениях при помощи механизма ЕСИА. Использование компонента позволяет выполнять настройку OIDC-приложений стандартизованным способом без необходимости отдельно настраивать специфичные для ЕСИА параметры. Обработка криптографических операций (подпись и проверка JWT) выполняется локально с использованием КриптоПро CSP, что повышает безопасность и упрощает интеграцию.

Компонент Avanpost ЕСИА Адаптер может использоваться для работы по протоколу OpenID Connect с любым OIDC-клиентом. В данной инструкции описывается настройка Avanpost ЕСИА Адаптер для Avanpost FAM Server (использование компонента совместно с Avanpost FAM доступно для версий FAM Server 1.13+).

Настройка Avanpost ЕСИА Адаптер

Настройку компонента следует выполнять следующим образом:

  1. Настроить в административной консоли FAM Server OIDC-приложение (описание настройки приложения представлено в статье Управление OpenID Connect-приложениями), в котором должен аутентифицироваться пользователь посредством ЕСИА.
  2. Настроить в административной консоли FAM Server IdP типа ЕСИА (описание настройки провайдера идентификации описано в статье Управление внешними провайдерами идентификации), посредством которого будет аутентифицироваться пользователь.
  3. Скачать Avanpost ЕСИА Адаптер из репозитория. 
  4. Настроить конфигурацию Avanpost ЕСИА Адаптер при помощи переменных окружения, задав следующие параметры:
    ПараметрЗначение
    DEBUGФлаг, активирующий режим отладки. При значении true сервис выводит расширенную диагностическую информацию в логи. В рабочей среде рекомендуется устанавливать значение false.
    BASE_URLБазовый URL-адрес, по которому доступен сервис Adapter. Должен соответствовать реальному сетевому расположению сервиса.
    HOSTСетевой интерфейс, на котором сервис Adapter принимает входящие HTTP-соединения. 
    PORTНомер порта, на котором сервис Adapter ожидает входящие HTTP-запросы.
    KEYПуть к файлу, содержащему приватный RSA-ключ в формате PEM. Этот ключ использует IdP Adapter для подписи JWT-токенов (ID Token, Access Token), выдаваемых OIDC-клиентам.
    KEY_IDУникальный идентификатор приватного ключа, указанного в параметре KEY. Используется для ссылки на конкретный ключ в наборе ключей.
    CLIENTSПуть к JSON-файлу, содержащему список доверенных OIDC-клиентов. Каждая запись в файле определяет ID клиента и его Domain для валидации redirect_uri.
    EXTIDPПуть к JSON-файлу, содержащему конфигурацию внешнего провайдера идентификации (ЕСИА). Данный файл определяет параметры подключения к ЕСИА.
    EXTIDP_REQUEST_TTLВремя жизни запроса к внешнему IdP (ЕСИА). Определяет, как долго Adapter будет хранить состояние (state) и другие параметры запроса авторизации до его завершения или истечения срока действия..
    EXTIDP_REQUEST_CLEAN_UPИнтервал времени, по истечении которого сервис выполняет фоновую очистку устаревших или завершенных запросов к внешнему IdP.
    USERINFO_TTLВремя жизни кэшированной информации о пользователе, полученной от ЕСИА. Используется, чтобы избежать повторных запросов к API ЕСИА в течение указанного периода.
    USERINFO_CLEAN_UPИнтервал времени, по прошествии сервис выполняет фоновую очистку устаревшей кэшированной информации о пользователях.
    SESSION_COOKIE_NAMEИмя HTTP cookie, используемой Adapter для хранения идентификатора сессии пользователя. 
    SESSION_COOKIE_LIFETIMEВремя жизни сессионной HTTP cookie в секундах. Определяет, как долго браузер пользователя будет хранить куку до ее автоматического удаления.
    SESSION_COOKIE_SECUREФлаг безопасности для сессионной cookie. При значении true cookie будет передаваться только по защищенному HTTPS-соединению.
    SESSION_EXPIREDВремя жизни серверной сессии пользователя в секундах. Определяет, через какое время неактивная сессия будет автоматически уничтожена на стороне сервера.
    CPROCSP_BIN_DIRПуть к директории, содержащей исполняемые файлы криптопровайдера КриптоПро CSP.
    NODEID

    Уникальный идентификатор экземпляра (ноды) адаптера. Используется для корректной работы внешней балансировки нагрузки ЕСИА Адаптера. Параметр (nid) передается внутри параметра state при формировании Authorization Code. В сценарии Authorization Code Flow код генерируется на одной ноде, и для его обмена на токен важно, чтобы запрос попал на ту же ноду. Параметр помогает обеспечить маршрутизацию последующих запросов от того же клиента на ту же ноду. 

    При использовании механизма PKCE (Proof Key for Code Exchange) для защиты потока Authorization Code Flow, Avanpost ЕСИА Адаптер формирует параметр code_verifier в соответствии с методическими рекомендациями по использованию ЕСИА. Для генерации значений code_verifier, соответствующих требованиям ЕСИА, используется утилита genguids (входит в поставку ЕСИА Адаптер). 

    GEN_GUIDS_BIN_PATHПуть к бинарному файлу утилиты genguids. Указывается абсолютный путь к исполняемому файлу. Пример значения: /opt/avanpost/esia-adapter/genguids.
    GEN_GUIDS_BATCH_SIZEКоличество генерируемых идентификаторов за один вызов утилиты genguids. Определяет, сколько UUID будет сгенерировано при каждом обращении к утилите.
    GEN_GUIDS_SEPARATORРазделитель идентификаторов в выводе утилиты genguids. Определяет символ или последовательность символов, которыми разделяются сгенерированные UUID в потоке вывода. Пример значения: \r.
  5. Для автоматического запуска Avanpost ЕСИА Адаптер при загрузке системы необходимо зарегистрировать его как службу systemd:
    1. Создать конфигурационный файл службы при помощи команды:
      sudo vi /etc/systemd/system/esia-idp.service
    2. Настроить конфигурационный файл согласно примеру: 
      [Unit]
      Description=Avanpost ESIA IdP Adapter
      After=network.target
      
      [Service]
      WorkingDirectory=/opt/esia-idp
      ExecStart=/opt/esia-idp/esia-idp
      Restart=always
      RestartSec=10
      SyslogIdentifier=esia-idp
      User=esia-idp
      Environment="DEBUG=false"
      Environment="BASE_URL=https://esia-adapter.corp:4010"
      Environment="CPROCSP_BIN_DIR=/opt/cprocsp/bin/amd64"
      
      [Install]
      WantedBy=multi-user.target
    3. Выполнить команды:
      sudo systemctl daemon-reload
      sudo systemctl enable esia-idp
      sudo systemctl start esia-idp
  6. Убедиться, что пользователь корректно проходит аутентификацию в целевом OIDC-приложении посредством ЕСИА. 

Выпуск сертификата ЕСИА Адаптер

Для интеграции Avanpost FAM с Единой системой идентификации и аутентификации (ЕСИА) требуется:

  • Выпустить ГОСТ-совместимый сертификат в тестовом удостоверяющем центре (УЦ) КриптоПро;
  • Зарегистрировать сертификат в ЕСИА.

До начала работы с УЦ требуется выполнить следующие действия:

  1. Установить СКЗИ КриптоПро CSP;
  2. Установить КриптоПро ЭЦП Browser plug-in;
  3. Скачать и установить корневой сертификат тестового Удостоверяющего центра в хранилище Доверенные корневые центры сертификации;
  4. Скачать и установить промежуточный сертификат тестового Удостоверяющего центра в хранилище Промежуточные центры сертификации.

    Для работы с УЦ рекомендуется использовать браузер с поддержкой ГОСТ TLS, например: chromium-gost, Internet Explorer, Яндекс.Браузер, Microsoft Edge (в режиме Internet Explorer). Для работы браузеров отличных от IE, следует установить расширение для Chrome-подобных браузеров или Яндекс.Браузера.

Выпуск и регистрацию сертификата для ЕСИА Адаптера требуется выполнять следующим образом:

  1. Войти в УЦ:
    1. Допускается авторизоваться под учётной записью тестового пользователя:
      • Логин: avanpost-dev-2;
      • Пароль: 7705494692.
    2. Допускается зарегистрировать нового пользователя.
  2. Убедиться, что в профиле заполнены все обязательные поля для ЕСИА:
    • Общее имя;
    • Страна;
    • Область;
    • Город;
    • Адрес;
    • Организация;
    • ОГРН;
    • ИНН ЮЛ.

       Если вышеперечисленные поля не заполнены, сертификат не будет валиден для ЕСИА.

  3. Перейти в раздел "Сертификаты", нажать кнопку "Создать" и создать сертификат со следующими параметрами:
    ПараметрЗначение
    Шаблон сертификатаТестовый квалифицированный сертификат
    КриптопровайдерCrypto-Pro GOST R 34.10-2012 Cryptographic Service Provider
    Ключ будет использоваться дляВыбрать "Подписи и шифрования"
    Размер ключа512
    Алгоритм хешированияГОСТ Р 34.11-2012 256 бит
  4. Выпустить сертификат, используя в качестве хранилища ключа HDImage (директория).
  5. Открыть утилиту «Инструменты КриптоПро» (входит в состав CSP) и выбрать хранилище HDImage.
  6. Выбрать созданный сертификат и нажать кнопку «Экспортировать ключи».
  7. Выбрать «Экспортировать PXF в файл», указать пароль и сохранить файл.
  8. При помощи утилиты «Инструменты КриптоПро» экспортировать сертификат (без закрытого ключа) для регистрации в ЕСИА, нажав кнопку «Экспортировать сертификаты».
  9. Войти в тестовую ЕСИА:
    • Логин: EsiaTest031@yandex.ru;
    • Пароль: 11111111. 
  10. Найти зарегистрированную информационную систему (интерфейс позволяет выполнить поиск по названию или по мнемонике).
  11. Нажать для перехода в раздел управления сертификатами.
  12. Нажать кнопку «Загрузить сертификат» и указать путь до ранее созданного сертификата. 

Утилита genguids для генерации GUID

Утилита genguids обеспечивает получение случайных значений на базе сертифицированного СКЗИ «КриптоПро CSP». При формировании авторизационного запроса к ЕСИА ESIA Adapter вызывает утилиту genguids. Утилита генерирует UUID v4 (random-based) в соответствии с RFC 4122. Рекомендуется использовать утилиту genguids с ОС Astra Linux Special Edition, совместимой с КриптоПро CSP 5.0 R3.

Утилита genguids выполняет следующие задачи:

  • генерация значений параметра state (параметр state используется в качестве анти-CSRF токена для защиты от CSRF-атак и обеспечения целостности сессии: значение state сохраняется в сессии и впоследствии сверяется при получении ответа от ЕСИА);
  • генерация значений параметра code_verifier (формируется при использовании механизма PKCE для защиты потока Authorization Code Flow и соответствует п. Б.6.2.2 методическим рекомендациям по использованию ЕСИА).

При развертывании следует выполнить тестовый пуск утилиты в консоли на целевой системе:

  1. Выполнить проверку зависимостей связанных библиотек:
    ldd ./genguids
  2. Запустить утилиту, задав через пробел количество генерируемых значений (в приведенном примере 1): 
    ./genguids 1
  3. Убедиться в корректности результата:  вывод результата в формате UUID (пример: 5f39e5c2-ffc0-4b17-ac2a-3187339b02e4) и полученный код возврата 0.

    Значения кодов возврата:

    • 0 – успешное выполнение;
    • 1 – ошибка аргумента командной строки;
    • 2 – ошибка инициализации API КриптоПро CSP;
    • 3 – ошибка генерации случайных чисел.
  4. Для автоматического запуска genguids компонентом настроить переменные окружения GEN_GUIDS_BIN_PATH, GEN_GUIDS_BATCH_SIZE, GEN_GUIDS_SEPARATOR.

Приложение А. Пример настройки переменных окружения 

Ниже приведен пример конфигурирования Avanpost ЕСИА Адаптер при помощи переменных окружения:

{
  "DEBUG": "false",
  "BASE_URL": "http://localhost:4010",
  "HOST": "0.0.0.0",
  "PORT": 4010,
  "KEY": "key.pem",
  "KEY_ID": 1,
  "CLIENTS": "clients.json",
  "EXTIDP": "extidp.json",
  "EXTIDP_REQUEST_TTL": "1h",
  "EXTIDP_REQUEST_CLEAN_UP": "1h",
  "USERINFO_TTL": "1h",
  "USERINFO_CLEAN_UP": "1h",
  "SESSION_COOKIE_NAME": "sid",
  "SESSION_COOKIE_LIFETIME": 3600,
  "SESSION_COOKIE_SECURE": "true",
  "SESSION_EXPIRED": 3600,
  "CPROCSP_BIN_DIR": "/opt/cprocsp/bin/amd64"
}

Приложение Б. Пример файла clients.json

Пример JSON-файла, содержащего список доверенных OIDC-клиентов

[
  {
    "ID": "pkce-only-client",
    "Domain": "localhost:9094"
  },
  {
    "ID": "client-with-secret",
    "Secret": "test-secret",
    "Domain": "example.com"
  }
]

Приложение В. Пример конфигурационного файла внешнего провайдера идентификации

Пример JSON-файла, содержащего конфигурацию внешнего провайдера идентификации (ЕСИА). Допускается использовать файл из DevTools браузера (доступен при просмотре деталей настроенного внешнего IdP). 

Параметру synonym должно соответствовать значение esia.

{
  "id": "db2b0bcf-92d5-4ab7-805a-2c3e9f444d4e",
  "authorizeUri": "{{.extidp_details.baseUrl}}/aas/oauth2/ac",
  "redirectMethod": 0,
  "type": "esia",
  "name": "esia",
  "synonym": "esia",
  "typeDetails": " {\n    \"baseUrl\": \"https://esia-portal1.test.gosuslugi.ru\",\n    \"clientCertHash\": \"\",\n    \"clientID\": \"04QN02\",\n    \"cryptoProvider\": {\n      \"details\": {\n        \"certPath\": \"/Users/michael/Downloads/esia/esia/TESIA GOST 2012 new.cer\",\n        \"containerPIN\": \"123\",\n        \"sha1Thumbprint\": \"7f159ecf770dfb58489c7ad9ef45132b7e181092\"\n      },\n      \"name\": \"cprocsp_cms\"\n    },\n    \"scope\": \"openid fullname email mobile\",\n    \"scopeOrg\": \"\"\n  }",
  "redirectForm": {
    "response_type": "code",
    "client_id": "{{ .extidp_details.clientID }}",
    "redirect_uri": "{{ .extidp_request.CallbackURI }}",
    "scope": "{{ .extidp_details.scope }}",
    "scope_org": "{{ .extidp_details.scopeOrg }}",
    "client_secret": "{{ $ts := timeFormat \"2006.01.02 15:04:05 Z0700\" .process_started}}{{$clientSecretRaw := join .extidp_details.scope $ts .extidp_details.clientID .extidp_request.ID}}{{ cryptoSign .context $clientSecretRaw .extidp_details.cryptoProvider | base64 }}",
    "client_certificate_hash": "{{ .extidp_details.clientCertHash }}",
    "timestamp": "{{ timeFormat \"2006.01.02 15:04:05 Z0700\" .process_started }}",
    "state": "{{ .extidp_request.ID }}",
    "access_type": "offline"
  },
  "userInfoPipeline": [
    {
      "t": 0,
      "r": "token",
      "d": {
        "client_certificate_hash": "{{.extidp_details.clientCertHash}}",
        "client_id": "{{.extidp_details.clientID}}",
        "client_secret": "{{$ts := timeFormat \"2006.01.02 15:04:05 Z0700\" .process_started}}{{$clientSecretRaw := join .extidp_details.scope $ts .extidp_details.clientID .extidp_request.ID}}{{ cryptoSign .context $clientSecretRaw .extidp_details.cryptoProvider | base64 }}",
        "code": "{{.code}}",
        "grant_type": "authorization_code",
        "redirect_uri": "{{.extidp_request.CallbackURI}}",
        "scope": "{{.extidp_details.scope}}",
        "state": "{{.extidp_request.ID}}",
        "timestamp": "{{timeFormat \"2006.01.02 15:04:05 Z0700\" .process_started}}",
        "token_type": "Bearer"
      },
      "m": "POST",
      "u": "{{.extidp_details.baseUrl}}/aas/oauth2/te"
    },
    {
      "t": 1,
      "d": {
        "verify_id_token": "{{ cryptoVerifyJWT .context .token.id_token .extidp_details.cryptoProvider }}"
      }
    },
    {
      "t": 2,
      "d": {
        "id_token_claims": "jwt(token.id_token)"
      }
    },
    {
      "t": 1,
      "d": {
        "is_trusted": "{{ with index .id_token_claims \"urn:esia:sbj\" \"urn:esia:sbj:is_tru\" }}{{ if not . }}{{ fail \"user is not trusted\" }}{{ end }}{{ end }}"
      }
    },
    {
      "t": 0,
      "r": "person",
      "m": "GET",
      "u": "{{.extidp_details.baseUrl}}/rs/prns/{{printf \"%.0f\" .id_token_claims.sub}}",
      "h": {
        "Authorization": "Bearer {{.token.access_token}}"
      }
    },
    {
      "t": 0,
      "r": "contacts",
      "d": {
        "embed": "(elements)"
      },
      "m": "GET",
      "u": "{{.extidp_details.baseUrl}}/rs/prns/{{printf \"%.0f\" .id_token_claims.sub}}/ctts",
      "h": {
        "Authorization": "Bearer {{.token.access_token}}"
      }
    },
    {
      "t": 2,
      "d": {
        "main_email": "email = \"\"; for (i = 0; i < contacts.elements.length; i++) {if (contacts.elements[i].type === \"EML\" && contacts.elements[i].vrfStu === \"VERIFIED\") {email = contacts.elements[i].value}} email;",
        "main_phone": "phone = \"\"; for (i = 0; i < contacts.elements.length; i++) {if (contacts.elements[i].type === \"MBT\" && contacts.elements[i].vrfStu === \"VERIFIED\") {phone = contacts.elements[i].value}} phone;"
      }
    }
  ],
  "attrMapping": {
    "fn": "{{.person.firstName}}",
    "ln": "{{.person.lastName}}",
    "mn": "{{.person.middleName}}",
    "un": "",
    "e": "{{.main_email}}",
    "p": "{{.main_phone}}",
    "l": "",
    "xid": "",
    "rid": "state",
    "x": {
      "snils": "{{.snils}}"
    }
  },
  "isActive": true
}

Приложение Г. Список эндпоинтов ЕСИА Адаптер 

ЭндпоинтОписание
/oauth2/authorize Запрос аутентификации.
/oauth2/tokenЗапрос токена.
/oauth2/logoutЗапрос выхода из системы.
/oauth2/userinfoВозврат JSON с информацией о пользователе, идентичный данным в id_token (используется для старых версий клиентов, подключаемых к ESIA Adapter).
/extidp/esiaЗапрос перенаправления в ЕСИА.
/extidp/esia/callbackОбработка перенаправления от ЕСИА.
/extidp/esia/logoutЗапрос перенаправления в ЕСИА для выхода.
/health/live

Проверка, что адаптер запущен и отвечает на запросы:

  • 200 OK – Адаптер запущен и работает;
  • 503 Service Unavailable – Адаптер не отвечает.
/health/ready

Проверка готовности адаптера к обработке запросов:

  • 200 OK – Адаптер готов к работе (тестовая подпись выполнена успешно);
  • 503 Service Unavailable - Адаптер не готов (ошибка инициализации КриптоПро CSP или конфигурации).



Обсуждение