Почему Private Key JWT - это новый стандарт безопасности для агентов Bedrock
Классическая OAuth 2.0 аутентификация с client_secret создает две фундаментальные проблемы для production-агентов. Долгоживущий секрет можно скомпрометировать через утечку репозитория, лог ошибки или внутреннюю атаку. Ротация секрета требует координации между командой безопасности, DevOps и владельцем агента - процесс, который редко выполняется вовремя.
Private Key JWT Client Authentication (RFC 7521, RFC 7523) заменяет статичный секрет на асимметричную криптографию. Агент генерирует подписанный JWT-токен, используя приватный ключ из AWS KMS, а провайдер идентификации проверяет подпись публичным ключом. Приватный ключ никогда не покидает KMS - даже ваш код не имеет к нему доступа. Компрометация подписанного токена ограничена его коротким временем жизни (обычно 5 минут), в отличие от client_secret, который остается валидным до ручного отзыва.
Amazon Bedrock AgentCore Identity интегрирует эту модель напрямую с AWS KMS. Вы создаете асимметричный ключ с операцией SIGN_VERIFY, указываете его ARN в конфигурации credential provider, и Bedrock автоматически подписывает JWT при каждом обращении агента к внешним API. Это соответствует лучшим практикам безопасности AWS Well-Architected Framework и устраняет необходимость хранить секреты в переменных окружения или Secrets Manager.
Архитектура решения: как Bedrock AgentCore Identity работает с KMS
Поток аутентификации состоит из четырех компонентов, взаимодействующих в строгой последовательности.
Агент Bedrock инициирует вызов к защищенному ресурсу. Вместо передачи client_secret он обращается к credential provider - конфигурации, которая определяет, как получить токен доступа. Credential provider формирует JWT с claims (issuer, subject, audience, время жизни) и отправляет его в AWS KMS на подпись. KMS выполняет операцию Sign с указанным асимметричным ключом и возвращает подпись, которая добавляется в JWT.
Подписанный JWT отправляется на token endpoint провайдера идентификации (Okta, Auth0, Azure AD, Amazon Cognito). Провайдер извлекает kid из заголовка JWT, находит соответствующий публичный ключ в своем хранилище JWKS и проверяет подпись. При успешной проверке провайдер возвращает access token, который агент использует для вызова целевого API.
AgentCore Identity выступает прослойкой между агентом и credential provider. Он управляет жизненным циклом JWT: кэширует подписанные assertion-токены до истечения срока, обрабатывает ошибки подписи и автоматически запрашивает refresh token при истечении access token. Для разработчика это выглядит как декларативная конфигурация - код агента не содержит криптографической логики.
Пошаговая настройка: от ключа KMS до первого вызова агента
Шаг 1: Создание асимметричного ключа в AWS KMS
Выбор спецификации ключа зависит от требований провайдера идентификации. ECC_NIST_P256 обеспечивает компактную подпись (64 байта) и высокую производительность. RSA_2048 совместим с более широким спектром legacy-систем. Для Bedrock AgentCore Identity рекомендуется ECC_NIST_P256, если провайдер поддерживает ES256.
Создайте ключ через AWS CLI:
aws kms create-key \
--key-spec ECC_NIST_P256 \
--key-usage SIGN_VERIFY \
--description "Bedrock AgentCore Identity signing key" \
--tags TagKey=Environment,TagValue=production
Команда возвращает ARN ключа. Создайте алиас для упрощения дальнейших операций:
aws kms create-alias \
--alias-name alias/bedrock-agentcore-signing-key \
--target-key-id <key-id>
Настройте key policy, разрешающую сервису Bedrock использовать ключ для подписи. Минимальная политика:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "bedrock.amazonaws.com"
},
"Action": "kms:Sign",
"Resource": "*",
"Condition": {
"StringEquals": {
"aws:SourceAccount": "<your-account-id>"
}
}
}
]
}
Без этого условия любой ресурс Bedrock в вашем аккаунте сможет использовать ключ. Для production-среды сузьте доступ через SourceArn конкретного credential provider.
Шаг 2: Экспорт публичного ключа и регистрация у провайдера идентификации
Публичный ключ из KMS извлекается в формаме DER. Для регистрации у провайдера его нужно конвертировать в PEM:
aws kms get-public-key \
--key-id alias/bedrock-agentcore-signing-key \
--output text \
--query PublicKey | base64 --decode > public_key.der
openssl ec -inform DER -in public_key.der -pubin -pubout -outform PEM -out public_key.pem
Провайдеры идентификации принимают ключи двумя способами. Ручная загрузка: в настройках OAuth-клиента найдите раздел «Client Authentication», выберите «Private Key JWT» и загрузите содержимое public_key.pem. JWKS endpoint: если провайдер поддерживает динамическое разрешение ключей, опубликуйте JWKS-документ с публичным ключом на HTTPS-эндпоинте, доступном провайдеру.
Для Okta настройка выглядит так: Applications → ваш OIDC-клиент → Client Credentials → Client authentication method → Private Key / JWT → загрузите PEM-файл. Okta автоматически извлечет kid и алгоритм из ключа.
Шаг 3: Конфигурация credential provider в консоли Amazon Bedrock
Credential provider связывает KMS-ключ с параметрами OAuth-клиента. Перейдите в консоли AWS: Bedrock → Agents → AgentCore Identity → Credential providers → Create credential provider.
Заполните обязательные поля:
- Provider name: логическое имя (например, «okta-production»).
- KMS key ID: ARN или алиас созданного ключа.
- Issuer URL: URL провайдера идентификации (https://dev-123456.okta.com).
- Token endpoint: полный путь к token endpoint (https://dev-123456.okta.com/oauth2/v1/token).
- Client ID: идентификатор OAuth-клиента, зарегистрированного у провайдера.
- Subject: субъект JWT - обычно совпадает с client_id для M2M-потоков.
- Audience: получатель токена - должен совпадать с token endpoint или issuer URL (зависит от провайдера).
После сохранения конфигурации нажмите «Test connection». Bedrock выполнит пробный вызов: сгенерирует JWT, подпишет его в KMS и отправит на token endpoint. Результат теста покажет HTTP-статус и тело ответа - успешный ответ содержит access_token и expires_in.
Критическая ошибка на этом этапе - несовпадение audience. Okta требует audience равным token endpoint, Auth0 - равным issuer URL. Проверьте документацию провайдера. Ошибка «Invalid grant» с кодом invalid_client означает, что публичный ключ не зарегистрирован или не соответствует приватному ключу в KMS.
Поддержка грантовых потоков: machine-to-machine, on-behalf-of и user-delegated
Machine-to-Machine (M2M): агент действует от своего имени
M2M-поток используется для фоновых задач без участия пользователя: nightly-сборка дашбордов, автоматическая модерация контента, синхронизация данных между системами. Агент аутентифицируется как самостоятельный субъект с собственными правами.
Конфигурация credential provider для M2M минимальна. Укажите grant_type = client_credentials в дополнительных параметрах провайдера. JWT, отправляемый на token endpoint, содержит следующие claims:
{
"iss": "https://dev-123456.okta.com",
"sub": "0oabc123def456",
"aud": "https://dev-123456.okta.com/oauth2/v1/token",
"iat": 1722345600,
"exp": 1722345900,
"jti": "unique-jwt-id-abc123"
}
sub равен client_id агента. Провайдер идентификации выпускает access token с claims, соответствующими разрешениям этого клиента. Типичная ошибка - указание audience, отличного от token endpoint. Провайдер возвращает «Invalid audience» и отказывает в выдаче токена.
On-Behalf-Of (OBO): агент выполняет действия от имени пользователя
OBO-поток позволяет агенту получить доступ к ресурсам пользователя после его аутентификации. Пользователь входит в приложение через стандартный OIDC-поток, получает access token и id token. Приложение передает user token агенту, который обменивает его на agent token через token exchange.
Настройка credential provider для OBO требует дополнительных параметров:
- grant_type: urn:ietf:params:oauth:grant-type:jwt-bearer
- assertion: user token, полученный от пользователя
- requested_token_type: urn:ietf:params:oauth:token-type:access_token
JWT, подписанный KMS, выступает как client assertion, а user token передается в параметре assertion. Провайдер проверяет оба токена и выпускает access token с claims пользователя, добавляя поле act (actor) с идентификатором агента:
{
"sub": "user-789",
"act": {
"sub": "agent-456"
},
"scope": "read:documents write:documents"
}
Это позволяет аудит-логам различать действия пользователя и агента, действующего от его имени. Refresh token, полученный в OBO-потоке, привязан к цепочке пользователь-агент и может быть использован для продления сессии без повторной аутентификации пользователя.
User-Delegated Access: агент с ограниченными правами пользователя
User-delegated access отличается от OBO тем, что пользователь явно ограничивает права агента через consent-экран. Агент получает не полный доступ пользователя, а только запрошенные scope.
Конфигурация credential provider включает параметр scope с перечнем запрашиваемых разрешений. При первой аутентификации провайдер показывает пользователю consent-экран: «Агент X запрашивает доступ к чтению ваших документов. Разрешить?» Пользователь может согласиться или отклонить отдельные scope.
JWT, выпущенный после consent, содержит поле scp (scope) с фактически предоставленными разрешениями:
{
"sub": "user-789",
"act": {
"sub": "agent-456"
},
"scp": ["read:documents"],
"scope": "read:documents"
}
Агент запрашивал read:documents и write:documents, но пользователь предоставил только чтение. Агент должен проверять scp в токене и обрабатывать отказ в доступе при попытке записи.
Аудит с CloudTrail: отслеживаем каждый вызов агента
CloudTrail автоматически логирует все вызовы KMS Sign, выполняемые Bedrock AgentCore Identity. Для включения расширенного логирования вызовов credential provider перейдите в CloudTrail → Event History → фильтр по Event source = bedrock.amazonaws.com.
Пример лога для успешной M2M-аутентификации
{
"eventVersion": "1.09",
"eventTime": "2026-07-30T10:15:30Z",
"eventSource": "bedrock.amazonaws.com",
"eventName": "GetAccessToken",
"userIdentity": {
"type": "AWSService",
"invokedBy": "bedrock.amazonaws.com"
},
"requestParameters": {
"credentialProviderArn": "arn:aws:bedrock:us-east-1:123456789:credential-provider/okta-production",
"grantType": "client_credentials",
"clientAssertionType": "urn:ietf:params:oauth:client-assertion-type:jwt-bearer"
},
"responseElements": {
"tokenType": "Bearer",
"expiresIn": 3600,
"scope": "read:data"
},
"requestID": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"eventID": "b2c3d4e5-f6a7-8901-bcde-f12345678901"
}
Ключевые поля для мониторинга: eventName = GetAccessToken фиксирует факт запроса токена, requestParameters.credentialProviderArn указывает конкретный провайдер, responseElements.expiresIn позволяет отслеживать аномально короткое время жизни токена (признак проблем с доверием).
Типичные ошибки и их отражение в CloudTrail
InvalidSignatureException возникает, когда KMS не может проверить целостность запроса на подпись. В логе присутствует errorCode = «InvalidSignatureException» и errorMessage с деталями. Причина обычно в несовпадении key spec (используется симметричный ключ вместо асимметричного) или повреждении JWT перед отправкой в KMS.
AccessDeniedException указывает на проблему с KMS key policy. Лог содержит errorCode = «AccessDeniedException», userIdentity.arn роли, пытавшейся выполнить операцию, и resourceArn ключа. Проверьте, что key policy разрешает действие kms:Sign для сервиса bedrock.amazonaws.com с условием SourceAccount.
InvalidGrantException возвращается провайдером идентификации и транслируется Bedrock в CloudTrail. errorMessage содержит ответ провайдера: «Invalid audience», «Client not found», «Invalid client assertion». Проверьте соответствие audience, client_id и зарегистрированного публичного ключа.
Очистка ресурсов: удаляем ключи и провайдеры без последствий
Порядок удаления важен: сначала отключите активные компоненты, затем удалите ключевой материал.
Удалите credential provider. Это немедленно прекращает все вызовы агентов, использующих этот провайдер:
aws bedrock delete-credential-provider \
--credential-provider-arn arn:aws:bedrock:us-east-1:123456789:credential-provider/okta-production
Запланируйте удаление KMS-ключа. AWS KMS требует окно ожидания 7-30 дней перед безвозвратным удалением:
aws kms schedule-key-deletion \
--key-id alias/bedrock-agentcore-signing-key \
--pending-window-in-days 7
В течение этого окна ключ недоступен для использования, но может быть восстановлен командой cancel-key-deletion. После истечения окна ключ и все данные, зашифрованные им, безвозвратно уничтожаются.
Отзовите публичный ключ у провайдера идентификации. В консоли провайдера удалите загруженный PEM-файл из настроек OAuth-клиента или переключите метод аутентификации обратно на client_secret. Без этого шага провайдер продолжит принимать JWT, подписанные удаленным ключом, если злоумышленник сохранил копию подписанного assertion-токена до истечения его срока жизни.
Проверьте через CloudTrail, что вызовы прекратились. Фильтр по eventName = GetAccessToken и credentialProviderArn удаленного провайдера должен показывать нулевую активность после удаления.
Сравнение с альтернативами и лучшие практики
| Критерий | Private Key JWT + KMS | client_secret | mTLS |
|---|---|---|---|
| Безопасность хранения | Приватный ключ в KMS, недоступен коду | Секрет в переменных окружения / Secrets Manager | Сертификат на диске агента |
| Ротация | Автоматическая через KMS, прозрачна для агента | Ручная, требует координации | Ручная, перевыпуск сертификата |
| Сложность настройки | Средняя (KMS + провайдер) | Низкая | Высокая (PKI, CRL) |
| Поддержка в Bedrock | Нативная через AgentCore Identity | Через Secrets Manager | Не поддерживается |
| Аудит операций | CloudTrail для каждого вызова KMS | Только доступ к секрету | Зависит от инфраструктуры |
| Время жизни токена | Короткоживущий JWT (5-10 минут) | Постоянный секрет | Сертификат на месяцы/годы |
Private Key JWT с KMS - обоснованный выбор для production-сред с высокими требованиями безопасности. Аудит каждого вызова через CloudTrail, автоматическая ротация ключей KMS и отсутствие долгоживущих секретов в коде устраняют основные векторы атак на аутентификацию агентов.
client_secret допустим для dev/test-сред, где скорость прототипирования важнее безопасности. В таких сценариях используйте AWS Secrets Manager с автоматической ротацией и никогда не храните секреты в коде или конфигурационных файлах.
mTLS не поддерживается Bedrock AgentCore Identity нативно и требует развертывания дополнительной инфраструктуры (private CA, управление сертификатами агентов). Этот метод оправдан только при интеграции с legacy-системами, которые не поддерживают OAuth 2.0.
Рекомендация: начинайте с Private Key JWT + KMS для всех новых агентов Bedrock. Затраты на первоначальную настройку (около 30 минут по инструкции выше) окупаются отсутствием инцидентов, связанных с утечкой секретов.