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

Однако большинство организаций предпочитают эфемерные токены сеанса для повседневной деятельности по автоматизации с использованием AWS CLI, CI / CD или DevOps Pipelines вместо ключей доступа и секретных ключей, и текущие ограничения включают:

  • Интеграция интерфейса командной строки AWS с AWS SSO с помощью команды cli «aws configure sso» не проверяет учетные данные до регистрации клиентского приложения AWS SSO, а также до регистрации устройства
  • URL-адрес подтверждения регистрации устройства требует доступа браузера для выполнения аутентификации и авторизации для создания токена сеанса, что является своего рода ручным шагом

В этой статье дано пошаговое руководство по обходному пути для решения вышеуказанных ограничений.

Примечание. Доступ браузера для аутентификации и авторизации нельзя обойти в соответствии с AWS.

Python и Selenium - веб-драйвер Microsoft Edge (для автоматизации браузера) был выбран для быстрого развертывания и тестирования решений, тогда как выбор языка программирования и веб-драйвера зависит только от вас.

Вы можете загрузить веб-драйвер Microsoft Edge по адресу: https://developer.microsoft.com/en-us/microsoft-edge/tools/webdriver/

Необходимые модули Python:

  • BOTO3
  • АДАЛ
  • СЕЛЕН
  • ИНСТРУМЕНТЫ ДЛЯ СЕЛЕНА MSEDGE

Установите необходимые модули Python, указанные выше, с помощью pip.

Примечание. Задайте указанные ниже переменные среды перед запуском модуля Python

  • Прокси-сервер HTTP и HTTPS в случае использования прокси-сервера предприятия
  • EDGE_DRIVER_PATH - укажите местоположение загруженного и извлеченного веб-драйвера MS Edge на вашем компьютере или сервере

Ниже приведен код Python для генерации токенов сеанса для авторизованного пользователя с использованием AWS SSO - AAD.

  • Импортировать необходимые модули Python
import os
import time
from adal import AuthenticationContext
import getpass
import boto3
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from msedge.selenium_tools import Edge, EdgeOptions
import argparse
EDGE_DRIVER = os.environ["EDGE_DRIVER_PATH"]
  • Разбор идентификатора учетной записи и входных аргументов роли
if __name__ == '__main__':
  parser = argparse.ArgumentParser()
  parser.add_argument('--accountId', default='', help='provide AWS Account ID eg., 123456789012')
  parser.add_argument('--roleId', default='', help='provide AWS Federated SAML Role ID eg., Developer')
  args = parser.parse_args()

  accountId = ""
  if args.accountId:
    accountId = args.accountId
  else:
    print("Please provide accountId argument")

  roleId = ""
  if args.roleId:
    roleId = args.roleId
  else:
    print("Please provide roleId argument")
  main(accountId, roleId)
  • предложить пользователю ввести адрес электронной почты и пароль пользователя
def main(accountId, roleId):
  print("User Email:", end=' ')
  username = input()
  password = getpass.getpass()
  • Убедитесь, что введенные пользователем адрес электронной почты и пароль действительны, для этого шага требуются идентификатор клиента AAD, идентификатор клиента AAD, URL-адрес авторизации AAD и URL-адрес ресурса AAD.
AAD_AUTHORITY_HOST_URI = 'https://login.microsoftonline.com'
AAD_TENANT_ID = << AAD AWS Enterprise App Tenant ID >>
AAD_RESOURCE_URI = 'https://graph.windows.net'
AAD_CLIENT_ID = << AAD AWS Enterprise App Client ID >>

authority_uri = AAD_AUTHORITY_HOST_URI + '/' + AAD_TENANT_ID

token = ""

try:
  context = AuthenticationContext(authority_uri, api_version=None)
  mgmt_token = context.acquire_token_with_username_password(AAD_RESOURCE_URI, username, password, AAD_CLIENT_ID)
  print("AAD Token Generated, AD Credentials are valid")

  token = mgmt_token["accessToken"]
except:
  print("Invalid AD Credentials, Please verify Entered Credentials")
  • Зарегистрируйте клиента, если введенные пользователем учетные данные действительны
REGION = 'us-east-1'
if token:
  sso_oidc = boto3.client('sso-oidc', region_name=REGION)
  client = sso_oidc.register_client(
    clientName= << Provide your appropriate Client Name >>,
    clientType='public'
  )
  client_id = client.get('clientId')
  client_secret = client.get('clientSecret')

  print("Client Registered Successfully")
  • Зарегистрируйте устройство с помощью сгенерированного идентификатора клиента и секрета клиента
AWS_SSO_START_URL = << AWS SSO Start URL from AWS SSO Console >>
authz = sso_oidc.start_device_authorization(
  clientId=client_id,
  clientSecret=client_secret,
  startUrl=AWS_SSO_START_URL
)
url = authz.get('verificationUriComplete')
deviceCode = authz.get('deviceCode')
print("Verification URL Generated Successfully")
  • Код для автоматизации шагов браузера приведен ниже, открывает браузер MS Edge в частном режиме, учитывает перенаправления и автоматически вводит адрес электронной почты пользователя и пароль при появлении запроса, а также авторизует устройство.
browserOptions = EdgeOptions()
browserOptions.use_chromium = True
browserOptions.add_argument("-inprivate")
browser = Edge(executable_path=EDGE_DRIVER, options=browserOptions)
browser.get(url)

wait = WebDriverWait(browser, 180)
waitLoginURL = AAD_AUTHORITY_HOST_URI + "/" + AAD_TENANT_ID + "/saml2"
wait.until(EC.url_contains(waitLoginURL))
nameWait = WebDriverWait(browser, 20)
nameWait.until(EC.visibility_of_any_elements_located((By.NAME, 'loginfmt')))
browser.find_element_by_name('loginfmt').send_keys(username)
browser.find_element_by_xpath("//input[@type='submit' and @value='Next']").click()
wait.until(EC.visibility_of_any_elements_located((By.NAME, 'passwd')))
browser.find_element_by_name('passwd').send_keys(password)
browser.find_element_by_xpath("//input[@type='submit' and @value='Sign in']").click()

wait.until(EC.url_contains('/start/user-consent/authorize.html'))
browser.find_element_by_id('cli_login_button').click()

print("Successful Device Registration")
  • Ввести 5-секундный спящий режим (на всякий случай, когда AWS необходимо распространить и сохранить событие авторизации API в базе данных AWS)
time.sleep(5)
  • Сгенерируйте токен SSO AWS, используя зарегистрированный идентификатор клиента, секрет клиента и код устройства.
token_response = sso_oidc.create_token(
  clientId=client_id,
  clientSecret=client_secret,
  grantType="urn:ietf:params:oauth:grant-type:device_code",
  deviceCode=deviceCode
)
sso_token = token_response.get('accessToken')
print("SSO Token Generated Successfully")
  • Получите список учетных записей AWS, к которым у пользователя есть доступ с помощью токена AWS SSO, созданного на шаге выше.
sso = boto3.client('sso', region_name=REGION)

listAccounts = ssoListAccounts(sso, sso_token)
  • Проверьте, входит ли предоставленный пользователем идентификатор учетной записи в авторизованный список учетных записей AWS, в случае положительного ответа продолжить и получить все роли учетных записей AWS.
if accountId in listAccounts:

  listAcctRoles = ssoListAccountRoles(sso, sso_token, accountId)
  • Проверьте, входит ли предоставленный пользователем идентификатор роли в авторизованный список ролей учетных записей AWS, если да, продолжайте и сгенерируйте токен сеанса AWS SSO
if roleId in listAcctRoles:
  sts_credentials = sso.get_role_credentials(
    accessToken=sso_token,
    accountId=accountId,
    roleName=roleId
  )

  aws_access_key_id = sts_credentials['roleCredentials']['accessKeyId']
  aws_secret_access_key = sts_credentials['roleCredentials']['secretAccessKey']
  aws_session_token = sts_credentials['roleCredentials']['sessionToken']
  • Закройте браузер Microsoft Edge.
browser.close()

Подфункции Python:

  • ssoListAccounts - это подфункция для обработки вызовов API разбивки на страницы в аккаунтах AWS SSO List, или вы можете использовать API разбиения на страницы boto3 по своему усмотрению.
def listAccounts(found_token, sso, sso_token):
  if found_token:
    accounts = sso.list_accounts(nextToken=found_token, accessToken=sso_token)
  else:
    accounts = sso.list_accounts(accessToken=sso_token)
  return accounts

def ssoListAccounts(sso, sso_token):
  records = []
  more_objects = True
  found_token = ""
  while more_objects:
    accounts = listAccounts(found_token, sso, sso_token)
    for account in accounts['accountList']:
      if 'accountId' in account:
          records.append(account['accountId'])

    # Now check there is more objects to list
    if 'nextToken' in accounts:
      found_token = accounts['nextToken']
      more_objects = True
    else:
      break
  return records
  • ssoListAccountRoles - это подфункция для обработки ролей учетных записей AWS SSO List.
def listAcctRoles(found_token, sso, sso_token, accountId):
  if found_token:
    roles_response = sso.list_account_roles(nextToken=found_token,accessToken=sso_token,accountId=accountId)
  else:
    roles_response = sso.list_account_roles(accessToken=sso_token,accountId=accountId)
  return roles_response

def ssoListAccountRoles(sso, sso_token, accountId):
  records = []
  more_objects = True
  found_token = ""
  while more_objects:
    accountRoles = listAcctRoles(found_token, sso, sso_token, accountId)
    for accountRole in accountRoles['roleList']:
      if 'roleName' in accountRole:
          records.append(accountRole['roleName'])

    # Now check there is more objects to list
    if 'nextToken' in accountRoles:
      found_token = accountRoles['nextToken']
      more_objects = True
    else:
      break
  return records

Это был настоящий путь, который нельзя было обойти, а также ввести дополнительные шаги проверки, чтобы заставить это работать. Надеюсь, это поможет другим.

Больше контента на plainenglish.io