Укрепление федеративного доступа к AWS с помощью OIDC

Для того чтобы избежать управления многочисленными долгосрочными пользователями IAM, AWS
предоставляет опции федеративного доступа, которые включают SAML2.0 и OIDC identity providers (IDP). В то время как опция SAML используется многими нашими клиентами, и существует множество примеров ее настройки, примеров использования OIDC гораздо меньше. Поэтому, выбирая свой собственный метод федерации доступа, мы решили опробовать OIDC, чтобы лучше понять его ограничения и преимущества и иметь возможность лучше консультировать наших клиентов.

Различия между SAML и федерацией идентификационных данных OIDC

Чтобы продемонстрировать ключевые различия между OIDC и SAML, я создал небольшую копию, которая позволяет развернуть Keycloak на экземпляре EC2, а затем настроить клиентов SAML и OIDC для работы с AWS.
Для тех, кто не знаком с Keycloak, это инструмент управления идентификацией и доступом с открытым исходным кодом.
и управления доступом, спонсируемый компанией RedHat и широко используемый многими нашими клиентами и нами самими в качестве поставщика идентификационных данных. Среди прочих возможностей, Keycloak поддерживает протоколы SAML и OIDC для управления идентификацией и обеспечивает федерацию пользователей через LDAP, что позволяет использовать его с существующей базой пользователей из Active Directory. После развертывания Keycloak и настройки клиентов SAML и OIDC мы можем использовать Keycloak для входа в AWS.
Вход в систему SAML можно выполнить, перейдя по адресу https://auth.${TF_VAR_root_dn}/realms/awsfed/protocol/saml/clients/amazon-aws, где ${TF_VAR_root_dn} — поддомен, который необходимо создать перед развертыванием. После ввода учетных данных пользователя testuser, созданного скриптами развертывания, мы получаем перенаправление на консоль AWS для учетной записи AWS, на которую был развернут Keycloak. Если бы мы назначили несколько ролей одной группе Keycloak (или несколько групп для testuser), появилась бы страница, подобная приведенной ниже (которая покажется знакомой всем, кто уже использовал федерацию SAML с AWS).

Если вы любите экспериментировать и развернули все из репозитория, вы можете перейти на вкладку network в инструментах разработки браузера, найти там документ saml и скопировать его содержимое.


Сохраните содержимое как aws-saml/assertion и запустите saml.sh из той же папки. Если вы достаточно быстры (по умолчанию утверждение SAML для AWS действует только в течение 5 минут), то предположение должно сработать для первой роли, но не сработать для второй. Если вы посмотрите на политики доверия для соответствующих ролей (имена которых должны заканчиваться на _Federated_Admin-SAML и _Federated_Admin-SAML2, соответственно), вы увидите, что они идентичны и разрешают операцию AssumeRoleWithSAML для одного и того же SAML-провайдера. Почему же доступ предоставляется для первой роли и отказывается для второй? Это происходит потому, что AWS фактически проверяет само утверждение SAML на наличие роли, которую вы пытаетесь взять на себя. Если посмотреть на сценарий, который мы запустили для настройки Keycloak, мы увидим эти две строки:

kcadm create "clients/$clientId/roles" -r ${REALM_NAME} -s "name=$(terraform output -raw role_arn),$(terraform output -raw provider_arn)" -s 'description=AWS Access'
kcadm add-roles -r ${REALM_NAME} --gname "${GROUP_NAME}" --cclientid 'urn:amazon:webservices'  --rolename "$(terraform output -raw role_arn),$(terraform output -raw provider_arn)"
Войти в полноэкранный режим Выйти из полноэкранного режима

Эти строки создают запись для первой роли (той, что без 2) в Keycloak и привязывают эту роль к группе aws_access, которая позже будет назначена нашему testuser. Таким образом, эта роль появляется в утверждении SAML и может быть принята. Поскольку то же самое не происходит для второй роли, доступ к ней testuser’у запрещен (конечно, это изменилось бы, если бы вы создали соответствующую запись и связку в Keycloak для этой роли).

Но что насчет OIDC? Запустив сценарий ./oidc.sh из папки aws-oidc, мы видим, что наш testuser может принять роль, для которой наш провайдер OIDC указан в политике доверия. Более пристальный взгляд на эту политику показывает, что она содержит только две вещи: ARN провайдера OIDC и ID клиента в качестве aud. Это соответствует тому, что делает AWS Console
если там создается роль.


Также обратите внимание, что (в отличие от случая SAML) после запуска скриптов terraform в папке aws-oidc в Keycloak ничего делать не нужно. Что это значит? Ну, в случае OIDC AWS не проверяет наличие ролей или групп в ID-токене. Единственные две вещи, которые имеют значение при настройках по умолчанию, это сам IDP (который определяется URL и отпечатком пальца, как вы можете видеть из файла openid.tf) и ID клиента (определенный в разделе aud политики доверия).

{
  "exp": 1657326250,
  "iat": 1657322650,
  "auth_time": 0,
  "jti": "a valid id must be here",
  "iss": "https://our.domain/realms/somerealm",
  "aud": "THISISWHATMATTERS",
  "sub": "typically_this_is_the_user_id",
  "typ": "ID",
  "azp": "the_same_as_aud",
  "session_state": "another id is here",
  "at_hash": "some stuff",
  "sid": "and yet another id",
  "email_verified": true,
  "groups": [
    "group1",
    "group2",
    "group3",
    "group4",
    "group5"
  ],
  "preferred_username": "some_user",
  "email": "some_user@our.domain",
  "username": "some_user"
}
Вход в полноэкранный режим Выйти из полноэкранного режима

Все это означает, что любой пользователь, имеющий доступ к соответствующему
Keycloak realm может взять на себя любую роль, которая доверяет IDP, что не очень-то и безопасно и сильно уступает SAML, верно? Это было бы так, если бы не очень важная вещь — то, как я использовал OIDC в этом примере, не является тем, как его следует использовать. Давайте посмотрим на скрипт oidc.sh более внимательно.

function getClientSecret(){
  kcadm get -r ${REALM_NAME} "clients/$(getClientId ${1})/client-secret" | jq -r '.value'
}
Вход в полноэкранный режим Выход из полноэкранного режима

Здесь я использую kcadm.sh (который является контейнерным и как бы скрыт за исходниками ../kcadm.sh) для получения клиентского секрета для клиента Keycloak OIDC. Эта операция требует прав администратора и равносильна передаче администратором Keycloak клиентского секрета пользователю в обычном контексте. Этот секрет затем используется вместе с именем пользователя и паролем для testuser для непосредственного получения ID-токена, который, в свою очередь, передается в AWS STS. Конечно, как администратор Keycloak я бы никогда не стал делать этого в нетестовой среде, потому что секрет клиента (который привязан к ID клиента, который, в свою очередь, указан в политике доверия IAM) не должен быть доступен пользователям. Но для чего же тогда он нужен? Обратившись к документации AWS по теме OIDC, мы увидим, что в ней упоминается брокер идентификации. И этот брокер идентификации (который не предоставляется AWS, как в случае с SAML) является тем, для кого предназначены идентификатор и секрет клиента.
Итак, что же такое брокер идентификации? Брокер идентификации (IB) — это приложение, которое должно функционировать как связующее звено между AWS и Keycloak и брать на себя управление правами пользователей (оно должно знать, какой пользователь может выполнять ту или иную роль). Правильный поток входа в OIDC должен быть запущен IB, который перенаправляет пользователя на IDP (Keycloak в нашем случае), который, после проверки учетных данных пользователя, предоставляет ID Token для этого пользователя IB. IB использует ID и секрет клиента для аутентификации на IDP. Как видно из сценария oidc.sh, было бы плохой идеей предоставлять ID-токен пользователю, потому что комбинация ARN роли и ID-токена — это все, что нужно для принятия роли в OIDC.
Вместо этого IB должен проверить, имеет ли пользователь доступ к запрашиваемой роли, затем использовать ID-токен для получения временных учетных данных AWS (с помощью операции AssumeRoleWithWebIdentity) и затем вернуть эти учетные данные пользователю (или использовать их для получения URL входа в консоль AWS). В моей демонстрации выше я использую cURL в качестве IB, что, очевидно, является очень плохим выбором для производственной среды, поскольку предоставляет доступ к любой роли любому пользователю.

Усиление ролей на основе OIDC

В то время как использование надлежащего брокера идентификации минимизирует риск неправильного использования доступа OIDC к AWS, эксперименты выше привели меня к вопросу о том, возможно ли заставить AWS STS смотреть на атрибуты пользователя из ID токена, а не только на ID клиента (aud) и сам IDP. Посмотрев документацию для GitHub (который также использует OIDC) в качестве IDP, я увидел, что там есть еще один атрибут — sub — который используется в политиках доверия. Для Keycloak значением sub по умолчанию является ID пользователя, что не очень полезно, но у Keycloak есть картографы, которые могут быть назначены для
клиентам и могут отменять значения по умолчанию. Экспериментируя с мапперами, я обнаружил, что действительно можно заставить Keycloak предоставлять любой атрибут пользователя LDAP (в нашей среде мы используем федерацию пользователей LDAP) в качестве sub для AWS. Единственной оговоркой здесь является то, что этот атрибут должен быть строкой, поэтому невозможно напрямую использовать членство в группах (которые были бы массивами) для дополнительной защиты политик доверия. Однако можно использовать оператор StringLike для сопоставления подстрок. Используя этот оператор, можно проверять наличие групп LDAP с AWS STS, если они являются строковыми. Например, следующая политика доверия проверяет наличие определенной группы (предоставленной terraform как ${var.group}) в строке группы, которая выглядит следующим образом:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Federated": aws_iam_openid_connect_provider.oidc.arn
      },
      "Action": "sts:AssumeRoleWithWebIdentity",
      "Condition": {
        "StringEquals": {
          "${var.oidc_provider}:aud": var.client_id
        },
        "StringLike":{
          "${var.oidc_provider}:sub": ["*-${var.group}-*"]
        }
      }
    }
  ]
}
Войти в полноэкранный режим Выход из полноэкранного режима

Итак, откуда может взяться эта групповая строка? Один из вариантов — написать собственный плагин для Keycloak, другой — позволить IB (который является пользовательским приложением) обрабатывать это. Мой репозиторий на самом деле содержит такой пользовательский маппер (следующий раздел также обсуждает это немного более подробно), который должен быть активен в вашем Keycloak, если вы развернули его, как описано выше. Чтобы увидеть маппер в действии, мы можем запустить скрипт ./oidc_protected.sh из папки aws-oidc. Как вы увидите, вы сможете взять на себя первую роль, но не вторую.

Почему? Давайте посмотрим на политики доверия: политика для первой роли содержит группу aws_access, которая, как мы знаем, назначена нашему testuser, а политика для второй роли относится к группе aws_access_exclusive, которая еще даже не существует в Keycloak. Таким образом, несмотря на то, что у нашего пользователя был действительный ID Token, он не мог принять защищенную роль, потому что этот токен не содержал правильной группы. Если вы хотите убедиться, что доступ будет предоставлен после создания соответствующей группы и назначения ее testuser
а также посмотреть на новый пользовательский интерфейс Keycloak (который находится в предварительной версии для
Keycloak 18.0.2), вы можете сделать это по адресу https://auth.${TF_VAR_root_dn}.
В этом случае необходимо использовать учетные данные администратора (admin и ${TF_VAR_keycloak_password}, определенные в export.sh). Как только группа создана и назначена, доступ работает, как ожидалось. Отлично!

Разработка пользовательских мапперов для Keycloak

В документации Keycloak упоминаются «JavaScript» провайдеры, которые могут быть использованы для создания пользовательских мапперов. Когда я читал JavaScript, я ожидал написать что-то вроде этого:

function stringifyGroups(groups){ 
    return groups.reduce((current, element)=>{ 
        return current+"-"+element; 
    }, "")+"-"; 
} 
token.setOtherClaims("sub",stringifyGroups(token.groups));
Войти в полноэкранный режим Выйти из полноэкранного режима

и затем поместить это в .jar файл, как описано в документации.
Оказалось, что это работает совсем не так. Во-первых,
пользовательские сценарии отключены по умолчанию, как я обнаружил, просматривая журналы Keycloak. Чтобы исправить это, нужно либо активировать функции предварительного просмотра, либо включить опцию скриптов самостоятельно, как описано здесь.
Во-вторых, «JavaScript» оказался очень Java-ориентированным и требует вызова функций из соответствующих Java-классов Keycloak:

var res="";
var forEach = Array.prototype.forEach;
forEach.call(user.getGroupsStream().toArray(), function (group) {
  res=res+"-"+group.getName();
});
res=res+"-";
exports=res;
Вход в полноэкранный режим Выход из полноэкранного режима

В репозитории показано, как все это работает.

Выводы

В заключение можно сказать, что и SAML2, и OIDC являются отличными вариантами федерации доступа для AWS и имеют свои преимущества и недостатки. Если вы, как и мы, решите использовать OIDC, вам понадобится брокер идентификации (IB), который обеспечит связь между IDP (например, Keycloak) и AWS. Было бы неразумно и потенциально опасно предоставлять ID-токены напрямую федеративным пользователям, поскольку комбинации такого токена с ARN роли обычно достаточно для того, чтобы иметь возможность принять эту роль. Конечно, было бы еще более неразумно предоставлять пользователям идентификатор клиента, которому доверяет AWS. Комбинация оператора StringLike и связующих Keycloak может быть использована для повышения безопасности OIDC-федерированных учетных записей AWS путем ограничения доступа к ролям определенными атрибутами пользователя, такими как принадлежность к группе, аналогично тому, как работает федерация SAML2.

Оцените статью
devanswers.ru
Добавить комментарий