В процессе настройки 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 2.0
- SAML Response (IdP -> SP)
- What is SAML and How Does it Work?
- How SAML Authentication Works
- и целая пачка документации тут: http://docs.oasis-open.org/security/saml/v2.0/
Содержание
SAML: обзор
SAML SSO аутентификация включает в себя два основных взаимодействующих объекта – Service Provider (SP), и Identity Provider (IdP), см. SAML роли, а сам процесс состоит из двух частей – Trust Establishment и непосредственно процесс аутентифкации.
Например, Jenkins в нашем примере будет Service Provider, а Okta – Identity Provider.
Вкратце, процесс аутентификации выглядит следующим образом
- пользователь открывает Jenkins в браузере
- Jenkins перенаправляет пользователя на страницу SSO URL, Okta
- Okta выполняет аутентификацию, и возвращает пользователя в Jenkins
- 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 передаётся такая информация, как “Это пользователь Вася Пупкин, его почтовый ящик [email protected], и он был аутентифицирован с помощью парольной аутентифкации“.
В свою очередь 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, что у Василия Пупкина имя Василий, почта [email protected], и он входит в группу 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">[email protected]</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:
- в первой с помощью префикса
saml:
строке объявляется, что ниже последует SAML assert (<saml2:Assertion>
) и время генерации ассерта (<IssueInstant>
) - далее указывается используемая версия –
Version
- уникальный индентификатор IdP, который сгенерировал данный ассерт –
<saml2:Issuer>
- в блоке
<saml2:Subject>
передаётся информация об аутентифицированном субъекте –<saml2:NameID>[email protected]</saml2:NameID>
и его индентификатор (имя) в формате почты –Format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
- в
<saml2:Conditions>
передаём условия, при которых данный ассерт будет валиден, например время (NotBefore
иNotOnOrAfter
) - и в
<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, в частности:
- во-первых, описывает направление инициализации подключения – IdP-initiated или SP-initiated
- во-вторых – какие 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
:
Схематично процесс можно отобразить так:
И процесс пошагово:
- пользователь открывает страницу sp.example.com (Jenkins в нашем случае), и у него нет текущий логин-сессии (т.е. security context). SP сохраняет запрошенный пользователем URL
- SP возвращает запрос браузеру пользователя, передавая ему код редиректа 302 или 303, и в HTTP заголовке Location указывает URL SSO-сервиса, а в URL-переменной
SAMLRequest
передаёт тело запроса<AuthnRequest>
Браузер обратывает полученный редирект и выполняет HTTPGET
к URL IdP, передавая емуSAMLRequest
- SSO сервис проверяет наличие активной логин-сессии пользователя, которая должна соответствовать запрошенному типу аутентифиации, как это передано от SP в
AuthnRequest
. Если сессия не найдена – пользователь должен будет аутентифицироваться в IdP со своим логином-паролем - пользователь проходит аутентификаицию в IdP, где ему создаётся security context
- IdP Single Sign-On Service генерирует SAML
<Response>
, в котором передаётся assert с security context пользователя. SAML<Response>
помещается в HTTPFORM
с именемSAMLResponse
:
ТелоSAMLResponse
передаётся в base64 в элементе<saml2p:Response>
:
Сгенерированный HTML передаётся браузеру пользователю в виде HTTP-ответа. - браузер пользователя выполняет
POST
-запрос к SP (Jenkins), передавая емуSAMLResponse
SP проверяет текст сообщения<Response>
, и создаёт у себя security context для пользователя - последним SP проводит авторизацию уже аутентифицированного пользователя для проверки того, разрешены ли ему запрошенные действия. Например, Jenkins может провести авторизацию используя Role-Based Security плагин
В целом – на этом всё.