What is: SAML — обзор, структура и трассировка запросов на примере Jenkins и Okta SAML SSO

Автор: | 11/02/2019
 

В процессе настройки SAML SSO для Jenkins столкнулся с проблемой, при которой не передаются часть атрибутов из Okta в Jenkins.

Собственно, в этом посте попробуем разобраться с тем, что такое SAML вообще, достаточно кратко рассмотрим его архитектуру и компонены, а затем выполним трассировку запросов между Okta и Jenkins, что бы посмотреть какие данные передаются.

Т.к. информации очень много, то более-менее детально коснёмся тех механизмов SAML, которые используются непосредственно для Okta + Jenkins SAML SSO.

См. посты Jenkins: SAML Authentication через Okta SSO и группы пользователей и Jenkins: SAML, Okta, группы пользователей и Role-Based Security.

Использовался документ Security Assertion Markup Language (SAML) V2.0 Technical Overview от 25 March 2008, так что могут быть неточности, но в целом схема выглядит верной.

Прочие материалы, использовавшиеся при написании:

SAML: обзор

SAML SSO аутентификация включает в себя два основных взаимодействующих объекта — Service Provider (SP), и Identity Provider (IdP), см. SAML роли, а сам процесс состоит из двух частей — Trust Establishment и непосредственно процесс аутентифкации.

Например, Jenkins в нашем примере будет Service Provider, а Okta — Identity Provider.

Вкратце, процесс аутентификации выглядит следующим образом

  1. пользователь открывает Jenkins в браузере
  2. Jenkins перенаправляет пользователя на страницу SSO URL, Okta
  3. Okta выполняет аутентификацию, и возвращает пользователя в Jenkins
  4. Jenkins выполняет процесс авторизации клиента, например через Role-Based Security плагин и роли

Для передачи данных о пользователе — SAML использует объект Assertion, в котором содержится вся информация — имя пользователя, статус его аутентификации, группы и прочее, в зависимости от типа Assert.

В свою очередь то, какие Assert требуются — определяется SAML протоколом (рассмотрим в SAML protocols), а уже SAML протоколы передаются по HTTP или SOAP, что задаётся в Bindings (рассмотрим в SAML bindings).

В свою очередь SAML protocols, Bindings и используемые Asserts объединяются в Profiles (см. SAML profiles), например — Web Browser SSO Profile (см. Web Browser SSO Profile), или LDAP-профайл, которые определяют — какие атрибуты и идентификаторы передаются в Assert-е.

SAML роли

  • Identity Provider (IdP): система, которая создаёт assert (объект, или документ, с информацией) об объекте. Например, это может быть пользователь, для которого IdP создаёт assert, в котором указано, что пользователь Вася Пупкин прошёл аутентификацию, и ему можно доверять, и у него имеются атрибуты типа Группа и почтовый ящик.
    Identity Provider так же обозначается как как SAML Authorities и Asserting Party.
  • Service Provider (SP): система, использующая информацию от IdP.  При этом Service Provider полагается на assert с данными аутентификации пользователя, но может применять собственные локальные политики для авторизации пользователя и определения того, какие действия в системе ему разрешены.
    Service Providers также известны как Relying Parties, так как они полагаются (rely on) на информацию в assert, полученную от от Identity Provider (Asserting Party).

SAML Use Cases

Перед тем, как углубляться в технические детали — рассмотрим варианты, при которых SAML используется.

SAML Participants

При обмене информации во время SAML-аутентификации учавствует как минимум две стороны процесса — SAML asserting party и SAML relying party.

SAML asserting party — система, которая создаёт SAML asserts, соответственно SAML relying party — сторона, которая использует эти assert-ы.

Когда SAML asserting party или SAML relying party выполняет запрос — она становится SAML requester стороной, а второй участник процесса — SAML responder.

Во время SAML аутентификации участники процесса оперируют SAML-ролями, которые описывают сервисы и протоколы SAML и типы asserts.

Например, для SSO SAML определяет на SAML-роли Identity Provider (IdP) и Service Provider (SP), рассмотренные выше.

Дургим примером может быть роль Attribute Authority, где один из участников процесса генерирует assert в ответ на запрос атрибутов от другого участника, являющегося Attribute Requester.

«Сердцем» большинства SAML assert является субъект, который должен быть аутентифицирован, для которого и генерируется assert, при этом таким объектом может как обычный пользователь, так и, например, отдельный сервер.

Как правило, в assert передаётся такая информация, как «Это пользователь Вася Пупкин, его почтовый ящик vasya.pupkin@yandex.ru, и он был аутентифицирован с помощью парольной аутентифкации«.

В свою очередь Service Provider сам сам определяет — какая из переданной информации нужна, и, в зависимости от локальных политик доступа, определяет к каким задачам Василию Пупкину разрешить доступ.

Web Single Sign-On Use Case

idP-initiated Web SSO

Multi-domain Web Single Sign-on — один из наиболее используемых вариантов применения SAML. В этом случае, у пользователя имеется его логин-сессия (т.н. security context) на каком-либо веб-сайте, например airline.example.com. В какой-то момент — явно или прозрачно для пользователя — его переадресовывает на веб-сайт партнёра, cars.example.co.uk, который в данном случае исполняет роль Service provider. Сайт, являющийся IdP, в данном примере это airline.example.com создаёт assert для SP, cars.example.co.uk, в котором указывает, что пользователь известен, аутентифицирован, и имеет определённый набор атрибутов.

Так как SP cars.example.co.uk доверяет своему IdP airline.example.com — он создаёт локальную сессию для этого пользователя:

При таком подходе — пользователь сначала аутентифицируется на IdP, а затем получает доступ к защищаемым ресурсам на SP.

Этот вариант известен как IdP-initiated Web SSO.

SP-initiated Web SSO

Более широкоиспользуемый вариант — обратный, когда пользователь сначала попадает на SP, а SP в свою очередь, перенаправляет его для прохождения аутентификации на IdP.

После того, как пользователь будет аутентифицирован на IdP — тот создаёт assert, который может быть использован SP для проверки прав доступа к пользователю — его авторизации на защищаемом ресурсе.

Этот вариант известен как SP-initiated Web SSO.

SAML Architecture

SAML «состоит» из множества «строительных блоков», которые вместе позволяют множество различных вариантов работы с SAML.

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

SAML statement

При SAML-аутентификации передаются asserts, которые включают в себя набор statements, в которых указываются данные о пользователе, например — statement, что у Василия Пупкина имя Василий, почта vasily.pupkin@yandex.ru, и он входит в группу DevOps.

SAML описывает три основных типа statements:

  • Authentication statements: создаются участником, успешно аутентифицировавшем пользователя (например — Okta в роли IdP)
  • Attribute statements: набор атрибутов пользователя, например его почтовый ящик
  • Authorization decision statements: набор правил авторизации пользователя — действия, которые ему разрешены

SAML asserts

SAML assert содержит в себе данные (statements) об аутентифицируемом объекте. Assert содержит subject этого assert-а, conditions с уловиями, при которых этот assert будет считаться валидным, и набор statements.

Структура SAML assert описывается в SAML assertion XML schema.

SAML protocols

Как правило, создаются assert-ы на стороне IdP, который получает запрос от SP, а для передачи этих запросов и ответов — используются SAML-протоколы, которые описывают их структуру и содержимое этих сообщений в SAML-defined protocol XML schema.

Примеры протоколов — Authentication Request Protocol, Single Logout Protocol, Assertion Query and Request Protocol и т.д.

SAML bindings

Способы доставки таких сообщений с использованием протоколов низкоуровневой коммуникации (HTTP или SOAP) в свою очередь задаются в SAML bindings.

Типы bindings — HTTP Redirect Binding, HTTP POST Binding, HTTP Artifact Binding и прочие. Ниже рассмотрим HTTP Redirect Binding, кторый используется при Jenkins <=> Okta аутентификации (см. SP-Initiated SSO: Redirect/POST Bindings).

SAML profiles

И последним — SAML-профили определяют варианты использования SAML для бизнеса, например — Web Browser SSO profile.

Профиль описывает содержимое SAML assert-ов, протоколов и bindings для решения бизнес-задач.

Также существуют Attribute Profiles, которые не относятся к протоколам сообщения или bindings, но описывают как обмениваться информацией об атрибутах, используя assert-ы.

SAML Metadata

Метаданные определяют способ передачи информации об используемой конфигурации между IdP и SP, например — поддерживаемые сторонами SAML bindings, роли каждого из участников (IdP, SP, etc), поддерживаемые атрибуты и информацию о шифровании в собственной SAML Metadata XML schema.

Authentication context

В некоторых случаях Service Provider может понадобиться детальная информация о типе аутентификации пользователя на стороне IdP.

В таком случае используется SAML Authentication Context, который добавляется в authentication statement передаваемого assert.

Кроме того, при создании SAML-запроса — SP может указать authentication context в его запросе к IdP с требованием аутентификации пользователя с использованием определённых механизмов, например — требование пройти двухфакторную аутентификацию.

Всё вместе можно отобразить такой схемой:

SAML XML Constructs and Examples

Relationship of SAML Components

SAML assert-ы включают в себя один или более statement, и передаются между участниками процесса по HTTP, SOAP или другому протоколу передачи данных:

saml-chrome-panel

Для трассировки SAML-запросов и ответов — используем плагин для Chrome saml-chrome-panel.

Открываем Chrome Developer Tools, открываем вкладку с Jenkins, логинимся — и посмотрим, чем обменивается Jenkins и Okta.

Assertion, Subject, and Statement Structure

Рассмотрим примеры Assert, Subject и statement в нём:

    <saml2:Assertion ID="id5145226399860569827675102" IssueInstant="2019-11-02T12:37:19.440Z"
        Version="2.0" xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion"
        xmlns:xs="http://www.w3.org/2001/XMLSchema">
        <saml2:Issuer Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity"
            xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">http://www.okta.com/exk1pgr3yktuVLp5a357</saml2:Issuer>
...
       <saml2:Subject xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">
            <saml2:NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress">arseniy@example.com</saml2:NameID>
            <saml2:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer"><saml2:SubjectConfirmationData InResponseTo="_oyzxonwjrhnetdqqctfwbbfwmvgtrfa9jy1xu2o"
                NotOnOrAfter="2019-11-02T12:42:19.440Z"
                Recipient="https://ci.example.com/securityRealm/finishLogin"/></saml2:SubjectConfirmation>
        </saml2:Subject>
        <saml2:Conditions NotBefore="2019-11-02T12:32:19.440Z" NotOnOrAfter="2019-11-02T12:42:19.440Z"
            xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">
            <saml2:AudienceRestriction>
                <saml2:Audience>https://ci.example.com/securityRealm/finishLogin</saml2:Audience>
            </saml2:AudienceRestriction>
        </saml2:Conditions>
        <saml2:AuthnStatement AuthnInstant="2019-11-02T12:37:16.992Z"
            SessionIndex="_oyzxonwjrhnetdqqctfwbbfwmvgtrfa9jy1xu2o"
            xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">
            <saml2:AuthnContext>
                <saml2:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport</saml2:AuthnContextClassRef>
            </saml2:AuthnContext>
        </saml2:AuthnStatement>
...

В этом XML-фрагменте видим пример ассерта с одним authentication statement:

  1. в первой с помощью префикса saml: строке объявляется, что ниже последует SAML assert (<saml2:Assertion>) и время  генерации ассерта (<IssueInstant>)
  2. далее указывается используемая версия — Version
  3. уникальный индентификатор IdP, который сгенерировал данный ассерт — <saml2:Issuer>
  4. в блоке <saml2:Subject> передаётся информация об аутентифицированном субъекте — <saml2:NameID>arseniy@example.com</saml2:NameID> и его индентификатор (имя) в формате почты — Format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
  5. в <saml2:Conditions> передаём условия, при которых данный ассерт будет валиден, например время (NotBefore и NotOnOrAfter)
  6. и в <saml2:AuthnStatement> передаётся тип аутентификации, использовавшийся для проверки пользователя (<saml2:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport</saml2:AuthnContextClassRef>)

Attribute Statement Structure

Следующей частью идёт <AttributeStatement>:

...
        <saml2:AttributeStatement xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">
            <saml2:Attribute Name="Group" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
                <saml2:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema"
                    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">Everyone</saml2:AttributeValue>
                <saml2:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema"
                    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">DevOps</saml2:AttributeValue>
            </saml2:Attribute>
        </saml2:AttributeStatement>
...

В дополение к информации об аутентификации, рассмотренной выше, IdP может передать набор атрибутов аутентифицированного субъекта.

  • передаём атрибут <saml2:Attribute Name="Group">, и два значения в нём — группа <saml2:AttributeValue>Everyone</saml2:AttributeValue>, и группа <saml2:AttributeValue>DevOps</saml2:AttributeValue>, используя SAML Basic attribute profile (NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic")

SAML Profiles

Как уже упоминалось, SAML определяет множество различных профайлов для различных задач.

В случае с Jenkins и Okta используется Web Browser SSO Profile, который является наиболее широкоиспользуемым вариантом SAML-аутентификации.

Web Browser SSO Profile

Web Browser SSO Profile определяет процедуру обмена SAML-сообщениями для реализации Web SSO, в частности:

  1. во-первых, описывает направление инициализации подключения — IdP-initiated или SP-initiated
  2. во-вторых — какие SAML bindings должны использоваться для передачи сообщений

IdP-initiated и SP-initiated уже рассмотрели выше, и в нашем случае процесс будет являться SP-initialized (пользователь заходит на Jenkins, который запрашивает аутентификацию у IdP).

При этом будет работать и обратный вариант, когда пользователь сначала логинится в свой Okta-аккаунт, а затем в нём кликает на приложение Jenkins — тогда Okta выступит инициализатором подключения, и сгененрирует assert, который будет отправлен Jenkins-у.

Второй момент, который определяется профайлом — типы используемых bindings для передачи данных между IdP и SP.

Например, Authentication Request может быть отправлен от SP к IdP используя HTTP Redirect Binding, HTTP POST Binding или HTTP Artifact Binding, а Response message может быть отправлено от IdP к SP используя либо HTTP POST Binding, либо HTTP Artifact Binding.

Таким образом, в рамках одной сессии Authentication Request может быть отправлен с ипользованием одного типа биндинга, а Response message — используя другой, что описывается конфигурацией IdP и SP.

Соответственно, имеется и несколько вариантов процесса аутентификации:

  • SP-initiated SSO используя Redirect Binding для передачи сообщения <AuthnRequest>  от SP к IdP, и используя POST Binding для передачи сообщения <Response> IdP к SP
  • SP-initiated SSO используя POST Binding для сообщения <AuthnRequest> и Artifact Binding для сообщения <Response>
  • IDP-initiated SSO используя POST Binding для передачи сообщения <Response> IdP к SP; <AuthnRequest> сообщения SP к IdP не используется
Jenkins и Okta — SP-Initiated SSO: Redirect/POST Binding

И наконец-то рассмотрим в деталях весь процесс SSO аутентификации между Jenkins и Okta с использованием SP-Initiated SSO: Redirect/POST Bindings, что легко определить по методам передачи из вывода saml-chrome-panel:

Схематично процесс можно отобразить так:

И процесс пошагово:

  1. пользователь открывает страницу sp.example.com (Jenkins в нашем случае), и у него нет текущий логин-сессии (т.е. security context). SP сохраняет запрошенный пользователем URL
  2. SP возвращает запрос браузеру пользователя, передавая ему код редиректа 302 или 303, и в HTTP заголовке Location указывает URL SSO-сервиса, а в URL-переменной SAMLRequest передаёт тело запроса <AuthnRequest>

    Браузер обратывает полученный редирект и выполняет HTTP GET к URL IdP, передавая ему SAMLRequest
  3. SSO сервис проверяет наличие активной логин-сессии пользователя, которая должна соответствовать запрошенному типу аутентифиации, как это передано от SP в AuthnRequest. Если сессия не найдена — пользователь должен будет аутентифицироваться в IdP со своим логином-паролем
  4. пользователь проходит аутентификаицию в IdP, где ему создаётся security context
  5. IdP Single Sign-On Service генерирует SAML <Response>, в котором передаётся assert с security context пользователя. SAML <Response> помещается в HTTP FORM с именем SAMLResponse:

    Тело SAMLResponse передаётся в base64 в элементе <saml2p:Response>:
    Сгенерированный HTML передаётся браузеру пользователю в виде HTTP-ответа.
  6. браузер пользователя выполняет POST-запрос к SP (Jenkins), передавая ему SAMLResponse
    SP проверяет текст сообщения <Response>, и создаёт у себя security context для пользователя
  7. последним SP проводит авторизацию уже аутентифицированного пользователя для проверки того, разрешены ли ему запрошенные действия. Например, Jenkins может провести авторизацию используя Role-Based Security плагин

В целом — на этом всё.